Skip to content

Commit 3f04567

Browse files
committed
Create a copy for form field with name contains dot symbol
DEVSIX-2187
1 parent 5b04a61 commit 3f04567

File tree

6 files changed

+190
-94
lines changed

6 files changed

+190
-94
lines changed

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

Lines changed: 160 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,30 @@ This file is part of the iText (R) project.
4545

4646
import com.itextpdf.forms.fields.PdfFormField;
4747
import com.itextpdf.io.LogMessageConstant;
48-
import com.itextpdf.kernel.pdf.*;
48+
import com.itextpdf.io.util.MessageFormatUtil;
49+
import com.itextpdf.kernel.pdf.IPdfPageExtraCopier;
50+
import com.itextpdf.kernel.pdf.PdfArray;
51+
import com.itextpdf.kernel.pdf.PdfDictionary;
52+
import com.itextpdf.kernel.pdf.PdfDocument;
53+
import com.itextpdf.kernel.pdf.PdfName;
54+
import com.itextpdf.kernel.pdf.PdfObject;
55+
import com.itextpdf.kernel.pdf.PdfPage;
56+
import com.itextpdf.kernel.pdf.PdfString;
4957
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
5058
import org.slf4j.Logger;
5159
import org.slf4j.LoggerFactory;
5260

53-
import com.itextpdf.io.util.MessageFormatUtil;
54-
import java.util.*;
61+
import java.util.ArrayList;
62+
import java.util.HashSet;
63+
import java.util.List;
64+
import java.util.Map;
65+
import java.util.Set;
5566

