aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/pdfbox/pdmodel/interactive/form
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/pdfbox/pdmodel/interactive/form')
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java328
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java645
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java187
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java95
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java127
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java610
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java218
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java84
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java170
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java90
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java64
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java72
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java324
-rw-r--r--src/main/java/org/pdfbox/pdmodel/interactive/form/package.html9
14 files changed, 3023 insertions, 0 deletions
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java
new file mode 100644
index 0000000..4fb576d
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAcroForm.java
@@ -0,0 +1,328 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.fdf.FDFDictionary;
+import org.pdfbox.pdmodel.fdf.FDFDocument;
+import org.pdfbox.pdmodel.fdf.FDFCatalog;
+import org.pdfbox.pdmodel.fdf.FDFField;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents the acroform of a PDF document.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class PDAcroForm
+{
+ private COSDictionary acroForm;
+ private PDDocument document;
+
+ private Map fieldCache;
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this form is part of.
+ */
+ public PDAcroForm( PDDocument doc )
+ {
+ document = doc;
+ acroForm = new COSDictionary();
+ COSArray fields = new COSArray();
+ acroForm.setItem( COSName.getPDFName( "Fields" ), fields );
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param doc The document that this form is part of.
+ * @param form The existing acroForm.
+ */
+ public PDAcroForm( PDDocument doc, COSDictionary form )
+ {
+ document = doc;
+ acroForm = form;
+ }
+
+ /**
+ * This will get the document associated with this form.
+ *
+ * @return The PDF document.
+ */
+ public PDDocument getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * This will get the dictionary that this form wraps.
+ *
+ * @return The dictionary for this form.
+ */
+ public COSDictionary getDictionary()
+ {
+ return acroForm;
+ }
+
+ /**
+ * This method will import an entire FDF document into the PDF document
+ * that this acroform is part of.
+ *
+ * @param fdf The FDF document to import.
+ *
+ * @throws IOException If there is an error doing the import.
+ */
+ public void importFDF( FDFDocument fdf ) throws IOException
+ {
+ List fields = fdf.getCatalog().getFDF().getFields();
+ if( fields != null )
+ {
+ for( int i=0; i<fields.size(); i++ )
+ {
+ FDFField fdfField = (FDFField)fields.get( i );
+ PDField docField = getField( fdfField.getPartialFieldName() );
+ if( docField != null )
+ {
+ docField.importFDF( fdfField );
+ }
+ }
+ }
+ }
+
+ /**
+ * This will export all FDF form data.
+ *
+ * @return An FDF document used to export the document.
+ * @throws IOException If there is an error when exporting the document.
+ */
+ public FDFDocument exportFDF() throws IOException
+ {
+ FDFDocument fdf = new FDFDocument();
+ FDFCatalog catalog = fdf.getCatalog();
+ FDFDictionary fdfDict = new FDFDictionary();
+ catalog.setFDF( fdfDict );
+
+ List fdfFields = new ArrayList();
+ List fields = getFields();
+ Iterator fieldIter = fields.iterator();
+ while( fieldIter.hasNext() )
+ {
+ PDField docField = (PDField)fieldIter.next();
+ addFieldAndChildren( docField, fdfFields );
+ }
+ fdfDict.setID( document.getDocument().getDocumentID() );
+ if( fdfFields.size() > 0 )
+ {
+ fdfDict.setFields( fdfFields );
+ }
+ return fdf;
+ }
+
+ private void addFieldAndChildren( PDField docField, List fdfFields ) throws IOException
+ {
+ Object fieldValue = docField.getValue();
+ FDFField fdfField = new FDFField();
+ fdfField.setPartialFieldName( docField.getPartialName() );
+ fdfField.setValue( fieldValue );
+ List kids = docField.getKids();
+ List childFDFFields = new ArrayList();
+ if( kids != null )
+ {
+
+ for( int i=0; i<kids.size(); i++ )
+ {
+ addFieldAndChildren( (PDField)kids.get( i ), childFDFFields );
+ }
+ if( childFDFFields.size() > 0 )
+ {
+ fdfField.setKids( childFDFFields );
+ }
+ }
+ if( fieldValue != null || childFDFFields.size() > 0 )
+ {
+ fdfFields.add( fdfField );
+ }
+ }
+
+ /**
+ * This will return all of the fields in the document. The type
+ * will be a org.pdfbox.pdmodel.field.PDField.
+ *
+ * @return A list of all the fields.
+ * @throws IOException If there is an error while getting the list of fields.
+ */
+ public List getFields() throws IOException
+ {
+ COSArray fields =
+ (COSArray) acroForm.getDictionaryObject(
+ COSName.getPDFName("Fields"));
+
+ List retval = new ArrayList();
+ for (int i = 0; i < fields.size(); i++)
+ {
+ COSDictionary element = (COSDictionary) fields.getObject(i);
+ if (element != null)
+ {
+ PDField field = PDFieldFactory.createField( this, element );
+ if( field != null )
+ {
+ retval.add(field);
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will tell this form to cache the fields into a Map structure
+ * for fast access via the getField method. The default is false. You would
+ * want this to be false if you were changing the COSDictionary behind the scenes,
+ * otherwise setting this to true is acceptable.
+ *
+ * @param cache A boolean telling if we should cache the fields.
+ * @throws IOException If there is an error while caching the fields.
+ */
+ public void setCacheFields( boolean cache ) throws IOException
+ {
+ if( cache )
+ {
+ fieldCache = new HashMap();
+ List fields = getFields();
+ Iterator fieldIter = fields.iterator();
+ while( fieldIter.hasNext() )
+ {
+ PDField next = (PDField)fieldIter.next();
+ fieldCache.put( next.getFullyQualifiedName(), next );
+ }
+ }
+ else
+ {
+ fieldCache = null;
+ }
+ }
+
+ /**
+ * This will tell if this acro form is caching the fields.
+ *
+ * @return true if the fields are being cached.
+ */
+ public boolean isCachingFields()
+ {
+ return fieldCache != null;
+ }
+
+ /**
+ * This will get a field by name, possibly using the cache if setCache is true.
+ *
+ * @param name The name of the field to get.
+ *
+ * @return The field with that name of null if one was not found.
+ *
+ * @throws IOException If there is an error getting the field type.
+ */
+ public PDField getField( String name ) throws IOException
+ {
+ PDField retval = null;
+ if( fieldCache != null )
+ {
+ retval = (PDField)fieldCache.get( name );
+ }
+ else
+ {
+ COSArray fields =
+ (COSArray) acroForm.getDictionaryObject(
+ COSName.getPDFName("Fields"));
+
+ for (int i = 0; i < fields.size() && retval == null; i++)
+ {
+ COSDictionary element = (COSDictionary) fields.getObject(i);
+ if( element != null )
+ {
+ COSString fieldName =
+ (COSString)element.getDictionaryObject( COSName.getPDFName( "T" ) );
+ if( fieldName.getString().equals( name ) )
+ {
+ retval = PDFieldFactory.createField( this, element );
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * This will get the default resources for the acro form.
+ *
+ * @return The default resources.
+ */
+ public PDResources getDefaultResources()
+ {
+ PDResources retval = null;
+ COSDictionary dr = (COSDictionary)acroForm.getDictionaryObject( COSName.getPDFName( "DR" ) );
+ if( dr != null )
+ {
+ retval = new PDResources( dr );
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the default resources for the acroform.
+ *
+ * @param dr The new default resources.
+ */
+ public void setDefaultResources( PDResources dr )
+ {
+ COSDictionary drDict = null;
+ if( dr != null )
+ {
+ drDict = dr.getCOSDictionary();
+ }
+ acroForm.setItem( COSName.getPDFName( "DR" ), drDict );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
new file mode 100644
index 0000000..ff18c86
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDAppearance.java
@@ -0,0 +1,645 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdfparser.PDFStreamParser;
+import org.pdfbox.pdfwriter.ContentStreamWriter;
+
+import org.pdfbox.pdmodel.PDResources;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+import org.pdfbox.pdmodel.font.PDFont;
+import org.pdfbox.pdmodel.font.PDFontDescriptor;
+import org.pdfbox.pdmodel.font.PDSimpleFont;
+
+import org.pdfbox.pdmodel.interactive.action.PDAdditionalActions;
+import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import org.pdfbox.util.PDFOperator;
+
+/**
+ * This one took me a while, but i'm proud to say that it handles
+ * the appearance of a textbox. This allows you to apply a value to
+ * a field in the document and handle the appearance so that the
+ * value is actually visible too.
+ * The problem was described by Ben Litchfield, the author of the
+ * example: org.pdfbox.examlpes.fdf.ImportFDF. So Ben, here is the
+ * solution.
+ *
+ * @author sug
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.17 $
+ */
+public class PDAppearance
+{
+ private PDVariableText parent;
+
+ private String value;
+ private COSString defaultAppearance;
+
+ private PDAcroForm acroForm;
+ private List widgets = new ArrayList();
+
+
+ /**
+ * Constructs a COSAppearnce from the given field.
+ *
+ * @param theAcroForm the acro form that this field is part of.
+ * @param field the field which you wish to control the appearance of
+ * @throws IOException If there is an error creating the appearance.
+ */
+ public PDAppearance( PDAcroForm theAcroForm, PDVariableText field ) throws IOException
+ {
+ acroForm = theAcroForm;
+ parent = field;
+
+ widgets = field.getKids();
+ if( widgets == null )
+ {
+ widgets = new ArrayList();
+ widgets.add( field.getWidget() );
+ }
+
+ defaultAppearance = getDefaultAppearance();
+
+
+ }
+
+ /**
+ * Returns the default apperance of a textbox. If the textbox
+ * does not have one, then it will be taken from the AcroForm.
+ * @return The DA element
+ */
+ private COSString getDefaultAppearance()
+ {
+
+ COSString dap = parent.getDefaultAppearance();
+ if (dap == null)
+ {
+ COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
+ if( kids != null && kids.size() > 0 )
+ {
+ COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
+ dap = (COSString)firstKid.getDictionaryObject( "DA" );
+ }
+ if( dap == null )
+ {
+ dap = (COSString) acroForm.getDictionary().getDictionaryObject(COSName.getPDFName("DA"));
+ }
+ }
+ return dap;
+ }
+
+ private int getQ()
+ {
+ int q = parent.getQ();
+ if( parent.getDictionary().getDictionaryObject( "Q" ) == null )
+ {
+ COSArray kids = (COSArray)parent.getDictionary().getDictionaryObject( "Kids" );
+ if( kids != null && kids.size() > 0 )
+ {
+ COSDictionary firstKid = (COSDictionary)kids.getObject( 0 );
+ COSNumber qNum = (COSNumber)firstKid.getDictionaryObject( "Q" );
+ if( qNum != null )
+ {
+ q = qNum.intValue();
+ }
+ }
+ }
+ return q;
+ }
+
+ /**
+ * Extracts the original appearance stream into a list of tokens.
+ *
+ * @return The tokens in the original appearance stream
+ */
+ private List getStreamTokens( PDAppearanceStream appearanceStream ) throws IOException
+ {
+ List tokens = null;
+ if( appearanceStream != null )
+ {
+ tokens = getStreamTokens( appearanceStream.getStream() );
+ }
+ return tokens;
+ }
+
+ private List getStreamTokens( COSString string ) throws IOException
+ {
+ PDFStreamParser parser;
+
+ List tokens = null;
+ if( string != null )
+ {
+ ByteArrayInputStream stream = new ByteArrayInputStream( string.getBytes() );
+ parser = new PDFStreamParser( stream, acroForm.getDocument().getDocument().getScratchFile() );
+ parser.parse();
+ tokens = parser.getTokens();
+ }
+ return tokens;
+ }
+
+ private List getStreamTokens( COSStream stream ) throws IOException
+ {
+ PDFStreamParser parser;
+
+ List tokens = null;
+ if( stream != null )
+ {
+ parser = new PDFStreamParser( stream );
+ parser.parse();
+ tokens = parser.getTokens();
+ }
+ return tokens;
+ }
+
+ /**
+ * Tests if the apperance stream already contains content.
+ *
+ * @return true if it contains any content
+ */
+ private boolean containsMarkedContent( List stream )
+ {
+ return stream.contains( PDFOperator.getOperator( "BMC" ) );
+ }
+
+ /**
+ * This is the public method for setting the appearance stream.
+ *
+ * @param apValue the String value which the apperance shoud represent
+ *
+ * @throws IOException If there is an error creating the stream.
+ */
+ public void setAppearanceValue(String apValue) throws IOException
+ {
+ // MulitLine check and set
+ if ( parent.isMultiline() && apValue.indexOf('\n') != -1 )
+ {
+ apValue = convertToMultiLine( apValue );
+ }
+
+ value = apValue;
+ Iterator widgetIter = widgets.iterator();
+ while( widgetIter.hasNext() )
+ {
+ Object next = widgetIter.next();
+ PDAnnotationWidget widget = null;
+ if( next instanceof PDField )
+ {
+ widget = ((PDField)next).getWidget();
+ }
+ else
+ {
+ widget = (PDAnnotationWidget)next;
+ }
+ PDAdditionalActions actions = widget.getActions();
+ if( actions != null &&
+ actions.getF() != null &&
+ widget.getDictionary().getDictionaryObject( "AP" ) ==null)
+ {
+ //do nothing because the field will be formatted by acrobat
+ //when it is opened. See FreedomExpressions.pdf for an example of this.
+ }
+ else
+ {
+
+ PDAppearanceDictionary appearance = widget.getAppearance();
+ if( appearance == null )
+ {
+ appearance = new PDAppearanceDictionary();
+ widget.setAppearance( appearance );
+ }
+
+ Map normalAppearance = appearance.getNormalAppearance();
+ PDAppearanceStream appearanceStream = (PDAppearanceStream)normalAppearance.get( "default" );
+ if( appearanceStream == null )
+ {
+ COSStream cosStream = new COSStream( acroForm.getDocument().getDocument().getScratchFile() );
+ appearanceStream = new PDAppearanceStream( cosStream );
+ appearanceStream.setBoundingBox( widget.getRectangle().createRetranslatedRectangle() );
+ appearance.setNormalAppearance( appearanceStream );
+ }
+
+ List tokens = getStreamTokens( appearanceStream );
+ List daTokens = getStreamTokens( getDefaultAppearance() );
+ PDFont pdFont = getFontAndUpdateResources( tokens, appearanceStream );
+
+ if (!containsMarkedContent( tokens ))
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+ //BJL 9/25/2004 Must prepend existing stream
+ //because it might have operators to draw things like
+ //rectangles and such
+ ContentStreamWriter writer = new ContentStreamWriter( output );
+ writer.writeTokens( tokens );
+
+ output.write( " /Tx BMC\n".getBytes() );
+ insertGeneratedAppearance( widget, output, pdFont, tokens, appearanceStream );
+ output.write( " EMC".getBytes() );
+ writeToStream( output.toByteArray(), appearanceStream );
+ }
+ else
+ {
+ if( tokens != null )
+ {
+ if( daTokens != null )
+ {
+ int bmcIndex = tokens.indexOf( PDFOperator.getOperator( "BMC" ));
+ int emcIndex = tokens.indexOf( PDFOperator.getOperator( "EMC" ));
+ if( bmcIndex != -1 && emcIndex != -1 &&
+ emcIndex == bmcIndex+1 )
+ {
+ //if the EMC immediately follows the BMC index then should
+ //insert the daTokens inbetween the two markers.
+ tokens.addAll( emcIndex, daTokens );
+ }
+ }
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ ContentStreamWriter writer = new ContentStreamWriter( output );
+ float fontSize = calculateFontSize( pdFont, appearanceStream.getBoundingBox(), tokens, null );
+ boolean foundString = false;
+ for( int i=0; i<tokens.size(); i++ )
+ {
+ if( tokens.get( i ) instanceof COSString )
+ {
+ foundString = true;
+ COSString drawnString =((COSString)tokens.get(i));
+ drawnString.reset();
+ drawnString.append( apValue.getBytes() );
+ }
+ }
+ int setFontIndex = tokens.indexOf( PDFOperator.getOperator( "Tf" ));
+ tokens.set( setFontIndex-1, new COSFloat( fontSize ) );
+ if( foundString )
+ {
+ writer.writeTokens( tokens );
+ }
+ else
+ {
+ int bmcIndex = tokens.indexOf( PDFOperator.getOperator( "BMC" ) );
+ int emcIndex = tokens.indexOf( PDFOperator.getOperator( "EMC" ) );
+
+ if( bmcIndex != -1 )
+ {
+ writer.writeTokens( tokens, 0, bmcIndex+1 );
+ }
+ else
+ {
+ writer.writeTokens( tokens );
+ }
+ output.write( "\n".getBytes() );
+ insertGeneratedAppearance( widget, output,
+ pdFont, tokens, appearanceStream );
+ if( emcIndex != -1 )
+ {
+ writer.writeTokens( tokens, emcIndex, tokens.size() );
+ }
+ }
+ writeToStream( output.toByteArray(), appearanceStream );
+ }
+ else
+ {
+ //hmm?
+ }
+ }
+ }
+ }
+ }
+
+ private void insertGeneratedAppearance( PDAnnotationWidget fieldWidget, OutputStream output,
+ PDFont pdFont, List tokens, PDAppearanceStream appearanceStream ) throws IOException
+ {
+ PrintWriter printWriter = new PrintWriter( output, true );
+ float fontSize = 0.0f;
+ PDRectangle boundingBox = null;
+ boundingBox = appearanceStream.getBoundingBox();
+ if( boundingBox == null )
+ {
+ boundingBox = fieldWidget.getRectangle().createRetranslatedRectangle();
+ }
+ printWriter.println( "BT" );
+ if( defaultAppearance != null )
+ {
+ String daString = defaultAppearance.getString();
+ PDFStreamParser daParser = new PDFStreamParser(new ByteArrayInputStream( daString.getBytes() ), null );
+ daParser.parse();
+ List daTokens = daParser.getTokens();
+ fontSize = calculateFontSize( pdFont, boundingBox, tokens, daTokens );
+ int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) );
+ if(fontIndex != -1 )
+ {
+ daTokens.set( fontIndex-1, new COSFloat( fontSize ) );
+ }
+ ContentStreamWriter daWriter = new ContentStreamWriter(output);
+ daWriter.writeTokens( daTokens );
+ }
+ printWriter.println( getTextPosition( boundingBox, pdFont, fontSize, tokens ) );
+ int q = getQ();
+ if( q == PDTextbox.QUADDING_LEFT )
+ {
+ //do nothing because left is default
+ }
+ else if( q == PDTextbox.QUADDING_CENTERED ||
+ q == PDTextbox.QUADDING_RIGHT )
+ {
+ float fieldWidth = boundingBox.getWidth();
+ float stringWidth = (pdFont.getStringWidth( value )/1000)*fontSize;
+ float adjustAmount = fieldWidth - stringWidth - 4;
+
+ if( q == PDTextbox.QUADDING_CENTERED )
+ {
+ adjustAmount = adjustAmount/2.0f;
+ }
+
+ printWriter.println( adjustAmount + " 0 Td" );
+ }
+ else
+ {
+ throw new IOException( "Error: Unknown justification value:" + q );
+ }
+ printWriter.println("(" + value + ") Tj");
+ printWriter.println("ET" );
+ printWriter.flush();
+ }
+
+ private PDFont getFontAndUpdateResources( List tokens, PDAppearanceStream appearanceStream ) throws IOException
+ {
+
+ PDFont retval = null;
+ PDResources streamResources = appearanceStream.getResources();
+ PDResources formResources = acroForm.getDefaultResources();
+ if( formResources != null )
+ {
+ if( streamResources == null )
+ {
+ streamResources = new PDResources();
+ appearanceStream.setResources( streamResources );
+ }
+
+ COSString da = getDefaultAppearance();
+ if( da != null )
+ {
+ String data = da.getString();
+ PDFStreamParser streamParser = new PDFStreamParser(
+ new ByteArrayInputStream( data.getBytes() ), null );
+ streamParser.parse();
+ tokens = streamParser.getTokens();
+ }
+
+ int setFontIndex = tokens.indexOf( PDFOperator.getOperator( "Tf" ));
+ COSName cosFontName = (COSName)tokens.get( setFontIndex-2 );
+ String fontName = cosFontName.getName();
+ retval = (PDFont)streamResources.getFonts().get( fontName );
+ if( retval == null )
+ {
+ retval = (PDFont)formResources.getFonts().get( fontName );
+ streamResources.getFonts().put( fontName, retval );
+ }
+ }
+ return retval;
+ }
+
+ private String convertToMultiLine( String line )
+ {
+ int currIdx = 0;
+ int lastIdx = 0;
+ StringBuffer result = new StringBuffer(line.length() + 64);
+ while( (currIdx = line.indexOf('\n',lastIdx )) > -1 )
+ {
+ result.append(value.substring(lastIdx,currIdx));
+ result.append(" ) Tj\n0 -13 Td\n(");
+ lastIdx = currIdx + 1;
+ }
+ result.append(line.substring(lastIdx));
+ return result.toString();
+ }
+
+ /**
+ * Writes the stream to the actual stream in the COSStream.
+ *
+ * @throws IOException If there is an error writing to the stream
+ */
+ private void writeToStream( byte[] data, PDAppearanceStream appearanceStream ) throws IOException
+ {
+ OutputStream out = appearanceStream.getStream().createUnfilteredStream();
+ out.write( data );
+ out.flush();
+ }
+
+
+ /**
+ * w in an appearance stream represents the lineWidth.
+ * @return the linewidth
+ */
+ private float getLineWidth( List tokens )
+ {
+
+ float retval = 1;
+ if( tokens != null )
+ {
+ int btIndex = tokens.indexOf(PDFOperator.getOperator( "BT" ));
+ int wIndex = tokens.indexOf(PDFOperator.getOperator( "w" ));
+ //the w should only be used if it is before the first BT.
+ if( (wIndex > 0) && (wIndex < btIndex) )
+ {
+ retval = ((COSNumber)tokens.get(wIndex-1)).floatValue();
+ }
+ }
+ return retval;
+ }
+
+ private PDRectangle getSmallestDrawnRectangle( PDRectangle boundingBox, List tokens )
+ {
+ PDRectangle smallest = boundingBox;
+ for( int i=0; i<tokens.size(); i++ )
+ {
+ Object next = tokens.get( i );
+ if( next == PDFOperator.getOperator( "re" ) )
+ {
+ COSNumber x = (COSNumber)tokens.get( i-4 );
+ COSNumber y = (COSNumber)tokens.get( i-3 );
+ COSNumber width = (COSNumber)tokens.get( i-2 );
+ COSNumber height = (COSNumber)tokens.get( i-1 );
+ PDRectangle potentialSmallest = new PDRectangle();
+ potentialSmallest.setLowerLeftX( x.floatValue() );
+ potentialSmallest.setLowerLeftY( y.floatValue() );
+ potentialSmallest.setUpperRightX( x.floatValue() + width.floatValue() );
+ potentialSmallest.setUpperRightY( y.floatValue() + height.floatValue() );
+ if( smallest == null ||
+ smallest.getLowerLeftX() < potentialSmallest.getLowerLeftX() ||
+ smallest.getUpperRightY() > potentialSmallest.getUpperRightY() )
+ {
+ smallest = potentialSmallest;
+ }
+
+ }
+ }
+ return smallest;
+ }
+
+ /**
+ * My "not so great" method for calculating the fontsize.
+ * It does not work superb, but it handles ok.
+ * @return the calculated font-size
+ *
+ * @throws IOException If there is an error getting the font height.
+ */
+ private float calculateFontSize( PDFont pdFont, PDRectangle boundingBox, List tokens, List daTokens )
+ throws IOException
+ {
+ float fontSize = 0;
+ if( daTokens != null )
+ {
+ //daString looks like "BMC /Helv 3.4 Tf EMC"
+
+ int fontIndex = daTokens.indexOf( PDFOperator.getOperator( "Tf" ) );
+ if(fontIndex != -1 )
+ {
+ fontSize = ((COSNumber)daTokens.get(fontIndex-1)).floatValue();
+ }
+ }
+ if( parent.doNotScroll() )
+ {
+ //if we don't scroll then we will shrink the font to fit into the text area.
+ float widthAtFontSize1 = pdFont.getStringWidth( value );
+ float availableWidth = boundingBox.getWidth();
+ float perfectFitFontSize = availableWidth / widthAtFontSize1;
+ }
+ else if( fontSize == 0 )
+ {
+ float lineWidth = getLineWidth( tokens );
+ float stringWidth = pdFont.getStringWidth( value );
+ float height = 0;
+ if( pdFont instanceof PDSimpleFont )
+ {
+ height = ((PDSimpleFont)pdFont).getFontDescriptor().getFontBoundingBox().getHeight();
+ }
+ else
+ {
+ //now much we can do, so lets assume font is square and use width
+ //as the height
+ height = pdFont.getAverageFontWidth();
+ }
+ height = height/1000f;
+
+ float availHeight = getAvailableHeight( boundingBox, lineWidth );
+ fontSize =(availHeight/height);
+ }
+ return fontSize;
+ }
+
+ /**
+ * Calculates where to start putting the text in the box.
+ * The positioning is not quite as accurate as when Acrobat
+ * places the elements, but it works though.
+ *
+ * @return the sting for representing the start position of the text
+ *
+ * @throws IOException If there is an error calculating the text position.
+ */
+ private String getTextPosition( PDRectangle boundingBox, PDFont pdFont, float fontSize, List tokens )
+ throws IOException
+ {
+ float lineWidth = getLineWidth( tokens );
+ float pos = 0.0f;
+ if(parent.isMultiline())
+ {
+ int rows = (int) (getAvailableHeight( boundingBox, lineWidth ) / ((int) fontSize));
+ pos = ((rows)*fontSize)-fontSize;
+ }
+ else
+ {
+ if( pdFont instanceof PDSimpleFont )
+ {
+ //BJL 9/25/2004
+ //This algorithm is a little bit of black magic. It does
+ //not appear to be documented anywhere. Through examining a few
+ //PDF documents and the value that Acrobat places in there I
+ //have determined that the below method of computing the position
+ //is correct for certain documents, but maybe not all. It does
+ //work f1040ez.pdf and Form_1.pdf
+ PDFontDescriptor fd = ((PDSimpleFont)pdFont).getFontDescriptor();
+ float bBoxHeight = boundingBox.getHeight();
+ float fontHeight = fd.getFontBoundingBox().getHeight() + 2 * fd.getDescent();
+ fontHeight = (fontHeight/1000) * fontSize;
+ pos = (bBoxHeight - fontHeight)/2;
+ }
+ else
+ {
+ throw new IOException( "Error: Don't know how to calculate the position for non-simple fonts" );
+ }
+ }
+ PDRectangle innerBox = getSmallestDrawnRectangle( boundingBox, tokens );
+ float xInset = 2+ 2*(boundingBox.getWidth() - innerBox.getWidth());
+ return Math.round(xInset) + " "+ pos + " Td";
+ }
+
+ /**
+ * calculates the available width of the box.
+ * @return the calculated available width of the box
+ */
+ private float getAvailableWidth( PDRectangle boundingBox, float lineWidth )
+ {
+ return boundingBox.getWidth() - 2 * lineWidth;
+ }
+
+ /**
+ * calculates the available height of the box.
+ * @return the calculated available height of the box
+ */
+ private float getAvailableHeight( PDRectangle boundingBox, float lineWidth )
+ {
+ return boundingBox.getHeight() - 2 * lineWidth;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java
new file mode 100644
index 0000000..2e175d0
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDCheckbox.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class for handling the PDF field as a checkbox.
+ *
+ * @author sug
+ * @version $Revision: 1.10 $
+ */
+public class PDCheckbox extends PDChoiceButton
+{
+ private static final COSName KEY = COSName.getPDFName("AS");
+ private static final COSName OFF_VALUE = COSName.getPDFName("Off");
+
+ private COSName value;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The checkbox field dictionary
+ */
+ public PDCheckbox( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ COSDictionary ap = (COSDictionary) field.getDictionaryObject(COSName.getPDFName("AP"));
+ if( ap != null )
+ {
+ COSBase n = ap.getDictionaryObject(COSName.getPDFName("N"));
+
+ if( n instanceof COSDictionary )
+ {
+ List li = ((COSDictionary)n).keyList();
+ for( int i=0; i<li.size(); i++ )
+ {
+ COSName name = (COSName)li.get( i );
+ if( !name.equals( OFF_VALUE ))
+ {
+ value = name;
+ }
+ }
+
+ }
+ }
+ else
+ {
+ value = (COSName)getDictionary().getDictionaryObject( "V" );
+ }
+ }
+
+ /**
+ * This will tell if this radio button is currently checked or not.
+ *
+ * @return true If the radio button is checked.
+ */
+ public boolean isChecked()
+ {
+ boolean retval = false;
+ String onValue = getOnValue();
+ COSName radioValue = (COSName)getDictionary().getDictionaryObject( KEY );
+ if( radioValue != null && value != null && radioValue.getName().equals( onValue ) )
+ {
+ retval = true;
+ }
+
+ return retval;
+ }
+
+ /**
+ * Checks the radiobutton.
+ */
+ public void check()
+ {
+ getDictionary().setItem(KEY, value);
+ }
+
+ /**
+ * Unchecks the radiobutton.
+ */
+ public void unCheck()
+ {
+ getDictionary().setItem(KEY, OFF_VALUE);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ */
+ public void setValue(String newValue)
+ {
+ getDictionary().setName( "V", newValue );
+ if( newValue == null )
+ {
+ getDictionary().setItem( KEY, OFF_VALUE );
+ }
+ else
+ {
+ getDictionary().setName( KEY, newValue );
+ }
+ }
+
+ /**
+ * This will get the value of the radio button.
+ *
+ * @return The value of the radio button.
+ */
+ public String getOffValue()
+ {
+ return OFF_VALUE.getName();
+ }
+
+ /**
+ * This will get the value of the radio button.
+ *
+ * @return The value of the radio button.
+ */
+ public String getOnValue()
+ {
+ String retval = null;
+ COSDictionary ap = (COSDictionary) getDictionary().getDictionaryObject(COSName.getPDFName("AP"));
+ COSBase n = ap.getDictionaryObject(COSName.getPDFName("N"));
+
+ //N can be a COSDictionary or a COSStream
+ if( n instanceof COSDictionary )
+ {
+ Iterator li = ((COSDictionary)n).keyList().iterator();
+ while( li.hasNext() )
+ {
+ Object key = li.next();
+ if( !key.equals( OFF_VALUE) )
+ {
+ retval = ((COSName)key).getName();
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * 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 String getValue() throws IOException
+ {
+ return getDictionary().getNameAsString( "V" );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java
new file mode 100644
index 0000000..5196d85
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceButton.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This holds common functionality for check boxes and radio buttons.
+ *
+ * @author sug
+ * @version $Revision: 1.4 $
+ */
+public abstract class PDChoiceButton extends PDField
+{
+
+ /**
+ * @see PDField#PDField(PDAcroForm,org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this button.
+ */
+ public PDChoiceButton( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * This will get the option values "Opt" entry of the pdf button.
+ *
+ * @return A list of java.lang.String values.
+ */
+ public List getOptions()
+ {
+ List retval = null;
+ COSArray array = (COSArray)getDictionary().getDictionaryObject( COSName.getPDFName( "Opt" ) );
+ if( array != null )
+ {
+ List strings = new ArrayList();
+ for( int i=0; i<array.size(); i++ )
+ {
+ strings.add( ((COSString)array.getObject( i )).getString() );
+ }
+ retval = new COSArrayList( strings, array );
+ }
+ return retval;
+ }
+
+ /**
+ * This will will set the list of options for this button.
+ *
+ * @param options The list of options for the button.
+ */
+ public void setOptions( List options )
+ {
+ getDictionary().setItem(
+ COSName.getPDFName( "Opt" ),
+ COSArrayList.converterToCOSArray( options ) );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java
new file mode 100644
index 0000000..ff02cc6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDChoiceField.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.cos.COSInteger;
+
+/**
+ * A class for handling the PDF field as a choicefield.
+ *
+ * @author sug
+ * @version $Revision: 1.6 $
+ */
+public class PDChoiceField extends PDVariableText
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this choice field.
+ */
+ public PDChoiceField( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#setValue(java.lang.String)
+ *
+ * @param optionValue The new value for this text field.
+ *
+ * @throws IOException If there is an error calculating the appearance stream or the value in not one
+ * of the existing options.
+ */
+ public void setValue(String optionValue) throws IOException
+ {
+ int indexSelected = -1;
+ COSArray options = (COSArray)getDictionary().getDictionaryObject( "Opt" );
+ if( options.size() == 0 )
+ {
+ throw new IOException( "Error: You cannot set a value for a choice field if there are no options." );
+ }
+ else
+ {
+ COSBase option = options.getObject( 0 );
+ if( option instanceof COSArray )
+ {
+ for( int i=0; i<options.size() && indexSelected == -1; i++ )
+ {
+ COSArray keyValuePair = (COSArray)options.get( i );
+ COSString key = (COSString)keyValuePair.getObject( 0 );
+ COSString value = (COSString)keyValuePair.getObject( 1 );
+ if( optionValue.equals( key.getString() ) || optionValue.equals( value.getString() ) )
+ {
+ //have the parent draw the appearance stream with the value
+ super.setValue( value.getString() );
+ //but then use the key as the V entry
+ getDictionary().setItem( COSName.getPDFName( "V" ), key );
+ indexSelected = i;
+ }
+ }
+ }
+ else
+ {
+ for( int i=0; i<options.size() && indexSelected == -1; i++ )
+ {
+ COSString value = (COSString)options.get( i );
+ if( optionValue.equals( value.getString() ) )
+ {
+ setValue( optionValue );
+ indexSelected = i;
+ }
+ }
+ }
+ }
+ if( indexSelected == -1 )
+ {
+ throw new IOException( "Error: '" + optionValue + "' was not an available option.");
+ }
+ else
+ {
+ COSArray indexArray = (COSArray)getDictionary().getDictionaryObject( "I" );
+ if( indexArray != null )
+ {
+ indexArray.clear();
+ indexArray.add( new COSInteger( indexSelected ) );
+ }
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java
new file mode 100644
index 0000000..c395794
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDField.java
@@ -0,0 +1,610 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.pdmodel.common.COSObjectable;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.PDTextStream;
+
+import org.pdfbox.pdmodel.fdf.FDFField;
+import org.pdfbox.util.BitFlagHelper;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the superclass for a Field element in a PDF.
+ * Based on the COS object model from PDFBox.
+ *
+ * @author sug
+ * @version $Revision: 1.21 $
+ */
+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( "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( "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 )
+ {
+ finalName = parentName + "." + finalName;
+ }
+ return finalName;
+ }
+
+ /**
+ * 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( "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( "FT" );
+ if( retval == null )
+ {
+ COSDictionary parent = (COSDictionary)dic.getDictionaryObject( "Parent" );
+ 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(), "Ff", FLAG_READ_ONLY, readonly );
+ }
+
+ /**
+ *
+ * @return true if the field is readonly
+ */
+ public boolean isReadonly()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_READ_ONLY );
+ }
+
+ /**
+ * sets the field to be required.
+ *
+ * @param required The new flag for required.
+ */
+ public void setRequired(boolean required)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_REQUIRED, required );
+ }
+
+ /**
+ *
+ * @return true if the field is required
+ */
+ public boolean isRequired()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "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(), "Ff", FLAG_NO_EXPORT, noExport );
+ }
+
+ /**
+ *
+ * @return true if the field is not to be exported.
+ */
+ public boolean isNoExport()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "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.getPDFName( "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 )
+ {
+ COSInteger ff = new COSInteger( flags );
+ getDictionary().setItem( COSName.getPDFName( "Ff" ), ff );
+ }
+
+ /**
+ * 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 = (FDFField)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 getKids(). If the kids entry is specified, then the 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;
+ }
+
+ /**
+ * 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( "Parent" );
+ 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 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);
+ COSDictionary parent = (COSDictionary)kidDictionary.getDictionaryObject( "Parent" );
+ if( kidDictionary.getDictionaryObject( "FT" ) != null ||
+ (parent != null && parent.getDictionaryObject( "FT" ) != null ) )
+ {
+ kidsList.add( PDFieldFactory.createField( acroForm, kidDictionary ));
+ }
+ else if( "Widget".equals( kidDictionary.getNameAsString( "Subtype" ) ) )
+ {
+ kidsList.add( new PDAnnotationWidget( kidDictionary ) );
+ }
+ else
+ {
+ //
+ kidsList.add( PDFieldFactory.createField( acroForm, 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.
+ */
+ public String toString()
+ {
+ return "" + getDictionary().getDictionaryObject( COSName.getPDFName( "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( "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( "AA", actions );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
new file mode 100644
index 0000000..c796714
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2003-2004, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
+
+import java.io.IOException;
+
+import java.util.List;
+
+/**
+ * This is the Factory for creating and returning the correct
+ * field elements.
+ *
+ * @author sug
+ * @version $Revision: 1.8 $
+ */
+public class PDFieldFactory
+{
+ private static final int RADIO_BITMASK = 32768;
+ private static final int PUSHBUTTON_BITMASK = 65536;
+ private static final int RADIOS_IN_UNISON_BITMASK = 33554432;
+
+ private static final String FIELD_TYPE_BTN = "Btn";
+ private static final String FIELD_TYPE_TX = "Tx";
+ private static final String FIELD_TYPE_CH = "Ch";
+ private static final String FIELD_TYPE_SIG = "Sig";
+
+ /**
+ * Utility class so no constructor.
+ */
+ private PDFieldFactory()
+ {
+ //do nothing.
+ }
+
+ /**
+ * This method creates a COSField subclass from the given field.
+ * The field is a PDF Dictionary object that must represent a
+ * field element. - othewise null is returned
+ *
+ * @param acroForm The form that the field will be part of.
+ * @param field The dictionary representing a field element
+ *
+ * @return a subclass to COSField according to the kind of field passed to createField
+ * @throws IOException If there is an error determining the field type.
+ */
+ public static PDField createField( PDAcroForm acroForm, COSDictionary field) throws IOException
+ {
+ PDField pdField = new PDUnknownField( acroForm, field );
+ if( isButton(pdField) )
+ {
+ int flags = pdField.getFieldFlags();
+ //BJL, I have found that the radio flag bit is not always set
+ //and that sometimes there is just a kids dictionary.
+ //so, if there is a kids dictionary then it must be a radio button
+ //group.
+ COSArray kids = (COSArray)field.getDictionaryObject( COSName.getPDFName( "Kids" ) );
+ if( kids != null || isRadio(flags) )
+ {
+ pdField = new PDRadioCollection( acroForm, field );
+ }
+ else if( isPushButton( flags ) )
+ {
+ pdField = new PDPushButton( acroForm, field );
+ }
+ else
+ {
+ pdField = new PDCheckbox( acroForm, field );
+ }
+
+ }
+ else if (isChoiceField(pdField))
+ {
+ pdField = new PDChoiceField( acroForm, field );
+ }
+ else if (isTextbox(pdField))
+ {
+ pdField = new PDTextbox( acroForm, field );
+ }
+ else if( isSignature( pdField ) )
+ {
+ pdField = new PDSignature( acroForm, field );
+ }
+ else
+ {
+ //do nothing and return an unknown field type.
+ }
+ return pdField;
+ }
+
+ /**
+ * This method determines if the given
+ * field is a radiobutton collection.
+ *
+ * @param flags The field flags.
+ *
+ * @return the result of the determination
+ */
+ private static boolean isRadio( int flags )
+ {
+ return (flags & RADIO_BITMASK) > 0;
+ }
+
+ /**
+ * This method determines if the given
+ * field is a pushbutton.
+ *
+ * @param flags The field flags.
+ *
+ * @return the result of the determination
+ */
+ private static boolean isPushButton( int flags )
+ {
+ return (flags & PUSHBUTTON_BITMASK) > 0;
+ }
+
+ /**
+ * This method determines if the given field is a choicefield
+ * Choicefields are either listboxes or comboboxes.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isChoiceField(PDField field) throws IOException
+ {
+ return FIELD_TYPE_CH.equals(field.findFieldType());
+ }
+
+ /**
+ * This method determines if the given field is a button.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ *
+ * @throws IOException If there is an error determining the field type.
+ */
+ private static boolean isButton(PDField field) throws IOException
+ {
+ String ft = field.findFieldType();
+ boolean retval = FIELD_TYPE_BTN.equals( ft );
+ List kids = field.getKids();
+ if( ft == null && kids != null && kids.size() > 0)
+ {
+ //sometimes if it is a button the type is only defined by one
+ //of the kids entries
+ Object obj = kids.get( 0 );
+ COSDictionary kidDict = null;
+ if( obj instanceof PDField )
+ {
+ kidDict = ((PDField)obj).getDictionary();
+ }
+ else if( obj instanceof PDAnnotationWidget )
+ {
+ kidDict = ((PDAnnotationWidget)obj).getDictionary();
+ }
+ else
+ {
+ throw new IOException( "Error:Unexpected type of kids field:" + obj );
+ }
+ retval = isButton( new PDUnknownField( field.getAcroForm(), kidDict ) );
+ }
+ return retval;
+ }
+
+ /**
+ * This method determines if the given field is a signature.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isSignature(PDField field) throws IOException
+ {
+ return FIELD_TYPE_SIG.equals(field.findFieldType());
+ }
+
+ /**
+ * This method determines if the given field is a Textbox.
+ *
+ * @param field the field to determine
+ * @return the result of the determination
+ */
+ private static boolean isTextbox(PDField field) throws IOException
+ {
+ return FIELD_TYPE_TX.equals(field.findFieldType());
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java
new file mode 100644
index 0000000..e832921
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDPushButton.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSString;
+
+import java.io.IOException;
+
+/**
+ * A class for handling the PDF field as a PDPushButton.
+ *
+ * @author sug
+ * @version $Revision: 1.3 $
+ */
+public class PDPushButton extends PDField
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field for this push button.
+ */
+ public PDPushButton( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm, field);
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @param value The new value for the field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ COSString fieldValue = new COSString(value);
+ getDictionary().setItem( COSName.getPDFName( "V" ), fieldValue );
+ getDictionary().setItem( COSName.getPDFName( "DV" ), fieldValue );
+ }
+
+ /**
+ * 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 String getValue() throws IOException
+ {
+ return getDictionary().getString( "V" );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java
new file mode 100644
index 0000000..27b48d6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDRadioCollection.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+
+import org.pdfbox.pdmodel.common.COSArrayList;
+import org.pdfbox.util.BitFlagHelper;
+
+/**
+ * A class for handling the PDF field as a Radio Collection.
+ * This class automatically keeps track of the child radio buttons
+ * in the collection.
+ *
+ * @see PDCheckbox
+ * @author sug
+ * @version $Revision: 1.12 $
+ */
+public class PDRadioCollection extends PDChoiceButton
+{
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_RADIOS_IN_UNISON = 1 << 25;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field that makes up the radio collection.
+ */
+ public PDRadioCollection( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm,field);
+ }
+
+ /**
+ * From the PDF Spec <br/>
+ * If set, a group of radio buttons within a radio button field that
+ * use the same value for the on state will turn on and off in unison; that is if
+ * one is checked, they are all checked. If clear, the buttons are mutually exclusive
+ * (the same behavior as HTML radio buttons).
+ *
+ * @param radiosInUnison The new flag for radiosInUnison.
+ */
+ public void setRadiosInUnison(boolean radiosInUnison)
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_RADIOS_IN_UNISON, radiosInUnison );
+ }
+
+ /**
+ *
+ * @return true If the flag is set for radios in unison.
+ */
+ public boolean isRadiosInUnison()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_RADIOS_IN_UNISON );
+ }
+
+ /**
+ * This setValue method iterates the collection of radiobuttons
+ * and checks or unchecks each radiobutton according to the
+ * given value.
+ * If the value is not represented by any of the radiobuttons,
+ * then none will be checked.
+ *
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ */
+ public void setValue(String value) throws IOException
+ {
+ getDictionary().setString( "V", value );
+ List kids = getKids();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ PDCheckbox btn = (PDCheckbox)kids.get(i);
+ if( btn.getOnValue().equals(value) )
+ {
+ btn.check();
+ }
+ else
+ {
+ btn.unCheck();
+ }
+ }
+ }
+
+ /**
+ * 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 String getValue()throws IOException
+ {
+ String retval = null;
+ List kids = getKids();
+ for (int i = 0; i < kids.size(); i++)
+ {
+ PDCheckbox btn = (PDCheckbox)kids.get(i);
+ if( btn.isChecked() )
+ {
+ retval = btn.getOnValue();
+ }
+ }
+ if( retval == null )
+ {
+ retval = getDictionary().getNameAsString( "V" );
+ }
+ return retval;
+ }
+
+
+ /**
+ * This will return a list of PDField objects that are part of this radio collection.
+ *
+ * @see PDField#getWidget()
+ * @return A list of PDWidget objects.
+ * @throws IOException if there is an error while creating the children objects.
+ */
+ 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++)
+ {
+ kidsList.add( PDFieldFactory.createField( getAcroForm(), (COSDictionary)kids.getObject(i) ) );
+ }
+ retval = new COSArrayList( kidsList, kids );
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java
new file mode 100644
index 0000000..66ed3e3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDSignature.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+
+import java.io.IOException;
+
+/**
+ * A class for handling the PDF field as a signature.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDSignature extends PDField
+{
+
+ /**
+ * @see org.pdfbox.pdmodel.field.PDField#COSField(org.pdfbox.cos.COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The dictionary for the signature.
+ */
+ public PDSignature( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super(theAcroForm,field);
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @param value The new value for the field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * @see as.interactive.pdf.form.cos.COSField#setValue(java.lang.String)
+ *
+ * @return The string value of this field.
+ *
+ * @throws IOException If there is an error creating the appearance stream.
+ */
+ public String getValue() throws IOException
+ {
+ throw new RuntimeException( "Not yet implemented" );
+ }
+
+ /**
+ * Return a string rep of this object.
+ *
+ * @return A string rep of this object.
+ */
+ public String toString()
+ {
+ return "PDSignature";
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java
new file mode 100644
index 0000000..fd9550e
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDTextbox.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * A class for handling the PDF field as a textbox.
+ *
+ * @author sug
+ * @version $Revision: 1.9 $
+ */
+public class PDTextbox extends PDVariableText
+{
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroform.
+ */
+ public PDTextbox( PDAcroForm theAcroForm )
+ {
+ super( theAcroForm );
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDTextbox( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java
new file mode 100644
index 0000000..806b5f5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDUnknownField.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import java.io.IOException;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This class represents a form field with an unknown type.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDUnknownField extends PDField
+{
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm, COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDUnknownField( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(String)
+ */
+ public void setValue(String value) throws IOException
+ {
+ //do nothing
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#getValue()
+ */
+ public String getValue() throws IOException
+ {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java
new file mode 100644
index 0000000..7ed5008
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/PDVariableText.java
@@ -0,0 +1,324 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdmodel.interactive.form;
+
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNumber;
+import org.pdfbox.cos.COSString;
+import org.pdfbox.util.BitFlagHelper;
+
+import java.io.IOException;
+
+/**
+ * A class for handling PDF fields that display text.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.6 $
+ */
+public abstract class PDVariableText extends PDField
+{
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_MULTILINE = 1 << 12;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_PASSWORD = 1 << 13;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_FILE_SELECT = 1 << 20;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_DO_NOT_SPELL_CHECK = 1 << 22;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_DO_NOT_SCROLL = 1 << 23;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_COMB = 1 << 24;
+ /**
+ * A Ff flag.
+ */
+ public static final int FLAG_RICH_TEXT = 1 << 25;
+
+
+ /**
+ * DA Default appearance.
+ */
+ private COSString da;
+
+ private PDAppearance appearance;
+
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_LEFT = 0;
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_CENTERED = 1;
+
+ /**
+ * A Q value.
+ */
+ public static final int QUADDING_RIGHT = 2;
+
+ /**
+ * @see PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroform.
+ */
+ public PDVariableText( PDAcroForm theAcroForm )
+ {
+ super( theAcroForm );
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#PDField(PDAcroForm,COSDictionary)
+ *
+ * @param theAcroForm The acroForm for this field.
+ * @param field The field's dictionary.
+ */
+ public PDVariableText( PDAcroForm theAcroForm, COSDictionary field)
+ {
+ super( theAcroForm, field);
+ da = (COSString) field.getDictionaryObject(COSName.getPDFName("DA"));
+ }
+
+ /**
+ * @see org.pdfbox.pdmodel.interactive.form.PDField#setValue(java.lang.String)
+ *
+ * @param value The new value for this text field.
+ *
+ * @throws IOException If there is an error calculating the appearance stream.
+ */
+ public void setValue(String value) throws IOException
+ {
+ COSString fieldValue = new COSString(value);
+ getDictionary().setItem( COSName.getPDFName( "V" ), fieldValue );
+
+ //hmm, not sure what the case where the DV gets set to the field
+ //value, for now leave blank until we can come up with a case
+ //where it needs to be in there
+ //getDictionary().setItem( COSName.getPDFName( "DV" ), fieldValue );
+ if(appearance == null)
+ {
+ this.appearance = new PDAppearance( getAcroForm(), this );
+ }
+ appearance.setAppearanceValue(value);
+ }
+
+ /**
+ * 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 String getValue() throws IOException
+ {
+ return getDictionary().getString( "V" );
+ }
+
+ /**
+ * @return true if the field is multiline
+ */
+ public boolean isMultiline()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_MULTILINE );
+ }
+
+ /**
+ * Set the multiline bit.
+ *
+ * @param multiline The value for the multiline.
+ */
+ public void setMultiline( boolean multiline )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_MULTILINE, multiline );
+ }
+
+ /**
+ * @return true if the field is a password field.
+ */
+ public boolean isPassword()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_PASSWORD );
+ }
+
+ /**
+ * Set the password bit.
+ *
+ * @param password The value for the password.
+ */
+ public void setPassword( boolean password )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_PASSWORD, password );
+ }
+
+ /**
+ * @return true if the field is a file select field.
+ */
+ public boolean isFileSelect()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_FILE_SELECT );
+ }
+
+ /**
+ * Set the file select bit.
+ *
+ * @param fileSelect The value for the fileSelect.
+ */
+ public void setFileSelect( boolean fileSelect )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_FILE_SELECT, fileSelect );
+ }
+
+ /**
+ * @return true if the field is not suppose to spell check.
+ */
+ public boolean doNotSpellCheck()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_DO_NOT_SPELL_CHECK );
+ }
+
+ /**
+ * Set the doNotSpellCheck bit.
+ *
+ * @param doNotSpellCheck The value for the doNotSpellCheck.
+ */
+ public void setDoNotSpellCheck( boolean doNotSpellCheck )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_DO_NOT_SPELL_CHECK, doNotSpellCheck );
+ }
+
+ /**
+ * @return true if the field is not suppose to scroll.
+ */
+ public boolean doNotScroll()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_DO_NOT_SCROLL );
+ }
+
+ /**
+ * Set the doNotScroll bit.
+ *
+ * @param doNotScroll The value for the doNotScroll.
+ */
+ public void setDoNotScroll( boolean doNotScroll )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_DO_NOT_SCROLL, doNotScroll );
+ }
+
+ /**
+ * @return true if the field is not suppose to comb the text display.
+ */
+ public boolean shouldComb()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_COMB );
+ }
+
+ /**
+ * Set the comb bit.
+ *
+ * @param comb The value for the comb.
+ */
+ public void setComb( boolean comb )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_COMB, comb );
+ }
+
+ /**
+ * @return true if the field is a rich text field.
+ */
+ public boolean isRichText()
+ {
+ return BitFlagHelper.getFlag( getDictionary(), "Ff", FLAG_RICH_TEXT );
+ }
+
+ /**
+ * Set the richText bit.
+ *
+ * @param richText The value for the richText.
+ */
+ public void setRichText( boolean richText )
+ {
+ BitFlagHelper.setFlag( getDictionary(), "Ff", FLAG_RICH_TEXT, richText );
+ }
+
+ /**
+ * @return the DA element of the dictionary object
+ */
+ protected COSString getDefaultAppearance()
+ {
+ return da;
+ }
+
+ /**
+ * This will get the 'quadding' or justification of the text to be displayed.
+ * 0 - Left(default)<br/>
+ * 1 - Centered<br />
+ * 2 - Right<br />
+ * Please see the QUADDING_CONSTANTS.
+ *
+ * @return The justification of the text strings.
+ */
+ public int getQ()
+ {
+ int retval = 0;
+ COSNumber number = (COSNumber)getDictionary().getDictionaryObject( COSName.getPDFName( "Q" ) );
+ if( number != null )
+ {
+ retval = number.intValue();
+ }
+ return retval;
+ }
+
+ /**
+ * This will set the quadding/justification of the text. See QUADDING constants.
+ *
+ * @param q The new text justification.
+ */
+ public void setQ( int q )
+ {
+ getDictionary().setItem( COSName.getPDFName( "Q" ), new COSInteger( q ) );
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html b/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html
new file mode 100644
index 0000000..36c4b4b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdmodel/interactive/form/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The interactive package contains classes that deal with interactive annotations such as textfields and buttons.
+</body>
+</html>