Skip to content

Commit c62a9ec

Browse files
committed
Do not fail if the appearance dictionary doesn't contain N entry.
DEVSIX-2902
1 parent e94e494 commit c62a9ec

File tree

6 files changed

+63
-39
lines changed

6 files changed

+63
-39
lines changed

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

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -676,51 +676,56 @@ public void flattenFields() {
676676
appDic = fieldObject.getAsDictionary(PdfName.AP);
677677
}
678678
}
679-
if (appDic != null) {
679+
if (null != appDic) {
680680
PdfObject normal = appDic.get(PdfName.N);
681-
PdfFormXObject xObject = null;
682-
if (normal.isStream()) {
683-
xObject = new PdfFormXObject((PdfStream) normal);
684-
} else if (normal.isDictionary()) {
685-
PdfName as = fieldObject.getAsName(PdfName.AS);
686-
if (((PdfDictionary) normal).getAsStream(as) != null) {
687-
xObject = new PdfFormXObject(((PdfDictionary) normal).getAsStream(as));
688-
xObject.makeIndirect(document);
681+
if (null == normal) {
682+
Logger logger = LoggerFactory.getLogger(PdfAcroForm.class);
683+
logger.error(LogMessageConstant.N_ENTRY_IS_REQUIRED_FOR_APPEARANCE_DICTIONARY);
684+
} else {
685+
PdfFormXObject xObject = null;
686+
if (normal.isStream()) {
687+
xObject = new PdfFormXObject((PdfStream) normal);
688+
} else if (normal.isDictionary()) {
689+
PdfName as = fieldObject.getAsName(PdfName.AS);
690+
if (((PdfDictionary) normal).getAsStream(as) != null) {
691+
xObject = new PdfFormXObject(((PdfDictionary) normal).getAsStream(as));
692+
xObject.makeIndirect(document);
693+
}
689694
}
690-
}
691695

692-
if (xObject != null) {
693-
//subtype is required field for FormXObject, but can be omitted in normal appearance.
694-
xObject.put(PdfName.Subtype, PdfName.Form);
695-
Rectangle annotBBox = fieldObject.getAsRectangle(PdfName.Rect);
696-
if (page.isFlushed()) {
697-
throw new PdfException(PdfException.PageAlreadyFlushedUseAddFieldAppearanceToPageMethodBeforePageFlushing);
698-
}
699-
PdfCanvas canvas = new PdfCanvas(page, !wrappedPages.contains(page));
700-
wrappedPages.add(page);
701-
702-
// Here we avoid circular reference which might occur when page resources and the appearance xObject's
703-
// resources are the same object
704-
PdfObject xObjectResources = xObject.getPdfObject().get(PdfName.Resources);
705-
PdfObject pageResources = page.getResources().getPdfObject();
706-
if (xObjectResources != null && pageResources != null &&
707-
xObjectResources == pageResources) {
708-
xObject.getPdfObject().put(PdfName.Resources, initialPageResourceClones.get(document.getPageNumber(page)));
709-
}
696+
if (xObject != null) {
697+
//subtype is required field for FormXObject, but can be omitted in normal appearance.
698+
xObject.put(PdfName.Subtype, PdfName.Form);
699+
Rectangle annotBBox = fieldObject.getAsRectangle(PdfName.Rect);
700+
if (page.isFlushed()) {
701+
throw new PdfException(PdfException.PageAlreadyFlushedUseAddFieldAppearanceToPageMethodBeforePageFlushing);
702+
}
703+
PdfCanvas canvas = new PdfCanvas(page, !wrappedPages.contains(page));
704+
wrappedPages.add(page);
705+
706+
// Here we avoid circular reference which might occur when page resources and the appearance xObject's
707+
// resources are the same object
708+
PdfObject xObjectResources = xObject.getPdfObject().get(PdfName.Resources);
709+
PdfObject pageResources = page.getResources().getPdfObject();
710+
if (xObjectResources != null && pageResources != null &&
711+
xObjectResources == pageResources) {
712+
xObject.getPdfObject().put(PdfName.Resources, initialPageResourceClones.get(document.getPageNumber(page)));
713+
}
710714

711-
if (tagPointer != null) {
712-
tagPointer.setPageForTagging(page);
713-
TagReference tagRef = tagPointer.getTagReference();
714-
canvas.openTag(tagRef);
715-
}
715+
if (tagPointer != null) {
716+
tagPointer.setPageForTagging(page);
717+
TagReference tagRef = tagPointer.getTagReference();
718+
canvas.openTag(tagRef);
719+
}
716720

717-
AffineTransform at = calcFieldAppTransformToAnnotRect(xObject, annotBBox);
718-
float[] m = new float[6];
719-
at.getMatrix(m);
720-
canvas.addXObject(xObject, m[0], m[1], m[2], m[3], m[4], m[5]);
721+
AffineTransform at = calcFieldAppTransformToAnnotRect(xObject, annotBBox);
722+
float[] m = new float[6];
723+
at.getMatrix(m);
724+
canvas.addXObject(xObject, m[0], m[1], m[2], m[3], m[4], m[5]);
721725

722-
if (tagPointer != null) {
723-
canvas.closeTag();
726+
if (tagPointer != null) {
727+
canvas.closeTag();
728+
}
724729
}
725730
}
726731
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ This file is part of the iText (R) project.
4242
*/
4343
package com.itextpdf.forms;
4444

45+
import com.itextpdf.io.LogMessageConstant;
4546
import com.itextpdf.kernel.pdf.PdfDocument;
4647
import com.itextpdf.kernel.pdf.PdfReader;
4748
import com.itextpdf.kernel.pdf.PdfWriter;
4849
import com.itextpdf.kernel.utils.CompareTool;
4950
import com.itextpdf.test.ExtendedITextTest;
51+
import com.itextpdf.test.annotations.LogMessage;
52+
import com.itextpdf.test.annotations.LogMessages;
5053
import com.itextpdf.test.annotations.type.IntegrationTest;
5154
import org.junit.Assert;
5255
import org.junit.BeforeClass;
@@ -79,4 +82,19 @@ public void formFlatteningTestWithAPWithoutSubtype() throws IOException, Interru
7982
Assert.assertNull(new CompareTool().compareByContent(dest, cmp, destinationFolder, "diff_"));
8083
}
8184

85+
@Test
86+
@LogMessages(messages = @LogMessage(messageTemplate = LogMessageConstant.N_ENTRY_IS_REQUIRED_FOR_APPEARANCE_DICTIONARY))
87+
public void formFlatteningTestWithoutNEntry() throws IOException, InterruptedException {
88+
String filename = "formFlatteningTestWithoutNEntry";
89+
String src = sourceFolder + filename + ".pdf";
90+
String dest = destinationFolder + filename + "_flattened.pdf";
91+
String cmp = sourceFolder + "cmp_" + filename + "_flattened.pdf";
92+
PdfDocument doc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
93+
94+
PdfAcroForm.getAcroForm(doc, false).flattenFields();
95+
doc.close();
96+
97+
Assert.assertNull(new CompareTool().compareByContent(dest, cmp, destinationFolder, "diff_"));
98+
}
99+
82100
}

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public final class LogMessageConstant {
125125
public static final String MAPPING_IN_STRUCT_ROOT_OVERWRITTEN = "Existing mapping for {0} in structure tree root role map was {1} and it was overwritten with {2}.";
126126
public static final String METHOD_IS_NOT_IMPLEMENTED_BY_DEFAULT_OTHER_METHOD_WILL_BE_USED = "Method {0} is not implemented by default: please, override and implement it. {1} will be used instead.";
127127
public static final String NAME_ALREADY_EXISTS_IN_THE_NAME_TREE = "Name \"{0}\" already exists in the name tree; old value will be replaced by the new one.";
128+
public static final String N_ENTRY_IS_REQUIRED_FOR_APPEARANCE_DICTIONARY = "\\N entry is required to be present in an appearance dictionary.";
128129
public static final String NOT_TAGGED_PAGES_IN_TAGGED_DOCUMENT = "Not tagged pages are copied to the tagged document. Destination document now may contain not tagged content.";
129130
public static final String NO_FIELDS_IN_ACROFORM = "Required AcroForm entry /Fields does not exist in the document. Empty array /Fields will be created.";
130131
public static final String NUM_TREE_SHALL_NOT_END_WITH_KEY = "Number tree ends with a key which is invalid according to the PDF specification.";

0 commit comments

Comments
 (0)