You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2018/02/15 20:43:42 UTC

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

Author: tilman
Date: Thu Feb 15 20:43:42 2018
New Revision: 1824363

URL: http://svn.apache.org/viewvc?rev=1824363&view=rev
Log:
PDFBOX-4106: Use 'vhea' and  'vmtx' tables to fix vertical displacements, by Aaron Madlon-Kay
PDFBOX-4093: make method that is called from constructor final

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

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java?rev=1824363&r1=1824362&r2=1824363&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFont.java Thu Feb 15 20:43:42 2018
@@ -49,9 +49,9 @@ public abstract class PDCIDFont implemen
     private float defaultWidth;
     private float averageWidth;
 
-    private final Map<Integer, Float> verticalDisplacementY = new HashMap<>(); // w1y
-    private final Map<Integer, Vector> positionVectors = new HashMap<>();     // v
-    private float[] dw2 = new float[] { 880, -1000 };
+    final Map<Integer, Float> verticalDisplacementY = new HashMap<>(); // w1y
+    final Map<Integer, Vector> positionVectors = new HashMap<>();     // v
+    float[] dw2 = new float[] { 880, -1000 };
 
     protected final COSDictionary dict;
     private PDFontDescriptor fontDescriptor;
@@ -359,6 +359,7 @@ public abstract class PDCIDFont implemen
      *
      * @param code character code
      * @return GID
+     * @throws java.io.IOException
      */
     public abstract int codeToGID(int code) throws IOException;
 

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=1824363&r1=1824362&r2=1824363&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 Feb 15 20:43:42 2018
@@ -19,20 +19,32 @@ package org.apache.pdfbox.pdmodel.font;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.GeneralPath;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 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.HorizontalMetricsTable;
 import org.apache.fontbox.ttf.OTFParser;
 import org.apache.fontbox.ttf.OpenTypeFont;
 import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.fontbox.ttf.VerticalHeaderTable;
+import org.apache.fontbox.ttf.VerticalMetricsTable;
 import org.apache.fontbox.util.BoundingBox;
+import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
 import org.apache.pdfbox.pdmodel.common.PDStream;
 import org.apache.pdfbox.util.Matrix;
+import org.apache.pdfbox.util.Vector;
 
 /**
  * Type 2 CIDFont (TrueType).
@@ -141,6 +153,85 @@ public class PDCIDFontType2 extends PDCI
         }
         cmap = ttf.getUnicodeCmapLookup(false);
         cid2gid = readCIDToGIDMap();
+        fixVerticalDisplacements();
+    }
+
+    private void fixVerticalDisplacements() throws IOException
+    {
+        if (!parent.dict.getCOSName(COSName.ENCODING).equals(COSName.IDENTITY_V))
+        {
+            return;
+        }
+        // The "vhea" and "vmtx" tables that specify vertical metrics shall never be used by a conforming
+        // reader. The only way to specify vertical metrics in PDF shall be by means of the DW2 and W2
+        // entries in a CIDFont dictionary.
+        VerticalHeaderTable vhea = ttf.getVerticalHeader();
+        if (vhea == null)
+        {
+            if (dict.getItem(COSName.DW2) == null)
+            {
+                LOG.warn("Identity-V font seems to be missing vertical metrics: it has no 'vhea' table and no DW2 entry");
+            }
+            return;
+        }
+        // Clear any data previously set from DW2 / W2
+        verticalDisplacementY.clear();
+        positionVectors.clear();
+
+        float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
+
+        dw2[0] = vhea.getAscender() * scaling;
+        dw2[1] = -vhea.getAdvanceHeightMax() * scaling;
+        Map<Integer, Integer> gid2cid = invert(cid2gid);
+        HorizontalMetricsTable hmtx = ttf.getHorizontalMetrics();
+        VerticalMetricsTable vmtx = ttf.getVerticalMetrics();
+        for (int gid = 0; gid < ttf.getNumberOfGlyphs(); gid++)
+        {
+            float advance = -vmtx.getAdvanceHeight(gid) * scaling;
+            if (advance != dw2[1])
+            {
+                int cid = gid2cid == null ? gid : gid2cid.get(gid);
+                verticalDisplacementY.put(cid, advance);
+                positionVectors.put(cid, new Vector(hmtx.getAdvanceWidth(gid) / 2f, dw2[0]));
+            }
+        }
+        freezeVerticalPositions();
+    }
+
+    private void freezeVerticalPositions() throws IOException
+    {
+        COSArray cosDw2 = new COSArray();
+        cosDw2.add(COSInteger.get(Math.round(dw2[0])));
+        cosDw2.add(COSInteger.get(Math.round(dw2[1])));
+        dict.setItem(COSName.DW2, cosDw2);
+
+        COSArray cosW2 = new COSArray();
+        COSArray values = new COSArray();
+        int prev = Integer.MIN_VALUE;
+        Set<Integer> keys = new TreeSet<>(verticalDisplacementY.keySet());
+        for (int cid : keys)
+        {
+            long w1 = Math.round(verticalDisplacementY.get(cid));
+            if (w1 == dw2[1])
+            {
+                continue;
+            }
+            Vector pos = positionVectors.get(cid);
+            long v_x = Math.round(pos.getX());
+            long v_y = Math.round(pos.getY());
+            // c [w1_1y v_1x v_1y w1_2y v_2x v_2y ... w1_ny v_nx v_ny]
+            if (prev != cid - 1)
+            {
+                values = new COSArray();
+                cosW2.add(COSInteger.get(cid)); // c
+                cosW2.add(values);
+            }
+            values.add(COSInteger.get(w1)); // w1_iy
+            values.add(COSInteger.get(v_x)); // v_ix
+            values.add(COSInteger.get(v_y)); // v_iy
+            prev = cid;
+        }
+        dict.setItem(COSName.W2, cosW2);
     }
 
     private TrueTypeFont findFontOrSubstitute() throws IOException
@@ -188,7 +279,8 @@ public class PDCIDFontType2 extends PDCI
 
     private BoundingBox generateBoundingBox() throws IOException
     {
-        if (getFontDescriptor() != null) {
+        if (getFontDescriptor() != null)
+        {
             PDRectangle bbox = getFontDescriptor().getFontBoundingBox();
             if (bbox != null &&
                     (bbox.getLowerLeftX() != 0 || bbox.getLowerLeftY() != 0
@@ -215,6 +307,20 @@ public class PDCIDFontType2 extends PDCI
         return cMap.toCID(code);
     }
 
+    private Map<Integer, Integer> invert(int[] cid2gid)
+    {
+        if (cid2gid == null)
+        {
+            return null;
+        }
+        Map<Integer, Integer> inverse = new HashMap<>(cid2gid.length);
+        for (int i = 0; i < cid2gid.length; i++)
+        {
+            inverse.put(cid2gid[i], i);
+        }
+        return inverse;
+    }
+
     /**
      * Returns the GID for the given character code.
      *