You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ms...@apache.org on 2020/09/30 08:19:45 UTC

svn commit: r1882149 - in /pdfbox/branches/2.0/pdfbox/src: main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java

Author: msahyoun
Date: Wed Sep 30 08:19:45 2020
New Revision: 1882149

URL: http://svn.apache.org/viewvc?rev=1882149&view=rev
Log:
PDFBOX-4958 apply changes from trunk to 2.0 branch

Modified:
    pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
    pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java

Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java?rev=1882149&r1=1882148&r2=1882149&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java Wed Sep 30 08:19:45 2020
@@ -16,6 +16,9 @@
  */
 package org.apache.pdfbox.pdmodel.interactive.form;
 
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,14 +31,11 @@ import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.pdfbox.contentstream.operator.Operator;
-import org.apache.pdfbox.contentstream.operator.OperatorName;
 import org.apache.pdfbox.cos.COSArray;
 import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.cos.COSNumber;
-import org.apache.pdfbox.pdfparser.PDFStreamParser;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.PDPage;
 import org.apache.pdfbox.pdmodel.PDPageContentStream;
@@ -49,7 +49,6 @@ import org.apache.pdfbox.pdmodel.fdf.FDF
 import org.apache.pdfbox.pdmodel.fdf.FDFDocument;
 import org.apache.pdfbox.pdmodel.fdf.FDFField;
 import org.apache.pdfbox.pdmodel.font.PDType1Font;
-import org.apache.pdfbox.pdmodel.graphics.PDXObject;
 import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
@@ -332,56 +331,12 @@ public final class PDAcroForm implements
                     
                     contentStream.saveGraphicsState();
                     
-                    // translate the appearance stream to the widget location if there is 
-                    // not already a transformation in place
-                    boolean needsTranslation = resolveNeedsTranslation(appearanceStream);
-
-                    // scale the appearance stream - mainly needed for images
-                    // in buttons and signatures
-                    boolean needsScaling = resolveNeedsScaling(annotation, page.getRotation());
-
-                    Matrix transformationMatrix = new Matrix();
-                    boolean transformed = false;
-                    
-                    if (needsTranslation)
-                    {
-                        transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(),
-                                annotation.getRectangle().getLowerLeftY());
-                        transformed = true;
-                    }
-
-                    // PDFBOX-4693: field could have a rotation matrix
-                    Matrix m = appearanceStream.getMatrix();
-                    int angle = (int) Math.round(Math.toDegrees(Math.atan2(m.getShearY(), m.getScaleY())));
-                    int rotation = (angle + 360) % 360;
-
-                    if (needsScaling)
-                    {
-                        PDRectangle bbox = appearanceStream.getBBox();
-                        PDRectangle fieldRect = annotation.getRectangle();
-
-                        float xScale;
-                        float yScale;
-                        if (rotation == 90 || rotation == 270)
-                        {
-                            xScale = fieldRect.getWidth() / bbox.getHeight();
-                            yScale = fieldRect.getHeight() / bbox.getWidth();
-                        }
-                        else
-                        {
-                            xScale = fieldRect.getWidth() / bbox.getWidth();
-                            yScale = fieldRect.getHeight() / bbox.getHeight();
-                        }
-                        Matrix scalingMatrix = Matrix.getScaleInstance(xScale, yScale);
-                        transformationMatrix.concatenate(scalingMatrix);
-                        transformed = true;
-                    }
-
-                    if (transformed)
-                    {
-                        contentStream.transform(transformationMatrix);
-                    }
-                    
+                    // see https://stackoverflow.com/a/54091766/1729265 for an explanation
+                    // of the steps required
+                    // this will transform the appearance stream form object into the rectangle of the
+                    // annotation bbox and map the coordinate systems
+                    Matrix transformationMatrix = resolveTransformationMatrix(annotation, appearanceStream);
+                    contentStream.transform(transformationMatrix);                    
                     contentStream.drawForm(fieldObject);
                     contentStream.restoreGraphicsState();
                     contentStream.close();
@@ -758,99 +713,34 @@ public final class PDAcroForm implements
         dictionary.setFlag(COSName.SIG_FLAGS, FLAG_APPEND_ONLY, appendOnly);
     }
     
