org.apache.pdfbox.pdmodel.interactive.form.PDField Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdfbox Show documentation
Show all versions of pdfbox Show documentation
The Apache PDFBox library is an open source Java tool for working with PDF documents.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.pdfbox.pdmodel.interactive.form;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
import org.apache.pdfbox.pdmodel.common.COSObjectable;
import org.apache.pdfbox.pdmodel.common.PDTextStream;
import org.apache.pdfbox.pdmodel.fdf.FDFField;
import org.apache.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.util.BitFlagHelper;
/**
* This is the superclass for a Field element in a PDF. Based on the COS object model from PDFBox.
*
* @author sug
*
*/
public abstract class PDField implements COSObjectable
{
/**
* A Ff flag.
*/
public static final int FLAG_READ_ONLY = 1;
/**
* A Ff flag.
*/
public static final int FLAG_REQUIRED = 1 << 1;
/**
* A Ff flag.
*/
public static final int FLAG_NO_EXPORT = 1 << 2;
private PDAcroForm acroForm;
private COSDictionary dictionary;
/**
* Constructor.
*
* @param theAcroForm The form that this field is part of.
*/
public PDField(PDAcroForm theAcroForm)
{
acroForm = theAcroForm;
dictionary = new COSDictionary();
// no required fields in base field class
}
/**
* Creates a COSField from a COSDictionary, expected to be a correct object definition for a field in PDF.
*
* @param theAcroForm The form that this field is part of.
* @param field the PDF objet to represent as a field.
*/
public PDField(PDAcroForm theAcroForm, COSDictionary field)
{
acroForm = theAcroForm;
dictionary = field;
}
/**
* Returns the partial name of the field.
*
* @return the name of the field
*/
public String getPartialName()
{
return getDictionary().getString(COSName.T);
}
/**
* This will set the partial name of the field.
*
* @param name The new name for the field.
*/
public void setPartialName(String name)
{
getDictionary().setString(COSName.T, name);
}
/**
* Returns the fully qualified name of the field, which is a concatenation of the names of all the parents fields.
*
* @return the name of the field
*
* @throws IOException If there is an error generating the fully qualified name.
*/
public String getFullyQualifiedName() throws IOException
{
PDField parent = getParent();
String parentName = null;
if (parent != null)
{
parentName = parent.getFullyQualifiedName();
}
String finalName = getPartialName();
if (parentName != null)
{
if (finalName != null)
{
finalName = parentName + "." + finalName;
}
else
{
finalName = parentName;
}
}
return finalName;
}
/**
* Gets the alternate name of the field.
*
* @return the alternate name of the field
*/
public String getAlternateFieldName()
{
return this.getDictionary().getString(COSName.TU);
}
/**
* This will set the alternate name of the field.
*
* @param alternateFieldName the alternate name of the field
*/
public void setAlternateFieldName(String alternateFieldName)
{
this.getDictionary().setString(COSName.TU, alternateFieldName);
}
/**
* Get the FT entry of the field. This is a read only field and is set depending on the actual type. The field type
* is an inheritable attribute. This method will return only the direct value on this object. Use the findFieldType
* for an upward recursive search.
*
* @return The Field type.
*
* @see PDField#findFieldType()
*/
public String getFieldType()
{
return getDictionary().getNameAsString(COSName.FT);
}
/**
* Find the field type and optionally do a recursive upward search. Sometimes the fieldtype will be specified on the
* parent instead of the direct object. This will look at this object for the field type, if none is specified then
* it will look to the parent if there is a parent. If there is no parent and no field type has been found then this
* will return null.
*
* @return The field type or null if none was found.
*/
public String findFieldType()
{
return findFieldType(getDictionary());
}
private String findFieldType(COSDictionary dic)
{
String retval = dic.getNameAsString(COSName.FT);
if (retval == null)
{
COSDictionary parent = (COSDictionary) dic.getDictionaryObject(COSName.PARENT, COSName.P);
if (parent != null)
{
retval = findFieldType(parent);
}
}
return retval;
}
/**
* setValue sets the fields value to a given string.
*
* @param value the string value
*
* @throws IOException If there is an error creating the appearance stream.
*/
public abstract void setValue(String value) throws IOException;
/**
* getValue gets the fields value to as a string.
*
* @return The string value of this field.
*
* @throws IOException If there is an error getting the value.
*/
public abstract String getValue() throws IOException;
/**
* sets the field to be read-only.
*
* @param readonly The new flag for readonly.
*/
public void setReadonly(boolean readonly)
{
BitFlagHelper.setFlag(getDictionary(), COSName.FF, FLAG_READ_ONLY, readonly);
}
/**
*
* @return true if the field is readonly
*/
public boolean isReadonly()
{
return BitFlagHelper.getFlag(getDictionary(), COSName.FF, FLAG_READ_ONLY);
}
/**
* sets the flag whether the field is to be required to have a value at the time it is exported
* by a submit-form action.
*
* @param required The new flag for required.
*/
public void setRequired(boolean required)
{
BitFlagHelper.setFlag(getDictionary(), COSName.FF, FLAG_REQUIRED, required);
}
/**
* @return true if the field is required to have a value at the time it is exported by a
* submit-form action.
*/
public boolean isRequired()
{
return BitFlagHelper.getFlag(getDictionary(), COSName.FF, FLAG_REQUIRED);
}
/**
* sets the field to be not exported..
*
* @param noExport The new flag for noExport.
*/
public void setNoExport(boolean noExport)
{
BitFlagHelper.setFlag(getDictionary(), COSName.FF, FLAG_NO_EXPORT, noExport);
}
/**
*
* @return true if the field is not to be exported.
*/
public boolean isNoExport()
{
return BitFlagHelper.getFlag(getDictionary(), COSName.FF, FLAG_NO_EXPORT);
}
/**
* This will get the flags for this field.
*
* @return flags The set of flags.
*/
public int getFieldFlags()
{
int retval = 0;
COSInteger ff = (COSInteger) getDictionary().getDictionaryObject(COSName.FF);
if (ff != null)
{
retval = ff.intValue();
}
return retval;
}
/**
* This will set the flags for this field.
*
* @param flags The new flags.
*/
public void setFieldFlags(int flags)
{
getDictionary().setInt(COSName.FF, flags);
}
/**
* This will import a fdf field from a fdf document.
*
* @param fdfField The fdf field to import.
*
* @throws IOException If there is an error importing the data for this field.
*/
public void importFDF(FDFField fdfField) throws IOException
{
Object fieldValue = fdfField.getValue();
int fieldFlags = getFieldFlags();
if (fieldValue != null)
{
if (fieldValue instanceof String)
{
setValue((String) fieldValue);
}
else if (fieldValue instanceof PDTextStream)
{
setValue(((PDTextStream) fieldValue).getAsString());
}
else
{
throw new IOException("Unknown field type:" + fieldValue.getClass().getName());
}
}
Integer ff = fdfField.getFieldFlags();
if (ff != null)
{
setFieldFlags(ff.intValue());
}
else
{
// these are suppose to be ignored if the Ff is set.
Integer setFf = fdfField.getSetFieldFlags();
if (setFf != null)
{
int setFfInt = setFf.intValue();
fieldFlags = fieldFlags | setFfInt;
setFieldFlags(fieldFlags);
}
Integer clrFf = fdfField.getClearFieldFlags();
if (clrFf != null)
{
// we have to clear the bits of the document fields for every bit that is
// set in this field.
//
// Example:
// docFf = 1011
// clrFf = 1101
// clrFfValue = 0010;
// newValue = 1011 & 0010 which is 0010
int clrFfValue = clrFf.intValue();
clrFfValue ^= 0xFFFFFFFF;
fieldFlags = fieldFlags & clrFfValue;
setFieldFlags(fieldFlags);
}
}
PDAnnotationWidget widget = getWidget();
if (widget != null)
{
int annotFlags = widget.getAnnotationFlags();
Integer f = fdfField.getWidgetFieldFlags();
if (f != null && widget != null)
{
widget.setAnnotationFlags(f.intValue());
}
else
{
// these are suppose to be ignored if the F is set.
Integer setF = fdfField.getSetWidgetFieldFlags();
if (setF != null)
{
annotFlags = annotFlags | setF.intValue();
widget.setAnnotationFlags(annotFlags);
}
Integer clrF = fdfField.getClearWidgetFieldFlags();
if (clrF != null)
{
// we have to clear the bits of the document fields for every bit that is
// set in this field.
//
// Example:
// docF = 1011
// clrF = 1101
// clrFValue = 0010;
// newValue = 1011 & 0010 which is 0010
int clrFValue = clrF.intValue();
clrFValue ^= 0xFFFFFFFFL;
annotFlags = annotFlags & clrFValue;
widget.setAnnotationFlags(annotFlags);
}
}
}
List fdfKids = fdfField.getKids();
List pdKids = getKids();
for (int i = 0; fdfKids != null && i < fdfKids.size(); i++)
{
FDFField fdfChild = fdfKids.get(i);
String fdfName = fdfChild.getPartialFieldName();
for (int j = 0; j < pdKids.size(); j++)
{
Object pdChildObj = pdKids.get(j);
if (pdChildObj instanceof PDField)
{
PDField pdChild = (PDField) pdChildObj;
if (fdfName != null && fdfName.equals(pdChild.getPartialName()))
{
pdChild.importFDF(fdfChild);
}
}
}
}
}
/**
* This will get the single associated widget that is part of this field. This occurs when the Widget is
* embedded in the fields dictionary. Sometimes there are multiple sub widgets associated with this field,
* in which case you want to use {@link #getWidgets()}. If the kids entry is specified, then only first
* entry in that list will be returned.
*
* @return The widget that is associated with this field.
* @throws IOException If there is an error getting the widget object.
*/
public PDAnnotationWidget getWidget() throws IOException
{
PDAnnotationWidget retval = null;
List kids = getKids();
if (kids == null)
{
retval = new PDAnnotationWidget(getDictionary());
}
else if (kids.size() > 0)
{
Object firstKid = kids.get(0);
if (firstKid instanceof PDAnnotationWidget)
{
retval = (PDAnnotationWidget) firstKid;
}
else
{
retval = ((PDField) firstKid).getWidget();
}
}
else
{
retval = null;
}
return retval;
}
/**
* Returns the widget annotations associated with this field.
*
* The widget annotations are returned for terminal fields (fields which
* do not have other fields as kids). In case of non terminal fields an
* empty List is returned.
*
* @return The list of widget annotations.
*/
public List getWidgets()
{
List widgets = new ArrayList();
COSArray kids = (COSArray) getDictionary().getDictionaryObject(COSName.KIDS);
if (kids == null)
{
// the field itself is a widget
widgets.add(new PDAnnotationWidget(getDictionary()));
}
else
{
for (int i = 0; i < kids.size(); i++)
{
COSDictionary kidDictionary = (COSDictionary) kids.getObject(i);
if (kidDictionary == null)
{
continue;
}
if ("Widget".equals(kidDictionary.getNameAsString(COSName.SUBTYPE)))
{
widgets.add(new PDAnnotationWidget(kidDictionary));
}
}
}
return widgets;
}
/**
* Get the parent field to this field, or null if none exists.
*
* @return The parent field.
*
* @throws IOException If there is an error creating the parent field.
*/
public PDField getParent() throws IOException
{
PDField parent = null;
COSDictionary parentDic = (COSDictionary) getDictionary().getDictionaryObject(COSName.PARENT, COSName.P);
if (parentDic != null)
{
parent = PDFieldFactory.createField(getAcroForm(), parentDic);
}
return parent;
}
/**
* Set the parent of this field.
*
* @param parent The parent to this field.
*/
public void setParent(PDField parent)
{
getDictionary().setItem("Parent", parent);
}
/**
* This will find one of the child elements. The name array are the components of the name to search down the tree
* of names. The nameIndex is where to start in that array. This method is called recursively until it finds the end
* point based on the name array.
*
* @param name An array that picks the path to the field.
* @param nameIndex The index into the array.
* @return The field at the endpoint or null if none is found.
* @throws IOException If there is an error creating the field.
*/
public PDField findKid(String[] name, int nameIndex) throws IOException
{
PDField retval = null;
COSArray kids = (COSArray) getDictionary().getDictionaryObject(COSName.KIDS);
if (kids != null)
{
for (int i = 0; retval == null && i < kids.size(); i++)
{
COSDictionary kidDictionary = (COSDictionary) kids.getObject(i);
if (name[nameIndex].equals(kidDictionary.getString("T")))
{
retval = PDFieldFactory.createField(acroForm, kidDictionary);
if (name.length > nameIndex + 1)
{
retval = retval.findKid(name, nameIndex + 1);
}
}
}
}
return retval;
}
/**
* This will get all the kids of this field. The values in the list will either be PDWidget or PDField. Normally
* they will be PDWidget objects unless this is a non-terminal field and they will be child PDField objects.
*
* @return A list of either PDWidget or PDField objects.
* @throws IOException If there is an error retrieving the kids.
*/
public List getKids() throws IOException
{
List retval = null;
COSArray kids = (COSArray) getDictionary().getDictionaryObject(COSName.KIDS);
if (kids != null)
{
List kidsList = new ArrayList();
for (int i = 0; i < kids.size(); i++)
{
COSDictionary kidDictionary = (COSDictionary) kids.getObject(i);
if (kidDictionary == null)
{
continue;
}
// Decide if the kid is field or a widget annotation.
// A field dictionary that does not have a partial field name (T entry)
// of its own shall not be considered a field but simply a Widget annotation.
if (kidDictionary.getDictionaryObject(COSName.T) != null)
{
PDField field = PDFieldFactory.createField(acroForm, kidDictionary);
if (field != null)
{
kidsList.add(field);
}
}
else if ("Widget".equals(kidDictionary.getNameAsString(COSName.SUBTYPE)))
{
kidsList.add(new PDAnnotationWidget(kidDictionary));
}
}
retval = new COSArrayList(kidsList, kids);
}
return retval;
}
/**
* This will set the list of kids.
*
* @param kids The list of child widgets.
*/
public void setKids(List kids)
{
COSArray kidsArray = COSArrayList.converterToCOSArray(kids);
getDictionary().setItem(COSName.KIDS, kidsArray);
}
/**
* This will return a string representation of this field.
*
* @return A string representation of this field.
*/
@Override
public String toString()
{
return "" + getDictionary().getDictionaryObject(COSName.V);
}
/**
* This will get the acroform that this field is part of.
*
* @return The form this field is on.
*/
public PDAcroForm getAcroForm()
{
return acroForm;
}
/**
* This will set the form this field is on.
*
* @param value The new form to use.
*/
public void setAcroForm(PDAcroForm value)
{
acroForm = value;
}
/**
* This will get the dictionary associated with this field.
*
* @return The dictionary that this class wraps.
*/
public COSDictionary getDictionary()
{
return dictionary;
}
/**
* Convert this standard java object to a COS object.
*
* @return The cos object that matches this Java object.
*/
public COSBase getCOSObject()
{
return dictionary;
}
/**
* Get the additional actions for this field. This will return null if there are no additional actions for this
* field.
*
* @return The actions of the field.
*/
public PDFormFieldAdditionalActions getActions()
{
COSDictionary aa = (COSDictionary) dictionary.getDictionaryObject(COSName.AA);
PDFormFieldAdditionalActions retval = null;
if (aa != null)
{
retval = new PDFormFieldAdditionalActions(aa);
}
return retval;
}
/**
* Set the actions of the field.
*
* @param actions The field actions.
*/
public void setActions(PDFormFieldAdditionalActions actions)
{
dictionary.setItem(COSName.AA, actions);
}
/**
* Set the field type.
*
* @param fieldType the field type string must be one of "Btn", "Ch", "Tx", "Sig"
*/
protected void setFieldType(String fieldType)
{
if (fieldType.compareTo("Btn") != 0 &&
fieldType.compareTo("Ch") != 0 &&
fieldType.compareTo("Tx") != 0 &&
fieldType.compareTo("Sig") != 0
)
{
throw new IllegalArgumentException("Unknown field type given " + fieldType);
}
else
{
getDictionary().setName(COSName.FT, fieldType);
}
}
}