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 2015/02/15 12:47:53 UTC

svn commit: r1659915 - /pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java

Author: tilman
Date: Sun Feb 15 11:47:52 2015
New Revision: 1659915

URL: http://svn.apache.org/r1659915
Log:
PDFBOX-1206: use PDF.js algorithm to calculate paths

Modified:
    pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java

Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java?rev=1659915&r1=1659914&r2=1659915&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/GlyphRenderer.java Sun Feb 15 11:47:52 2015
@@ -20,12 +20,21 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import java.awt.geom.GeneralPath;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class provides a glyph to GeneralPath conversion for true type fonts.
- * Based on code from Apache Batik a subproject of Apache XMLGraphics.
+ * Based on code from Apache Batik, a subproject of Apache XMLGraphics.
+ *
+ * @see
+ * <a href="http://xmlgraphics.apache.org/batik">http://xmlgraphics.apache.org/batik</a>
+ * 
+ * Contour rendering ported from PDF.js, viewed on 14.2.2015, rev 2e97c0d
+ *
+ * @see
+ * <a href="https://github.com/mozilla/pdf.js/blob/c0d17013a28ee7aa048831560b6494a26c52360c/src/core/font_renderer.js">pdf.js/src/core/font_renderer.js</a>
  *
- * @see <a href="http://xmlgraphics.apache.org/batik">http://xmlgraphics.apache.org/batik</a>
  */
 class GlyphRenderer
 {
@@ -33,7 +42,8 @@ class GlyphRenderer
 
     private GlyphDescription glyphDescription;
 
-    public GlyphRenderer(GlyphDescription glyphDescription) {
+    public GlyphRenderer(GlyphDescription glyphDescription)
+    {
         this.glyphDescription = glyphDescription;
     }
 
@@ -77,147 +87,59 @@ class GlyphRenderer
     private GeneralPath calculatePath(Point[] points)
     {
         GeneralPath path = new GeneralPath();
-        int numberOfPoints = points.length;
-        int i = 0;
-        boolean endOfContour = true;
-        Point startingPoint = null;
-        Point offCurveStartPoint = null;
-        while (i < numberOfPoints)
+        int start = 0;
+        for (int p = 0, len = points.length; p < len; ++p)
         {
-            Point point = points[i % numberOfPoints];
-            Point nextPoint1 = points[(i + 1) % numberOfPoints];
-            Point nextPoint2 = points[(i + 2) % numberOfPoints];
-            // new contour
-            if (endOfContour)
+            if (points[p].endOfContour)
             {
-                // skip endOfContour points
-                if (point.endOfContour)
+                Point firstPoint = points[start];
+                Point lastPoint = points[p];
+                List<Point> contour = new ArrayList<Point>();
+                for (int q = start; q <= p; ++q)
                 {
-                    i++;
-                    continue;
+                    contour.add(points[q]);
                 }
-                // move to the starting point
-                moveTo(path, point);
-                endOfContour = false;
-                startingPoint = point;
-
-                offCurveStartPoint = null;
-                if (!point.onCurve && !nextPoint1.onCurve)
+                if (points[start].onCurve)
                 {
-                    // off curve start
-                    offCurveStartPoint = point;
-                    startingPoint = midValue(point, nextPoint1);
-                    moveTo(path, startingPoint);
+                    // using start point at the contour end
+                    contour.add(firstPoint);
                 }
-            }
-
-            if (point.onCurve)
-            {
-                offCurveStartPoint = null;
-            }
-            // lineTo
-            if (point.onCurve && nextPoint1.onCurve)
-            {
-                lineTo(path, nextPoint1);
-                i++;
-                if (point.endOfContour || nextPoint1.endOfContour)
+                else if (points[p].onCurve)
                 {
-                    endOfContour = true;
-                    closePath(path);
-                }
-                continue;
-            }
-            // quadratic bezier
-            if (point.onCurve && !nextPoint1.onCurve && nextPoint2.onCurve)
-            {
-                if (nextPoint1.endOfContour)
-                {
-                    // use the starting point as end point
-                    quadTo(path, nextPoint1, startingPoint);
+                    // first is off-curve point, trying to use one from the end
+                    contour.add(0, lastPoint);
                 }
                 else
                 {
-                    quadTo(path, nextPoint1, nextPoint2);
-                }
-                if (nextPoint1.endOfContour || nextPoint2.endOfContour)
-                {
-                    endOfContour = true;
-                    closePath(path);
-                }
-                i += 2;
-                continue;
-            }
-
-            // TH segment for curves that start with an off-curve point
-            if (offCurveStartPoint != null && !nextPoint1.onCurve && !nextPoint2.onCurve)
-            {
-                // interpolate endPoint
-                quadTo(path, nextPoint1, midValue(nextPoint1, nextPoint2));
-                if (point.endOfContour || nextPoint1.endOfContour || nextPoint2.endOfContour)
-                {
-                    quadTo(path, nextPoint2, midValue(nextPoint2, offCurveStartPoint));
-                    quadTo(path, offCurveStartPoint, startingPoint);
-                    endOfContour = true;
-                    i += 2;
-                    continue;
+                    // start and end are off-curve points, creating implicit one
+                    Point pmid = midValue(firstPoint, lastPoint);
+                    contour.add(0, pmid);
+                    contour.add(pmid);
+                }
+                moveTo(path, contour.get(0));
+                for (int j = 1, clen = contour.size(); j < clen; j++)
+                {
+                    Point pnow = contour.get(j);
+                    if (pnow.onCurve)
+                    {
+                        lineTo(path, pnow);
+                    }
+                    else if (contour.get(j + 1).onCurve)
+                    {
+                        quadTo(path, pnow, contour.get(j + 1));
+                        ++j;
+                    }
+                    else
+                    {
+                        quadTo(path, pnow, midValue(pnow, contour.get(j + 1)));
+                    }
                 }
-                ++i;
-                continue;
+                start = p + 1;
             }
-
-            if (point.onCurve && !nextPoint1.onCurve && !nextPoint2.onCurve)
-            {
-                // interpolate endPoint
-                quadTo(path, nextPoint1, midValue(nextPoint1, nextPoint2));
-                if (point.endOfContour || nextPoint1.endOfContour || nextPoint2.endOfContour)
-                {
-                    quadTo(path, nextPoint2, startingPoint);
-                    endOfContour = true;
-                    closePath(path);
-                }
-                i += 2;
-                continue;
-            }
-
-            // TH the control point is never interpolated
-            if (!point.onCurve && !nextPoint1.onCurve)
-            {
-                quadTo(path, point, midValue(point, nextPoint1));
-                if (point.endOfContour || nextPoint1.endOfContour)
-                {
-                    endOfContour = true;
-                    quadTo(path, nextPoint1, startingPoint);
-                }
-                i++;
-                continue;
-            }
-
-            if (!point.onCurve && nextPoint1.onCurve)
-            {
-                quadTo(path, point, nextPoint1);
-                if (point.endOfContour || nextPoint1.endOfContour)
-                {
-                    endOfContour = true;
-                    closePath(path);
-                }
-                i++;
-                continue;
-            }
-            LOG.error("Unknown glyph command!!");
-            break;
         }
         return path;
     }
 
-    private void closePath(GeneralPath path)
-    {
-        path.closePath();
-        if (LOG.isDebugEnabled())
-        {
-            LOG.trace("closePath");
-        }
-    }
-
     private void moveTo(GeneralPath path, Point point)
     {
         path.moveTo(point.x, point.y);
@@ -251,6 +173,7 @@ class GlyphRenderer
         return a + (b - a) / 2;
     }
 
+    // this creates an onCurve point that is between point1 and point2
     private Point midValue(Point point1, Point point2)
     {
         return new Point(midValue(point1.x, point2.x), midValue(point1.y, point2.y));
@@ -274,9 +197,10 @@ class GlyphRenderer
             endOfContour = endOfContourValue;
         }
 
+        // this constructs an on-curve, non-endofcountour point
         Point(int xValue, int yValue)
         {
-            this(xValue, yValue, false, false);
+            this(xValue, yValue, true, false);
         }
 
         @Override