2498 lines
106 KiB
C#
2498 lines
106 KiB
C#
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;
|
||
}
|
||
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.AddValue(widget);
|
||
else
|
||
item.AddValue(value);
|
||
item.AddWidget(widget);
|
||
item.AddWidgetRef(annots.GetAsIndirectObject(j)); // must be a reference
|
||
if (top != null)
|
||
dic.MergeDifferent(top);
|
||
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 <CODE>null</CODE> 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;
|
||
}
|
||
}
|
||
}
|
||
for (int k = 0; k < fd.Size; ++k) {
|
||
PdfDictionary dic = fd.GetWidget( k );
|
||
dic = dic.GetAsDict(PdfName.AP);
|
||
if (dic == null)
|
||
continue;
|
||
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
|
||
* <CODE>null</CODE>.
|
||
* @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
|
||
* <CODE>null</CODE>.
|
||
* @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 <CODE>exportValues</CODE>
|
||
* or <CODE>displayValues</CODE> may be <CODE>null</CODE> but not both. This method will only
|
||
* set the list but will not set the value or appearance. For that, calling <CODE>setField()</CODE>
|
||
* is required.
|
||
* <p>
|
||
* An example:
|
||
* <p>
|
||
* <PRE>
|
||
* 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();
|
||
* </PRE>
|
||
* @param fieldName the field name
|
||
* @param exportValues the export values
|
||
* @param displayValues the display values
|
||
* @return <CODE>true</CODE> if the operation succeeded, <CODE>false</CODE> 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: <CODE>FIELD_TYPE_PUSHBUTTON</CODE>,
|
||
* <CODE>FIELD_TYPE_CHECKBOX</CODE>, <CODE>FIELD_TYPE_RADIOBUTTON</CODE>,
|
||
* <CODE>FIELD_TYPE_TEXT</CODE>, <CODE>FIELD_TYPE_LIST</CODE>,
|
||
* <CODE>FIELD_TYPE_COMBO</CODE> or <CODE>FIELD_TYPE_SIGNATURE</CODE>.
|
||
* <p>
|
||
* If the field does not exist or is invalid it returns
|
||
* <CODE>FIELD_TYPE_NONE</CODE>.
|
||
* @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;
|
||
PdfNumber ffo = merged.GetAsNumber(PdfName.FF);
|
||
if (ffo != null) {
|
||
ff = 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;
|
||
}
|
||
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 <CODE>true</CODE> if the renaming was successful, <CODE>false</CODE>
|
||
* 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) {
|
||
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 = 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;
|
||
switch (ar.Size) {
|
||
case 1:
|
||
return new GrayColor(ar.GetAsNumber(0).FloatValue);
|
||
case 3:
|
||
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(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;
|
||
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 "";
|
||
if (v is PRStream) {
|
||
try {
|
||
byte[] valBytes = PdfReader.GetStreamBytes((PRStream)v);
|
||
return PdfEncodings.ConvertToString(valBytes, BaseFont.WINANSI);
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
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();
|
||
} 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
|
||
* @return the field value
|
||
* @since 2.1.3
|
||
*/
|
||
public String[] GetListSelection(String name) {
|
||
String[] ret;
|
||
String s = GetField(name);
|
||
if (s == null) {
|
||
ret = new String[]{};
|
||
}
|
||
else {
|
||
ret = new String[]{ s };
|
||
}
|
||
Item item = (Item)fields[name];
|
||
if (item == null)
|
||
return ret;
|
||
PdfArray values = item.GetMerged(0).GetAsArray(PdfName.I);
|
||
if (values == null)
|
||
return ret;
|
||
ret = new String[values.Size];
|
||
String[] options = GetListOptionExport(name);
|
||
int idx = 0;
|
||
foreach (PdfNumber n in values.ArrayList) {
|
||
ret[idx++] = options[n.IntValue];
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* Sets a field property. Valid property names are:
|
||
* <p>
|
||
* <ul>
|
||
* <li>textfont - sets the text font. The value for this entry is a <CODE>BaseFont</CODE>.<br>
|
||
* <li>textcolor - sets the text color. The value for this entry is a <CODE>java.awt.Color</CODE>.<br>
|
||
* <li>textsize - sets the text size. The value for this entry is a <CODE>Float</CODE>.
|
||
* <li>bgcolor - sets the background color. The value for this entry is a <CODE>java.awt.Color</CODE>.
|
||
* If <code>null</code> removes the background.<br>
|
||
* <li>bordercolor - sets the border color. The value for this entry is a <CODE>java.awt.Color</CODE>.
|
||
* If <code>null</code> removes the border.<br>
|
||
* </ul>
|
||
* @param field the field name
|
||
* @param name the property name
|
||
* @param value the property value
|
||
* @param inst an array of <CODE>int</CODE> indexing into <CODE>AcroField.Item.merged</CODE> elements to process.
|
||
* Set to <CODE>null</CODE> to process all
|
||
* @return <CODE>true</CODE> if the property exists, <CODE>false</CODE> 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.Size; ++k) {
|
||
if (hit.IsHit(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:
|
||
* <p>
|
||
* <ul>
|
||
* <li>flags - a set of flags specifying various characteristics of the field<6C>s widget annotation.
|
||
* The value of this entry replaces that of the F entry in the form<72>s corresponding annotation dictionary.<br>
|
||
* <li>setflags - a set of flags to be set (turned on) in the F entry of the form<72>s corresponding
|
||
* widget annotation dictionary. Bits equal to 1 cause the corresponding bits in F to be set to 1.<br>
|
||
* <li>clrflags - a set of flags to be cleared (turned off) in the F entry of the form<72>s corresponding
|
||
* widget annotation dictionary. Bits equal to 1 cause the corresponding
|
||
* bits in F to be set to 0.<br>
|
||
* <li>fflags - a set of flags specifying various characteristics of the field. The value
|
||
* of this entry replaces that of the Ff entry in the form<72>s corresponding field dictionary.<br>
|
||
* <li>setfflags - a set of flags to be set (turned on) in the Ff entry of the form<72>s corresponding
|
||
* field dictionary. Bits equal to 1 cause the corresponding bits in Ff to be set to 1.<br>
|
||
* <li>clrfflags - a set of flags to be cleared (turned off) in the Ff entry of the form<72>s corresponding
|
||
* field dictionary. Bits equal to 1 cause the corresponding bits in Ff
|
||
* to be set to 0.<br>
|
||
* </ul>
|
||
* @param field the field name
|
||
* @param name the property name
|
||
* @param value the property value
|
||
* @param inst an array of <CODE>int</CODE> indexing into <CODE>AcroField.Item.merged</CODE> elements to process.
|
||
* Set to <CODE>null</CODE> to process all
|
||
* @return <CODE>true</CODE> if the property exists, <CODE>false</CODE> 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);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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 <CODE>true</CODE> if the field was found and changed,
|
||
* <CODE>false</CODE> 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 <CODE>true</CODE> if the field was found and changed,
|
||
* <CODE>false</CODE> 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 <CODE>null</CODE>
|
||
* the <CODE>value</CODE> parameter will be used
|
||
* @return <CODE>true</CODE> if the field was found and changed,
|
||
* <CODE>false</CODE> 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 = 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;
|
||
}
|
||
|
||
/**
|
||
* 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 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));
|
||
}
|
||
}
|
||
}
|
||
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 <CODE>AcroFields.Item</CODE>.
|
||
* @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 <CODE>null</CODE> 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 <CODE>float</CODE>
|
||
* 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 <CODE>null</CODE> 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 array.Size;
|
||
PdfIndirectReference refi = (PdfIndirectReference)refo;
|
||
for (int j = 0; j < array.Size; ++j) {
|
||
PdfObject obj = array[j];
|
||
if (!obj.IsIndirect())
|
||
continue;
|
||
if (((PdfIndirectReference)obj).Number == refi.Number)
|
||
array.Remove(j--);
|
||
}
|
||
return array.Size;
|
||
}
|
||
|
||
/**
|
||
* Removes all the fields from <CODE>page</CODE>.
|
||
* @param page the page to remove the fields from
|
||
* @return <CODE>true</CODE> if any field was removed, <CODE>false otherwise</CODE>
|
||
*/
|
||
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
|
||
* <CODE>name</CODE> 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 <CODE>true</CODE> if the field exists, <CODE>false otherwise</CODE>
|
||
*/
|
||
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.Size; ++k) {
|
||
int pageV = item.GetPage(k);
|
||
if (page != -1 && page != pageV)
|
||
continue;
|
||
PdfIndirectReference refi = item.GetWidgetRef(k);
|
||
PdfDictionary wd = item.GetWidget( k );
|
||
PdfDictionary pageDic = reader.GetPageN(pageV);
|
||
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 <CODE>true</CODE> if the field exists, <CODE>false otherwise</CODE>
|
||
*/
|
||
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 <CODE>true</CODE>.
|
||
* @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 {
|
||
|
||
/**
|
||
* <CODE>writeToAll</CODE> constant.
|
||
*
|
||
* @since 2.1.5
|
||
*/
|
||
public const int WRITE_MERGED = 1;
|
||
|
||
/**
|
||
* <CODE>writeToAll</CODE> and <CODE>markUsed</CODE> constant.
|
||
*
|
||
* @since 2.1.5
|
||
*/
|
||
public const int WRITE_WIDGET = 2;
|
||
|
||
/**
|
||
* <CODE>writeToAll</CODE> and <CODE>markUsed</CODE> 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 <code>writeFlags</code> 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);
|
||
}
|
||
}
|
||
if ((writeFlags & WRITE_WIDGET) != 0) {
|
||
for (i = 0; i < widgets.Count; ++i) {
|
||
curDict = GetWidget(i);
|
||
curDict.Put(key, value);
|
||
}
|
||
}
|
||
if ((writeFlags & WRITE_VALUE) != 0) {
|
||
for (i = 0; i < values.Count; ++i) {
|
||
curDict = GetValue(i);
|
||
curDict.Put(key, value);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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));
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* An array of <CODE>PdfDictionary</CODE> where the value tag /V
|
||
* is present.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList values = new ArrayList();
|
||
|
||
/**
|
||
* An array of <CODE>PdfDictionary</CODE> with the widgets.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList widgets = new ArrayList();
|
||
|
||
/**
|
||
* An array of <CODE>PdfDictionary</CODE> with the widget references.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList widget_refs = new ArrayList();
|
||
|
||
/**
|
||
* An array of <CODE>PdfDictionary</CODE> with all the field
|
||
* and widget tags merged.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList merged = new ArrayList();
|
||
|
||
/**
|
||
* An array of <CODE>Integer</CODE> with the page numbers where
|
||
* the widgets are displayed.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList page = new ArrayList();
|
||
/**
|
||
* An array of <CODE>Integer</CODE> with the tab order of the field in the page.
|
||
*
|
||
* @deprecated (will remove 'public' in the future)
|
||
*/
|
||
public ArrayList tabOrder = new ArrayList();
|
||
|
||
/**
|
||
* 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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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;
|
||
PdfDictionary v = merged.GetAsDict(PdfName.V);
|
||
if (v == null)
|
||
continue;
|
||
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 <CODE>null</CODE> 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 <CODE>true</CODE> if the signature covers the entire document,
|
||
* <CODE>false</CODE> 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:
|
||
* <p>
|
||
* <pre>
|
||
* 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]);
|
||
* }
|
||
* </pre>
|
||
* @param name the signature field name
|
||
* @return a <CODE>PdfPKCS7</CODE> 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];
|
||
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 <CODE>field</CODE> 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 <CODE>Stream</CODE> covering the revision. Returns <CODE>null</CODE> 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:
|
||
* <p>
|
||
* <pre>
|
||
* 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();
|
||
* }
|
||
* </pre>
|
||
* @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 <CODE>BaseFont</CODE> and can also be <CODE>null</CODE>. 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 <CODE>null</CODE> 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 <CODE>null</CODE> 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 = item.GetMerged(order);
|
||
DecodeGenericDictionary(dic, newButton);
|
||
PdfDictionary mk = dic.GetAsDict(PdfName.MK);
|
||
if (mk != null) {
|
||
PdfString text = mk.GetAsString(PdfName.CA);
|
||
if (text != null)
|
||
newButton.Text = text.ToUnicodeString();
|
||
PdfNumber tp = mk.GetAsNumber(PdfName.TP);
|
||
if (tp != null)
|
||
newButton.Layout = tp.IntValue + 1;
|
||
PdfDictionary ifit = mk.GetAsDict(PdfName.IF);
|
||
if (ifit != null) {
|
||
PdfName sw = ifit.GetAsName(PdfName.SW);
|
||
if (sw != null) {
|
||
int scale = PushbuttonField.SCALE_ICON_ALWAYS;
|
||
if (sw.Equals(PdfName.B))
|
||
scale = PushbuttonField.SCALE_ICON_IS_TOO_BIG;
|
||
else if (sw.Equals(PdfName.S))
|
||
scale = PushbuttonField.SCALE_ICON_IS_TOO_SMALL;
|
||
else if (sw.Equals(PdfName.N))
|
||
scale = PushbuttonField.SCALE_ICON_NEVER;
|
||
newButton.ScaleIcon = scale;
|
||
}
|
||
sw = ifit.GetAsName(PdfName.S);
|
||
if (sw != null) {
|
||
if (sw.Equals(PdfName.A))
|
||
newButton.ProportionalIcon = false;
|
||
}
|
||
PdfArray aj = ifit.GetAsArray(PdfName.A);
|
||
if (aj != null && aj.Size == 2) {
|
||
float left = aj.GetAsNumber(0).FloatValue;
|
||
float bottom = aj.GetAsNumber(1).FloatValue;
|
||
newButton.IconHorizontalAdjustment = left;
|
||
newButton.IconVerticalAdjustment = bottom;
|
||
}
|
||
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 <CODE>PdfFormField</CODE> representing the pushbutton
|
||
* @return <CODE>true</CODE> if the field was replaced, <CODE>false</CODE> 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 <CODE>PdfFormField</CODE> representing the pushbutton
|
||
* @param order the field order in fields with same name
|
||
* @return <CODE>true</CODE> if the field was replaced, <CODE>false</CODE> 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;
|
||
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;
|
||
}
|
||
}
|
||
}
|