5667
/**
5768
* A sample implementation of the {#link IPdfPageExtraCopier} interface which
5869
* copies only AcroForm fields to a new page.
59-
*
60-
*
70+
* <p>
71+
* <p>
6172
* NOTE: While it's absolutely not necessary to use the same PdfPageFormCopier instance for copying operations,
6273
* it is still worth to know that PdfPageFormCopier uses some caching logic which can potentially improve performance
6374
* in case of the reusing of the same instance.
@@ -80,87 +91,110 @@ public void copy(PdfPage fromPage, PdfPage toPage) {
8091
documentTo = toPage.getDocument();
8192
formTo = PdfAcroForm.getAcroForm(documentTo, true);
8293
}
83-
if (formFrom != null) {
84-
//duplicate AcroForm dictionary
85-
List<PdfName> excludedKeys = new ArrayList<>();
86-
excludedKeys.add(PdfName.Fields);
87-
excludedKeys.add(PdfName.DR);
88-
PdfDictionary dict = formFrom.getPdfObject().copyTo(documentTo, excludedKeys, false);
89-
formTo.getPdfObject().mergeDifferent(dict);
94+
95+
if (formFrom == null) {
96+
return;
9097
}
9198

92-
if (formFrom != null) {
93-
Map<String, PdfFormField> fieldsFrom = formFrom.getFormFields();
94-
if (fieldsFrom.size() > 0) {
95-
Map<String, PdfFormField> fieldsTo = formTo.getFormFields();
96-
List<PdfAnnotation> annots = toPage.getAnnotations();
97-
for (PdfAnnotation annot : annots) {
98-
if (annot.getSubtype().equals(PdfName.Widget)) {
99-
PdfDictionary parent = annot.getPdfObject().getAsDictionary(PdfName.Parent);
100-
if (parent != null) {
101-
PdfFormField parentField = getParentField(parent, documentTo);
102-
PdfString parentName = parentField.getFieldName();
103-
if (parentName == null) {
104-
continue;
105-
}
106-
if (!fieldsTo.containsKey(parentName.toUnicodeString())) {
107-
PdfFormField field = createParentFieldCopy(annot.getPdfObject(), documentTo);
108-
PdfArray kids = field.getKids();
109-
field.getPdfObject().remove(PdfName.Kids);
110-
formTo.addField(field, toPage);
111-
field.getPdfObject().put(PdfName.Kids, kids);
112-
} else {
113-
PdfFormField field = PdfFormField.makeFormField(annot.getPdfObject(), documentTo);
114-
PdfString fieldName = field.getFieldName();
115-
if (fieldName != null) {
116-
PdfFormField existingField = fieldsTo.get(fieldName.toUnicodeString());
117-
if (existingField != null) {
118-
PdfFormField clonedField = PdfFormField.makeFormField(field.getPdfObject().clone().makeIndirect(documentTo), documentTo);
119-
toPage.getPdfObject().getAsArray(PdfName.Annots).add(clonedField.getPdfObject());
120-
toPage.removeAnnotation(annot);
121-
mergeFieldsWithTheSameName(clonedField);
122-
} else {
123-
HashSet<String> existingFields = new HashSet<>();
124-
getAllFieldNames(formTo.getFields(), existingFields);
125-
addChildToExistingParent(annot.getPdfObject(), existingFields);
126-
}
127-
} else {
128-
if (!parentField.getKids().contains(field.getPdfObject())) {
129-
HashSet<String> existingFields = new HashSet<>();
130-
getAllFieldNames(formTo.getFields(), existingFields);
131-
addChildToExistingParent(annot.getPdfObject(), existingFields);
132-
}
133-
}
134-
}
135-
} else {
136-
PdfString annotName = annot.getPdfObject().getAsString(PdfName.T);
137-
String annotNameString = null;
138-
if (annotName != null) {
139-
annotNameString = annotName.toUnicodeString();
140-
}
141-
if (annotNameString != null && fieldsFrom.containsKey(annotNameString)) {
142-
PdfFormField field = fieldsTo.get(annotNameString);
143-
if (field != null) {
144-
PdfDictionary clonedAnnot = (PdfDictionary) annot.getPdfObject().clone().makeIndirect(documentTo);
145-
toPage.getPdfObject().getAsArray(PdfName.Annots).add(clonedAnnot);
146-
toPage.removeAnnotation(annot);
147-
field = mergeFieldsWithTheSameName(PdfFormField.makeFormField(clonedAnnot, toPage.getDocument()));
148-
149-
logger.warn(MessageFormatUtil.format(LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD, annotNameString));
150-
PdfArray kids = field.getKids();
151-
if (kids != null) {
152-
field.getPdfObject().remove(PdfName.Kids);
153-
formTo.addField(field, toPage);
154-
field.getPdfObject().put(PdfName.Kids, kids);
155-
} else {
156-
formTo.addField(field, toPage);
157-
}
158-
} else {
159-
formTo.addField(PdfFormField.makeFormField(annot.getPdfObject(), documentTo), null);
160-
}
161-
}
162-
}
163-
}
99+
//duplicate AcroForm dictionary
100+
List<PdfName> excludedKeys = new ArrayList<>();
101+
excludedKeys.add(PdfName.Fields);
102+
excludedKeys.add(PdfName.DR);
103+
104+
PdfDictionary dict = formFrom.getPdfObject().copyTo(documentTo, excludedKeys, false);
105+
formTo.getPdfObject().mergeDifferent(dict);
106+
107+
Map<String, PdfFormField> fieldsFrom = formFrom.getFormFields();
108+
if (fieldsFrom.size() <= 0) {
109+
return;
110+
}
111+
Map<String, PdfFormField> fieldsTo = formTo.getFormFields();
112+
113+
List<PdfAnnotation> annots = toPage.getAnnotations();
114+
115+
for (PdfAnnotation annot : annots) {
116+
if (!annot.getSubtype().equals(PdfName.Widget)) {
117+
continue;
118+
}
119+
copyField(toPage, fieldsFrom, fieldsTo, annot);
120+
}
121+
}
122+
123+
private void copyField(PdfPage toPage, Map<String, PdfFormField> fieldsFrom,
124+
Map<String, PdfFormField> fieldsTo, PdfAnnotation currentAnnot) {
125+
PdfDictionary parent = currentAnnot.getPdfObject().getAsDictionary(PdfName.Parent);
126+
if (parent != null) {
127+
PdfFormField parentField = getParentField(parent, documentTo);
128+
PdfString parentName = parentField.getFieldName();
129+
if (parentName == null) {
130+
return;
131+
}
132+
copyParentFormField(toPage, fieldsTo, currentAnnot, parentField);
133+
} else {
134+
PdfString annotName = currentAnnot.getPdfObject().getAsString(PdfName.T);
135+
String annotNameString = null;
136+
if (annotName != null) {
137+
annotNameString = annotName.toUnicodeString();
138+
}
139+
if (annotNameString != null && fieldsFrom.containsKey(annotNameString)) {
140+
PdfFormField field = fieldsTo.get(annotNameString);
141+
if (field == null) {
142+
formTo.addField(PdfFormField.makeFormField(currentAnnot.getPdfObject(), documentTo), null);
143+
} else {
144+
copyExistingField(toPage, currentAnnot, annotNameString);
145+
}
146+
}
147+
}
148+
}
149+
150+
private void copyExistingField(PdfPage toPage, PdfAnnotation currentAnnot, String annotNameString) {
151+
PdfFormField field;
152+
PdfDictionary clonedAnnot = (PdfDictionary) currentAnnot.getPdfObject().clone().makeIndirect(documentTo);
153+
toPage.getPdfObject().getAsArray(PdfName.Annots).add(clonedAnnot);
154+
toPage.removeAnnotation(currentAnnot);
155+
field = mergeFieldsWithTheSameName(PdfFormField.makeFormField(clonedAnnot, toPage.getDocument()));
156+
157+
logger.warn(MessageFormatUtil.format(LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD, annotNameString));
158+
PdfArray kids = field.getKids();
159+
if (kids != null) {
160+
field.getPdfObject().remove(PdfName.Kids);
161+
formTo.addField(field, toPage);
162+
field.getPdfObject().put(PdfName.Kids, kids);
163+
} else {
164+
formTo.addField(field, toPage);
165+
}
166+
}
167+
168+
private void copyParentFormField(PdfPage toPage, Map<String, PdfFormField> fieldsTo,
169+
PdfAnnotation annot, PdfFormField parentField) {
170+
PdfString parentName = parentField.getFieldName();
171+
if (!fieldsTo.containsKey(parentName.toUnicodeString())) {
172+
PdfFormField field = createParentFieldCopy(annot.getPdfObject(), documentTo);
173+
PdfArray kids = field.getKids();
174+
field.getPdfObject().remove(PdfName.Kids);
175+
formTo.addField(field, toPage);
176+
field.getPdfObject().put(PdfName.Kids, kids);
177+
} else {
178+
PdfFormField field = PdfFormField.makeFormField(annot.getPdfObject(), documentTo);
179+
PdfString fieldName = field.getFieldName();
180+
if (fieldName != null) {
181+
PdfFormField existingField = fieldsTo.get(fieldName.toUnicodeString());
182+
if (existingField != null) {
183+
PdfFormField clonedField = PdfFormField.makeFormField(field.getPdfObject().clone().makeIndirect(documentTo), documentTo);
184+
toPage.getPdfObject().getAsArray(PdfName.Annots).add(clonedField.getPdfObject());
185+
toPage.removeAnnotation(annot);
186+
mergeFieldsWithTheSameName(clonedField);
187+
} else {
188+
HashSet<String> existingFields = new HashSet<>();
189+
getAllFieldNames(formTo.getFields(), existingFields);
190+
addChildToExistingParent(annot.getPdfObject(), existingFields,
191+
fieldsTo, toPage, annot);
192+
}
193+
} else {
194+
if (!parentField.getKids().contains(field.getPdfObject())) {
195+
HashSet<String> existingFields = new HashSet<>();
196+
getAllFieldNames(formTo.getFields(), existingFields);
197+
addChildToExistingParent(annot.getPdfObject(), existingFields);
164198
}
165199
}
166200
}
@@ -177,7 +211,7 @@ private PdfFormField mergeFieldsWithTheSameName(PdfFormField newField) {
177211
index++;
178212
newField.setFieldName(fieldName.toUnicodeString() + "_#" + index);
179213
fullFieldName = newField.getFieldName().toUnicodeString();
180-
} while(formTo.getField(fullFieldName) != null);
214+
} while (formTo.getField(fullFieldName) != null);
181215
return newField;
182216
}
183217
newField.getPdfObject().remove(PdfName.T);
@@ -217,7 +251,6 @@ private PdfFormField mergeFieldsWithTheSameName(PdfFormField newField) {
217251
if (value != null) {
218252
mergedField.put(PdfName.V, existingField.getPdfObject().get(PdfName.V));
219253
}
220-
221254
return mergedField;
222255
}
223256

@@ -233,21 +266,28 @@ private PdfFormField getParentField(PdfDictionary parent, PdfDocument pdfDoc) {
233266
}
234267

235268
private PdfFormField createParentFieldCopy(PdfDictionary fieldDic, PdfDocument pdfDoc) {
236-
fieldDic.remove(PdfName.Kids);
237-
238269
PdfDictionary parent = fieldDic.getAsDictionary(PdfName.Parent);
239270
PdfFormField field = PdfFormField.makeFormField(fieldDic, pdfDoc);
240271

241272
if (parent != null) {
242273
field = createParentFieldCopy(parent, pdfDoc);
243-
parent.put(PdfName.Kids, new PdfArray(fieldDic));
274+
PdfArray kids = (PdfArray) parent.get(PdfName.Kids);
275+
if (kids == null) {
276+
parent.put(PdfName.Kids, new PdfArray(fieldDic));
277+
} else {
278+
kids.add(fieldDic);
279+
}
244280
}
245281

246282
return field;
247283
}
248284

249285
private void addChildToExistingParent(PdfDictionary fieldDic, Set<String> existingFields) {
250286
PdfDictionary parent = fieldDic.getAsDictionary(PdfName.Parent);
287+
if (parent == null) {
288+
return;
289+
}
290+
251291
PdfString parentName = parent.getAsString(PdfName.T);
252292
if (parentName != null) {
253293
String name = parentName.toUnicodeString();
@@ -261,6 +301,37 @@ private void addChildToExistingParent(PdfDictionary fieldDic, Set<String> existi
261301
}
262302
}
263303

304+
private void addChildToExistingParent(PdfDictionary fieldDic, Set<String> existingFields,
305+
Map<String, PdfFormField> fieldsTo,
306+
PdfPage toPage, PdfAnnotation annot) {
307+
PdfDictionary parent = fieldDic.getAsDictionary(PdfName.Parent);
308+
if (parent == null) {
309+
return;
310+
}
311+
312+
PdfString parentName = parent.getAsString(PdfName.T);
313+
if (parentName != null) {
314+
String name = parentName.toUnicodeString();
315+
if (existingFields.contains(name)) {
316+
PdfArray kids = parent.getAsArray(PdfName.Kids);
317+
for (PdfObject kid : kids) {
318+
if (((PdfDictionary) kid).get(PdfName.T).equals(fieldDic.get(PdfName.T))) {
319+
PdfFormField kidField = PdfFormField.makeFormField(kid, documentTo);
320+
fieldsTo.put(kidField.getFieldName().toUnicodeString(), kidField);
321+
logger.warn(MessageFormatUtil.format(LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD,
322+
kidField.getFieldName().toUnicodeString()));
323+
mergeFieldsWithTheSameName(PdfFormField.makeFormField(fieldDic, documentTo));
324+
return;
325+
}
326+
}
327+
kids.add(fieldDic);
328+
} else {
329+
parent.put(PdfName.Kids, new PdfArray(fieldDic));
330+
addChildToExistingParent(parent, existingFields);
331+
}
332+
}
333+
}
334+
264335
private void getAllFieldNames(PdfArray fields, Set<String> existingFields) {
265336
for (PdfObject field : fields) {
266337
if (field.isFlushed()) {

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public void copyFieldsTest05() throws IOException, InterruptedException {
195195

196196
@Test
197197
@LogMessages(messages = {
198-
@LogMessage(messageTemplate = LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD, count = 2)
198+
@LogMessage(messageTemplate = LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD, count = 4)
199199
})
200200
public void copyMultipleSubfieldsTest01() throws IOException, InterruptedException {
201201
String srcFilename = sourceFolder + "copyMultipleSubfieldsTest01.pdf";
@@ -210,7 +210,7 @@ public void copyMultipleSubfieldsTest01() throws IOException, InterruptedExcepti
210210
srcDoc.copyPagesTo(1, 1, destDoc, pdfPageFormCopier);
211211
}
212212

213-
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(destDoc,false);
213+
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(destDoc, false);
214214

215215
acroForm.getField("text_1").setValue("Text 1!");
216216
acroForm.getField("text_2").setValue("Text 2!");
@@ -387,6 +387,33 @@ public void copyFieldsTest12() throws IOException, InterruptedException {
387387
Assert.assertNull(new CompareTool().compareByContent(destFilename, sourceFolder + "cmp_copyFields12.pdf", destinationFolder, "diff_"));
388388
}
389389

390+
@Test
391+
@LogMessages(messages = {
392+
@LogMessage(messageTemplate = LogMessageConstant.DOCUMENT_ALREADY_HAS_FIELD, count = 1)
393+
})
394+
public void copyFieldsTest13() throws IOException, InterruptedException {
395+
String srcFilename = sourceFolder + "copyFields13.pdf";
396+
String destFilename = destinationFolder + "copyFields13.pdf";
397+
398+
PdfDocument srcDoc = new PdfDocument(new PdfReader(srcFilename));
399+
PdfDocument destDoc = new PdfDocument(new PdfWriter(destFilename));
400+
401+
PdfPageFormCopier pdfPageFormCopier = new PdfPageFormCopier();
402+
403+
for (int i = 0; i < 1; ++i) {
404+
srcDoc.copyPagesTo(1, 1, destDoc, pdfPageFormCopier);
405+
}
406+
407+
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(destDoc, false);
408+
409+
acroForm.getField("text").setValue("Text!");
410+
411+
destDoc.close();
412+
srcDoc.close();
413+
414+
Assert.assertNull(new CompareTool().compareByContent(destFilename, sourceFolder + "cmp_copyFields13.pdf", destinationFolder, "diff_"));
415+
}
416+
390417

391418
@Test
392419
public void copyPagesWithInheritedResources() throws IOException, InterruptedException {

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfPage.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,10 +1306,8 @@ private void rebuildFormFieldParent(PdfDictionary field, PdfDictionary newField,
13061306

13071307
PdfArray kids = newParent.getAsArray(PdfName.Kids);
13081308
if (kids == null) {
1309-
kids = new PdfArray();
1310-
newParent.put(PdfName.Kids, kids);
1309+
newParent.put(PdfName.Kids, new PdfArray());
13111310
}
1312-
kids.add(newField);
13131311
newField.put(PdfName.Parent, newParent);
13141312
}
13151313
}

0 commit comments

Comments
 (0)