Author: tilman
Date: Sat Sep  5 16:27:15 2026
New Revision: 1937911

Log:
PDFBOX-5960: when the descriptor has contradictory flags and the Encoding 
dictionary has a recognized /BaseEncoding, resolve by glyph name first, falling 
back to the existing code-based cmap logic only if that yields GID 0, by Valery 
Bokov; closes #523

Modified:
   
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
        Sat Sep  5 16:27:11 2026        (r1937910)
+++ 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
        Sat Sep  5 16:27:15 2026        (r1937911)
@@ -51,6 +51,7 @@ import org.apache.pdfbox.pdmodel.common.
 import org.apache.pdfbox.pdmodel.common.PDStream;
 import org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName;
 import org.apache.pdfbox.pdmodel.font.encoding.BuiltInEncoding;
+import org.apache.pdfbox.pdmodel.font.encoding.DictionaryEncoding;
 import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
 import org.apache.pdfbox.pdmodel.font.encoding.GlyphList;
 import org.apache.pdfbox.pdmodel.font.encoding.MacOSRomanEncoding;
@@ -626,9 +627,23 @@ public class PDTrueTypeFont extends PDSi
         }
         else // symbolic
         {
+            // PDFBOX-5960: some fonts have both the Symbolic and NonSymbolic 
flags set in their
+            // FontDescriptor, which is self-contradictory. When such a font 
also has an Encoding
+            // dictionary with a recognised /BaseEncoding, resolve the glyph 
by name first (as if
+            // the font were non-symbolic), and fall back to the code-based 
cmap lookup below only
+            // if that fails.
+            if (hasContradictorySymbolicFlags() && encoding instanceof 
DictionaryEncoding
+                    && isRecognizedBaseEncoding(((DictionaryEncoding) 
encoding).getBaseEncoding()))
+            {
+                String name = encoding.getName(code);
+                if (name != null && !".notdef".equals(name))
+                {
+                    gid = codeToGIDByName(name);
+                }
+            }
             // PDFBOX-4755 / PDF.js #5501
             // PDFBOX-3965: fallback for font has that the symbol flag but 
isn't
-            if (cmapWinUnicode != null)
+            if (gid == 0 && cmapWinUnicode != null)
             {
                 if (encoding instanceof WinAnsiEncoding || encoding instanceof 
MacRomanEncoding)
                 {
@@ -726,6 +741,29 @@ public class PDTrueTypeFont extends PDSi
     }
 
     /**
+     * PDFBOX-5960: some malformed fonts have both the Symbolic and 
NonSymbolic flags set in
+     * their FontDescriptor, which is self-contradictory and means the 
Symbolic flag can't be
+     * trusted on its own.
+     *
+     * @return true if the font descriptor has both the Symbolic and 
NonSymbolic flags set
+     */
+    private boolean hasContradictorySymbolicFlags()
+    {
+        PDFontDescriptor fd = getFontDescriptor();
+        return fd != null && fd.isSymbolic() && fd.isNonSymbolic();
+    }
+
+    /**
+     * @return true if the given encoding is one of the standard named 
encodings, i.e. it came
+     * from a recognized /BaseEncoding entry rather than being synthesized as 
a fallback.
+     */
+    private static boolean isRecognizedBaseEncoding(Encoding encoding)
+    {
+        return encoding instanceof StandardEncoding || encoding instanceof 
WinAnsiEncoding
+                || encoding instanceof MacRomanEncoding;
+    }
+
+    /**
      * extract all useful "cmap" subtables.
      */
     private void extractCmapTable() throws IOException

Reply via email to