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/06/08 17:07:30 UTC

svn commit: r1833197 - in /pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers: PDAbstractAppearanceHandler.java PDTextAppearanceHandler.java

Author: tilman
Date: Fri Jun  8 17:07:30 2018
New Revision: 1833197

URL: http://svn.apache.org/viewvc?rev=1833197&view=rev
Log:
PDFBOX-3353: support /Circle and /Insert; use the Adobe width for /Note; draw circle counterclockwise

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDAbstractAppearanceHandler.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDAbstractAppearanceHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDAbstractAppearanceHandler.java?rev=1833197&r1=1833196&r2=1833197&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDAbstractAppearanceHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDAbstractAppearanceHandler.java Fri Jun  8 17:07:30 2018
@@ -365,14 +365,14 @@ public abstract class PDAbstractAppearan
     }
 
     /**
-     * Add a circle shape to the path.
+     * Add a circle shape to the path in clockwise direction.
      *
      * @param cs Content stream
      * @param x
      * @param y
      * @param r Radius
      * 
-     * @throws IOException If the content stream could not be written
+     * @throws IOException If the content stream could not be written.
      */
     void addCircle(PDAppearanceContentStream cs, float x, float y, float r) throws IOException
     {
@@ -386,6 +386,29 @@ public abstract class PDAbstractAppearan
         cs.closePath();
     }
 