-    /**
-     * Check if there is a translation needed to place the annotations content.
-     * 
-     * @param appearanceStream
-     * @return the need for a translation transformation.
-     */
-    private boolean resolveNeedsTranslation(PDAppearanceStream appearanceStream)
+    private Matrix resolveTransformationMatrix(PDAnnotation annotation, PDAppearanceStream appearanceStream)
     {
-        PDResources resources = appearanceStream.getResources();
-        if (resources == null || !resources.getXObjectNames().iterator().hasNext())
-        {
-            return true;
-        }
-        Iterator<COSName> xObjectNames = resources.getXObjectNames().iterator();
-        List<Object> tokens;
-        try
-        {
-            PDFStreamParser pdfStreamParser = new PDFStreamParser(appearanceStream);
-            pdfStreamParser.parse();
-            tokens = pdfStreamParser.getTokens();
-        }
-        catch (IOException ex)
-        {
-            LOG.debug("Couldn't not parse appearance content stream - content might be misplaced", ex);
-            return true;
-        }
-        while (xObjectNames.hasNext())
-        {
-            try
-            {
-                // if the BBox of the PDFormXObject does not start at 0,0
-                // there is no need do translate as this is done by the BBox definition.
-                COSName name = xObjectNames.next();
-                PDXObject xObject = resources.getXObject(name);
-                if (xObject instanceof PDFormXObject)
-                {
-                    PDRectangle bbox = ((PDFormXObject) xObject).getBBox();
-                    float llX = bbox.getLowerLeftX();
-                    float llY = bbox.getLowerLeftY();
-                    if (Float.compare(llX, 0) != 0 && Float.compare(llY, 0) != 0)
-                    {
-                        // PDFBOX-4955: only if used
-                        for (int i = 0; i < tokens.size(); ++i)
-                        {
-                            if (tokens.get(i).equals(name) && i < tokens.size() - 1 &&
-                                tokens.get(i + 1).equals(Operator.getOperator(OperatorName.DRAW_OBJECT)))
-                            {
-                                return false;
-                            }
-                        }
-                    }
-                }
-            }
-            catch (IOException e)
-            {
-                // we can safely ignore the exception here
-                // as this might only cause a misplacement
-            }
-        }
+        // 1st step transform appearance stream bbox with appearance stream matrix
+        Rectangle2D transformedAppearanceBox = getTransformedAppearanceBBox(appearanceStream);
+        PDRectangle annotationRect = annotation.getRectangle();
 
-        // a field without specific settings typically needs to be translated
-        // to the correct position
-        return true;
+        // 2nd step caclulate matrix to transform calculated rectangle into the annotation Rect boundaries
+        Matrix transformationMatrix = new Matrix();
+        transformationMatrix.translate((float) (annotationRect.getLowerLeftX()-transformedAppearanceBox.getX()), (float) (annotationRect.getLowerLeftY()-transformedAppearanceBox.getY()));
+        transformationMatrix.scale((float) (annotationRect.getWidth()/transformedAppearanceBox.getWidth()), (float) (annotationRect.getHeight()/transformedAppearanceBox.getHeight()));
+        return transformationMatrix;
     }
-    
+
     /**
-     * Check if there needs to be a scaling transformation applied.
+     * Calculate the transformed appearance box.
      * 
-     * @param annotation
-     * @param rotation 
-     * @return the need for a scaling transformation.
-     */    
-    private boolean resolveNeedsScaling(PDAnnotation annotation, int rotation)
+     * Apply the Matrix (or an identity transform) to the BBox of
+     * the appearance stream
+     * 
+     * @param appearanceStream
+     * @return the transformed rectangle
+     */
+    private Rectangle2D getTransformedAppearanceBBox(PDAppearanceStream appearanceStream)
     {
-        PDAppearanceStream appearanceStream = annotation.getNormalAppearanceStream();
-        // Check if there is a transformation within the XObjects content
-        PDResources resources = appearanceStream.getResources();
-        if (resources != null && resources.getXObjectNames().iterator().hasNext())
-        {
-            return true;
-        }
-        PDRectangle bbox = appearanceStream.getBBox();
-        PDRectangle fieldRect = annotation.getRectangle();
-        if (rotation == 90 || rotation == 270)
-        {
-            return Float.compare(bbox.getWidth(),  fieldRect.getHeight()) != 0 ||
-                   Float.compare(bbox.getHeight(), fieldRect.getWidth()) != 0;
-        }
-        else
-        {
-            return Float.compare(bbox.getWidth(),  fieldRect.getWidth()) != 0 ||
-                   Float.compare(bbox.getHeight(), fieldRect.getHeight()) != 0;
-        }
+        Matrix appearanceStreamMatrix = appearanceStream.getMatrix();
+        PDRectangle appearanceStreamBBox = appearanceStream.getBBox();        
+        GeneralPath transformedAppearanceBox = appearanceStreamBBox.transform(appearanceStreamMatrix);
+        return transformedAppearanceBox.getBounds2D();
     }
 
     private Map<COSDictionary,Set<COSDictionary>> buildPagesWidgetsMap(List<PDField> fields) throws IOException

Modified: pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java?rev=1882149&r1=1882148&r2=1882149&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java Wed Sep 30 08:19:45 2020
@@ -71,7 +71,7 @@ public class PDAcroFormFlattenTest
     /*
      * PDFBOX-563 Filled template.
      */
-    // @Test
+    @Test
     public void testFlattenPDFBOX563() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12425859/TestFax_56972.pdf";
@@ -95,7 +95,7 @@ public class PDAcroFormFlattenTest
     /*
      * PDFBOX-2469 Filled template.
      */
-    // @Test
+    @Test
     public void testFlattenPDFBOX2469Filled() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12678455/testPDF_acroForm.pdf";
@@ -191,7 +191,7 @@ public class PDAcroFormFlattenTest
     /*
      * PDFBOX-3587 Empty template.
      */
-    // @Test
+    @Test
     public void testFlattenOpenOfficeForm() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12839977/OpenOfficeForm.pdf";
@@ -239,7 +239,7 @@ public class PDAcroFormFlattenTest
     /**
      * PDFBOX-4615 Filled template.
      */
-    // @Test
+    @Test
     public void testFlattenPDFBox4615() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976452/resetboundingbox-filled.pdf";
@@ -300,6 +300,15 @@ public class PDAcroFormFlattenTest
 
         flattenAndCompare(sourceUrl, targetFileName);
     }
+
+    @Test
+    public void testFlattenPDFBox4958() throws IOException
+    {
+        String sourceUrl = "https://issues.apache.org/jira/secure/attachment/13012242/PDFBOX-4958.pdf";
+        String targetFileName = "PDFBOX-4958-flattened.pdf";
+
+        flattenAndCompare(sourceUrl, targetFileName);
+    }
 
     /*
      * Flatten and compare with generated image samples.