Author: tilman
Date: Sat Sep  5 16:27:11 2026
New Revision: 1937910

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/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java

Modified: 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
==============================================================================
--- 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
 Sat Sep  5 16:27:07 2026        (r1937909)
+++ 
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
 Sat Sep  5 16:27:11 2026        (r1937910)
@@ -40,6 +40,7 @@ import org.apache.pdfbox.pdmodel.PDDocum
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.common.PDStream;
 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;
@@ -572,6 +573,20 @@ 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 (gid == 0 && cmapWinUnicode != null)
@@ -597,7 +612,7 @@ public class PDTrueTypeFont extends PDSi
             }
 
             // (3, 0) - (Windows, Symbol)
-            if (cmapWinSymbol != null)
+            if (gid == 0 && cmapWinSymbol != null)
             {
                 gid = cmapWinSymbol.getGlyphId(code);
                 if (code >= 0 && code <= 0xFF)
@@ -671,6 +686,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