You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2023/07/06 06:31:37 UTC

svn commit: r1910805 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font: PDCIDFontType2.java PDTrueTypeFont.java

Author: lehmi
Date: Thu Jul  6 06:31:36 2023
New Revision: 1910805

URL: http://svn.apache.org/viewvc?rev=1910805&view=rev
Log:
PDFBOX-5634: allow OpenType fonts using CFF-bases outlines for rendering and don't replace them

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java
    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/PDCIDFontType2.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java?rev=1910805&r1=1910804&r2=1910805&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2.java Thu Jul  6 06:31:36 2023
@@ -19,20 +19,24 @@ package org.apache.pdfbox.pdmodel.font;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.GeneralPath;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.cff.CFFFont;
 import org.apache.fontbox.cff.Type2CharString;
 import org.apache.fontbox.cmap.CMap;
 import org.apache.fontbox.ttf.CmapLookup;
 import org.apache.fontbox.ttf.GlyphData;
 import org.apache.fontbox.ttf.OTFParser;
 import org.apache.fontbox.ttf.OpenTypeFont;
+import org.apache.fontbox.ttf.TTFParser;
 import org.apache.fontbox.ttf.TrueTypeFont;
 import org.apache.fontbox.util.BoundingBox;
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.io.RandomAccessRead;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.common.PDStream;
 import org.apache.pdfbox.util.Matrix;
@@ -47,6 +51,7 @@ public class PDCIDFontType2 extends PDCI
     private static final Log LOG = LogFactory.getLog(PDCIDFontType2.class);
 
     private final TrueTypeFont ttf;
+    private final OpenTypeFont otf;
     private final int[] cid2gid;
     private final boolean isEmbedded;
     private final boolean isDamaged;
@@ -83,6 +88,9 @@ public class PDCIDFontType2 extends PDCI
         if (trueTypeFont != null)
         {
             ttf = trueTypeFont;
+            otf = trueTypeFont instanceof OpenTypeFont
+                    && ((OpenTypeFont) trueTypeFont).isSupportedOTF() ? (OpenTypeFont) trueTypeFont
+                            : null;
             isEmbedded = true;
             isDamaged = false;
         }
@@ -110,16 +118,10 @@ public class PDCIDFontType2 extends PDCI
                 try
                 {
                     // embedded OTF or TTF
-                    OTFParser otfParser = new OTFParser(true);
-                    OpenTypeFont otf = otfParser.parse(stream.getCOSObject().createView());
-                    ttfFont = otf;
-    
-                    if (otf.isPostScript())
-                    {
-                        // PDFBOX-3344 contains PostScript outlines instead of TrueType
-                        fontIsDamaged = true;
-                        LOG.warn("Found CFF/OTF but expected embedded TTF font " + fd.getFontName());
-                    }
+                    RandomAccessRead view = stream.getCOSObject().createView();
+                    TTFParser ttfParser = getParser(view, true);
+                    ttfFont = ttfParser.parse(view);
+                    ttfFont.close();
                 }
                 catch (IOException e)
                 {
@@ -134,6 +136,8 @@ public class PDCIDFontType2 extends PDCI
             {
                 ttfFont = findFontOrSubstitute();
             }
+            otf = ttfFont instanceof OpenTypeFont && ((OpenTypeFont) ttfFont).isSupportedOTF()
+                    ? (OpenTypeFont) ttfFont : null;
             ttf = ttfFont;
         }
         cmap = ttf.getUnicodeCmapLookup(false);
