/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.acroform; import org.icepdf.core.pobjects.*; import org.icepdf.core.pobjects.acroform.signature.exceptions.SignatureIntegrityException; import org.icepdf.core.pobjects.annotations.SignatureWidgetAnnotation; import org.icepdf.core.util.Library; import org.icepdf.core.util.Utils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.logging.Logger; /** * An interactive form (PDF 1.2)—sometimes referred to as an AcroForm is a * collection of fields for gathering information interactively from the user. * A PDF document may contain any number of fields appearing on any combination * of pages, all of which make up a single, global interactive form spanning * the entire document. * <br> * Each field in a document’s interactive form shall be defined by a field * dictionary (see 12.7.3, "Field Dictionaries"). For purposes of definition and * naming, the fields can be organized hierarchically and can inherit attributes * from their ancestors in the field hierarchy. A field’s children in the hierarchy * may also include widget annotations (see 12.5.6.19, "Widget Annotations") that * define its appearance on the page. A field that has children that are fields * is called a non-terminal field. A field that does not have children that are * fields is called a terminal field. * <br> * The contents and properties of a document’s interactive form shall be defined * by an interactive form dictionary that shall be referenced from the AcroForm * entry in the document catalogue (see 7.7.2, "Document Catalog"). * * @since 5.1 */ public class InteractiveForm extends Dictionary { /** * (Required) An array of references to the document’s root fields(those with * no ancestors in the field hierarchy). */ public static final Name FIELDS_KEY = new Name("Fields"); /** * (Optional) A flag specifying whether to construct appearance streams and * appearance dictionaries for all widget annotations in the document (see * 12.7.3.3, "Variable Text"). Default value: false. */ public static final Name NEEDS_APPEARANCES_KEY = new Name("NeedAppearances"); /** * (Optional; PDF 1.3) A set of flags specifying various document-level * characteristics related to signature fields (see Table 219, and 12.7.4.5, * "Signature Fields"). Default value: 0. */ public static final Name SIG_FLAGS_KEY = new Name("SigFlags"); /** * (Required if any fields in the document have additional-actions * dictionaries containing a C entry; PDF 1.3) An array of indirect * references to field dictionaries with calculation actions, defining the * calculation order in which their values will be recalculated when the * value of any field changes (see 12.6.3, "Trigger Events"). */ public static final Name CO_KEY = new Name("CO"); /** * (Optional) A resource dictionary (see 7.8.3, "Resource Dictionaries") * containing default resources (such as fonts, patterns, or colour spaces) * that shall be used by form field appearance streams. At a minimum, this * dictionary shall contain a Font entry specifying the resource name and * font dictionary of the default font for displaying text. */ public static final Name DR_KEY = new Name("DR"); /** * (Optional) A document-wide default value for the DA attribute of variable * text fields (see 12.7.3.3, "Variable Text"). */ public static final Name DA_KEY = new Name("DA"); /** * (Optional) A document-wide default value for the Q attribute of variable * text fields (see 12.7.3.3, "Variable Text"). */ public static final Name Q_KEY = new Name("Q"); /** * If set, the document contains at least one signature field. This flag * allows a conforming reader to enable user interface items (such as menu * items or pushbuttons) related to signature processing without having to * scan the entire document for the presence of signature fields. */ public static final int SIG_FLAGS_SIGNATURES_EXIST = 1; /** * If set, the document contains signatures that may be invalidated if the * file is saved (written) in a way that alters its previous contents, as * opposed to an incremental update. Merely updating the file by appending * new information to the end of the previous version is safe * (see H.7, "Updating Example"). Conforming readers may use this flag to * inform a user requesting a full save that signatures will be invalidated * and require explicit confirmation before continuing with the operation. */ public static final int SIG_FLAGS_APPEND_ONLY = 2; private static final Logger logger = Logger.getLogger(InteractiveForm.class.toString()); // field list, we keep reference as we don't want these garbage collected. private ArrayList<Object> fields; // A flag specifying whether to construct appearance streams and appearance dictionaries for all // widget annotations in the document (see 12.7.3.3, "Variable Text"). Default value: false. private boolean needAppearances; // A set of flags specifying various document-level characteristics related to signature fields . private int sigFlags; // An array of indirect references to field dictionaries with calculation actions, defining the calculation order in // which their values will be recalculated when the value of any field changes private List<Reference> calculationOrder; // A resource dictionary containing default resources (such as fonts, patterns, or colour spaces) that shall be used // by form field appearance streams. private Resources resources; // A document-wide default value for the DA attribute of variable text fields private String defaultVariableTextDAField; // A document-wide default value for the Q attribute of variable text fields // 0 - left-justified, 1 Centered, 2 right-justified. private int defaultVariableTextQField; // todo XFA entry stream or array processing. // important to test for data import reasons. public InteractiveForm(Library library, HashMap entries) { super(library, entries); } @SuppressWarnings("unchecked") public void init() { // load the resources needAppearances = library.getBoolean(entries, NEEDS_APPEARANCES_KEY); // sig flags. Object tmp = library.getObject(entries, SIG_FLAGS_KEY); if (tmp instanceof HashMap) { sigFlags = library.getInt(entries, SIG_FLAGS_KEY); } // load the resources tmp = library.getObject(entries, DR_KEY); if (tmp instanceof HashMap) { resources = library.getResources(entries, DR_KEY); } // load the resources, useful for rebuilding form elements. tmp = library.getObject(entries, SIG_FLAGS_KEY); if (tmp instanceof HashMap) { resources = library.getResources(entries, DR_KEY); } // get the calculation order array. tmp = library.getObject(entries, CO_KEY); if (tmp instanceof List) { calculationOrder = library.getArray(entries, CO_KEY); } tmp = library.getObject(entries, Q_KEY); if (tmp instanceof List) { defaultVariableTextQField = library.getInt(entries, Q_KEY); } // load the default appearance. tmp = library.getObject(entries, DA_KEY); if (tmp instanceof StringObject) { defaultVariableTextDAField = Utils.convertStringObject(library, (StringObject) tmp); } // get the fields in the document, keeping hierarchy intact. tmp = library.getObject(entries, FIELDS_KEY); if (tmp instanceof List) { List tmpFields = (List) tmp; fields = new ArrayList(tmpFields.size()); Object annotObj; for (Object fieldRef : tmpFields) { if (fieldRef instanceof Reference) { // add them all as we find them. annotObj = library.getObject((Reference) fieldRef); if (annotObj instanceof HashMap) { annotObj = FieldDictionaryFactory.buildField(library, (HashMap) annotObj); } if (annotObj != null) { fields.add(annotObj); } } } } } /** * Gets the fields associated with this form. * * @return array of fields. */ public ArrayList<Object> getFields() { return fields; } /** * Gets the signature fields associated with this form. A new array that references the forms signature annotations. * If no fields are found an empty list is returned. * * @return a list of form signature objects. */ public ArrayList<SignatureWidgetAnnotation> getSignatureFields() { // capture the document signatures. ArrayList<SignatureWidgetAnnotation> signatures = new ArrayList<SignatureWidgetAnnotation>(); if (fields != null) { for (Object field : fields) { if (field instanceof SignatureWidgetAnnotation) { signatures.add((SignatureWidgetAnnotation) field); } } } return signatures; } /** * Test the byte range of the signature in this form to see if they cover the document in it's entirety. This * should to be confused with validating a signature this just indicates that there are bytes that have been * written to the file that aren't covered by one of the documents signature. * * @return true if signatures cover the length of the document or false if the signatures don't dover the document * or there are no signatures. */ public boolean isSignaturesCoverDocumentLength() { SignatureWidgetAnnotation signatureWidgetAnnotation; try { if (fields != null) { boolean isValidByteRange = false; for (Object field : fields) { if (field instanceof SignatureWidgetAnnotation) { signatureWidgetAnnotation = (SignatureWidgetAnnotation) field; if (signatureWidgetAnnotation.getSignatureValidator() != null && signatureWidgetAnnotation.getSignatureValidator().checkByteRange()) { isValidByteRange = true; break; } } } if (isValidByteRange) { for (Object field : fields) { if (field instanceof SignatureWidgetAnnotation) { signatureWidgetAnnotation = (SignatureWidgetAnnotation) field; signatureWidgetAnnotation.getSignatureValidator().setSignaturesCoverDocumentLength(true); } } } } } catch (SignatureIntegrityException e) { logger.warning("Signature validation error has occurred"); } return false; } /** * Checks to see if the fields list contains any signature anntoations. * * @return true if there are any signatures, otherwise false. */ public boolean isSignatureFields() { boolean foundSignature = false; ArrayList<Object> fields = getFields(); if (fields != null) { for (Object field : fields) { if (field instanceof SignatureWidgetAnnotation) { foundSignature = true; break; } } } return foundSignature; } /** * A set of flags specifying various document-level characteristics related to signature fields. It should * be noted that this filed is not used very often and {@link #isSignatureFields} should be used instead. * * @return true if enabled, false otherwise. */ public boolean signatureExists() { return ((sigFlags & SIG_FLAGS_SIGNATURES_EXIST) == SIG_FLAGS_SIGNATURES_EXIST); } /** * If set, the document contains signatures that may be invalidated if the * file is saved (written) in a way that alters its previous contents, as * opposed to an incremental update * * @return true if enabled, false otherwise. */ public boolean signatureAppendOnly() { return ((sigFlags & SIG_FLAGS_APPEND_ONLY) == SIG_FLAGS_APPEND_ONLY); } /** * A flag specifying whether to construct appearance streams and appearance dictionaries for all * widget annotations in the document. Default value: false. * * @return true if appearance streams need to be generated, otherwise false. */ public boolean needAppearances() { return needAppearances; } /** * Gets a list of indirect references to fields that have calculation actions, defining the calculation * order in which values should be calculated when the value of any field changes. * * @return list of indirect references if present, otherwise null; */ public List<Reference> getCalculationOrder() { return calculationOrder; } /** * Get the resources associated with the child widgets. * * @return common resources to all child widgets. Can be null. */ public Resources getResources() { return resources; } /** * Gest the default variable text DA entry which can be helpful for rebuilding field data appearance streams. * * @return default da values, null if not present. */ public String getDefaultVariableTextDAField() { return defaultVariableTextDAField; } /** * Ges the default variable text quadding rule. * * @return integer represented by VariableTextFieldDictionary.QUADDING_LEFT_JUSTIFIED, VariableTextFieldDictionary.QUADDING_CENTERED or * VariableTextFieldDictionary.QUADDING_RIGHT_JUSTIFIED. */ public int getDefaultVariableTextQField() { return defaultVariableTextQField; } }