Skip to content

Commit 8af5aef

Browse files
committed
Support "Forms" rules for PDF/UA-2
DEVSIX-9022
1 parent 5acab7c commit 8af5aef

File tree

16 files changed

+1189
-93
lines changed

16 files changed

+1189
-93
lines changed

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,27 @@ public static PdfName getFormType(PdfDictionary fieldDict) {
333333
return formType;
334334
}
335335

336+
/**
337+
* Retrieves string value from {@link PdfObject} representing text string or text stream.
338+
*
339+
* @param value {@link PdfObject} representing text string or text stream
340+
*
341+
* @return {@link String} value
342+
*/
343+
public static String getStringValue(PdfObject value) {
344+
if (value == null) {
345+
return "";
346+
} else if (value instanceof PdfStream) {
347+
return new String(((PdfStream) value).getBytes(), StandardCharsets.UTF_8);
348+
} else if (value instanceof PdfName) {
349+
return ((PdfName) value).getValue();
350+
} else if (value instanceof PdfString) {
351+
return ((PdfString) value).toUnicodeString();
352+
} else {
353+
return "";
354+
}
355+
}
356+
336357
/**
337358
* Returns the type of the parent form field, or of the wrapped
338359
* <PdfDictionary> object.
@@ -775,17 +796,7 @@ public PdfObject getValue() {
775796
*/
776797
public String getValueAsString() {
777798
PdfObject value = getValue();
778-
if (value == null) {
779-
return "";
780-
} else if (value instanceof PdfStream) {
781-
return new String(((PdfStream) value).getBytes(), StandardCharsets.UTF_8);
782-
} else if (value instanceof PdfName) {
783-
return ((PdfName) value).getValue();
784-
} else if (value instanceof PdfString) {
785-
return ((PdfString) value).toUnicodeString();
786-
} else {
787-
return "";
788-
}
799+
return getStringValue(value);
789800
}
790801

791802
/**
@@ -1044,10 +1055,11 @@ public PdfObject getRichText() {
10441055

10451056
/**
10461057
* Sets a rich text string, as described in "Rich Text Strings" section of Pdf spec.
1047-
* May be either {@link PdfStream} or {@link PdfString}.
1058+
* It may be either {@link PdfStream} or {@link PdfString}.
10481059
*
1049-
* @param richText a new rich text value.
1050-
* @return the edited {@link PdfFormField}.
1060+
* @param richText a new rich text value
1061+
*
1062+
* @return the edited {@link PdfFormField}
10511063
*/
10521064
public PdfFormField setRichText(PdfObject richText) {
10531065
put(PdfName.RV, richText);

io/src/main/java/com/itextpdf/io/util/XmlUtil.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ This file is part of the iText (R) project.
2626
// Android-Conversion-Replace import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
2727
// Android-Conversion-Replace import org.apache.xerces.jaxp.SAXParserFactoryImpl;
2828
import org.w3c.dom.Document;
29+
import org.xml.sax.SAXException;
2930

3031
import javax.xml.parsers.DocumentBuilderFactory;
3132
import javax.xml.parsers.ParserConfigurationException;
3233
import javax.xml.parsers.SAXParserFactory;
34+
import java.io.IOException;
35+
import java.io.InputStream;
3336

3437
/**
3538
* This file is a helper class for internal usage only.
@@ -70,4 +73,20 @@ public static Document initNewXmlDocument() throws ParserConfigurationException
7073
return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
7174
}
7275

76+
/**
77+
* This method creates new Xml document from input stream.
78+
*
79+
* @param inputStream to parse
80+
*
81+
* @return parsed Xml document
82+
*
83+
* @throws ParserConfigurationException if an error occurs while creating the document
84+
* @throws SAXException if any parse errors occur
85+
* @throws IOException if any IO errors occur
86+
*/
87+
public static Document initXmlDocument(InputStream inputStream)
88+
throws ParserConfigurationException, IOException, SAXException {
89+
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);
90+
}
91+
7392
}