@@ -426,39 +430,38 @@ public class PDCIDFontType2 extends PDCI
     @Override
     public GeneralPath getPath(int code) throws IOException
     {
-        if (ttf instanceof OpenTypeFont && ((OpenTypeFont)ttf).isPostScript())
+        if (otf != null && otf.isPostScript())
         {
-            // we're not supposed to have CFF fonts inside PDCIDFontType2, but if we do,
-            // then we treat their CIDs as GIDs, see PDFBOX-3344
-            int cid = codeToGID(code);
-            Type2CharString charstring = ((OpenTypeFont)ttf).getCFF().getFont().getType2CharString(cid);
-            return charstring.getPath();
+            GeneralPath path = getPathFromOutlines(code);
+            return path == null ? new GeneralPath() : path;
         }
-        else
+        int gid = codeToGID(code);
+        GlyphData glyph = ttf.getGlyph().getGlyph(gid);
+        if (glyph != null)
         {
-            int gid = codeToGID(code);
-            GlyphData glyph = ttf.getGlyph().getGlyph(gid);
-            if (glyph != null)
-            {
-                return glyph.getPath();
-            }
-            return new GeneralPath();
+            return glyph.getPath();
         }
+        return new GeneralPath();
     }
 
     @Override
     public GeneralPath getNormalizedPath(int code) throws IOException
     {
-        int gid = codeToGID(code);
-
-        GeneralPath path = getPath(code);
-
-        // Acrobat only draws GID 0 for embedded CIDFonts, see PDFBOX-2372
-        if (gid == 0 && !isEmbedded())
+        GeneralPath path = null;
+        if (otf != null && otf.isPostScript())
         {
-            path = null;
+            path = getPathFromOutlines(code);
+        }
+        else
+        {
+            int gid = codeToGID(code);
+            path = getPath(code);
+            // Acrobat only draws GID 0 for embedded CIDFonts, see PDFBOX-2372
+            if (gid == 0 && !isEmbedded())
+            {
+                path = null;
+            }
         }
-
         if (path == null)
         {
             // empty glyph (e.g. space, newline)
@@ -477,9 +480,42 @@ public class PDCIDFontType2 extends PDCI
         return path;
     }
 
+    private GeneralPath getPathFromOutlines(int code) throws IOException
+    {
+        CFFFont cffFont = otf.getCFF().getFont();
+        int gid = codeToGID(code);
+        Type2CharString type2CharString = cffFont.getType2CharString(gid);
+        return type2CharString != null ? type2CharString.getPath() : null;
+    }
+
     @Override
     public boolean hasGlyph(int code) throws IOException
     {
         return codeToGID(code) != 0;
     }
+
+    private TTFParser getParser(RandomAccessRead randomAccessRead, boolean isEmbedded)
+            throws IOException
+    {
+        long startPos = randomAccessRead.getPosition();
+        byte[] tagBytes = new byte[4];
+        int remainingBytes = tagBytes.length;
+        int amountRead;
+        while ((amountRead = randomAccessRead.read(tagBytes, tagBytes.length - remainingBytes,
+                remainingBytes)) > 0)
+        {
+            remainingBytes -= amountRead;
+        }
+        randomAccessRead.seek(startPos);
+        if ("OTTO".equals(new String(tagBytes, StandardCharsets.US_ASCII)))
+        {
+            return new OTFParser(isEmbedded);
+        }
+        else
+        {
+            return new TTFParser(isEmbedded);
+        }
+    }
+
 }
+

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java?rev=1910805&r1=1910804&r2=1910805&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDTrueTypeFont.java Thu Jul  6 06:31:36 2023
@@ -21,15 +21,20 @@ import java.awt.geom.GeneralPath;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.fontbox.FontBoxFont;
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.Type2CharString;
 import org.apache.fontbox.ttf.CmapSubtable;
 import org.apache.fontbox.ttf.CmapTable;
 import org.apache.fontbox.ttf.GlyphData;
 import org.apache.fontbox.ttf.GlyphTable;
+import org.apache.fontbox.ttf.OTFParser;
+import org.apache.fontbox.ttf.OpenTypeFont;
 import org.apache.fontbox.ttf.PostScriptTable;
 import org.apache.fontbox.ttf.TTFParser;
 import org.apache.fontbox.ttf.TrueTypeFont;
@@ -81,6 +86,7 @@ public class PDTrueTypeFont extends PDSi
     }
 
     private final TrueTypeFont ttf;
+    private final OpenTypeFont otf;
     private final boolean isEmbedded;
     private final boolean isDamaged;
     private CmapSubtable cmapWinUnicode = null;
@@ -112,9 +118,9 @@ public class PDTrueTypeFont extends PDSi
                 RandomAccessRead view = null;
                 try
                 {
-                    // embedded
-                    TTFParser ttfParser = new TTFParser(true);
                     view = ff2Stream.getCOSObject().createView();
+                    // embedded
+                    TTFParser ttfParser = getParser(view, true);
                     ttfFont = ttfParser.parse(view);
                     ttfFont.close();
                 }
