1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.pdfbox.pdmodel.interactive.form; 18 19 import org.apache.pdfbox.cos.COSArray; 20 import org.apache.pdfbox.cos.COSBase; 21 import org.apache.pdfbox.cos.COSDictionary; 22 import org.apache.pdfbox.cos.COSName; 23 import org.apache.pdfbox.cos.COSString; 24 25 import org.apache.pdfbox.pdmodel.PDDocument; 26 import org.apache.pdfbox.pdmodel.PDResources; 27 28 import org.apache.pdfbox.pdmodel.common.COSArrayList; 29 import org.apache.pdfbox.pdmodel.common.COSObjectable; 30 import org.apache.pdfbox.pdmodel.fdf.FDFDictionary; 31 import org.apache.pdfbox.pdmodel.fdf.FDFDocument; 32 import org.apache.pdfbox.pdmodel.fdf.FDFCatalog; 33 import org.apache.pdfbox.pdmodel.fdf.FDFField; 34 35 import java.io.IOException; 36 37 import java.util.ArrayList; 38 import java.util.HashMap; 39 import java.util.Iterator; 40 import java.util.List; 41 import java.util.Map; 42 43 /** 44 * This class represents the acroform of a PDF document. 45 * 46 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 47 * @version $Revision: 1.14 $ 48 */ 49 public class PDAcroForm implements COSObjectable 50 { 51 private COSDictionary acroForm; 52 private PDDocument document; 53 54 private Map fieldCache; 55 56 /** 57 * Constructor. 58 * 59 * @param doc The document that this form is part of. 60 */ 61 public PDAcroForm( PDDocument doc ) 62 { 63 document = doc; 64 acroForm = new COSDictionary(); 65 COSArray fields = new COSArray(); 66 acroForm.setItem( COSName.getPDFName( "Fields" ), fields ); 67 } 68 69 /** 70 * Constructor. 71 * 72 * @param doc The document that this form is part of. 73 * @param form The existing acroForm. 74 */ 75 public PDAcroForm( PDDocument doc, COSDictionary form ) 76 { 77 document = doc; 78 acroForm = form; 79 } 80 81 /** 82 * This will get the document associated with this form. 83 * 84 * @return The PDF document. 85 */ 86 public PDDocument getDocument() 87 { 88 return document; 89 } 90 91 /** 92 * This will get the dictionary that this form wraps. 93 * 94 * @return The dictionary for this form. 95 */ 96 public COSDictionary getDictionary() 97 { 98 return acroForm; 99 } 100 101 /** 102 * This method will import an entire FDF document into the PDF document 103 * that this acroform is part of. 104 * 105 * @param fdf The FDF document to import. 106 * 107 * @throws IOException If there is an error doing the import. 108 */ 109 public void importFDF( FDFDocument fdf ) throws IOException 110 { 111 List fields = fdf.getCatalog().getFDF().getFields(); 112 if( fields != null ) 113 { 114 for( int i=0; i<fields.size(); i++ ) 115 { 116 FDFField fdfField = (FDFField)fields.get( i ); 117 PDField docField = getField( fdfField.getPartialFieldName() ); 118 if( docField != null ) 119 { 120 docField.importFDF( fdfField ); 121 } 122 } 123 } 124 } 125 126 /** 127 * This will export all FDF form data. 128 * 129 * @return An FDF document used to export the document. 130 * @throws IOException If there is an error when exporting the document. 131 */ 132 public FDFDocument exportFDF() throws IOException 133 { 134 FDFDocument fdf = new FDFDocument(); 135 FDFCatalog catalog = fdf.getCatalog(); 136 FDFDictionary fdfDict = new FDFDictionary(); 137 catalog.setFDF( fdfDict ); 138 139 List fdfFields = new ArrayList(); 140 List fields = getFields(); 141 Iterator fieldIter = fields.iterator(); 142 while( fieldIter.hasNext() ) 143 { 144 PDField docField = (PDField)fieldIter.next(); 145 addFieldAndChildren( docField, fdfFields ); 146 } 147 fdfDict.setID( document.getDocument().getDocumentID() ); 148 if( fdfFields.size() > 0 ) 149 { 150 fdfDict.setFields( fdfFields ); 151 } 152 return fdf; 153 } 154 155 private void addFieldAndChildren( PDField docField, List fdfFields ) throws IOException 156 { 157 Object fieldValue = docField.getValue(); 158 FDFField fdfField = new FDFField(); 159 fdfField.setPartialFieldName( docField.getPartialName() ); 160 fdfField.setValue( fieldValue ); 161 List kids = docField.getKids(); 162 List childFDFFields = new ArrayList(); 163 if( kids != null ) 164 { 165 166 for( int i=0; i<kids.size(); i++ ) 167 { 168 addFieldAndChildren( (PDField)kids.get( i ), childFDFFields ); 169 } 170 if( childFDFFields.size() > 0 ) 171 { 172 fdfField.setKids( childFDFFields ); 173 } 174 } 175 if( fieldValue != null || childFDFFields.size() > 0 ) 176 { 177 fdfFields.add( fdfField ); 178 } 179 } 180 181 /** 182 * This will return all of the fields in the document. The type 183 * will be a org.apache.pdfbox.pdmodel.field.PDField. 184 * 185 * @return A list of all the fields. 186 * @throws IOException If there is an error while getting the list of fields. 187 */ 188 public List getFields() throws IOException 189 { 190 List retval = null; 191 COSArray fields = 192 (COSArray) acroForm.getDictionaryObject( 193 COSName.getPDFName("Fields")); 194 195 if( fields != null ) 196 { 197 List actuals = new ArrayList(); 198 for (int i = 0; i < fields.size(); i++) 199 { 200 COSDictionary element = (COSDictionary) fields.getObject(i); 201 if (element != null) 202 { 203 PDField field = PDFieldFactory.createField( this, element ); 204 if( field != null ) 205 { 206 actuals.add(field); 207 } 208 } 209 } 210 retval = new COSArrayList( actuals, fields ); 211 } 212 return retval; 213 } 214 215 /** 216 * Set the fields that are part of this AcroForm. 217 * 218 * @param fields The fields that are part of this form. 219 */ 220 public void setFields( List fields ) 221 { 222 acroForm.setItem( "Fields", COSArrayList.converterToCOSArray( fields )); 223 } 224 225 /** 226 * This will tell this form to cache the fields into a Map structure 227 * for fast access via the getField method. The default is false. You would 228 * want this to be false if you were changing the COSDictionary behind the scenes, 229 * otherwise setting this to true is acceptable. 230 * 231 * @param cache A boolean telling if we should cache the fields. 232 * @throws IOException If there is an error while caching the fields. 233 */ 234 public void setCacheFields( boolean cache ) throws IOException 235 { 236 if( cache ) 237 { 238 fieldCache = new HashMap(); 239 List fields = getFields(); 240 Iterator fieldIter = fields.iterator(); 241 while( fieldIter.hasNext() ) 242 { 243 PDField next = (PDField)fieldIter.next(); 244 fieldCache.put( next.getFullyQualifiedName(), next ); 245 } 246 } 247 else 248 { 249 fieldCache = null; 250 } 251 } 252 253 /** 254 * This will tell if this acro form is caching the fields. 255 * 256 * @return true if the fields are being cached. 257 */ 258 public boolean isCachingFields() 259 { 260 return fieldCache != null; 261 } 262 263 /** 264 * This will get a field by name, possibly using the cache if setCache is true. 265 * 266 * @param name The name of the field to get. 267 * 268 * @return The field with that name of null if one was not found. 269 * 270 * @throws IOException If there is an error getting the field type. 271 */ 272 public PDField getField( String name ) throws IOException 273 { 274 PDField retval = null; 275 if( fieldCache != null ) 276 { 277 retval = (PDField)fieldCache.get( name ); 278 } 279 else 280 { 281 String[] nameSubSection = name.split( "\\." ); 282 COSArray fields = 283 (COSArray) acroForm.getDictionaryObject( 284 COSName.getPDFName("Fields")); 285 286 for (int i = 0; i < fields.size() && retval == null; i++) 287 { 288 COSDictionary element = (COSDictionary) fields.getObject(i); 289 if( element != null ) 290 { 291 COSString fieldName = 292 (COSString)element.getDictionaryObject( COSName.getPDFName( "T" ) ); 293 if( fieldName.getString().equals( name ) || 294 fieldName.getString().equals( nameSubSection[0] ) ) 295 { 296 PDField root = PDFieldFactory.createField( this, element ); 297 298 if( nameSubSection.length > 1 ) 299 { 300 PDField kid = root.findKid( nameSubSection, 1 ); 301 if( kid != null ) 302 { 303 retval = kid; 304 } 305 else 306 { 307 retval = root; 308 } 309 } 310 else 311 { 312 retval = root; 313 } 314 } 315 } 316 } 317 } 318 return retval; 319 } 320 321 /** 322 * This will get the default resources for the acro form. 323 * 324 * @return The default resources. 325 */ 326 public PDResources getDefaultResources() 327 { 328 PDResources retval = null; 329 COSDictionary dr = (COSDictionary)acroForm.getDictionaryObject( COSName.getPDFName( "DR" ) ); 330 if( dr != null ) 331 { 332 retval = new PDResources( dr ); 333 } 334 return retval; 335 } 336 337 /** 338 * This will set the default resources for the acroform. 339 * 340 * @param dr The new default resources. 341 */ 342 public void setDefaultResources( PDResources dr ) 343 { 344 COSDictionary drDict = null; 345 if( dr != null ) 346 { 347 drDict = dr.getCOSDictionary(); 348 } 349 acroForm.setItem( COSName.getPDFName( "DR" ), drDict ); 350 } 351 352 /** 353 * {@inheritDoc} 354 */ 355 public COSBase getCOSObject() 356 { 357 return acroForm; 358 } 359 360 /** 361 * Get the XFA resource, the XFA resource is only used for PDF 1.5+ forms. 362 * 363 * @return The xfa resource or null if it does not exist. 364 */ 365 public PDXFA getXFA() 366 { 367 PDXFA xfa = null; 368 COSBase base = acroForm.getDictionaryObject( "XFA" ); 369 if( base != null ) 370 { 371 xfa = new PDXFA( base ); 372 } 373 return xfa; 374 } 375 376 /** 377 * Set the XFA resource, this is only used for PDF 1.5+ forms. 378 * 379 * @param xfa The xfa resource. 380 */ 381 public void setXFA( PDXFA xfa ) 382 { 383 acroForm.setItem( "XFA", xfa ); 384 } 385 }