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/28 19:37:50 UTC

svn commit: r1882099 - in /pdfbox/trunk/pdfbox/src: main/java/org/apache/pdfbox/pdmodel/interactive/form/ test/java/org/apache/pdfbox/pdmodel/interactive/form/ test/resources/org/apache/pdfbox/pdmodel/interactive/form/

Author: msahyoun
Date: Mon Sep 28 19:37:50 2020
New Revision: 1882099

URL: http://svn.apache.org/viewvc?rev=1882099&view=rev
Log:
PDFBOX-4958 change calculation of transformation as suggested by mkl

Added:
    pdfbox/trunk/pdfbox/src/test/resources/org/apache/pdfbox/pdmodel/interactive/form/PDFBOX-4958.pdf   (with props)
Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java?rev=1882099&r1=1882098&r2=1882099&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroForm.java Mon Sep 28 19:37:50 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;
@@ -330,56 +329,15 @@ public final class PDAcroForm implements
                     
                     contentStream.saveGraphicsState();
 
-                    //TODO read this: https://stackoverflow.com/a/54091766/535646
-
-                    // 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());
+                    // 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);
+                    Matrix appearanceStreamMatrix = appearanceStream.getMatrix();
+                    appearanceStreamMatrix.concatenate(transformationMatrix);
 
-                    Matrix transformationMatrix = new Matrix();
-                    boolean transformed = false;
-                    
-                    if (needsTranslation)
-                    {
-                        transformationMatrix.translate(annotation.getRectangle().getLowerLeftX(),
-                                annotation.getRectangle().getLowerLeftY());
-                        transformed = true;
-                    }
-
-                    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();
-                        }
-                        transformationMatrix.scale(xScale, yScale);
-                        transformed = true;
-                    }
-
-                    if (transformed)
-                    {
-                        contentStream.transform(transformationMatrix);
-                    }
-                    
+                    contentStream.transform(appearanceStreamMatrix);
                     contentStream.drawForm(fieldObject);
                     contentStream.restoreGraphicsState();
                     contentStream.close();
@@ -755,99 +713,32 @@ 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)
-    {
-        PDResources resources = appearanceStream.getResources();
-        if (resources == null || !resources.getXObjectNames().iterator().hasNext())
-        {
-            return true;
-        }
-        Iterator<COSName> xObjectNames = resources.getXObjectNames().iterator();
-        List<Object> tokens;
-        try
-        {
-            tokens = new PDFStreamParser(appearanceStream).parse();
-        }
-        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
-                LOG.debug("Couldn't resolve possible need for translation - ignoring, content might be misplaced", e);
-            }
-        }
 
-        // a field without specific settings typically needs to be translated
-        // to the correct position
-        return true;
+    private Matrix resolveTransformationMatrix(PDAnnotation annotation, PDAppearanceStream appearanceStream)
+    {
+        Rectangle2D transformedAppearanceBox = getTransformedAppearanceBBox(appearanceStream);
+        PDRectangle annotationRect = annotation.getRectangle();
+        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.
+     * 
+     * Apply the Matrix (or an identity transform) to the BBox of
+     * the appearance stream
      * 
-     * @param annotation
-     * @param rotation 
-     * @return the need for a scaling transformation.
-     */    
-    private boolean resolveNeedsScaling(PDAnnotation annotation, int rotation)
+     * @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/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java?rev=1882099&r1=1882098&r2=1882099&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java (original)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/interactive/form/PDAcroFormFlattenTest.java Mon Sep 28 19:37:50 2020
@@ -230,7 +230,7 @@ public class PDAcroFormFlattenTest
      *
      * @throws IOException
      */
-    // @Test
+    @Test
     public void testFlattenOpenOfficeFormFilled() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12840280/OpenOfficeForm_filled.pdf";
@@ -245,7 +245,7 @@ public class PDAcroFormFlattenTest
      *
      * @throws IOException
      */
-    // @Test
+    @Test
     public void testFlattenPDFBox4157() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976553/PDFBOX-4157-filled.pdf";
@@ -259,7 +259,7 @@ public class PDAcroFormFlattenTest
      *
      * @throws IOException
      */
-    // @Test
+    @Test
     public void testFlattenPDFBox4172() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976552/PDFBOX-4172-filled.pdf";
@@ -273,7 +273,7 @@ public class PDAcroFormFlattenTest
      *
      * @throws IOException
      */
-    // @Test
+    @Test
     public void testFlattenPDFBox4615() throws IOException
     {
         String sourceUrl = "https://issues.apache.org/jira/secure/attachment/12976452/resetboundingbox-filled.pdf";
@@ -339,6 +339,20 @@ public class PDAcroFormFlattenTest
         flattenAndCompare(sourceUrl, targetFileName);
     }
 
+    /**
+     * PDFBOX-4958 text and button with image.
+     *
+     * @throws IOException
+     */
+    @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.
      *
@@ -370,7 +384,7 @@ public class PDAcroFormFlattenTest
             // cleanup input and output directory for matching files.
             removeAllRenditions(inputFile);
             inputFile.delete();
-            outputFile.delete();
+            //outputFile.delete();
         }
     }
 

Added: pdfbox/trunk/pdfbox/src/test/resources/org/apache/pdfbox/pdmodel/interactive/form/PDFBOX-4958.pdf
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/resources/org/apache/pdfbox/pdmodel/interactive/form/PDFBOX-4958.pdf?rev=1882099&view=auto
==============================================================================
Binary file - no diff available.

Propchange: pdfbox/trunk/pdfbox/src/test/resources/org/apache/pdfbox/pdmodel/interactive/form/PDFBOX-4958.pdf
------------------------------------------------------------------------------
    svn:mime-type = application/pdf