Skip to content

Commit 6467b68

Browse files
author
Fedorov Alexandr
committed
Fix flattening problem with stamps
DEVSIX-9160
1 parent fc00408 commit 6467b68

File tree

9 files changed

+103
-48
lines changed

9 files changed

+103
-48
lines changed

forms/src/main/java/com/itextpdf/forms/PdfAcroForm.java

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ This file is part of the iText (R) project.
3737
import com.itextpdf.forms.xfa.XfaForm;
3838
import com.itextpdf.kernel.exceptions.PdfException;
3939
import com.itextpdf.kernel.geom.AffineTransform;
40-
import com.itextpdf.kernel.geom.Point;
4140
import com.itextpdf.kernel.geom.Rectangle;
4241
import com.itextpdf.kernel.pdf.PdfArray;
4342
import com.itextpdf.kernel.pdf.PdfBoolean;
@@ -808,7 +807,7 @@ public void flattenFields() {
808807
canvas.openTag(tagRef);
809808
}
810809

811-
AffineTransform at = calcFieldAppTransformToAnnotRect(xObject, annotBBox);
810+
AffineTransform at = PdfFormXObject.calcAppearanceTransformToAnnotRect(xObject, annotBBox);
812811
float[] m = new float[6];
813812
at.getMatrix(m);
814813
canvas.addXObjectWithTransformationMatrix(xObject, m[0], m[1], m[2], m[3], m[4], m[5]);
@@ -1206,50 +1205,7 @@ private Set<PdfFormField> prepareFieldsForFlattening(PdfFormField field) {
12061205
return preparedFields;
12071206
}
12081207

1209-
private AffineTransform calcFieldAppTransformToAnnotRect(PdfFormXObject xObject, Rectangle annotBBox) {
1210-
PdfArray bBox = xObject.getBBox();
1211-
if (bBox.size() != 4) {
1212-
bBox = new PdfArray(new Rectangle(0, 0));
1213-
xObject.setBBox(bBox);
1214-
}
1215-
float[] xObjBBox = bBox.toFloatArray();
1216-
1217-
PdfArray xObjMatrix = xObject.getPdfObject().getAsArray(PdfName.Matrix);
1218-
Rectangle transformedRect;
1219-
if (xObjMatrix != null && xObjMatrix.size() == 6) {
1220-
Point[] xObjRectPoints = new Point[]{
1221-
new Point(xObjBBox[0], xObjBBox[1]),
1222-
new Point(xObjBBox[0], xObjBBox[3]),
1223-
new Point(xObjBBox[2], xObjBBox[1]),
1224-
new Point(xObjBBox[2], xObjBBox[3])
1225-
};
1226-
Point[] transformedAppBoxPoints = new Point[xObjRectPoints.length];
1227-
new AffineTransform(xObjMatrix.toDoubleArray()).transform(xObjRectPoints, 0, transformedAppBoxPoints, 0, xObjRectPoints.length);
1228-
1229-
float[] transformedRectArr = new float[] {
1230-
Float.MAX_VALUE, Float.MAX_VALUE,
1231-
-Float.MAX_VALUE, -Float.MAX_VALUE,
1232-
};
1233-
for (Point p : transformedAppBoxPoints) {
1234-
transformedRectArr[0] = (float) Math.min(transformedRectArr[0], p.getX());
1235-
transformedRectArr[1] = (float) Math.min(transformedRectArr[1], p.getY());
1236-
transformedRectArr[2] = (float) Math.max(transformedRectArr[2], p.getX());
1237-
transformedRectArr[3] = (float) Math.max(transformedRectArr[3], p.getY());
1238-
}
12391208

1240-
transformedRect = new Rectangle(transformedRectArr[0], transformedRectArr[1], transformedRectArr[2] - transformedRectArr[0], transformedRectArr[3] - transformedRectArr[1]);
1241-
} else {
1242-
transformedRect = new Rectangle(0, 0).setBbox(xObjBBox[0], xObjBBox[1], xObjBBox[2], xObjBBox[3]);
1243-
}
1244-
1245-
AffineTransform at = AffineTransform.getTranslateInstance(-transformedRect.getX(), -transformedRect.getY());
1246-
float scaleX = transformedRect.getWidth() == 0 ? 1 : annotBBox.getWidth() / transformedRect.getWidth();
1247-
float scaleY = transformedRect.getHeight() == 0 ? 1 : annotBBox.getHeight() / transformedRect.getHeight();
1248-
at.preConcatenate(AffineTransform.getScaleInstance(scaleX, scaleY));
1249-
at.preConcatenate(AffineTransform.getTranslateInstance(annotBBox.getX(), annotBBox.getY()));
1250-
1251-
return at;
1252-
}
12531209

