Skip to content

Commit 2dc9118

Browse files
author
Samuel Huylebroeck
committed
Fix forms moved while flattening on a rotated page
Default appearances for fields with no AP dictionary were generated incorrectly, causing them to appear moved when flattening. Additionally, fields only took page rotation into account and not their own. Fix by properly calculating transformation matrix during default appearance generation. SUP-1555, DEVSIX-832
1 parent aed7ccc commit 2dc9118

File tree

35 files changed

+202
-7
lines changed

35 files changed

+202
-7
lines changed

forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ This file is part of the iText (R) project.
5858
import com.itextpdf.kernel.color.DeviceRgb;
5959
import com.itextpdf.kernel.font.PdfFont;
6060
import com.itextpdf.kernel.font.PdfFontFactory;
61+
import com.itextpdf.kernel.geom.AffineTransform;
62+
import com.itextpdf.kernel.geom.Matrix;
6163
import com.itextpdf.kernel.geom.Rectangle;
6264
import com.itextpdf.kernel.pdf.*;
6365
import com.itextpdf.kernel.pdf.action.PdfAction;
@@ -1654,8 +1656,10 @@ public PdfFormField setRotation(int degRotation) {
16541656
PdfDictionary mk = getWidgets().get(0).getAppearanceCharacteristics();
16551657
if (mk == null) {
16561658
mk = new PdfDictionary();
1659+
this.put(PdfName.MK,mk);
16571660
}
16581661
mk.put(PdfName.R, new PdfNumber(degRotation));
1662+
16591663
this.rotation = degRotation;
16601664
regenerateField();
16611665
return this;
@@ -1761,17 +1765,74 @@ public boolean regenerateField() {
17611765
fontSize = (float)DEFAULT_FONT_SIZE;
17621766
}
17631767

1764-
int rotation = 0;
1768+
//Apply Page rotation, clockwise
1769+
int pageRotation = 0;
17651770
if (page != null) {
1766-
rotation = page.getRotation();
1771+
pageRotation = page.getRotation();
17671772
}
17681773
PdfArray matrix = null;
1769-
if (rotation != 0 && rotation % 90 == 0) {
1770-
double angle = Math.PI * rotation / 180;
1771-
matrix = new PdfArray(new double[]{Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0, 0});
1774+
if (pageRotation % 90 == 0) {
1775+
//Cast angle to [0, 360]
1776+
double angle = pageRotation%360;
1777+
//Get angle in radians
1778+
angle = Math.PI * angle / 180;
1779+
//Calculate origin offset
1780+
double translationWidth = 0;
1781+
double translationHeight = 0;
1782+
if(angle >= Math.PI/2 && angle <= Math.PI){
1783+
translationWidth = bBox.toRectangle().getWidth();
1784+
}
1785+
if(angle >= Math.PI){
1786+
translationHeight = bBox.toRectangle().getHeight();
1787+
}
1788+
//Store rotation and translation in the matrix
1789+
matrix = new PdfArray(new double[]{Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle),translationWidth, translationHeight});
17721790
Rectangle rect = bBox.toRectangle();
1773-
rect.setWidth(bBox.toRectangle().getHeight());
1774-
rect.setHeight(bBox.toRectangle().getWidth());
1791+
//If the angle is 90 or 270, height and width of the bounding box need to be switched
1792+
if(angle == Math.PI/2 || angle == 3*Math.PI/2){
1793+
rect.setWidth(bBox.toRectangle().getHeight());
1794+
rect.setHeight(bBox.toRectangle().getWidth());
1795+
}
1796+
//Copy Bounding box
1797+
bBox = new PdfArray(rect);
1798+
}
1799+
//Apply field rotation
1800+
float rotation = 0;
1801+
if(this.getPdfObject().getAsDictionary(PdfName.MK)!= null
1802+
&& this.getPdfObject().getAsDictionary(PdfName.MK).get(PdfName.R) != null){
1803+
rotation = this.getPdfObject().getAsDictionary(PdfName.MK).getAsFloat(PdfName.R);
1804+
}
1805+
//rotation = this.rotation;
1806+
//Field rotation is specified as counterclockwise
1807+
//rotation *=-1;
1808+
if (rotation % 90 == 0) {
1809+
//Cast angle to [0, 360]
1810+
double angle = rotation%360;
1811+
//Get angle in radians
1812+
angle = Math.PI * angle / 180;
1813+
//Calculate origin offset
1814+
double translationWidth = 0;
1815+
double translationHeight = 0;
1816+
if(angle >= Math.PI/2 && angle <= Math.PI){
1817+
translationWidth = bBox.toRectangle().getWidth();
1818+
}
1819+
if(angle >= Math.PI){
1820+
translationHeight = bBox.toRectangle().getHeight();
1821+
}
1822+
//Concatenate rotation and translation into the matrix
1823+
Matrix currentMatrix = new Matrix(matrix.getAsNumber(0).floatValue(),matrix.getAsNumber(1).floatValue(),matrix.getAsNumber(2).floatValue(),matrix.getAsNumber(3).floatValue(),matrix.getAsNumber(4).floatValue(),matrix.getAsNumber(5).floatValue());
1824+
Matrix toConcatenate = new Matrix((float)Math.cos(angle), (float)Math.sin(angle),(float) (-Math.sin(angle)), (float)(Math.cos(angle)),(float)translationWidth, (float)translationHeight);
1825+
currentMatrix = currentMatrix.multiply(toConcatenate);
1826+
1827+
matrix = new PdfArray(new float[]{currentMatrix.get(0), currentMatrix.get(1), currentMatrix.get(3), currentMatrix.get(4),currentMatrix.get(6), currentMatrix.get(7)});
1828+
1829+
Rectangle rect = bBox.toRectangle();
1830+
//If the angle is 90 or 270, height and width of the bounding box need to be switched
1831+
if(angle == Math.PI/2 || angle == 3*Math.PI/2){
1832+
rect.setWidth(bBox.toRectangle().getHeight());
1833+
rect.setHeight(bBox.toRectangle().getWidth());
1834+
}
1835+
//Copy Bounding box
17751836
bBox = new PdfArray(rect);
17761837
}
17771838

forms/src/test/java/com/itextpdf/forms/FormFieldFlatteningTest.java

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.itextpdf.forms;
22

3+
import com.itextpdf.forms.fields.PdfFormField;
34
import com.itextpdf.kernel.pdf.PdfDocument;
45
import com.itextpdf.kernel.pdf.PdfReader;
56
import com.itextpdf.kernel.pdf.PdfWriter;
@@ -42,4 +43,116 @@ public void formFlatteningTest01() throws IOException, InterruptedException {
4243
Assert.fail(errorMessage);
4344
}
4445
}
46+
47+
@Test
48+
public void formFlatteningTest_DefaultAppearanceGeneration_Rot0() throws IOException, InterruptedException {
49+
String srcFilePattern = "FormFlatteningDefaultAppearance_0.0_";
50+
String destPattern = "FormFlatteningDefaultAppearance_0.0_";
51+
52+
for(float i = 0; i<360;i+=90){
53+
String src = sourceFolder + srcFilePattern + i+".pdf";
54+
String dest = destinationFolder + destPattern + i +"_flattened.pdf";
55+
String cmp = sourceFolder + "cmp_" + srcFilePattern + i + ".pdf";
56+
PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
57+
58+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
59+
for(PdfFormField field: form.getFormFields().values()){
60+
field.setValue("Test");
61+
}
62+
form.flattenFields();
63+
64+
doc.close();
65+
66+
CompareTool compareTool = new CompareTool();
67+
String errorMessage = compareTool.compareByContent(dest, cmp, destinationFolder, "diff_");
68+
if (errorMessage != null) {
69+
Assert.fail(errorMessage);
70+
}
71+
}
72+
73+
}
74+
75+
@Test
76+
public void formFlatteningTest_DefaultAppearanceGeneration_Rot90() throws IOException, InterruptedException {
77+
String srcFilePattern = "FormFlatteningDefaultAppearance_90.0_";
78+
String destPattern = "FormFlatteningDefaultAppearance_90.0_";
79+
80+
for(float i = 0; i<360;i+=90){
81+
String src = sourceFolder + srcFilePattern + i+".pdf";
82+
String dest = destinationFolder + destPattern + i +"_flattened.pdf";
83+
String cmp = sourceFolder + "cmp_" + srcFilePattern + i + ".pdf";
84+
PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
85+
86+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
87+
for(PdfFormField field: form.getFormFields().values()){
88+
field.setValue("Test");
89+
}
90+
form.flattenFields();
91+
92+
doc.close();
93+
94+
CompareTool compareTool = new CompareTool();
95+
String errorMessage = compareTool.compareByContent(dest, cmp, destinationFolder, "diff_");
96+
if (errorMessage != null) {
97+
Assert.fail(errorMessage);
98+
}
99+
}
100+
101+
}
102+
103+
@Test
104+
public void formFlatteningTest_DefaultAppearanceGeneration_Rot180() throws IOException, InterruptedException {
105+
String srcFilePattern = "FormFlatteningDefaultAppearance_180.0_";
106+
String destPattern = "FormFlatteningDefaultAppearance_180.0_";
107+
108+
for(float i = 0; i<360;i+=90){
109+
String src = sourceFolder + srcFilePattern + i+".pdf";
110+
String dest = destinationFolder + destPattern + i +"_flattened.pdf";
111+
String cmp = sourceFolder + "cmp_" + srcFilePattern + i + ".pdf";
112+
PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
113+
114+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
115+
for(PdfFormField field: form.getFormFields().values()){
116+
field.setValue("Test");
117+
}
118+
form.flattenFields();
119+
120+
doc.close();
121+
122+
CompareTool compareTool = new CompareTool();
123+
String errorMessage = compareTool.compareByContent(dest, cmp, destinationFolder, "diff_");
124+
if (errorMessage != null) {
125+
Assert.fail(errorMessage);
126+
}
127+
}
128+
129+
}
130+
131+
@Test
132+
public void formFlatteningTest_DefaultAppearanceGeneration_Rot270() throws IOException, InterruptedException {
133+
String srcFilePattern = "FormFlatteningDefaultAppearance_270.0_";
134+
String destPattern = "FormFlatteningDefaultAppearance_270.0_";
135+
136+
for(float i = 0; i<360;i+=90){
137+
String src = sourceFolder + srcFilePattern + i+".pdf";
138+
String dest = destinationFolder + destPattern + i +"_flattened.pdf";
139+
String cmp = sourceFolder + "cmp_" + srcFilePattern + i + ".pdf";
140+
PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
141+
142+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
143+
for(PdfFormField field: form.getFormFields().values()){
144+
field.setValue("Test");
145+
}
146+
form.flattenFields();
147+
148+
doc.close();
149+
150+
CompareTool compareTool = new CompareTool();
151+
String errorMessage = compareTool.compareByContent(dest, cmp, destinationFolder, "diff_");
152+
if (errorMessage != null) {
153+
Assert.fail(errorMessage);
154+
}
155+
}
156+
157+
}
45158
}

0 commit comments

Comments
 (0)