From 5b3065c1dff33d159423db625975a4b9ae89bb1d Mon Sep 17 00:00:00 2001 From: psoares33 Date: Sat, 13 Jun 2009 18:09:14 +0000 Subject: [PATCH] Porting update. git-svn-id: svn://svn.code.sf.net/p/itextsharp/code/trunk@29 820d3149-562b-4f88-9aa4-a8e61a3485cf --- src/core/iTextSharp/text/pdf/AcroFields.cs | 4383 +++++++++-------- src/core/iTextSharp/text/pdf/FdfWriter.cs | 3 +- src/core/iTextSharp/text/pdf/PdfStamper.cs | 22 +- .../iTextSharp/text/pdf/PushbuttonField.cs | 1244 ++--- .../iTextSharp/text/pdf/SimpleBookmark.cs | 29 +- .../text/pdf/SimpleNamedDestination.cs | 6 +- src/core/iTextSharp/text/pdf/XfdfReader.cs | 448 +- .../text/xml/simpleparser/SimpleXMLParser.cs | 28 +- src/core/iTextSharp/text/xml/xmp/PdfSchema.cs | 2 +- 9 files changed, 3284 insertions(+), 2881 deletions(-) diff --git a/src/core/iTextSharp/text/pdf/AcroFields.cs b/src/core/iTextSharp/text/pdf/AcroFields.cs index c7e37f2..84ba439 100644 --- a/src/core/iTextSharp/text/pdf/AcroFields.cs +++ b/src/core/iTextSharp/text/pdf/AcroFields.cs @@ -1,786 +1,822 @@ -using System; -using System.Collections; -using System.IO; -using System.Xml; -using System.util; -/* - * Copyright 2003-2005 by Paulo Soares. - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 - * (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the License. - * - * The Original Code is 'iText, a free JAVA-PDF library'. - * - * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by - * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. - * All Rights Reserved. - * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer - * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. - * - * Contributor(s): all the names of the contributors are added in the source code - * where applicable. - * - * Alternatively, the contents of this file may be used under the terms of the - * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the - * provisions of LGPL are applicable instead of those above. If you wish to - * allow use of your version of this file only under the terms of the LGPL - * License and not to allow others to use your version of this file under - * the MPL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the LGPL. - * If you do not delete the provisions above, a recipient may use your version - * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MPL as stated above or under the terms of the GNU - * Library General Public License as published by the Free Software Foundation; - * either version 2 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more - * details. - * - * If you didn't download this code from the following link, you should check if - * you aren't using an obsolete version: - * http://www.lowagie.com/iText/ - */ - -namespace iTextSharp.text.pdf { - /** Query and change fields in existing documents either by method - * calls or by FDF merging. - * @author Paulo Soares (psoares@consiste.pt) - */ - public class AcroFields { - - internal PdfReader reader; - internal PdfWriter writer; - internal Hashtable fields; - private int topFirst; - private Hashtable sigNames; - private bool append; - public const int DA_FONT = 0; - public const int DA_SIZE = 1; - public const int DA_COLOR = 2; - private Hashtable extensionFonts = new Hashtable(); - private XfaForm xfa; - /** - * A field type invalid or not found. - */ - public const int FIELD_TYPE_NONE = 0; - /** - * A field type. - */ - public const int FIELD_TYPE_PUSHBUTTON = 1; - /** - * A field type. - */ - public const int FIELD_TYPE_CHECKBOX = 2; - /** - * A field type. - */ - public const int FIELD_TYPE_RADIOBUTTON = 3; - /** - * A field type. - */ - public const int FIELD_TYPE_TEXT = 4; - /** - * A field type. - */ - public const int FIELD_TYPE_LIST = 5; - /** - * A field type. - */ - public const int FIELD_TYPE_COMBO = 6; - /** - * A field type. - */ - public const int FIELD_TYPE_SIGNATURE = 7; - - private bool lastWasString; - - /** Holds value of property generateAppearances. */ - private bool generateAppearances = true; - - private Hashtable localFonts = new Hashtable(); - - private float extraMarginLeft; - private float extraMarginTop; - private ArrayList substitutionFonts; - - internal AcroFields(PdfReader reader, PdfWriter writer) { - this.reader = reader; - this.writer = writer; - xfa = new XfaForm(reader); - if (writer is PdfStamperImp) { - append = ((PdfStamperImp)writer).append; - } - Fill(); - } - - internal void Fill() { - fields = new Hashtable(); - PdfDictionary top = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM)); - if (top == null) - return; - PdfArray arrfds = (PdfArray)PdfReader.GetPdfObjectRelease(top.Get(PdfName.FIELDS)); - if (arrfds == null || arrfds.Size == 0) - return; - arrfds = null; - for (int k = 1; k <= reader.NumberOfPages; ++k) { - PdfDictionary page = reader.GetPageNRelease(k); - PdfArray annots = (PdfArray)PdfReader.GetPdfObjectRelease(page.Get(PdfName.ANNOTS), page); - if (annots == null) - continue; - ArrayList arr = annots.ArrayList; - for (int j = 0; j < arr.Count; ++j) { - PdfObject annoto = PdfReader.GetPdfObject((PdfObject)arr[j], annots); - if (!(annoto is PdfDictionary)) { - PdfReader.ReleaseLastXrefPartial((PdfObject)arr[j]); +using System; +using System.Collections; +using System.IO; +using System.Xml; +using System.util; +/* + * Copyright 2003-2005 by Paulo Soares. + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ + +namespace iTextSharp.text.pdf { + /** Query and change fields in existing documents either by method + * calls or by FDF merging. + * @author Paulo Soares (psoares@consiste.pt) + */ + public class AcroFields { + + internal PdfReader reader; + internal PdfWriter writer; + internal Hashtable fields; + private int topFirst; + private Hashtable sigNames; + private bool append; + public const int DA_FONT = 0; + public const int DA_SIZE = 1; + public const int DA_COLOR = 2; + private Hashtable extensionFonts = new Hashtable(); + private XfaForm xfa; + /** + * A field type invalid or not found. + */ + public const int FIELD_TYPE_NONE = 0; + /** + * A field type. + */ + public const int FIELD_TYPE_PUSHBUTTON = 1; + /** + * A field type. + */ + public const int FIELD_TYPE_CHECKBOX = 2; + /** + * A field type. + */ + public const int FIELD_TYPE_RADIOBUTTON = 3; + /** + * A field type. + */ + public const int FIELD_TYPE_TEXT = 4; + /** + * A field type. + */ + public const int FIELD_TYPE_LIST = 5; + /** + * A field type. + */ + public const int FIELD_TYPE_COMBO = 6; + /** + * A field type. + */ + public const int FIELD_TYPE_SIGNATURE = 7; + + private bool lastWasString; + + /** Holds value of property generateAppearances. */ + private bool generateAppearances = true; + + private Hashtable localFonts = new Hashtable(); + + private float extraMarginLeft; + private float extraMarginTop; + private ArrayList substitutionFonts; + + internal AcroFields(PdfReader reader, PdfWriter writer) { + this.reader = reader; + this.writer = writer; + xfa = new XfaForm(reader); + if (writer is PdfStamperImp) { + append = ((PdfStamperImp)writer).append; + } + Fill(); + } + + internal void Fill() { + fields = new Hashtable(); + PdfDictionary top = (PdfDictionary)PdfReader.GetPdfObjectRelease(reader.Catalog.Get(PdfName.ACROFORM)); + if (top == null) + return; + PdfArray arrfds = (PdfArray)PdfReader.GetPdfObjectRelease(top.Get(PdfName.FIELDS)); + if (arrfds == null || arrfds.Size == 0) + return; + for (int k = 1; k <= reader.NumberOfPages; ++k) { + PdfDictionary page = reader.GetPageNRelease(k); + PdfArray annots = (PdfArray)PdfReader.GetPdfObjectRelease(page.Get(PdfName.ANNOTS), page); + if (annots == null) + continue; + for (int j = 0; j < annots.Size; ++j) { + PdfDictionary annot = annots.GetAsDict(j); + if (annot == null) { + PdfReader.ReleaseLastXrefPartial(annots.GetAsIndirectObject(j)); continue; } - PdfDictionary annot = (PdfDictionary)annoto; - if (!PdfName.WIDGET.Equals(annot.Get(PdfName.SUBTYPE))) { - PdfReader.ReleaseLastXrefPartial((PdfObject)arr[j]); - continue; - } - PdfDictionary widget = annot; - PdfDictionary dic = new PdfDictionary(); - dic.Merge(annot); - String name = ""; - PdfDictionary value = null; - PdfObject lastV = null; - while (annot != null) { - dic.MergeDifferent(annot); - PdfString t = (PdfString)PdfReader.GetPdfObject(annot.Get(PdfName.T)); - if (t != null) - name = t.ToUnicodeString() + "." + name; - if (lastV == null && annot.Get(PdfName.V) != null) - lastV = PdfReader.GetPdfObjectRelease(annot.Get(PdfName.V)); - if (value == null && t != null) { - value = annot; - if (annot.Get(PdfName.V) == null && lastV != null) - value.Put(PdfName.V, lastV); - } - annot = (PdfDictionary)PdfReader.GetPdfObject(annot.Get(PdfName.PARENT), annot); - } - if (name.Length > 0) - name = name.Substring(0, name.Length - 1); - Item item = (Item)fields[name]; - if (item == null) { - item = new Item(); - fields[name] = item; - } + if (!PdfName.WIDGET.Equals(annot.GetAsName(PdfName.SUBTYPE))) { + PdfReader.ReleaseLastXrefPartial(annots.GetAsIndirectObject(j)); + continue; + } + PdfDictionary widget = annot; + PdfDictionary dic = new PdfDictionary(); + dic.Merge(annot); + String name = ""; + PdfDictionary value = null; + PdfObject lastV = null; + while (annot != null) { + dic.MergeDifferent(annot); + PdfString t = annot.GetAsString(PdfName.T); + if (t != null) + name = t.ToUnicodeString() + "." + name; + if (lastV == null && annot.Get(PdfName.V) != null) + lastV = PdfReader.GetPdfObjectRelease(annot.Get(PdfName.V)); + if (value == null && t != null) { + value = annot; + if (annot.Get(PdfName.V) == null && lastV != null) + value.Put(PdfName.V, lastV); + } + annot = annot.GetAsDict(PdfName.PARENT); + } + if (name.Length > 0) + name = name.Substring(0, name.Length - 1); + Item item = (Item)fields[name]; + if (item == null) { + item = new Item(); + fields[name] = item; + } if (value == null) - item.values.Add(widget); + item.AddValue(widget); else - item.values.Add(value); - item.widgets.Add(widget); - item.widget_refs.Add(arr[j]); // must be a reference + item.AddValue(value); + item.AddWidget(widget); + item.AddWidgetRef(annots.GetAsIndirectObject(j)); // must be a reference if (top != null) dic.MergeDifferent(top); - item.merged.Add(dic); - item.page.Add(k); - item.tabOrder.Add(j); + item.AddMerged(dic); + item.AddPage(k); + item.AddTabOrder(j); + } + } + // some tools produce invisible signatures without an entry in the page annotation array + // look for a single level annotation + PdfNumber sigFlags = top.GetAsNumber(PdfName.SIGFLAGS); + if (sigFlags == null || (sigFlags.IntValue & 1) != 1) + return; + for (int j = 0; j < arrfds.Size; ++j) { + PdfDictionary annot = arrfds.GetAsDict(j); + if (annot == null) { + PdfReader.ReleaseLastXrefPartial(arrfds.GetAsIndirectObject(j)); + continue; } + if (!PdfName.WIDGET.Equals(annot.GetAsName(PdfName.SUBTYPE))) { + PdfReader.ReleaseLastXrefPartial(arrfds.GetAsIndirectObject(j)); + continue; + } + PdfArray kids = (PdfArray)PdfReader.GetPdfObjectRelease(annot.Get(PdfName.KIDS)); + if (kids != null) + continue; + PdfDictionary dic = new PdfDictionary(); + dic.Merge(annot); + PdfString t = annot.GetAsString(PdfName.T); + if (t == null) + continue; + String name = t.ToUnicodeString(); + if (fields.ContainsKey(name)) + continue; + Item item = new Item(); + fields[name] = item; + item.AddValue(dic); + item.AddWidget(dic); + item.AddWidgetRef(arrfds.GetAsIndirectObject(j)); // must be a reference + item.AddMerged(dic); + item.AddPage(-1); + item.AddTabOrder(-1); } - } - - /** Gets the list of appearance names. Use it to get the names allowed - * with radio and checkbox fields. If the /Opt key exists the values will - * also be included. The name 'Off' may also be valid - * even if not returned in the list. - * @param fieldName the fully qualified field name - * @return the list of names or null if the field does not exist - */ - public String[] GetAppearanceStates(String fieldName) { - Item fd = (Item)fields[fieldName]; - if (fd == null) - return null; - Hashtable names = new Hashtable(); - PdfDictionary vals = (PdfDictionary)fd.values[0]; - PdfObject opts = PdfReader.GetPdfObject(vals.Get(PdfName.OPT)); - if (opts != null) { - if (opts.IsString()) - names[((PdfString)opts).ToUnicodeString()] = null; - else if (opts.IsArray()) { - ArrayList list = ((PdfArray)opts).ArrayList; - for (int k = 0; k < list.Count; ++k) { - PdfObject v = PdfReader.GetPdfObject((PdfObject)list[k]); - if (v != null && v.IsString()) - names[((PdfString)v).ToUnicodeString()] = null; + } + + /** Gets the list of appearance names. Use it to get the names allowed + * with radio and checkbox fields. If the /Opt key exists the values will + * also be included. The name 'Off' may also be valid + * even if not returned in the list. + * @param fieldName the fully qualified field name + * @return the list of names or null if the field does not exist + */ + public String[] GetAppearanceStates(String fieldName) { + Item fd = (Item)fields[fieldName]; + if (fd == null) + return null; + Hashtable names = new Hashtable(); + PdfDictionary vals = fd.GetValue(0); + PdfString stringOpt = vals.GetAsString( PdfName.OPT ); + if (stringOpt != null) { + names[stringOpt.ToUnicodeString()] = null; + } + else { + PdfArray arrayOpt = vals.GetAsArray(PdfName.OPT); + if (arrayOpt != null) { + for (int k = 0; k < arrayOpt.Size; ++k) { + PdfString valStr = arrayOpt.GetAsString( k ); + if (valStr != null) + names[valStr.ToUnicodeString()] = null; } } } - ArrayList wd = fd.widgets; - for (int k = 0; k < wd.Count; ++k) { - PdfDictionary dic = (PdfDictionary)wd[k]; - dic = (PdfDictionary)PdfReader.GetPdfObject(dic.Get(PdfName.AP)); + for (int k = 0; k < fd.Size; ++k) { + PdfDictionary dic = fd.GetWidget( k ); + dic = dic.GetAsDict(PdfName.AP); if (dic == null) continue; - PdfObject ob = PdfReader.GetPdfObject(dic.Get(PdfName.N)); - if (ob == null || !ob.IsDictionary()) - continue; - dic = (PdfDictionary)ob; - foreach (PdfName pname in dic.Keys) { - String name = PdfName.DecodeName(pname.ToString()); - names[name] = null; - } - } - string[] outs = new string[names.Count]; - names.Keys.CopyTo(outs, 0); - return outs; - } - - private String[] GetListOption(String fieldName, int idx) { - Item fd = GetFieldItem(fieldName); - if (fd == null) - return null; - PdfObject obj = PdfReader.GetPdfObject(((PdfDictionary)fd.merged[0]).Get(PdfName.OPT)); - if (obj == null || !obj.IsArray()) - return null; - PdfArray ar = (PdfArray)obj; - String[] ret = new String[ar.Size]; - ArrayList a = ar.ArrayList; - for (int k = 0; k < a.Count; ++k) { - obj = PdfReader.GetPdfObject((PdfObject)a[k]); - try { - if (obj.IsArray()) { - obj = (PdfObject)((PdfArray)obj).ArrayList[idx]; - } - if (obj.IsString()) - ret[k] = ((PdfString)obj).ToUnicodeString(); - else - ret[k] = obj.ToString(); - } - catch { - ret[k] = ""; - } - } - return ret; - } - - /** - * Gets the list of export option values from fields of type list or combo. - * If the field doesn't exist or the field type is not list or combo it will return - * null. - * @param fieldName the field name - * @return the list of export option values from fields of type list or combo - */ - public String[] GetListOptionExport(String fieldName) { - return GetListOption(fieldName, 0); - } - - /** - * Gets the list of display option values from fields of type list or combo. - * If the field doesn't exist or the field type is not list or combo it will return - * null. - * @param fieldName the field name - * @return the list of export option values from fields of type list or combo - */ - public String[] GetListOptionDisplay(String fieldName) { - return GetListOption(fieldName, 1); - } - - /** - * Sets the option list for fields of type list or combo. One of exportValues - * or displayValues may be null but not both. This method will only - * set the list but will not set the value or appearance. For that, calling setField() - * is required. - *

- * An example: - *

- *