+    /**
+     * Add a circle shape to the path in counterclockwise direction. You'll need this e.g. when
+     * drawing a doughnut shape. See "Nonzero Winding Number Rule" for more information.
+     *
+     * @param cs Content stream
+     * @param x
+     * @param y
+     * @param r Radius
+     *
+     * @throws IOException If the content stream could not be written.
+     */
+    void addCircle2(PDAppearanceContentStream cs, float x, float y, float r) throws IOException
+    {
+        // http://stackoverflow.com/a/2007782/535646
+        float magic = r * 0.551784f;
+        cs.moveTo(x, y + r);
+        cs.curveTo(x - magic, y + r, x - r, y + magic, x - r, y);
+        cs.curveTo(x - r, y - magic, x - magic, y - r, x, y - r);
+        cs.curveTo(x + magic, y - r, x + r, y - magic, x + r, y);
+        cs.curveTo(x + r, y + magic, x + magic, y + r, x, y + r);
+        cs.closePath();
+    }
+
     private static Set<String> createShortStyles()
     {
         Set<String> shortStyles = new HashSet<>();

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java?rev=1833197&r1=1833196&r2=1833197&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/handlers/PDTextAppearanceHandler.java Fri Jun  8 17:07:30 2018
@@ -20,9 +20,11 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.pdmodel.PDAppearanceContentStream;
 import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.graphics.blend.BlendMode;
+import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
+import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationText;
-import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
 
 /**
  *
@@ -49,47 +51,62 @@ public class PDTextAppearanceHandler ext
     public void generateNormalAppearance()
     {
         PDAnnotationText annotation = (PDAnnotationText) getAnnotation();
-        if (!PDAnnotationText.NAME_NOTE.equals(annotation.getName()))
+        if (!PDAnnotationText.NAME_NOTE.equals(annotation.getName()) &&
+                !PDAnnotationText.NAME_INSERT.equals(annotation.getName()) &&
+                !"Circle".equals(annotation.getName()))
         {
-            //TODO Comment, Key, Help, NewParagraph, Paragraph, Insert
+            //TODO Comment, Key, Help, NewParagraph, Paragraph
             return;
         }
 
         try (PDAppearanceContentStream contentStream = getNormalAppearanceAsContentStream())
         {
-            boolean hasBackground = contentStream.setNonStrokingColorOnDemand(getColor());
+            PDColor bgColor = getColor();
+            if (bgColor == null)
+            {
+                // White is used by Adobe when /C entry is missing
+                contentStream.setNonStrokingColor(1f);
+            }
+            else
+            {
+                contentStream.setNonStrokingColor(bgColor);
+            }
+            // stroking color is always black which is the PDF default
+
             setOpacity(contentStream, annotation.getConstantOpacity());
             
-            //TODO find out what Adobe chooses if color is missing
-
             PDRectangle rect = getRectangle();
-            PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
             PDRectangle bbox = rect.createRetranslatedRectangle();
-            appearanceStream.setBBox(bbox);
+            annotation.getNormalAppearanceStream().setBBox(bbox);
 
             switch (annotation.getName())
             {
                 case PDAnnotationText.NAME_NOTE:
-                    drawNote(contentStream, bbox, hasBackground);
+                    drawNote(contentStream, bbox);
+                    break;
+                case "Circle": //TODO constant
+                    drawCircles(contentStream, bbox);
+                    break;
+                case PDAnnotationText.NAME_INSERT:
+                    drawInsert(contentStream, bbox);
                     break;
 
                 default:
                     break;
             }
-
         }
         catch (IOException e)
         {
             LOG.error(e);
         }
-
     }
 
-    private void drawNote(final PDAppearanceContentStream contentStream, PDRectangle bbox, boolean hasBackground)
+    private void drawNote(final PDAppearanceContentStream contentStream, PDRectangle bbox)
             throws IOException
     {
-        contentStream.setLineJoinStyle(1); // round edge
-        contentStream.addRect(1, 1, bbox.getWidth() - 2,  bbox.getHeight() - 2);
+        contentStream.setLineJoinStyle(1); // get round edge the easy way
+        contentStream.setLineWidth(0.61f); // value from Adobe
+        contentStream.addRect(1, 1, bbox.getWidth() - 2, bbox.getHeight() - 2);
         contentStream.moveTo(bbox.getWidth() / 4,         bbox.getHeight() / 7 * 2);
         contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 2);
         contentStream.moveTo(bbox.getWidth() / 4,         bbox.getHeight() / 7 * 3);
@@ -98,7 +115,58 @@ public class PDTextAppearanceHandler ext
         contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 4);
         contentStream.moveTo(bbox.getWidth() / 4,         bbox.getHeight() / 7 * 5);
         contentStream.lineTo(bbox.getWidth() * 3 / 4 - 1, bbox.getHeight() / 7 * 5);
-        contentStream.drawShape(1, true, hasBackground);
+        contentStream.fillAndStroke();
+    }
+
+    private void drawCircles(final PDAppearanceContentStream contentStream, PDRectangle bbox)
+            throws IOException
+    {
+        // strategy used by Adobe:
+        // 1) add small circle in white using /ca /CA 0.6 and width 1
+        // 2) fill
+        // 3) add small circle in one direction
+        // 4) add large circle in other direction
+        // 5) stroke + fill
+        // with square width 20 small r = 6.36, large r = 9.756
+
+        // should be a square, but who knows...
+        float min = Math.min(bbox.getWidth(), bbox.getHeight());
+        float smallR = min / 20 * 6.36f;
+        float largeR = min / 20 * 9.756f;
+
+        contentStream.setMiterLimit(4);
+        contentStream.setLineJoinStyle(1);
+        contentStream.setLineCapStyle(0);
+        contentStream.saveGraphicsState();
+        contentStream.setLineWidth(1);
+        PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
+        gs.setAlphaSourceFlag(false);
+        gs.setStrokingAlphaConstant(0.6f);
+        gs.setNonStrokingAlphaConstant(0.6f);
+        gs.setBlendMode(BlendMode.NORMAL);
+        contentStream.setGraphicsStateParameters(gs);
+        contentStream.setNonStrokingColor(1f);
+        addCircle(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, smallR);
+        contentStream.fill();
+        contentStream.restoreGraphicsState();
+
+        contentStream.setLineWidth(0.59f); // value from Adobe
+        addCircle(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, smallR);
+        addCircle2(contentStream, bbox.getWidth() / 2, bbox.getHeight() / 2, largeR);
+        contentStream.fillAndStroke();
+    }
+
+    private void drawInsert(final PDAppearanceContentStream contentStream, PDRectangle bbox)
+            throws IOException
+    {
+        contentStream.setMiterLimit(4);
+        contentStream.setLineJoinStyle(0);
+        contentStream.setLineCapStyle(0);
+        contentStream.setLineWidth(0.59f); // value from Adobe
+        contentStream.moveTo(bbox.getWidth() / 2 - 1, bbox.getHeight() - 2);
+        contentStream.lineTo(1, 1);
+        contentStream.lineTo(bbox.getWidth() - 2, 1);
+        contentStream.closeAndFillAndStroke();
     }
 
     @Override