001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - 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: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.pd;
031:
032: import java.util.Iterator;
033: import java.util.List;
034: import de.intarsys.pdf.cos.COSArray;
035: import de.intarsys.pdf.cos.COSBasedObject;
036: import de.intarsys.pdf.cos.COSDictionary;
037: import de.intarsys.pdf.cos.COSInteger;
038: import de.intarsys.pdf.cos.COSName;
039: import de.intarsys.pdf.cos.COSObject;
040: import de.intarsys.pdf.cos.COSTrue;
041:
042: /**
043: * The logical AcroForm hosted in a PDF document.
044: *
045: */
046: public class PDAcroForm extends PDAcroFormNode {
047: /**
048: * The meta class implementation
049: */
050: static public class MetaClass extends PDAcroFormNode.MetaClass {
051: protected MetaClass(Class instanceClass) {
052: super (instanceClass);
053: }
054:
055: protected COSBasedObject doCreateCOSBasedObject(COSObject object) {
056: return new PDAcroForm(object);
057: }
058: }
059:
060: /** The name of the fields entry. */
061: static public final COSName DK_Fields = COSName.constant("Fields"); // //$NON-NLS-1$
062:
063: /** The name of the NeedApperances entry. */
064: static public final COSName DK_NeedAppearances = COSName
065: .constant("NeedAppearances"); // //$NON-NLS-1$
066:
067: /**
068: * The name of the SignatureFlags entry.
069: * <p>
070: * For a list of possible flags:
071: * </p>
072: *
073: * @see de.intarsys.pdf.pd.AcroFormSigFlags
074: */
075: static public final COSName DK_SigFlags = COSName
076: .constant("SigFlags"); //$NON-NLS-1$
077:
078: /** The name of the CalculationOrder entry. */
079: static public final COSName DK_CO = COSName.constant("CO"); //$NON-NLS-1$
080:
081: /** The name of the XFAResources entry. */
082: static public final COSName DK_XFA = COSName.constant("XFA"); // //$NON-NLS-1$
083:
084: /** The meta class instance */
085: static public final MetaClass META = new MetaClass(MetaClass.class
086: .getDeclaringClass());
087:
088: private AcroFormSigFlags sigFlags;
089:
090: private List cachedFields;
091:
092: private boolean fieldsChecked = false;
093:
094: protected PDAcroForm(COSObject object) {
095: super (object);
096: }
097:
098: /*
099: * (non-Javadoc)
100: *
101: * @see de.intarsys.pdf.pd.PDAcroFormNode#getAcroForm()
102: */
103: public PDAcroForm getAcroForm() {
104: return this ;
105: }
106:
107: protected void checkFields() {
108: if (fieldsChecked) {
109: return;
110: }
111: fieldsChecked = true;
112: // reconstruct form fields if there is an empty fields array
113: COSArray fields = cosGetField(DK_Fields).asArray();
114: if ((fields != null) && (fields.size() == 0)) {
115: reconstruct(getDoc());
116: }
117: }
118:
119: /**
120: * Assign the default resource dictionary to be used with the form.
121: *
122: * @param newResources
123: * The new default resource dictionary.
124: */
125: public void setDefaultResources(PDResources newResources) {
126: setFieldObject(DK_DR, newResources);
127: }
128:
129: /**
130: * The default resource dictionary.
131: *
132: * <p>
133: * With 1.5 this is no longer supported as an entry in field dictionaries,
134: * only in the form itself.
135: * </p>
136: *
137: * @return The default resource dictionary.
138: */
139: public PDResources getDefaultResources() {
140: return (PDResources) PDResources.META
141: .createFromCos(cosGetField(DK_DR));
142: }
143:
144: /**
145: * A collection containing the PDAcroFormField objects in their calculation
146: * order or null if no /C events are defined.
147: *
148: * @return A collection containing the PDAcroFormField objects in their
149: * calculation order or null if no /C events are defined.
150: */
151: public List getCalculationOrder() {
152: return getPDObjects(DK_CO, PDAcroFormField.META, false);
153: }
154:
155: /**
156: * A list of all direct {@link PDAcroFormField} instances associated with
157: * this object.
158: *
159: * @return A list of all direct {@link PDAcroFormField} instances associated
160: * with this object.
161: */
162: public List getFields() {
163: checkFields();
164: if (cachedFields == null) {
165: cachedFields = getPDObjects(DK_Fields,
166: PDAcroFormField.META, true);
167: }
168: return cachedFields;
169: }
170:
171: /*
172: * (non-Javadoc)
173: *
174: * @see de.intarsys.pdf.pd.PDObject#getGenericChildren()
175: */
176: public List getGenericChildren() {
177: return getFields();
178: }
179:
180: /*
181: * (non-Javadoc)
182: *
183: * @see de.intarsys.pdf.pd.PDObject#setGenericParent(de.intarsys.pdf.pd.PDObject)
184: */
185: public void setGenericParent(PDObject parent) {
186: throw new IllegalStateException(
187: "AcroForm may not have a parent"); //$NON-NLS-1$
188: }
189:
190: /**
191: * Set the /NeedAppearances field for the form. When <code>true</code>, a
192: * viewer application is required to re-create the visual appearances for
193: * the fields.
194: *
195: * @param newNeedAppearances
196: * The new state for /NewwdAppearances
197: */
198: public void setNeedAppearances(boolean newNeedAppearances) {
199: if (newNeedAppearances) {
200: cosSetField(DK_NeedAppearances, COSTrue.create());
201: } else { // default
202: cosRemoveField(DK_NeedAppearances);
203: }
204: }
205:
206: /**
207: * <code>true</code> if /NeedAppearances is set for this form.
208: *
209: * @return <code>true</code> if /NeedAppearances is set for this form.
210: */
211: public boolean getNeedAppearances() {
212: return getFieldBoolean(DK_NeedAppearances, false);
213: }
214:
215: /**
216: * The flags associated with an AcroForm.
217: *
218: * @return The flags associated with an AcroForm.
219: */
220: public AcroFormSigFlags getSigFlags() {
221: if (sigFlags == null) {
222: sigFlags = new AcroFormSigFlags(this );
223: }
224: return sigFlags;
225: }
226:
227: /*
228: * (non-Javadoc)
229: *
230: * @see de.intarsys.pdf.pd.PDAcroFormNode#addField(de.intarsys.pdf.pd.PDAcroFormField)
231: */
232: public void addField(PDAcroFormField field) {
233: checkFields();
234: cosAddField(field.cosGetDict());
235: field.setParent(null);
236: if (field.isTypeSig()) {
237: getSigFlags().setSignatureExists(true);
238: }
239: }
240:
241: /*
242: * (non-Javadoc)
243: *
244: * @see de.intarsys.pdf.pd.PDAcroFormNode#removeField(de.intarsys.pdf.pd.PDAcroFormField)
245: */
246: public boolean removeField(PDAcroFormField field) {
247: getFields().remove(field);
248: COSArray cosFields = cosGetField(DK_Fields).asArray();
249: if (cosFields == null) {
250: return false;
251: }
252: boolean removed = cosFields.remove(field.cosGetDict());
253: getSigFlags().setSignatureExists(isSignatureExists());
254: return removed;
255: }
256:
257: public String toString() {
258: return "AcroForm " + super .toString(); //$NON-NLS-1$
259: }
260:
261: protected void setCalculationOrder(List newCalculationOrder) {
262: setPDObjects(DK_CO, newCalculationOrder);
263: }
264:
265: protected void cosSetSigFlags(int newFlags) {
266: if (newFlags != 0) { // default
267: cosSetField(DK_SigFlags, COSInteger.create(newFlags));
268: } else {
269: cosRemoveField(DK_SigFlags);
270: }
271: }
272:
273: /**
274: * This method scans the document for all WidgetAnnotation objects.
275: *
276: * <p>
277: * This is done because some writer do not create a correct list of all
278: * PDAcroFormField objects in the AcroForm. In the case that the list of
279: * children is empty, we go and search ourselve for candidates...
280: * </p>
281: *
282: * @param doc
283: * The document to reconstruct.
284: */
285: protected void reconstruct(PDDocument doc) {
286: if (doc == null) {
287: return;
288: }
289: PDPageTree pageTree = doc.getPageTree();
290: if (pageTree == null) {
291: return;
292: }
293: for (PDPage page = pageTree.getFirstPage(); page != null; page = page
294: .getNextPage()) {
295: List annotations = page.getAnnotations();
296: if (annotations == null) {
297: continue;
298: }
299: for (Iterator it = annotations.iterator(); it.hasNext();) {
300: PDAnnotation annot = (PDAnnotation) it.next();
301: if (annot.isWidgetAnnotation()) {
302: PDAcroFormField field = (PDAcroFormField) PDAcroFormField.META
303: .createFromCos(annot.cosGetDict());
304: addField(field);
305: }
306: }
307: }
308: }
309:
310: private void cosAddField(COSDictionary field) {
311: COSArray cosFields = cosGetField(DK_Fields).asArray();
312: if (cosFields == null) {
313: cosFields = COSArray.create();
314: cosFields.beIndirect();
315: cosSetField(DK_Fields, cosFields);
316: }
317: cosFields.add(field);
318: }
319:
320: /*
321: * (non-Javadoc)
322: *
323: * @see de.intarsys.pdf.pd.PDAcroFormNode#invalidateCaches()
324: */
325: public void invalidateCaches() {
326: super .invalidateCaches();
327: COSArray cosFields = cosGetField(DK_Fields).asArray();
328: if (cosFields != null) {
329: cosFields.removeObjectListener(this );
330: }
331: cachedFields = null;
332: }
333:
334: /**
335: * The /XFA entry of this.
336: *
337: * @return The /XFA entry of this.
338: */
339: public COSObject cosGetXfa() {
340: return cosGetField(DK_XFA);
341: }
342:
343: /**
344: * <code>true</code> if this form has a signed signature field.
345: *
346: * @return <code>true</code> if this form has a signed signature field.
347: */
348: public boolean isSigned() {
349: for (Iterator i = collectLeafFields().iterator(); i.hasNext();) {
350: PDAcroFormField field = (PDAcroFormField) i.next();
351: if (field.isTypeSig()
352: && ((PDAFSignatureField) field).isSigned()) {
353: return true;
354: }
355: }
356: return false;
357: }
358:
359: /**
360: * <code>true</code> if this form has a signature field. This is NOT the
361: * same as the flag in the SigFlags entry but may be used to compute this
362: * entry.
363: *
364: * @return <code>true</code> if this form has a signature field.
365: */
366: public boolean isSignatureExists() {
367: for (Iterator i = collectLeafFields().iterator(); i.hasNext();) {
368: PDAcroFormField field = (PDAcroFormField) i.next();
369: if (field.isTypeSig()) {
370: return true;
371: }
372: }
373: return false;
374: }
375: }
|