Skip to content

GlyphLayoutManager for correct glyph layout per document#1519

Open
vk-github18 wants to merge 31 commits intoLibrePDF:masterfrom
vk-github18:master-03-23
Open

GlyphLayoutManager for correct glyph layout per document#1519
vk-github18 wants to merge 31 commits intoLibrePDF:masterfrom
vk-github18:master-03-23

Conversation

@vk-github18
Copy link
Copy Markdown
Contributor

@vk-github18 vk-github18 commented Mar 23, 2026

Description of the new Feature/Bugfix

New classes GlyphLayoutManager, GlyphLayoutFontManager for correct glyph layout per document.
These classes should also work in multi-threading environments like in Eclipse BIRT.

Related Issue: #1307

See also eclipse-birt/birt#2144

Unit-Tests for the new Feature/Bugfix

Example classes in pdf-toolbox/src/test/java/org/openpdf/examples/glyphlayout

Compatibilities Issues

New classes that can be used, otherwise no change.

Your real name

Volker Kunert

Testing details

See example classes in pdf-toolbox/src/test/java/org/openpdf/examples/glyphlayout
check the resulting pdf files

Description for Wiki

Correct positioning of accents

To process text containing letters composed of multiple Unicode glyphs e.g. letters with accents,
it is necessary to compute the correct positioning of the glyphs and code this positions into the resulting
PDF file. For complex scripts glyph substitution and reordering is necessary.

OpenPDF can process such texts starting with release 1.3.24.
This page describes the usage for release 3.0.4 or newer with GlyphLayoutManager.

For release 3.0.3 using (the now deprecated) LayoutProcessor see Accents, DIN 91379, non Latin scripts (2025-09-21),
for older releases see Accents, DIN 91379, non Latin scripts (2025-06-06).

Internally OpenPDF uses Java2D builtin routines for glyph layout, reordering and substitution.
Since Java 9 these routines rely on the HarfBuzz shaping library.

DIN 91379

We tested this approach with letters conforming to "DIN 91379: Characters and defined character sequences in Unicode for the electronic processing of names and data exchange in Europe, with CD-ROM" (and the predecessor DIN SPEC 91379) which describes a subset of Unicode consisting mainly of
Latin letters and diacritic signs. This standard is mandatory for the data exchange of the German administration with citizens and businesses since Nov. 2024.

Non-Latin scripts

The processing of text in other languages and scripts using this approach should be possible, you are invited
to try it and share the results.

Multithreading

GlyphLayoutManager is enabled per Document, and has no static state, so it is designed to work in a
multithreading environment processing multiple documents in separate threads.

Usage

1. Step: Provide an OpenType font

Provide an OpenType font containing the necessary characters and positioning information, see below
for some open source fonts.
If no OpenType font is provided, GlyphLayoutManager will throw an exception.

	
	import org.openpdf.text.pdf.GlyphLayoutManager;
...
        float fontSize = 12.0f;
        GlyphLayoutManager glyphLayoutManager  = new GlyphLayoutManager();
        // The  OpenType fonts loaded with glyphLayoutManager.loadFont() are
        // available for glyph layout. Only these fonts can be used.
        String fontDir = "org/openpdf/examples/fonts/";
        Font sans = glyphLayoutManager.loadFont(fontDir + "noto/NotoSans-Regular.ttf", fontSize);

You can also load the font from an input source. You have to supply a name for loading the font that ends
with ".ttf" or ".otf".

	
	import org.openpdf.text.pdf.GlyphLayoutManager;