@@ -142,6 +148,8 @@ public class PDTrueTypeFont extends PDSi
                 LOG.warn("Using fallback font " + ttfFont + " for " + getBaseFont());
             }
         }
+        otf = ttfFont instanceof OpenTypeFont && ((OpenTypeFont) ttfFont).isSupportedOTF()
+                ? (OpenTypeFont) ttfFont : null;
         ttf = ttfFont;
         readEncoding();
     }
@@ -156,6 +164,8 @@ public class PDTrueTypeFont extends PDSi
                                                                      encoding);
         this.encoding = encoding;
         this.ttf = ttf;
+        // OpenTypeFonts are not fully supported yet
+        otf = null;
         setFontDescriptor(embedder.getFontDescriptor());
         isEmbedded = true;
         isDamaged = false;
@@ -470,6 +480,11 @@ public class PDTrueTypeFont extends PDSi
     @Override
     public GeneralPath getPath(int code) throws IOException
     {
+        if (otf != null && otf.isPostScript())
+        {
+            GeneralPath path = getPathFromOutlines(code);
+            return path == null ? new GeneralPath() : path;
+        }
         int gid = codeToGID(code);
         GlyphTable glyphTable = ttf.getGlyph();
         if (glyphTable == null)
@@ -532,22 +547,25 @@ public class PDTrueTypeFont extends PDSi
     @Override
     public GeneralPath getNormalizedPath(int code) throws IOException
     {
-        int gid = codeToGID(code);
-
-        GeneralPath path = getPath(code);
-
-        // Acrobat only draws GID 0 for embedded or "Standard 14" fonts, see PDFBOX-2372
-        if (gid == 0 && !isEmbedded() && !isStandard14())
+        GeneralPath path = null;
+        if (otf != null && otf.isPostScript())
         {
-            path = null;
+            path = getPathFromOutlines(code);
+        }
+        else
+        {
+            int gid = codeToGID(code);
+            path = getPath(code);
+            // Acrobat only draws GID 0 for embedded or "Standard 14" fonts, see PDFBOX-2372
+            if (gid == 0 && !isEmbedded() && !isStandard14())
+            {
+                path = null;
+            }
         }
-
         if (path == null)
         {
-            // empty glyph (e.g. space, newline)
             return new GeneralPath();
         }
-
         if (ttf.getUnitsPerEm() != 1000)
         {
             float scale = 1000f / ttf.getUnitsPerEm();
@@ -557,6 +575,16 @@ public class PDTrueTypeFont extends PDSi
         return path;
     }
 
+    private GeneralPath getPathFromOutlines(int code) throws IOException
+    {
+        CFFFont cffFont = otf.getCFF().getFont();
+        String name = getEncoding().getName(code);
+        int sid = cffFont.getCharset().getSID(name);
+        int gid = cffFont.getCharset().getGIDForSID(sid);
+        Type2CharString type2CharString = cffFont.getType2CharString(gid);
+        return type2CharString != null ? type2CharString.getPath() : null;
+    }
+
     @Override
     public boolean hasGlyph(String name) throws IOException
     {
@@ -737,4 +765,28 @@ public class PDTrueTypeFont extends PDSi
         }
         cmapInitialized = true;
     }
+
+    private TTFParser getParser(RandomAccessRead randomAccessRead, boolean isEmbedded)
+            throws IOException
+    {
+        long startPos = randomAccessRead.getPosition();
+        byte[] tagBytes = new byte[4];
+        int remainingBytes = tagBytes.length;
+        int amountRead;
+        while ((amountRead = randomAccessRead.read(tagBytes, tagBytes.length - remainingBytes,
+                remainingBytes)) > 0)
+        {
+            remainingBytes -= amountRead;
+        }
+        randomAccessRead.seek(startPos);
+        if ("OTTO".equals(new String(tagBytes, StandardCharsets.US_ASCII)))
+        {
+            return new OTFParser(isEmbedded);
+        }
+        else
+        {
+            return new TTFParser(isEmbedded);
+        }
+    }
+
 }