/* * 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.annotations.AbstractWidgetAnnotation; 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; /** * Each field in a document’s interactive form shall be defined by a field * dictionary, which shall be an indirect object. The field dictionaries may be * organized hierarchically into one or more tree structures. Many field * attributes are inheritable, meaning that if they are not explicitly specified * for a given field, their values are taken from those of its parent in the field * hierarchy. Such inheritable attributes shall be designated as such in the * Tables 220 and 221. The designation (Required; inheritable) means that an * attribute shall be defined for every field, whether explicitly in its own * field dictionary or by inheritance from an ancestor in the hierarchy. Table * 220 shows those entries that are common to all field dictionaries, regardless * of type. Entries that pertain only to a particular type of field are described * in the relevant sub-clauses in Table 220. * * @since 5.0 */ public class FieldDictionary extends Dictionary { private static final Logger logger = Logger.getLogger(FieldDictionary.class.toString()); /** * Required for terminal fields; inheritable) The type of field that this * dictionary describes: * <br> * Button - Button (see 12.7.4.2, "Button Fields") * <br> * Text - Text (see 12.7.4.3, "Text Fields") * <br> * Choice - Choice (see 12.7.4.4, "Choice Fields") * <br> * Signature(PDF 1.3) - Signature (see 12.7.4.5, "Signature Fields") * <br> * This entry may be present in a non-terminal field (one whose descendants * are fields) to provide an inheritable FT value. However, a non-terminal * field does not logically have a type of its own; it is merely a container * for inheritable attributes that are intended for descendant terminal * fields of any type. */ public static final Name FT_KEY = new Name("FT"); /** * (Sometimes required, as described below) An array of indirect references * to the immediate children of this field. * <br> * In a non-terminal field, the Kids array shall refer to field dictionaries * that are immediate descendants of this field. In a terminal field, the Kids * array ordinarily shall refer to one or more separate widget annotations that * are associated with this field. However, if there is only one associated * widget annotation, and its contents have been merged into the field dictionary, * Kids shall be omitted. */ public static final Name KIDS_KEY = new Name("Kids"); /** * (Required if this field is the child of another in the field hierarchy; * absent otherwise) The field that is the immediate parent of this one (the * field, if any, whose Kids array includes this field). A field can have at * most one parent; that is, it can be included in the Kids array of at most * one other field. */ public static final Name PARENT_KEY = new Name("Parent"); /** * (Optional) The partial field name (see 12.7.3.2, "Field Names"). */ public static final Name T_KEY = new Name("T"); /** * (Optional; PDF 1.3) An alternate field name that shall be used in place * of the actual field name wherever the field shall be identified in the * user interface (such as in error or status messages referring to the field). * This text is also useful when extracting the document’s contents in support * of accessibility to users with disabilities or for other purposes * (see 14.9.3, "Alternate Descriptions"). */ public static final Name TU_KEY = new Name("TU"); /** * (Optional; PDF 1.3) The mapping name that shall be used when exporting * interactive form field data from the document. */ public static final Name TM_KEY = new Name("TM"); /** * (Optional; inheritable) A set of flags specifying various characteristics * of the field (see Table 221). Default value: 0. */ public static final Name Ff_KEY = new Name("Ff"); /** * (Optional; inheritable) The field’s value, whose format varies depending * on the field type. See the descriptions of individual field types for * further information. */ public static final Name V_KEY = new Name("V"); /** * (Optional; inheritable) The default value to which the field reverts when * a reset-form action is executed (see 12.7.5.3, "Reset-Form Action"). The * format of this value is the same as that of V. */ public static final Name DV_KEY = new Name("DV"); /** * (Optional; PDF 1.2) An additional-actions dictionary defining the field’s * behaviour in response to various trigger events (see 12.6.3, "Trigger Events"). * This entry has exactly the same meaning as the AA entry in an annotation * dictionary (see 12.5.2, "Annotation Dictionaries"). */ public static final Name AA_KEY = new Name("AA"); /** general field flags **/ /** * If set, the user may not change the value of the field. Any associated * widget annotations will not interact with the user; that is, they will * not respond to mouse clicks or change their appearance in response to mouse * motions. This flag is useful for fields whose values are computed or * imported from a database. */ public static final int READ_ONLY_BIT_FLAG = 0x1; /** * If set, the field shall have a value at the time it is exported by a * submit-form action (see 12.7.5.2, "Submit-Form Action"). */ public static final int REQUIRED_BIT_FLAG = 0x2; /** * If set, the field shall not be exported by a submit-form action (see 12.7.5.2, "Submit-Form Action"). */ public static final int NO_EXPORT_BIT_FLAG = 0x4; protected Name fieldType; protected FieldDictionary parentField; protected ArrayList<Object> kids; protected String partialFieldName; protected String alternativeFieldName; protected String exportMappingName; private int flags; protected Object fieldValue; protected Object defaultFieldValue; protected AdditionalActionsDictionary additionalActionsDictionary; @SuppressWarnings("unchecked") public FieldDictionary(Library library, HashMap entries) { super(library, entries); // field name Object value = library.getObject(entries, T_KEY); if (value != null && value instanceof StringObject) { StringObject text = (StringObject) value; partialFieldName = Utils.convertStringObject(library, text); } else if (value instanceof String) { partialFieldName = (String) value; } // alternate field name. value = library.getObject(entries, TU_KEY); if (value != null && value instanceof StringObject) { StringObject text = (StringObject) value; alternativeFieldName = Utils.convertStringObject(library, text); } else if (value instanceof String) { alternativeFieldName = (String) value; } // mapping name for data export. value = library.getObject(entries, TM_KEY); if (value != null && value instanceof StringObject) { StringObject text = (StringObject) value; exportMappingName = Utils.convertStringObject(library, text); } else if (value instanceof String) { exportMappingName = (String) value; } // value field // getFieldValue(); // todo default value, see 12.7.5.3, Reset-Form Action. value = library.getObject(entries, DV_KEY); if (value != null) { defaultFieldValue = value; } value = library.getObject(entries, AA_KEY); if (value != null && value instanceof HashMap) { additionalActionsDictionary = new AdditionalActionsDictionary(library, (HashMap)value); } } /** * Gets the kids of of terminal and non terminal fields. For non-terminal this * can be a mix of field data and widget annotations. And for terminal notes * the array will only contain widget annotations. * * @return list of child elements. */ @SuppressWarnings("unchecked") public ArrayList<Object> getKids() { // find some kids. if (kids == null) { Object value = library.getObject(entries, KIDS_KEY); if (value != null && value instanceof List) { List<Reference> children = (List<Reference>) value; kids = new ArrayList(children.size()); Object tmp; for (Reference aChildren : children) { tmp = library.getObject(aChildren); // have a deeper structure, shouldn't happen though or at least no examples yet. if (tmp instanceof PObject){ tmp = ((PObject)tmp).getObject(); } if (tmp instanceof HashMap) { kids.add(FieldDictionaryFactory.buildField(library, (HashMap) tmp)); } else if (tmp instanceof AbstractWidgetAnnotation) { kids.add(tmp); } } } } return kids; } public FieldDictionary getParent() { // parent field there is an intermediary parent field dictionary define, not present // if the widget annotation and dictionary have been flattened. if (parentField == null) { Object value = library.getObject(entries, PARENT_KEY); if (value instanceof HashMap) { parentField = FieldDictionaryFactory.buildField(library, (HashMap) value); if (parentField != null) { parentField.setPObjectReference((Reference) entries.get(PARENT_KEY)); } } } return parentField; } public Name getFieldType() { // get teh field type. if (fieldType == null) { Object value = library.getName(entries, FT_KEY); if (value != null) { fieldType = (Name) value; } else { if (getParent() != null) { fieldType = parentField.getFieldType(); } } } return fieldType; } public int getFlags() { // behaviour flags flags = library.getInt(entries, Ff_KEY); // check parent for flags value. if (flags == 0) { FieldDictionary parent = getParent(); if (parent != null) { flags = parent.getFlags(); } } return flags; } public String getPartialFieldName() { return partialFieldName; } public String getAlternativeFieldName() { return alternativeFieldName; } public String getExportMappingName() { return exportMappingName; } /** * If set, the user may not change the value of the field. Any associated widget annotations will not interact with * the user; that is, they will not respond to mouse clicks or change their appearance in response to mouse motions. * This flag is useful for fields whose values are computed or imported from a database. * * @return true if read only, otherwise false. */ public boolean isReadOnly() { return ((flags & READ_ONLY_BIT_FLAG) == READ_ONLY_BIT_FLAG); } /** * If set, the field shall have a value at the time it is exported by a submit-form action * * @return true if value is required, otherwise false. */ public boolean isRequired() { return ((flags & REQUIRED_BIT_FLAG) == REQUIRED_BIT_FLAG); } /** * If set, the field shall not be exported by a submit-form action. * * @return true if no value should be exported, otherwise false. */ public boolean isNoExport() { return ((flags & NO_EXPORT_BIT_FLAG) == NO_EXPORT_BIT_FLAG); } /** * The T entry in the field dictionary (see Table 220) holds a text string defining the field’s partial field name. * The fully qualified field name is not explicitly defined but shall be constructed from the partial field names * of the field and all of its ancestors. * <br> * This method will climb back up the inheritance tree to build the name. * * @return fully quality name of the field. */ public String getFullyQualifiedFieldName() { String qualifiedFieldName = partialFieldName; if (parentField != null) { return parentField.getFullyQualifiedFieldName().concat(".").concat(partialFieldName); } return qualifiedFieldName; } public Object getFieldValue() { Object value = library.getObject(entries, V_KEY); if (value instanceof Name) { fieldValue = value; } else if (value instanceof StringObject) { StringObject text = (StringObject) value; fieldValue = Utils.convertStringObject(library, text); } else if (value instanceof String) { fieldValue = value; } else { fieldValue = ""; } return fieldValue; } public boolean hasFieldValue(){ return entries.containsKey(V_KEY); } public boolean hasDefaultValue(){ return entries.containsKey(DV_KEY); } /** * Set the value field of the field dictionary (/V). The value can be anything but special attention is given * to Strings especially if the document is already encrypted. We need to stored String in an Encrypted environment * as encrypted so that we can write it correctly later. * * @param fieldValue value to write. * @param parentReference parent reference. */ public void setFieldValue(Object fieldValue, Reference parentReference) { this.fieldValue = fieldValue; if (fieldValue instanceof String) { // make sure we store an encrypted documents string as encrypted setString(V_KEY, (String) fieldValue); } else { entries.put(V_KEY, fieldValue); } } public Object getDefaultFieldValue() { return defaultFieldValue; } public AdditionalActionsDictionary getAdditionalActionsDictionary() { return additionalActionsDictionary; } }