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