12541210
private Set<PdfFormField> getAllFormFieldsWithoutNames() {
12551211
if (fields.isEmpty()) {

kernel/src/main/java/com/itextpdf/kernel/pdf/xobject/PdfFormXObject.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.kernel.pdf.xobject;
2424

25+
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
2526
import com.itextpdf.kernel.exceptions.PdfException;
27+
import com.itextpdf.kernel.geom.AffineTransform;
2628
import com.itextpdf.kernel.geom.Matrix;
27-
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
29+
import com.itextpdf.kernel.geom.Point;
2830
import com.itextpdf.kernel.geom.Rectangle;
2931
import com.itextpdf.kernel.geom.Vector;
3032
import com.itextpdf.kernel.pdf.PdfArray;
@@ -133,6 +135,66 @@ public static Rectangle calculateBBoxMultipliedByMatrix(PdfFormXObject form) {
133135
return new Rectangle(bBoxMinByMatrix.get(Vector.I1), bBoxMinByMatrix.get(Vector.I2), width, height);
134136
}
135137

138+
/**
139+
* Calculates an {@link AffineTransform} that maps the coordinate system of a given {@link PdfFormXObject}
140+
* to fit within a specified annotation bounding box.
141+
*
142+
* <p>
143+
*
144+
* This transformation includes translation and scaling so that the xObject's transformed bounding box
145+
* aligns with the specified annotation rectangle.
146+
*
147+
* @param xObject the {@link PdfFormXObject} whose appearance is to be transformed.
148+
* @param annotBBox the {@link Rectangle} representing the target annotation's bounding box to which the
149+
* appearance should be fitted.
150+
* @return an {@link AffineTransform} that, when applied to the appearance stream's graphics, maps it
151+
* to the target annotation rectangle.
152+
*/
153+
public static AffineTransform calcAppearanceTransformToAnnotRect(PdfFormXObject xObject, Rectangle annotBBox) {
154+
PdfArray bBox = xObject.getBBox();
155+
if (bBox.size() != 4) {
156+
bBox = new PdfArray(new Rectangle(0, 0));
157+
xObject.setBBox(bBox);
158+
}
159+
float[] xObjBBox = bBox.toFloatArray();
160+
161+
PdfArray xObjMatrix = xObject.getPdfObject().getAsArray(PdfName.Matrix);
162+
Rectangle transformedRect;
163+
if (xObjMatrix != null && xObjMatrix.size() == 6) {
164+
Point[] xObjRectPoints = new Point[]{
165+
new Point(xObjBBox[0], xObjBBox[1]),
166+
new Point(xObjBBox[0], xObjBBox[3]),
167+
new Point(xObjBBox[2], xObjBBox[1]),
168+
new Point(xObjBBox[2], xObjBBox[3])
169+
};
170+
Point[] transformedAppBoxPoints = new Point[xObjRectPoints.length];
171+
new AffineTransform(xObjMatrix.toDoubleArray()).transform(xObjRectPoints, 0, transformedAppBoxPoints, 0, xObjRectPoints.length);
172+
173+
float[] transformedRectArr = new float[] {
174+
Float.MAX_VALUE, Float.MAX_VALUE,
175+
-Float.MAX_VALUE, -Float.MAX_VALUE,
176+
};
177+
for (Point p : transformedAppBoxPoints) {
178+
transformedRectArr[0] = (float) Math.min(transformedRectArr[0], p.getX());
179+
transformedRectArr[1] = (float) Math.min(transformedRectArr[1], p.getY());
180+
transformedRectArr[2] = (float) Math.max(transformedRectArr[2], p.getX());
181+
transformedRectArr[3] = (float) Math.max(transformedRectArr[3], p.getY());
182+
}
183+
184+
transformedRect = new Rectangle(transformedRectArr[0], transformedRectArr[1], transformedRectArr[2] - transformedRectArr[0], transformedRectArr[3] - transformedRectArr[1]);
185+
} else {
186+
transformedRect = new Rectangle(0, 0).setBbox(xObjBBox[0], xObjBBox[1], xObjBBox[2], xObjBBox[3]);
187+
}
188+
189+
AffineTransform at = AffineTransform.getTranslateInstance(-transformedRect.getX(), -transformedRect.getY());
190+
float scaleX = transformedRect.getWidth() == 0 ? 1 : annotBBox.getWidth() / transformedRect.getWidth();
191+
float scaleY = transformedRect.getHeight() == 0 ? 1 : annotBBox.getHeight() / transformedRect.getHeight();
192+
at.preConcatenate(AffineTransform.getScaleInstance(scaleX, scaleY));
193+
at.preConcatenate(AffineTransform.getTranslateInstance(annotBBox.getX(), annotBBox.getY()));
194+
195+
return at;
196+
}
197+
136198
/**
137199
* Gets {@link PdfResources} of the Form XObject.
138200
* Note, if there is no resources, a new instance will be created.

kernel/src/main/java/com/itextpdf/kernel/utils/annotationsflattening/DefaultAnnotationFlattener.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This file is part of the iText (R) project.
2525
import com.itextpdf.commons.utils.MessageFormatUtil;
2626
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
2727
import com.itextpdf.kernel.exceptions.PdfException;
28+
import com.itextpdf.kernel.geom.AffineTransform;
2829
import com.itextpdf.kernel.geom.Rectangle;
2930
import com.itextpdf.kernel.logs.KernelLogMessageConstant;
3031
import com.itextpdf.kernel.pdf.PdfArray;
@@ -78,7 +79,11 @@ public boolean flatten(PdfAnnotation annotation, PdfPage page) {
7879
final Rectangle area = annotation.getRectangle().toRectangle();
7980
final PdfCanvas under = this.createCanvas(page);
8081
final PdfStream annotationNormalAppearanceStream = (PdfStream) normalAppearance;
81-
under.addXObjectFittedIntoRectangle(new PdfFormXObject(annotationNormalAppearanceStream), area);
82+
PdfFormXObject xObject = new PdfFormXObject(annotationNormalAppearanceStream);
83+
AffineTransform at = PdfFormXObject.calcAppearanceTransformToAnnotRect(xObject, area);
84+
float[] m = new float[6];
85+
at.getMatrix(m);
86+
under.addXObjectWithTransformationMatrix(xObject, m[0], m[1], m[2], m[3], m[4], m[5]);
8287
page.removeAnnotation(annotation);
8388
return true;
8489
}

kernel/src/test/java/com/itextpdf/kernel/pdf/xobject/PdfXObjectUnitTest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ This file is part of the iText (R) project.
2323
package com.itextpdf.kernel.pdf.xobject;
2424

2525
import com.itextpdf.kernel.exceptions.KernelExceptionMessageConstant;
26+
import com.itextpdf.kernel.geom.AffineTransform;
27+
import com.itextpdf.kernel.geom.Rectangle;
28+
import com.itextpdf.kernel.pdf.PdfArray;
2629
import com.itextpdf.kernel.pdf.PdfName;
2730
import com.itextpdf.kernel.pdf.PdfStream;
2831
import com.itextpdf.test.ExtendedITextTest;
2932

3033
import org.junit.jupiter.api.Assertions;
31-
import org.junit.jupiter.api.Test;
3234
import org.junit.jupiter.api.Tag;
35+
import org.junit.jupiter.api.Test;
3336

3437
@Tag("UnitTest")
3538
public class PdfXObjectUnitTest extends ExtendedITextTest {
@@ -50,4 +53,16 @@ public void unsupportedSubTypeIsSet() {
5053
() -> PdfXObject.makeXObject(pdfStream));
5154
Assertions.assertEquals(KernelExceptionMessageConstant.UNSUPPORTED_XOBJECT_TYPE, exception.getMessage());
5255
}
56+
57+
@Test
58+
public void invalidBBoxParametersTest() {
59+
AffineTransform expectedTransform = new AffineTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
60+
PdfFormXObject pdfFormXObject = new PdfFormXObject(new Rectangle(200, 200));
61+
pdfFormXObject.setBBox(new PdfArray(new int[]{0, 0, 20}));
62+
AffineTransform affineTransform = PdfFormXObject.calcAppearanceTransformToAnnotRect(
63+
pdfFormXObject, new Rectangle(200, 200));
64+
65+
Assertions.assertEquals(expectedTransform,affineTransform);
66+
67+
}
5368
}

kernel/src/test/java/com/itextpdf/kernel/utils/PdfAnnotationFlattenerTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,23 @@ public void flattenPrinterMarkAnnotationTest() throws IOException, InterruptedEx
621621
DESTINATION_FOLDER, "diff_"));
622622
}
623623

624+
@Test
625+
public void flattenAnnotationWithRotationTest() throws IOException, InterruptedException {
626+
String sourceFile = SOURCE_FOLDER + "rotatedStampAnnotation.pdf";
627+
String resultFile = DESTINATION_FOLDER + "rotatedStampAnnotationTest.pdf";
628+
try (PdfDocument document = new PdfDocument(new PdfReader(sourceFile), CompareTool.createTestPdfWriter(resultFile))) {
629+
PdfAnnotationFlattener flattener = new PdfAnnotationFlattener();
630+
flattener.flatten(document);
631+
632+
Assertions.assertEquals(0, document.getFirstPage().getAnnotations().size());
633+
}
634+
635+
Assertions.assertNull(
636+
new CompareTool().compareByContent(resultFile,
637+
SOURCE_FOLDER + "cmp_rotatedStampAnnotation.pdf",
638+
DESTINATION_FOLDER, "diff_"));
639+
}
640+
624641
@Test
625642
public void flattenTrapNetAnnotationTest() throws IOException, InterruptedException {
626643
String sourceFile = SOURCE_FOLDER + "flattenTrapNetAnnotationTest.pdf";
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)