001: /**
002: * Copyright (c) 2003-2006, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.pdmodel.interactive.form;
031:
032: import org.pdfbox.pdmodel.interactive.action.PDFormFieldAdditionalActions;
033: import org.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
034:
035: import org.pdfbox.pdmodel.common.COSArrayList;
036: import org.pdfbox.pdmodel.common.COSObjectable;
037:
038: import org.pdfbox.cos.COSArray;
039: import org.pdfbox.cos.COSBase;
040: import org.pdfbox.cos.COSDictionary;
041: import org.pdfbox.cos.COSInteger;
042: import org.pdfbox.cos.COSName;
043:
044: import org.pdfbox.pdmodel.common.PDTextStream;
045:
046: import org.pdfbox.pdmodel.fdf.FDFField;
047: import org.pdfbox.util.BitFlagHelper;
048:
049: import java.io.IOException;
050:
051: import java.util.ArrayList;
052: import java.util.List;
053:
054: /**
055: * This is the superclass for a Field element in a PDF.
056: * Based on the COS object model from PDFBox.
057: *
058: * @author sug
059: * @version $Revision: 1.23 $
060: */
061: public abstract class PDField implements COSObjectable {
062: /**
063: * A Ff flag.
064: */
065: public static final int FLAG_READ_ONLY = 1;
066: /**
067: * A Ff flag.
068: */
069: public static final int FLAG_REQUIRED = 1 << 1;
070: /**
071: * A Ff flag.
072: */
073: public static final int FLAG_NO_EXPORT = 1 << 2;
074:
075: private PDAcroForm acroForm;
076:
077: private COSDictionary dictionary;
078:
079: /**
080: * Constructor.
081: *
082: * @param theAcroForm The form that this field is part of.
083: */
084: public PDField(PDAcroForm theAcroForm) {
085: acroForm = theAcroForm;
086: dictionary = new COSDictionary();
087: //no required fields in base field class
088: }
089:
090: /**
091: * Creates a COSField from a COSDictionary, expected to be
092: * a correct object definition for a field in PDF.
093: *
094: * @param theAcroForm The form that this field is part of.
095: * @param field the PDF objet to represent as a field.
096: */
097: public PDField(PDAcroForm theAcroForm, COSDictionary field) {
098: acroForm = theAcroForm;
099: dictionary = field;
100: }
101:
102: /**
103: * Returns the partial name of the field.
104: *
105: * @return the name of the field
106: */
107: public String getPartialName() {
108: return getDictionary().getString("T");
109: }
110:
111: /**
112: * This will set the partial name of the field.
113: *
114: * @param name The new name for the field.
115: */
116: public void setPartialName(String name) {
117: getDictionary().setString("T", name);
118: }
119:
120: /**
121: * Returns the fully qualified name of the field, which is a concatenation of
122: * the names of all the parents fields.
123: *
124: * @return the name of the field
125: *
126: * @throws IOException If there is an error generating the fully qualified name.
127: */
128: public String getFullyQualifiedName() throws IOException {
129: PDField parent = getParent();
130: String parentName = null;
131: if (parent != null) {
132: parentName = parent.getFullyQualifiedName();
133: }
134: String finalName = getPartialName();
135: if (parentName != null) {
136: finalName = parentName + "." + finalName;
137: }
138: return finalName;
139: }
140:
141: /**
142: * Get the FT entry of the field. This is a read only field and is set depending
143: * on the actual type. The field type is an inheritable attribute. This method will
144: * return only the direct value on this object. Use the findFieldType for an upward
145: * recursive search.
146: *
147: * @return The Field type.
148: *
149: * @see PDField#findFieldType()
150: */
151: public String getFieldType() {
152: return getDictionary().getNameAsString("FT");
153: }
154:
155: /**
156: * Find the field type and optionally do a recursive upward search. Sometimes the fieldtype
157: * will be specified on the parent instead of the direct object. This will look at this
158: * object for the field type, if none is specified then it will look to the parent if there
159: * is a parent. If there is no parent and no field type has been found then this
160: * will return null.
161: *
162: * @return The field type or null if none was found.
163: */
164: public String findFieldType() {
165: return findFieldType(getDictionary());
166: }
167:
168: private String findFieldType(COSDictionary dic) {
169: String retval = dic.getNameAsString("FT");
170: if (retval == null) {
171: COSDictionary parent = (COSDictionary) dic
172: .getDictionaryObject("Parent", "P");
173: if (parent != null) {
174: retval = findFieldType(parent);
175: }
176: }
177: return retval;
178:
179: }
180:
181: /**
182: * setValue sets the fields value to a given string.
183: *
184: * @param value the string value
185: *
186: * @throws IOException If there is an error creating the appearance stream.
187: */
188: public abstract void setValue(String value) throws IOException;
189:
190: /**
191: * getValue gets the fields value to as a string.
192: *
193: * @return The string value of this field.
194: *
195: * @throws IOException If there is an error getting the value.
196: */
197: public abstract String getValue() throws IOException;
198:
199: /**
200: * sets the field to be read-only.
201: *
202: * @param readonly The new flag for readonly.
203: */
204: public void setReadonly(boolean readonly) {
205: BitFlagHelper.setFlag(getDictionary(), "Ff", FLAG_READ_ONLY,
206: readonly);
207: }
208:
209: /**
210: *
211: * @return true if the field is readonly
212: */
213: public boolean isReadonly() {
214: return BitFlagHelper.getFlag(getDictionary(), "Ff",
215: FLAG_READ_ONLY);
216: }
217:
218: /**
219: * sets the field to be required.
220: *
221: * @param required The new flag for required.
222: */
223: public void setRequired(boolean required) {
224: BitFlagHelper.setFlag(getDictionary(), "Ff", FLAG_REQUIRED,
225: required);
226: }
227:
228: /**
229: *
230: * @return true if the field is required
231: */
232: public boolean isRequired() {
233: return BitFlagHelper.getFlag(getDictionary(), "Ff",
234: FLAG_REQUIRED);
235: }
236:
237: /**
238: * sets the field to be not exported..
239: *
240: * @param noExport The new flag for noExport.
241: */
242: public void setNoExport(boolean noExport) {
243: BitFlagHelper.setFlag(getDictionary(), "Ff", FLAG_NO_EXPORT,
244: noExport);
245: }
246:
247: /**
248: *
249: * @return true if the field is not to be exported.
250: */
251: public boolean isNoExport() {
252: return BitFlagHelper.getFlag(getDictionary(), "Ff",
253: FLAG_NO_EXPORT);
254: }
255:
256: /**
257: * This will get the flags for this field.
258: *
259: * @return flags The set of flags.
260: */
261: public int getFieldFlags() {
262: int retval = 0;
263: COSInteger ff = (COSInteger) getDictionary()
264: .getDictionaryObject(COSName.getPDFName("Ff"));
265: if (ff != null) {
266: retval = ff.intValue();
267: }
268: return retval;
269: }
270:
271: /**
272: * This will set the flags for this field.
273: *
274: * @param flags The new flags.
275: */
276: public void setFieldFlags(int flags) {
277: COSInteger ff = new COSInteger(flags);
278: getDictionary().setItem(COSName.getPDFName("Ff"), ff);
279: }
280:
281: /**
282: * This will import a fdf field from a fdf document.
283: *
284: * @param fdfField The fdf field to import.
285: *
286: * @throws IOException If there is an error importing the data for this field.
287: */
288: public void importFDF(FDFField fdfField) throws IOException {
289: Object fieldValue = fdfField.getValue();
290: int fieldFlags = getFieldFlags();
291:
292: if (fieldValue != null) {
293: if (fieldValue instanceof String) {
294: setValue((String) fieldValue);
295: } else if (fieldValue instanceof PDTextStream) {
296: setValue(((PDTextStream) fieldValue).getAsString());
297: } else {
298: throw new IOException("Unknown field type:"
299: + fieldValue.getClass().getName());
300: }
301: }
302: Integer ff = fdfField.getFieldFlags();
303: if (ff != null) {
304: setFieldFlags(ff.intValue());
305: } else {
306: //these are suppose to be ignored if the Ff is set.
307: Integer setFf = fdfField.getSetFieldFlags();
308:
309: if (setFf != null) {
310: int setFfInt = setFf.intValue();
311: fieldFlags = fieldFlags | setFfInt;
312: setFieldFlags(fieldFlags);
313: }
314:
315: Integer clrFf = fdfField.getClearFieldFlags();
316: if (clrFf != null) {
317: //we have to clear the bits of the document fields for every bit that is
318: //set in this field.
319: //
320: //Example:
321: //docFf = 1011
322: //clrFf = 1101
323: //clrFfValue = 0010;
324: //newValue = 1011 & 0010 which is 0010
325: int clrFfValue = clrFf.intValue();
326: clrFfValue ^= 0xFFFFFFFF;
327: fieldFlags = fieldFlags & clrFfValue;
328: setFieldFlags(fieldFlags);
329: }
330: }
331:
332: PDAnnotationWidget widget = getWidget();
333: if (widget != null) {
334: int annotFlags = widget.getAnnotationFlags();
335: Integer f = fdfField.getWidgetFieldFlags();
336: if (f != null && widget != null) {
337: widget.setAnnotationFlags(f.intValue());
338: } else {
339: //these are suppose to be ignored if the F is set.
340: Integer setF = fdfField.getSetWidgetFieldFlags();
341: if (setF != null) {
342: annotFlags = annotFlags | setF.intValue();
343: widget.setAnnotationFlags(annotFlags);
344: }
345:
346: Integer clrF = fdfField.getClearWidgetFieldFlags();
347: if (clrF != null) {
348: //we have to clear the bits of the document fields for every bit that is
349: //set in this field.
350: //
351: //Example:
352: //docF = 1011
353: //clrF = 1101
354: //clrFValue = 0010;
355: //newValue = 1011 & 0010 which is 0010
356: int clrFValue = clrF.intValue();
357: clrFValue ^= 0xFFFFFFFFL;
358: annotFlags = annotFlags & clrFValue;
359: widget.setAnnotationFlags(annotFlags);
360: }
361: }
362: }
363: List fdfKids = fdfField.getKids();
364: List pdKids = getKids();
365: for (int i = 0; fdfKids != null && i < fdfKids.size(); i++) {
366: FDFField fdfChild = (FDFField) fdfKids.get(i);
367: String fdfName = fdfChild.getPartialFieldName();
368: for (int j = 0; j < pdKids.size(); j++) {
369: Object pdChildObj = pdKids.get(j);
370: if (pdChildObj instanceof PDField) {
371: PDField pdChild = (PDField) pdChildObj;
372: if (fdfName != null
373: && fdfName.equals(pdChild.getPartialName())) {
374: pdChild.importFDF(fdfChild);
375: }
376: }
377: }
378: }
379: }
380:
381: /**
382: * This will get the single associated widget that is part of this field. This
383: * occurs when the Widget is embedded in the fields dictionary. Sometimes there
384: * are multiple sub widgets associated with this field, in which case you want to
385: * use getKids(). If the kids entry is specified, then the first entry in that
386: * list will be returned.
387: *
388: * @return The widget that is associated with this field.
389: * @throws IOException If there is an error getting the widget object.
390: */
391: public PDAnnotationWidget getWidget() throws IOException {
392: PDAnnotationWidget retval = null;
393: List kids = getKids();
394: if (kids == null) {
395: retval = new PDAnnotationWidget(getDictionary());
396: } else if (kids.size() > 0) {
397: Object firstKid = kids.get(0);
398: if (firstKid instanceof PDAnnotationWidget) {
399: retval = (PDAnnotationWidget) firstKid;
400: } else {
401: retval = ((PDField) firstKid).getWidget();
402: }
403: } else {
404: retval = null;
405: }
406: return retval;
407: }
408:
409: /**
410: * Get the parent field to this field, or null if none exists.
411: *
412: * @return The parent field.
413: *
414: * @throws IOException If there is an error creating the parent field.
415: */
416: public PDField getParent() throws IOException {
417: PDField parent = null;
418: COSDictionary parentDic = (COSDictionary) getDictionary()
419: .getDictionaryObject("Parent");
420: if (parentDic != null) {
421: parent = PDFieldFactory.createField(getAcroForm(),
422: parentDic);
423: }
424: return parent;
425: }
426:
427: /**
428: * Set the parent of this field.
429: *
430: * @param parent The parent to this field.
431: */
432: public void setParent(PDField parent) {
433: getDictionary().setItem("Parent", parent);
434: }
435:
436: /**
437: * This will find one of the child elements. The name array are the components
438: * of the name to search down the tree of names. The nameIndex is where to
439: * start in that array. This method is called recursively until it finds
440: * the end point based on the name array.
441: *
442: * @param name An array that picks the path to the field.
443: * @param nameIndex The index into the array.
444: * @return The field at the endpoint or null if none is found.
445: * @throws IOException If there is an error creating the field.
446: */
447: public PDField findKid(String[] name, int nameIndex)
448: throws IOException {
449: PDField retval = null;
450: COSArray kids = (COSArray) getDictionary().getDictionaryObject(
451: COSName.KIDS);
452: if (kids != null) {
453: for (int i = 0; retval == null && i < kids.size(); i++) {
454: COSDictionary kidDictionary = (COSDictionary) kids
455: .getObject(i);
456: if (name[nameIndex]
457: .equals(kidDictionary.getString("T"))) {
458: retval = PDFieldFactory.createField(acroForm,
459: kidDictionary);
460: if (name.length > nameIndex + 1) {
461: retval = retval.findKid(name, nameIndex + 1);
462: }
463: }
464: }
465: }
466: return retval;
467: }
468:
469: /**
470: * This will get all the kids of this field. The values in the list
471: * will either be PDWidget or PDField. Normally they will be PDWidget objects
472: * unless this is a non-terminal field and they will be child PDField objects.
473: *
474: * @return A list of either PDWidget or PDField objects.
475: * @throws IOException If there is an error retrieving the kids.
476: */
477: public List getKids() throws IOException {
478: List retval = null;
479: COSArray kids = (COSArray) getDictionary().getDictionaryObject(
480: COSName.KIDS);
481: if (kids != null) {
482: List kidsList = new ArrayList();
483: for (int i = 0; i < kids.size(); i++) {
484: COSDictionary kidDictionary = (COSDictionary) kids
485: .getObject(i);
486: COSDictionary parent = (COSDictionary) kidDictionary
487: .getDictionaryObject("Parent");
488: if (kidDictionary.getDictionaryObject("FT") != null
489: || (parent != null && parent
490: .getDictionaryObject("FT") != null)) {
491: kidsList.add(PDFieldFactory.createField(acroForm,
492: kidDictionary));
493: } else if ("Widget".equals(kidDictionary
494: .getNameAsString("Subtype"))) {
495: kidsList.add(new PDAnnotationWidget(kidDictionary));
496: } else {
497: //
498: kidsList.add(PDFieldFactory.createField(acroForm,
499: kidDictionary));
500: }
501: }
502: retval = new COSArrayList(kidsList, kids);
503: }
504: return retval;
505: }
506:
507: /**
508: * This will set the list of kids.
509: *
510: * @param kids The list of child widgets.
511: */
512: public void setKids(List kids) {
513: COSArray kidsArray = COSArrayList.converterToCOSArray(kids);
514: getDictionary().setItem(COSName.KIDS, kidsArray);
515: }
516:
517: /**
518: * This will return a string representation of this field.
519: *
520: * @return A string representation of this field.
521: */
522: public String toString() {
523: return ""
524: + getDictionary().getDictionaryObject(
525: COSName.getPDFName("V"));
526: }
527:
528: /**
529: * This will get the acroform that this field is part of.
530: *
531: * @return The form this field is on.
532: */
533: public PDAcroForm getAcroForm() {
534: return acroForm;
535: }
536:
537: /**
538: * This will set the form this field is on.
539: *
540: * @param value The new form to use.
541: */
542: public void setAcroForm(PDAcroForm value) {
543: acroForm = value;
544: }
545:
546: /**
547: * This will get the dictionary associated with this field.
548: *
549: * @return The dictionary that this class wraps.
550: */
551: public COSDictionary getDictionary() {
552: return dictionary;
553: }
554:
555: /**
556: * Convert this standard java object to a COS object.
557: *
558: * @return The cos object that matches this Java object.
559: */
560: public COSBase getCOSObject() {
561: return dictionary;
562: }
563:
564: /**
565: * Get the additional actions for this field. This will return null
566: * if there are no additional actions for this field.
567: *
568: * @return The actions of the field.
569: */
570: public PDFormFieldAdditionalActions getActions() {
571: COSDictionary aa = (COSDictionary) dictionary
572: .getDictionaryObject("AA");
573: PDFormFieldAdditionalActions retval = null;
574: if (aa != null) {
575: retval = new PDFormFieldAdditionalActions(aa);
576: }
577: return retval;
578: }
579:
580: /**
581: * Set the actions of the field.
582: *
583: * @param actions The field actions.
584: */
585: public void setActions(PDFormFieldAdditionalActions actions) {
586: dictionary.setItem("AA", actions);
587: }
588: }
|