...
        float fontSize = 12.0f;
        GlyphLayoutManager glyphLayoutManager  = new GlyphLayoutManager();
        // Provide the input source
        inputSource = ... ;
        Font sans = glyphLayoutManager.loadFont(NotoSans-Regular.ttf", inputSource, fontSize);

If an error occurs while loading a font, a GlyphLayoutFontManager.FontLoadException is thrown.

2. Step: Enable advanced glyph layout

You enable advanced glyph layout by registering the glyphLayoutManager with the Document.

        try (Document document = new Document().setGlyphLayoutManager(glyphLayoutManager)) {
        	
        // proceed as usual
        }

You can also use the following form:

        try (Document document = new Document()) {
            document.setGlyphLayoutManager(glyphLayoutManager);
            PdfWriter writer = PdfWriter.getInstance(document, Files.newOutputStream(Paths.get(fileName)));
            
            document.open();
            document.add(new Chunk("A̋ C̀ C̄ C̆ C̈ C̕ C̣ C̦ C̨̆ D̂ F̀ F̄ G̀ H̄ H̦ H̱ J́ J̌ K̀ K̂ K̄ K̇ K̕ K̛ K̦ K͟H K͟h, serif));
            // ...
        }

Set Options

Default Options

Optionally you can set the default GlyphLayoutManager font options before loading the fonts.
These options are used for all fonts loaded with this GlyphLayoutManager.

        GlyphLayoutManager glyphLayoutManager  =
                new GlyphLayoutManager().setDefaultFontOptions(new FontOptions().setKerningOn().setLigaturesOn());

Options per font

If you want to use different options, you can set the font options per font while loading the font.

        Font serifKerning = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setKerningOn());
        Font serifLigatures = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setLigaturesOn());
        Font serifKerningLigatures = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setKerningOn().setLigaturesOn());

Exceptions

Checked Exceptions

GlyphLayoutManager.loadFont throws GlyphLayoutFontManager.FontLoadException if the font can not be loaded
or it is not an OpenType font.

Unchecked Exceptions

The constructor of GlyphLayoutManager throws an IllegalStateException
if LayoutProcessor is enabled. Don't use the deprecated LayoutProcessor!

If a font is used that has not been loaded GlyphLayoutManager.loadFont throws an UnsupportedOperationException
is thrown. All fonts have to be loaded with GlyphLayoutManager.loadFont.

LayoutProcessor

LayoutProcessor is the predecessor of GlyphLayoutManager and is deprecated now, use GlyphLayoutManager.

FopGlyphProcessor

GlyphLayoutManager and FopGlyphProcessor can't be used together, you have to decide for one of them.
If you use GlyphLayoutProcessor, FopGlyphProcessor is switched off using document.setGlyphSubstitutionEnabled().
This call disables FopGlyphProcessor, and its functionality like glyph substitution and more will be provided by GlyphLayoutManager.

Examples

Producing a document

This example shows the correct rendering for all letters from DIN 91379.

Code: GlyphLayoutDin91379.java

Result: GlyphLayoutDin91379.pdf

Processing a form

GlyphLayoutFormDin91379.java

GlyphLayoutFormDin91379.pdf

Producing a document with bidirectional text

Java's Bidi-class is used to deduce the text direction for each chunk of text,
it should not be necessary to specify the text direction per font explicitly.

GlyphLayoutBidi.java

GlyphLayoutBidi.pdf

GlyphLayoutBidiRotated.java

GlyphLayoutBidiRotated.pdf

Specify direction per font

It is possible to set the direction per font, but this should not be necessary.

GlyphLayoutBidiPerFont.java

GlyphLayoutBidiPerFont.pdf

Load the font from an input source

You can load the font from an input source.
GlyphLayoutInputSource.java

GlyphLayoutInputStream.pdf

Specify kerning and ligatures per document

Optionally you can specify kerning and ligatures per document.
GlyphLayoutKernLigaPerFont.java

GlyphLayoutKernLiga.pdf

Specify kerning and ligatures per font

Optionally you can specify kerning and ligatures per font.
GlyphLayoutKernLigaPerFont.java

GlyphLayoutKernLigaPerFont.pdf

Use GlyphLayoutManager for Letters from the Unicode Supplementary Multilingual Plane

Show letters and symbols from the Unicode Supplementary Multilingual Plane,

GlyphLayoutSMP

GlyphLayoutSMP.pdf

Use GlyphLayoutManager with an image

GlyphLayoutWithImage

GlyphLayoutWithImage.pdf

Open source OpenType fonts

  1. Google Noto fonts., Latin, Greek, Cyrillic at GitHub
  2. Noto Arimo
  3. Sudo coding font

