diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBengali.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBengali.java
new file mode 100644
index 00000000000..f7f89dc4fc6
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBengali.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.pdfbox.examples.pdmodel.BengaliPdfGenerationHelloWorld;
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+/**
+ * DoGlyphLayoutBengali
+ *
+ * This class is adapted from:
+ * BengaliPdfGenerationHelloWorld
+ * Inspired from PdfBox
+ * Example. This attempts to correctly demonstrate to what extent Bengali text rendering is
+ * supported. We read large amount of text from a file and try to render it properly.
+ *
+ * @author Palash Ray
+ *
+ */
+public class DoGlyphLayoutBengali
+{
+ private static final int LINE_GAP = 5;
+ private static final String LOHIT_BENGALI_TTF = "/org/apache/pdfbox/resources/ttf/Lohit-Bengali.ttf";
+ private static final String TEXT_SOURCE_FILE = "/org/apache/pdfbox/resources/ttf/bengali-samples.txt";
+ private static final int FONT_SIZE = 20;
+ private static final int MARGIN = 20;
+
+ public static void main(String[] args) {
+ try {
+ new DoGlyphLayoutBengali().test();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void test() throws IOException {
+ String filename = "DoGlyphLayoutBengali.pdf";
+
+ System.out.println("The generated pdf filename is: " + filename);
+
+ try (PDDocument doc = new PDDocument())
+ {
+ // Create GlyphLayoutProcessor load font
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+ PDType0Font font = glyphLayoutProcessor.loadFont(doc, this.getClass().
+ getResourceAsStream(LOHIT_BENGALI_TTF));
+
+ PDRectangle rectangle = getPageSize();
+ float workablePageWidth = rectangle.getWidth() - 2 * MARGIN;
+ float workablePageHeight = rectangle.getHeight() - 2 * MARGIN;
+
+ List> pagedTexts = getReAlignedTextBasedOnPageHeight(
+ getReAlignedTextBasedOnPageWidth(getBengaliTextFromFile(), font,
+ workablePageWidth),
+ font, workablePageHeight);
+
+ for (List linesForPage : pagedTexts)
+ {
+ PDPage page = new PDPage(getPageSize());
+ doc.addPage(page);
+
+ try (PDPageContentStream contents = new PDPageContentStream(doc, page))
+ {
+ // set GlyphLayoutProcessor for content stream
+ contents.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ contents.beginText();
+ contents.setFont(font, FONT_SIZE);
+ contents.newLineAtOffset(rectangle.getLowerLeftX() + MARGIN,
+ rectangle.getUpperRightY() - MARGIN);
+
+ for (String line : linesForPage)
+ {
+ contents.showText(line);
+ contents.newLineAtOffset(0, -(FONT_SIZE + LINE_GAP));
+ }
+
+ contents.endText();
+
+ }
+ }
+
+ doc.save(filename);
+ }
+ }
+
+ private static List> getReAlignedTextBasedOnPageHeight(List originalLines,
+ PDFont font, float workablePageHeight)
+ {
+ final float newLineHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000
+ * FONT_SIZE + LINE_GAP;
+ List> realignedTexts = new ArrayList<>();
+ float consumedHeight = 0;
+ List linesInAPage = new ArrayList<>();
+ for (String line : originalLines)
+ {
+ if (newLineHeight + consumedHeight < workablePageHeight)
+ {
+ consumedHeight += newLineHeight;
+ }
+ else
+ {
+ consumedHeight = newLineHeight;
+ realignedTexts.add(linesInAPage);
+ linesInAPage = new ArrayList<>();
+ }
+
+ linesInAPage.add(line);
+ }
+ realignedTexts.add(linesInAPage);
+ return realignedTexts;
+ }
+
+ private static List getReAlignedTextBasedOnPageWidth(List originalLines,
+ PDFont font, float workablePageWidth) throws IOException
+ {
+ List uniformlyWideTexts = new ArrayList<>();
+ float consumedWidth = 0;
+ StringBuilder sb = new StringBuilder();
+ for (String line : originalLines)
+ {
+ float newTokenWidth = 0;
+ StringTokenizer st = new StringTokenizer(line, " ", true);
+ while (st.hasMoreElements())
+ {
+ String token = st.nextToken();
+ newTokenWidth = font.getStringWidth(token) / 1000 * FONT_SIZE;
+ if (newTokenWidth + consumedWidth < workablePageWidth)
+ {
+ consumedWidth += newTokenWidth;
+ }
+ else
+ {
+ // add a new text chunk
+ uniformlyWideTexts.add(sb.toString());
+ consumedWidth = newTokenWidth;
+ sb = new StringBuilder();
+ }
+
+ sb.append(token);
+ }
+
+ // add a new text chunk
+ uniformlyWideTexts.add(sb.toString());
+ consumedWidth = newTokenWidth;
+ sb = new StringBuilder();
+ }
+
+ return uniformlyWideTexts;
+ }
+
+ private static PDRectangle getPageSize()
+ {
+ return PDRectangle.A4;
+ }
+
+ private static List getBengaliTextFromFile() throws IOException
+ {
+ List lines = new ArrayList<>();
+
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(
+ BengaliPdfGenerationHelloWorld.class.getResourceAsStream(TEXT_SOURCE_FILE), StandardCharsets.UTF_8)))
+ {
+ while (true)
+ {
+ String line = br.readLine();
+
+ if (line == null)
+ {
+ break;
+ }
+
+ if (!line.startsWith("#"))
+ {
+ lines.add(line);
+ }
+ }
+ }
+
+ return lines;
+ }
+
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBidi.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBidi.java
new file mode 100644
index 00000000000..b064e6a2177
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutBidi.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Examples for bidirectional text with GlyphLayoutProcessor
+ *
+ * @author Volker Kunert
+ * @version 2026-05
+ */
+public class DoGlyphLayoutBidi {
+ public static final String TEXT1 = "نحن الآن في شهر رمضان 1447 هجري";
+ public static final String TEXT2 = "Guten Tag ";
+ public static final String TEXT3 = "السلام عليكم";
+ public static final String TEXT4 = " Good afternoon";
+
+ /*
+ * Main
+ */
+ public static void main(String[] args) {
+ new DoGlyphLayoutBidi().test();
+ }
+
+ /*
+ * show one line
+ */
+ public static float showLine(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String text) throws IOException {
+ return showLine(cs, new PDType0Font[]{font}, fontSize, x, y, new String[]{text});
+ }
+
+ /*
+ * show one line
+ */
+ public static float showLine(PDPageContentStream cs, PDType0Font[] fonts, float fontSize,
+ float x, float y, String[] texts) throws IOException {
+ cs.beginText();
+ cs.newLineAtOffset(x, y);
+
+ if (fonts.length != texts.length) {
+ throw new IllegalArgumentException("Size of fonts and texts is different");
+ }
+ for (int i = 0; i < texts.length; i++) {
+ cs.setFont(fonts[i], fontSize);
+ cs.showText(texts[i]);
+ }
+ cs.endText();
+
+ float height = fonts[0].getBoundingBox().getHeight();
+ y -= height / 1000f * fontSize;
+ return y;
+ }
+
+ /**
+ * Start the example
+ */
+ public void test() {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = "DoGlyphLayoutBidi.pdf";
+ String arabicPath = "/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansArabic-Regular.ttf";
+ String lgcPath = "/org/apache/pdfbox/examples/glyphlayout/noto/NotoSans-Regular.ttf";
+
+ float fontSize = 12.0f;
+
+ PDDocument pdDocument = new PDDocument();
+
+ PDType0Font arabicFont = createPdType0Font(glyphLayoutProcessor, pdDocument, arabicPath);
+ PDType0Font lgcFont = createPdType0Font(glyphLayoutProcessor, pdDocument, lgcPath);
+
+ PDPage blankPage = new PDPage();
+ pdDocument.addPage(blankPage);
+ PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0),
+ PDPageContentStream.AppendMode.APPEND, true);
+ cs.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ float x = blankPage.getBBox().getLowerLeftX() + fontSize;
+ float y = blankPage.getBBox().getUpperRightY() - fontSize;
+
+ y = showLine(cs, arabicFont, fontSize, x, y, TEXT1);
+ showLine(cs, new PDType0Font[]{lgcFont, arabicFont, lgcFont}, fontSize, x, y, new String[]{TEXT2, TEXT3, TEXT4});
+ cs.close();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * Create the PDType0Font font
+ */
+ private PDType0Font createPdType0Font(GlyphLayoutProcessor glyphLayoutProcessor, PDDocument pdDocument,
+ String fontPath) {
+ InputStream fontStream = this.getClass().
+ getResourceAsStream(fontPath);
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream);
+ return font;
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379.java
new file mode 100644
index 00000000000..de22c97784d
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Example of formatting for letters defined in: DIN 91379: Characters in Unicode for the electronic processing
+ * of names and data exchange in Europe
+ *
+ * Font used: Arimo-Regular.ttf, see
+ * https://fonts.google.com/specimen/Arimo
+ *
+ * Use of the positioning features of Java.
+ *
+ * @author Volker Kunert
+ * @version 2026-04-04
+ */
+public class DoGlyphLayoutDin91379 {
+ public static String LATIN_CHARS_DIN_91379 =
+ "DIN 91379: Characters in Unicode for the electronic processing of names \n"
+ + "and data exchange in Europe\n"
+ + "Font used: Arimo-Regular.ttf\n"
+ + "\n"
+ + "bll; Latin Letters (normative)\n"
+ + "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z\n"
+ + "À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó\n"
+ + "ô õ ö ø ù ú û ü ý þ ÿ Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ħ\n"
+ + "ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ Ş\n"
+ + "ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž Ƈ ƈ Ə Ɨ Ơ ơ Ư ư Ʒ Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ\n"
+ + "Ǘ ǘ Ǚ ǚ Ǜ ǜ Ǟ ǟ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ Ǵ ǵ Ǹ ǹ Ǻ ǻ Ǽ ǽ Ǿ ǿ Ȓ ȓ Ș ș Ț ț Ȟ ȟ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ\n"
+ + "Ȯ ȯ Ȱ ȱ Ȳ ȳ ə ɨ ʒ Ḃ ḃ Ḇ ḇ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ ḗ Ḝ ḝ Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḻ ḻ Ṁ\n"
+ + "ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṟ ṟ Ṡ ṡ Ṣ ṣ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ẁ ẁ Ẃ ẃ Ẅ ẅ Ẇ ẇ Ẍ ẍ Ẏ ẏ Ẑ ẑ Ẓ ẓ Ẕ ẕ\n"
+ + "ẖ ẗ ẞ Ạ ạ Ả ả Ấ ấ Ầ ầ Ẩ ẩ Ẫ ẫ Ậ ậ Ắ ắ Ằ ằ Ẳ ẳ Ẵ ẵ Ặ ặ Ẹ ẹ Ẻ ẻ Ẽ ẽ Ế ế Ề ề Ể ể Ễ ễ Ệ ệ Ỉ ỉ Ị ị Ọ ọ Ỏ ỏ Ố\n"
+ + "ố Ồ ồ Ổ ổ Ỗ ỗ Ộ ộ Ớ ớ Ờ ờ Ở ở Ỡ ỡ Ợ ợ Ụ ụ Ủ ủ Ứ ứ Ừ ừ Ử ử Ữ ữ Ự ự Ỳ ỳ Ỵ ỵ Ỷ ỷ Ỹ ỹ\n"
+ + "Sequences\n"
+ + "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 \n"
+ + "K͟h L̂ L̥ L̥̄ L̦ M̀ M̂ M̆ M̐ N̂ N̄ N̆ N̦ P̀ P̄ P̕ P̣ R̆ R̥ R̥̄ S̀ S̄ S̛̄ S̱ T̀ T̄ \n"
+ + "T̈ T̕ T̛ U̇ Z̀ Z̄ Z̆ Z̈ Z̧ a̋ c̀ c̄ c̆ c̈ c̕ c̣ c̦ c̨̆ d̂ f̀ f̄ g̀ h̄ h̦ j́ k̀ \n"
+ + "k̂ k̄ k̇ k̕ k̛ k̦ k͟h l̂ l̥ l̥̄ l̦ m̀ m̂ m̆ m̐ n̂ n̄ n̆ n̦ p̀ p̄ p̕ p̣ r̆ r̥ r̥̄ \n"
+ + "s̀ s̄ s̛̄ s̱ t̀ t̄ t̕ t̛ u̇ z̀ z̄ z̆ z̈ z̧ Ç̆ Û̄ ç̆ û̄ ÿ́ Č̕ Č̣ č̕ č̣ ē̍ Ī́ ī́ \n"
+ + "ō̍ Ž̦ Ž̧ ž̦ ž̧ Ḳ̄ ḳ̄ Ṣ̄ ṣ̄ Ṭ̄ ṭ̄ Ạ̈ ạ̈ Ọ̈ ọ̈ Ụ̄ Ụ̈ ụ̄ ụ̈ \n"
+ + "bnlreq; Non-Letters N1 (normative)\n"
+ + " ' , - . ` ~ ¨ ´ · ʹ ʺ ʾ ʿ ˈ ˌ ’ ‡ \n"
+ + "bnl; Non-Letters N2 (normative)\n"
+ + "! \" # $ % & ( ) * + / 0 1 2 3 4 5 6 7 8 9 : ; < = > \n"
+ + "? @ [ \\ ] ^ _ { | } ¡ ¢ £ ¥ § © ª « ¬ ® ¯ ° ± ² ³ µ \n"
+ + "¶ ¹ º » ¿ × ÷ € \n"
+ + "bnlopt; Non-Letters N3 (normative)\n"
+ + "¤ ¦ ¸ ¼ ½ ¾ \n"
+ + "gl; Greek Letters (extended)\n"
+ + "Ά Έ Ή Ί Ό Ύ Ώ ΐ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ \n"
+ + "Τ Υ Φ Χ Ψ Ω Ϊ Ϋ ά έ ή ί ΰ α β γ δ ε ζ η θ ι κ λ μ ν \n"
+ + "ξ ο π ρ ς σ τ υ φ χ ψ ω ϊ ϋ ό ύ ώ \n"
+ + "cl; Cyrillic Letters (extended)\n"
+ + "Ѝ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш \n"
+ + "Щ Ъ Ь Ю Я а б в г д е ж з и й к л м н о п р с т у ф \n"
+ + "х ц ч ш щ ъ ь ю я ѝ \n"
+ + "enl; Non-Letters E1 (extended)\n"
+ + "ƒ ʰ ʳ ˆ ˜ ˢ ᵈ ᵗ ‘ ‚ “ ” „ † … ‰ ′ ″ ‹ › ⁰ ⁴ ⁵ ⁶ ⁷ ⁸ \n"
+ + "⁹ ⁿ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ™ ∞ ≤ ≥ \n"
+ + "Additional non-letters (not included in DIN 91379): – — •�";
+
+ /*
+ * Start the test
+ */
+ public static void main(String[] args) {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = "DoGlyphLayoutDin91379.pdf";
+ float fontSize = 12.0f;
+
+ PDDocument pdDocument = new PDDocument();
+ InputStream fontStream = DoGlyphLayoutDin91379Form.class.
+ getResourceAsStream("/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf");
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream);
+
+ PDPage blankPage = new PDPage();
+ pdDocument.addPage(blankPage);
+ PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0),
+ PDPageContentStream.AppendMode.APPEND, true);
+ cs.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ float x = blankPage.getBBox().getLowerLeftX() + fontSize;
+ float y = blankPage.getBBox().getUpperRightY() - fontSize;
+ showComposites(cs, font, fontSize, x, y, LATIN_CHARS_DIN_91379);
+ cs.close();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * break the text into lines and show them
+ */
+ public static void showComposites(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String s) throws Exception {
+
+ s = s.replaceAll("\t", " ");
+ String[] lines = s.split("[\n]");
+
+ float height = font.getBoundingBox().getHeight();
+
+ for (String line : lines) {
+ if (!line.isEmpty()) {
+ showCompositesLine(cs, font, fontSize, x, y, line);
+ y -= height / 1000f * fontSize;
+ }
+ }
+ }
+
+ /*
+ * show one line
+ */
+ public static void showCompositesLine(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String line) throws IOException {
+ cs.beginText();
+ cs.setFont(font, fontSize);
+ cs.newLineAtOffset(x, y);
+ cs.showText(line);
+ cs.endText();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379Form.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379Form.java
new file mode 100644
index 00000000000..7acf34ca2c9
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutDin91379Form.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.pdmodel.*;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Objects;
+
+/*
+ * Example of formatting for letters defined in:
+ * DIN 91379: Characters and defined character sequences in Unicode for the electronic processing of names
+ * and data exchange in Europe
+ *
+ * Font used: Arimo-Regular.ttf, see https://fonts.google.com/specimen/Arimo
+ *
+ * Use of the glyph layout features of Java that are based on HarfBuzz
+ *
+ * @author Volker Kunert
+ * @date 2026-04-04
+ */
+
+public class DoGlyphLayoutDin91379Form {
+
+ public static String LATIN_CHARS_DIN_91379 =
+ "DIN 91379: Characters in Unicode for the electronic processing of names "
+ + "and data exchange in Europe\n"
+ + "bll; Latin Letters (normative)\n"
+ + "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z "
+ + "À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó "
+ + "ô õ ö ø ù ú û ü ý þ ÿ Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď ď Đ đ Ē ē Ĕ ĕ Ė ė Ę ę Ě ě Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ħ "
+ + "ħ Ĩ ĩ Ī ī Ĭ ĭ Į į İ ı IJ ij Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ō ō Ŏ ŏ Ő ő Œ œ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ Ş "
+ + "ş Š š Ţ ţ Ť ť Ŧ ŧ Ũ ũ Ū ū Ŭ ŭ Ů ů Ű ű Ų ų Ŵ ŵ Ŷ ŷ Ÿ Ź ź Ż ż Ž ž Ƈ ƈ Ə Ɨ Ơ ơ Ư ư Ʒ Ǎ ǎ Ǐ ǐ Ǒ ǒ Ǔ ǔ Ǖ ǖ "
+ + "Ǘ ǘ Ǚ ǚ Ǜ ǜ Ǟ ǟ Ǣ ǣ Ǥ ǥ Ǧ ǧ Ǩ ǩ Ǫ ǫ Ǭ ǭ Ǯ ǯ ǰ Ǵ ǵ Ǹ ǹ Ǻ ǻ Ǽ ǽ Ǿ ǿ Ȓ ȓ Ș ș Ț ț Ȟ ȟ ȧ Ȩ ȩ Ȫ ȫ Ȭ ȭ "
+ + "Ȯ ȯ Ȱ ȱ Ȳ ȳ ə ɨ ʒ Ḃ ḃ Ḇ ḇ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ ḗ Ḝ ḝ Ḟ ḟ Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḻ ḻ Ṁ "
+ + "ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṟ ṟ Ṡ ṡ Ṣ ṣ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ẁ ẁ Ẃ ẃ Ẅ ẅ Ẇ ẇ Ẍ ẍ Ẏ ẏ Ẑ ẑ Ẓ ẓ Ẕ ẕ "
+ + "ẖ ẗ ẞ Ạ ạ Ả ả Ấ ấ Ầ ầ Ẩ ẩ Ẫ ẫ Ậ ậ Ắ ắ Ằ ằ Ẳ ẳ Ẵ ẵ Ặ ặ Ẹ ẹ Ẻ ẻ Ẽ ẽ Ế ế Ề ề Ể ể Ễ ễ Ệ ệ Ỉ ỉ Ị ị Ọ ọ Ỏ ỏ Ố "
+ + "ố Ồ ồ Ổ ổ Ỗ ỗ Ộ ộ Ớ ớ Ờ ờ Ở ở Ỡ ỡ Ợ ợ Ụ ụ Ủ ủ Ứ ứ Ừ ừ Ử ử Ữ ữ Ự ự Ỳ ỳ Ỵ ỵ Ỷ ỷ Ỹ ỹ\n"
+ + "Sequences\n"
+ + "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 L̂ L̥ L̥̄ L̦ M̀ M̂ M̆ M̐ N̂ N̄ N̆ N̦ P̀ P̄ P̕ P̣ R̆ R̥ R̥̄ S̀ S̄ S̛̄ S̱ T̀ T̄ "
+ + "T̈ T̕ T̛ U̇ Z̀ Z̄ Z̆ Z̈ Z̧ a̋ c̀ c̄ c̆ c̈ c̕ c̣ c̦ c̨̆ d̂ f̀ f̄ g̀ h̄ h̦ j́ k̀ "
+ + "k̂ k̄ k̇ k̕ k̛ k̦ k͟h l̂ l̥ l̥̄ l̦ m̀ m̂ m̆ m̐ n̂ n̄ n̆ n̦ p̀ p̄ p̕ p̣ r̆ r̥ r̥ "
+ + "s̀ s̄ s̛̄ s̱ t̀ t̄ t̕ t̛ u̇ z̀ z̄ z̆ z̈ z̧ Ç̆ Û̄ ç̆ û̄ ÿ́ Č̕ Č̣ č̕ č̣ ē̍ Ī́ ī́ "
+ + "ō̍ Ž̦ Ž̧ ž̦ ž̧ Ḳ̄ ḳ̄ Ṣ̄ ṣ̄ Ṭ̄ ṭ̄ Ạ̈ ạ̈ Ọ̈ ọ̈ Ụ̄ Ụ̈ ụ̄ ụ̈\n"
+ + "bnlreq; Non-Letters N1 (normative)\n"
+ + " ' , - . ` ~ ¨ ´ · ʹ ʺ ʾ ʿ ˈ ˌ ’ ‡\n"
+ + "bnl; Non-Letters N2 (normative)\n"
+ + "! \" # $ % & ( ) * + / 0 1 2 3 4 5 6 7 8 9 : ; < = > "
+ + "? @ [ \\ ] ^ _ { | } ¡ ¢ £ ¥ § © ª « ¬ ® ¯ ° ± ² ³ µ "
+ + "¶ ¹ º » ¿ × ÷ € \n"
+ + "bnlopt; Non-Letters N3 (normative)\n"
+ + "¤ ¦ ¸ ¼ ½ ¾ \n"
+ + "gl; Greek Letters (extended)\n"
+ + "Ά Έ Ή Ί Ό Ύ Ώ ΐ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ "
+ + "Τ Υ Φ Χ Ψ Ω Ϊ Ϋ ά έ ή ί ΰ α β γ δ ε ζ η θ ι κ λ μ ν "
+ + "ξ ο π ρ ς σ τ υ φ χ ψ ω ϊ ϋ ό ύ ώ \n"
+ + "cl; Cyrillic Letters (extended)\n"
+ + "Ѝ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш "
+ + "Щ Ъ Ь Ю Я а б в г д е ж з и й к л м н о п р с т у ф "
+ + "х ц ч ш щ ъ ь ю я ѝ \n"
+ + "enl; Non-Letters E1 (extended)\n"
+ + "ƒ ʰ ʳ ˆ ˜ ˢ ᵈ ᵗ ‘ ‚ “ ” „ † … ‰ ′ ″ ‹ › ⁰ ⁴ ⁵ ⁶ ⁷ ⁸ "
+ + "⁹ ⁿ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ™ ∞ ≤ ≥ \n"
+ + "Additional non-letters (not included in DIN 91379): – — •�";
+
+
+ /*
+ * Run the example
+ */
+
+ public static void main(String[] args) {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = "DoGlyphLayoutDin91379Form.pdf";
+
+ String fontSizeString = "12";
+
+ InputStream fontStream = DoGlyphLayoutDin91379Form.class.
+ getResourceAsStream("/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf");
+ File pdfForm = new File(Objects.requireNonNull(DoGlyphLayoutDin91379Form.class.
+ getResource("/org/apache/pdfbox/examples/glyphlayout/PdfForm.pdf")).toURI());
+
+ PDDocument pdDocument = Loader.loadPDF(pdfForm);
+
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream, false);
+ // embedSubset has to be set to false
+
+ PDDocumentCatalog docCatalog = pdDocument.getDocumentCatalog();
+ PDAcroForm acroForm = docCatalog.getAcroForm();
+
+ acroForm.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ PDResources resources = new PDResources();
+ String resourceFontName = resources.add(font).getName();
+ acroForm.setDefaultResources(resources);
+ for (PDField field : acroForm.getFields()) {
+ if (field instanceof PDTextField) {
+ PDTextField textfield = (PDTextField) field;
+ textfield.setDefaultAppearance(
+ String.format("/%s %s Tf 0 g ", resourceFontName, fontSizeString));
+ textfield.setValue(LATIN_CHARS_DIN_91379);
+ }
+ }
+ acroForm.refreshAppearances();
+ acroForm.flatten();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutLigaturesAndKerning.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutLigaturesAndKerning.java
new file mode 100644
index 00000000000..413d7b4a8f1
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutLigaturesAndKerning.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.pdmodel.GlyphLayoutFontLoader;
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Examples for ligatures and kerning
+ * See PDFBOX-4951
+ *
+ * The default processing of GlyphLayoutProcessor is with ligatures and kerning disabled.
+ * You can enable ligatures and kerning using FontOptions, see below.
+ *
+ * @author Volker Kunert
+ * @version 2026-05
+ */
+public class DoGlyphLayoutLigaturesAndKerning {
+ public static final String firaString = "!= == === >= <=";
+ public static final String dejavuString = "AVATAR, effective, affiliation, float, film, affluent";
+ /*
+ * Start the test
+ */
+ public static void main(String[] args) {
+ new DoGlyphLayoutLigaturesAndKerning().test();
+ }
+
+ /**
+ * Start the example
+ */
+ public void test() {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = "DoGlyphLayoutLigaturesAndKerning.pdf";
+ String firaPath = "/org/apache/pdfbox/examples/glyphlayout/firacode/FiraCode-Regular.ttf";
+ String dejavuPath = "/org/apache/pdfbox/examples/glyphlayout/dejavu/DejaVuSans.ttf";
+
+ float fontSize = 12.0f;
+
+ PDDocument pdDocument = new PDDocument();
+
+ PDType0Font firaFont = createPdType0Font(glyphLayoutProcessor, pdDocument, firaPath);
+ PDType0Font firaLigFont = createPdType0Font(glyphLayoutProcessor, pdDocument, firaPath,
+ new GlyphLayoutFontLoader.FontOptions().setLigaturesOn());
+
+ PDType0Font dejavuFont = createPdType0Font(glyphLayoutProcessor, pdDocument, dejavuPath);
+
+ PDType0Font dejavuLigFont = createPdType0Font(glyphLayoutProcessor, pdDocument, dejavuPath,
+ new GlyphLayoutFontLoader.FontOptions().setLigaturesOn());
+
+ PDType0Font dejavuKernFont = createPdType0Font(glyphLayoutProcessor, pdDocument, dejavuPath,
+ new GlyphLayoutFontLoader.FontOptions().setKerningOn());
+
+ PDType0Font dejavuLigKernFont = createPdType0Font(glyphLayoutProcessor, pdDocument, dejavuPath,
+ new GlyphLayoutFontLoader.FontOptions().setLigaturesOn().setKerningOn());
+
+
+
+ PDPage blankPage = new PDPage();
+ pdDocument.addPage(blankPage);
+ PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0),
+ PDPageContentStream.AppendMode.APPEND, true);
+ cs.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ float x = blankPage.getBBox().getLowerLeftX() + fontSize;
+ float y = blankPage.getBBox().getUpperRightY() - fontSize;
+ y = showComposites(cs, firaFont, fontSize, x, y, firaString);
+ y = showComposites(cs, firaLigFont, fontSize, x, y, firaString + " (Ligatures)");
+ y = showComposites(cs, dejavuFont, fontSize, x, y, dejavuString);
+ y = showComposites(cs, dejavuLigFont, fontSize, x, y, dejavuString + " (Ligatures)");
+ y = showComposites(cs, dejavuKernFont, fontSize, x, y, dejavuString + " (Kerning)");
+ showComposites(cs, dejavuLigKernFont, fontSize, x, y, dejavuString + " (Ligatures and kerning)");
+ cs.close();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * Create the PDType0Font font
+ */
+ private PDType0Font createPdType0Font(GlyphLayoutProcessor glyphLayoutProcessor, PDDocument pdDocument,
+ String fontPath) {
+ InputStream fontStream = this.getClass().
+ getResourceAsStream(fontPath);
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream);
+ return font;
+ }
+
+ /*
+ * Create the PDType0Font font with font options
+ */
+ private PDType0Font createPdType0Font(GlyphLayoutProcessor glyphLayoutProcessor, PDDocument pdDocument,
+ String fontPath, GlyphLayoutFontLoader.FontOptions fontOptions) {
+ InputStream fontStream = this.getClass().
+ getResourceAsStream(fontPath);
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream, fontOptions);
+ return font;
+ }
+
+ /*
+ * break the text into lines and show them
+ */
+ public static float showComposites(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String s) throws Exception {
+
+ s = s.replaceAll("\t", " ");
+ String[] lines = s.split("[\n]");
+
+ float height = font.getBoundingBox().getHeight();
+
+ for (String line : lines) {
+ if (!line.isEmpty()) {
+ showCompositesLine(cs, font, fontSize, x, y, line);
+ y -= height / 1000f * fontSize;
+ }
+ }
+ return y;
+ }
+
+ /*
+ * show one line
+ */
+ public static void showCompositesLine(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String line) throws IOException {
+ cs.beginText();
+ cs.setFont(font, fontSize);
+ cs.newLineAtOffset(x, y);
+ cs.showText(line);
+ cs.endText();
+ }
+}
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutMissingGlyphs.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutMissingGlyphs.java
new file mode 100644
index 00000000000..e485dab6834
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutMissingGlyphs.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Example for missing glyphs
+ */
+public class DoGlyphLayoutMissingGlyphs {
+ private static final String LOHIT_BENGALI_TTF = "/org/apache/pdfbox/resources/ttf/Lohit-Bengali.ttf";
+
+ /*
+ * Main
+ */
+ public static void main(String[] args) {
+ new DoGlyphLayoutMissingGlyphs().test();
+ }
+
+
+ /**
+ * Start the example
+ */
+ public void test() {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = this.getClass().getName() + ".pdf";
+ String bengaliFontPath = "/org/apache/pdfbox/resources/ttf/Lohit-Bengali.ttf";
+
+ float fontSize = 12.0f;
+
+ PDDocument pdDocument = new PDDocument();
+
+ PDType0Font bengaliFont = createPdType0Font(glyphLayoutProcessor, pdDocument, bengaliFontPath);
+
+ PDPage blankPage = new PDPage();
+ pdDocument.addPage(blankPage);
+ PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0),
+ PDPageContentStream.AppendMode.OVERWRITE, true);
+
+ cs.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ float x = blankPage.getBBox().getLowerLeftX() + fontSize;
+ float y = blankPage.getBBox().getUpperRightY() - fontSize;
+
+ y = showLine(cs, bengaliFont, fontSize, x, y, "123ABC");
+
+ cs.close();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * Create the PDType0Font font
+ */
+ private PDType0Font createPdType0Font(GlyphLayoutProcessor glyphLayoutProcessor, PDDocument pdDocument,
+ String fontPath) {
+ InputStream fontStream = this.getClass().
+ getResourceAsStream(fontPath);
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream);
+ return font;
+ }
+
+ /*
+ * show one line
+ */
+ public static float showLine(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String text) throws IOException {
+ cs.beginText();
+ cs.newLineAtOffset(x, y);
+
+ cs.setFont(font, fontSize);
+ cs.showText(text);
+ cs.endText();
+
+ float height = font.getBoundingBox().getHeight();
+ y -= height / 1000f * fontSize;
+ return y;
+ }
+}
+
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutSMP.java b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutSMP.java
new file mode 100644
index 00000000000..921707e4941
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/glyphlayout/DoGlyphLayoutSMP.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.examples.pdmodel.glyphlayout;
+
+import org.apache.pdfbox.pdmodel.GlyphLayoutProcessor;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Examples for Supplementary Multilingual Plane with GlyphLayoutProcessor
+ * (Output is identical with plain PDFBox)
+ *
+ * @author Volker Kunert
+ * @version 2026-05
+ */
+public class DoGlyphLayoutSMP {
+
+ private static final String TEXT_INTRO =
+ "Test of Letters from the Supplementary Multilingual Plane, Mathematical Alphanumeric Symbols";
+
+ private static final int[] MATHEMATICAL_CODEPOINTS = new int[]{0x1D504, 0x1D505, 0x212D, 0x1D507, 0x1D508, 0x1D509,
+ 0x1D50A, 0x210C, 0x2111, 0x1D50D, 0x1D50E, 0x1D50F, 0x1D510, 0x1D511, 0x1D512, 0x1D513, 0x1D514, 0x211C,
+ 0x1D516, 0x1D517, 0x1D518, 0x1D519, 0x1D51A, 0x1D51B, 0x1D51C, 0x2128, 0x0A, 0x1D51E, 0x1D51F, 0x1D520,
+ 0x1D521, 0x1D522, 0x1D523, 0x1D524, 0x1D525, 0x1D526, 0x1D527, 0x1D528, 0x1D529, 0x1D52A, 0x1D52B, 0x1D52C,
+ 0x1D52D, 0x1D52E, 0x1D52F, 0x1D530, 0x1D531, 0x1D532, 0x1D533, 0x1D534, 0x1D535, 0x1D536, 0x1D537,
+ 0x0A, 0x0A,
+ 0x1D7D8, 0x1D7D9, 0x1D7DA, 0x1D7DB, 0x1D7DC, 0x1D7DD, 0x1D7DE, 0x1D7DF, 0x1D7E0, 0x1D7E1,
+ 0x0A, 0x0A,
+ 0x1D49C, 0x212C, 0x1D49E, 0x1D49F, 0x2130, 0x2131, 0x1D4A2, 0x210B, 0x2110, 0x1D4A5, 0x1D4A6, 0x2112,
+ 0x2133, 0x1D4A9, 0x1D4AA, 0x1D4AB, 0x1D4AC, 0x211B, 0x1D4AE, 0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2, 0x1D4B3,
+ 0x1D4B4, 0x1D4B5,
+ 0x0A,
+ 0x1D4B6, 0x1D4B7, 0x1D4B8, 0x1D4B9, 0x212F, 0x1D4BB, 0x210A, 0x1D4BD, 0x1D4BE,
+ 0x1D4BF, 0x1D4C0, 0x1D4C1, 0x1D4C2, 0x1D4C3, 0x2134, 0x1D4C5, 0x1D4C6, 0x1D4C7, 0x1D4C8, 0x1D4C9, 0x1D4CA,
+ 0x1D4CB, 0x1D4CC, 0x1D4CD, 0x1D4CE, 0x1D4CF,
+ 0x0A, 0x0A};
+
+ private static final String MATHEMATICAL = new String(MATHEMATICAL_CODEPOINTS, 0, MATHEMATICAL_CODEPOINTS.length);
+
+ /*
+ * Main
+ */
+ public static void main(String[] args) {
+ new DoGlyphLayoutSMP().test();
+ }
+
+ /*
+ * break the text into lines and show them
+ */
+ public static float showLines(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String s) throws Exception {
+
+ s = s.replaceAll("\t", " ");
+ String[] lines = s.split("[\n]");
+
+ float height = font.getBoundingBox().getHeight();
+
+ for (String line : lines) {
+ if (!line.isEmpty()) {
+ y = showLine(cs, font, fontSize, x, y, line);
+ }
+ }
+ return y;
+ }
+
+
+ /*
+ * show one line
+ */
+ public static float showLine(PDPageContentStream cs, PDType0Font font, float fontSize,
+ float x, float y, String text) throws IOException {
+ cs.beginText();
+ cs.newLineAtOffset(x, y);
+
+ cs.setFont(font, fontSize);
+ cs.showText(text);
+ cs.endText();
+
+ float height = font.getBoundingBox().getHeight();
+ y -= height / 1000f * fontSize;
+ return y;
+ }
+
+ /**
+ * Start the example
+ */
+ public void test() {
+ try {
+ GlyphLayoutProcessor glyphLayoutProcessor = new GlyphLayoutProcessor();
+
+ String outputFilename = "DoGlyphLayoutSMP.pdf";
+ String sansFontPath = "/org/apache/pdfbox/examples/glyphlayout/noto/NotoSans-Regular.ttf";
+ String sansMonoFontPath = "/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMono-Regular.ttf";
+ String mathFontPath = "/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMath-Regular.ttf";
+
+ float fontSize = 12.0f;
+
+ PDDocument pdDocument = new PDDocument();
+
+ PDType0Font sansFont = createPdType0Font(glyphLayoutProcessor, pdDocument, sansFontPath);
+ PDType0Font sansMonoFont = createPdType0Font(glyphLayoutProcessor, pdDocument, sansMonoFontPath);
+ PDType0Font mathFont = createPdType0Font(glyphLayoutProcessor, pdDocument, mathFontPath);
+
+
+ PDPage blankPage = new PDPage();
+ pdDocument.addPage(blankPage);
+ PDPageContentStream cs = new PDPageContentStream(pdDocument, pdDocument.getPage(0),
+ PDPageContentStream.AppendMode.APPEND, true);
+ cs.setGlyphLayoutProcessor(glyphLayoutProcessor);
+
+ float x = blankPage.getBBox().getLowerLeftX() + fontSize;
+ float y = blankPage.getBBox().getUpperRightY() - fontSize;
+
+ y = showLine(cs, sansFont, fontSize, x, y, TEXT_INTRO);
+ y = showLine(cs, sansFont, fontSize, x, y, "Font used: " + mathFont.getName());
+ y = showLines(cs, mathFont, fontSize, x, y, MATHEMATICAL);
+ cs.close();
+ pdDocument.save(outputFilename);
+ pdDocument.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * Create the PDType0Font font
+ */
+ private PDType0Font createPdType0Font(GlyphLayoutProcessor glyphLayoutProcessor, PDDocument pdDocument,
+ String fontPath) {
+ InputStream fontStream = this.getClass().
+ getResourceAsStream(fontPath);
+ PDType0Font font = glyphLayoutProcessor.loadFont(pdDocument, fontStream);
+ return font;
+ }
+}
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.odt b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.odt
new file mode 100644
index 00000000000..f61b63c03e1
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.odt differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.pdf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.pdf
new file mode 100644
index 00000000000..21c5c488824
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/PdfForm.pdf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf
new file mode 100644
index 00000000000..c14e0a41892
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/Arimo-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt
new file mode 100644
index 00000000000..d6456956733
--- /dev/null
+++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/arimo/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/DejaVuSans.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/DejaVuSans.ttf
new file mode 100644
index 00000000000..e5f7eecce43
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/DejaVuSans.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/LICENSE b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/LICENSE
new file mode 100644
index 00000000000..df52c1709be
--- /dev/null
+++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/dejavu/LICENSE
@@ -0,0 +1,187 @@
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
+
+
+Bitstream Vera Fonts Copyright
+------------------------------
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
+a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute the
+Font Software, including without limitation the rights to use, copy, merge,
+publish, distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright and trademark notices and this permission notice shall
+be included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional glyphs or characters may be added to the Fonts, only if the fonts
+are renamed to names not containing either the words "Bitstream" or the word
+"Vera".
+
+This License becomes null and void to the extent applicable to Fonts or Font
+Software that has been modified and is distributed under the "Bitstream
+Vera" names.
+
+The Font Software may be sold as part of a larger software package but no
+copy of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
+ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
+FONT SOFTWARE.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font Software
+without prior written authorization from the Gnome Foundation or Bitstream
+Inc., respectively. For further information, contact: fonts at gnome dot
+org.
+
+Arev Fonts Copyright
+------------------------------
+
+Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and
+associated documentation files (the "Font Software"), to reproduce
+and distribute the modifications to the Bitstream Vera Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute, and/or sell copies of the Font Software, and to permit
+persons to whom the Font Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Tavmjong Bah" or the word "Arev".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Tavmjong Bah Arev" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
+TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
+
+Except as contained in this notice, the name of Tavmjong Bah shall not
+be used in advertising or otherwise to promote the sale, use or other
+dealings in this Font Software without prior written authorization
+from Tavmjong Bah. For further information, contact: tavmjong @ free
+. fr.
+
+TeX Gyre DJV Math
+-----------------
+Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
+
+Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
+(on behalf of TeX users groups) are in public domain.
+
+Letters imported from Euler Fraktur from AMSfonts are (c) American
+Mathematical Society (see below).
+Bitstream Vera Fonts Copyright
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
+is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of the fonts accompanying this license (“Fonts”) and associated
+documentation
+files (the “Font Software”), to reproduce and distribute the Font Software,
+including without limitation the rights to use, copy, merge, publish,
+distribute,
+and/or sell copies of the Font Software, and to permit persons to whom
+the Font Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be
+included in all copies of one or more of the Font Software typefaces.
+
+The Font Software may be modified, altered, or added to, and in particular
+the designs of glyphs or characters in the Fonts may be modified and
+additional
+glyphs or characters may be added to the Fonts, only if the fonts are
+renamed
+to names not containing either the words “Bitstream” or the word “Vera”.
+
+This License becomes null and void to the extent applicable to Fonts or
+Font Software
+that has been modified and is distributed under the “Bitstream Vera”
+names.
+
+The Font Software may be sold as part of a larger software package but
+no copy
+of one or more of the Font Software typefaces may be sold by itself.
+
+THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
+TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
+FOUNDATION
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
+SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
+ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
+INABILITY TO USE
+THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
+Except as contained in this notice, the names of GNOME, the GNOME
+Foundation,
+and Bitstream Inc., shall not be used in advertising or otherwise to promote
+the sale, use or other dealings in this Font Software without prior written
+authorization from the GNOME Foundation or Bitstream Inc., respectively.
+For further information, contact: fonts at gnome dot org.
+
+AMSFonts (v. 2.2) copyright
+
+The PostScript Type 1 implementation of the AMSFonts produced by and
+previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
+available for general use. This has been accomplished through the
+cooperation
+of a consortium of scientific publishers with Blue Sky Research and Y&Y.
+Members of this consortium include:
+
+Elsevier Science IBM Corporation Society for Industrial and Applied
+Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
+
+In order to assure the authenticity of these fonts, copyright will be
+held by
+the American Mathematical Society. This is not meant to restrict in any way
+the legitimate use of the fonts, such as (but not limited to) electronic
+distribution of documents containing these fonts, inclusion of these fonts
+into other public domain or commercial font collections or computer
+applications, use of the outline data to create derivative fonts and/or
+faces, etc. However, the AMS does require that the AMS copyright notice be
+removed from any derivative versions of the fonts which have been altered in
+any way. In addition, to ensure the fidelity of TeX documents using Computer
+Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
+has requested that any alterations which yield different font metrics be
+given a different name.
+
+$Id$
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/FiraCode-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/FiraCode-Regular.ttf
new file mode 100644
index 00000000000..bd736851948
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/FiraCode-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/LICENSE.txt b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/LICENSE.txt
new file mode 100644
index 00000000000..805e0b38b5c
--- /dev/null
+++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/firacode/LICENSE.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/LICENSE b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/LICENSE
new file mode 100644
index 00000000000..573283cc13c
--- /dev/null
+++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/LICENSE
@@ -0,0 +1,94 @@
+Copyright 2018 The Noto Project Authors (github.com/googlei18n/noto-fonts)
+
+This Font Software is licensed under the SIL Open Font License,
+Version 1.1.
+
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font
+creation efforts of academic and linguistic communities, and to
+provide a free and open framework in which fonts may be shared and
+improved in partnership with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply to
+any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software
+components as distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to,
+deleting, or substituting -- in part or in whole -- any of the
+components of the Original Version, by changing formats or by porting
+the Font Software to a new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed,
+modify, redistribute, and sell modified and unmodified copies of the
+Font Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components, in
+Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the
+corresponding Copyright Holder. This restriction only applies to the
+primary font name as presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created using
+the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSans-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSans-Regular.ttf
new file mode 100644
index 00000000000..31e5409a7ca
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSans-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansArabic-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansArabic-Regular.ttf
new file mode 100644
index 00000000000..79359c460b1
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansArabic-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMath-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMath-Regular.ttf
new file mode 100644
index 00000000000..162fb91275a
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMath-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMono-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMono-Regular.ttf
new file mode 100644
index 00000000000..541efd808df
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSansMono-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSerif-Regular.ttf b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSerif-Regular.ttf
new file mode 100644
index 00000000000..8ef59c11cbd
Binary files /dev/null and b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/NotoSerif-Regular.ttf differ
diff --git a/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/README b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/README
new file mode 100644
index 00000000000..a74beaa2e23
--- /dev/null
+++ b/examples/src/main/resources/org/apache/pdfbox/examples/glyphlayout/noto/README
@@ -0,0 +1,5 @@
+
+The fonts Noto*.ttf are licensed under the SIL Open Font License
+see LICENSE.
+The newest version is available under
+https://github.com/notofonts/latin-greek-cyrillic
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutFontLoader.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutFontLoader.java
new file mode 100644
index 00000000000..584ae54eb33
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutFontLoader.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.pdmodel;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.awt.Font;
+import java.awt.font.TextAttribute;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Loads the PDType0Font and awt.Font for GlyphLayoutProcessor
+ *
+ * Use an object of this class only in one thread.
+ *
+ * @author Volker Kunert
+ */
+public class GlyphLayoutFontLoader {
+
+ private final Map awtFontMap = new HashMap<>();
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @return pdType0Font PDFBox font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, boolean embedSubset) {
+ return loadFont(pdDocument, inputStream, embedSubset, null);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @param fontOptions Options for font
+ * @return pdType0Font PDFBox font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, boolean embedSubset, FontOptions fontOptions) {
+ PDType0Font pdType0Font = null;
+
+ try {
+ // Copy font stream into memory to read it twice
+ // for creation of PDType0Font and aww.Font
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[2048];
+ int bytes_read = -1;
+ while ((bytes_read = inputStream.read(buffer)) > 0) {
+ baos.write(buffer, 0, bytes_read);
+ }
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+
+ pdType0Font = PDType0Font.load(pdDocument, bais, embedSubset);
+ bais.reset();
+ loadAwtFont(pdType0Font, bais, fontOptions);
+
+ } catch (Exception e) {
+ throw new RuntimeException("Font creation failed.", e);
+ }
+ return pdType0Font;
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @return pdType0Font PDFBox font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream) {
+ return loadFont(pdDocument, inputStream, true, null);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @param fontOptions options for font
+ * @return pdType0Font PDFBox font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, FontOptions fontOptions) {
+ return loadFont(pdDocument, inputStream, true, fontOptions);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdType0Font PDFBox font
+ * @param inputStream of the font file
+ * @throws RuntimeException if font can not be loaded
+ */
+ public void loadAwtFont(PDType0Font pdType0Font, InputStream inputStream) {
+ loadAwtFont(pdType0Font, inputStream, null);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdType0Font PDFBox font
+ * @param inputStream of the font file
+ * @throws RuntimeException if font can not be loaded
+ */
+ public void loadAwtFont(PDType0Font pdType0Font, InputStream inputStream, FontOptions fontOptions) {
+ Font awtFont = null;
+ if (fontOptions == null) {
+ fontOptions = new FontOptions();
+ }
+ try {
+ if (!awtFontMap.containsKey(pdType0Font)) {
+ awtFont = Font.createFont(java.awt.Font.TRUETYPE_FONT, inputStream)
+ .deriveFont(fontOptions.getTextAttributes());
+ if (awtFont == null) {
+ throw new RuntimeException("Font is null");
+ }
+ awtFontMap.put(pdType0Font, awtFont);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("AWT Font creation failed for %s.", pdType0Font.getName()), e);
+ } finally {
+ try {
+ inputStream.close();
+ } catch (Exception e) {
+ //ignore
+ }
+ }
+ }
+
+ /**
+ * Determines if glyph layout is supported for this font
+ *
+ * @param font PDFBox font
+ * @return true if glyph layout is supported for this font
+ */
+ public boolean supportsFont(PDFont font) {
+ boolean supports = font instanceof PDType0Font &&
+ awtFontMap.containsKey((PDType0Font) font);
+ return supports;
+ }
+
+ /**
+ * Gets the corresponding AWT-font for the given PDFBox-font
+ *
+ * @param font PDFBox font
+ * @return AWT font if available
+ */
+ public Font getAwtFont(PDType0Font font) {
+ return awtFontMap.get(font);
+ }
+
+ /**
+ * Specify Options for an AWT font
+ */
+ public static class FontOptions {
+
+ private final Map textAttributes = new HashMap<>();
+
+ public Map getTextAttributes() {
+ // always return an unmodifiableMap, so that internal state can not be changed
+ // by changing the returned map
+ return Collections.unmodifiableMap(textAttributes);
+ }
+
+ public FontOptions setKerningOn() {
+ textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
+ return this;
+ }
+
+ public FontOptions setLigaturesOn() {
+ textAttributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON);
+ return this;
+ }
+ }
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutProcessor.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutProcessor.java
new file mode 100644
index 00000000000..0a9e0d5453d
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphLayoutProcessor.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.pdmodel;
+
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType0Font;
+
+import java.awt.Font;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.AttributedString;
+import java.text.Bidi;
+
+/**
+ * Processor for glyph layout
+ *
+ * Use an object of this class only in one thread.
+ *
+ * @author Volker Kunert
+ */
+public class GlyphLayoutProcessor {
+ private final GlyphLayoutFontLoader glyphLayoutFontLoader;
+ private PDType0Font font;
+ private float fontSize;
+ private PDAbstractContentStream contentStream;
+
+ /**
+ * Constructs a GlyphLayoutProcessor
+ *
+ */
+ public GlyphLayoutProcessor() {
+ this.glyphLayoutFontLoader = new GlyphLayoutFontLoader();
+ }
+
+ /**
+ * Checks if the glyphVector contains adjustments
+ * that make advanced layout necessary
+ *
+ * @param glyphVector glyph vector containing the positions
+ * @return true if the glyphVector contains adjustments
+ */
+ private static boolean hasAdjustments(GlyphVector glyphVector) {
+ boolean retVal = false;
+ float lastX = 0f;
+ float lastY = 0f;
+
+ for (int i = 0; i < glyphVector.getNumGlyphs(); i++) {
+ Point2D p = glyphVector.getGlyphPosition(i);
+ float dx = (float) p.getX() - lastX;
+ float dy = (float) p.getY() - lastY;
+
+ float ax = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceX();
+ float ay = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceY();
+
+ if (dx != ax || dy != ay) {
+ retVal = true;
+ break;
+ }
+ lastX = (float) p.getX();
+ lastY = (float) p.getY();
+ }
+ return retVal;
+ }
+
+ private static void checkMissingGlyphs(String text, Font awtFont) {
+ int firstMissingCharacter = awtFont.canDisplayUpTo(text);
+ if (firstMissingCharacter != -1) {
+ char c = text.charAt(firstMissingCharacter);
+ int codepoint = text.codePointAt(firstMissingCharacter);
+
+ throw new IllegalArgumentException(
+ String.format("Missing glyph in font '%s' for the character '%c', codePoint: %d (U+%04x).",
+ awtFont.getName(), c, codepoint, codepoint));
+ }
+ }
+
+ /**
+ * Sets the content stream
+ *
+ * @param contentStream content stream
+ */
+ public void setContentStream(PDAbstractContentStream contentStream) {
+ this.contentStream = contentStream;
+ }
+
+ /**
+ *
+ * @param font to be checked
+ * @return true if font is supported
+ */
+ public boolean supportsFont(PDFont font) {
+ return glyphLayoutFontLoader.supportsFont(font);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @param embedSubset must be false for PDF forms
+ * @param fontOptions options for font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, boolean embedSubset,
+ GlyphLayoutFontLoader.FontOptions fontOptions) {
+ return glyphLayoutFontLoader.loadFont(pdDocument, inputStream, embedSubset, fontOptions);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @param embedSubset must be false for PDF forms
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream, boolean embedSubset) {
+ return glyphLayoutFontLoader.loadFont(pdDocument, inputStream, embedSubset);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream,
+ GlyphLayoutFontLoader.FontOptions fontOptions) {
+ return glyphLayoutFontLoader.loadFont(pdDocument, inputStream, true, fontOptions);
+ }
+
+ /**
+ * Loads the AWT font needed for layout
+ *
+ * @param pdDocument document
+ * @param inputStream of the font
+ * @throws RuntimeException if font can not be loaded
+ */
+ public PDType0Font loadFont(PDDocument pdDocument, InputStream inputStream) {
+ return glyphLayoutFontLoader.loadFont(pdDocument, inputStream, true);
+ }
+
+ /**
+ * Computes glyph positioning
+ *
+ * @param text input text
+ * @return glyph vector containing reordered text, width and positioning info
+ */
+ private GlyphVector computeGlyphVector(String text) {
+ char[] chars = text.toCharArray();
+
+ FontRenderContext fontRenderContext = new FontRenderContext(new AffineTransform(), false, true);
+ // use fractional metrics
+ AttributedString as = new AttributedString(text);
+ Bidi bidi = new Bidi(as.getIterator());
+ int localFlags = bidi.isLeftToRight() ? java.awt.Font.LAYOUT_LEFT_TO_RIGHT : java.awt.Font.LAYOUT_RIGHT_TO_LEFT;
+
+ java.awt.Font awtFont = glyphLayoutFontLoader.getAwtFont(font).deriveFont(fontSize);
+
+ checkMissingGlyphs(text, awtFont);
+
+ GlyphVector glyphVector = awtFont.layoutGlyphVector(fontRenderContext, chars, 0, chars.length, localFlags);
+ return glyphVector;
+ }
+
+ /**
+ * Sets the font and fontSize
+ *
+ * @param font to be set
+ * @param fontSize font size
+ */
+ void setFontAndSize(PDType0Font font, float fontSize) {
+ this.font = font;
+ this.fontSize = fontSize;
+ }
+
+
+ /**
+ * Shows a text using glyph positioning (if needed)
+ *
+ * @param text text to show
+ * @throws IOException
+ */
+ void showText(String text) throws IOException {
+
+ if (ifMixedThenDivideTextAndShow(text)) {
+ return;
+ }
+
+ GlyphVector glyphVector = computeGlyphVector(text);
+
+ if (!hasAdjustments(glyphVector)) {
+ contentStream.showTextPDType0Font(glyphVector, 0, glyphVector.getNumGlyphs());
+ return;
+ }
+
+ final float delta = 1e-5f;
+ final float factorX = 1000f / fontSize;
+ float lastX = 0f;
+
+ GlyphsAndPositions ga = new GlyphsAndPositions();
+
+ for (int i = 0; i < glyphVector.getNumGlyphs(); i++) {
+ Point2D p = glyphVector.getGlyphPosition(i);
+ float ax = (i == 0) ? 0.0f : glyphVector.getGlyphMetrics(i - 1).getAdvanceX();
+ float dx = (float) p.getX() - lastX - ax;
+ float py = (float) p.getY();
+
+ if (Math.abs(py) >= delta) {
+ if (!ga.isEmpty()) {
+ contentStream.showGlyphsWithPositioning(ga);
+ ga.clear();
+ }
+ contentStream.setTextRise(-py);
+ }
+ if (Math.abs(dx) >= delta) {
+ ga.add(-dx * factorX);
+ }
+ ga.add(glyphVector.getGlyphCode(i));
+ if (Math.abs(py) >= delta) {
+ contentStream.showGlyphsWithPositioning(ga);
+ ga.clear();
+ contentStream.setTextRise(0.0f);
+ }
+ lastX = (float) p.getX();
+ }
+ // adjust the end position
+ Point2D p = glyphVector.getGlyphPosition(glyphVector.getNumGlyphs());
+ float ax = (glyphVector.getNumGlyphs() == 0) ? 0.0f
+ : glyphVector.getGlyphMetrics(glyphVector.getNumGlyphs() - 1).getAdvanceX();
+ float dx = (float) p.getX() - lastX - ax;
+ if (Math.abs(dx) >= delta) {
+ ga.add(-dx * factorX);
+ }
+ contentStream.showGlyphsWithPositioning(ga);
+ ga.clear();
+ }
+
+ /**
+ * Check if bidi is mixed, then call showText for each part
+ *
+ * @param text text
+ * @return true, if bidi is mixed, else false
+ */
+ protected boolean ifMixedThenDivideTextAndShow(String text) throws IOException {
+ if (Bidi.requiresBidi(text.toCharArray(), 0, text.length())) {
+ Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
+ if (bidi.isMixed()) {
+ //See Unicode Bidirectional Algorithm.
+ // Split the text if isMixed
+ for (int i = 0; i < bidi.getRunCount(); i++) {
+ int run = bidi.baseIsLeftToRight() ? i : bidi.getRunCount() - 1 - i;
+ int start = bidi.getRunStart(run);
+ int limit = bidi.getRunLimit(run);
+ String part = text.substring(start, limit);
+ showText(part);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphsAndPositions.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphsAndPositions.java
new file mode 100644
index 00000000000..2a5a4eefc9e
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/GlyphsAndPositions.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.pdfbox.pdmodel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Stores sublists of glyphs and positions in a list
+ */
+public class GlyphsAndPositions {
+ private final ArrayList