-        * PdfReader pdf = new PdfReader("input.pdf");
-        * PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("output.pdf"));
-        * AcroFields af = stp.GetAcroFields();
-        * af.SetListOption("ComboBox", new String[]{"a", "b", "c"}, new String[]{"first", "second", "third"});
-        * af.SetField("ComboBox", "b");
-        * stp.Close();
-        * 
- * @param fieldName the field name - * @param exportValues the export values - * @param displayValues the display values - * @return true if the operation succeeded, false otherwise - */ - public bool SetListOption(String fieldName, String[] exportValues, String[] displayValues) { - if (exportValues == null && displayValues == null) - return false; - if (exportValues != null && displayValues != null && exportValues.Length != displayValues.Length) - throw new ArgumentException("The export and the display array must have the same size."); - int ftype = GetFieldType(fieldName); - if (ftype != FIELD_TYPE_COMBO && ftype != FIELD_TYPE_LIST) - return false; - Item fd = (Item)fields[fieldName]; - String[] sing = null; - if (exportValues == null && displayValues != null) - sing = displayValues; - else if (exportValues != null && displayValues == null) - sing = exportValues; - PdfArray opt = new PdfArray(); - if (sing != null) { - for (int k = 0; k < sing.Length; ++k) - opt.Add(new PdfString(sing[k], PdfObject.TEXT_UNICODE)); - } - else { - for (int k = 0; k < exportValues.Length; ++k) { - PdfArray a = new PdfArray(); - a.Add(new PdfString(exportValues[k], PdfObject.TEXT_UNICODE)); - a.Add(new PdfString(displayValues[k], PdfObject.TEXT_UNICODE)); - opt.Add(a); - } - } - ((PdfDictionary)fd.values[0]).Put(PdfName.OPT, opt); - for (int j = 0; j < fd.merged.Count; ++j) - ((PdfDictionary)fd.merged[j]).Put(PdfName.OPT, opt); - return true; - } - - /** - * Gets the field type. The type can be one of: FIELD_TYPE_PUSHBUTTON, - * FIELD_TYPE_CHECKBOX, FIELD_TYPE_RADIOBUTTON, - * FIELD_TYPE_TEXT, FIELD_TYPE_LIST, - * FIELD_TYPE_COMBO or FIELD_TYPE_SIGNATURE. - *

- * If the field does not exist or is invalid it returns - * FIELD_TYPE_NONE. - * @param fieldName the field name - * @return the field type - */ - public int GetFieldType(String fieldName) { - Item fd = GetFieldItem(fieldName); - if (fd == null) - return FIELD_TYPE_NONE; - PdfObject type = PdfReader.GetPdfObject(((PdfDictionary)fd.merged[0]).Get(PdfName.FT)); + dic = dic.GetAsDict(PdfName.N); + if (dic == null) + continue; + foreach (PdfName pname in dic.Keys) { + String name = PdfName.DecodeName(pname.ToString()); + names[name] = null; + } + } + string[] outs = new string[names.Count]; + names.Keys.CopyTo(outs, 0); + return outs; + } + + private String[] GetListOption(String fieldName, int idx) { + Item fd = GetFieldItem(fieldName); + if (fd == null) + return null; + PdfArray ar = fd.GetMerged(0).GetAsArray(PdfName.OPT); + if (ar == null) + return null; + String[] ret = new String[ar.Size]; + for (int k = 0; k < ar.Size; ++k) { + PdfObject obj = ar.GetDirectObject( k ); + try { + if (obj.IsArray()) { + obj = ((PdfArray)obj).GetDirectObject(idx); + } + if (obj.IsString()) + ret[k] = ((PdfString)obj).ToUnicodeString(); + else + ret[k] = obj.ToString(); + } + catch { + ret[k] = ""; + } + } + return ret; + } + + /** + * Gets the list of export option values from fields of type list or combo. + * If the field doesn't exist or the field type is not list or combo it will return + * null. + * @param fieldName the field name + * @return the list of export option values from fields of type list or combo + */ + public String[] GetListOptionExport(String fieldName) { + return GetListOption(fieldName, 0); + } + + /** + * Gets the list of display option values from fields of type list or combo. + * If the field doesn't exist or the field type is not list or combo it will return + * null. + * @param fieldName the field name + * @return the list of export option values from fields of type list or combo + */ + public String[] GetListOptionDisplay(String fieldName) { + return GetListOption(fieldName, 1); + } + + /** + * Sets the option list for fields of type list or combo. One of exportValues + * or displayValues may be null but not both. This method will only + * set the list but will not set the value or appearance. For that, calling setField() + * is required. + *

+ * An example: + *

+ *

+        * PdfReader pdf = new PdfReader("input.pdf");
+        * PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("output.pdf"));
+        * AcroFields af = stp.GetAcroFields();
+        * af.SetListOption("ComboBox", new String[]{"a", "b", "c"}, new String[]{"first", "second", "third"});
+        * af.SetField("ComboBox", "b");
+        * stp.Close();
+        * 
+ * @param fieldName the field name + * @param exportValues the export values + * @param displayValues the display values + * @return true if the operation succeeded, false otherwise + */ + public bool SetListOption(String fieldName, String[] exportValues, String[] displayValues) { + if (exportValues == null && displayValues == null) + return false; + if (exportValues != null && displayValues != null && exportValues.Length != displayValues.Length) + throw new ArgumentException("The export and the display array must have the same size."); + int ftype = GetFieldType(fieldName); + if (ftype != FIELD_TYPE_COMBO && ftype != FIELD_TYPE_LIST) + return false; + Item fd = (Item)fields[fieldName]; + String[] sing = null; + if (exportValues == null && displayValues != null) + sing = displayValues; + else if (exportValues != null && displayValues == null) + sing = exportValues; + PdfArray opt = new PdfArray(); + if (sing != null) { + for (int k = 0; k < sing.Length; ++k) + opt.Add(new PdfString(sing[k], PdfObject.TEXT_UNICODE)); + } + else { + for (int k = 0; k < exportValues.Length; ++k) { + PdfArray a = new PdfArray(); + a.Add(new PdfString(exportValues[k], PdfObject.TEXT_UNICODE)); + a.Add(new PdfString(displayValues[k], PdfObject.TEXT_UNICODE)); + opt.Add(a); + } + } + fd.WriteToAll( PdfName.OPT, opt, Item.WRITE_VALUE | Item.WRITE_MERGED ); + return true; + } + + /** + * Gets the field type. The type can be one of: FIELD_TYPE_PUSHBUTTON, + * FIELD_TYPE_CHECKBOX, FIELD_TYPE_RADIOBUTTON, + * FIELD_TYPE_TEXT, FIELD_TYPE_LIST, + * FIELD_TYPE_COMBO or FIELD_TYPE_SIGNATURE. + *

+ * If the field does not exist or is invalid it returns + * FIELD_TYPE_NONE. + * @param fieldName the field name + * @return the field type + */ + public int GetFieldType(String fieldName) { + Item fd = GetFieldItem(fieldName); + if (fd == null) + return FIELD_TYPE_NONE; + PdfDictionary merged = fd.GetMerged( 0 ); + PdfName type = merged.GetAsName(PdfName.FT); if (type == null) return FIELD_TYPE_NONE; int ff = 0; - PdfObject ffo = PdfReader.GetPdfObject(((PdfDictionary)fd.merged[0]).Get(PdfName.FF)); - if (ffo != null && ffo.Type == PdfObject.NUMBER) - ff = ((PdfNumber)ffo).IntValue; - if (PdfName.BTN.Equals(type)) { - if ((ff & PdfFormField.FF_PUSHBUTTON) != 0) - return FIELD_TYPE_PUSHBUTTON; - if ((ff & PdfFormField.FF_RADIO) != 0) - return FIELD_TYPE_RADIOBUTTON; - else - return FIELD_TYPE_CHECKBOX; + PdfNumber ffo = merged.GetAsNumber(PdfName.FF); + if (ffo != null) { + ff = ffo.IntValue; } - else if (PdfName.TX.Equals(type)) { - return FIELD_TYPE_TEXT; - } - else if (PdfName.CH.Equals(type)) { - if ((ff & PdfFormField.FF_COMBO) != 0) - return FIELD_TYPE_COMBO; - else - return FIELD_TYPE_LIST; - } - else if (PdfName.SIG.Equals(type)) { - return FIELD_TYPE_SIGNATURE; - } - return FIELD_TYPE_NONE; - } - - /** - * Export the fields as a FDF. - * @param writer the FDF writer - */ - public void ExportAsFdf(FdfWriter writer) { - foreach (DictionaryEntry entry in fields) { - Item item = (Item)entry.Value; - string name = (String)entry.Key; - PdfObject v = PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.V)); - if (v == null) - continue; - string value = GetField(name); - if (lastWasString) - writer.SetFieldAsString(name, value); - else - writer.SetFieldAsName(name, value); - } - } - - /** - * Renames a field. Only the last part of the name can be renamed. For example, - * if the original field is "ab.cd.ef" only the "ef" part can be renamed. - * @param oldName the old field name - * @param newName the new field name - * @return true if the renaming was successful, false - * otherwise - */ - public bool RenameField(String oldName, String newName) { - int idx1 = oldName.LastIndexOf('.') + 1; - int idx2 = newName.LastIndexOf('.') + 1; - if (idx1 != idx2) - return false; - if (!oldName.Substring(0, idx1).Equals(newName.Substring(0, idx2))) - return false; - if (fields.ContainsKey(newName)) - return false; - Item item = (Item)fields[oldName]; - if (item == null) - return false; - newName = newName.Substring(idx2); - PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE); - for (int k = 0; k < item.merged.Count; ++k) { - PdfDictionary dic = (PdfDictionary)item.values[k]; - dic.Put(PdfName.T, ss); - MarkUsed(dic); - dic = (PdfDictionary)item.merged[k]; - dic.Put(PdfName.T, ss); - } - fields.Remove(oldName); - fields[newName] = item; - return true; - } - - public static Object[] SplitDAelements(String da) { - PRTokeniser tk = new PRTokeniser(PdfEncodings.ConvertToBytes(da, null)); - ArrayList stack = new ArrayList(); - Object[] ret = new Object[3]; - while (tk.NextToken()) { - if (tk.TokenType == PRTokeniser.TK_COMMENT) - continue; - if (tk.TokenType == PRTokeniser.TK_OTHER) { - String oper = tk.StringValue; - if (oper.Equals("Tf")) { - if (stack.Count >= 2) { - ret[DA_FONT] = stack[stack.Count - 2]; - ret[DA_SIZE] = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); - } - } - else if (oper.Equals("g")) { - if (stack.Count >= 1) { - float gray = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); - if (gray != 0) - ret[DA_COLOR] = new GrayColor(gray); - } - } - else if (oper.Equals("rg")) { - if (stack.Count >= 3) { - float red = float.Parse((String)stack[stack.Count - 3], System.Globalization.NumberFormatInfo.InvariantInfo); - float green = float.Parse((String)stack[stack.Count - 2], System.Globalization.NumberFormatInfo.InvariantInfo); - float blue = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); - ret[DA_COLOR] = new Color(red, green, blue); - } - } - else if (oper.Equals("k")) { - if (stack.Count >= 4) { - float cyan = float.Parse((String)stack[stack.Count - 4], System.Globalization.NumberFormatInfo.InvariantInfo); - float magenta = float.Parse((String)stack[stack.Count - 3], System.Globalization.NumberFormatInfo.InvariantInfo); - float yellow = float.Parse((String)stack[stack.Count - 2], System.Globalization.NumberFormatInfo.InvariantInfo); - float black = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); - ret[DA_COLOR] = new CMYKColor(cyan, magenta, yellow, black); - } - } - stack.Clear(); - } - else - stack.Add(tk.StringValue); - } - return ret; - } - - public void DecodeGenericDictionary(PdfDictionary merged, BaseField tx) { - int flags = 0; - // the text size and color - PdfString da = (PdfString)PdfReader.GetPdfObject(merged.Get(PdfName.DA)); - if (da != null) { - Object[] dab = SplitDAelements(da.ToUnicodeString()); - if (dab[DA_SIZE] != null) - tx.FontSize = (float)dab[DA_SIZE]; - if (dab[DA_COLOR] != null) - tx.TextColor = (Color)dab[DA_COLOR]; - if (dab[DA_FONT] != null) { - PdfDictionary font = (PdfDictionary)PdfReader.GetPdfObject(merged.Get(PdfName.DR)); - if (font != null) { - font = (PdfDictionary)PdfReader.GetPdfObject(font.Get(PdfName.FONT)); - if (font != null) { - PdfObject po = font.Get(new PdfName((String)dab[DA_FONT])); - if (po != null && po.Type == PdfObject.INDIRECT) { - PRIndirectReference por = (PRIndirectReference)po; - BaseFont bp = new DocumentFont((PRIndirectReference)po); - tx.Font = bp; - int porkey = por.Number; - BaseFont porf = (BaseFont)extensionFonts[porkey]; - if (porf == null) { - if (!extensionFonts.ContainsKey(porkey)) { - PdfDictionary fo = (PdfDictionary)PdfReader.GetPdfObject(po); - PdfDictionary fd = (PdfDictionary)PdfReader.GetPdfObject(fo.Get(PdfName.FONTDESCRIPTOR)); - if (fd != null) { - PRStream prs = (PRStream)PdfReader.GetPdfObject(fd.Get(PdfName.FONTFILE2)); - if (prs == null) - prs = (PRStream)PdfReader.GetPdfObject(fd.Get(PdfName.FONTFILE3)); - if (prs == null) { - extensionFonts[porkey] = null; - } - else { - try { - porf = BaseFont.CreateFont("font.ttf", BaseFont.IDENTITY_H, true, false, PdfReader.GetStreamBytes(prs), null); - } - catch { - } - extensionFonts[porkey] = porf; - } - } - } - } - if (tx is TextField) - ((TextField)tx).ExtensionFont = porf; - } - else { - BaseFont bf = (BaseFont)localFonts[dab[DA_FONT]]; - if (bf == null) { - String[] fn = (String[])stdFieldFontNames[dab[DA_FONT]]; - if (fn != null) { - try { - String enc = "winansi"; - if (fn.Length > 1) - enc = fn[1]; - bf = BaseFont.CreateFont(fn[0], enc, false); - tx.Font = bf; - } - catch { - // empty - } - } - } - else - tx.Font = bf; - } - } - } - } - } - //rotation, border and backgound color - PdfDictionary mk = (PdfDictionary)PdfReader.GetPdfObject(merged.Get(PdfName.MK)); - if (mk != null) { - PdfArray ar = (PdfArray)PdfReader.GetPdfObject(mk.Get(PdfName.BC)); - Color border = GetMKColor(ar); - tx.BorderColor = border; - if (border != null) - tx.BorderWidth = 1; - ar = (PdfArray)PdfReader.GetPdfObject(mk.Get(PdfName.BG)); - tx.BackgroundColor = GetMKColor(ar); - PdfNumber rotation = (PdfNumber)PdfReader.GetPdfObject(mk.Get(PdfName.R)); - if (rotation != null) - tx.Rotation = rotation.IntValue; - } - //flags - PdfNumber nfl = (PdfNumber)PdfReader.GetPdfObject(merged.Get(PdfName.F)); - flags = 0; - tx.Visibility = BaseField.VISIBLE_BUT_DOES_NOT_PRINT; - if (nfl != null) { - flags = nfl.IntValue; - if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) != 0) - tx.Visibility = BaseField.HIDDEN; - else if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_NOVIEW) != 0) - tx.Visibility = BaseField.HIDDEN_BUT_PRINTABLE; - else if ((flags & PdfFormField.FLAGS_PRINT) != 0) - tx.Visibility = BaseField.VISIBLE; - } - //multiline - nfl = (PdfNumber)PdfReader.GetPdfObject(merged.Get(PdfName.FF)); - flags = 0; - if (nfl != null) - flags = nfl.IntValue; - tx.Options = flags; - if ((flags & PdfFormField.FF_COMB) != 0) { - PdfNumber maxLen = (PdfNumber)PdfReader.GetPdfObject(merged.Get(PdfName.MAXLEN)); - int len = 0; - if (maxLen != null) - len = maxLen.IntValue; - tx.MaxCharacterLength = len; - } - //alignment - nfl = (PdfNumber)PdfReader.GetPdfObject(merged.Get(PdfName.Q)); - if (nfl != null) { - if (nfl.IntValue == PdfFormField.Q_CENTER) - tx.Alignment = Element.ALIGN_CENTER; - else if (nfl.IntValue == PdfFormField.Q_RIGHT) - tx.Alignment = Element.ALIGN_RIGHT; - } - //border styles - PdfDictionary bs = (PdfDictionary)PdfReader.GetPdfObject(merged.Get(PdfName.BS)); - if (bs != null) { - PdfNumber w = (PdfNumber)PdfReader.GetPdfObject(bs.Get(PdfName.W)); - if (w != null) - tx.BorderWidth = w.FloatValue; - PdfName s = (PdfName)PdfReader.GetPdfObject(bs.Get(PdfName.S)); - if (PdfName.D.Equals(s)) - tx.BorderStyle = PdfBorderDictionary.STYLE_DASHED; - else if (PdfName.B.Equals(s)) - tx.BorderStyle = PdfBorderDictionary.STYLE_BEVELED; - else if (PdfName.I.Equals(s)) - tx.BorderStyle = PdfBorderDictionary.STYLE_INSET; - else if (PdfName.U.Equals(s)) - tx.BorderStyle = PdfBorderDictionary.STYLE_UNDERLINE; - } - else { - PdfArray bd = (PdfArray)PdfReader.GetPdfObject(merged.Get(PdfName.BORDER)); + if (PdfName.BTN.Equals(type)) { + if ((ff & PdfFormField.FF_PUSHBUTTON) != 0) + return FIELD_TYPE_PUSHBUTTON; + if ((ff & PdfFormField.FF_RADIO) != 0) + return FIELD_TYPE_RADIOBUTTON; + else + return FIELD_TYPE_CHECKBOX; + } + else if (PdfName.TX.Equals(type)) { + return FIELD_TYPE_TEXT; + } + else if (PdfName.CH.Equals(type)) { + if ((ff & PdfFormField.FF_COMBO) != 0) + return FIELD_TYPE_COMBO; + else + return FIELD_TYPE_LIST; + } + else if (PdfName.SIG.Equals(type)) { + return FIELD_TYPE_SIGNATURE; + } + return FIELD_TYPE_NONE; + } + + /** + * Export the fields as a FDF. + * @param writer the FDF writer + */ + public void ExportAsFdf(FdfWriter writer) { + foreach (DictionaryEntry entry in fields) { + Item item = (Item)entry.Value; + string name = (String)entry.Key; + PdfObject v = item.GetMerged(0).Get(PdfName.V); + if (v == null) + continue; + string value = GetField(name); + if (lastWasString) + writer.SetFieldAsString(name, value); + else + writer.SetFieldAsName(name, value); + } + } + + /** + * Renames a field. Only the last part of the name can be renamed. For example, + * if the original field is "ab.cd.ef" only the "ef" part can be renamed. + * @param oldName the old field name + * @param newName the new field name + * @return true if the renaming was successful, false + * otherwise + */ + public bool RenameField(String oldName, String newName) { + int idx1 = oldName.LastIndexOf('.') + 1; + int idx2 = newName.LastIndexOf('.') + 1; + if (idx1 != idx2) + return false; + if (!oldName.Substring(0, idx1).Equals(newName.Substring(0, idx2))) + return false; + if (fields.ContainsKey(newName)) + return false; + Item item = (Item)fields[oldName]; + if (item == null) + return false; + newName = newName.Substring(idx2); + PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE); + item.WriteToAll( PdfName.T, ss, Item.WRITE_VALUE | Item.WRITE_MERGED); + item.MarkUsed( this, Item.WRITE_VALUE ); + fields.Remove(oldName); + fields[newName] = item; + return true; + } + + public static Object[] SplitDAelements(String da) { + PRTokeniser tk = new PRTokeniser(PdfEncodings.ConvertToBytes(da, null)); + ArrayList stack = new ArrayList(); + Object[] ret = new Object[3]; + while (tk.NextToken()) { + if (tk.TokenType == PRTokeniser.TK_COMMENT) + continue; + if (tk.TokenType == PRTokeniser.TK_OTHER) { + String oper = tk.StringValue; + if (oper.Equals("Tf")) { + if (stack.Count >= 2) { + ret[DA_FONT] = stack[stack.Count - 2]; + ret[DA_SIZE] = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); + } + } + else if (oper.Equals("g")) { + if (stack.Count >= 1) { + float gray = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); + if (gray != 0) + ret[DA_COLOR] = new GrayColor(gray); + } + } + else if (oper.Equals("rg")) { + if (stack.Count >= 3) { + float red = float.Parse((String)stack[stack.Count - 3], System.Globalization.NumberFormatInfo.InvariantInfo); + float green = float.Parse((String)stack[stack.Count - 2], System.Globalization.NumberFormatInfo.InvariantInfo); + float blue = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); + ret[DA_COLOR] = new Color(red, green, blue); + } + } + else if (oper.Equals("k")) { + if (stack.Count >= 4) { + float cyan = float.Parse((String)stack[stack.Count - 4], System.Globalization.NumberFormatInfo.InvariantInfo); + float magenta = float.Parse((String)stack[stack.Count - 3], System.Globalization.NumberFormatInfo.InvariantInfo); + float yellow = float.Parse((String)stack[stack.Count - 2], System.Globalization.NumberFormatInfo.InvariantInfo); + float black = float.Parse((String)stack[stack.Count - 1], System.Globalization.NumberFormatInfo.InvariantInfo); + ret[DA_COLOR] = new CMYKColor(cyan, magenta, yellow, black); + } + } + stack.Clear(); + } + else + stack.Add(tk.StringValue); + } + return ret; + } + + public void DecodeGenericDictionary(PdfDictionary merged, BaseField tx) { + int flags = 0; + // the text size and color + PdfString da = merged.GetAsString(PdfName.DA); + if (da != null) { + Object[] dab = SplitDAelements(da.ToUnicodeString()); + if (dab[DA_SIZE] != null) + tx.FontSize = (float)dab[DA_SIZE]; + if (dab[DA_COLOR] != null) + tx.TextColor = (Color)dab[DA_COLOR]; + if (dab[DA_FONT] != null) { + PdfDictionary font = merged.GetAsDict(PdfName.DR); + if (font != null) { + font = font.GetAsDict(PdfName.FONT); + if (font != null) { + PdfObject po = font.Get(new PdfName((String)dab[DA_FONT])); + if (po != null && po.Type == PdfObject.INDIRECT) { + PRIndirectReference por = (PRIndirectReference)po; + BaseFont bp = new DocumentFont((PRIndirectReference)po); + tx.Font = bp; + int porkey = por.Number; + BaseFont porf = (BaseFont)extensionFonts[porkey]; + if (porf == null) { + if (!extensionFonts.ContainsKey(porkey)) { + PdfDictionary fo = (PdfDictionary)PdfReader.GetPdfObject(po); + PdfDictionary fd = fo.GetAsDict(PdfName.FONTDESCRIPTOR); + if (fd != null) { + PRStream prs = (PRStream)PdfReader.GetPdfObject(fd.Get(PdfName.FONTFILE2)); + if (prs == null) + prs = (PRStream)PdfReader.GetPdfObject(fd.Get(PdfName.FONTFILE3)); + if (prs == null) { + extensionFonts[porkey] = null; + } + else { + try { + porf = BaseFont.CreateFont("font.ttf", BaseFont.IDENTITY_H, true, false, PdfReader.GetStreamBytes(prs), null); + } + catch { + } + extensionFonts[porkey] = porf; + } + } + } + } + if (tx is TextField) + ((TextField)tx).ExtensionFont = porf; + } + else { + BaseFont bf = (BaseFont)localFonts[dab[DA_FONT]]; + if (bf == null) { + String[] fn = (String[])stdFieldFontNames[dab[DA_FONT]]; + if (fn != null) { + try { + String enc = "winansi"; + if (fn.Length > 1) + enc = fn[1]; + bf = BaseFont.CreateFont(fn[0], enc, false); + tx.Font = bf; + } + catch { + // empty + } + } + } + else + tx.Font = bf; + } + } + } + } + } + //rotation, border and backgound color + PdfDictionary mk = merged.GetAsDict(PdfName.MK); + if (mk != null) { + PdfArray ar = mk.GetAsArray(PdfName.BC); + Color border = GetMKColor(ar); + tx.BorderColor = border; + if (border != null) + tx.BorderWidth = 1; + ar = mk.GetAsArray(PdfName.BG); + tx.BackgroundColor = GetMKColor(ar); + PdfNumber rotation = mk.GetAsNumber(PdfName.R); + if (rotation != null) + tx.Rotation = rotation.IntValue; + } + //flags + PdfNumber nfl = merged.GetAsNumber(PdfName.F); + flags = 0; + tx.Visibility = BaseField.VISIBLE_BUT_DOES_NOT_PRINT; + if (nfl != null) { + flags = nfl.IntValue; + if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) != 0) + tx.Visibility = BaseField.HIDDEN; + else if ((flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_NOVIEW) != 0) + tx.Visibility = BaseField.HIDDEN_BUT_PRINTABLE; + else if ((flags & PdfFormField.FLAGS_PRINT) != 0) + tx.Visibility = BaseField.VISIBLE; + } + //multiline + nfl = merged.GetAsNumber(PdfName.FF); + flags = 0; + if (nfl != null) + flags = nfl.IntValue; + tx.Options = flags; + if ((flags & PdfFormField.FF_COMB) != 0) { + PdfNumber maxLen = merged.GetAsNumber(PdfName.MAXLEN); + int len = 0; + if (maxLen != null) + len = maxLen.IntValue; + tx.MaxCharacterLength = len; + } + //alignment + nfl = merged.GetAsNumber(PdfName.Q); + if (nfl != null) { + if (nfl.IntValue == PdfFormField.Q_CENTER) + tx.Alignment = Element.ALIGN_CENTER; + else if (nfl.IntValue == PdfFormField.Q_RIGHT) + tx.Alignment = Element.ALIGN_RIGHT; + } + //border styles + PdfDictionary bs = merged.GetAsDict(PdfName.BS); + if (bs != null) { + PdfNumber w = bs.GetAsNumber(PdfName.W); + if (w != null) + tx.BorderWidth = w.FloatValue; + PdfName s = bs.GetAsName(PdfName.S); + if (PdfName.D.Equals(s)) + tx.BorderStyle = PdfBorderDictionary.STYLE_DASHED; + else if (PdfName.B.Equals(s)) + tx.BorderStyle = PdfBorderDictionary.STYLE_BEVELED; + else if (PdfName.I.Equals(s)) + tx.BorderStyle = PdfBorderDictionary.STYLE_INSET; + else if (PdfName.U.Equals(s)) + tx.BorderStyle = PdfBorderDictionary.STYLE_UNDERLINE; + } + else { + PdfArray bd = merged.GetAsArray(PdfName.BORDER); if (bd != null) { - ArrayList ar = bd.ArrayList; - if (ar.Count >= 3) - tx.BorderWidth = ((PdfNumber)ar[2]).FloatValue; - if (ar.Count >= 4) - tx.BorderStyle = PdfBorderDictionary.STYLE_DASHED; - } - } - } - - internal PdfAppearance GetAppearance(PdfDictionary merged, String text, String fieldName) { - topFirst = 0; - TextField tx = null; - if (fieldCache == null || !fieldCache.ContainsKey(fieldName)) { - tx = new TextField(writer, null, null); - tx.SetExtraMargin(extraMarginLeft, extraMarginTop); - tx.BorderWidth = 0; - tx.SubstitutionFonts = substitutionFonts; - DecodeGenericDictionary(merged, tx); - //rect - PdfArray rect = (PdfArray)PdfReader.GetPdfObject(merged.Get(PdfName.RECT)); - Rectangle box = PdfReader.GetNormalizedRectangle(rect); - if (tx.Rotation == 90 || tx.Rotation == 270) - box = box.Rotate(); - tx.Box = box; - if (fieldCache != null) - fieldCache[fieldName] = tx; - } - else { - tx = (TextField)fieldCache[fieldName]; - tx.Writer = writer; - } - PdfName fieldType = (PdfName)PdfReader.GetPdfObject(merged.Get(PdfName.FT)); - if (PdfName.TX.Equals(fieldType)) { - tx.Text = text; - return tx.GetAppearance(); - } - if (!PdfName.CH.Equals(fieldType)) - throw new DocumentException("An appearance was requested without a variable text field."); - PdfArray opt = (PdfArray)PdfReader.GetPdfObject(merged.Get(PdfName.OPT)); + if (bd.Size >= 3) + tx.BorderWidth = bd.GetAsNumber(2).FloatValue; + if (bd.Size >= 4) + tx.BorderStyle = PdfBorderDictionary.STYLE_DASHED; + } + } + } + + internal PdfAppearance GetAppearance(PdfDictionary merged, String text, String fieldName) { + topFirst = 0; + TextField tx = null; + if (fieldCache == null || !fieldCache.ContainsKey(fieldName)) { + tx = new TextField(writer, null, null); + tx.SetExtraMargin(extraMarginLeft, extraMarginTop); + tx.BorderWidth = 0; + tx.SubstitutionFonts = substitutionFonts; + DecodeGenericDictionary(merged, tx); + //rect + PdfArray rect = merged.GetAsArray(PdfName.RECT); + Rectangle box = PdfReader.GetNormalizedRectangle(rect); + if (tx.Rotation == 90 || tx.Rotation == 270) + box = box.Rotate(); + tx.Box = box; + if (fieldCache != null) + fieldCache[fieldName] = tx; + } + else { + tx = (TextField)fieldCache[fieldName]; + tx.Writer = writer; + } + PdfName fieldType = merged.GetAsName(PdfName.FT); + if (PdfName.TX.Equals(fieldType)) { + tx.Text = text; + return tx.GetAppearance(); + } + if (!PdfName.CH.Equals(fieldType)) + throw new DocumentException("An appearance was requested without a variable text field."); + PdfArray opt = merged.GetAsArray(PdfName.OPT); int flags = 0; - PdfNumber nfl = (PdfNumber)PdfReader.GetPdfObject(merged.Get(PdfName.FF)); - if (nfl != null) - flags = nfl.IntValue; - if ((flags & PdfFormField.FF_COMBO) != 0 && opt == null) { - tx.Text = text; - return tx.GetAppearance(); - } - if (opt != null) { - ArrayList op = opt.ArrayList; - String[] choices = new String[op.Count]; - String[] choicesExp = new String[op.Count]; - for (int k = 0; k < op.Count; ++k) { - PdfObject obj = (PdfObject)op[k]; - if (obj.IsString()) { - choices[k] = choicesExp[k] = ((PdfString)obj).ToUnicodeString(); - } - else { - ArrayList opar = ((PdfArray)obj).ArrayList; - choicesExp[k] = ((PdfString)opar[0]).ToUnicodeString(); - choices[k] = ((PdfString)opar[1]).ToUnicodeString(); - } - } - if ((flags & PdfFormField.FF_COMBO) != 0) { - for (int k = 0; k < choices.Length; ++k) { - if (text.Equals(choicesExp[k])) { - text = choices[k]; - break; - } - } - tx.Text = text; - return tx.GetAppearance(); - } - int idx = 0; - for (int k = 0; k < choicesExp.Length; ++k) { - if (text.Equals(choicesExp[k])) { - idx = k; - break; - } - } - tx.Choices = choices; - tx.ChoiceExports = choicesExp; - tx.ChoiceSelection = idx; - } - PdfAppearance app = tx.GetListAppearance(); - topFirst = tx.TopFirst; - return app; - } - - internal Color GetMKColor(PdfArray ar) { + PdfNumber nfl = merged.GetAsNumber(PdfName.FF); + if (nfl != null) + flags = nfl.IntValue; + if ((flags & PdfFormField.FF_COMBO) != 0 && opt == null) { + tx.Text = text; + return tx.GetAppearance(); + } + if (opt != null) { + String[] choices = new String[opt.Size]; + String[] choicesExp = new String[opt.Size]; + for (int k = 0; k < opt.Size; ++k) { + PdfObject obj = opt[k]; + if (obj.IsString()) { + choices[k] = choicesExp[k] = ((PdfString)obj).ToUnicodeString(); + } + else { + PdfArray a = (PdfArray) obj; + choicesExp[k] = a.GetAsString(0).ToUnicodeString(); + choices[k] = a.GetAsString(1).ToUnicodeString(); + } + } + if ((flags & PdfFormField.FF_COMBO) != 0) { + for (int k = 0; k < choices.Length; ++k) { + if (text.Equals(choicesExp[k])) { + text = choices[k]; + break; + } + } + tx.Text = text; + return tx.GetAppearance(); + } + int idx = 0; + for (int k = 0; k < choicesExp.Length; ++k) { + if (text.Equals(choicesExp[k])) { + idx = k; + break; + } + } + tx.Choices = choices; + tx.ChoiceExports = choicesExp; + tx.ChoiceSelection = idx; + } + PdfAppearance app = tx.GetListAppearance(); + topFirst = tx.TopFirst; + return app; + } + + internal Color GetMKColor(PdfArray ar) { if (ar == null) return null; - ArrayList cc = ar.ArrayList; - switch (cc.Count) { + switch (ar.Size) { case 1: - return new GrayColor(((PdfNumber)cc[0]).FloatValue); + return new GrayColor(ar.GetAsNumber(0).FloatValue); case 3: - return new Color(ExtendedColor.Normalize(((PdfNumber)cc[0]).FloatValue), ExtendedColor.Normalize(((PdfNumber)cc[1]).FloatValue), ExtendedColor.Normalize(((PdfNumber)cc[2]).FloatValue)); + return new Color(ExtendedColor.Normalize(ar.GetAsNumber(0).FloatValue), ExtendedColor.Normalize(ar.GetAsNumber(1).FloatValue), ExtendedColor.Normalize(ar.GetAsNumber(2).FloatValue)); case 4: - return new CMYKColor(((PdfNumber)cc[0]).FloatValue, ((PdfNumber)cc[1]).FloatValue, ((PdfNumber)cc[2]).FloatValue, ((PdfNumber)cc[3]).FloatValue); + return new CMYKColor(ar.GetAsNumber(0).FloatValue, ar.GetAsNumber(1).FloatValue, ar.GetAsNumber(2).FloatValue, ar.GetAsNumber(3).FloatValue); default: return null; - } - } - - /** Gets the field value. - * @param name the fully qualified field name - * @return the field value - */ - public String GetField(String name) { - if (xfa.XfaPresent) { - name = xfa.FindFieldName(name, this); - if (name == null) - return null; - name = XfaForm.Xml2Som.GetShortName(name); - return XfaForm.GetNodeText(xfa.FindDatasetsNode(name)); - } - Item item = (Item)fields[name]; - if (item == null) - return null; - lastWasString = false; - PdfObject v = PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.V)); + } + } + + /** Gets the field value. + * @param name the fully qualified field name + * @return the field value + */ + public String GetField(String name) { + if (xfa.XfaPresent) { + name = xfa.FindFieldName(name, this); + if (name == null) + return null; + name = XfaForm.Xml2Som.GetShortName(name); + return XfaForm.GetNodeText(xfa.FindDatasetsNode(name)); + } + Item item = (Item)fields[name]; + if (item == null) + return null; + lastWasString = false; + PdfDictionary mergedDict = item.GetMerged( 0 ); + + // Jose A. Rodriguez posted a fix to the mailing list (May 11, 2009) + // explaining that the value can also be a stream value + // the fix was made against an old iText version. Bruno adapted it. + PdfObject v = PdfReader.GetPdfObject(mergedDict.Get(PdfName.V)); if (v == null) return ""; - PdfName type = (PdfName)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.FT)); - if (PdfName.BTN.Equals(type)) { - PdfNumber ff = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.FF)); - int flags = 0; - if (ff != null) - flags = ff.IntValue; - if ((flags & PdfFormField.FF_PUSHBUTTON) != 0) + if (v is PRStream) { + try { + byte[] valBytes = PdfReader.GetStreamBytes((PRStream)v); + return PdfEncodings.ConvertToString(valBytes, BaseFont.WINANSI); + } catch { return ""; - String value = ""; - if (v.IsName()) - value = PdfName.DecodeName(v.ToString()); - else if (v.IsString()) - value = ((PdfString)v).ToUnicodeString(); - PdfObject opts = PdfReader.GetPdfObject(((PdfDictionary)item.values[0]).Get(PdfName.OPT)); - if (opts != null && opts.IsArray()) { - ArrayList list = ((PdfArray)opts).ArrayList; - int idx = 0; - try { - idx = int.Parse(value); - PdfString ps = (PdfString)list[idx]; - value = ps.ToUnicodeString(); - lastWasString = true; - } - catch { - } } - return value; } - if (v.IsString()) { + + PdfName type = mergedDict.GetAsName(PdfName.FT); + if (PdfName.BTN.Equals(type)) { + PdfNumber ff = mergedDict.GetAsNumber(PdfName.FF); + int flags = 0; + if (ff != null) + flags = ff.IntValue; + if ((flags & PdfFormField.FF_PUSHBUTTON) != 0) + return ""; + String value = ""; + if (v is PdfName) + value = PdfName.DecodeName(v.ToString()); + else if (v is PdfString) + value = ((PdfString)v).ToUnicodeString(); + PdfArray opts = item.GetValue(0).GetAsArray(PdfName.OPT); + if (opts != null) { + int idx = 0; + try { + idx = int.Parse(value); + PdfString ps = opts.GetAsString(idx); + value = ps.ToUnicodeString(); + lastWasString = true; + } + catch { + } + } + return value; + } + if (v is PdfString) { lastWasString = true; return ((PdfString)v).ToUnicodeString(); - } - return PdfName.DecodeName(v.ToString()); - } - + } else if (v is PdfName) { + return PdfName.DecodeName(v.ToString()); + } else + return ""; + } + /** * Gets the field values of a Choice field. * @param name the fully qualified field name @@ -799,7 +835,7 @@ namespace iTextSharp.text.pdf { Item item = (Item)fields[name]; if (item == null) return ret; - PdfArray values = (PdfArray)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.I)); + PdfArray values = item.GetMerged(0).GetAsArray(PdfName.I); if (values == null) return ret; ret = new String[values.Size]; @@ -809,1268 +845,1573 @@ namespace iTextSharp.text.pdf { ret[idx++] = options[n.IntValue]; } return ret; - } - - /** - * Sets a field property. Valid property names are: - *

- *

- * @param field the field name - * @param name the property name - * @param value the property value - * @param inst an array of int indexing into AcroField.Item.merged elements to process. - * Set to null to process all - * @return true if the property exists, false otherwise - */ - public bool SetFieldProperty(String field, String name, Object value, int[] inst) { - if (writer == null) - throw new Exception("This AcroFields instance is read-only."); - Item item = (Item)fields[field]; - if (item == null) - return false; - InstHit hit = new InstHit(inst); + } + + /** + * Sets a field property. Valid property names are: + *

+ *

+ * @param field the field name + * @param name the property name + * @param value the property value + * @param inst an array of int indexing into AcroField.Item.merged elements to process. + * Set to null to process all + * @return true if the property exists, false otherwise + */ + public bool SetFieldProperty(String field, String name, Object value, int[] inst) { + if (writer == null) + throw new Exception("This AcroFields instance is read-only."); + Item item = (Item)fields[field]; + if (item == null) + return false; + InstHit hit = new InstHit(inst); + PdfDictionary merged; + PdfString da; if (Util.EqualsIgnoreCase(name, "textfont")) { - for (int k = 0; k < item.merged.Count; ++k) { + for (int k = 0; k < item.Size; ++k) { if (hit.IsHit(k)) { - PdfString da = (PdfString)PdfReader.GetPdfObject(((PdfDictionary)item.merged[k]).Get(PdfName.DA)); - PdfDictionary dr = (PdfDictionary)PdfReader.GetPdfObject(((PdfDictionary)item.merged[k]).Get(PdfName.DR)); - if (da != null && dr != null) { - Object[] dao = SplitDAelements(da.ToUnicodeString()); - PdfAppearance cb = new PdfAppearance(); - if (dao[DA_FONT] != null) { - BaseFont bf = (BaseFont)value; - PdfName psn = (PdfName)PdfAppearance.stdFieldFontNames[bf.PostscriptFontName]; - if (psn == null) { - psn = new PdfName(bf.PostscriptFontName); - } - PdfDictionary fonts = (PdfDictionary)PdfReader.GetPdfObject(dr.Get(PdfName.FONT)); - if (fonts == null) { - fonts = new PdfDictionary(); - dr.Put(PdfName.FONT, fonts); - } - PdfIndirectReference fref = (PdfIndirectReference)fonts.Get(psn); - PdfDictionary top = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM)); - MarkUsed(top); - dr = (PdfDictionary)PdfReader.GetPdfObject(top.Get(PdfName.DR)); - if (dr == null) { - dr = new PdfDictionary(); - top.Put(PdfName.DR, dr); - } - MarkUsed(dr); - PdfDictionary fontsTop = (PdfDictionary)PdfReader.GetPdfObject(dr.Get(PdfName.FONT)); - if (fontsTop == null) { - fontsTop = new PdfDictionary(); - dr.Put(PdfName.FONT, fontsTop); - } - MarkUsed(fontsTop); - PdfIndirectReference frefTop = (PdfIndirectReference)fontsTop.Get(psn); - if (frefTop != null) { - if (fref == null) - fonts.Put(psn, frefTop); - } - else if (fref == null) { - FontDetails fd; - if (bf.FontType == BaseFont.FONT_TYPE_DOCUMENT) { - fd = new FontDetails(null, ((DocumentFont)bf).IndirectReference, bf); - } - else { - bf.Subset = false; - fd = writer.AddSimple(bf); - localFonts[psn.ToString().Substring(1)] = bf; - } - fontsTop.Put(psn, fd.IndirectReference); - fonts.Put(psn, fd.IndirectReference); - } - ByteBuffer buf = cb.InternalBuffer; - buf.Append(psn.GetBytes()).Append(' ').Append((float)dao[DA_SIZE]).Append(" Tf "); - if (dao[DA_COLOR] != null) - cb.SetColorFill((Color)dao[DA_COLOR]); - PdfString s = new PdfString(cb.ToString()); - ((PdfDictionary)item.merged[k]).Put(PdfName.DA, s); - ((PdfDictionary)item.widgets[k]).Put(PdfName.DA, s); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } + merged = item.GetMerged( k ); + da = merged.GetAsString(PdfName.DA); + PdfDictionary dr = merged.GetAsDict(PdfName.DR); + if (da != null && dr != null) { + Object[] dao = SplitDAelements(da.ToUnicodeString()); + PdfAppearance cb = new PdfAppearance(); + if (dao[DA_FONT] != null) { + BaseFont bf = (BaseFont)value; + PdfName psn = (PdfName)PdfAppearance.stdFieldFontNames[bf.PostscriptFontName]; + if (psn == null) { + psn = new PdfName(bf.PostscriptFontName); + } + PdfDictionary fonts = dr.GetAsDict(PdfName.FONT); + if (fonts == null) { + fonts = new PdfDictionary(); + dr.Put(PdfName.FONT, fonts); + } + PdfIndirectReference fref = (PdfIndirectReference)fonts.Get(psn); + PdfDictionary top = reader.Catalog.GetAsDict(PdfName.ACROFORM); + MarkUsed(top); + dr = top.GetAsDict(PdfName.DR); + if (dr == null) { + dr = new PdfDictionary(); + top.Put(PdfName.DR, dr); + } + MarkUsed(dr); + PdfDictionary fontsTop = dr.GetAsDict(PdfName.FONT); + if (fontsTop == null) { + fontsTop = new PdfDictionary(); + dr.Put(PdfName.FONT, fontsTop); + } + MarkUsed(fontsTop); + PdfIndirectReference frefTop = (PdfIndirectReference)fontsTop.Get(psn); + if (frefTop != null) { + if (fref == null) + fonts.Put(psn, frefTop); + } + else if (fref == null) { + FontDetails fd; + if (bf.FontType == BaseFont.FONT_TYPE_DOCUMENT) { + fd = new FontDetails(null, ((DocumentFont)bf).IndirectReference, bf); + } + else { + bf.Subset = false; + fd = writer.AddSimple(bf); + localFonts[psn.ToString().Substring(1)] = bf; + } + fontsTop.Put(psn, fd.IndirectReference); + fonts.Put(psn, fd.IndirectReference); + } + ByteBuffer buf = cb.InternalBuffer; + buf.Append(psn.GetBytes()).Append(' ').Append((float)dao[DA_SIZE]).Append(" Tf "); + if (dao[DA_COLOR] != null) + cb.SetColorFill((Color)dao[DA_COLOR]); + PdfString s = new PdfString(cb.ToString()); + item.GetMerged(k).Put(PdfName.DA, s); + item.GetWidget(k).Put(PdfName.DA, s); + MarkUsed(item.GetWidget(k)); + } + } + } + } + } + else if (Util.EqualsIgnoreCase(name, "textcolor")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + merged = item.GetMerged( k ); + da = merged.GetAsString(PdfName.DA); + if (da != null) { + Object[] dao = SplitDAelements(da.ToUnicodeString()); + PdfAppearance cb = new PdfAppearance(); + if (dao[DA_FONT] != null) { + ByteBuffer buf = cb.InternalBuffer; + buf.Append(new PdfName((String)dao[DA_FONT]).GetBytes()).Append(' ').Append((float)dao[DA_SIZE]).Append(" Tf "); + cb.SetColorFill((Color)value); + PdfString s = new PdfString(cb.ToString()); + item.GetMerged(k).Put(PdfName.DA, s); + item.GetWidget(k).Put(PdfName.DA, s); + MarkUsed(item.GetWidget(k)); + } + } + } + } + } + else if (Util.EqualsIgnoreCase(name, "textsize")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + merged = item.GetMerged( k ); + da = merged.GetAsString(PdfName.DA); + if (da != null) { + Object[] dao = SplitDAelements(da.ToUnicodeString()); + PdfAppearance cb = new PdfAppearance(); + if (dao[DA_FONT] != null) { + ByteBuffer buf = cb.InternalBuffer; + buf.Append(new PdfName((String)dao[DA_FONT]).GetBytes()).Append(' ').Append((float)value).Append(" Tf "); + if (dao[DA_COLOR] != null) + cb.SetColorFill((Color)dao[DA_COLOR]); + PdfString s = new PdfString(cb.ToString()); + item.GetMerged(k).Put(PdfName.DA, s); + item.GetWidget(k).Put(PdfName.DA, s); + MarkUsed(item.GetWidget(k)); + } + } + } + } + } + else if (Util.EqualsIgnoreCase(name, "bgcolor") || Util.EqualsIgnoreCase(name, "bordercolor")) { + PdfName dname = (Util.EqualsIgnoreCase(name, "bgcolor") ? PdfName.BG : PdfName.BC); + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + merged = item.GetMerged( k ); + PdfDictionary mk = merged.GetAsDict(PdfName.MK); + if (mk == null) { + if (value == null) + return true; + mk = new PdfDictionary(); + item.GetMerged(k).Put(PdfName.MK, mk); + item.GetWidget(k).Put(PdfName.MK, mk); + MarkUsed(item.GetWidget(k)); + } else { + MarkUsed( mk ); + } + if (value == null) + mk.Remove(dname); + else + mk.Put(dname, PdfFormField.GetMKColor((Color)value)); + } + } + } + else + return false; + return true; + } + + /** + * Sets a field property. Valid property names are: + *

+ *

+ * @param field the field name + * @param name the property name + * @param value the property value + * @param inst an array of int indexing into AcroField.Item.merged elements to process. + * Set to null to process all + * @return true if the property exists, false otherwise + */ + public bool SetFieldProperty(String field, String name, int value, int[] inst) { + if (writer == null) + throw new Exception("This AcroFields instance is read-only."); + Item item = (Item)fields[field]; + if (item == null) + return false; + InstHit hit = new InstHit(inst); + if (Util.EqualsIgnoreCase(name, "flags")) { + PdfNumber num = new PdfNumber(value); + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + item.GetMerged(k).Put(PdfName.F, num); + item.GetWidget(k).Put(PdfName.F, num); + MarkUsed(item.GetWidget(k)); + } + } + } + else if (Util.EqualsIgnoreCase(name, "setflags")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + PdfNumber num = item.GetWidget(k).GetAsNumber(PdfName.F); + int val = 0; + if (num != null) + val = num.IntValue; + num = new PdfNumber(val | value); + item.GetMerged(k).Put(PdfName.F, num); + item.GetWidget(k).Put(PdfName.F, num); + MarkUsed(item.GetWidget(k)); + } + } + } + else if (Util.EqualsIgnoreCase(name, "clrflags")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + PdfDictionary widget = item.GetWidget( k ); + PdfNumber num = widget.GetAsNumber(PdfName.F); + int val = 0; + if (num != null) + val = num.IntValue; + num = new PdfNumber(val & (~value)); + item.GetMerged(k).Put(PdfName.F, num); + widget.Put(PdfName.F, num); + MarkUsed(widget); + } + } + } + else if (Util.EqualsIgnoreCase(name, "fflags")) { + PdfNumber num = new PdfNumber(value); + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + item.GetMerged(k).Put(PdfName.FF, num); + item.GetValue(k).Put(PdfName.FF, num); + MarkUsed(item.GetValue(k)); + } + } + } + else if (Util.EqualsIgnoreCase(name, "setfflags")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + PdfDictionary valDict = item.GetValue( k ); + PdfNumber num = valDict.GetAsNumber( PdfName.FF ); + int val = 0; + if (num != null) + val = num.IntValue; + num = new PdfNumber(val | value); + item.GetMerged(k).Put(PdfName.FF, num); + valDict.Put(PdfName.FF, num); + MarkUsed(valDict); + } + } + } + else if (Util.EqualsIgnoreCase(name, "clrfflags")) { + for (int k = 0; k < item.Size; ++k) { + if (hit.IsHit(k)) { + PdfDictionary valDict = item.GetValue( k ); + PdfNumber num = valDict.GetAsNumber(PdfName.FF); + int val = 0; + if (num != null) + val = num.IntValue; + num = new PdfNumber(val & (~value)); + item.GetMerged(k).Put(PdfName.FF, num); + valDict.Put(PdfName.FF, num); + MarkUsed(valDict); + } + } + } + else + return false; + return true; + } + + /** + * Merges an XML data structure into this form. + * @param n the top node of the data structure + * @throws java.io.IOException on error + * @throws com.lowagie.text.DocumentException o error + */ + public void MergeXfaData(XmlNode n) { + XfaForm.Xml2SomDatasets data = new XfaForm.Xml2SomDatasets(n); + foreach (String name in data.Order) { + String text = XfaForm.GetNodeText((XmlNode)data.Name2Node[name]); + SetField(name, text); + } + } + + /** Sets the fields by FDF merging. + * @param fdf the FDF form + * @throws IOException on error + * @throws DocumentException on error + */ + public void SetFields(FdfReader fdf) { + Hashtable fd = fdf.Fields; + foreach (string f in fd.Keys) { + String v = fdf.GetFieldValue(f); + if (v != null) + SetField(f, v); + } + } + + /** Sets the fields by XFDF merging. + * @param xfdf the XFDF form + * @throws IOException on error + * @throws DocumentException on error + */ + + public void SetFields(XfdfReader xfdf) { + Hashtable fd = xfdf.Fields; + foreach (string f in fd.Keys) { + String v = xfdf.GetFieldValue(f); + if (v != null) + SetField(f, v); + ArrayList l = xfdf.GetListValues(f); + if (l != null) { + string[] ar = (string[])l.ToArray(typeof(string[])); + SetListSelection(v, ar); } - } - else if (Util.EqualsIgnoreCase(name, "textcolor")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfString da = (PdfString)PdfReader.GetPdfObject(((PdfDictionary)item.merged[k]).Get(PdfName.DA)); - if (da != null) { - Object[] dao = SplitDAelements(da.ToUnicodeString()); - PdfAppearance cb = new PdfAppearance(); - if (dao[DA_FONT] != null) { - ByteBuffer buf = cb.InternalBuffer; - buf.Append(new PdfName((String)dao[DA_FONT]).GetBytes()).Append(' ').Append((float)dao[DA_SIZE]).Append(" Tf "); - cb.SetColorFill((Color)value); - PdfString s = new PdfString(cb.ToString()); - ((PdfDictionary)item.merged[k]).Put(PdfName.DA, s); - ((PdfDictionary)item.widgets[k]).Put(PdfName.DA, s); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } - } - } - else if (Util.EqualsIgnoreCase(name, "textsize")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfString da = (PdfString)PdfReader.GetPdfObject(((PdfDictionary)item.merged[k]).Get(PdfName.DA)); - if (da != null) { - Object[] dao = SplitDAelements(da.ToUnicodeString()); - PdfAppearance cb = new PdfAppearance(); - if (dao[DA_FONT] != null) { - ByteBuffer buf = cb.InternalBuffer; - buf.Append(new PdfName((String)dao[DA_FONT]).GetBytes()).Append(' ').Append((float)value).Append(" Tf "); - if (dao[DA_COLOR] != null) - cb.SetColorFill((Color)dao[DA_COLOR]); - PdfString s = new PdfString(cb.ToString()); - ((PdfDictionary)item.merged[k]).Put(PdfName.DA, s); - ((PdfDictionary)item.widgets[k]).Put(PdfName.DA, s); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } - } - } - else if (Util.EqualsIgnoreCase(name, "bgcolor") || Util.EqualsIgnoreCase(name, "bordercolor")) { - PdfName dname = (Util.EqualsIgnoreCase(name, "bgcolor") ? PdfName.BG : PdfName.BC); - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfObject obj = PdfReader.GetPdfObject(((PdfDictionary)item.merged[k]).Get(PdfName.MK)); - MarkUsed(obj); - PdfDictionary mk = (PdfDictionary)obj; - if (mk == null) { - if (value == null) - return true; - mk = new PdfDictionary(); - ((PdfDictionary)item.merged[k]).Put(PdfName.MK, mk); - ((PdfDictionary)item.widgets[k]).Put(PdfName.MK, mk); - MarkUsed((PdfDictionary)item.widgets[k]); - } - if (value == null) - mk.Remove(dname); - else - mk.Put(dname, PdfFormField.GetMKColor((Color)value)); - } - } - } - else - return false; - return true; - } - - /** - * Sets a field property. Valid property names are: - *

- *

- * @param field the field name - * @param name the property name - * @param value the property value - * @param inst an array of int indexing into AcroField.Item.merged elements to process. - * Set to null to process all - * @return true if the property exists, false otherwise - */ - public bool SetFieldProperty(String field, String name, int value, int[] inst) { - if (writer == null) - throw new Exception("This AcroFields instance is read-only."); - Item item = (Item)fields[field]; - if (item == null) - return false; - InstHit hit = new InstHit(inst); - if (Util.EqualsIgnoreCase(name, "flags")) { - PdfNumber num = new PdfNumber(value); - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - ((PdfDictionary)item.merged[k]).Put(PdfName.F, num); - ((PdfDictionary)item.widgets[k]).Put(PdfName.F, num); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } - else if (Util.EqualsIgnoreCase(name, "setflags")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfNumber num = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.widgets[k]).Get(PdfName.F)); - int val = 0; - if (num != null) - val = num.IntValue; - num = new PdfNumber(val | value); - ((PdfDictionary)item.merged[k]).Put(PdfName.F, num); - ((PdfDictionary)item.widgets[k]).Put(PdfName.F, num); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } - else if (Util.EqualsIgnoreCase(name, "clrflags")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfNumber num = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.widgets[k]).Get(PdfName.F)); - int val = 0; - if (num != null) - val = num.IntValue; - num = new PdfNumber(val & (~value)); - ((PdfDictionary)item.merged[k]).Put(PdfName.F, num); - ((PdfDictionary)item.widgets[k]).Put(PdfName.F, num); - MarkUsed((PdfDictionary)item.widgets[k]); - } - } - } - else if (Util.EqualsIgnoreCase(name, "fflags")) { - PdfNumber num = new PdfNumber(value); - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - ((PdfDictionary)item.merged[k]).Put(PdfName.FF, num); - ((PdfDictionary)item.values[k]).Put(PdfName.FF, num); - MarkUsed((PdfDictionary)item.values[k]); - } - } - } - else if (Util.EqualsIgnoreCase(name, "setfflags")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfNumber num = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.values[k]).Get(PdfName.FF)); - int val = 0; - if (num != null) - val = num.IntValue; - num = new PdfNumber(val | value); - ((PdfDictionary)item.merged[k]).Put(PdfName.FF, num); - ((PdfDictionary)item.values[k]).Put(PdfName.FF, num); - MarkUsed((PdfDictionary)item.values[k]); - } - } - } - else if (Util.EqualsIgnoreCase(name, "clrfflags")) { - for (int k = 0; k < item.merged.Count; ++k) { - if (hit.IsHit(k)) { - PdfNumber num = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.values[k]).Get(PdfName.FF)); - int val = 0; - if (num != null) - val = num.IntValue; - num = new PdfNumber(val & (~value)); - ((PdfDictionary)item.merged[k]).Put(PdfName.FF, num); - ((PdfDictionary)item.values[k]).Put(PdfName.FF, num); - MarkUsed((PdfDictionary)item.values[k]); - } - } - } - else - return false; - return true; - } - - /** - * Merges an XML data structure into this form. - * @param n the top node of the data structure - * @throws java.io.IOException on error - * @throws com.lowagie.text.DocumentException o error - */ - public void MergeXfaData(XmlNode n) { - XfaForm.Xml2SomDatasets data = new XfaForm.Xml2SomDatasets(n); - foreach (String name in data.Order) { - String text = XfaForm.GetNodeText((XmlNode)data.Name2Node[name]); - SetField(name, text); - } - } - - /** Sets the fields by FDF merging. - * @param fdf the FDF form - * @throws IOException on error - * @throws DocumentException on error - */ - public void SetFields(FdfReader fdf) { - Hashtable fd = fdf.Fields; - foreach (string f in fd.Keys) { - String v = fdf.GetFieldValue(f); - if (v != null) - SetField(f, v); - } - } - - /** Sets the fields by XFDF merging. - * @param xfdf the XFDF form - * @throws IOException on error - * @throws DocumentException on error - */ - - public void SetFields(XfdfReader xfdf) { - Hashtable fd = xfdf.Fields; - foreach (string f in fd.Keys) { - String v = xfdf.GetFieldValue(f); - if (v != null) - SetField(f, v); - } - } - - /** - * Regenerates the field appearance. - * This is usefull when you change a field property, but not its value, - * for instance form.SetFieldProperty("f", "bgcolor", Color.BLUE, null); - * This won't have any effect, unless you use RegenerateField("f") after changing - * the property. - * - * @param name the fully qualified field name or the partial name in the case of XFA forms - * @throws IOException on error - * @throws DocumentException on error - * @return true if the field was found and changed, - * false otherwise - */ - public bool RegenerateField(String name) { - String value = GetField(name); - return SetField(name, value, value); - } - - /** Sets the field value. - * @param name the fully qualified field name or the partial name in the case of XFA forms - * @param value the field value - * @throws IOException on error - * @throws DocumentException on error - * @return true if the field was found and changed, - * false otherwise - */ - public bool SetField(String name, String value) { - return SetField(name, value, null); - } - - /** Sets the field value and the display string. The display string - * is used to build the appearance in the cases where the value - * is modified by Acrobat with JavaScript and the algorithm is - * known. - * @param name the fully qualified field name or the partial name in the case of XFA forms - * @param value the field value - * @param display the string that is used for the appearance. If null - * the value parameter will be used - * @return true if the field was found and changed, - * false otherwise - * @throws IOException on error - * @throws DocumentException on error - */ - public bool SetField(String name, String value, String display) { - if (writer == null) - throw new DocumentException("This AcroFields instance is read-only."); - if (xfa.XfaPresent) { - name = xfa.FindFieldName(name, this); - if (name == null) - return false; - String shortName = XfaForm.Xml2Som.GetShortName(name); - XmlNode xn = xfa.FindDatasetsNode(shortName); - if (xn == null) { - xn = xfa.DatasetsSom.InsertNode(xfa.DatasetsNode, shortName); - } - xfa.SetNodeText(xn, value); - } - Item item = (Item)fields[name]; - if (item == null) - return false; - PdfName type = (PdfName)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.FT)); + } + } + + /** + * Regenerates the field appearance. + * This is usefull when you change a field property, but not its value, + * for instance form.SetFieldProperty("f", "bgcolor", Color.BLUE, null); + * This won't have any effect, unless you use RegenerateField("f") after changing + * the property. + * + * @param name the fully qualified field name or the partial name in the case of XFA forms + * @throws IOException on error + * @throws DocumentException on error + * @return true if the field was found and changed, + * false otherwise + */ + public bool RegenerateField(String name) { + String value = GetField(name); + return SetField(name, value, value); + } + + /** Sets the field value. + * @param name the fully qualified field name or the partial name in the case of XFA forms + * @param value the field value + * @throws IOException on error + * @throws DocumentException on error + * @return true if the field was found and changed, + * false otherwise + */ + public bool SetField(String name, String value) { + return SetField(name, value, null); + } + + /** Sets the field value and the display string. The display string + * is used to build the appearance in the cases where the value + * is modified by Acrobat with JavaScript and the algorithm is + * known. + * @param name the fully qualified field name or the partial name in the case of XFA forms + * @param value the field value + * @param display the string that is used for the appearance. If null + * the value parameter will be used + * @return true if the field was found and changed, + * false otherwise + * @throws IOException on error + * @throws DocumentException on error + */ + public bool SetField(String name, String value, String display) { + if (writer == null) + throw new DocumentException("This AcroFields instance is read-only."); + if (xfa.XfaPresent) { + name = xfa.FindFieldName(name, this); + if (name == null) + return false; + String shortName = XfaForm.Xml2Som.GetShortName(name); + XmlNode xn = xfa.FindDatasetsNode(shortName); + if (xn == null) { + xn = xfa.DatasetsSom.InsertNode(xfa.DatasetsNode, shortName); + } + xfa.SetNodeText(xn, value); + } + Item item = (Item)fields[name]; + if (item == null) + return false; + PdfDictionary merged = item.GetMerged( 0 ); + PdfName type = merged.GetAsName(PdfName.FT); if (PdfName.TX.Equals(type)) { - PdfNumber maxLen = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.MAXLEN)); - int len = 0; - if (maxLen != null) - len = maxLen.IntValue; - if (len > 0) - value = value.Substring(0, Math.Min(len, value.Length)); - } - if (display == null) - display = value; - if (PdfName.TX.Equals(type) || PdfName.CH.Equals(type)) { - PdfString v = new PdfString(value, PdfObject.TEXT_UNICODE); - for (int idx = 0; idx < item.values.Count; ++idx) { - PdfDictionary valueDic = (PdfDictionary)item.values[idx]; - valueDic.Put(PdfName.V, v); - valueDic.Remove(PdfName.I); - MarkUsed(valueDic); - PdfDictionary merged = (PdfDictionary)item.merged[idx]; - merged.Remove(PdfName.I); - merged.Put(PdfName.V, v); - PdfDictionary widget = (PdfDictionary)item.widgets[idx]; - if (generateAppearances) { - PdfAppearance app = GetAppearance(merged, display, name); - if (PdfName.CH.Equals(type)) { - PdfNumber n = new PdfNumber(topFirst); - widget.Put(PdfName.TI, n); - merged.Put(PdfName.TI, n); - } - PdfDictionary appDic = (PdfDictionary)PdfReader.GetPdfObject(widget.Get(PdfName.AP)); - if (appDic == null) { - appDic = new PdfDictionary(); - widget.Put(PdfName.AP, appDic); - merged.Put(PdfName.AP, appDic); - } - appDic.Put(PdfName.N, app.IndirectReference); - writer.ReleaseTemplate(app); - } - else { - widget.Remove(PdfName.AP); - merged.Remove(PdfName.AP); - } - MarkUsed(widget); - } - return true; - } - else if (PdfName.BTN.Equals(type)) { - PdfNumber ff = (PdfNumber)PdfReader.GetPdfObject(((PdfDictionary)item.merged[0]).Get(PdfName.FF)); - int flags = 0; - if (ff != null) - flags = ff.IntValue; - if ((flags & PdfFormField.FF_PUSHBUTTON) != 0) { - //we'll assume that the value is an image in base64 - Image img; - try { - img = Image.GetInstance(Convert.FromBase64String(value)); - } - catch { - return false; - } - PushbuttonField pb = GetNewPushbuttonFromField(name); - pb.Image = img; - ReplacePushbuttonField(name, pb.Field); - return true; - } - PdfName v = new PdfName(value); - ArrayList lopt = new ArrayList(); - PdfObject opts = PdfReader.GetPdfObject(((PdfDictionary)item.values[0]).Get(PdfName.OPT)); - if (opts != null && opts.IsArray()) { - ArrayList list = ((PdfArray)opts).ArrayList; - for (int k = 0; k < list.Count; ++k) { - PdfObject vv = PdfReader.GetPdfObject((PdfObject)list[k]); - if (vv != null && vv.IsString()) - lopt.Add(((PdfString)vv).ToUnicodeString()); - else - lopt.Add(null); - } - } - int vidx = lopt.IndexOf(value); - PdfName valt = null; - PdfName vt; - if (vidx >= 0) { - vt = valt = new PdfName(vidx.ToString()); - } - else - vt = v; - for (int idx = 0; idx < item.values.Count; ++idx) { - PdfDictionary merged = (PdfDictionary)item.merged[idx]; - PdfDictionary widget = (PdfDictionary)item.widgets[idx]; - MarkUsed((PdfDictionary)item.values[idx]); - if (valt != null) { - PdfString ps = new PdfString(value, PdfObject.TEXT_UNICODE); - ((PdfDictionary)item.values[idx]).Put(PdfName.V, ps); - merged.Put(PdfName.V, ps); - } - else { - ((PdfDictionary)item.values[idx]).Put(PdfName.V, v); - merged.Put(PdfName.V, v); - } - MarkUsed(widget); - if (IsInAP(widget, vt)) { - merged.Put(PdfName.AS, vt); - widget.Put(PdfName.AS, vt); - } - else { - merged.Put(PdfName.AS, PdfName.Off_); - widget.Put(PdfName.AS, PdfName.Off_); - } - } - return true; - } - return false; - } - - internal bool IsInAP(PdfDictionary dic, PdfName check) { - PdfDictionary appDic = (PdfDictionary)PdfReader.GetPdfObject(dic.Get(PdfName.AP)); - if (appDic == null) - return false; - PdfDictionary NDic = (PdfDictionary)PdfReader.GetPdfObject(appDic.Get(PdfName.N)); - return (NDic != null && NDic.Get(check) != null); - } - - /** Gets all the fields. The fields are keyed by the fully qualified field name and - * the value is an instance of AcroFields.Item. - * @return all the fields - */ - public Hashtable Fields { - get { - return fields; - } - } - + PdfNumber maxLen = merged.GetAsNumber(PdfName.MAXLEN); + int len = 0; + if (maxLen != null) + len = maxLen.IntValue; + if (len > 0) + value = value.Substring(0, Math.Min(len, value.Length)); + } + if (display == null) + display = value; + if (PdfName.TX.Equals(type) || PdfName.CH.Equals(type)) { + PdfString v = new PdfString(value, PdfObject.TEXT_UNICODE); + for (int idx = 0; idx < item.Size; ++idx) { + PdfDictionary valueDic = item.GetValue(idx); + valueDic.Put(PdfName.V, v); + valueDic.Remove(PdfName.I); + MarkUsed(valueDic); + merged = item.GetMerged(idx); + merged.Remove(PdfName.I); + merged.Put(PdfName.V, v); + PdfDictionary widget = item.GetWidget(idx); + if (generateAppearances) { + PdfAppearance app = GetAppearance(merged, display, name); + if (PdfName.CH.Equals(type)) { + PdfNumber n = new PdfNumber(topFirst); + widget.Put(PdfName.TI, n); + merged.Put(PdfName.TI, n); + } + PdfDictionary appDic = widget.GetAsDict(PdfName.AP); + if (appDic == null) { + appDic = new PdfDictionary(); + widget.Put(PdfName.AP, appDic); + merged.Put(PdfName.AP, appDic); + } + appDic.Put(PdfName.N, app.IndirectReference); + writer.ReleaseTemplate(app); + } + else { + widget.Remove(PdfName.AP); + merged.Remove(PdfName.AP); + } + MarkUsed(widget); + } + return true; + } + else if (PdfName.BTN.Equals(type)) { + PdfNumber ff = item.GetMerged(0).GetAsNumber(PdfName.FF); + int flags = 0; + if (ff != null) + flags = ff.IntValue; + if ((flags & PdfFormField.FF_PUSHBUTTON) != 0) { + //we'll assume that the value is an image in base64 + Image img; + try { + img = Image.GetInstance(Convert.FromBase64String(value)); + } + catch { + return false; + } + PushbuttonField pb = GetNewPushbuttonFromField(name); + pb.Image = img; + ReplacePushbuttonField(name, pb.Field); + return true; + } + PdfName v = new PdfName(value); + ArrayList lopt = new ArrayList(); + PdfArray opts = item.GetValue(0).GetAsArray(PdfName.OPT); + if (opts != null) { + for (int k = 0; k < opts.Size; ++k) { + PdfString valStr = opts.GetAsString(k); + if (valStr != null) + lopt.Add(valStr.ToUnicodeString()); + else + lopt.Add(null); + } + } + int vidx = lopt.IndexOf(value); + PdfName valt = null; + PdfName vt; + if (vidx >= 0) { + vt = valt = new PdfName(vidx.ToString()); + } + else + vt = v; + for (int idx = 0; idx < item.Size; ++idx) { + merged = item.GetMerged(idx); + PdfDictionary widget = item.GetWidget(idx); + PdfDictionary valDict = item.GetValue(idx); + MarkUsed(item.GetValue(idx)); + if (valt != null) { + PdfString ps = new PdfString(value, PdfObject.TEXT_UNICODE); + valDict.Put(PdfName.V, ps); + merged.Put(PdfName.V, ps); + } + else { + valDict.Put(PdfName.V, v); + merged.Put(PdfName.V, v); + } + MarkUsed(widget); + if (IsInAP(widget, vt)) { + merged.Put(PdfName.AS, vt); + widget.Put(PdfName.AS, vt); + } + else { + merged.Put(PdfName.AS, PdfName.Off_); + widget.Put(PdfName.AS, PdfName.Off_); + } + } + return true; + } + return false; + } + /** - * Gets the field structure. - * @param name the name of the field - * @return the field structure or null if the field - * does not exist - */ - public Item GetFieldItem(String name) { - if (xfa.XfaPresent) { - name = xfa.FindFieldName(name, this); - if (name == null) - return null; - } - return (Item)fields[name]; - } - - /** - * Gets the long XFA translated name. - * @param name the name of the field - * @return the long field name - */ - public String GetTranslatedFieldName(String name) { - if (xfa.XfaPresent) { - String namex = xfa.FindFieldName(name, this); - if (namex != null) - name = namex; - } - return name; - } - - /** - * Gets the field box positions in the document. The return is an array of float - * multiple of 5. For each of this groups the values are: [page, llx, lly, urx, - * ury]. The coordinates have the page rotation in consideration. - * @param name the field name - * @return the positions or null if field does not exist - */ - public float[] GetFieldPositions(String name) { + * Sets different values in a list selection. + * No appearance is generated yet; nor does the code check if multiple select is allowed. + * + * @param name the name of the field + * @param value an array with values that need to be selected + * @return true only if the field value was changed + * @since 2.1.4 + */ + public bool SetListSelection(String name, String[] value) { Item item = GetFieldItem(name); if (item == null) - return null; - float[] ret = new float[item.page.Count * 5]; - int ptr = 0; - for (int k = 0; k < item.page.Count; ++k) { - try { - PdfDictionary wd = (PdfDictionary)item.widgets[k]; - PdfArray rect = (PdfArray)wd.Get(PdfName.RECT); - if (rect == null) - continue; - Rectangle r = PdfReader.GetNormalizedRectangle(rect); - int page = (int)item.page[k]; - int rotation = reader.GetPageRotation(page); - ret[ptr++] = page; - if (rotation != 0) { - Rectangle pageSize = reader.GetPageSize(page); - switch (rotation) { - case 270: - r = new Rectangle( - pageSize.Top - r.Bottom, - r.Left, - pageSize.Top - r.Top, - r.Right); - break; - case 180: - r = new Rectangle( - pageSize.Right - r.Left, - pageSize.Top - r.Bottom, - pageSize.Right - r.Right, - pageSize.Top - r.Top); - break; - case 90: - r = new Rectangle( - r.Bottom, - pageSize.Right - r.Left, - r.Top, - pageSize.Right - r.Right); - break; - } - r.Normalize(); + return false; + PdfName type = item.GetMerged(0).GetAsName(PdfName.FT); + if (!PdfName.CH.Equals(type)) { + return false; + } + String[] options = GetListOptionExport(name); + PdfArray array = new PdfArray(); + for (int i = 0; i < value.Length; i++) { + for (int j = 0; j < options.Length; j++) { + if (options[j].Equals(value[i])) { + array.Add(new PdfNumber(j)); } - ret[ptr++] = r.Left; - ret[ptr++] = r.Bottom; - ret[ptr++] = r.Right; - ret[ptr++] = r.Top; - } - catch { - // empty on purpose } } - if (ptr < ret.Length) { - float[] ret2 = new float[ptr]; - System.Array.Copy(ret, 0, ret2, 0, ptr); - return ret2; - } - return ret; - } - - private int RemoveRefFromArray(PdfArray array, PdfObject refo) { - ArrayList ar = array.ArrayList; + item.WriteToAll(PdfName.I, array, Item.WRITE_MERGED | Item.WRITE_VALUE); + item.WriteToAll(PdfName.V, null, Item.WRITE_MERGED | Item.WRITE_VALUE); + item.WriteToAll(PdfName.AP, null, Item.WRITE_MERGED | Item.WRITE_WIDGET); + item.MarkUsed( this, Item.WRITE_VALUE | Item.WRITE_WIDGET ); + return true; + } + + internal bool IsInAP(PdfDictionary dic, PdfName check) { + PdfDictionary appDic = dic.GetAsDict(PdfName.AP); + if (appDic == null) + return false; + PdfDictionary NDic = appDic.GetAsDict(PdfName.N); + return (NDic != null && NDic.Get(check) != null); + } + + /** Gets all the fields. The fields are keyed by the fully qualified field name and + * the value is an instance of AcroFields.Item. + * @return all the fields + */ + public Hashtable Fields { + get { + return fields; + } + } + + /** + * Gets the field structure. + * @param name the name of the field + * @return the field structure or null if the field + * does not exist + */ + public Item GetFieldItem(String name) { + if (xfa.XfaPresent) { + name = xfa.FindFieldName(name, this); + if (name == null) + return null; + } + return (Item)fields[name]; + } + + /** + * Gets the long XFA translated name. + * @param name the name of the field + * @return the long field name + */ + public String GetTranslatedFieldName(String name) { + if (xfa.XfaPresent) { + String namex = xfa.FindFieldName(name, this); + if (namex != null) + name = namex; + } + return name; + } + + /** + * Gets the field box positions in the document. The return is an array of float + * multiple of 5. For each of this groups the values are: [page, llx, lly, urx, + * ury]. The coordinates have the page rotation in consideration. + * @param name the field name + * @return the positions or null if field does not exist + */ + public float[] GetFieldPositions(String name) { + Item item = GetFieldItem(name); + if (item == null) + return null; + float[] ret = new float[item.Size * 5]; + int ptr = 0; + for (int k = 0; k < item.Size; ++k) { + try { + PdfDictionary wd = item.GetWidget(k); + PdfArray rect = wd.GetAsArray(PdfName.RECT); + if (rect == null) + continue; + Rectangle r = PdfReader.GetNormalizedRectangle(rect); + int page = item.GetPage(k); + int rotation = reader.GetPageRotation(page); + ret[ptr++] = page; + if (rotation != 0) { + Rectangle pageSize = reader.GetPageSize(page); + switch (rotation) { + case 270: + r = new Rectangle( + pageSize.Top - r.Bottom, + r.Left, + pageSize.Top - r.Top, + r.Right); + break; + case 180: + r = new Rectangle( + pageSize.Right - r.Left, + pageSize.Top - r.Bottom, + pageSize.Right - r.Right, + pageSize.Top - r.Top); + break; + case 90: + r = new Rectangle( + r.Bottom, + pageSize.Right - r.Left, + r.Top, + pageSize.Right - r.Right); + break; + } + r.Normalize(); + } + ret[ptr++] = r.Left; + ret[ptr++] = r.Bottom; + ret[ptr++] = r.Right; + ret[ptr++] = r.Top; + } + catch { + // empty on purpose + } + } + if (ptr < ret.Length) { + float[] ret2 = new float[ptr]; + System.Array.Copy(ret, 0, ret2, 0, ptr); + return ret2; + } + return ret; + } + + private int RemoveRefFromArray(PdfArray array, PdfObject refo) { if (refo == null || !refo.IsIndirect()) - return ar.Count; + return array.Size; PdfIndirectReference refi = (PdfIndirectReference)refo; - for (int j = 0; j < ar.Count; ++j) { - PdfObject obj = (PdfObject)ar[j]; + for (int j = 0; j < array.Size; ++j) { + PdfObject obj = array[j]; if (!obj.IsIndirect()) continue; if (((PdfIndirectReference)obj).Number == refi.Number) - ar.RemoveAt(j--); + array.Remove(j--); } - return ar.Count; - } - - /** - * Removes all the fields from page. - * @param page the page to remove the fields from - * @return true if any field was removed, false otherwise - */ - public bool RemoveFieldsFromPage(int page) { - if (page < 1) - return false; - String[] names = new String[fields.Count]; - fields.Keys.CopyTo(names, 0); - bool found = false; - for (int k = 0; k < names.Length; ++k) { - bool fr = RemoveField(names[k], page); - found = (found || fr); - } - return found; - } - - /** - * Removes a field from the document. If page equals -1 all the fields with this - * name are removed from the document otherwise only the fields in - * that particular page are removed. - * @param name the field name - * @param page the page to remove the field from or -1 to remove it from all the pages - * @return true if the field exists, false otherwise - */ - public bool RemoveField(String name, int page) { - Item item = GetFieldItem(name); - if (item == null) - return false; - PdfDictionary acroForm = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM), reader.Catalog); - - if (acroForm == null) - return false; - PdfArray arrayf = (PdfArray)PdfReader.GetPdfObject(acroForm.Get(PdfName.FIELDS), acroForm); + return array.Size; + } + + /** + * Removes all the fields from page. + * @param page the page to remove the fields from + * @return true if any field was removed, false otherwise + */ + public bool RemoveFieldsFromPage(int page) { + if (page < 1) + return false; + String[] names = new String[fields.Count]; + fields.Keys.CopyTo(names, 0); + bool found = false; + for (int k = 0; k < names.Length; ++k) { + bool fr = RemoveField(names[k], page); + found = (found || fr); + } + return found; + } + + /** + * Removes a field from the document. If page equals -1 all the fields with this + * name are removed from the document otherwise only the fields in + * that particular page are removed. + * @param name the field name + * @param page the page to remove the field from or -1 to remove it from all the pages + * @return true if the field exists, false otherwise + */ + public bool RemoveField(String name, int page) { + Item item = GetFieldItem(name); + if (item == null) + return false; + PdfDictionary acroForm = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM), reader.Catalog); + + if (acroForm == null) + return false; + PdfArray arrayf = acroForm.GetAsArray(PdfName.FIELDS); if (arrayf == null) return false; - for (int k = 0; k < item.widget_refs.Count; ++k) { - int pageV = (int)item.page[k]; + for (int k = 0; k < item.Size; ++k) { + int pageV = item.GetPage(k); if (page != -1 && page != pageV) continue; - PdfIndirectReference refi = (PdfIndirectReference)item.widget_refs[k]; - PdfDictionary wd = (PdfDictionary)PdfReader.GetPdfObject(refi); + PdfIndirectReference refi = item.GetWidgetRef(k); + PdfDictionary wd = item.GetWidget( k ); PdfDictionary pageDic = reader.GetPageN(pageV); - PdfArray annots = (PdfArray)PdfReader.GetPdfObject(pageDic.Get(PdfName.ANNOTS), pageDic); - if (annots != null) { - if (RemoveRefFromArray(annots, refi) == 0) { - pageDic.Remove(PdfName.ANNOTS); - MarkUsed(pageDic); + PdfArray annots = pageDic.GetAsArray(PdfName.ANNOTS); + if (annots != null) { + if (RemoveRefFromArray(annots, refi) == 0) { + pageDic.Remove(PdfName.ANNOTS); + MarkUsed(pageDic); + } + else + MarkUsed(annots); + } + PdfReader.KillIndirect(refi); + PdfIndirectReference kid = refi; + while ((refi = wd.GetAsIndirectObject(PdfName.PARENT)) != null) { + wd = wd.GetAsDict( PdfName.PARENT ); + PdfArray kids = wd.GetAsArray(PdfName.KIDS); + if (RemoveRefFromArray(kids, kid) != 0) + break; + kid = refi; + PdfReader.KillIndirect(refi); + } + if (refi == null) { + RemoveRefFromArray(arrayf, kid); + MarkUsed(arrayf); + } + if (page != -1) { + item.Remove( k ); + --k; + } + } + if (page == -1 || item.Size == 0) + fields.Remove(name); + return true; + } + + /** + * Removes a field from the document. + * @param name the field name + * @return true if the field exists, false otherwise + */ + public bool RemoveField(String name) { + return RemoveField(name, -1); + } + + /** Sets the option to generate appearances. Not generating apperances + * will speed-up form filling but the results can be + * unexpected in Acrobat. Don't use it unless your environment is well + * controlled. The default is true. + * @param generateAppearances the option to generate appearances + */ + public bool GenerateAppearances { + set { + generateAppearances = value; + PdfDictionary top = reader.Catalog.GetAsDict(PdfName.ACROFORM); + if (generateAppearances) + top.Remove(PdfName.NEEDAPPEARANCES); + else + top.Put(PdfName.NEEDAPPEARANCES, PdfBoolean.PDFTRUE); + } + get { + return generateAppearances; + } + } + + /** The field representations for retrieval and modification. */ + public class Item { + + /** + * writeToAll constant. + * + * @since 2.1.5 + */ + public const int WRITE_MERGED = 1; + + /** + * writeToAll and markUsed constant. + * + * @since 2.1.5 + */ + public const int WRITE_WIDGET = 2; + + /** + * writeToAll and markUsed constant. + * + * @since 2.1.5 + */ + public const int WRITE_VALUE = 4; + + /** + * This function writes the given key/value pair to all the instances + * of merged, widget, and/or value, depending on the writeFlags setting + * + * @since 2.1.5 + * + * @param key you'll never guess what this is for. + * @param value if value is null, the key will be removed + * @param writeFlags ORed together WRITE_* flags + */ + public void WriteToAll(PdfName key, PdfObject value, int writeFlags) { + int i; + PdfDictionary curDict = null; + if ((writeFlags & WRITE_MERGED) != 0) { + for (i = 0; i < merged.Count; ++i) { + curDict = GetMerged(i); + curDict.Put(key, value); } - else - MarkUsed(annots); } - PdfReader.KillIndirect(refi); - PdfIndirectReference kid = refi; - while ((refi = (PdfIndirectReference)wd.Get(PdfName.PARENT)) != null) { - wd = (PdfDictionary)PdfReader.GetPdfObject(refi); - PdfArray kids = (PdfArray)PdfReader.GetPdfObject(wd.Get(PdfName.KIDS)); - if (RemoveRefFromArray(kids, kid) != 0) - break; - kid = refi; - PdfReader.KillIndirect(refi); + if ((writeFlags & WRITE_WIDGET) != 0) { + for (i = 0; i < widgets.Count; ++i) { + curDict = GetWidget(i); + curDict.Put(key, value); + } } - if (refi == null) { - RemoveRefFromArray(arrayf, kid); - MarkUsed(arrayf); - } - if (page != -1) { - item.merged.RemoveAt(k); - item.page.RemoveAt(k); - item.values.RemoveAt(k); - item.widget_refs.RemoveAt(k); - item.widgets.RemoveAt(k); - --k; + if ((writeFlags & WRITE_VALUE) != 0) { + for (i = 0; i < values.Count; ++i) { + curDict = GetValue(i); + curDict.Put(key, value); + } } } - if (page == -1 || item.merged.Count == 0) - fields.Remove(name); - return true; - } - - /** - * Removes a field from the document. - * @param name the field name - * @return true if the field exists, false otherwise - */ - public bool RemoveField(String name) { - return RemoveField(name, -1); - } - - /** Sets the option to generate appearances. Not generating apperances - * will speed-up form filling but the results can be - * unexpected in Acrobat. Don't use it unless your environment is well - * controlled. The default is true. - * @param generateAppearances the option to generate appearances - */ - public bool GenerateAppearances { - set { - generateAppearances = value; - PdfDictionary top = (PdfDictionary)PdfReader.GetPdfObject(reader.Catalog.Get(PdfName.ACROFORM)); - if (generateAppearances) - top.Remove(PdfName.NEEDAPPEARANCES); - else - top.Put(PdfName.NEEDAPPEARANCES, PdfBoolean.PDFTRUE); + + /** + * Mark all the item dictionaries used matching the given flags + * + * @since 2.1.5 + * @param writeFlags WRITE_MERGED is ignored + */ + public void MarkUsed( AcroFields parentFields, int writeFlags ) { + if ((writeFlags & WRITE_VALUE) != 0) { + for (int i = 0; i < Size; ++i) { + parentFields.MarkUsed( GetValue( i ) ); + } + } + if ((writeFlags & WRITE_WIDGET) != 0) { + for (int i = 0; i < Size; ++i) { + parentFields.MarkUsed(GetWidget(i)); + } + } } - get { - return generateAppearances; - } - } - - /** The field representations for retrieval and modification. */ - public class Item { - /** An array of PdfDictionary where the value tag /V + + /** + * An array of PdfDictionary where the value tag /V * is present. - */ + * + * @deprecated (will remove 'public' in the future) + */ public ArrayList values = new ArrayList(); - /** An array of PdfDictionary with the widgets. - */ + + /** + * An array of PdfDictionary with the widgets. + * + * @deprecated (will remove 'public' in the future) + */ public ArrayList widgets = new ArrayList(); - /** An array of PdfDictionary with the widget references. + + /** + * An array of PdfDictionary with the widget references. + * + * @deprecated (will remove 'public' in the future) */ public ArrayList widget_refs = new ArrayList(); - /** An array of PdfDictionary with all the field - * and widget tags merged. - */ - public ArrayList merged = new ArrayList(); - /** An array of Integer with the page numbers where - * the widgets are displayed. - */ - public ArrayList page = new ArrayList(); - /** An array of Integer with the tab order of the field in the page. - */ - public ArrayList tabOrder = new ArrayList(); - } - - private class InstHit { - IntHashtable hits; - public InstHit(int[] inst) { - if (inst == null) - return; - hits = new IntHashtable(); - for (int k = 0; k < inst.Length; ++k) - hits[inst[k]] = 1; - } - public bool IsHit(int n) { - if (hits == null) - return true; - return hits.ContainsKey(n); - } - } + /** + * An array of PdfDictionary with all the field + * and widget tags merged. + * + * @deprecated (will remove 'public' in the future) + */ + public ArrayList merged = new ArrayList(); + + /** + * An array of Integer with the page numbers where + * the widgets are displayed. + * + * @deprecated (will remove 'public' in the future) + */ + public ArrayList page = new ArrayList(); + /** + * An array of Integer with the tab order of the field in the page. + * + * @deprecated (will remove 'public' in the future) + */ + public ArrayList tabOrder = new ArrayList(); - private void FindSignatureNames() { - if (sigNames != null) - return; - sigNames = new Hashtable(); - ArrayList sorter = new ArrayList(); - foreach (DictionaryEntry entry in fields) { - Item item = (Item)entry.Value; - PdfDictionary merged = (PdfDictionary)item.merged[0]; - if (!PdfName.SIG.Equals(merged.Get(PdfName.FT))) - continue; - PdfObject vo = PdfReader.GetPdfObject(merged.Get(PdfName.V)); - if (vo == null || vo.Type != PdfObject.DICTIONARY) - continue; - PdfDictionary v = (PdfDictionary)vo; - PdfObject contents = v.Get(PdfName.CONTENTS); - if (contents == null || contents.Type != PdfObject.STRING) - continue; - PdfObject ro = v.Get(PdfName.BYTERANGE); - if (ro == null || ro.Type != PdfObject.ARRAY) - continue; - ArrayList ra = ((PdfArray)ro).ArrayList; - if (ra.Count < 2) - continue; - int length = ((PdfNumber)ra[ra.Count - 1]).IntValue + ((PdfNumber)ra[ra.Count - 2]).IntValue; - sorter.Add(new Object[]{entry.Key, new int[]{length, 0}}); - } - sorter.Sort(new AcroFields.ISorterComparator()); - if (sorter.Count > 0) { - if (((int[])((Object[])sorter[sorter.Count - 1])[1])[0] == reader.FileLength) - totalRevisions = sorter.Count; - else - totalRevisions = sorter.Count + 1; - for (int k = 0; k < sorter.Count; ++k) { - Object[] objs = (Object[])sorter[k]; - String name = (String)objs[0]; - int[] p = (int[])objs[1]; - p[1] = k + 1; - sigNames[name] = p; + /** + * Preferred method of determining the number of instances + * of a given field. + * + * @since 2.1.5 + * @return number of instances + */ + public int Size { + get { + return values.Count; } } - } - /** - * Gets the field names that have signatures and are signed. - * @return the field names that have signatures and are signed - */ - public ArrayList GetSignatureNames() { - FindSignatureNames(); - return new ArrayList(sigNames.Keys); - } - - /** - * Gets the field names that have blank signatures. - * @return the field names that have blank signatures - */ - public ArrayList GetBlankSignatureNames() { - FindSignatureNames(); - ArrayList sigs = new ArrayList(); - foreach (DictionaryEntry entry in fields) { - Item item = (Item)entry.Value; - PdfDictionary merged = (PdfDictionary)item.merged[0]; + /** + * Remove the given instance from this item. It is possible to + * remove all instances using this function. + * + * @since 2.1.5 + * @param killIdx + */ + internal void Remove(int killIdx) { + values.RemoveAt(killIdx); + widgets.RemoveAt(killIdx); + widget_refs.RemoveAt(killIdx); + merged.RemoveAt(killIdx); + page.RemoveAt(killIdx); + tabOrder.RemoveAt(killIdx); + } + + /** + * Retrieve the value dictionary of the given instance + * + * @since 2.1.5 + * @param idx instance index + * @return dictionary storing this instance's value. It may be shared across instances. + */ + public PdfDictionary GetValue(int idx) { + return (PdfDictionary) values[idx]; + } + + /** + * Add a value dict to this Item + * + * @since 2.1.5 + * @param value new value dictionary + */ + internal void AddValue(PdfDictionary value) { + values.Add(value); + } + + /** + * Retrieve the widget dictionary of the given instance + * + * @since 2.1.5 + * @param idx instance index + * @return The dictionary found in the appropriate page's Annot array. + */ + public PdfDictionary GetWidget(int idx) { + return (PdfDictionary) widgets[idx]; + } + + /** + * Add a widget dict to this Item + * + * @since 2.1.5 + * @param widget + */ + internal void AddWidget(PdfDictionary widget) { + widgets.Add(widget); + } + + /** + * Retrieve the reference to the given instance + * + * @since 2.1.5 + * @param idx instance index + * @return reference to the given field instance + */ + public PdfIndirectReference GetWidgetRef(int idx) { + return (PdfIndirectReference) widget_refs[idx]; + } + + /** + * Add a widget ref to this Item + * + * @since 2.1.5 + * @param widgRef + */ + internal void AddWidgetRef(PdfIndirectReference widgRef) { + widget_refs.Add(widgRef); + } + + /** + * Retrieve the merged dictionary for the given instance. The merged + * dictionary contains all the keys present in parent fields, though they + * may have been overwritten (or modified?) by children. + * Example: a merged radio field dict will contain /V + * + * @since 2.1.5 + * @param idx instance index + * @return the merged dictionary for the given instance + */ + public PdfDictionary GetMerged(int idx) { + return (PdfDictionary) merged[idx]; + } + + /** + * Adds a merged dictionary to this Item. + * + * @since 2.1.5 + * @param mergeDict + */ + internal void AddMerged(PdfDictionary mergeDict) { + merged.Add(mergeDict); + } + + /** + * Retrieve the page number of the given instance + * + * @since 2.1.5 + * @param idx + * @return remember, pages are "1-indexed", not "0-indexed" like field instances. + */ + public int GetPage(int idx) { + return (int) page[idx]; + } + + /** + * Adds a page to the current Item. + * + * @since 2.1.5 + * @param pg + */ + internal void AddPage(int pg) { + page.Add(pg); + } + + /** + * forces a page value into the Item. + * + * @since 2.1.5 + * @param idx + */ + internal void ForcePage(int idx, int pg) { + page[idx] = pg; + } + + /** + * Gets the tabOrder. + * + * @since 2.1.5 + * @param idx + * @return tab index of the given field instance + */ + public int GetTabOrder(int idx) { + return (int) tabOrder[idx]; + } + + /** + * Adds a tab order value to this Item. + * + * @since 2.1.5 + * @param order + */ + internal void AddTabOrder(int order) { + tabOrder.Add(order); + } + } + + private class InstHit { + IntHashtable hits; + public InstHit(int[] inst) { + if (inst == null) + return; + hits = new IntHashtable(); + for (int k = 0; k < inst.Length; ++k) + hits[inst[k]] = 1; + } + + public bool IsHit(int n) { + if (hits == null) + return true; + return hits.ContainsKey(n); + } + } + + private void FindSignatureNames() { + if (sigNames != null) + return; + sigNames = new Hashtable(); + ArrayList sorter = new ArrayList(); + foreach (DictionaryEntry entry in fields) { + Item item = (Item)entry.Value; + PdfDictionary merged = item.GetMerged(0); if (!PdfName.SIG.Equals(merged.Get(PdfName.FT))) continue; - if (sigNames.ContainsKey(entry.Key)) + PdfDictionary v = merged.GetAsDict(PdfName.V); + if (v == null) continue; - sigs.Add(entry.Key); - } - return sigs; - } - - /** - * Gets the signature dictionary, the one keyed by /V. - * @param name the field name - * @return the signature dictionary keyed by /V or null if the field is not - * a signature - */ - public PdfDictionary GetSignatureDictionary(String name) { - FindSignatureNames(); - name = GetTranslatedFieldName(name); - if (!sigNames.ContainsKey(name)) - return null; - Item item = (Item)fields[name]; - PdfDictionary merged = (PdfDictionary)item.merged[0]; - return (PdfDictionary)PdfReader.GetPdfObject(merged.Get(PdfName.V)); - } - - /** - * Checks is the signature covers the entire document or just part of it. - * @param name the signature field name - * @return true if the signature covers the entire document, - * false otherwise - */ - public bool SignatureCoversWholeDocument(String name) { - FindSignatureNames(); - name = GetTranslatedFieldName(name); - if (!sigNames.ContainsKey(name)) - return false; - return ((int[])sigNames[name])[0] == reader.FileLength; - } - - /** - * Verifies a signature. An example usage is: - *

- *

-        * KeyStore kall = PdfPKCS7.LoadCacertsKeyStore();
-        * PdfReader reader = new PdfReader("my_signed_doc.pdf");
-        * AcroFields af = reader.GetAcroFields();
-        * ArrayList names = af.GetSignatureNames();
-        * for (int k = 0; k < names.Size(); ++k) {
-        *    String name = (String)names.Get(k);
-        *    System.out.Println("Signature name: " + name);
-        *    System.out.Println("Signature covers whole document: " + af.SignatureCoversWholeDocument(name));
-        *    PdfPKCS7 pk = af.VerifySignature(name);
-        *    Calendar cal = pk.GetSignDate();
-        *    Certificate pkc[] = pk.GetCertificates();
-        *    System.out.Println("Subject: " + PdfPKCS7.GetSubjectFields(pk.GetSigningCertificate()));
-        *    System.out.Println("Document modified: " + !pk.Verify());
-        *    Object fails[] = PdfPKCS7.VerifyCertificates(pkc, kall, null, cal);
-        *    if (fails == null)
-        *        System.out.Println("Certificates verified against the KeyStore");
-        *    else
-        *        System.out.Println("Certificate failed: " + fails[1]);
-        * }
-        * 
- * @param name the signature field name - * @return a PdfPKCS7 class to continue the verification - */ - public PdfPKCS7 VerifySignature(String name) { - PdfDictionary v = GetSignatureDictionary(name); - if (v == null) - return null; - PdfName sub = (PdfName)PdfReader.GetPdfObject(v.Get(PdfName.SUBFILTER)); - PdfString contents = (PdfString)PdfReader.GetPdfObject(v.Get(PdfName.CONTENTS)); - PdfPKCS7 pk = null; - if (sub.Equals(PdfName.ADBE_X509_RSA_SHA1)) { - PdfString cert = (PdfString)PdfReader.GetPdfObject(v.Get(PdfName.CERT)); - pk = new PdfPKCS7(contents.GetOriginalBytes(), cert.GetBytes()); - } - else - pk = new PdfPKCS7(contents.GetOriginalBytes()); - UpdateByteRange(pk, v); - PdfString str = (PdfString)PdfReader.GetPdfObject(v.Get(PdfName.M)); - if (str != null) - pk.SignDate = PdfDate.Decode(str.ToString()); - PdfObject obj = PdfReader.GetPdfObject(v.Get(PdfName.NAME)); - if (obj != null) { - if (obj.IsString()) - pk.SignName = ((PdfString)obj).ToUnicodeString(); - else if(obj.IsName()) - pk.SignName = PdfName.DecodeName(obj.ToString()); - } - str = (PdfString)PdfReader.GetPdfObject(v.Get(PdfName.REASON)); - if (str != null) - pk.Reason = str.ToUnicodeString(); - str = (PdfString)PdfReader.GetPdfObject(v.Get(PdfName.LOCATION)); - if (str != null) - pk.Location = str.ToUnicodeString(); - return pk; - } - - private void UpdateByteRange(PdfPKCS7 pkcs7, PdfDictionary v) { - PdfArray b = (PdfArray)PdfReader.GetPdfObject(v.Get(PdfName.BYTERANGE)); + PdfString contents = v.GetAsString(PdfName.CONTENTS); + if (contents == null) + continue; + PdfArray ro = v.GetAsArray(PdfName.BYTERANGE); + if (ro == null) + continue; + int rangeSize = ro.Size; + if (rangeSize < 2) + continue; + int length = ro.GetAsNumber(rangeSize - 1).IntValue + ro.GetAsNumber(rangeSize - 2).IntValue; + sorter.Add(new Object[]{entry.Key, new int[]{length, 0}}); + } + sorter.Sort(new AcroFields.ISorterComparator()); + if (sorter.Count > 0) { + if (((int[])((Object[])sorter[sorter.Count - 1])[1])[0] == reader.FileLength) + totalRevisions = sorter.Count; + else + totalRevisions = sorter.Count + 1; + for (int k = 0; k < sorter.Count; ++k) { + Object[] objs = (Object[])sorter[k]; + String name = (String)objs[0]; + int[] p = (int[])objs[1]; + p[1] = k + 1; + sigNames[name] = p; + } + } + } + + /** + * Gets the field names that have signatures and are signed. + * @return the field names that have signatures and are signed + */ + public ArrayList GetSignatureNames() { + FindSignatureNames(); + return new ArrayList(sigNames.Keys); + } + + /** + * Gets the field names that have blank signatures. + * @return the field names that have blank signatures + */ + public ArrayList GetBlankSignatureNames() { + FindSignatureNames(); + ArrayList sigs = new ArrayList(); + foreach (DictionaryEntry entry in fields) { + Item item = (Item)entry.Value; + PdfDictionary merged = item.GetMerged(0); + if (!PdfName.SIG.Equals(merged.GetAsName(PdfName.FT))) + continue; + if (sigNames.ContainsKey(entry.Key)) + continue; + sigs.Add(entry.Key); + } + return sigs; + } + + /** + * Gets the signature dictionary, the one keyed by /V. + * @param name the field name + * @return the signature dictionary keyed by /V or null if the field is not + * a signature + */ + public PdfDictionary GetSignatureDictionary(String name) { + FindSignatureNames(); + name = GetTranslatedFieldName(name); + if (!sigNames.ContainsKey(name)) + return null; + Item item = (Item)fields[name]; + PdfDictionary merged = item.GetMerged(0); + return merged.GetAsDict(PdfName.V); + } + + /** + * Checks is the signature covers the entire document or just part of it. + * @param name the signature field name + * @return true if the signature covers the entire document, + * false otherwise + */ + public bool SignatureCoversWholeDocument(String name) { + FindSignatureNames(); + name = GetTranslatedFieldName(name); + if (!sigNames.ContainsKey(name)) + return false; + return ((int[])sigNames[name])[0] == reader.FileLength; + } + + /** + * Verifies a signature. An example usage is: + *

+ *

+        * KeyStore kall = PdfPKCS7.LoadCacertsKeyStore();
+        * PdfReader reader = new PdfReader("my_signed_doc.pdf");
+        * AcroFields af = reader.GetAcroFields();
+        * ArrayList names = af.GetSignatureNames();
+        * for (int k = 0; k < names.Size(); ++k) {
+        *    String name = (String)names.Get(k);
+        *    System.out.Println("Signature name: " + name);
+        *    System.out.Println("Signature covers whole document: " + af.SignatureCoversWholeDocument(name));
+        *    PdfPKCS7 pk = af.VerifySignature(name);
+        *    Calendar cal = pk.GetSignDate();
+        *    Certificate pkc[] = pk.GetCertificates();
+        *    System.out.Println("Subject: " + PdfPKCS7.GetSubjectFields(pk.GetSigningCertificate()));
+        *    System.out.Println("Document modified: " + !pk.Verify());
+        *    Object fails[] = PdfPKCS7.VerifyCertificates(pkc, kall, null, cal);
+        *    if (fails == null)
+        *        System.out.Println("Certificates verified against the KeyStore");
+        *    else
+        *        System.out.Println("Certificate failed: " + fails[1]);
+        * }
+        * 
+ * @param name the signature field name + * @return a PdfPKCS7 class to continue the verification + */ + public PdfPKCS7 VerifySignature(String name) { + PdfDictionary v = GetSignatureDictionary(name); + if (v == null) + return null; + PdfName sub = v.GetAsName(PdfName.SUBFILTER); + PdfString contents = v.GetAsString(PdfName.CONTENTS); + PdfPKCS7 pk = null; + if (sub.Equals(PdfName.ADBE_X509_RSA_SHA1)) { + PdfString cert = v.GetAsString(PdfName.CERT); + pk = new PdfPKCS7(contents.GetOriginalBytes(), cert.GetBytes()); + } + else + pk = new PdfPKCS7(contents.GetOriginalBytes()); + UpdateByteRange(pk, v); + PdfString str = v.GetAsString(PdfName.M); + if (str != null) + pk.SignDate = PdfDate.Decode(str.ToString()); + PdfObject obj = PdfReader.GetPdfObject(v.Get(PdfName.NAME)); + if (obj != null) { + if (obj.IsString()) + pk.SignName = ((PdfString)obj).ToUnicodeString(); + else if(obj.IsName()) + pk.SignName = PdfName.DecodeName(obj.ToString()); + } + str = v.GetAsString(PdfName.REASON); + if (str != null) + pk.Reason = str.ToUnicodeString(); + str = v.GetAsString(PdfName.LOCATION); + if (str != null) + pk.Location = str.ToUnicodeString(); + return pk; + } + + private void UpdateByteRange(PdfPKCS7 pkcs7, PdfDictionary v) { + PdfArray b = v.GetAsArray(PdfName.BYTERANGE); RandomAccessFileOrArray rf = reader.SafeFile; try { rf.ReOpen(); byte[] buf = new byte[8192]; - ArrayList ar = b.ArrayList; - for (int k = 0; k < ar.Count; ++k) { - int start = ((PdfNumber)ar[k]).IntValue; - int length = ((PdfNumber)ar[++k]).IntValue; - rf.Seek(start); - while (length > 0) { - int rd = rf.Read(buf, 0, Math.Min(length, buf.Length)); - if (rd <= 0) - break; - length -= rd; - pkcs7.Update(buf, 0, rd); - } - } - } - finally { - try{rf.Close();}catch{} - } - } - - /** - * Gets the total number of revisions this document has. - * @return the total number of revisions - */ - public int TotalRevisions { - get { - FindSignatureNames(); - return this.totalRevisions; - } - } - - /** - * Gets this field revision. - * @param field the signature field name - * @return the revision or zero if it's not a signature field - */ - public int GetRevision(String field) { - FindSignatureNames(); - field = GetTranslatedFieldName(field); - if (!sigNames.ContainsKey(field)) - return 0; - return ((int[])sigNames[field])[1]; - } - - /** - * Extracts a revision from the document. - * @param field the signature field name - * @return an Stream covering the revision. Returns null if - * it's not a signature field - * @throws IOException on error - */ - public Stream ExtractRevision(String field) { - FindSignatureNames(); - field = GetTranslatedFieldName(field); - if (!sigNames.ContainsKey(field)) - return null; - int length = ((int[])sigNames[field])[0]; - RandomAccessFileOrArray raf = reader.SafeFile; - raf.ReOpen(); - raf.Seek(0); - return new RevisionStream(raf, length); - } - - /** - * Sets a cache for field appearances. Parsing the existing PDF to - * create a new TextField is time expensive. For those tasks that repeatedly - * fill the same PDF with different field values the use of the cache has dramatic - * speed advantages. An example usage: - *

- *

-        * String pdfFile = ...;// the pdf file used as template
-        * ArrayList xfdfFiles = ...;// the xfdf file names
-        * ArrayList pdfOutFiles = ...;// the output file names, one for each element in xpdfFiles
-        * Hashtable cache = new Hashtable();// the appearances cache
-        * PdfReader originalReader = new PdfReader(pdfFile);
-        * for (int k = 0; k < xfdfFiles.Size(); ++k) {
-        *    PdfReader reader = new PdfReader(originalReader);
-        *    XfdfReader xfdf = new XfdfReader((String)xfdfFiles.Get(k));
-        *    PdfStamper stp = new PdfStamper(reader, new FileOutputStream((String)pdfOutFiles.Get(k)));
-        *    AcroFields af = stp.GetAcroFields();
-        *    af.SetFieldCache(cache);
-        *    af.SetFields(xfdf);
-        *    stp.Close();
-        * }
-        * 
- * @param fieldCache an HasMap that will carry the cached appearances - */ - public Hashtable FieldCache { - set { - fieldCache = value; - } - get { - return fieldCache; - } - } - - private void MarkUsed(PdfObject obj) { - if (!append) - return; - ((PdfStamperImp)writer).MarkUsed(obj); - } - - /** - * Sets extra margins in text fields to better mimic the Acrobat layout. - * @param extraMarginLeft the extra marging left - * @param extraMarginTop the extra margin top - */ - public void SetExtraMargin(float extraMarginLeft, float extraMarginTop) { - this.extraMarginLeft = extraMarginLeft; - this.extraMarginTop = extraMarginTop; - } - - /** - * Adds a substitution font to the list. The fonts in this list will be used if the original - * font doesn't contain the needed glyphs. - * @param font the font - */ - public void AddSubstitutionFont(BaseFont font) { - if (substitutionFonts == null) - substitutionFonts = new ArrayList(); - substitutionFonts.Add(font); - } - - private static Hashtable stdFieldFontNames = new Hashtable(); - - /** - * Holds value of property fieldCache. - */ - private Hashtable fieldCache; - - private int totalRevisions; - - static AcroFields() { - stdFieldFontNames["CoBO"] = new String[]{"Courier-BoldOblique"}; - stdFieldFontNames["CoBo"] = new String[]{"Courier-Bold"}; - stdFieldFontNames["CoOb"] = new String[]{"Courier-Oblique"}; - stdFieldFontNames["Cour"] = new String[]{"Courier"}; - stdFieldFontNames["HeBO"] = new String[]{"Helvetica-BoldOblique"}; - stdFieldFontNames["HeBo"] = new String[]{"Helvetica-Bold"}; - stdFieldFontNames["HeOb"] = new String[]{"Helvetica-Oblique"}; - stdFieldFontNames["Helv"] = new String[]{"Helvetica"}; - stdFieldFontNames["Symb"] = new String[]{"Symbol"}; - stdFieldFontNames["TiBI"] = new String[]{"Times-BoldItalic"}; - stdFieldFontNames["TiBo"] = new String[]{"Times-Bold"}; - stdFieldFontNames["TiIt"] = new String[]{"Times-Italic"}; - stdFieldFontNames["TiRo"] = new String[]{"Times-Roman"}; - stdFieldFontNames["ZaDb"] = new String[]{"ZapfDingbats"}; - stdFieldFontNames["HySm"] = new String[]{"HYSMyeongJo-Medium", "UniKS-UCS2-H"}; - stdFieldFontNames["HyGo"] = new String[]{"HYGoThic-Medium", "UniKS-UCS2-H"}; - stdFieldFontNames["KaGo"] = new String[]{"HeiseiKakuGo-W5", "UniKS-UCS2-H"}; - stdFieldFontNames["KaMi"] = new String[]{"HeiseiMin-W3", "UniJIS-UCS2-H"}; - stdFieldFontNames["MHei"] = new String[]{"MHei-Medium", "UniCNS-UCS2-H"}; - stdFieldFontNames["MSun"] = new String[]{"MSung-Light", "UniCNS-UCS2-H"}; - stdFieldFontNames["STSo"] = new String[]{"STSong-Light", "UniGB-UCS2-H"}; - } - - public class RevisionStream : Stream { - private byte[] b = new byte[1]; - private RandomAccessFileOrArray raf; - private int length; - private int rangePosition = 0; - private bool closed; - - internal RevisionStream(RandomAccessFileOrArray raf, int length) { - this.raf = raf; - this.length = length; - } - - public override int ReadByte() { - int n = Read(b, 0, 1); - if (n != 1) - return -1; - return b[0] & 0xff; - } - - public override int Read(byte[] b, int off, int len) { - if (b == null) { - throw new ArgumentNullException(); - } else if ((off < 0) || (off > b.Length) || (len < 0) || - ((off + len) > b.Length) || ((off + len) < 0)) { - throw new ArgumentOutOfRangeException(); - } else if (len == 0) { - return 0; - } - if (rangePosition >= length) { - Close(); - return -1; - } - int elen = Math.Min(len, length - rangePosition); - raf.ReadFully(b, off, elen); - rangePosition += elen; - return elen; - } - - public override void Close() { - if (!closed) { - raf.Close(); - closed = true; - } - } - - public override bool CanRead { - get { - return true; - } - } - - public override bool CanSeek { - get { - return false; - } - } - - public override bool CanWrite { - get { - return false; - } - } - - public override long Length { - get { - return 0; - } - } - - public override long Position { - get { - return 0; - } - set { - } - } - - public override void Flush() { - } - - public override long Seek(long offset, SeekOrigin origin) { - return 0; - } - - public override void SetLength(long value) { - } - - public override void Write(byte[] buffer, int offset, int count) { - } - } - - private class ISorterComparator : IComparer { - public int Compare(Object o1, Object o2) { - int n1 = ((int[])((Object[])o1)[1])[0]; - int n2 = ((int[])((Object[])o2)[1])[0]; - return n1 - n2; - } - } - - /** - * Sets a list of substitution fonts. The list is composed of BaseFont and can also be null. The fonts in this list will be used if the original - * font doesn't contain the needed glyphs. - * @param substitutionFonts the list - */ - public ArrayList SubstitutionFonts { - set { - substitutionFonts = value; - } - get { - return substitutionFonts; - } - } - - /** - * Gets the XFA form processor. - * @return the XFA form processor - */ - public XfaForm Xfa { - get { - return xfa; - } - } - - private static readonly PdfName[] buttonRemove = {PdfName.MK, PdfName.F , PdfName.FF , PdfName.Q , PdfName.BS , PdfName.BORDER}; - - /** - * Creates a new pushbutton from an existing field. If there are several pushbuttons with the same name - * only the first one is used. This pushbutton can be changed and be used to replace - * an existing one, with the same name or other name, as long is it is in the same document. To replace an existing pushbutton - * call {@link #replacePushbuttonField(String,PdfFormField)}. - * @param field the field name that should be a pushbutton - * @return a new pushbutton or null if the field is not a pushbutton - */ - public PushbuttonField GetNewPushbuttonFromField(String field) { - return GetNewPushbuttonFromField(field, 0); - } - - /** - * Creates a new pushbutton from an existing field. This pushbutton can be changed and be used to replace - * an existing one, with the same name or other name, as long is it is in the same document. To replace an existing pushbutton - * call {@link #replacePushbuttonField(String,PdfFormField,int)}. - * @param field the field name that should be a pushbutton - * @param order the field order in fields with same name - * @return a new pushbutton or null if the field is not a pushbutton - */ - public PushbuttonField GetNewPushbuttonFromField(String field, int order) { - if (GetFieldType(field) != FIELD_TYPE_PUSHBUTTON) - return null; - Item item = GetFieldItem(field); - if (order >= item.merged.Count) + for (int k = 0; k < b.Size; ++k) { + int start = b.GetAsNumber(k).IntValue; + int length = b.GetAsNumber(++k).IntValue; + rf.Seek(start); + while (length > 0) { + int rd = rf.Read(buf, 0, Math.Min(length, buf.Length)); + if (rd <= 0) + break; + length -= rd; + pkcs7.Update(buf, 0, rd); + } + } + } + finally { + try{rf.Close();}catch{} + } + } + + /** + * Gets the total number of revisions this document has. + * @return the total number of revisions + */ + public int TotalRevisions { + get { + FindSignatureNames(); + return this.totalRevisions; + } + } + + /** + * Gets this field revision. + * @param field the signature field name + * @return the revision or zero if it's not a signature field + */ + public int GetRevision(String field) { + FindSignatureNames(); + field = GetTranslatedFieldName(field); + if (!sigNames.ContainsKey(field)) + return 0; + return ((int[])sigNames[field])[1]; + } + + /** + * Extracts a revision from the document. + * @param field the signature field name + * @return an Stream covering the revision. Returns null if + * it's not a signature field + * @throws IOException on error + */ + public Stream ExtractRevision(String field) { + FindSignatureNames(); + field = GetTranslatedFieldName(field); + if (!sigNames.ContainsKey(field)) + return null; + int length = ((int[])sigNames[field])[0]; + RandomAccessFileOrArray raf = reader.SafeFile; + raf.ReOpen(); + raf.Seek(0); + return new RevisionStream(raf, length); + } + + /** + * Sets a cache for field appearances. Parsing the existing PDF to + * create a new TextField is time expensive. For those tasks that repeatedly + * fill the same PDF with different field values the use of the cache has dramatic + * speed advantages. An example usage: + *

+ *

+        * String pdfFile = ...;// the pdf file used as template
+        * ArrayList xfdfFiles = ...;// the xfdf file names
+        * ArrayList pdfOutFiles = ...;// the output file names, one for each element in xpdfFiles
+        * Hashtable cache = new Hashtable();// the appearances cache
+        * PdfReader originalReader = new PdfReader(pdfFile);
+        * for (int k = 0; k < xfdfFiles.Size(); ++k) {
+        *    PdfReader reader = new PdfReader(originalReader);
+        *    XfdfReader xfdf = new XfdfReader((String)xfdfFiles.Get(k));
+        *    PdfStamper stp = new PdfStamper(reader, new FileOutputStream((String)pdfOutFiles.Get(k)));
+        *    AcroFields af = stp.GetAcroFields();
+        *    af.SetFieldCache(cache);
+        *    af.SetFields(xfdf);
+        *    stp.Close();
+        * }
+        * 
+ * @param fieldCache an HasMap that will carry the cached appearances + */ + public Hashtable FieldCache { + set { + fieldCache = value; + } + get { + return fieldCache; + } + } + + private void MarkUsed(PdfObject obj) { + if (!append) + return; + ((PdfStamperImp)writer).MarkUsed(obj); + } + + /** + * Sets extra margins in text fields to better mimic the Acrobat layout. + * @param extraMarginLeft the extra marging left + * @param extraMarginTop the extra margin top + */ + public void SetExtraMargin(float extraMarginLeft, float extraMarginTop) { + this.extraMarginLeft = extraMarginLeft; + this.extraMarginTop = extraMarginTop; + } + + /** + * Adds a substitution font to the list. The fonts in this list will be used if the original + * font doesn't contain the needed glyphs. + * @param font the font + */ + public void AddSubstitutionFont(BaseFont font) { + if (substitutionFonts == null) + substitutionFonts = new ArrayList(); + substitutionFonts.Add(font); + } + + private static Hashtable stdFieldFontNames = new Hashtable(); + + /** + * Holds value of property fieldCache. + */ + private Hashtable fieldCache; + + private int totalRevisions; + + static AcroFields() { + stdFieldFontNames["CoBO"] = new String[]{"Courier-BoldOblique"}; + stdFieldFontNames["CoBo"] = new String[]{"Courier-Bold"}; + stdFieldFontNames["CoOb"] = new String[]{"Courier-Oblique"}; + stdFieldFontNames["Cour"] = new String[]{"Courier"}; + stdFieldFontNames["HeBO"] = new String[]{"Helvetica-BoldOblique"}; + stdFieldFontNames["HeBo"] = new String[]{"Helvetica-Bold"}; + stdFieldFontNames["HeOb"] = new String[]{"Helvetica-Oblique"}; + stdFieldFontNames["Helv"] = new String[]{"Helvetica"}; + stdFieldFontNames["Symb"] = new String[]{"Symbol"}; + stdFieldFontNames["TiBI"] = new String[]{"Times-BoldItalic"}; + stdFieldFontNames["TiBo"] = new String[]{"Times-Bold"}; + stdFieldFontNames["TiIt"] = new String[]{"Times-Italic"}; + stdFieldFontNames["TiRo"] = new String[]{"Times-Roman"}; + stdFieldFontNames["ZaDb"] = new String[]{"ZapfDingbats"}; + stdFieldFontNames["HySm"] = new String[]{"HYSMyeongJo-Medium", "UniKS-UCS2-H"}; + stdFieldFontNames["HyGo"] = new String[]{"HYGoThic-Medium", "UniKS-UCS2-H"}; + stdFieldFontNames["KaGo"] = new String[]{"HeiseiKakuGo-W5", "UniKS-UCS2-H"}; + stdFieldFontNames["KaMi"] = new String[]{"HeiseiMin-W3", "UniJIS-UCS2-H"}; + stdFieldFontNames["MHei"] = new String[]{"MHei-Medium", "UniCNS-UCS2-H"}; + stdFieldFontNames["MSun"] = new String[]{"MSung-Light", "UniCNS-UCS2-H"}; + stdFieldFontNames["STSo"] = new String[]{"STSong-Light", "UniGB-UCS2-H"}; + } + + public class RevisionStream : Stream { + private byte[] b = new byte[1]; + private RandomAccessFileOrArray raf; + private int length; + private int rangePosition = 0; + private bool closed; + + internal RevisionStream(RandomAccessFileOrArray raf, int length) { + this.raf = raf; + this.length = length; + } + + public override int ReadByte() { + int n = Read(b, 0, 1); + if (n != 1) + return -1; + return b[0] & 0xff; + } + + public override int Read(byte[] b, int off, int len) { + if (b == null) { + throw new ArgumentNullException(); + } else if ((off < 0) || (off > b.Length) || (len < 0) || + ((off + len) > b.Length) || ((off + len) < 0)) { + throw new ArgumentOutOfRangeException(); + } else if (len == 0) { + return 0; + } + if (rangePosition >= length) { + Close(); + return -1; + } + int elen = Math.Min(len, length - rangePosition); + raf.ReadFully(b, off, elen); + rangePosition += elen; + return elen; + } + + public override void Close() { + if (!closed) { + raf.Close(); + closed = true; + } + } + + public override bool CanRead { + get { + return true; + } + } + + public override bool CanSeek { + get { + return false; + } + } + + public override bool CanWrite { + get { + return false; + } + } + + public override long Length { + get { + return 0; + } + } + + public override long Position { + get { + return 0; + } + set { + } + } + + public override void Flush() { + } + + public override long Seek(long offset, SeekOrigin origin) { + return 0; + } + + public override void SetLength(long value) { + } + + public override void Write(byte[] buffer, int offset, int count) { + } + } + + private class ISorterComparator : IComparer { + public int Compare(Object o1, Object o2) { + int n1 = ((int[])((Object[])o1)[1])[0]; + int n2 = ((int[])((Object[])o2)[1])[0]; + return n1 - n2; + } + } + + /** + * Sets a list of substitution fonts. The list is composed of BaseFont and can also be null. The fonts in this list will be used if the original + * font doesn't contain the needed glyphs. + * @param substitutionFonts the list + */ + public ArrayList SubstitutionFonts { + set { + substitutionFonts = value; + } + get { + return substitutionFonts; + } + } + + /** + * Gets the XFA form processor. + * @return the XFA form processor + */ + public XfaForm Xfa { + get { + return xfa; + } + } + + private static readonly PdfName[] buttonRemove = {PdfName.MK, PdfName.F , PdfName.FF , PdfName.Q , PdfName.BS , PdfName.BORDER}; + + /** + * Creates a new pushbutton from an existing field. If there are several pushbuttons with the same name + * only the first one is used. This pushbutton can be changed and be used to replace + * an existing one, with the same name or other name, as long is it is in the same document. To replace an existing pushbutton + * call {@link #replacePushbuttonField(String,PdfFormField)}. + * @param field the field name that should be a pushbutton + * @return a new pushbutton or null if the field is not a pushbutton + */ + public PushbuttonField GetNewPushbuttonFromField(String field) { + return GetNewPushbuttonFromField(field, 0); + } + + /** + * Creates a new pushbutton from an existing field. This pushbutton can be changed and be used to replace + * an existing one, with the same name or other name, as long is it is in the same document. To replace an existing pushbutton + * call {@link #replacePushbuttonField(String,PdfFormField,int)}. + * @param field the field name that should be a pushbutton + * @param order the field order in fields with same name + * @return a new pushbutton or null if the field is not a pushbutton + */ + public PushbuttonField GetNewPushbuttonFromField(String field, int order) { + if (GetFieldType(field) != FIELD_TYPE_PUSHBUTTON) + return null; + Item item = GetFieldItem(field); + if (order >= item.Size) return null; int posi = order * 5; float[] pos = GetFieldPositions(field); Rectangle box = new Rectangle(pos[posi + 1], pos[posi + 2], pos[posi + 3], pos[posi + 4]); PushbuttonField newButton = new PushbuttonField(writer, box, null); - PdfDictionary dic = (PdfDictionary)item.merged[order]; + PdfDictionary dic = item.GetMerged(order); DecodeGenericDictionary(dic, newButton); - PdfDictionary mk = (PdfDictionary)PdfReader.GetPdfObject(dic.Get(PdfName.MK)); + PdfDictionary mk = dic.GetAsDict(PdfName.MK); if (mk != null) { - PdfString text = (PdfString)PdfReader.GetPdfObject(mk.Get(PdfName.CA)); + PdfString text = mk.GetAsString(PdfName.CA); if (text != null) newButton.Text = text.ToUnicodeString(); - PdfNumber tp = (PdfNumber)PdfReader.GetPdfObject(mk.Get(PdfName.TP)); + PdfNumber tp = mk.GetAsNumber(PdfName.TP); if (tp != null) newButton.Layout = tp.IntValue + 1; - PdfDictionary ifit = (PdfDictionary)PdfReader.GetPdfObject(mk.Get(PdfName.IF)); + PdfDictionary ifit = mk.GetAsDict(PdfName.IF); if (ifit != null) { - PdfName sw = (PdfName)PdfReader.GetPdfObject(ifit.Get(PdfName.SW)); + PdfName sw = ifit.GetAsName(PdfName.SW); if (sw != null) { int scale = PushbuttonField.SCALE_ICON_ALWAYS; if (sw.Equals(PdfName.B)) @@ -2081,76 +2422,76 @@ namespace iTextSharp.text.pdf { scale = PushbuttonField.SCALE_ICON_NEVER; newButton.ScaleIcon = scale; } - sw = (PdfName)PdfReader.GetPdfObject(ifit.Get(PdfName.S)); + sw = ifit.GetAsName(PdfName.S); if (sw != null) { if (sw.Equals(PdfName.A)) newButton.ProportionalIcon = false; } - PdfArray aj = (PdfArray)PdfReader.GetPdfObject(ifit.Get(PdfName.A)); + PdfArray aj = ifit.GetAsArray(PdfName.A); if (aj != null && aj.Size == 2) { - float left = ((PdfNumber)PdfReader.GetPdfObject((PdfObject)aj.ArrayList[0])).FloatValue; - float bottom = ((PdfNumber)PdfReader.GetPdfObject((PdfObject)aj.ArrayList[1])).FloatValue; + float left = aj.GetAsNumber(0).FloatValue; + float bottom = aj.GetAsNumber(1).FloatValue; newButton.IconHorizontalAdjustment = left; newButton.IconVerticalAdjustment = bottom; } - PdfObject fb = PdfReader.GetPdfObject(ifit.Get(PdfName.FB)); - if (fb != null && fb.ToString().Equals("true")) - newButton.IconFitToBounds = true; - } - PdfObject i = mk.Get(PdfName.I); - if (i != null && i.IsIndirect()) - newButton.IconReference = (PRIndirectReference)i; - } - return newButton; - } - - /** - * Replaces the first field with a new pushbutton. The pushbutton can be created with - * {@link #getNewPushbuttonFromField(String)} from the same document or it can be a - * generic PdfFormField of the type pushbutton. - * @param field the field name - * @param button the PdfFormField representing the pushbutton - * @return true if the field was replaced, false if the field - * was not a pushbutton - */ - public bool ReplacePushbuttonField(String field, PdfFormField button) { - return ReplacePushbuttonField(field, button, 0); - } - - /** - * Replaces the designated field with a new pushbutton. The pushbutton can be created with - * {@link #getNewPushbuttonFromField(String,int)} from the same document or it can be a - * generic PdfFormField of the type pushbutton. - * @param field the field name - * @param button the PdfFormField representing the pushbutton - * @param order the field order in fields with same name - * @return true if the field was replaced, false if the field - * was not a pushbutton - */ - public bool ReplacePushbuttonField(String field, PdfFormField button, int order) { - if (GetFieldType(field) != FIELD_TYPE_PUSHBUTTON) + PdfBoolean fb = ifit.GetAsBoolean(PdfName.FB); + if (fb != null && fb.BooleanValue) + newButton.IconFitToBounds = true; + } + PdfObject i = mk.Get(PdfName.I); + if (i != null && i.IsIndirect()) + newButton.IconReference = (PRIndirectReference)i; + } + return newButton; + } + + /** + * Replaces the first field with a new pushbutton. The pushbutton can be created with + * {@link #getNewPushbuttonFromField(String)} from the same document or it can be a + * generic PdfFormField of the type pushbutton. + * @param field the field name + * @param button the PdfFormField representing the pushbutton + * @return true if the field was replaced, false if the field + * was not a pushbutton + */ + public bool ReplacePushbuttonField(String field, PdfFormField button) { + return ReplacePushbuttonField(field, button, 0); + } + + /** + * Replaces the designated field with a new pushbutton. The pushbutton can be created with + * {@link #getNewPushbuttonFromField(String,int)} from the same document or it can be a + * generic PdfFormField of the type pushbutton. + * @param field the field name + * @param button the PdfFormField representing the pushbutton + * @param order the field order in fields with same name + * @return true if the field was replaced, false if the field + * was not a pushbutton + */ + public bool ReplacePushbuttonField(String field, PdfFormField button, int order) { + if (GetFieldType(field) != FIELD_TYPE_PUSHBUTTON) + return false; + Item item = GetFieldItem(field); + if (order >= item.Size) return false; - Item item = GetFieldItem(field); - if (order >= item.merged.Count) - return false; - PdfDictionary merged = (PdfDictionary)item.merged[order]; - PdfDictionary values = (PdfDictionary)item.values[order]; - PdfDictionary widgets = (PdfDictionary)item.widgets[order]; - for (int k = 0; k < buttonRemove.Length; ++k) { - merged.Remove(buttonRemove[k]); - values.Remove(buttonRemove[k]); - widgets.Remove(buttonRemove[k]); - } - foreach (PdfName key in button.Keys) { - if (key.Equals(PdfName.T) || key.Equals(PdfName.RECT)) - continue; - if (key.Equals(PdfName.FF)) - values.Put(key, button.Get(key)); - else - widgets.Put(key, button.Get(key)); - merged.Put(key, button.Get(key)); - } - return true; - } - } -} + PdfDictionary merged = item.GetMerged(order); + PdfDictionary values = item.GetValue(order); + PdfDictionary widgets = item.GetWidget(order); + for (int k = 0; k < buttonRemove.Length; ++k) { + merged.Remove(buttonRemove[k]); + values.Remove(buttonRemove[k]); + widgets.Remove(buttonRemove[k]); + } + foreach (PdfName key in button.Keys) { + if (key.Equals(PdfName.T) || key.Equals(PdfName.RECT)) + continue; + if (key.Equals(PdfName.FF)) + values.Put(key, button.Get(key)); + else + widgets.Put(key, button.Get(key)); + merged.Put(key, button.Get(key)); + } + return true; + } + } +} diff --git a/src/core/iTextSharp/text/pdf/FdfWriter.cs b/src/core/iTextSharp/text/pdf/FdfWriter.cs index 3f5bcad..783f789 100644 --- a/src/core/iTextSharp/text/pdf/FdfWriter.cs +++ b/src/core/iTextSharp/text/pdf/FdfWriter.cs @@ -271,8 +271,7 @@ namespace iTextSharp.text.pdf { foreach (DictionaryEntry entry in af.Fields) { String fn = (String)entry.Key; AcroFields.Item item = (AcroFields.Item)entry.Value; - PdfDictionary dic = (PdfDictionary)item.merged[0]; - //PdfDictionary dic = item.GetMerged(0); + PdfDictionary dic = item.GetMerged(0); PdfObject v = PdfReader.GetPdfObjectRelease(dic.Get(PdfName.V)); if (v == null) continue; diff --git a/src/core/iTextSharp/text/pdf/PdfStamper.cs b/src/core/iTextSharp/text/pdf/PdfStamper.cs index bb96bbe..50f9916 100644 --- a/src/core/iTextSharp/text/pdf/PdfStamper.cs +++ b/src/core/iTextSharp/text/pdf/PdfStamper.cs @@ -401,7 +401,27 @@ namespace iTextSharp.text.pdf { public void AddAnnotation(PdfAnnotation annot, int page) { stamper.AddAnnotation(annot, page); } - + + /** + * Adds an empty signature. + * @param name the name of the signature + * @param page the page number + * @param llx lower left x coordinate of the signature's position + * @param lly lower left y coordinate of the signature's position + * @param urx upper right x coordinate of the signature's position + * @param ury upper right y coordinate of the signature's position + * @return a signature form field + * @since 2.1.4 + */ + public PdfFormField AddSignature(String name, int page, float llx, float lly, float urx, float ury) { + PdfAcroForm acroForm = stamper.AcroForm; + PdfFormField signature = PdfFormField.CreateSignature(stamper); + acroForm.SetSignatureParams(signature, name, llx, lly, urx, ury); + acroForm.DrawSignatureAppearences(signature, llx, lly, urx, ury); + AddAnnotation(signature, page); + return signature; + } + /** * Adds the comments present in an FDF file. * @param fdf the FDF file diff --git a/src/core/iTextSharp/text/pdf/PushbuttonField.cs b/src/core/iTextSharp/text/pdf/PushbuttonField.cs index ba455b2..c75f8da 100644 --- a/src/core/iTextSharp/text/pdf/PushbuttonField.cs +++ b/src/core/iTextSharp/text/pdf/PushbuttonField.cs @@ -1,628 +1,628 @@ using System; using iTextSharp.text; -/* - * Copyright 2005 by Paulo Soares. - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 - * (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the License. - * - * The Original Code is 'iText, a free JAVA-PDF library'. - * - * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by - * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. - * All Rights Reserved. - * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer - * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. - * - * Contributor(s): all the names of the contributors are added in the source code - * where applicable. - * - * Alternatively, the contents of this file may be used under the terms of the - * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the - * provisions of LGPL are applicable instead of those above. If you wish to - * allow use of your version of this file only under the terms of the LGPL - * License and not to allow others to use your version of this file under - * the MPL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the LGPL. - * If you do not delete the provisions above, a recipient may use your version - * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MPL as stated above or under the terms of the GNU - * Library General Public License as published by the Free Software Foundation; - * either version 2 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more - * details. - * - * If you didn't download this code from the following link, you should check if - * you aren't using an obsolete version: - * http://www.lowagie.com/iText/ - */ +/* + * Copyright 2005 by Paulo Soares. + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ namespace iTextSharp.text.pdf { - /** - * Creates a pushbutton field. It supports all the text and icon alignments. - * The icon may be an image or a template. - *

- * Example usage: - *

- *

-    * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
-    * PdfWriter writer = PdfWriter.GetInstance(document, new FileOutputStream("output.pdf"));
-    * document.Open();
-    * PdfContentByte cb = writer.GetDirectContent();
-    * Image img = Image.GetInstance("image.png");
-    * PushbuttonField bt = new PushbuttonField(writer, new Rectangle(100, 100, 200, 200), "Button1");
-    * bt.SetText("My Caption");
-    * bt.SetFontSize(0);
-    * bt.SetImage(img);
-    * bt.SetLayout(PushbuttonField.LAYOUT_ICON_TOP_LABEL_BOTTOM);
-    * bt.SetBackgroundColor(Color.cyan);
-    * bt.SetBorderStyle(PdfBorderDictionary.STYLE_SOLID);
-    * bt.SetBorderColor(Color.red);
-    * bt.SetBorderWidth(3);
-    * PdfFormField ff = bt.GetField();
-    * PdfAction ac = PdfAction.CreateSubmitForm("http://www.submit-site.com", null, 0);
-    * ff.SetAction(ac);
-    * writer.AddAnnotation(ff);
-    * document.Close();
-    * 
- * @author Paulo Soares (psoares@consiste.pt) - */ - public class PushbuttonField : BaseField { - - /** A layout option */ - public const int LAYOUT_LABEL_ONLY = 1; - /** A layout option */ - public const int LAYOUT_ICON_ONLY = 2; - /** A layout option */ - public const int LAYOUT_ICON_TOP_LABEL_BOTTOM = 3; - /** A layout option */ - public const int LAYOUT_LABEL_TOP_ICON_BOTTOM = 4; - /** A layout option */ - public const int LAYOUT_ICON_LEFT_LABEL_RIGHT = 5; - /** A layout option */ - public const int LAYOUT_LABEL_LEFT_ICON_RIGHT = 6; - /** A layout option */ - public const int LAYOUT_LABEL_OVER_ICON = 7; - /** An icon scaling option */ - public const int SCALE_ICON_ALWAYS = 1; - /** An icon scaling option */ - public const int SCALE_ICON_NEVER = 2; - /** An icon scaling option */ - public const int SCALE_ICON_IS_TOO_BIG = 3; - /** An icon scaling option */ - public const int SCALE_ICON_IS_TOO_SMALL = 4; - - /** - * Holds value of property layout. - */ - private int layout = LAYOUT_LABEL_ONLY; - - /** - * Holds value of property image. - */ - private Image image; - - /** - * Holds value of property template. - */ - private PdfTemplate template; - - /** - * Holds value of property scaleIcon. - */ - private int scaleIcon = SCALE_ICON_ALWAYS; - - /** - * Holds value of property proportionalIcon. - */ - private bool proportionalIcon = true; - - /** - * Holds value of property iconVerticalAdjustment. - */ - private float iconVerticalAdjustment = 0.5f; - - /** - * Holds value of property iconHorizontalAdjustment. - */ - private float iconHorizontalAdjustment = 0.5f; - - /** - * Holds value of property iconFitToBounds. - */ - private bool iconFitToBounds; - - private PdfTemplate tp; - - /** - * Creates a new instance of PushbuttonField - * @param writer the document PdfWriter - * @param box the field location and dimensions - * @param fieldName the field name. If null only the widget keys - * will be included in the field allowing it to be used as a kid field. - */ - public PushbuttonField(PdfWriter writer, Rectangle box, String fieldName) : base(writer, box, fieldName) { - } - - /** - * Sets the icon and label layout. Possible values are LAYOUT_LABEL_ONLY, - * LAYOUT_ICON_ONLY, LAYOUT_ICON_TOP_LABEL_BOTTOM, - * LAYOUT_LABEL_TOP_ICON_BOTTOM, LAYOUT_ICON_LEFT_LABEL_RIGHT, - * LAYOUT_LABEL_LEFT_ICON_RIGHT and LAYOUT_LABEL_OVER_ICON. - * The default is LAYOUT_LABEL_ONLY. - * @param layout New value of property layout. - */ - public int Layout { - set { - if (value < LAYOUT_LABEL_ONLY || value > LAYOUT_LABEL_OVER_ICON) - throw new ArgumentException("Layout out of bounds."); - this.layout = value; - } - get { - return layout; - } - } - - /** - * Sets the icon as an image. - * @param image the image - */ - public Image Image { - get { - return this.image; - } - set { - image = value; - template = null; - } - } - - /** - * Sets the icon as a template. - * @param template the template - */ - public PdfTemplate Template { - set { - this.template = value; - image = null; - } - get { - return template; - } - } - - /** - * Sets the way the icon will be scaled. Possible values are - * SCALE_ICON_ALWAYS, SCALE_ICON_NEVER, - * SCALE_ICON_IS_TOO_BIG and SCALE_ICON_IS_TOO_SMALL. - * The default is SCALE_ICON_ALWAYS. - * @param scaleIcon the way the icon will be scaled - */ - public int ScaleIcon { - set { - if (value < SCALE_ICON_ALWAYS || value > SCALE_ICON_IS_TOO_SMALL) - scaleIcon = SCALE_ICON_ALWAYS; - else - scaleIcon = value; - } - get { - return scaleIcon; - } - } - - /** - * Sets the way the icon is scaled. If true the icon is scaled proportionally, - * if false the scaling is done anamorphicaly. - * @param proportionalIcon the way the icon is scaled - */ - public bool ProportionalIcon { - get { - return proportionalIcon; - } - set { - proportionalIcon = value; - } - } - - /** - * A number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon. - * A value of 0 positions the icon at the bottom of the annotation rectangle. - * A value of 0.5 centers it within the rectangle. The default is 0.5. - * @param iconVerticalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon - */ - public float IconVerticalAdjustment { - get { - return iconVerticalAdjustment; - } - set { - iconVerticalAdjustment = value; - if (iconVerticalAdjustment < 0) - iconVerticalAdjustment = 0; - else if (iconVerticalAdjustment > 1) - iconVerticalAdjustment = 1; - } - } - - /** - * A number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon. - * A value of 0 positions the icon at the left of the annotation rectangle. - * A value of 0.5 centers it within the rectangle. The default is 0.5. - * @param iconHorizontalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon - */ - public float IconHorizontalAdjustment { - get { - return iconHorizontalAdjustment; - } - set { - iconHorizontalAdjustment = value; - if (iconHorizontalAdjustment < 0) - iconHorizontalAdjustment = 0; - else if (iconHorizontalAdjustment > 1) - iconHorizontalAdjustment = 1; - } - } - private float CalculateFontSize(float w, float h) { - BaseFont ufont = RealFont; - float fsize = fontSize; - if (fsize == 0) { - float bw = ufont.GetWidthPoint(text, 1); - if (bw == 0) - fsize = 12; - else - fsize = w / bw; - float nfsize = h / (1 - ufont.GetFontDescriptor(BaseFont.DESCENT, 1)); - fsize = Math.Min(fsize, nfsize); - if (fsize < 4) - fsize = 4; - } - return fsize; - } - - /** - * Gets the button appearance. - * @throws IOException on error - * @throws DocumentException on error - * @return the button appearance - */ - public PdfAppearance GetAppearance() { - PdfAppearance app = GetBorderAppearance(); - Rectangle box = new Rectangle(app.BoundingBox); - if ((text == null || text.Length == 0) && (layout == LAYOUT_LABEL_ONLY || (image == null && template == null && iconReference == null))) { - return app; - } - if (layout == LAYOUT_ICON_ONLY && image == null && template == null && iconReference == null) - return app; - BaseFont ufont = RealFont; - bool borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET; - float h = box.Height - borderWidth * 2; - float bw2 = borderWidth; - if (borderExtra) { - h -= borderWidth * 2; - bw2 *= 2; - } - float offsetX = (borderExtra ? 2 * borderWidth : borderWidth); - offsetX = Math.Max(offsetX, 1); - float offX = Math.Min(bw2, offsetX); - tp = null; - float textX = float.NaN; - float textY = 0; - float fsize = fontSize; - float wt = box.Width - 2 * offX - 2; - float ht = box.Height - 2 * offX; - float adj = (iconFitToBounds ? 0 : offX + 1); - int nlayout = layout; - if (image == null && template == null && iconReference == null) - nlayout = LAYOUT_LABEL_ONLY; - Rectangle iconBox = null; - while (true) { - switch (nlayout) { - case LAYOUT_LABEL_ONLY: - case LAYOUT_LABEL_OVER_ICON: - if (text != null && text.Length > 0 && wt > 0 && ht > 0) { - fsize = CalculateFontSize(wt, ht); - textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; - textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; - } - goto case LAYOUT_ICON_ONLY; - case LAYOUT_ICON_ONLY: - if (nlayout == LAYOUT_LABEL_OVER_ICON || nlayout == LAYOUT_ICON_ONLY) - iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, box.Right - adj, box.Top - adj); - break; - case LAYOUT_ICON_TOP_LABEL_BOTTOM: - if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { - nlayout = LAYOUT_ICON_ONLY; - continue; - } - float nht = box.Height * 0.35f - offX; - if (nht > 0) - fsize = CalculateFontSize(wt, nht); - else - fsize = 4; - textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; - textY = offX - ufont.GetFontDescriptor(BaseFont.DESCENT, fsize); - iconBox = new Rectangle(box.Left + adj, textY + fsize, box.Right - adj, box.Top - adj); - break; - case LAYOUT_LABEL_TOP_ICON_BOTTOM: - if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { - nlayout = LAYOUT_ICON_ONLY; - continue; - } - nht = box.Height * 0.35f - offX; - if (nht > 0) - fsize = CalculateFontSize(wt, nht); - else - fsize = 4; - textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; - textY = box.Height - offX - fsize; - if (textY < offX) - textY = offX; - iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, box.Right - adj, textY + ufont.GetFontDescriptor(BaseFont.DESCENT, fsize)); - break; - case LAYOUT_LABEL_LEFT_ICON_RIGHT: - if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { - nlayout = LAYOUT_ICON_ONLY; - continue; - } - float nw = box.Width * 0.35f - offX; - if (nw > 0) - fsize = CalculateFontSize(wt, nw); - else - fsize = 4; - if (ufont.GetWidthPoint(text, fsize) >= wt) { - nlayout = LAYOUT_LABEL_ONLY; - fsize = fontSize; - continue; - } - textX = offX + 1; - textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; - iconBox = new Rectangle(textX + ufont.GetWidthPoint(text, fsize), box.Bottom + adj, box.Right - adj, box.Top - adj); - break; - case LAYOUT_ICON_LEFT_LABEL_RIGHT: - if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { - nlayout = LAYOUT_ICON_ONLY; - continue; - } - nw = box.Width * 0.35f - offX; - if (nw > 0) - fsize = CalculateFontSize(wt, nw); - else - fsize = 4; - if (ufont.GetWidthPoint(text, fsize) >= wt) { - nlayout = LAYOUT_LABEL_ONLY; - fsize = fontSize; - continue; - } - textX = box.Width - ufont.GetWidthPoint(text, fsize) - offX - 1; - textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; - iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, textX - 1, box.Top - adj); - break; - } - break; - } - if (textY < box.Bottom + offX) - textY = box.Bottom + offX; - if (iconBox != null && (iconBox.Width <= 0 || iconBox.Height <= 0)) - iconBox = null; - bool haveIcon = false; - float boundingBoxWidth = 0; - float boundingBoxHeight = 0; - PdfArray matrix = null; - if (iconBox != null) { - if (image != null) { - tp = new PdfTemplate(writer); - tp.BoundingBox = new Rectangle(image); - writer.AddDirectTemplateSimple(tp, PdfName.FRM); - tp.AddImage(image, image.Width, 0, 0, image.Height, 0, 0); - haveIcon = true; - boundingBoxWidth = tp.BoundingBox.Width; - boundingBoxHeight = tp.BoundingBox.Height; - } - else if (template != null) { - tp = new PdfTemplate(writer); - tp.BoundingBox = new Rectangle(template.Width, template.Height); - writer.AddDirectTemplateSimple(tp, PdfName.FRM); - tp.AddTemplate(template, template.BoundingBox.Left, template.BoundingBox.Bottom); - haveIcon = true; - boundingBoxWidth = tp.BoundingBox.Width; - boundingBoxHeight = tp.BoundingBox.Height; - } - else if (iconReference != null) { - PdfDictionary dic = (PdfDictionary)PdfReader.GetPdfObject(iconReference); - if (dic != null) { - Rectangle r2 = PdfReader.GetNormalizedRectangle((PdfArray)PdfReader.GetPdfObject(dic.Get(PdfName.BBOX))); - matrix = (PdfArray)PdfReader.GetPdfObject(dic.Get(PdfName.MATRIX)); - haveIcon = true; - boundingBoxWidth = r2.Width; - boundingBoxHeight = r2.Height; - } - } - } - if (haveIcon) { - float icx = iconBox.Width / boundingBoxWidth; - float icy = iconBox.Height / boundingBoxHeight; - if (proportionalIcon) { - switch (scaleIcon) { - case SCALE_ICON_IS_TOO_BIG: - icx = Math.Min(icx, icy); - icx = Math.Min(icx, 1); - break; - case SCALE_ICON_IS_TOO_SMALL: - icx = Math.Min(icx, icy); - icx = Math.Max(icx, 1); - break; - case SCALE_ICON_NEVER: - icx = 1; - break; - default: - icx = Math.Min(icx, icy); - break; - } - icy = icx; - } - else { - switch (scaleIcon) { - case SCALE_ICON_IS_TOO_BIG: - icx = Math.Min(icx, 1); - icy = Math.Min(icy, 1); - break; - case SCALE_ICON_IS_TOO_SMALL: - icx = Math.Max(icx, 1); - icy = Math.Max(icy, 1); - break; - case SCALE_ICON_NEVER: - icx = icy = 1; - break; - default: - break; - } - } - float xpos = iconBox.Left + (iconBox.Width - (boundingBoxWidth * icx)) * iconHorizontalAdjustment; - float ypos = iconBox.Bottom + (iconBox.Height - (boundingBoxHeight * icy)) * iconVerticalAdjustment; - app.SaveState(); - app.Rectangle(iconBox.Left, iconBox.Bottom, iconBox.Width, iconBox.Height); - app.Clip(); - app.NewPath(); - if (tp != null) - app.AddTemplate(tp, icx, 0, 0, icy, xpos, ypos); - else { - float cox = 0; - float coy = 0; - if (matrix != null && matrix.Size == 6) { - PdfNumber nm = (PdfNumber)PdfReader.GetPdfObject((PdfObject)matrix.ArrayList[4]); - if (nm != null) - cox = nm.FloatValue; - nm = (PdfNumber)PdfReader.GetPdfObject((PdfObject)matrix.ArrayList[5]); - if (nm != null) - coy = nm.FloatValue; - } - app.AddTemplateReference(iconReference, PdfName.FRM, icx, 0, 0, icy, xpos - cox * icx, ypos - coy * icy); - } - app.RestoreState(); - } - if (!float.IsNaN(textX)) { - app.SaveState(); - app.Rectangle(offX, offX, box.Width - 2 * offX, box.Height - 2 * offX); - app.Clip(); - app.NewPath(); - if (textColor == null) - app.ResetGrayFill(); - else - app.SetColorFill(textColor); - app.BeginText(); - app.SetFontAndSize(ufont, fsize); - app.SetTextMatrix(textX, textY); - app.ShowText(text); - app.EndText(); - app.RestoreState(); - } - return app; - } - - /** - * Gets the pushbutton field. - * @throws IOException on error - * @throws DocumentException on error - * @return the pushbutton field - */ - public PdfFormField Field { - get { - PdfFormField field = PdfFormField.CreatePushButton(writer); - field.SetWidget(box, PdfAnnotation.HIGHLIGHT_INVERT); - if (fieldName != null) { - field.FieldName = fieldName; - if ((options & READ_ONLY) != 0) - field.SetFieldFlags(PdfFormField.FF_READ_ONLY); - if ((options & REQUIRED) != 0) - field.SetFieldFlags(PdfFormField.FF_REQUIRED); - } - if (text != null) - field.MKNormalCaption = text; - if (rotation != 0) - field.MKRotation = rotation; - field.BorderStyle = new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)); - PdfAppearance tpa = GetAppearance(); - field.SetAppearance(PdfAnnotation.APPEARANCE_NORMAL, tpa); - PdfAppearance da = (PdfAppearance)tpa.Duplicate; - da.SetFontAndSize(RealFont, fontSize); - if (textColor == null) - da.SetGrayFill(0); - else - da.SetColorFill(textColor); - field.DefaultAppearanceString = da; - if (borderColor != null) - field.MKBorderColor = borderColor; - if (backgroundColor != null) - field.MKBackgroundColor = backgroundColor; - switch (visibility) { - case HIDDEN: - field.Flags = PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN; - break; - case VISIBLE_BUT_DOES_NOT_PRINT: - break; - case HIDDEN_BUT_PRINTABLE: - field.Flags = PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW; - break; - default: - field.Flags = PdfAnnotation.FLAGS_PRINT; - break; - } - if (tp != null) - field.MKNormalIcon = tp; - field.MKTextPosition = layout - 1; - PdfName scale = PdfName.A; - if (scaleIcon == SCALE_ICON_IS_TOO_BIG) - scale = PdfName.B; - else if (scaleIcon == SCALE_ICON_IS_TOO_SMALL) - scale = PdfName.S; - else if (scaleIcon == SCALE_ICON_NEVER) - scale = PdfName.N; - field.SetMKIconFit(scale, proportionalIcon ? PdfName.P : PdfName.A, iconHorizontalAdjustment, - iconVerticalAdjustment, iconFitToBounds); - return field; - } - } - - /** - * If true the icon will be scaled to fit fully within the bounds of the annotation, - * if false the border width will be taken into account. The default - * is false. - * @param iconFitToBounds if true the icon will be scaled to fit fully within the bounds of the annotation, - * if false the border width will be taken into account - */ - public bool IconFitToBounds { - get { - return iconFitToBounds; - } - set { - iconFitToBounds = value; - } - } - /** - * Holds value of property iconReference. - */ - private PRIndirectReference iconReference; - - /** - * Sets the reference to an existing icon. - * @param iconReference the reference to an existing icon - */ - public PRIndirectReference IconReference { - get { - return iconReference; - } - set { - iconReference = value; - } - } + /** + * Creates a pushbutton field. It supports all the text and icon alignments. + * The icon may be an image or a template. + *

+ * Example usage: + *

+ *

+    * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+    * PdfWriter writer = PdfWriter.GetInstance(document, new FileOutputStream("output.pdf"));
+    * document.Open();
+    * PdfContentByte cb = writer.GetDirectContent();
+    * Image img = Image.GetInstance("image.png");
+    * PushbuttonField bt = new PushbuttonField(writer, new Rectangle(100, 100, 200, 200), "Button1");
+    * bt.SetText("My Caption");
+    * bt.SetFontSize(0);
+    * bt.SetImage(img);
+    * bt.SetLayout(PushbuttonField.LAYOUT_ICON_TOP_LABEL_BOTTOM);
+    * bt.SetBackgroundColor(Color.cyan);
+    * bt.SetBorderStyle(PdfBorderDictionary.STYLE_SOLID);
+    * bt.SetBorderColor(Color.red);
+    * bt.SetBorderWidth(3);
+    * PdfFormField ff = bt.GetField();
+    * PdfAction ac = PdfAction.CreateSubmitForm("http://www.submit-site.com", null, 0);
+    * ff.SetAction(ac);
+    * writer.AddAnnotation(ff);
+    * document.Close();
+    * 
+ * @author Paulo Soares (psoares@consiste.pt) + */ + public class PushbuttonField : BaseField { + + /** A layout option */ + public const int LAYOUT_LABEL_ONLY = 1; + /** A layout option */ + public const int LAYOUT_ICON_ONLY = 2; + /** A layout option */ + public const int LAYOUT_ICON_TOP_LABEL_BOTTOM = 3; + /** A layout option */ + public const int LAYOUT_LABEL_TOP_ICON_BOTTOM = 4; + /** A layout option */ + public const int LAYOUT_ICON_LEFT_LABEL_RIGHT = 5; + /** A layout option */ + public const int LAYOUT_LABEL_LEFT_ICON_RIGHT = 6; + /** A layout option */ + public const int LAYOUT_LABEL_OVER_ICON = 7; + /** An icon scaling option */ + public const int SCALE_ICON_ALWAYS = 1; + /** An icon scaling option */ + public const int SCALE_ICON_NEVER = 2; + /** An icon scaling option */ + public const int SCALE_ICON_IS_TOO_BIG = 3; + /** An icon scaling option */ + public const int SCALE_ICON_IS_TOO_SMALL = 4; + + /** + * Holds value of property layout. + */ + private int layout = LAYOUT_LABEL_ONLY; + + /** + * Holds value of property image. + */ + private Image image; + + /** + * Holds value of property template. + */ + private PdfTemplate template; + + /** + * Holds value of property scaleIcon. + */ + private int scaleIcon = SCALE_ICON_ALWAYS; + + /** + * Holds value of property proportionalIcon. + */ + private bool proportionalIcon = true; + + /** + * Holds value of property iconVerticalAdjustment. + */ + private float iconVerticalAdjustment = 0.5f; + + /** + * Holds value of property iconHorizontalAdjustment. + */ + private float iconHorizontalAdjustment = 0.5f; + + /** + * Holds value of property iconFitToBounds. + */ + private bool iconFitToBounds; + + private PdfTemplate tp; + + /** + * Creates a new instance of PushbuttonField + * @param writer the document PdfWriter + * @param box the field location and dimensions + * @param fieldName the field name. If null only the widget keys + * will be included in the field allowing it to be used as a kid field. + */ + public PushbuttonField(PdfWriter writer, Rectangle box, String fieldName) : base(writer, box, fieldName) { + } + + /** + * Sets the icon and label layout. Possible values are LAYOUT_LABEL_ONLY, + * LAYOUT_ICON_ONLY, LAYOUT_ICON_TOP_LABEL_BOTTOM, + * LAYOUT_LABEL_TOP_ICON_BOTTOM, LAYOUT_ICON_LEFT_LABEL_RIGHT, + * LAYOUT_LABEL_LEFT_ICON_RIGHT and LAYOUT_LABEL_OVER_ICON. + * The default is LAYOUT_LABEL_ONLY. + * @param layout New value of property layout. + */ + public int Layout { + set { + if (value < LAYOUT_LABEL_ONLY || value > LAYOUT_LABEL_OVER_ICON) + throw new ArgumentException("Layout out of bounds."); + this.layout = value; + } + get { + return layout; + } + } + + /** + * Sets the icon as an image. + * @param image the image + */ + public Image Image { + get { + return this.image; + } + set { + image = value; + template = null; + } + } + + /** + * Sets the icon as a template. + * @param template the template + */ + public PdfTemplate Template { + set { + this.template = value; + image = null; + } + get { + return template; + } + } + + /** + * Sets the way the icon will be scaled. Possible values are + * SCALE_ICON_ALWAYS, SCALE_ICON_NEVER, + * SCALE_ICON_IS_TOO_BIG and SCALE_ICON_IS_TOO_SMALL. + * The default is SCALE_ICON_ALWAYS. + * @param scaleIcon the way the icon will be scaled + */ + public int ScaleIcon { + set { + if (value < SCALE_ICON_ALWAYS || value > SCALE_ICON_IS_TOO_SMALL) + scaleIcon = SCALE_ICON_ALWAYS; + else + scaleIcon = value; + } + get { + return scaleIcon; + } + } + + /** + * Sets the way the icon is scaled. If true the icon is scaled proportionally, + * if false the scaling is done anamorphicaly. + * @param proportionalIcon the way the icon is scaled + */ + public bool ProportionalIcon { + get { + return proportionalIcon; + } + set { + proportionalIcon = value; + } + } + + /** + * A number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon. + * A value of 0 positions the icon at the bottom of the annotation rectangle. + * A value of 0.5 centers it within the rectangle. The default is 0.5. + * @param iconVerticalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon + */ + public float IconVerticalAdjustment { + get { + return iconVerticalAdjustment; + } + set { + iconVerticalAdjustment = value; + if (iconVerticalAdjustment < 0) + iconVerticalAdjustment = 0; + else if (iconVerticalAdjustment > 1) + iconVerticalAdjustment = 1; + } + } + + /** + * A number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon. + * A value of 0 positions the icon at the left of the annotation rectangle. + * A value of 0.5 centers it within the rectangle. The default is 0.5. + * @param iconHorizontalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon + */ + public float IconHorizontalAdjustment { + get { + return iconHorizontalAdjustment; + } + set { + iconHorizontalAdjustment = value; + if (iconHorizontalAdjustment < 0) + iconHorizontalAdjustment = 0; + else if (iconHorizontalAdjustment > 1) + iconHorizontalAdjustment = 1; + } + } + private float CalculateFontSize(float w, float h) { + BaseFont ufont = RealFont; + float fsize = fontSize; + if (fsize == 0) { + float bw = ufont.GetWidthPoint(text, 1); + if (bw == 0) + fsize = 12; + else + fsize = w / bw; + float nfsize = h / (1 - ufont.GetFontDescriptor(BaseFont.DESCENT, 1)); + fsize = Math.Min(fsize, nfsize); + if (fsize < 4) + fsize = 4; + } + return fsize; + } + + /** + * Gets the button appearance. + * @throws IOException on error + * @throws DocumentException on error + * @return the button appearance + */ + public PdfAppearance GetAppearance() { + PdfAppearance app = GetBorderAppearance(); + Rectangle box = new Rectangle(app.BoundingBox); + if ((text == null || text.Length == 0) && (layout == LAYOUT_LABEL_ONLY || (image == null && template == null && iconReference == null))) { + return app; + } + if (layout == LAYOUT_ICON_ONLY && image == null && template == null && iconReference == null) + return app; + BaseFont ufont = RealFont; + bool borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET; + float h = box.Height - borderWidth * 2; + float bw2 = borderWidth; + if (borderExtra) { + h -= borderWidth * 2; + bw2 *= 2; + } + float offsetX = (borderExtra ? 2 * borderWidth : borderWidth); + offsetX = Math.Max(offsetX, 1); + float offX = Math.Min(bw2, offsetX); + tp = null; + float textX = float.NaN; + float textY = 0; + float fsize = fontSize; + float wt = box.Width - 2 * offX - 2; + float ht = box.Height - 2 * offX; + float adj = (iconFitToBounds ? 0 : offX + 1); + int nlayout = layout; + if (image == null && template == null && iconReference == null) + nlayout = LAYOUT_LABEL_ONLY; + Rectangle iconBox = null; + while (true) { + switch (nlayout) { + case LAYOUT_LABEL_ONLY: + case LAYOUT_LABEL_OVER_ICON: + if (text != null && text.Length > 0 && wt > 0 && ht > 0) { + fsize = CalculateFontSize(wt, ht); + textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; + textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; + } + goto case LAYOUT_ICON_ONLY; + case LAYOUT_ICON_ONLY: + if (nlayout == LAYOUT_LABEL_OVER_ICON || nlayout == LAYOUT_ICON_ONLY) + iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, box.Right - adj, box.Top - adj); + break; + case LAYOUT_ICON_TOP_LABEL_BOTTOM: + if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { + nlayout = LAYOUT_ICON_ONLY; + continue; + } + float nht = box.Height * 0.35f - offX; + if (nht > 0) + fsize = CalculateFontSize(wt, nht); + else + fsize = 4; + textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; + textY = offX - ufont.GetFontDescriptor(BaseFont.DESCENT, fsize); + iconBox = new Rectangle(box.Left + adj, textY + fsize, box.Right - adj, box.Top - adj); + break; + case LAYOUT_LABEL_TOP_ICON_BOTTOM: + if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { + nlayout = LAYOUT_ICON_ONLY; + continue; + } + nht = box.Height * 0.35f - offX; + if (nht > 0) + fsize = CalculateFontSize(wt, nht); + else + fsize = 4; + textX = (box.Width - ufont.GetWidthPoint(text, fsize)) / 2; + textY = box.Height - offX - fsize; + if (textY < offX) + textY = offX; + iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, box.Right - adj, textY + ufont.GetFontDescriptor(BaseFont.DESCENT, fsize)); + break; + case LAYOUT_LABEL_LEFT_ICON_RIGHT: + if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { + nlayout = LAYOUT_ICON_ONLY; + continue; + } + float nw = box.Width * 0.35f - offX; + if (nw > 0) + fsize = CalculateFontSize(wt, nw); + else + fsize = 4; + if (ufont.GetWidthPoint(text, fsize) >= wt) { + nlayout = LAYOUT_LABEL_ONLY; + fsize = fontSize; + continue; + } + textX = offX + 1; + textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; + iconBox = new Rectangle(textX + ufont.GetWidthPoint(text, fsize), box.Bottom + adj, box.Right - adj, box.Top - adj); + break; + case LAYOUT_ICON_LEFT_LABEL_RIGHT: + if (text == null || text.Length == 0 || wt <= 0 || ht <= 0) { + nlayout = LAYOUT_ICON_ONLY; + continue; + } + nw = box.Width * 0.35f - offX; + if (nw > 0) + fsize = CalculateFontSize(wt, nw); + else + fsize = 4; + if (ufont.GetWidthPoint(text, fsize) >= wt) { + nlayout = LAYOUT_LABEL_ONLY; + fsize = fontSize; + continue; + } + textX = box.Width - ufont.GetWidthPoint(text, fsize) - offX - 1; + textY = (box.Height - ufont.GetFontDescriptor(BaseFont.ASCENT, fsize)) / 2; + iconBox = new Rectangle(box.Left + adj, box.Bottom + adj, textX - 1, box.Top - adj); + break; + } + break; + } + if (textY < box.Bottom + offX) + textY = box.Bottom + offX; + if (iconBox != null && (iconBox.Width <= 0 || iconBox.Height <= 0)) + iconBox = null; + bool haveIcon = false; + float boundingBoxWidth = 0; + float boundingBoxHeight = 0; + PdfArray matrix = null; + if (iconBox != null) { + if (image != null) { + tp = new PdfTemplate(writer); + tp.BoundingBox = new Rectangle(image); + writer.AddDirectTemplateSimple(tp, PdfName.FRM); + tp.AddImage(image, image.Width, 0, 0, image.Height, 0, 0); + haveIcon = true; + boundingBoxWidth = tp.BoundingBox.Width; + boundingBoxHeight = tp.BoundingBox.Height; + } + else if (template != null) { + tp = new PdfTemplate(writer); + tp.BoundingBox = new Rectangle(template.Width, template.Height); + writer.AddDirectTemplateSimple(tp, PdfName.FRM); + tp.AddTemplate(template, template.BoundingBox.Left, template.BoundingBox.Bottom); + haveIcon = true; + boundingBoxWidth = tp.BoundingBox.Width; + boundingBoxHeight = tp.BoundingBox.Height; + } + else if (iconReference != null) { + PdfDictionary dic = (PdfDictionary)PdfReader.GetPdfObject(iconReference); + if (dic != null) { + Rectangle r2 = PdfReader.GetNormalizedRectangle(dic.GetAsArray(PdfName.BBOX)); + matrix = dic.GetAsArray(PdfName.MATRIX); + haveIcon = true; + boundingBoxWidth = r2.Width; + boundingBoxHeight = r2.Height; + } + } + } + if (haveIcon) { + float icx = iconBox.Width / boundingBoxWidth; + float icy = iconBox.Height / boundingBoxHeight; + if (proportionalIcon) { + switch (scaleIcon) { + case SCALE_ICON_IS_TOO_BIG: + icx = Math.Min(icx, icy); + icx = Math.Min(icx, 1); + break; + case SCALE_ICON_IS_TOO_SMALL: + icx = Math.Min(icx, icy); + icx = Math.Max(icx, 1); + break; + case SCALE_ICON_NEVER: + icx = 1; + break; + default: + icx = Math.Min(icx, icy); + break; + } + icy = icx; + } + else { + switch (scaleIcon) { + case SCALE_ICON_IS_TOO_BIG: + icx = Math.Min(icx, 1); + icy = Math.Min(icy, 1); + break; + case SCALE_ICON_IS_TOO_SMALL: + icx = Math.Max(icx, 1); + icy = Math.Max(icy, 1); + break; + case SCALE_ICON_NEVER: + icx = icy = 1; + break; + default: + break; + } + } + float xpos = iconBox.Left + (iconBox.Width - (boundingBoxWidth * icx)) * iconHorizontalAdjustment; + float ypos = iconBox.Bottom + (iconBox.Height - (boundingBoxHeight * icy)) * iconVerticalAdjustment; + app.SaveState(); + app.Rectangle(iconBox.Left, iconBox.Bottom, iconBox.Width, iconBox.Height); + app.Clip(); + app.NewPath(); + if (tp != null) + app.AddTemplate(tp, icx, 0, 0, icy, xpos, ypos); + else { + float cox = 0; + float coy = 0; + if (matrix != null && matrix.Size == 6) { + PdfNumber nm = matrix.GetAsNumber(4); + if (nm != null) + cox = nm.FloatValue; + nm = matrix.GetAsNumber(5); + if (nm != null) + coy = nm.FloatValue; + } + app.AddTemplateReference(iconReference, PdfName.FRM, icx, 0, 0, icy, xpos - cox * icx, ypos - coy * icy); + } + app.RestoreState(); + } + if (!float.IsNaN(textX)) { + app.SaveState(); + app.Rectangle(offX, offX, box.Width - 2 * offX, box.Height - 2 * offX); + app.Clip(); + app.NewPath(); + if (textColor == null) + app.ResetGrayFill(); + else + app.SetColorFill(textColor); + app.BeginText(); + app.SetFontAndSize(ufont, fsize); + app.SetTextMatrix(textX, textY); + app.ShowText(text); + app.EndText(); + app.RestoreState(); + } + return app; + } + + /** + * Gets the pushbutton field. + * @throws IOException on error + * @throws DocumentException on error + * @return the pushbutton field + */ + public PdfFormField Field { + get { + PdfFormField field = PdfFormField.CreatePushButton(writer); + field.SetWidget(box, PdfAnnotation.HIGHLIGHT_INVERT); + if (fieldName != null) { + field.FieldName = fieldName; + if ((options & READ_ONLY) != 0) + field.SetFieldFlags(PdfFormField.FF_READ_ONLY); + if ((options & REQUIRED) != 0) + field.SetFieldFlags(PdfFormField.FF_REQUIRED); + } + if (text != null) + field.MKNormalCaption = text; + if (rotation != 0) + field.MKRotation = rotation; + field.BorderStyle = new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)); + PdfAppearance tpa = GetAppearance(); + field.SetAppearance(PdfAnnotation.APPEARANCE_NORMAL, tpa); + PdfAppearance da = (PdfAppearance)tpa.Duplicate; + da.SetFontAndSize(RealFont, fontSize); + if (textColor == null) + da.SetGrayFill(0); + else + da.SetColorFill(textColor); + field.DefaultAppearanceString = da; + if (borderColor != null) + field.MKBorderColor = borderColor; + if (backgroundColor != null) + field.MKBackgroundColor = backgroundColor; + switch (visibility) { + case HIDDEN: + field.Flags = PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN; + break; + case VISIBLE_BUT_DOES_NOT_PRINT: + break; + case HIDDEN_BUT_PRINTABLE: + field.Flags = PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW; + break; + default: + field.Flags = PdfAnnotation.FLAGS_PRINT; + break; + } + if (tp != null) + field.MKNormalIcon = tp; + field.MKTextPosition = layout - 1; + PdfName scale = PdfName.A; + if (scaleIcon == SCALE_ICON_IS_TOO_BIG) + scale = PdfName.B; + else if (scaleIcon == SCALE_ICON_IS_TOO_SMALL) + scale = PdfName.S; + else if (scaleIcon == SCALE_ICON_NEVER) + scale = PdfName.N; + field.SetMKIconFit(scale, proportionalIcon ? PdfName.P : PdfName.A, iconHorizontalAdjustment, + iconVerticalAdjustment, iconFitToBounds); + return field; + } + } + + /** + * If true the icon will be scaled to fit fully within the bounds of the annotation, + * if false the border width will be taken into account. The default + * is false. + * @param iconFitToBounds if true the icon will be scaled to fit fully within the bounds of the annotation, + * if false the border width will be taken into account + */ + public bool IconFitToBounds { + get { + return iconFitToBounds; + } + set { + iconFitToBounds = value; + } + } + /** + * Holds value of property iconReference. + */ + private PRIndirectReference iconReference; + + /** + * Sets the reference to an existing icon. + * @param iconReference the reference to an existing icon + */ + public PRIndirectReference IconReference { + get { + return iconReference; + } + set { + iconReference = value; + } + } } } \ No newline at end of file diff --git a/src/core/iTextSharp/text/pdf/SimpleBookmark.cs b/src/core/iTextSharp/text/pdf/SimpleBookmark.cs index ed541b9..35e55cf 100644 --- a/src/core/iTextSharp/text/pdf/SimpleBookmark.cs +++ b/src/core/iTextSharp/text/pdf/SimpleBookmark.cs @@ -109,12 +109,11 @@ namespace iTextSharp.text.pdf { PdfString title = (PdfString)PdfReader.GetPdfObjectRelease(outline.Get(PdfName.TITLE)); map["Title"] = title.ToUnicodeString(); PdfArray color = (PdfArray)PdfReader.GetPdfObjectRelease(outline.Get(PdfName.C)); - if (color != null && color.ArrayList.Count == 3) { + if (color != null && color.Size == 3) { ByteBuffer outp = new ByteBuffer(); - ArrayList arr = color.ArrayList; - outp.Append(((PdfNumber)arr[0]).FloatValue).Append(' '); - outp.Append(((PdfNumber)arr[1]).FloatValue).Append(' '); - outp.Append(((PdfNumber)arr[2]).FloatValue); + outp.Append(color.GetAsNumber(0).FloatValue).Append(' '); + outp.Append(color.GetAsNumber(1).FloatValue).Append(' '); + outp.Append(color.GetAsNumber(2).FloatValue); map["Color"] = PdfEncodings.ConvertToString(outp.ToByteArray(), null); } PdfNumber style = (PdfNumber)PdfReader.GetPdfObjectRelease(outline.Get(PdfName.F)); @@ -158,11 +157,11 @@ namespace iTextSharp.text.pdf { else if (dest.IsName()) map["NamedN"] = PdfName.DecodeName(dest.ToString()); else if (dest.IsArray()) { - ArrayList arr = ((PdfArray)dest).ArrayList; + PdfArray arr = (PdfArray)dest; StringBuilder s = new StringBuilder(); s.Append(arr[0].ToString()); s.Append(' ').Append(arr[1].ToString()); - for (int k = 2; k < arr.Count; ++k) + for (int k = 2; k < arr.Size; ++k) s.Append(' ').Append(arr[k].ToString()); map["Page"] = s.ToString(); } @@ -226,15 +225,15 @@ namespace iTextSharp.text.pdf { private static String MakeBookmarkParam(PdfArray dest, IntHashtable pages) { - ArrayList arr = dest.ArrayList; StringBuilder s = new StringBuilder(); - if (((PdfObject)arr[0]).IsNumber()) - s.Append(((PdfNumber)arr[0]).IntValue + 1); + PdfObject obj = dest[0]; + if (obj.IsNumber()) + s.Append(((PdfNumber)obj).IntValue + 1); else - s.Append(pages[GetNumber((PdfIndirectReference)arr[0])]); //changed by ujihara 2004-06-13 - s.Append(' ').Append(arr[1].ToString().Substring(1)); - for (int k = 2; k < arr.Count; ++k) - s.Append(' ').Append(arr[k].ToString()); + s.Append(pages[GetNumber((PdfIndirectReference)obj)]); //changed by ujihara 2004-06-13 + s.Append(' ').Append(dest[1].ToString().Substring(1)); + for (int k = 2; k < dest.Size; ++k) + s.Append(' ').Append(dest[k].ToString()); return s.ToString(); } @@ -250,7 +249,7 @@ namespace iTextSharp.text.pdf { if (pdfObj.Contains(PdfName.TYPE) && pdfObj.Get(PdfName.TYPE).Equals(PdfName.PAGES) && pdfObj.Contains(PdfName.KIDS)) { PdfArray kids = (PdfArray)pdfObj.Get(PdfName.KIDS); - indirect = (PdfIndirectReference)kids.ArrayList[0]; + indirect = (PdfIndirectReference)kids[0]; } return indirect.Number; } diff --git a/src/core/iTextSharp/text/pdf/SimpleNamedDestination.cs b/src/core/iTextSharp/text/pdf/SimpleNamedDestination.cs index 4032a72..e815a75 100644 --- a/src/core/iTextSharp/text/pdf/SimpleNamedDestination.cs +++ b/src/core/iTextSharp/text/pdf/SimpleNamedDestination.cs @@ -73,12 +73,12 @@ namespace iTextSharp.text.pdf { String[] keys = new String[names.Count]; names.Keys.CopyTo(keys, 0); foreach (String name in keys) { - ArrayList arr = ((PdfArray)names[name]).ArrayList; + PdfArray arr = (PdfArray)names[name]; StringBuilder s = new StringBuilder(); try { - s.Append(pages[((PdfIndirectReference)arr[0]).Number]); + s.Append(pages[(arr.GetAsIndirectObject(0)).Number]); s.Append(' ').Append(arr[1].ToString().Substring(1)); - for (int k = 2; k < arr.Count; ++k) + for (int k = 2; k < arr.Size; ++k) s.Append(' ').Append(arr[k].ToString()); names[name] = s.ToString(); } diff --git a/src/core/iTextSharp/text/pdf/XfdfReader.cs b/src/core/iTextSharp/text/pdf/XfdfReader.cs index bf0802d..814207c 100644 --- a/src/core/iTextSharp/text/pdf/XfdfReader.cs +++ b/src/core/iTextSharp/text/pdf/XfdfReader.cs @@ -2,222 +2,248 @@ using System; using System.IO; using System.Collections; using iTextSharp.text.xml.simpleparser; -/* - * - * Copyright 2004 by Leonard Rosenthol. - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 - * (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the License. - * - * The Original Code is 'iText, a free JAVA-PDF library'. - * - * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by - * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. - * All Rights Reserved. - * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer - * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. - * - * Contributor(s): all the names of the contributors are added in the source code - * where applicable. - * - * Alternatively, the contents of this file may be used under the terms of the - * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the - * provisions of LGPL are applicable instead of those above. If you wish to - * allow use of your version of this file only under the terms of the LGPL - * License and not to allow others to use your version of this file under - * the MPL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the LGPL. - * If you do not delete the provisions above, a recipient may use your version - * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MPL as stated above or under the terms of the GNU - * Library General Public License as published by the Free Software Foundation; - * either version 2 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more - * details. - * - * If you didn't download this code from the following link, you should check if - * you aren't using an obsolete version: - * http://www.lowagie.com/iText/ - */ +/* + * + * Copyright 2004 by Leonard Rosenthol. + * + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the License. + * + * The Original Code is 'iText, a free JAVA-PDF library'. + * + * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by + * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. + * All Rights Reserved. + * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer + * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. + * + * Contributor(s): all the names of the contributors are added in the source code + * where applicable. + * + * Alternatively, the contents of this file may be used under the terms of the + * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the + * provisions of LGPL are applicable instead of those above. If you wish to + * allow use of your version of this file only under the terms of the LGPL + * License and not to allow others to use your version of this file under + * the MPL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the LGPL. + * If you do not delete the provisions above, a recipient may use your version + * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MPL as stated above or under the terms of the GNU + * Library General Public License as published by the Free Software Foundation; + * either version 2 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more + * details. + * + * If you didn't download this code from the following link, you should check if + * you aren't using an obsolete version: + * http://www.lowagie.com/iText/ + */ namespace iTextSharp.text.pdf { - /** - * Reads a XFDF. - * @author Leonard Rosenthol (leonardr@pdfsages.com) - */ - public class XfdfReader : ISimpleXMLDocHandler { - // stuff used during parsing to handle state - private bool foundRoot = false; - private Stackr fieldNames = new Stackr(); - private Stackr fieldValues = new Stackr(); - - // storage for the field list and their values - internal Hashtable fields; - - // storage for the path to referenced PDF, if any - internal String fileSpec; - - /** Reads an XFDF form. - * @param filename the file name of the form - * @throws IOException on error - */ - public XfdfReader(String filename) { - FileStream fin = null; - try { - fin = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - SimpleXMLParser.Parse(this, fin); - } - finally { - try{if (fin != null) fin.Close();}catch{} - } - } - - /** Reads an XFDF form. - * @param xfdfIn the byte array with the form - * @throws IOException on error - */ - public XfdfReader(byte[] xfdfIn) { - SimpleXMLParser.Parse( this, new MemoryStream(xfdfIn)); - } - - /** Gets all the fields. The map is keyed by the fully qualified - * field name and the value is a merged PdfDictionary - * with the field content. - * @return all the fields - */ - public Hashtable Fields { - get { - return fields; - } - } - - /** Gets the field value. - * @param name the fully qualified field name - * @return the field's value - */ - public String GetField(String name) { - return (String)fields[name]; - } - - /** Gets the field value or null if the field does not + /** + * Reads a XFDF. + * @author Leonard Rosenthol (leonardr@pdfsages.com) + */ + public class XfdfReader : ISimpleXMLDocHandler { + // stuff used during parsing to handle state + private bool foundRoot = false; + private Stackr fieldNames = new Stackr(); + private Stackr fieldValues = new Stackr(); + + // storage for the field list and their values + internal Hashtable fields; + /** + * Storage for field values if there's more than one value for a field. + * @since 2.1.4 + */ + protected Hashtable listFields; + // storage for the path to referenced PDF, if any + internal String fileSpec; + + /** Reads an XFDF form. + * @param filename the file name of the form + * @throws IOException on error + */ + public XfdfReader(String filename) { + FileStream fin = null; + try { + fin = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + SimpleXMLParser.Parse(this, fin); + } + finally { + try{if (fin != null) fin.Close();}catch{} + } + } + + /** Reads an XFDF form. + * @param xfdfIn the byte array with the form + * @throws IOException on error + */ + public XfdfReader(byte[] xfdfIn) { + SimpleXMLParser.Parse( this, new MemoryStream(xfdfIn)); + } + + /** Gets all the fields. The map is keyed by the fully qualified + * field name and the value is a merged PdfDictionary + * with the field content. + * @return all the fields + */ + public Hashtable Fields { + get { + return fields; + } + } + + /** Gets the field value. + * @param name the fully qualified field name + * @return the field's value + */ + public String GetField(String name) { + return (String)fields[name]; + } + + /** Gets the field value or null if the field does not + * exist or has no value defined. + * @param name the fully qualified field name + * @return the field value or null + */ + public String GetFieldValue(String name) { + String field = (String)fields[name]; + if (field == null) + return null; + else + return field; + } + + /** + * Gets the field values for a list or null if the field does not * exist or has no value defined. * @param name the fully qualified field name - * @return the field value or null + * @return the field values or null + * @since 2.1.4 */ - public String GetFieldValue(String name) { - String field = (String)fields[name]; - if (field == null) - return null; - else - return field; + public ArrayList GetListValues(String name) { + return (ArrayList)listFields[name]; } - - /** Gets the PDF file specification contained in the FDF. - * @return the PDF file specification contained in the FDF - */ - public String FileSpec { - get { - return fileSpec; - } - } - - /** - * Called when a start tag is found. - * @param tag the tag name - * @param h the tag's attributes - */ - public void StartElement(String tag, Hashtable h) { - if ( !foundRoot ) { - if (!tag.Equals("xfdf")) - throw new Exception("Root element is not Bookmark."); - else - foundRoot = true; - } - - if ( tag.Equals("xfdf") ){ - - } else if ( tag.Equals("f") ) { - fileSpec = (String)h[ "href" ]; - } else if ( tag.Equals("fields") ) { - fields = new Hashtable(); // init it! - } else if ( tag.Equals("field") ) { - String fName = (String) h[ "name" ]; - fieldNames.Push( fName ); - } else if ( tag.Equals("value") ) { - fieldValues.Push(""); - } - } - /** - * Called when an end tag is found. - * @param tag the tag name - */ - public void EndElement(String tag) { - if ( tag.Equals("value") ) { - String fName = ""; - for (int k = 0; k < fieldNames.Count; ++k) { - fName += "." + (String)fieldNames[k]; - } - if (fName.StartsWith(".")) - fName = fName.Substring(1); - String fVal = (String) fieldValues.Pop(); + + /** Gets the PDF file specification contained in the FDF. + * @return the PDF file specification contained in the FDF + */ + public String FileSpec { + get { + return fileSpec; + } + } + + /** + * Called when a start tag is found. + * @param tag the tag name + * @param h the tag's attributes + */ + public void StartElement(String tag, Hashtable h) { + if ( !foundRoot ) { + if (!tag.Equals("xfdf")) + throw new Exception("Root element is not Bookmark."); + else + foundRoot = true; + } + + if ( tag.Equals("xfdf") ){ + + } else if ( tag.Equals("f") ) { + fileSpec = (String)h[ "href" ]; + } else if ( tag.Equals("fields") ) { + fields = new Hashtable(); // init it! + listFields = new Hashtable(); + } else if ( tag.Equals("field") ) { + String fName = (String) h[ "name" ]; + fieldNames.Push( fName ); + } else if ( tag.Equals("value") ) { + fieldValues.Push(""); + } + } + /** + * Called when an end tag is found. + * @param tag the tag name + */ + public void EndElement(String tag) { + if ( tag.Equals("value") ) { + String fName = ""; + for (int k = 0; k < fieldNames.Count; ++k) { + fName += "." + (String)fieldNames[k]; + } + if (fName.StartsWith(".")) + fName = fName.Substring(1); + String fVal = (String) fieldValues.Pop(); + String old = (String)fields[fName]; fields[fName] = fVal; - } - else if (tag.Equals("field") ) { - if (fieldNames.Count != 0) - fieldNames.Pop(); - } - } - - /** - * Called when the document starts to be parsed. - */ - public void StartDocument() - { - fileSpec = ""; // and this too... - } - /** - * Called after the document is parsed. - */ - public void EndDocument() - { - - } - /** - * Called when a text element is found. - * @param str the text element, probably a fragment. - */ - public void Text(String str) - { - if (fieldNames.Count == 0 || fieldValues.Count == 0) - return; - - String val = (String)fieldValues.Pop(); - val += str; - fieldValues.Push(val); - } - - internal class Stackr : ArrayList { - internal void Push(object obj) { - Add(obj); - } - - internal object Pop() { - if (Count == 0) - throw new InvalidOperationException("The stack is empty."); - object obj = this[Count - 1]; - RemoveAt(Count - 1); - return obj; - } - } - } + if (old != null) { + ArrayList l = (ArrayList) listFields[fName]; + if (l == null) { + l = new ArrayList(); + l.Add(old); + } + l.Add(fVal); + listFields[fName] = l; + } + } + else if (tag.Equals("field") ) { + if (fieldNames.Count != 0) + fieldNames.Pop(); + } + } + + /** + * Called when the document starts to be parsed. + */ + public void StartDocument() + { + fileSpec = ""; // and this too... + } + /** + * Called after the document is parsed. + */ + public void EndDocument() + { + + } + /** + * Called when a text element is found. + * @param str the text element, probably a fragment. + */ + public void Text(String str) + { + if (fieldNames.Count == 0 || fieldValues.Count == 0) + return; + + String val = (String)fieldValues.Pop(); + val += str; + fieldValues.Push(val); + } + + internal class Stackr : ArrayList { + internal void Push(object obj) { + Add(obj); + } + + internal object Pop() { + if (Count == 0) + throw new InvalidOperationException("The stack is empty."); + object obj = this[Count - 1]; + RemoveAt(Count - 1); + return obj; + } + } + } } \ No newline at end of file diff --git a/src/core/iTextSharp/text/xml/simpleparser/SimpleXMLParser.cs b/src/core/iTextSharp/text/xml/simpleparser/SimpleXMLParser.cs index aee15d2..9b0f9bb 100644 --- a/src/core/iTextSharp/text/xml/simpleparser/SimpleXMLParser.cs +++ b/src/core/iTextSharp/text/xml/simpleparser/SimpleXMLParser.cs @@ -129,6 +129,13 @@ namespace iTextSharp.text.xml.simpleparser { internal int columns = 0; /** was the last character equivalent to a newline? */ internal bool eol = false; + /** + * A boolean indicating if the next character should be taken into account + * if it's a space character. When nospace is false, the previous character + * wasn't whitespace. + * @since 2.1.5 + */ + internal bool nowhite = false; /** the current state */ internal int state; /** Are we parsing HTML? */ @@ -231,8 +238,14 @@ namespace iTextSharp.text.xml.simpleparser { SaveState(state); entity.Length = 0; state = ENTITY; - } else + } else if (Char.IsWhiteSpace((char)character)) { + if (nowhite) + text.Append((char)character); + nowhite = false; + } else { text.Append((char)character); + nowhite = true; + } break; // we have just seen a < and are wondering what we are looking at // , , , etc. @@ -658,10 +671,15 @@ namespace iTextSharp.text.xml.simpleparser { sb.Append("'"); break; default: - if (onlyASCII && c > 127) - sb.Append("&#").Append(c).Append(';'); - else - sb.Append((char)c); + if ((c == 0x9) || (c == 0xA) || (c == 0xD) + || ((c >= 0x20) && (c <= 0xD7FF)) + || ((c >= 0xE000) && (c <= 0xFFFD)) + || ((c >= 0x10000) && (c <= 0x10FFFF))) { + if (onlyASCII && c > 127) + sb.Append("&#").Append(c).Append(';'); + else + sb.Append((char)c); + } break; } } diff --git a/src/core/iTextSharp/text/xml/xmp/PdfSchema.cs b/src/core/iTextSharp/text/xml/xmp/PdfSchema.cs index 3397e3f..dfa2e9a 100644 --- a/src/core/iTextSharp/text/xml/xmp/PdfSchema.cs +++ b/src/core/iTextSharp/text/xml/xmp/PdfSchema.cs @@ -64,7 +64,7 @@ namespace iTextSharp.text.xml.xmp { /** default namespace uri*/ public const String DEFAULT_XPATH_URI = "http://ns.adobe.com/pdf/1.3/"; /** Keywords. */ - public const String KEYWORDS = "pdf:Keywords"; + public const String KEYWORDS = "pdf:keywords"; /** The PDF file version (for example: 1.0, 1.3, and so on). */ public const String VERSION = "pdf:PDFVersion"; /** The Producer. */