References

  1. DIN 91379 (English Wikipedia)
  2. DIN 91379 (German Wikipedia)
  3. DIN 91379 Characters and Sequences (GitHub)
  4. DIN 91379:2022-08: Characters and defined character sequences in Unicode for the electronic processing of names and data exchange in Europe, with CD-ROM (access chargeable)
  5. Decision of IT Planungsrat 2022/51 (in German)
  6. HarfBuzz text shaping library

Correct positioning of accents

To process text containing letters composed of multiple Unicode glyphs e.g. letters with accents,
it is necessary to compute the correct positioning of the glyphs and code this positions into the resulting
PDF file. For complex scripts glyph substitution and reordering is necessary.

OpenPDF can process such texts starting with release 1.3.24.
This page describes the usage for release 3.0.4 or newer with GlyphLayoutManager.

For release 3.0.3 using (the now deprecated) LayoutProcessor see Accents, DIN 91379, non Latin scripts (2025-09-21),
for older releases see Accents, DIN 91379, non Latin scripts (2025-06-06).

Internally OpenPDF uses Java2D builtin routines for glyph layout, reordering and substitution.
Since Java 9 these routines rely on the HarfBuzz shaping library.

DIN 91379

We tested this approach with letters conforming to "DIN 91379: Characters and defined character sequences in Unicode for the electronic processing of names and data exchange in Europe, with CD-ROM" (and the predecessor DIN SPEC 91379) which describes a subset of Unicode consisting mainly of
Latin letters and diacritic signs. This standard is mandatory for the data exchange of the German administration with citizens and businesses since Nov. 2024.

Non-Latin scripts

The processing of text in other languages and scripts using this approach should be possible, you are invited
to try it and share the results.

Multithreading

GlyphLayoutManager is enabled per Document, and has no static state, so it is designed to work in a
multithreading environment processing multiple documents in separate threads.

Usage

1. Step: Provide an OpenType font

Provide an OpenType font containing the necessary characters and positioning information, see below
for some open source fonts.
If no OpenType font is provided, GlyphLayoutManager will throw an exception.

	
	import org.openpdf.text.pdf.GlyphLayoutManager;
...
        float fontSize = 12.0f;
        GlyphLayoutManager glyphLayoutManager  = new GlyphLayoutManager();
        // The  OpenType fonts loaded with glyphLayoutManager.loadFont() are
        // available for glyph layout. Only these fonts can be used.
        String fontDir = "org/openpdf/examples/fonts/";
        Font sans = glyphLayoutManager.loadFont(fontDir + "noto/NotoSans-Regular.ttf", fontSize);

You can also load the font from an input source. You have to supply a name for loading the font that ends
with ".ttf" or ".otf".

	
	import org.openpdf.text.pdf.GlyphLayoutManager;
