KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > AcroFields


1 /*
2  * Copyright 2003-2005 by Paulo Soares.
3  *
4  * The contents of this file are subject to the Mozilla Public License Version 1.1
5  * (the "License"); you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the License.
11  *
12  * The Original Code is 'iText, a free JAVA-PDF library'.
13  *
14  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
15  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
16  * All Rights Reserved.
17  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
18  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
19  *
20  * Contributor(s): all the names of the contributors are added in the source code
21  * where applicable.
22  *
23  * Alternatively, the contents of this file may be used under the terms of the
24  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
25  * provisions of LGPL are applicable instead of those above. If you wish to
26  * allow use of your version of this file only under the terms of the LGPL
27  * License and not to allow others to use your version of this file under
28  * the MPL, indicate your decision by deleting the provisions above and
29  * replace them with the notice and other provisions required by the LGPL.
30  * If you do not delete the provisions above, a recipient may use your version
31  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
32  *
33  * This library is free software; you can redistribute it and/or modify it
34  * under the terms of the MPL as stated above or under the terms of the GNU
35  * Library General Public License as published by the Free Software Foundation;
36  * either version 2 of the License, or any later version.
37  *
38  * This library is distributed in the hope that it will be useful, but WITHOUT
39  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
41  * details.
42  *
43  * If you didn't download this code from the following link, you should check if
44  * you aren't using an obsolete version:
45  * http://www.lowagie.com/iText/
46  */

47 package com.lowagie.text.pdf;
48
49 import java.awt.Color JavaDoc;
50 import java.io.IOException JavaDoc;
51 import java.io.InputStream JavaDoc;
52 import java.util.ArrayList JavaDoc;
53 import java.util.Collections JavaDoc;
54 import java.util.Comparator JavaDoc;
55 import java.util.HashMap JavaDoc;
56 import java.util.Iterator JavaDoc;
57 import java.util.Map JavaDoc;
58
59 import org.w3c.dom.Node JavaDoc;
60
61 import com.lowagie.text.DocumentException;
62 import com.lowagie.text.Element;
63 import com.lowagie.text.ExceptionConverter;
64 import com.lowagie.text.Rectangle;
65
66 /** Query and change fields in existing documents either by method
67  * calls or by FDF merging.
68  * @author Paulo Soares (psoares@consiste.pt)
69  */

70 public class AcroFields {
71
72     PdfReader reader;
73     PdfWriter writer;
74     HashMap JavaDoc fields;
75     private int topFirst;
76     private HashMap JavaDoc sigNames;
77     private boolean append;
78     public static final int DA_FONT = 0;
79     public static final int DA_SIZE = 1;
80     public static final int DA_COLOR = 2;
81     private HashMap JavaDoc extensionFonts = new HashMap JavaDoc();
82     private XfaForm xfa;
83     /**
84      * A field type invalid or not found.
85      */

86     public static final int FIELD_TYPE_NONE = 0;
87     /**
88      * A field type.
89      */

90     public static final int FIELD_TYPE_PUSHBUTTON = 1;
91     /**
92      * A field type.
93      */

94     public static final int FIELD_TYPE_CHECKBOX = 2;
95     /**
96      * A field type.
97      */

98     public static final int FIELD_TYPE_RADIOBUTTON = 3;
99     /**
100      * A field type.
101      */

102     public static final int FIELD_TYPE_TEXT = 4;
103     /**
104      * A field type.
105      */

106     public static final int FIELD_TYPE_LIST = 5;
107     /**
108      * A field type.
109      */

110     public static final int FIELD_TYPE_COMBO = 6;
111     /**
112      * A field type.
113      */

114     public static final int FIELD_TYPE_SIGNATURE = 7;
115     
116     private boolean lastWasString;
117     
118     /** Holds value of property generateAppearances. */
119     private boolean generateAppearances = true;
120     
121     private HashMap JavaDoc localFonts = new HashMap JavaDoc();
122     
123     private float extraMarginLeft;
124     private float extraMarginTop;
125     private ArrayList JavaDoc substitutionFonts;
126     
127     AcroFields(PdfReader reader, PdfWriter writer) {
128         this.reader = reader;
129         this.writer = writer;
130         try {
131             xfa = new XfaForm(reader);
132         }
133         catch (Exception JavaDoc e) {
134             throw new ExceptionConverter(e);
135         }
136         if (writer instanceof PdfStamperImp) {
137             append = ((PdfStamperImp)writer).isAppend();
138         }
139         fill();
140     }
141
142     void fill() {
143         fields = new HashMap JavaDoc();
144         PdfDictionary top = (PdfDictionary)PdfReader.getPdfObjectRelease(reader.getCatalog().get(PdfName.ACROFORM));
145         if (top == null)
146             return;
147         PdfArray arrfds = (PdfArray)PdfReader.getPdfObjectRelease(top.get(PdfName.FIELDS));
148         if (arrfds == null || arrfds.size() == 0)
149             return;
150         arrfds = null;
151         for (int k = 1; k <= reader.getNumberOfPages(); ++k) {
152             PdfDictionary page = reader.getPageNRelease(k);
153             PdfArray annots = (PdfArray)PdfReader.getPdfObjectRelease(page.get(PdfName.ANNOTS), page);
154             if (annots == null)
155                 continue;
156             ArrayList JavaDoc arr = annots.getArrayList();
157             for (int j = 0; j < arr.size(); ++j) {
158                 PdfObject annoto = PdfReader.getPdfObject((PdfObject)arr.get(j), annots);
159                 if (!(annoto instanceof PdfDictionary)) {
160                     PdfReader.releaseLastXrefPartial((PdfObject)arr.get(j));
161                     continue;
162                 }
163                 PdfDictionary annot = (PdfDictionary)annoto;
164                 if (!PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE))) {
165                     PdfReader.releaseLastXrefPartial((PdfObject)arr.get(j));
166                     continue;
167                 }
168                 PdfDictionary widget = annot;
169                 PdfDictionary dic = new PdfDictionary();
170                 dic.putAll(annot);
171                 String JavaDoc name = "";
172                 PdfDictionary value = null;
173                 PdfObject lastV = null;
174                 while (annot != null) {
175                     dic.mergeDifferent(annot);
176                     PdfString t = (PdfString)PdfReader.getPdfObject(annot.get(PdfName.T));
177                     if (t != null)
178                         name = t.toUnicodeString() + "." + name;
179                     if (lastV == null && annot.get(PdfName.V) != null)
180                         lastV = PdfReader.getPdfObjectRelease(annot.get(PdfName.V));
181                     if (value == null && t != null) {
182                         value = annot;
183                         if (annot.get(PdfName.V) == null && lastV != null)
184                             value.put(PdfName.V, lastV);
185                     }
186                     annot = (PdfDictionary)PdfReader.getPdfObject(annot.get(PdfName.PARENT), annot);
187                 }
188                 if (name.length() > 0)
189                     name = name.substring(0, name.length() - 1);
190                 Item item = (Item)fields.get(name);
191                 if (item == null) {
192                     item = new Item();
193                     fields.put(name, item);
194                 }
195                 if (value == null)
196                     item.values.add(widget);
197                 else
198                     item.values.add(value);
199                 item.widgets.add(widget);
200                 item.widget_refs.add(arr.get(j)); // must be a reference
201
if (top != null)
202                     dic.mergeDifferent(top);
203                 item.merged.add(dic);
204                 item.page.add(new Integer JavaDoc(k));
205                 item.tabOrder.add(new Integer JavaDoc(j));
206             }
207         }
208     }
209     
210     /** Gets the list of appearance names. Use it to get the names allowed
211      * with radio and checkbox fields. If the /Opt key exists the values will
212      * also be included. The name 'Off' may also be valid
213      * even if not returned in the list.
214      * @param fieldName the fully qualified field name
215      * @return the list of names or <CODE>null</CODE> if the field does not exist
216      */

217     public String JavaDoc[] getAppearanceStates(String JavaDoc fieldName) {
218         Item fd = (Item)fields.get(fieldName);
219         if (fd == null)
220             return null;
221         HashMap JavaDoc names = new HashMap JavaDoc();
222         PdfDictionary vals = (PdfDictionary)fd.values.get(0);
223         PdfObject opts = PdfReader.getPdfObject(vals.get(PdfName.OPT));
224         if (opts != null) {
225             if (opts.isString())
226                 names.put(((PdfString)opts).toUnicodeString(), null);
227             else if (opts.isArray()) {
228                 ArrayList JavaDoc list = ((PdfArray)opts).getArrayList();
229                 for (int k = 0; k < list.size(); ++k) {
230                     PdfObject v = PdfReader.getPdfObject((PdfObject)list.get(k));
231                     if (v != null && v.isString())
232                         names.put(((PdfString)v).toUnicodeString(), null);
233                 }
234             }
235         }
236         ArrayList JavaDoc wd = fd.widgets;
237         for (int k = 0; k < wd.size(); ++k) {
238             PdfDictionary dic = (PdfDictionary)wd.get(k);
239             dic = (PdfDictionary)PdfReader.getPdfObject(dic.get(PdfName.AP));
240             if (dic == null)
241                 continue;
242             PdfObject ob = PdfReader.getPdfObject(dic.get(PdfName.N));
243             if (ob == null || !ob.isDictionary())
244                 continue;
245             dic = (PdfDictionary)ob;
246             for (Iterator JavaDoc it = dic.getKeys().iterator(); it.hasNext();) {
247                 String JavaDoc name = PdfName.decodeName(((PdfName)it.next()).toString());
248                 names.put(name, null);
249             }
250         }
251         String JavaDoc out[] = new String JavaDoc[names.size()];
252         return (String JavaDoc[])names.keySet().toArray(out);
253     }
254     
255     private String JavaDoc[] getListOption(String JavaDoc fieldName, int idx) {
256         Item fd = getFieldItem(fieldName);
257         if (fd == null)
258             return null;
259         PdfObject obj = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.OPT));
260         if (obj == null || !obj.isArray())
261             return null;
262         PdfArray ar = (PdfArray)obj;
263         String JavaDoc[] ret = new String JavaDoc[ar.size()];
264         ArrayList JavaDoc a = ar.getArrayList();
265         for (int k = 0; k < a.size(); ++k) {
266             obj = PdfReader.getPdfObject((PdfObject)a.get(k));
267             try {
268                 if (obj.isArray()) {
269                     obj = (PdfObject)((PdfArray)obj).getArrayList().get(idx);
270                 }
271                 if (obj.isString())
272                     ret[k] = ((PdfString)obj).toUnicodeString();
273                 else
274                     ret[k] = obj.toString();
275             }
276             catch (Exception JavaDoc e) {
277                 ret[k] = "";
278             }
279         }
280         return ret;
281     }
282     
283     /**
284      * Gets the list of export option values from fields of type list or combo.
285      * If the field doesn't exist or the field type is not list or combo it will return
286      * <CODE>null</CODE>.
287      * @param fieldName the field name
288      * @return the list of export option values from fields of type list or combo
289      */

290     public String JavaDoc[] getListOptionExport(String JavaDoc fieldName) {
291         return getListOption(fieldName, 0);
292     }
293     
294     /**
295      * Gets the list of display option values from fields of type list or combo.
296      * If the field doesn't exist or the field type is not list or combo it will return
297      * <CODE>null</CODE>.
298      * @param fieldName the field name
299      * @return the list of export option values from fields of type list or combo
300      */

301     public String JavaDoc[] getListOptionDisplay(String JavaDoc fieldName) {
302         return getListOption(fieldName, 1);
303     }
304     
305     /**
306      * Sets the option list for fields of type list or combo. One of <CODE>exportValues</CODE>
307      * or <CODE>displayValues</CODE> may be <CODE>null</CODE> but not both. This method will only
308      * set the list but will not set the value or appearance. For that, calling <CODE>setField()</CODE>
309      * is required.
310      * <p>
311      * An example:
312      * <p>
313      * <PRE>
314      * PdfReader pdf = new PdfReader("input.pdf");
315      * PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("output.pdf"));
316      * AcroFields af = stp.getAcroFields();
317      * af.setListOption("ComboBox", new String[]{"a", "b", "c"}, new String[]{"first", "second", "third"});
318      * af.setField("ComboBox", "b");
319      * stp.close();
320      * </PRE>
321      * @param fieldName the field name
322      * @param exportValues the export values
323      * @param displayValues the display values
324      * @return <CODE>true</CODE> if the operation succeeded, <CODE>false</CODE> otherwise
325      */

326     public boolean setListOption(String JavaDoc fieldName, String JavaDoc[] exportValues, String JavaDoc[] displayValues) {
327         if (exportValues == null && displayValues == null)
328             return false;
329         if (exportValues != null && displayValues != null && exportValues.length != displayValues.length)
330             throw new IllegalArgumentException JavaDoc("The export and the display array must have the same size.");
331         int ftype = getFieldType(fieldName);
332         if (ftype != FIELD_TYPE_COMBO && ftype != FIELD_TYPE_LIST)
333             return false;
334         Item fd = (Item)fields.get(fieldName);
335         String JavaDoc[] sing = null;
336         if (exportValues == null && displayValues != null)
337             sing = displayValues;
338         else if (exportValues != null && displayValues == null)
339             sing = exportValues;
340         PdfArray opt = new PdfArray();
341         if (sing != null) {
342             for (int k = 0; k < sing.length; ++k)
343                 opt.add(new PdfString(sing[k], PdfObject.TEXT_UNICODE));
344         }
345         else {
346             for (int k = 0; k < exportValues.length; ++k) {
347                 PdfArray a = new PdfArray();
348                 a.add(new PdfString(exportValues[k], PdfObject.TEXT_UNICODE));
349                 a.add(new PdfString(displayValues[k], PdfObject.TEXT_UNICODE));
350                 opt.add(a);
351             }
352         }
353         ((PdfDictionary)fd.values.get(0)).put(PdfName.OPT, opt);
354         for (int j = 0; j < fd.merged.size(); ++j)
355             ((PdfDictionary)fd.merged.get(j)).put(PdfName.OPT, opt);
356         return true;
357     }
358     
359     /**
360      * Gets the field type. The type can be one of: <CODE>FIELD_TYPE_PUSHBUTTON</CODE>,
361      * <CODE>FIELD_TYPE_CHECKBOX</CODE>, <CODE>FIELD_TYPE_RADIOBUTTON</CODE>,
362      * <CODE>FIELD_TYPE_TEXT</CODE>, <CODE>FIELD_TYPE_LIST</CODE>,
363      * <CODE>FIELD_TYPE_COMBO</CODE> or <CODE>FIELD_TYPE_SIGNATURE</CODE>.
364      * <p>
365      * If the field does not exist or is invalid it returns
366      * <CODE>FIELD_TYPE_NONE</CODE>.
367      * @param fieldName the field name
368      * @return the field type
369      */

370     public int getFieldType(String JavaDoc fieldName) {
371         Item fd = getFieldItem(fieldName);
372         if (fd == null)
373             return FIELD_TYPE_NONE;
374         PdfObject type = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.FT));
375         if (type == null)
376             return FIELD_TYPE_NONE;
377         int ff = 0;
378         PdfObject ffo = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.FF));
379         if (ffo != null && ffo.type() == PdfObject.NUMBER)
380             ff = ((PdfNumber)ffo).intValue();
381         if (PdfName.BTN.equals(type)) {
382             if ((ff & PdfFormField.FF_PUSHBUTTON) != 0)
383                 return FIELD_TYPE_PUSHBUTTON;
384             if ((ff & PdfFormField.FF_RADIO) != 0)
385                 return FIELD_TYPE_RADIOBUTTON;
386             else
387                 return FIELD_TYPE_CHECKBOX;
388         }
389         else if (PdfName.TX.equals(type)) {
390             return FIELD_TYPE_TEXT;
391         }
392         else if (PdfName.CH.equals(type)) {
393             if ((ff & PdfFormField.FF_COMBO) != 0)
394                 return FIELD_TYPE_COMBO;
395             else
396                 return FIELD_TYPE_LIST;
397         }
398         else if (PdfName.SIG.equals(type)) {
399             return FIELD_TYPE_SIGNATURE;
400         }
401         return FIELD_TYPE_NONE;
402     }
403     
404     /**
405      * Export the fields as a FDF.
406      * @param writer the FDF writer
407      */

408     public void exportAsFdf(FdfWriter writer) {
409         for (Iterator JavaDoc it = fields.entrySet().iterator(); it.hasNext();) {
410             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
411             Item item = (Item)entry.getValue();
412             String JavaDoc name = (String JavaDoc)entry.getKey();
413             PdfObject v = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.V));
414             if (v == null)
415                 continue;
416             String JavaDoc value = getField(name);
417             if (lastWasString)
418                 writer.setFieldAsString(name, value);
419             else
420                 writer.setFieldAsName(name, value);
421         }
422     }
423     
424     /**
425      * Renames a field. Only the last part of the name can be renamed. For example,
426      * if the original field is "ab.cd.ef" only the "ef" part can be renamed.
427      * @param oldName the old field name
428      * @param newName the new field name
429      * @return <CODE>true</CODE> if the renaming was successful, <CODE>false</CODE>
430      * otherwise
431      */

432     public boolean renameField(String JavaDoc oldName, String JavaDoc newName) {
433         int idx1 = oldName.lastIndexOf('.') + 1;
434         int idx2 = newName.lastIndexOf('.') + 1;
435         if (idx1 != idx2)
436             return false;
437         if (!oldName.substring(0, idx1).equals(newName.substring(0, idx2)))
438             return false;
439         if (fields.containsKey(newName))
440             return false;
441         Item item = (Item)fields.get(oldName);
442         if (item == null)
443             return false;
444         newName = newName.substring(idx2);
445         PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE);
446         for (int k = 0; k < item.merged.size(); ++k) {
447             PdfDictionary dic = (PdfDictionary)item.values.get(k);
448             dic.put(PdfName.T, ss);
449             markUsed(dic);
450             dic = (PdfDictionary)item.merged.get(k);
451             dic.put(PdfName.T, ss);
452         }
453         fields.remove(oldName);
454         fields.put(newName, item);
455         return true;
456     }
457     
458     public static Object JavaDoc[] splitDAelements(String JavaDoc da) {
459         try {
460             PRTokeniser tk = new PRTokeniser(PdfEncodings.convertToBytes(da, null));
461             ArrayList JavaDoc stack = new ArrayList JavaDoc();
462             Object JavaDoc ret[] = new Object JavaDoc[3];
463             while (tk.nextToken()) {
464                 if (tk.getTokenType() == PRTokeniser.TK_COMMENT)
465                     continue;
466                 if (tk.getTokenType() == PRTokeniser.TK_OTHER) {
467                     String JavaDoc operator = tk.getStringValue();
468                     if (operator.equals("Tf")) {
469                         if (stack.size() >= 2) {
470                             ret[DA_FONT] = stack.get(stack.size() - 2);
471                             ret[DA_SIZE] = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 1));
472                         }
473                     }
474                     else if (operator.equals("g")) {
475                         if (stack.size() >= 1) {
476                             float gray = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 1)).floatValue();
477                             if (gray != 0)
478                                 ret[DA_COLOR] = new GrayColor(gray);
479                         }
480                     }
481                     else if (operator.equals("rg")) {
482                         if (stack.size() >= 3) {
483                             float red = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 3)).floatValue();
484                             float green = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 2)).floatValue();
485                             float blue = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 1)).floatValue();
486                             ret[DA_COLOR] = new Color JavaDoc(red, green, blue);
487                         }
488                     }
489                     else if (operator.equals("k")) {
490                         if (stack.size() >= 4) {
491                             float cyan = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 4)).floatValue();
492                             float magenta = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 3)).floatValue();
493                             float yellow = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 2)).floatValue();
494                             float black = new Float JavaDoc((String JavaDoc)stack.get(stack.size() - 1)).floatValue();
495                             ret[DA_COLOR] = new CMYKColor(cyan, magenta, yellow, black);
496                         }
497                     }
498                     stack.clear();
499                 }
500                 else
501                     stack.add(tk.getStringValue());
502             }
503             return ret;
504         }
505         catch (IOException JavaDoc ioe) {
506             throw new ExceptionConverter(ioe);
507         }
508     }
509     
510     public void decodeGenericDictionary(PdfDictionary merged, BaseField tx) throws IOException JavaDoc, DocumentException {
511         int flags = 0;
512         // the text size and color
513
PdfString da = (PdfString)PdfReader.getPdfObject(merged.get(PdfName.DA));
514         if (da != null) {
515             Object JavaDoc dab[] = splitDAelements(da.toUnicodeString());
516             if (dab[DA_SIZE] != null)
517                 tx.setFontSize(((Float JavaDoc)dab[DA_SIZE]).floatValue());
518             if (dab[DA_COLOR] != null)
519                 tx.setTextColor((Color JavaDoc)dab[DA_COLOR]);
520             if (dab[DA_FONT] != null) {
521                 PdfDictionary font = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.DR));
522                 if (font != null) {
523                     font = (PdfDictionary)PdfReader.getPdfObject(font.get(PdfName.FONT));
524                     if (font != null) {
525                         PdfObject po = font.get(new PdfName((String JavaDoc)dab[DA_FONT]));
526                         if (po != null && po.type() == PdfObject.INDIRECT) {
527                             PRIndirectReference por = (PRIndirectReference)po;
528                             BaseFont bp = new DocumentFont((PRIndirectReference)po);
529                             tx.setFont(bp);
530                             Integer JavaDoc porkey = new Integer JavaDoc(por.getNumber());
531                             BaseFont porf = (BaseFont)extensionFonts.get(porkey);
532                             if (porf == null) {
533                                 if (!extensionFonts.containsKey(porkey)) {
534                                     PdfDictionary fo = (PdfDictionary)PdfReader.getPdfObject(po);
535                                     PdfDictionary fd = (PdfDictionary)PdfReader.getPdfObject(fo.get(PdfName.FONTDESCRIPTOR));
536                                     if (fd != null) {
537                                         PRStream prs = (PRStream)PdfReader.getPdfObject(fd.get(PdfName.FONTFILE2));
538                                         if (prs == null)
539                                             prs = (PRStream)PdfReader.getPdfObject(fd.get(PdfName.FONTFILE3));
540                                         if (prs == null) {
541                                             extensionFonts.put(porkey, null);
542                                         }
543                                         else {
544                                             try {
545                                                 porf = BaseFont.createFont("font.ttf", BaseFont.IDENTITY_H, true, false, PdfReader.getStreamBytes(prs), null);
546                                             }
547                                             catch (Exception JavaDoc e) {
548                                             }
549                                             extensionFonts.put(porkey, porf);
550                                         }
551                                     }
552                                 }
553                             }
554                             if (tx instanceof TextField)
555                                 ((TextField)tx).setExtensionFont(porf);
556                         }
557                         else {
558                             BaseFont bf = (BaseFont)localFonts.get(dab[DA_FONT]);
559                             if (bf == null) {
560                                 String JavaDoc fn[] = (String JavaDoc[])stdFieldFontNames.get(dab[DA_FONT]);
561                                 if (fn != null) {
562                                     try {
563                                         String JavaDoc enc = "winansi";
564                                         if (fn.length > 1)
565                                             enc = fn[1];
566                                         bf = BaseFont.createFont(fn[0], enc, false);
567                                         tx.setFont(bf);
568                                     }
569                                     catch (Exception JavaDoc e) {
570                                         // empty
571
}
572                                 }
573                             }
574                             else
575                                 tx.setFont(bf);
576                         }
577                     }
578                 }
579             }
580         }
581         //rotation, border and backgound color
582
PdfDictionary mk = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.MK));
583         if (mk != null) {
584             PdfArray ar = (PdfArray)PdfReader.getPdfObject(mk.get(PdfName.BC));
585             Color JavaDoc border = getMKColor(ar);
586             tx.setBorderColor(border);
587             if (border != null)
588                 tx.setBorderWidth(1);
589             ar = (PdfArray)PdfReader.getPdfObject(mk.get(PdfName.BG));
590             tx.setBackgroundColor(getMKColor(ar));
591             PdfNumber rotation = (PdfNumber)PdfReader.getPdfObject(mk.get(PdfName.R));
592             if (rotation != null)
593                 tx.setRotation(rotation.intValue());
594         }
595         //flags
596
PdfNumber nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.F));
597         flags = 0;
598         if (nfl != null) {
599             flags = nfl.intValue();
600             if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) != 0)
601                 tx.setVisibility(BaseField.HIDDEN);
602             else if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_NOVIEW) != 0)
603                 tx.setVisibility(BaseField.HIDDEN_BUT_PRINTABLE);
604             else if ((flags & PdfFormField.FLAGS_PRINT) != 0)
605                 tx.setVisibility(BaseField.VISIBLE);
606             else
607                 tx.setVisibility(BaseField.VISIBLE_BUT_DOES_NOT_PRINT);
608         }
609         //multiline
610
nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.FF));
611         flags = 0;
612         if (nfl != null)
613             flags = nfl.intValue();
614         tx.setOptions(flags);
615         if ((flags & PdfFormField.FF_COMB) != 0) {
616             PdfNumber maxLen = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.MAXLEN));
617             int len = 0;
618             if (maxLen != null)
619                 len = maxLen.intValue();
620             tx.setMaxCharacterLength(len);
621         }
622         //alignment
623
nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.Q));
624         if (nfl != null) {
625             if (nfl.intValue() == PdfFormField.Q_CENTER)
626                 tx.setAlignment(Element.ALIGN_CENTER);
627             else if (nfl.intValue() == PdfFormField.Q_RIGHT)
628                 tx.setAlignment(Element.ALIGN_RIGHT);
629         }
630         //border styles
631
PdfDictionary bs = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.BS));
632         if (bs != null) {
633             PdfNumber w = (PdfNumber)PdfReader.getPdfObject(bs.get(PdfName.W));
634             if (w != null)
635                 tx.setBorderWidth(w.floatValue());
636             PdfName s = (PdfName)PdfReader.getPdfObject(bs.get(PdfName.S));
637             if (PdfName.D.equals(s))
638                 tx.setBorderStyle(PdfBorderDictionary.STYLE_DASHED);
639             else if (PdfName.B.equals(s))
640                 tx.setBorderStyle(PdfBorderDictionary.STYLE_BEVELED);
641             else if (PdfName.I.equals(s))
642                 tx.setBorderStyle(PdfBorderDictionary.STYLE_INSET);
643             else if (PdfName.U.equals(s))
644                 tx.setBorderStyle(PdfBorderDictionary.STYLE_UNDERLINE);
645         }
646         else {
647             PdfArray bd = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.BORDER));
648             if (bd != null) {
649                 ArrayList JavaDoc ar = bd.getArrayList();
650                 if (ar.size() >= 3)
651                     tx.setBorderWidth(((PdfNumber)ar.get(2)).floatValue());
652                 if (ar.size() >= 4)
653                     tx.setBorderStyle(PdfBorderDictionary.STYLE_DASHED);
654             }
655         }
656     }
657     
658     PdfAppearance getAppearance(PdfDictionary merged, String JavaDoc text, String JavaDoc fieldName) throws IOException JavaDoc, DocumentException {
659         topFirst = 0;
660         TextField tx = null;
661         if (fieldCache == null || !fieldCache.containsKey(fieldName)) {
662             tx = new TextField(writer, null, null);
663             tx.setExtraMargin(extraMarginLeft, extraMarginTop);
664             tx.setBorderWidth(0);
665             tx.setSubstitutionFonts(substitutionFonts);
666             decodeGenericDictionary(merged, tx);
667             //rect
668
PdfArray rect = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.RECT));
669             Rectangle box = PdfReader.getNormalizedRectangle(rect);
670             if (tx.getRotation() == 90 || tx.getRotation() == 270)
671                 box = box.rotate();
672             tx.setBox(box);
673             if (fieldCache != null)
674                 fieldCache.put(fieldName, tx);
675         }
676         else {
677             tx = (TextField)fieldCache.get(fieldName);
678             tx.setWriter(writer);
679         }
680         PdfName fieldType = (PdfName)PdfReader.getPdfObject(merged.get(PdfName.FT));
681         if (PdfName.TX.equals(fieldType)) {
682             tx.setText(text);
683             return tx.getAppearance();
684         }
685         if (!PdfName.CH.equals(fieldType))
686             throw new DocumentException("An appearance was requested without a variable text field.");
687         PdfArray opt = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.OPT));
688         int flags = 0;
689         PdfNumber nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.FF));
690         if (nfl != null)
691             flags = nfl.intValue();
692         if ((flags & PdfFormField.FF_COMBO) != 0 && opt == null) {
693             tx.setText(text);
694             return tx.getAppearance();
695         }
696         if (opt != null) {
697             ArrayList JavaDoc op = opt.getArrayList();
698             String JavaDoc choices[] = new String JavaDoc[op.size()];
699             String JavaDoc choicesExp[] = new String JavaDoc[op.size()];
700             for (int k = 0; k < op.size(); ++k) {
701                 PdfObject obj = (PdfObject)op.get(k);
702                 if (obj.isString()) {
703                     choices[k] = choicesExp[k] = ((PdfString)obj).toUnicodeString();
704                 }
705                 else {
706                     ArrayList JavaDoc opar = ((PdfArray)obj).getArrayList();
707                     choicesExp[k] = ((PdfString)opar.get(0)).toUnicodeString();
708                     choices[k] = ((PdfString)opar.get(1)).toUnicodeString();
709                 }
710             }
711             if ((flags & PdfFormField.FF_COMBO) != 0) {
712                 for (int k = 0; k < choices.length; ++k) {
713                     if (text.equals(choicesExp[k])) {
714                         text = choices[k];
715                         break;
716                     }
717                 }
718                 tx.setText(text);
719                 return tx.getAppearance();
720             }
721             int idx = 0;
722             for (int k = 0; k < choicesExp.length; ++k) {
723                 if (text.equals(choicesExp[k])) {
724                     idx = k;
725                     break;
726                 }
727             }
728             tx.setChoices(choices);
729             tx.setChoiceExports(choicesExp);
730             tx.setChoiceSelection(idx);
731         }
732         PdfAppearance app = tx.getListAppearance();
733         topFirst = tx.getTopFirst();
734         return app;
735     }
736     
737     Color JavaDoc getMKColor(PdfArray ar) {
738         if (ar == null)
739             return null;
740         ArrayList JavaDoc cc = ar.getArrayList();
741         switch (cc.size()) {
742             case 1:
743                 return new GrayColor(((PdfNumber)cc.get(0)).floatValue());
744             case 3:
745                 return new Color JavaDoc(ExtendedColor.normalize(((PdfNumber)cc.get(0)).floatValue()), ExtendedColor.normalize(((PdfNumber)cc.get(1)).floatValue()), ExtendedColor.normalize(((PdfNumber)cc.get(2)).floatValue()));
746             case 4:
747                 return new CMYKColor(((PdfNumber)cc.get(0)).floatValue(), ((PdfNumber)cc.get(1)).floatValue(), ((PdfNumber)cc.get(2)).floatValue(), ((PdfNumber)cc.get(3)).floatValue());
748             default:
749                 return null;
750         }
751     }
752     
753     /** Gets the field value.
754      * @param name the fully qualified field name
755      * @return the field value
756      */

757     public String JavaDoc getField(String JavaDoc name) {
758         if (xfa.isXfaPresent()) {
759             name = xfa.findFieldName(name, this);
760             if (name == null)
761                 return null;
762             name = XfaForm.Xml2Som.getShortName(name);
763             return XfaForm.getNodeText(xfa.findDatasetsNode(name));
764         }
765         Item item = (Item)fields.get(name);
766         if (item == null)
767             return null;
768         lastWasString = false;
769         PdfObject v = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.V));
770         if (v == null)
771             return "";
772         PdfName type = (PdfName)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FT));
773         if (PdfName.BTN.equals(type)) {
774             PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FF));
775             int flags = 0;
776             if (ff != null)
777                 flags = ff.intValue();
778             if ((flags & PdfFormField.FF_PUSHBUTTON) != 0)
779                 return "";
780             String JavaDoc value = "";
781             if (v.isName())
782                 value = PdfName.decodeName(v.toString());
783             else if (v.isString())
784                 value = ((PdfString)v).toUnicodeString();
785             PdfObject opts = PdfReader.getPdfObject(((PdfDictionary)item.values.get(0)).get(PdfName.OPT));
786             if (opts != null && opts.isArray()) {
787                 ArrayList JavaDoc list = ((PdfArray)opts).getArrayList();
788                 int idx = 0;
789                 try {
790                     idx = Integer.parseInt(value);
791                     PdfString ps = (PdfString)list.get(idx);
792                     value = ps.toUnicodeString();
793                     lastWasString = true;
794                 }
795                 catch (Exception JavaDoc e) {
796                 }
797             }
798             return value;
799         }
800         if (v.isString()) {
801             lastWasString = true;
802             return ((PdfString)v).toUnicodeString();
803         }
804         return PdfName.decodeName(v.toString());
805     }
806
807     /**
808      * Sets a field property. Valid property names are:
809      * <p>
810      * <ul>
811      * <li>textfont - sets the text font. The value for this entry is a <CODE>BaseFont</CODE>.<br>
812      * <li>textcolor - sets the text color. The value for this entry is a <CODE>java.awt.Color</CODE>.<br>
813      * <li>textsize - sets the text size. The value for this entry is a <CODE>Float</CODE>.
814      * <li>bgcolor - sets the background color. The value for this entry is a <CODE>java.awt.Color</CODE>.
815      * If <code>null</code> removes the background.<br>
816      * <li>bordercolor - sets the border color. The value for this entry is a <CODE>java.awt.Color</CODE>.
817      * If <code>null</code> removes the border.<br>
818      * </ul>
819      * @param field the field name
820      * @param name the property name
821      * @param value the property value
822      * @param inst an array of <CODE>int</CODE> indexing into <CODE>AcroField.Item.merged</CODE> elements to process.
823      * Set to <CODE>null</CODE> to process all
824      * @return <CODE>true</CODE> if the property exists, <CODE>false</CODE> otherwise
825      */

826     public boolean setFieldProperty(String JavaDoc field, String JavaDoc name, Object JavaDoc value, int inst[]) {
827         if (writer == null)
828             throw new RuntimeException JavaDoc("This AcroFields instance is read-only.");
829         try {
830             Item item = (Item)fields.get(field);
831             if (item == null)
832                 return false;
833             InstHit hit = new InstHit(inst);
834             if (name.equalsIgnoreCase("textfont")) {
835                 for (int k = 0; k < item.merged.size(); ++k) {
836                     if (hit.isHit(k)) {
837                         PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
838                         PdfDictionary dr = (PdfDictionary)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DR));
839                         if (da != null && dr != null) {
840                             Object JavaDoc dao[] = splitDAelements(da.toUnicodeString());
841                             PdfAppearance cb = new PdfAppearance();
842                             if (dao[DA_FONT] != null) {
843                                 BaseFont bf = (BaseFont)value;
844                                 PdfName psn = (PdfName)PdfAppearance.stdFieldFontNames.get(bf.getPostscriptFontName());
845                                 if (psn == null) {
846                                     psn = new PdfName(bf.getPostscriptFontName());
847                                 }
848                                 PdfDictionary fonts = (PdfDictionary)PdfReader.getPdfObject(dr.get(PdfName.FONT));
849                                 if (fonts == null) {
850                                     fonts = new PdfDictionary();
851                                     dr.put(PdfName.FONT, fonts);
852                                 }
853                                 PdfIndirectReference fref = (PdfIndirectReference)fonts.get(psn);
854                                 PdfDictionary top = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM));
855                                 markUsed(top);
856                                 dr = (PdfDictionary)PdfReader.getPdfObject(top.get(PdfName.DR));
857                                 if (dr == null) {
858                                     dr = new PdfDictionary();
859                                     top.put(PdfName.DR, dr);
860                                 }
861                                 markUsed(dr);
862                                 PdfDictionary fontsTop = (PdfDictionary)PdfReader.getPdfObject(dr.get(PdfName.FONT));
863                                 if (fontsTop == null) {
864                                     fontsTop = new PdfDictionary();
865                                     dr.put(PdfName.FONT, fontsTop);
866                                 }
867                                 markUsed(fontsTop);
868                                 PdfIndirectReference frefTop = (PdfIndirectReference)fontsTop.get(psn);
869                                 if (frefTop != null) {
870                                     if (fref == null)
871                                         fonts.put(psn, frefTop);
872                                 }
873                                 else if (fref == null) {
874                                     FontDetails fd;
875                                     if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
876                                         fd = new FontDetails(null, ((DocumentFont)bf).getIndirectReference(), bf);
877                                     }
878                                     else {
879                                         bf.setSubset(false);
880                                         fd = writer.addSimple(bf);
881                                         localFonts.put(psn.toString().substring(1), bf);
882                                     }
883                                     fontsTop.put(psn, fd.getIndirectReference());
884                                     fonts.put(psn, fd.getIndirectReference());
885                                 }
886                                 ByteBuffer buf = cb.getInternalBuffer();
887                                 buf.append(psn.getBytes()).append(' ').append(((Float JavaDoc)dao[DA_SIZE]).floatValue()).append(" Tf ");
888                                 if (dao[DA_COLOR] != null)
889                                     cb.setColorFill((Color JavaDoc)dao[DA_COLOR]);
890                                 PdfString s = new PdfString(cb.toString());
891                                 ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
892                                 ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
893                                 markUsed((PdfDictionary)item.widgets.get(k));
894                             }
895                         }
896                     }
897                 }
898             }
899             else if (name.equalsIgnoreCase("textcolor")) {
900                 for (int k = 0; k < item.merged.size(); ++k) {
901                     if (hit.isHit(k)) {
902                         PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
903                         if (da != null) {
904                             Object JavaDoc dao[] = splitDAelements(da.toUnicodeString());
905                             PdfAppearance cb = new PdfAppearance();
906                             if (dao[DA_FONT] != null) {
907                                 ByteBuffer buf = cb.getInternalBuffer();
908                                 buf.append(new PdfName((String JavaDoc)dao[DA_FONT]).getBytes()).append(' ').append(((Float JavaDoc)dao[DA_SIZE]).floatValue()).append(" Tf ");
909                                 cb.setColorFill((Color JavaDoc)value);
910                                 PdfString s = new PdfString(cb.toString());
911                                 ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
912                                 ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
913                                 markUsed((PdfDictionary)item.widgets.get(k));
914                             }
915                         }
916                     }
917                 }
918             }
919             else if (name.equalsIgnoreCase("textsize")) {
920                 for (int k = 0; k < item.merged.size(); ++k) {
921                     if (hit.isHit(k)) {
922                         PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
923                         if (da != null) {
924                             Object JavaDoc dao[] = splitDAelements(da.toUnicodeString());
925                             PdfAppearance cb = new PdfAppearance();
926                             if (dao[DA_FONT] != null) {
927                                 ByteBuffer buf = cb.getInternalBuffer();
928                                 buf.append(new PdfName((String JavaDoc)dao[DA_FONT]).getBytes()).append(' ').append(((Float JavaDoc)value).floatValue()).append(" Tf ");
929                                 if (dao[DA_COLOR] != null)
930                                     cb.setColorFill((Color JavaDoc)dao[DA_COLOR]);
931                                 PdfString s = new PdfString(cb.toString());
932                                 ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
933                                 ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
934                                 markUsed((PdfDictionary)item.widgets.get(k));
935                             }
936                         }
937                     }
938                 }
939             }
940             else if (name.equalsIgnoreCase("bgcolor") || name.equalsIgnoreCase("bordercolor")) {
941                 PdfName dname = (name.equalsIgnoreCase("bgcolor") ? PdfName.BG : PdfName.BC);
942                 for (int k = 0; k < item.merged.size(); ++k) {
943                     if (hit.isHit(k)) {
944                         PdfObject obj = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.MK));
945                         markUsed(obj);
946                         PdfDictionary mk = (PdfDictionary)obj;
947                         if (mk == null) {
948                             if (value == null)
949                                 return true;
950                             mk = new PdfDictionary();
951                             ((PdfDictionary)item.merged.get(k)).put(PdfName.MK, mk);
952                             ((PdfDictionary)item.widgets.get(k)).put(PdfName.MK, mk);
953                             markUsed((PdfDictionary)item.widgets.get(k));
954                         }
955                         if (value == null)
956                             mk.remove(dname);
957                         else
958                             mk.put(dname, PdfFormField.getMKColor((Color JavaDoc)value));
959                     }
960                 }
961             }
962             else
963                 return false;
964             return true;
965         }
966         catch (Exception JavaDoc e) {
967             throw new ExceptionConverter(e);
968         }
969     }
970     
971     /**
972      * Sets a field property. Valid property names are:
973      * <p>
974      * <ul>
975      * <li>flags - a set of flags specifying various characteristics of the field's widget annotation.
976      * The value of this entry replaces that of the F entry in the form's corresponding annotation dictionary.<br>
977      * <li>setflags - a set of flags to be set (turned on) in the F entry of the form's corresponding
978      * widget annotation dictionary. Bits equal to 1 cause the corresponding bits in F to be set to 1.<br>
979      * <li>clrflags - a set of flags to be cleared (turned off) in the F entry of the form's corresponding
980      * widget annotation dictionary. Bits equal to 1 cause the corresponding
981      * bits in F to be set to 0.<br>
982      * <li>fflags - a set of flags specifying various characteristics of the field. The value
983      * of this entry replaces that of the Ff entry in the form's corresponding field dictionary.<br>
984      * <li>setfflags - a set of flags to be set (turned on) in the Ff entry of the form's corresponding
985      * field dictionary. Bits equal to 1 cause the corresponding bits in Ff to be set to 1.<br>
986      * <li>clrfflags - a set of flags to be cleared (turned off) in the Ff entry of the form's corresponding
987      * field dictionary. Bits equal to 1 cause the corresponding bits in Ff
988      * to be set to 0.<br>
989      * </ul>
990      * @param field the field name
991      * @param name the property name
992      * @param value the property value
993      * @param inst an array of <CODE>int</CODE> indexing into <CODE>AcroField.Item.merged</CODE> elements to process.
994      * Set to <CODE>null</CODE> to process all
995      * @return <CODE>true</CODE> if the property exists, <CODE>false</CODE> otherwise
996      */

997     public boolean setFieldProperty(String JavaDoc field, String JavaDoc name, int value, int inst[]) {
998         if (writer == null)
999             throw new RuntimeException JavaDoc("This AcroFields instance is read-only.");
1000        Item item = (Item)fields.get(field);
1001        if (item == null)
1002            return false;
1003        InstHit hit = new InstHit(inst);
1004        if (name.equalsIgnoreCase("flags")) {
1005            PdfNumber num = new PdfNumber(value);
1006            for (int k = 0; k < item.merged.size(); ++k) {
1007                if (hit.isHit(k)) {
1008                    ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
1009                    ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
1010                    markUsed((PdfDictionary)item.widgets.get(k));
1011                }
1012            }
1013        }
1014        else if (name.equalsIgnoreCase("setflags")) {
1015            for (int k = 0; k < item.merged.size(); ++k) {
1016                if (hit.isHit(k)) {
1017                    PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.widgets.get(k)).get(PdfName.F));
1018                    int val = 0;
1019                    if (num != null)
1020                        val = num.intValue();
1021                    num = new PdfNumber(val | value);
1022                    ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
1023                    ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
1024                    markUsed((PdfDictionary)item.widgets.get(k));
1025                }
1026            }
1027        }
1028        else if (name.equalsIgnoreCase("clrflags")) {
1029            for (int k = 0; k < item.merged.size(); ++k) {
1030                if (hit.isHit(k)) {
1031                    PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.widgets.get(k)).get(PdfName.F));
1032                    int val = 0;
1033                    if (num != null)
1034                        val = num.intValue();
1035                    num = new PdfNumber(val & (~value));
1036                    ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
1037                    ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
1038                    markUsed((PdfDictionary)item.widgets.get(k));
1039                }
1040            }
1041        }
1042        else if (name.equalsIgnoreCase("fflags")) {
1043            PdfNumber num = new PdfNumber(value);
1044            for (int k = 0; k < item.merged.size(); ++k) {
1045                if (hit.isHit(k)) {
1046                    ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
1047                    ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
1048                    markUsed((PdfDictionary)item.values.get(k));
1049                }
1050            }
1051        }
1052        else if (name.equalsIgnoreCase("setfflags")) {
1053            for (int k = 0; k < item.merged.size(); ++k) {
1054                if (hit.isHit(k)) {
1055                    PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.values.get(k)).get(PdfName.FF));
1056                    int val = 0;
1057                    if (num != null)
1058                        val = num.intValue();
1059                    num = new PdfNumber(val | value);
1060                    ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
1061                    ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
1062                    markUsed((PdfDictionary)item.values.get(k));
1063                }
1064            }
1065        }
1066        else if (name.equalsIgnoreCase("clrfflags")) {
1067            for (int k = 0; k < item.merged.size(); ++k) {
1068                if (hit.isHit(k)) {
1069                    PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.values.get(k)).get(PdfName.FF));
1070                    int val = 0;
1071                    if (num != null)
1072                        val = num.intValue();
1073                    num = new PdfNumber(val & (~value));
1074                    ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
1075                    ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
1076                    markUsed((PdfDictionary)item.values.get(k));
1077                }
1078            }
1079        }
1080        else
1081            return false;
1082        return true;
1083    }
1084    
1085    /**
1086     * Merges an XML data structure into this form.
1087     * @param n the top node of the data structure
1088     * @throws java.io.IOException on error
1089     * @throws com.lowagie.text.DocumentException o error
1090     */

1091    public void mergeXfaData(Node JavaDoc n) throws IOException JavaDoc, DocumentException {
1092        XfaForm.Xml2SomDatasets data = new XfaForm.Xml2SomDatasets(n);
1093        for (Iterator JavaDoc it = data.getOrder().iterator(); it.hasNext();) {
1094            String JavaDoc name = (String JavaDoc)it.next();
1095            String JavaDoc text = XfaForm.getNodeText((Node JavaDoc)data.getName2Node().get(name));
1096            setField(name, text);
1097        }
1098    }
1099    
1100    /** Sets the fields by FDF merging.
1101     * @param fdf the FDF form
1102     * @throws IOException on error
1103     * @throws DocumentException on error
1104     */

1105    public void setFields(FdfReader fdf) throws IOException JavaDoc, DocumentException {
1106        HashMap JavaDoc fd = fdf.getFields();
1107        for (Iterator JavaDoc i = fd.keySet().iterator(); i.hasNext();) {
1108            String JavaDoc f = (String JavaDoc)i.next();
1109            String JavaDoc v = fdf.getFieldValue(f);
1110            if (v != null)
1111                setField(f, v);
1112        }
1113    }
1114    
1115    /** Sets the fields by XFDF merging.
1116     * @param xfdf the XFDF form
1117     * @throws IOException on error
1118     * @throws DocumentException on error
1119     */

1120    
1121    public void setFields(XfdfReader xfdf) throws IOException JavaDoc, DocumentException {
1122        HashMap JavaDoc fd = xfdf.getFields();
1123        for (Iterator JavaDoc i = fd.keySet().iterator(); i.hasNext();) {
1124            String JavaDoc f = (String JavaDoc)i.next();
1125            String JavaDoc v = xfdf.getFieldValue(f);
1126            if (v != null)
1127                setField(f, v);
1128        }
1129    }
1130
1131    /**
1132     * Regenerates the field appearance.
1133     * This is usefull when you change a field property, but not its value,
1134     * for instance form.setFieldProperty("f", "bgcolor", Color.BLUE, null);
1135     * This won't have any effect, unless you use regenerateField("f") after changing
1136     * the property.
1137     *
1138     * @param name the fully qualified field name or the partial name in the case of XFA forms
1139     * @throws IOException on error
1140     * @throws DocumentException on error
1141     * @return <CODE>true</CODE> if the field was found and changed,
1142     * <CODE>false</CODE> otherwise
1143     */

1144    public boolean regenerateField(String JavaDoc name) throws IOException JavaDoc, DocumentException {
1145        String JavaDoc value = getField(name);
1146        return setField(name, value, value);
1147    }
1148
1149    /** Sets the field value.
1150     * @param name the fully qualified field name or the partial name in the case of XFA forms
1151     * @param value the field value
1152     * @throws IOException on error
1153     * @throws DocumentException on error
1154     * @return <CODE>true</CODE> if the field was found and changed,
1155     * <CODE>false</CODE> otherwise
1156     */

1157    public boolean setField(String JavaDoc name, String JavaDoc value) throws IOException JavaDoc, DocumentException {
1158        return setField(name, value, null);
1159    }
1160    
1161    /** Sets the field value and the display string. The display string
1162     * is used to build the appearance in the cases where the value
1163     * is modified by Acrobat with JavaScript and the algorithm is
1164     * known.
1165     * @param name the fully qualified field name or the partial name in the case of XFA forms
1166     * @param value the field value
1167     * @param display the string that is used for the appearance. If <CODE>null</CODE>
1168     * the <CODE>value</CODE> parameter will be used
1169     * @return <CODE>true</CODE> if the field was found and changed,
1170     * <CODE>false</CODE> otherwise
1171     * @throws IOException on error
1172     * @throws DocumentException on error
1173     */

1174    public boolean setField(String JavaDoc name, String JavaDoc value, String JavaDoc display) throws IOException JavaDoc, DocumentException {
1175        if (writer == null)
1176            throw new DocumentException("This AcroFields instance is read-only.");
1177        if (xfa.isXfaPresent()) {
1178            name = xfa.findFieldName(name, this);
1179            if (name == null)
1180                return false;
1181            String JavaDoc shortName = XfaForm.Xml2Som.getShortName(name);
1182            Node JavaDoc xn = xfa.findDatasetsNode(shortName);
1183            if (xn == null) {
1184                xn = xfa.getDatasetsSom().insertNode(xfa.getDatasetsNode(), shortName);
1185            }
1186            xfa.setNodeText(xn, value);
1187        }
1188        Item item = (Item)fields.get(name);
1189        if (item == null)
1190            return false;
1191        PdfName type = (PdfName)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FT));
1192        if (PdfName.TX.equals(type)) {
1193            PdfNumber maxLen = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.MAXLEN));
1194            int len = 0;
1195            if (maxLen != null)
1196                len = maxLen.intValue();
1197            if (len > 0)
1198                value = value.substring(0, Math.min(len, value.length()));
1199        }
1200        if (display == null)
1201            display = value;
1202        if (PdfName.TX.equals(type) || PdfName.CH.equals(type)) {
1203            PdfString v = new PdfString(value, PdfObject.TEXT_UNICODE);
1204            for (int idx = 0; idx < item.values.size(); ++idx) {
1205                PdfDictionary valueDic = (PdfDictionary)item.values.get(idx);
1206                valueDic.put(PdfName.V, v);
1207                valueDic.remove(PdfName.I);
1208                markUsed(valueDic);
1209                PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
1210                merged.remove(PdfName.I);
1211                merged.put(PdfName.V, v);
1212                PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
1213                if (generateAppearances) {
1214                    PdfAppearance app = getAppearance(merged, display, name);
1215                    if (PdfName.CH.equals(type)) {
1216                        PdfNumber n = new PdfNumber(topFirst);
1217                        widget.put(PdfName.TI, n);
1218                        merged.put(PdfName.TI, n);
1219                    }
1220                    PdfDictionary appDic = (PdfDictionary)PdfReader.getPdfObject(widget.get(PdfName.AP));
1221                    if (appDic == null) {
1222                        appDic = new PdfDictionary();
1223                        widget.put(PdfName.AP, appDic);
1224                        merged.put(PdfName.AP, appDic);
1225                    }
1226                    appDic.put(PdfName.N, app.getIndirectReference());
1227                    writer.releaseTemplate(app);
1228                }
1229                else {
1230                    widget.remove(PdfName.AP);
1231                    merged.remove(PdfName.AP);
1232                }
1233                markUsed(widget);
1234            }
1235            return true;
1236        }
1237        else if (PdfName.BTN.equals(type)) {
1238            PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FF));
1239            int flags = 0;
1240            if (ff != null)
1241                flags = ff.intValue();
1242            if ((flags & PdfFormField.FF_PUSHBUTTON) != 0)
1243                return true;
1244            PdfName v = new PdfName(value);
1245            if ((flags & PdfFormField.FF_RADIO) == 0) {
1246                for (int idx = 0; idx < item.values.size(); ++idx) {
1247                    ((PdfDictionary)item.values.get(idx)).put(PdfName.V, v);
1248                    markUsed((PdfDictionary)item.values.get(idx));
1249                    PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
1250                    merged.put(PdfName.V, v);
1251                    merged.put(PdfName.AS, v);
1252                    PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
1253                    if (isInAP(widget, v))
1254                        widget.put(PdfName.AS, v);
1255                    else
1256                        widget.put(PdfName.AS, PdfName.Off);
1257                    markUsed(widget);
1258                }
1259            }
1260            else {
1261                ArrayList JavaDoc lopt = new ArrayList JavaDoc();
1262                PdfObject opts = PdfReader.getPdfObject(((PdfDictionary)item.values.get(0)).get(PdfName.OPT));
1263                if (opts != null && opts.isArray()) {
1264                    ArrayList JavaDoc list = ((PdfArray)opts).getArrayList();
1265                    for (int k = 0; k < list.size(); ++k) {
1266                        PdfObject vv = PdfReader.getPdfObject((PdfObject)list.get(k));
1267                        if (vv != null && vv.isString())
1268                            lopt.add(((PdfString)vv).toUnicodeString());
1269                        else
1270                            lopt.add(null);
1271                    }
1272                }
1273                int vidx = lopt.indexOf(value);
1274                PdfName valt = null;
1275                PdfName vt;
1276                if (vidx >= 0) {
1277                    vt = valt = new PdfName(String.valueOf(vidx));
1278                }
1279                else
1280                    vt = v;
1281                for (int idx = 0; idx < item.values.size(); ++idx) {
1282                    PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
1283                    PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
1284                    markUsed((PdfDictionary)item.values.get(idx));
1285                    if (valt != null) {
1286                        PdfString ps = new PdfString(value, PdfObject.TEXT_UNICODE);
1287                        ((PdfDictionary)item.values.get(idx)).put(PdfName.V, ps);
1288                        merged.put(PdfName.V, ps);
1289                    }
1290                    else {
1291                        ((PdfDictionary)item.values.get(idx)).put(PdfName.V, v);
1292                        merged.put(PdfName.V, v);
1293                    }
1294                    markUsed(widget);
1295                    if (isInAP(widget, vt)) {
1296                        merged.put(PdfName.AS, vt);
1297                        widget.put(PdfName.AS, vt);
1298                    }
1299                    else {
1300                        merged.put(PdfName.AS, PdfName.Off);
1301                        widget.put(PdfName.AS, PdfName.Off);
1302                    }
1303                }
1304            }
1305            return true;
1306        }
1307        return false;
1308    }
1309    
1310    boolean isInAP(PdfDictionary dic, PdfName check) {
1311        PdfDictionary appDic = (PdfDictionary)PdfReader.getPdfObject(dic.get(PdfName.AP));
1312        if (appDic == null)
1313            return false;
1314        PdfDictionary NDic = (PdfDictionary)PdfReader.getPdfObject(appDic.get(PdfName.N));
1315        return (NDic != null && NDic.get(check) != null);
1316    }
1317    
1318    /** Gets all the fields. The fields are keyed by the fully qualified field name and
1319     * the value is an instance of <CODE>AcroFields.Item</CODE>.
1320     * @return all the fields
1321     */

1322    public HashMap JavaDoc getFields() {
1323        return fields;
1324    }
1325    
1326    /**
1327     * Gets the field structure.
1328     * @param name the name of the field
1329     * @return the field structure or <CODE>null</CODE> if the field
1330     * does not exist
1331     */

1332    public Item getFieldItem(String JavaDoc name) {
1333        if (xfa.isXfaPresent()) {
1334            name = xfa.findFieldName(name, this);
1335            if (name == null)
1336                return null;
1337        }
1338        return (Item)fields.get(name);
1339    }
1340    
1341    /**
1342     * Gets the long XFA translated name.
1343     * @param name the name of the field
1344     * @return the long field name
1345     */

1346    public String JavaDoc getTranslatedFieldName(String JavaDoc name) {
1347        if (xfa.isXfaPresent()) {
1348            String JavaDoc namex = xfa.findFieldName(name, this);
1349            if (namex != null)
1350                name = namex;
1351        }
1352        return name;
1353    }
1354    
1355    /**
1356     * Gets the field box positions in the document. The return is an array of <CODE>float</CODE>
1357     * multiple of 5. For each of this groups the values are: [page, llx, lly, urx,
1358     * ury]. The coordinates have the page rotation in consideration.
1359     * @param name the field name
1360     * @return the positions or <CODE>null</CODE> if field does not exist
1361     */

1362    public float[] getFieldPositions(String JavaDoc name) {
1363        Item item = getFieldItem(name);
1364        if (item == null)
1365            return null;
1366        float ret[] = new float[item.page.size() * 5];
1367        int ptr = 0;
1368        for (int k = 0; k < item.page.size(); ++k) {
1369            try {
1370                PdfDictionary wd = (PdfDictionary)item.widgets.get(k);
1371                PdfArray rect = (PdfArray)wd.get(PdfName.RECT);
1372                if (rect == null)
1373                    continue;
1374                Rectangle r = PdfReader.getNormalizedRectangle(rect);
1375                int page = ((Integer JavaDoc)item.page.get(k)).intValue();
1376                int rotation = reader.getPageRotation(page);
1377                ret[ptr++] = page;
1378                if (rotation != 0) {
1379                    Rectangle pageSize = reader.getPageSize(page);
1380                    switch (rotation) {
1381                        case 270:
1382                            r = new Rectangle(
1383                                pageSize.getTop() - r.getBottom(),
1384                                r.getLeft(),
1385                                pageSize.getTop() - r.getTop(),
1386                                r.getRight());
1387                            break;
1388                        case 180:
1389                            r = new Rectangle(
1390                                pageSize.getRight() - r.getLeft(),
1391                                pageSize.getTop() - r.getBottom(),
1392                                pageSize.getRight() - r.getRight(),
1393                                pageSize.getTop() - r.getTop());
1394                            break;
1395                        case 90:
1396                            r = new Rectangle(
1397                                r.getBottom(),
1398                                pageSize.getRight() - r.getLeft(),
1399                                r.getTop(),
1400                                pageSize.getRight() - r.getRight());
1401                            break;
1402                    }
1403                    r.normalize();
1404                }
1405                ret[ptr++] = r.getLeft();
1406                ret[ptr++] = r.getBottom();
1407                ret[ptr++] = r.getRight();
1408                ret[ptr++] = r.getTop();
1409            }
1410            catch (Exception JavaDoc e) {
1411                // empty on purpose
1412
}
1413        }
1414        if (ptr < ret.length) {
1415            float ret2[] = new float[ptr];
1416            System.arraycopy(ret, 0, ret2, 0, ptr);
1417            return ret2;
1418        }
1419        return ret;
1420    }
1421    
1422    private int removeRefFromArray(PdfArray array, PdfObject refo) {
1423        ArrayList JavaDoc ar = array.getArrayList();
1424        if (refo == null || !refo.isIndirect())
1425            return ar.size();
1426        PdfIndirectReference ref = (PdfIndirectReference)refo;
1427        for (int j = 0; j < ar.size(); ++j) {
1428            PdfObject obj = (PdfObject)ar.get(j);
1429            if (!obj.isIndirect())
1430                continue;
1431            if (((PdfIndirectReference)obj).getNumber() == ref.getNumber())
1432                ar.remove(j--);
1433        }
1434        return ar.size();
1435    }
1436    
1437    /**
1438     * Removes all the fields from <CODE>page</CODE>.
1439     * @param page the page to remove the fields from
1440     * @return <CODE>true</CODE> if any field was removed, <CODE>false otherwise</CODE>
1441     */

1442    public boolean removeFieldsFromPage(int page) {
1443        if (page < 1)
1444            return false;
1445        String JavaDoc names[] = new String JavaDoc[fields.size()];
1446        fields.keySet().toArray(names);
1447        boolean found = false;
1448        for (int k = 0; k < names.length; ++k) {
1449            boolean fr = removeField(names[k], page);
1450            found = (found || fr);
1451        }
1452        return found;
1453    }
1454    
1455    /**
1456     * Removes a field from the document. If page equals -1 all the fields with this
1457     * <CODE>name</CODE> are removed from the document otherwise only the fields in
1458     * that particular page are removed.
1459     * @param name the field name
1460     * @param page the page to remove the field from or -1 to remove it from all the pages
1461     * @return <CODE>true</CODE> if the field exists, <CODE>false otherwise</CODE>
1462     */

1463    public boolean removeField(String JavaDoc name, int page) {
1464        Item item = getFieldItem(name);
1465        if (item == null)
1466            return false;
1467        PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM), reader.getCatalog());
1468        
1469        if (acroForm == null)
1470            return false;
1471        PdfArray arrayf = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm);
1472        if (arrayf == null)
1473            return false;
1474        for (int k = 0; k < item.widget_refs.size(); ++k) {
1475            int pageV = ((Integer JavaDoc)item.page.get(k)).intValue();
1476            if (page != -1 && page != pageV)
1477                continue;
1478            PdfIndirectReference ref = (PdfIndirectReference)item.widget_refs.get(k);
1479            PdfDictionary wd = (PdfDictionary)PdfReader.getPdfObject(ref);
1480            PdfDictionary pageDic = reader.getPageN(pageV);
1481            PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS), pageDic);
1482            if (annots != null) {
1483                if (removeRefFromArray(annots, ref) == 0) {
1484                    pageDic.remove(PdfName.ANNOTS);
1485                    markUsed(pageDic);
1486                }
1487                else
1488                    markUsed(annots);
1489            }
1490            PdfReader.killIndirect(ref);
1491            PdfIndirectReference kid = ref;
1492            while ((ref = (PdfIndirectReference)wd.get(PdfName.PARENT)) != null) {
1493                wd = (PdfDictionary)PdfReader.getPdfObject(ref);
1494                PdfArray kids = (PdfArray)PdfReader.getPdfObject(wd.get(PdfName.KIDS));
1495                if (removeRefFromArray(kids, kid) != 0)
1496                    break;
1497                kid = ref;
1498                PdfReader.killIndirect(ref);
1499            }
1500            if (ref == null) {
1501                removeRefFromArray(arrayf, kid);
1502                markUsed(arrayf);
1503            }
1504            if (page != -1) {
1505                item.merged.remove(k);
1506                item.page.remove(k);
1507                item.values.remove(k);
1508                item.widget_refs.remove(k);
1509                item.widgets.remove(k);
1510                --k;
1511            }
1512        }
1513        if (page == -1 || item.merged.size() == 0)
1514            fields.remove(name);
1515        return true;
1516    }
1517    
1518    /**
1519     * Removes a field from the document.
1520     * @param name the field name
1521     * @return <CODE>true</CODE> if the field exists, <CODE>false otherwise</CODE>
1522     */

1523    public boolean removeField(String JavaDoc name) {
1524        return removeField(name, -1);
1525    }
1526    
1527    /** Gets the property generateAppearances.
1528     * @return the property generateAppearances
1529     */

1530    public boolean isGenerateAppearances() {
1531        return this.generateAppearances;
1532    }
1533    
1534    /** Sets the option to generate appearances. Not generating apperances
1535     * will speed-up form filling but the results can be
1536     * unexpected in Acrobat. Don't use it unless your environment is well
1537     * controlled. The default is <CODE>true</CODE>.
1538     * @param generateAppearances the option to generate appearances
1539     */

1540    public void setGenerateAppearances(boolean generateAppearances) {
1541        this.generateAppearances = generateAppearances;
1542        PdfDictionary top = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM));
1543        if (generateAppearances)
1544            top.remove(PdfName.NEEDAPPEARANCES);
1545        else
1546            top.put(PdfName.NEEDAPPEARANCES, PdfBoolean.PDFTRUE);
1547    }
1548    
1549    /** The field representations for retrieval and modification. */
1550    public static class Item {
1551        /** An array of <CODE>PdfDictionary</CODE> where the value tag /V
1552         * is present.
1553         */

1554        public ArrayList JavaDoc values = new ArrayList JavaDoc();
1555        /** An array of <CODE>PdfDictionary</CODE> with the widgets.
1556         */

1557        public ArrayList JavaDoc widgets = new ArrayList JavaDoc();
1558        /** An array of <CODE>PdfDictionary</CODE> with the widget references.
1559         */

1560        public ArrayList JavaDoc widget_refs = new ArrayList JavaDoc();
1561        /** An array of <CODE>PdfDictionary</CODE> with all the field
1562         * and widget tags merged.
1563         */

1564        public ArrayList JavaDoc merged = new ArrayList JavaDoc();
1565        /** An array of <CODE>Integer</CODE> with the page numbers where
1566         * the widgets are displayed.
1567         */

1568        public ArrayList JavaDoc page = new ArrayList JavaDoc();
1569        /** An array of <CODE>Integer</CODE> with the tab order of the field in the page.
1570         */

1571        public ArrayList JavaDoc tabOrder = new ArrayList JavaDoc();
1572    }
1573    
1574    private static class InstHit {
1575        IntHashtable hits;
1576        public InstHit(int inst[]) {
1577            if (inst == null)
1578                return;
1579            hits = new IntHashtable();
1580            for (int k = 0; k < inst.length; ++k)
1581                hits.put(inst[k], 1);
1582        }
1583        
1584        public boolean isHit(int n) {
1585            if (hits == null)
1586                return true;
1587            return hits.containsKey(n);
1588        }
1589    }
1590    
1591    /**
1592     * Gets the field names that have signatures and are signed.
1593     * @return the field names that have signatures and are signed
1594     */

1595    public ArrayList JavaDoc getSignatureNames() {
1596        if (sigNames != null)
1597            return new ArrayList JavaDoc(sigNames.keySet());
1598        sigNames = new HashMap JavaDoc();
1599        ArrayList JavaDoc sorter = new ArrayList JavaDoc();
1600        for (Iterator JavaDoc it = fields.entrySet().iterator(); it.hasNext();) {
1601            Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
1602            Item item = (Item)entry.getValue();
1603            PdfDictionary merged = (PdfDictionary)item.merged.get(0);
1604            if (!PdfName.SIG.equals(merged.get(PdfName.FT)))
1605                continue;
1606            PdfObject vo = PdfReader.getPdfObject(merged.get(PdfName.V));
1607            if (vo == null || vo.type() != PdfObject.DICTIONARY)
1608                continue;
1609            PdfDictionary v = (PdfDictionary)vo;
1610            PdfObject contents = v.get(PdfName.CONTENTS);
1611            if (contents == null || contents.type() != PdfObject.STRING)
1612                continue;
1613            PdfObject ro = v.get(PdfName.BYTERANGE);
1614            if (ro == null || ro.type() != PdfObject.ARRAY)
1615                continue;
1616            ArrayList JavaDoc ra = ((PdfArray)ro).getArrayList();
1617            if (ra.size() < 2)
1618                continue;
1619            int length = ((PdfNumber)ra.get(ra.size() - 1)).intValue() + ((PdfNumber)ra.get(ra.size() - 2)).intValue();
1620            sorter.add(new Object JavaDoc[]{entry.getKey(), new int[]{length, 0}});
1621        }
1622        Collections.sort(sorter, new AcroFields.SorterComparator());
1623        if (!sorter.isEmpty()) {
1624            if (((int[])((Object JavaDoc[])sorter.get(sorter.size() - 1))[1])[0] == reader.getFileLength())
1625                totalRevisions = sorter.size();
1626            else
1627                totalRevisions = sorter.size() + 1;
1628            for (int k = 0; k < sorter.size(); ++k) {
1629                Object JavaDoc objs[] = (Object JavaDoc[])sorter.get(k);
1630                String JavaDoc name = (String JavaDoc)objs[0];
1631                int p[] = (int[])objs[1];
1632                p[1] = k + 1;
1633                sigNames.put(name, p);
1634            }
1635        }
1636        return new ArrayList JavaDoc(sigNames.keySet());
1637    }
1638    
1639    /**
1640     * Gets the field names that have blank signatures.
1641     * @return the field names that have blank signatures
1642     */

1643    public ArrayList JavaDoc getBlankSignatureNames() {
1644        getSignatureNames();
1645        ArrayList JavaDoc sigs = new ArrayList JavaDoc();
1646        for (Iterator JavaDoc it = fields.entrySet().iterator(); it.hasNext();) {
1647            Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
1648            Item item = (Item)entry.getValue();
1649            PdfDictionary merged = (PdfDictionary)item.merged.get(0);
1650            if (!PdfName.SIG.equals(merged.get(PdfName.FT)))
1651                continue;
1652            if (sigNames.containsKey(entry.getKey()))
1653                continue;
1654            sigs.add(entry.getKey());
1655        }
1656        return sigs;
1657    }
1658    
1659    /**
1660     * Gets the signature dictionary, the one keyed by /V.
1661     * @param name the field name
1662     * @return the signature dictionary keyed by /V or <CODE>null</CODE> if the field is not
1663     * a signature
1664     */

1665    public PdfDictionary getSignatureDictionary(String JavaDoc name) {
1666        getSignatureNames();
1667        name = getTranslatedFieldName(name);
1668        if (!sigNames.containsKey(name))
1669            return null;
1670        Item item = (Item)fields.get(name);
1671        PdfDictionary merged = (PdfDictionary)item.merged.get(0);
1672        return (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.V));
1673    }
1674    
1675    /**
1676     * Checks is the signature covers the entire document or just part of it.
1677     * @param name the signature field name
1678     * @return <CODE>true</CODE> if the signature covers the entire document,
1679     * <CODE>false</CODE> otherwise
1680     */

1681    public boolean signatureCoversWholeDocument(String JavaDoc name) {
1682        getSignatureNames();
1683        name = getTranslatedFieldName(name);
1684        if (!sigNames.containsKey(name))
1685            return false;
1686        return ((int[])sigNames.get(name))[0] == reader.getFileLength();
1687    }
1688    
1689    /**
1690     * Verifies a signature. An example usage is:
1691     * <p>
1692     * <pre>
1693     * KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
1694     * PdfReader reader = new PdfReader("my_signed_doc.pdf");
1695     * AcroFields af = reader.getAcroFields();
1696     * ArrayList names = af.getSignatureNames();
1697     * for (int k = 0; k &lt; names.size(); ++k) {
1698     * String name = (String)names.get(k);
1699     * System.out.println("Signature name: " + name);
1700     * System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
1701     * PdfPKCS7 pk = af.verifySignature(name);
1702     * Calendar cal = pk.getSignDate();
1703     * Certificate pkc[] = pk.getCertificates();
1704     * System.out.println("Subject: " + PdfPKCS7.getSubjectFields(pk.getSigningCertificate()));
1705     * System.out.println("Document modified: " + !pk.verify());
1706     * Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal);
1707     * if (fails == null)
1708     * System.out.println("Certificates verified against the KeyStore");
1709     * else
1710     * System.out.println("Certificate failed: " + fails[1]);
1711     * }
1712     * </pre>
1713     * @param name the signature field name
1714     * @return a <CODE>PdfPKCS7</CODE> class to continue the verification
1715     */

1716    public PdfPKCS7 verifySignature(String JavaDoc name) {
1717        return verifySignature(name, null);
1718    }
1719    
1720    /**
1721     * Verifies a signature. An example usage is:
1722     * <p>
1723     * <pre>
1724     * KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
1725     * PdfReader reader = new PdfReader("my_signed_doc.pdf");
1726     * AcroFields af = reader.getAcroFields();
1727     * ArrayList names = af.getSignatureNames();
1728     * for (int k = 0; k &lt; names.size(); ++k) {
1729     * String name = (String)names.get(k);
1730     * System.out.println("Signature name: " + name);
1731     * System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
1732     * PdfPKCS7 pk = af.verifySignature(name);
1733     * Calendar cal = pk.getSignDate();
1734     * Certificate pkc[] = pk.getCertificates();
1735     * System.out.println("Subject: " + PdfPKCS7.getSubjectFields(pk.getSigningCertificate()));
1736     * System.out.println("Document modified: " + !pk.verify());
1737     * Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal);
1738     * if (fails == null)
1739     * System.out.println("Certificates verified against the KeyStore");
1740     * else
1741     * System.out.println("Certificate failed: " + fails[1]);
1742     * }
1743     * </pre>
1744     * @param name the signature field name
1745     * @param provider the provider or <code>null</code> for the default provider
1746     * @return a <CODE>PdfPKCS7</CODE> class to continue the verification
1747     */

1748    public PdfPKCS7 verifySignature(String JavaDoc name, String JavaDoc provider) {
1749        PdfDictionary v = getSignatureDictionary(name);
1750        if (v == null)
1751            return null;
1752        try {
1753            PdfName sub = (PdfName)PdfReader.getPdfObject(v.get(PdfName.SUBFILTER));
1754            PdfString contents = (PdfString)PdfReader.getPdfObject(v.get(PdfName.CONTENTS));
1755            PdfPKCS7 pk = null;
1756            if (sub.equals(PdfName.ADBE_X509_RSA_SHA1)) {
1757                PdfString cert = (PdfString)PdfReader.getPdfObject(v.get(PdfName.CERT));
1758                pk = new PdfPKCS7(contents.getOriginalBytes(), cert.getBytes(), provider);
1759            }
1760            else
1761                pk = new PdfPKCS7(contents.getOriginalBytes(), provider);
1762            updateByteRange(pk, v);
1763            PdfString str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.M));
1764            if (str != null)
1765                pk.setSignDate(PdfDate.decode(str.toString()));
1766            PdfObject obj = PdfReader.getPdfObject(v.get(PdfName.NAME));
1767            if (obj != null) {
1768              if (obj.isString())
1769                pk.setSignName(((PdfString)obj).toUnicodeString());
1770              else if(obj.isName())
1771                pk.setSignName(PdfName.decodeName(obj.toString()));
1772            }
1773            str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.REASON));
1774            if (str != null)
1775                pk.setReason(str.toUnicodeString());
1776            str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.LOCATION));
1777            if (str != null)
1778                pk.setLocation(str.toUnicodeString());
1779            return pk;
1780        }
1781        catch (Exception JavaDoc e) {
1782            throw new ExceptionConverter(e);
1783        }
1784    }
1785    
1786    private void updateByteRange(PdfPKCS7 pkcs7, PdfDictionary v) {
1787        PdfArray b = (PdfArray)PdfReader.getPdfObject(v.get(PdfName.BYTERANGE));
1788        RandomAccessFileOrArray rf = reader.getSafeFile();
1789        try {
1790            rf.reOpen();
1791            byte buf[] = new byte[8192];
1792            ArrayList JavaDoc ar = b.getArrayList();
1793            for (int k = 0; k < ar.size(); ++k) {
1794                int start = ((PdfNumber)ar.get(k)).intValue();
1795                int length = ((PdfNumber)ar.get(++k)).intValue();
1796                rf.seek(start);
1797                while (length > 0) {
1798                    int rd = rf.read(buf, 0, Math.min(length, buf.length));
1799                    if (rd <= 0)
1800                        break;
1801                    length -= rd;
1802                    pkcs7.update(buf, 0, rd);
1803                }
1804            }
1805        }
1806        catch (Exception JavaDoc e) {
1807            throw new ExceptionConverter(e);
1808        }
1809        finally {
1810            try{rf.close();}catch(Exception JavaDoc e){}
1811        }
1812    }
1813
1814    private void markUsed(PdfObject obj) {
1815        if (!append)
1816            return;
1817        ((PdfStamperImp)writer).markUsed(obj);
1818    }
1819    
1820    /**
1821     * Gets the total number of revisions this document has.
1822     * @return the total number of revisions
1823     */

1824    public int getTotalRevisions() {
1825        getSignatureNames();
1826        return this.totalRevisions;
1827    }
1828    
1829    /**
1830     * Gets this <CODE>field</CODE> revision.
1831     * @param field the signature field name
1832     * @return the revision or zero if it's not a signature field
1833     */

1834    public int getRevision(String JavaDoc field) {
1835        getSignatureNames();
1836        field = getTranslatedFieldName(field);
1837        if (!sigNames.containsKey(field))
1838            return 0;
1839        return ((int[])sigNames.get(field))[1];
1840    }
1841    
1842    /**
1843     * Extracts a revision from the document.
1844     * @param field the signature field name
1845     * @return an <CODE>InputStream</CODE> covering the revision. Returns <CODE>null</CODE> if
1846     * it's not a signature field
1847     * @throws IOException on error
1848     */

1849    public InputStream JavaDoc extractRevision(String JavaDoc field) throws IOException JavaDoc {
1850        getSignatureNames();
1851        field = getTranslatedFieldName(field);
1852        if (!sigNames.containsKey(field))
1853            return null;
1854        int length = ((int[])sigNames.get(field))[0];
1855        RandomAccessFileOrArray raf = reader.getSafeFile();
1856        raf.reOpen();
1857        raf.seek(0);
1858        return new RevisionStream(raf, length);
1859    }
1860
1861    /**
1862     * Gets the appearances cache.
1863     * @return the appearances cache
1864     */

1865    public HashMap JavaDoc getFieldCache() {
1866        return this.fieldCache;
1867    }
1868    
1869    /**
1870     * Sets a cache for field appearances. Parsing the existing PDF to
1871     * create a new TextField is time expensive. For those tasks that repeatedly
1872     * fill the same PDF with different field values the use of the cache has dramatic
1873     * speed advantages. An example usage:
1874     * <p>
1875     * <pre>
1876     * String pdfFile = ...;// the pdf file used as template
1877     * ArrayList xfdfFiles = ...;// the xfdf file names
1878     * ArrayList pdfOutFiles = ...;// the output file names, one for each element in xpdfFiles
1879     * HashMap cache = new HashMap();// the appearances cache
1880     * PdfReader originalReader = new PdfReader(pdfFile);
1881     * for (int k = 0; k &lt; xfdfFiles.size(); ++k) {
1882     * PdfReader reader = new PdfReader(originalReader);
1883     * XfdfReader xfdf = new XfdfReader((String)xfdfFiles.get(k));
1884     * PdfStamper stp = new PdfStamper(reader, new FileOutputStream((String)pdfOutFiles.get(k)));
1885     * AcroFields af = stp.getAcroFields();
1886     * af.setFieldCache(cache);
1887     * af.setFields(xfdf);
1888     * stp.close();
1889     * }
1890     * </pre>
1891     * @param fieldCache an HasMap that will carry the cached appearances
1892     */

1893    public void setFieldCache(HashMap JavaDoc fieldCache) {
1894        this.fieldCache = fieldCache;
1895    }
1896    
1897    /**
1898     * Sets extra margins in text fields to better mimic the Acrobat layout.
1899     * @param extraMarginLeft the extra marging left
1900     * @param extraMarginTop the extra margin top
1901     */

1902    public void setExtraMargin(float extraMarginLeft, float extraMarginTop) {
1903        this.extraMarginLeft = extraMarginLeft;
1904        this.extraMarginTop = extraMarginTop;
1905    }
1906    
1907    /**
1908     * Adds a substitution font to the list. The fonts in this list will be used if the original
1909     * font doesn't contain the needed glyphs.
1910     * @param font the font
1911     */

1912    public void addSubstitutionFont(BaseFont font) {
1913        if (substitutionFonts == null)
1914            substitutionFonts = new ArrayList JavaDoc();
1915        substitutionFonts.add(font);
1916    }
1917
1918    private static final HashMap JavaDoc stdFieldFontNames = new HashMap JavaDoc();
1919    
1920    /**
1921     * Holds value of property totalRevisions.
1922     */

1923    private int totalRevisions;
1924    
1925    /**
1926     * Holds value of property fieldCache.
1927     */

1928    private HashMap JavaDoc fieldCache;
1929    
1930    static {
1931        stdFieldFontNames.put("CoBO", new String JavaDoc[]{"Courier-BoldOblique"});
1932        stdFieldFontNames.put("CoBo", new String JavaDoc[]{"Courier-Bold"});
1933        stdFieldFontNames.put("CoOb", new String JavaDoc[]{"Courier-Oblique"});
1934        stdFieldFontNames.put("Cour", new String JavaDoc[]{"Courier"});
1935        stdFieldFontNames.put("HeBO", new String JavaDoc[]{"Helvetica-BoldOblique"});
1936        stdFieldFontNames.put("HeBo", new String JavaDoc[]{"Helvetica-Bold"});
1937        stdFieldFontNames.put("HeOb", new String JavaDoc[]{"Helvetica-Oblique"});
1938        stdFieldFontNames.put("Helv", new String JavaDoc[]{"Helvetica"});
1939        stdFieldFontNames.put("Symb", new String JavaDoc[]{"Symbol"});
1940        stdFieldFontNames.put("TiBI", new String JavaDoc[]{"Times-BoldItalic"});
1941        stdFieldFontNames.put("TiBo", new String JavaDoc[]{"Times-Bold"});
1942        stdFieldFontNames.put("TiIt", new String JavaDoc[]{"Times-Italic"});
1943        stdFieldFontNames.put("TiRo", new String JavaDoc[]{"Times-Roman"});
1944        stdFieldFontNames.put("ZaDb", new String JavaDoc[]{"ZapfDingbats"});
1945        stdFieldFontNames.put("HySm", new String JavaDoc[]{"HYSMyeongJo-Medium", "UniKS-UCS2-H"});
1946        stdFieldFontNames.put("HyGo", new String JavaDoc[]{"HYGoThic-Medium", "UniKS-UCS2-H"});
1947        stdFieldFontNames.put("KaGo", new String JavaDoc[]{"HeiseiKakuGo-W5", "UniKS-UCS2-H"});
1948        stdFieldFontNames.put("KaMi", new String JavaDoc[]{"HeiseiMin-W3", "UniJIS-UCS2-H"});
1949        stdFieldFontNames.put("MHei", new String JavaDoc[]{"MHei-Medium", "UniCNS-UCS2-H"});
1950        stdFieldFontNames.put("MSun", new String JavaDoc[]{"MSung-Light", "UniCNS-UCS2-H"});
1951        stdFieldFontNames.put("STSo", new String JavaDoc[]{"STSong-Light", "UniGB-UCS2-H"});
1952    }
1953
1954    private static class RevisionStream extends InputStream JavaDoc {
1955        private byte b[] = new byte[1];
1956        private RandomAccessFileOrArray raf;
1957        private int length;
1958        private int rangePosition = 0;
1959        private boolean closed;
1960        
1961        private RevisionStream(RandomAccessFileOrArray raf, int length) {
1962            this.raf = raf;
1963            this.length = length;
1964        }
1965        
1966        public int read() throws IOException JavaDoc {
1967            int n = read(b);
1968            if (n != 1)
1969                return -1;
1970            return b[0] & 0xff;
1971        }
1972        
1973        public int read(byte[] b, int off, int len) throws IOException JavaDoc {
1974            if (b == null) {
1975                throw new NullPointerException JavaDoc();
1976            } else if ((off < 0) || (off > b.length) || (len < 0) ||
1977            ((off + len) > b.length) || ((off + len) < 0)) {
1978                throw new IndexOutOfBoundsException JavaDoc();
1979            } else if (len == 0) {
1980                return 0;
1981            }
1982            if (rangePosition >= length) {
1983                close();
1984                return -1;
1985            }
1986            int elen = Math.min(len, length - rangePosition);
1987            raf.readFully(b, off, elen);
1988            rangePosition += elen;
1989            return elen;
1990        }
1991        
1992        public void close() throws IOException JavaDoc {
1993            if (!closed) {
1994                raf.close();
1995                closed = true;
1996            }
1997        }
1998    }
1999    
2000    private static class SorterComparator implements Comparator JavaDoc {
2001        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2002            int n1 = ((int[])((Object JavaDoc[])o1)[1])[0];
2003            int n2 = ((int[])((Object JavaDoc[])o2)[1])[0];
2004            return n1 - n2;
2005        }
2006    }
2007
2008    /**
2009     * Gets the list of substitution fonts. The list is composed of <CODE>BaseFont</CODE> and can be <CODE>null</CODE>. The fonts in this list will be used if the original
2010     * font doesn't contain the needed glyphs.
2011     * @return the list
2012     */

2013    public ArrayList JavaDoc getSubstitutionFonts() {
2014        return substitutionFonts;
2015    }
2016
2017    /**
2018     * Sets a list of substitution fonts. The list is composed of <CODE>BaseFont</CODE> and can also be <CODE>null</CODE>. The fonts in this list will be used if the original
2019     * font doesn't contain the needed glyphs.
2020     * @param substitutionFonts the list
2021     */

2022    public void setSubstitutionFonts(ArrayList JavaDoc substitutionFonts) {
2023        this.substitutionFonts = substitutionFonts;
2024    }
2025
2026    /**
2027     * Gets the XFA form processor.
2028     * @return the XFA form processor
2029     */

2030    public XfaForm getXfa() {
2031        return xfa;
2032    }
2033    
2034    private static final PdfName[] buttonRemove = {PdfName.MK, PdfName.F , PdfName.FF , PdfName.Q , PdfName.BS , PdfName.BORDER};
2035    
2036    /**
2037     * Creates a new pushbutton from an existing field. This pushbutton can be changed and be used to replace
2038     * an existing one, with the same name or other name, as long is it is in the same document. To replace an existing pushbutton
2039     * call {@link #replacePushbuttonField(String,PdfFormField)}.
2040     * @param field the field name that should be a pushbutton
2041     * @return a new pushbutton or <CODE>null</CODE> if the field is not a pushbutton
2042     */

2043    public PushbuttonField getNewPushbuttonFromField(String JavaDoc field) {
2044        try {
2045            if (getFieldType(field) != FIELD_TYPE_PUSHBUTTON)
2046                return null;
2047            float[] pos = getFieldPositions(field);
2048            Rectangle box = new Rectangle(pos[1], pos[2], pos[3], pos[4]);
2049            PushbuttonField newButton = new PushbuttonField(writer, box, null);
2050            Item item = getFieldItem(field);
2051            PdfDictionary dic = (PdfDictionary)item.merged.get(0);
2052            decodeGenericDictionary(dic, newButton);
2053            PdfDictionary mk = (PdfDictionary)PdfReader.getPdfObject(dic.get(PdfName.MK));
2054            if (mk != null) {
2055                PdfString text = (PdfString)PdfReader.getPdfObject(mk.get(PdfName.CA));
2056                if (text != null)
2057                    newButton.setText(text.toUnicodeString());
2058                PdfNumber tp = (PdfNumber)PdfReader.getPdfObject(mk.get(PdfName.TP));
2059                if (tp != null)
2060                    newButton.setLayout(tp.intValue() + 1);
2061                PdfDictionary ifit = (PdfDictionary)PdfReader.getPdfObject(mk.get(PdfName.IF));
2062                if (ifit != null) {
2063                    PdfName sw = (PdfName)PdfReader.getPdfObject(ifit.get(PdfName.SW));
2064                    if (sw != null) {
2065                        int scale = PushbuttonField.SCALE_ICON_ALWAYS;
2066                        if (sw.equals(PdfName.B))
2067                            scale = PushbuttonField.SCALE_ICON_IS_TOO_BIG;
2068                        else if (sw.equals(PdfName.S))
2069                            scale = PushbuttonField.SCALE_ICON_IS_TOO_SMALL;
2070                        else if (sw.equals(PdfName.N))
2071                            scale = PushbuttonField.SCALE_ICON_NEVER;
2072                        newButton.setScaleIcon(scale);
2073                    }
2074                    sw = (PdfName)PdfReader.getPdfObject(ifit.get(PdfName.S));
2075                    if (sw != null) {
2076                        if (sw.equals(PdfName.A))
2077                            newButton.setProportionalIcon(false);
2078                    }
2079                    PdfArray aj = (PdfArray)PdfReader.getPdfObject(ifit.get(PdfName.A));
2080                    if (aj != null && aj.size() == 2) {
2081                        float left = ((PdfNumber)PdfReader.getPdfObject((PdfObject)aj.getArrayList().get(0))).floatValue();
2082                        float bottom = ((PdfNumber)PdfReader.getPdfObject((PdfObject)aj.getArrayList().get(1))).floatValue();
2083                        newButton.setIconHorizontalAdjustment(left);
2084                        newButton.setIconVerticalAdjustment(bottom);
2085                    }
2086                    PdfObject fb = PdfReader.getPdfObject(ifit.get(PdfName.FB));
2087                    if (fb != null && fb.toString().equals("true"))
2088                        newButton.setIconFitToBounds(true);
2089                }
2090                PdfObject i = mk.get(PdfName.I);
2091                if (i != null && i.isIndirect())
2092                    newButton.setIconReference((PRIndirectReference)i);
2093            }
2094            return newButton;
2095        }
2096        catch (Exception JavaDoc e) {
2097            throw new ExceptionConverter(e);
2098        }
2099    }
2100    
2101    /**
2102     * Replaces the field with a new pushbutton. The pushbutton can be created with
2103     * {@link #getNewPushbuttonFromField(String)} from the same document or it can be a
2104     * generic PdfFormField of the type pushbutton.
2105     * @param field the field name
2106     * @param button the <CODE>PdfFormField</CODE> representing the pushbutton
2107     * @return <CODE>true</CODE> if the field was replaced, <CODE>false</CODE> if the field
2108     * was not a pushbutton
2109     */

2110    public boolean replacePushbuttonField(String JavaDoc field, PdfFormField button) {
2111        if (getFieldType(field) != FIELD_TYPE_PUSHBUTTON)
2112            return false;
2113        Item item = getFieldItem(field);
2114        PdfDictionary merged = (PdfDictionary)item.merged.get(0);
2115        PdfDictionary values = (PdfDictionary)item.values.get(0);
2116        PdfDictionary widgets = (PdfDictionary)item.widgets.get(0);
2117        for (int k = 0; k < buttonRemove.length; ++k) {
2118            merged.remove(buttonRemove[k]);
2119            values.remove(buttonRemove[k]);
2120            widgets.remove(buttonRemove[k]);
2121        }
2122        for (Iterator JavaDoc it = button.getKeys().iterator(); it.hasNext();) {
2123            PdfName key = (PdfName)it.next();
2124            if (key.equals(PdfName.T) || key.equals(PdfName.RECT))
2125                continue;
2126            merged.put(key, button.get(key));
2127            widgets.put(key, button.get(key));
2128        }
2129        return true;
2130    }
2131}
2132
Popular Tags