kernel/src/main/java/com/itextpdf/kernel/utils/checkers/PdfCheckersUtil.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ 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.pdf.PdfArray;
2829
import com.itextpdf.kernel.pdf.PdfConformance;
2930
import com.itextpdf.kernel.pdf.PdfDictionary;
3031
import com.itextpdf.kernel.pdf.PdfName;
32+
import com.itextpdf.kernel.pdf.PdfObject;
3133
import com.itextpdf.kernel.pdf.PdfStream;
3234
import com.itextpdf.kernel.xmp.XMPConst;
3335
import com.itextpdf.kernel.xmp.XMPException;
@@ -111,6 +113,25 @@ public static void checkMetadata(PdfDictionary catalog, PdfConformance conforman
111113
}
112114
}
113115

116+
/**
117+
* Gets all the descending kids including widgets for a given {@link PdfArray} representing array of form fields.
118+
*
119+
* @param array the {@link PdfArray} of form fields {@link PdfDictionary} objects
120+
*
121+
* @return the {@link PdfArray} of all form fields
122+
*/
123+
public static PdfArray getFormFields(PdfArray array) {
124+
PdfArray fields = new PdfArray();
125+
for (PdfObject field : array) {
126+
PdfArray kids = ((PdfDictionary) field).getAsArray(PdfName.Kids);
127+
fields.add(field);
128+
if (kids != null) {
129+
fields.addAll(getFormFields(kids));
130+
}
131+
}
132+
return fields;
133+
}
134+
114135
/**
115136
* Validates {@code pdfuaid:rev} value which is four-digit year of the date of publication or revision.
116137
*

pdfa/src/main/java/com/itextpdf/pdfa/checker/PdfA1Checker.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ This file is part of the iText (R) project.
5353
import com.itextpdf.kernel.pdf.colorspace.PdfPattern;
5454
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs;
5555
import com.itextpdf.kernel.utils.checkers.FontCheckUtil;
56+
import com.itextpdf.kernel.utils.checkers.PdfCheckersUtil;
5657
import com.itextpdf.pdfa.exceptions.PdfAConformanceException;
5758
import com.itextpdf.pdfa.exceptions.PdfaExceptionMessageConstant;
5859
import com.itextpdf.pdfa.logs.PdfAConformanceLogMessageConstant;
60+
import org.slf4j.Logger;
61+
import org.slf4j.LoggerFactory;
5962

6063
import java.io.IOException;
6164
import java.util.ArrayList;
@@ -64,8 +67,6 @@ This file is part of the iText (R) project.
6467
import java.util.HashSet;
6568
import java.util.List;
6669
import java.util.Set;
67-
import org.slf4j.Logger;
68-
import org.slf4j.LoggerFactory;
6970

7071
/**
7172
* PdfA1Checker defines the requirements of the PDF/A-1 standard and contains
@@ -704,7 +705,7 @@ protected void checkForm(PdfDictionary form) {
704705

705706
PdfArray fields = form.getAsArray(PdfName.Fields);
706707
if (fields != null) {
707-
fields = getFormFields(fields);
708+
fields = PdfCheckersUtil.getFormFields(fields);
708709
for (PdfObject field : fields) {
709710
PdfDictionary fieldDic = (PdfDictionary) field;
710711
if (fieldDic.containsKey(PdfName.A) || fieldDic.containsKey(PdfName.AA)) {
@@ -785,7 +786,10 @@ protected void checkTrailer(PdfDictionary trailer) {
785786
* @param array the {@link PdfArray} of form fields {@link PdfDictionary} objects
786787
*
787788
* @return the {@link PdfArray} of form fields
789+
*
790+
* @deprecated in favour of {@link PdfCheckersUtil#getFormFields(PdfArray)}
788791
*/
792+
@Deprecated
789793
protected PdfArray getFormFields(PdfArray array) {
790794
PdfArray fields = new PdfArray();
791795
for (PdfObject field : array) {

pdfa/src/main/java/com/itextpdf/pdfa/checker/PdfA2Checker.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ This file is part of the iText (R) project.
5353
import com.itextpdf.kernel.pdf.colorspace.PdfPattern;
5454
import com.itextpdf.kernel.pdf.colorspace.PdfSpecialCs;
5555
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
56+
import com.itextpdf.kernel.utils.checkers.PdfCheckersUtil;
5657
import com.itextpdf.pdfa.exceptions.PdfAConformanceException;
5758
import com.itextpdf.pdfa.exceptions.PdfaExceptionMessageConstant;
5859
import com.itextpdf.pdfa.logs.PdfAConformanceLogMessageConstant;
@@ -583,7 +584,7 @@ protected void checkForm(PdfDictionary form) {
583584

584585
PdfArray fields = form.getAsArray(PdfName.Fields);
585586
if (fields != null) {
586-
fields = getFormFields(fields);
587+
fields = PdfCheckersUtil.getFormFields(fields);
587588
for (PdfObject field : fields) {
588589
PdfDictionary fieldDic = (PdfDictionary) field;
589590
checkResources(fieldDic.getAsDictionary(PdfName.DR), fieldDic);

pdfua/src/main/java/com/itextpdf/pdfua/checkers/PdfUA1Checker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ This file is part of the iText (R) project.
5454
import com.itextpdf.kernel.xmp.XMPMeta;
5555
import com.itextpdf.layout.validation.context.LayoutValidationContext;
5656
import com.itextpdf.pdfua.checkers.utils.AnnotationCheckUtil;
57-
import com.itextpdf.pdfua.checkers.utils.FormCheckUtil;
5857
import com.itextpdf.pdfua.checkers.utils.GraphicsCheckUtil;
5958
import com.itextpdf.pdfua.checkers.utils.LayoutCheckUtil;
6059
import com.itextpdf.pdfua.checkers.utils.PdfUAValidationContext;
6160
import com.itextpdf.pdfua.checkers.utils.tables.TableCheckUtil;
61+
import com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1FormChecker;
6262
import com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1FormulaChecker;
6363
import com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1HeadingsChecker;
6464
import com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1ListChecker;
@@ -271,7 +271,7 @@ private void checkStructureTreeRoot(PdfStructTreeRoot structTreeRoot) {
271271
tagTreeIterator.addHandler(new PdfUA1HeadingsChecker.PdfUA1HeadingHandler(context));
272272
tagTreeIterator.addHandler(new TableCheckUtil.TableHandler(context));
273273
tagTreeIterator.addHandler(new AnnotationCheckUtil.AnnotationHandler(context));
274-
tagTreeIterator.addHandler(new FormCheckUtil.FormTagHandler(context));
274+
tagTreeIterator.addHandler(new PdfUA1FormChecker.PdfUA1FormTagHandler(context));
275275
tagTreeIterator.addHandler(new PdfUA1ListChecker.PdfUA1ListHandler(context));
276276
tagTreeIterator.traverse();
277277
}

pdfua/src/main/java/com/itextpdf/pdfua/checkers/PdfUA2Checker.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ This file is part of the iText (R) project.
4747
import com.itextpdf.pdfua.checkers.utils.LayoutCheckUtil;
4848
import com.itextpdf.pdfua.checkers.utils.PdfUAValidationContext;
4949
import com.itextpdf.pdfua.checkers.utils.tables.TableCheckUtil;
50+
import com.itextpdf.pdfua.checkers.utils.ua2.PdfUA2FormChecker;
5051
import com.itextpdf.pdfua.checkers.utils.ua2.PdfUA2FormulaChecker;
5152
import com.itextpdf.pdfua.checkers.utils.ua2.PdfUA2HeadingsChecker;
5253
import com.itextpdf.pdfua.checkers.utils.ua2.PdfUA2ListChecker;
@@ -158,6 +159,9 @@ private void checkCatalog(PdfCatalog catalog) {
158159
checkMetadata(catalog);
159160
checkViewerPreferences(catalog);
160161
checkOCProperties(catalog.getPdfObject().getAsDictionary(PdfName.OCProperties));
162+
PdfUA2FormChecker formChecker = new PdfUA2FormChecker(context);
163+
formChecker.checkFormFields(catalog.getPdfObject().getAsDictionary(PdfName.AcroForm));
164+
formChecker.checkWidgetAnnotations(this.pdfDocument);
161165
}
162166

163167
/**
@@ -209,6 +213,7 @@ private void checkStructureTreeRoot(PdfStructTreeRoot structTreeRoot) {
209213
tagTreeIterator.addHandler(new PdfUA2HeadingsChecker.PdfUA2HeadingHandler(context));
210214
tagTreeIterator.addHandler(new TableCheckUtil.TableHandler(context));
211215
// TODO DEVSIX-9016 Support PDF/UA-2 rules for annotation types
216+
tagTreeIterator.addHandler(new PdfUA2FormChecker.PdfUA2FormTagHandler(context));
212217
tagTreeIterator.addHandler(new PdfUA2ListChecker.PdfUA2ListHandler(context));
213218
tagTreeIterator.addHandler(new PdfUA2NotesChecker.PdfUA2NotesHandler(context));
214219
tagTreeIterator.addHandler(new PdfUA2TableOfContentsChecker.PdfUA2TableOfContentsHandler(context));

pdfua/src/main/java/com/itextpdf/pdfua/checkers/utils/FormCheckUtil.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ This file is part of the iText (R) project.
2626
import com.itextpdf.kernel.pdf.PdfName;
2727
import com.itextpdf.kernel.pdf.tagging.IStructureNode;
2828
import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
29+
import com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1FormChecker;
2930
import com.itextpdf.pdfua.exceptions.PdfUAConformanceException;
3031
import com.itextpdf.pdfua.exceptions.PdfUAExceptionMessageConstants;
3132

3233
/**
3334
* Class that provides methods for checking PDF/UA compliance of interactive form fields.
35+
*
36+
* @deprecated in favour of {@link PdfUA1FormChecker}
3437
*/
38+
@Deprecated
3539
public class FormCheckUtil {
3640

3741
/**
@@ -43,11 +47,14 @@ private FormCheckUtil() {
4347

4448
/**
4549
* Handler for checking form field elements in the tag tree.
50+
*
51+
* @deprecated in favour of {@link PdfUA1FormChecker.PdfUA1FormTagHandler}
4652
*/
53+
@Deprecated
4754
public static class FormTagHandler extends ContextAwareTagTreeIteratorHandler {
4855

4956
/**
50-
* Creates a new {@link FormulaCheckUtil.FormulaTagHandler} instance.
57+
* Creates a new {@link FormCheckUtil.FormTagHandler} instance.
5158
*
5259
* @param context The validation context.
5360
*/

pdfua/src/main/java/com/itextpdf/pdfua/checkers/utils/NoteCheckUtil.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ This file is part of the iText (R) project.
3838
public class NoteCheckUtil {
3939
/**
4040
* Handler for checking Note elements in the TagTree.
41+
*
42+
* @deprecated in favor of {@link com.itextpdf.pdfua.checkers.utils.ua1.PdfUA1NotesChecker.PdfUA1NotesTagHandler}
4143
*/
44+
@Deprecated
4245
public static class NoteTagHandler extends ContextAwareTagTreeIteratorHandler {
4346

4447
/**

pdfua/src/main/java/com/itextpdf/pdfua/checkers/utils/PdfUAValidationContext.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.pdfua.checkers.utils;
2424

25+
import com.itextpdf.kernel.pdf.PdfDictionary;
2526
import com.itextpdf.kernel.pdf.PdfDocument;
2627
import com.itextpdf.kernel.pdf.PdfName;
2728
import com.itextpdf.kernel.pdf.PdfUAConformance;
2829
import com.itextpdf.kernel.pdf.tagging.IStructureNode;
2930
import com.itextpdf.kernel.pdf.tagging.PdfNamespace;
31+
import com.itextpdf.kernel.pdf.tagging.PdfObjRef;
3032
import com.itextpdf.kernel.pdf.tagging.PdfStructElem;
3133
import com.itextpdf.kernel.pdf.tagutils.IRoleMappingResolver;
3234

@@ -124,6 +126,18 @@ public PdfStructElem getElementIfRoleMatches(PdfName role, IStructureNode struct
124126
return null;
125127
}
126128

129+
/**
130+
* Retrieves object reference instance by provided structure parent index.
131+
*
132+
* @param i index of the structure parent
133+
* @param pageDict {@link PdfDictionary} of the page that {@link PdfObjRef} belong to
134+
*
135+
* @return {@link PdfObjRef} instance
136+
*/
137+
public PdfObjRef findObjRefByStructParentIndex(int i, PdfDictionary pageDict) {
138+
return pdfDocument.getStructTreeRoot().findObjRefByStructParentIndex(pageDict, i);
139+
}
140+
127141
/**
128142
* Retrieves the PDF/UA conformance of the {@link PdfDocument}.
129143
*

0 commit comments

Comments
 (0)