...
        float fontSize = 12.0f;
        GlyphLayoutManager glyphLayoutManager  = new GlyphLayoutManager();
        // Provide the input source
        inputSource = ... ;
        Font sans = glyphLayoutManager.loadFont(NotoSans-Regular.ttf", inputSource, fontSize);

If an error occurs while loading a font, a GlyphLayoutFontManager.FontLoadException is thrown.

2. Step: Enable advanced glyph layout

You enable advanced glyph layout by registering the glyphLayoutManager with the Document.

        try (Document document = new Document().setGlyphLayoutManager(glyphLayoutManager)) {
        	
        // proceed as usual
        }

You can also use the following form:

        try (Document document = new Document()) {
            document.setGlyphLayoutManager(glyphLayoutManager);
            PdfWriter writer = PdfWriter.getInstance(document, Files.newOutputStream(Paths.get(fileName)));
            
            document.open();
            document.add(new Chunk("A̋ C̀ C̄ C̆ C̈ C̕ C̣ C̦ C̨̆ D̂ F̀ F̄ G̀ H̄ H̦ H̱ J́ J̌ K̀ K̂ K̄ K̇ K̕ K̛ K̦ K͟H K͟h, serif));
            // ...
        }

Set Options

Default Options

Optionally you can set the default GlyphLayoutManager font options before loading the fonts.
These options are used for all fonts loaded with this GlyphLayoutManager.

        GlyphLayoutManager glyphLayoutManager  =
                new GlyphLayoutManager().setDefaultFontOptions(new FontOptions().setKerningOn().setLigaturesOn());

Options per font

If you want to use different options, you can set the font options per font while loading the font.

        Font serifKerning = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setKerningOn());
        Font serifLigatures = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setLigaturesOn());
        Font serifKerningLigatures = glyphLayoutManager.loadFont(fontDir + "noto/NotoSerif-Regular.ttf", 
                fontSize, new FontOptions().setKerningOn().setLigaturesOn());

Exceptions

Checked Exceptions

GlyphLayoutManager.loadFont throws GlyphLayoutFontManager.FontLoadException if the font can not be loaded
or it is not an OpenType font.

Unchecked Exceptions

The constructor of GlyphLayoutManager throws an IllegalStateException
if LayoutProcessor is enabled. Don't use the deprecated LayoutProcessor!

If a font is used that has not been loaded GlyphLayoutManager.loadFont throws an UnsupportedOperationException
is thrown. All fonts have to be loaded with GlyphLayoutManager.loadFont.

LayoutProcessor

LayoutProcessor is the predecessor of GlyphLayoutManager and is deprecated now, use GlyphLayoutManager.

FopGlyphProcessor

GlyphLayoutManager and FopGlyphProcessor can't be used together, you have to decide for one of them.
If you use GlyphLayoutProcessor, FopGlyphProcessor is switched off using document.setGlyphSubstitutionEnabled().
This call disables FopGlyphProcessor, and its functionality like glyph substitution and more will be provided by GlyphLayoutManager.

Examples

Producing a document

This example shows the correct rendering for all letters from DIN 91379.

Code: GlyphLayoutDin91379.java

Result: GlyphLayoutDin91379.pdf

Processing a form

GlyphLayoutFormDin91379.java

GlyphLayoutFormDin91379.pdf

Producing a document with bidirectional text

Java's Bidi-class is used to deduce the text direction for each chunk of text,
it should not be necessary to specify the text direction per font explicitly.

GlyphLayoutBidi.java

GlyphLayoutBidi.pdf

GlyphLayoutBidiRotated.java

GlyphLayoutBidiRotated.pdf

Specify direction per font

It is possible to set the direction per font, but this should not be necessary.

GlyphLayoutBidiPerFont.java

GlyphLayoutBidiPerFont.pdf

Load the font from an input stream

You can load the font from an input stream.
GlyphLayoutInputStream.java

GlyphLayoutInputStream.pdf

Specify kerning and ligatures per document

Optionally you can specify kerning and ligatures per document.
GlyphLayoutKernLiga.java

GlyphLayoutKernLiga.pdf

Specify kerning and ligatures per font

Optionally you can specify kerning and ligatures per font.
GlyphLayoutKernLigaPerFont.java

GlyphLayoutKernLigaPerFont.pdf

Use GlyphLayoutManager for Letters from the Unicode Supplementary Multilingual Plane

Show letters and symbols from the Unicode Supplementary Multilingual Plane,

GlyphLayoutSMP

GlyphLayoutSMP.pdf

Use GlyphLayoutManager with an image

GlyphLayoutWithImage

GlyphLayoutWithImage.pdf

Open source OpenType fonts

  1. Google Noto fonts., Latin, Greek, Cyrillic at GitHub
  2. Noto Arimo
  3. Sudo coding font

References

  1. DIN 91379 (English Wikipedia)
  2. DIN 91379 (German Wikipedia)
  3. DIN 91379 Characters and Sequences (GitHub)
  4. DIN 91379:2022-08: Characters and defined character sequences in Unicode for the electronic processing of names and data exchange in Europe, with CD-ROM (access chargeable)
  5. Decision of IT Planungsrat 2022/51 (in German)
  6. HarfBuzz text shaping library

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant