#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Stefan Lange
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using PdfSharp.Pdf.Advanced;
namespace PdfSharp.Pdf.AcroForms
{
///
/// Represents the base class for all interactive field dictionaries.
///
public abstract class PdfAcroField : PdfDictionary
{
///
/// Initializes a new instance of PdfAcroField.
///
internal PdfAcroField(PdfDocument document)
: base(document)
{ }
///
/// Initializes a new instance of the class. Used for type transformation.
///
protected PdfAcroField(PdfDictionary dict)
: base(dict)
{ }
///
/// Gets the name of this field.
///
public string Name
{
get
{
string name = Elements.GetString(Keys.T);
return name;
}
}
///
/// Gets the field flags of this instance.
///
public PdfAcroFieldFlags Flags
{
// TODO: This entry is inheritable, thus the implementation is incorrect...
get { return (PdfAcroFieldFlags)Elements.GetInteger(Keys.Ff); }
}
internal PdfAcroFieldFlags SetFlags
{
get { return (PdfAcroFieldFlags)Elements.GetInteger(Keys.Ff); }
set { Elements.SetInteger(Keys.Ff, (int)value); }
}
///
/// Gets or sets the value of the field.
///
public virtual PdfItem Value
{
get { return Elements[Keys.V]; }
set
{
if (ReadOnly)
throw new InvalidOperationException("The field is read only.");
if (value is PdfString || value is PdfName)
Elements[Keys.V] = value;
else
throw new NotImplementedException("Values other than string cannot be set.");
}
}
///
/// Gets or sets a value indicating whether the field is read only.
///
public bool ReadOnly
{
get { return (Flags & PdfAcroFieldFlags.ReadOnly) != 0; }
set
{
if (value)
SetFlags |= PdfAcroFieldFlags.ReadOnly;
else
SetFlags &= ~PdfAcroFieldFlags.ReadOnly;
}
}
///
/// Gets the field with the specified name.
///
public PdfAcroField this[string name]
{
get { return GetValue(name); }
}
///
/// Gets a child field by name.
///
protected virtual PdfAcroField GetValue(string name)
{
if (String.IsNullOrEmpty(name))
return this;
if (HasKids)
return Fields.GetValue(name);
return null;
}
///
/// Indicates whether the field has child fields.
///
public bool HasKids
{
get
{
PdfItem item = Elements[Keys.Kids];
if (item == null)
return false;
if (item is PdfArray)
return ((PdfArray)item).Elements.Count > 0;
return false;
}
}
///
/// Gets the names of all descendants of this field.
///
[Obsolete("Use GetDescendantNames")]
public string[] DescendantNames // Properties should not return arrays.
{
get { return GetDescendantNames(); }
}
///
/// Gets the names of all descendants of this field.
///
public string[] GetDescendantNames()
{
List names = new List();
if (HasKids)
{
PdfAcroFieldCollection fields = Fields;
fields.GetDescendantNames(ref names, null);
}
List temp = new List();
foreach (string name in names)
temp.Add(name);
return temp.ToArray();
}
///
/// Gets the names of all appearance dictionaries of this AcroField.
///
public string[] GetAppearanceNames()
{
Dictionary names = new Dictionary();
PdfDictionary dict = Elements["/AP"] as PdfDictionary;
if (dict != null)
{
AppDict(dict, names);
if (HasKids)
{
PdfItem[] kids = Fields.Elements.Items;
foreach (PdfItem pdfItem in kids)
{
if (pdfItem is PdfReference)
{
PdfDictionary xxx = ((PdfReference)pdfItem).Value as PdfDictionary;
if (xxx != null)
AppDict(xxx, names);
}
}
//((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1);
}
}
string[] array = new string[names.Count];
names.Keys.CopyTo(array, 0);
return array;
}
//static string[] AppearanceNames(PdfDictionary dictIn)
//{
// Dictionary names = new Dictionary();
// PdfDictionary dict = dictIn["/AP"] as PdfDictionary;
// if (dict != null)
// {
// AppDict(dict, names);
// if (HasKids)
// {
// PdfItem[] kids = Fields.Elements.Items;
// foreach (PdfItem pdfItem in kids)
// {
// if (pdfItem is PdfReference)
// {
// PdfDictionary xxx = ((PdfReference)pdfItem).Value as PdfDictionary;
// if (xxx != null)
// AppDict(xxx, names);
// }
// }
// //((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1);
// }
// }
// string[] array = new string[names.Count];
// names.Keys.CopyTo(array, 0);
// return array;
//}
static void AppDict(PdfDictionary dict, Dictionary names)
{
PdfDictionary sub;
if ((sub = dict.Elements["/D"] as PdfDictionary) != null)
AppDict2(sub, names);
if ((sub = dict.Elements["/N"] as PdfDictionary) != null)
AppDict2(sub, names);
}
static void AppDict2(PdfDictionary dict, Dictionary names)
{
foreach (string key in dict.Elements.Keys)
{
if (!names.ContainsKey(key))
names.Add(key, null);
}
}
internal virtual void GetDescendantNames(ref List names, string partialName)
{
if (HasKids)
{
PdfAcroFieldCollection fields = Fields;
string t = Elements.GetString(Keys.T);
Debug.Assert(t != "");
if (t.Length > 0)
{
if (!String.IsNullOrEmpty(partialName))
partialName += "." + t;
else
partialName = t;
fields.GetDescendantNames(ref names, partialName);
}
}
else
{
string t = Elements.GetString(Keys.T);
Debug.Assert(t != "");
if (t.Length > 0)
{
if (!String.IsNullOrEmpty(partialName))
names.Add(partialName + "." + t);
else
names.Add(t);
}
}
}
///
/// Gets the collection of fields within this field.
///
public PdfAcroFieldCollection Fields
{
get
{
if (_fields == null)
{
object o = Elements.GetValue(Keys.Kids, VCF.CreateIndirect);
_fields = (PdfAcroFieldCollection)o;
}
return _fields;
}
}
PdfAcroFieldCollection _fields;
///
/// Holds a collection of interactive fields.
///
public sealed class PdfAcroFieldCollection : PdfArray
{
PdfAcroFieldCollection(PdfArray array)
: base(array)
{ }
///
/// Gets the number of elements in the array.
///
public int Count
{
get
{
return Elements.Count;
}
}
///
/// Gets the names of all fields in the collection.
///
public string[] Names
{
get
{
int count = Elements.Count;
string[] names = new string[count];
for (int idx = 0; idx < count; idx++)
names[idx] = ((PdfDictionary)((PdfReference)Elements[idx]).Value).Elements.GetString(Keys.T);
return names;
}
}
///
/// Gets an array of all descendant names.
///
public string[] DescendantNames
{
get
{
List names = new List();
GetDescendantNames(ref names, null);
//List temp = new List();
//foreach (PdfName name in names)
// temp.Add(name.ToString());
return names.ToArray();
}
}
internal void GetDescendantNames(ref List names, string partialName)
{
int count = Elements.Count;
for (int idx = 0; idx < count; idx++)
{
PdfAcroField field = this[idx];
if (field != null)
field.GetDescendantNames(ref names, partialName);
}
}
///
/// Gets a field from the collection. For your convenience an instance of a derived class like
/// PdfTextField or PdfCheckBox is returned if PDFsharp can guess the actual type of the dictionary.
/// If the actual type cannot be guessed by PDFsharp the function returns an instance
/// of PdfGenericField.
///
public PdfAcroField this[int index]
{
get
{
PdfItem item = Elements[index];
Debug.Assert(item is PdfReference);
PdfDictionary dict = ((PdfReference)item).Value as PdfDictionary;
Debug.Assert(dict != null);
PdfAcroField field = dict as PdfAcroField;
if (field == null && dict != null)
{
// Do type transformation
field = CreateAcroField(dict);
//Elements[index] = field.XRef;
}
return field;
}
}
///
/// Gets the field with the specified name.
///
public PdfAcroField this[string name]
{
get { return GetValue(name); }
}
internal PdfAcroField GetValue(string name)
{
if (String.IsNullOrEmpty(name))
return null;
int dot = name.IndexOf('.');
string prefix = dot == -1 ? name : name.Substring(0, dot);
string suffix = dot == -1 ? "" : name.Substring(dot + 1);
int count = Elements.Count;
for (int idx = 0; idx < count; idx++)
{
PdfAcroField field = this[idx];
if (field.Name == prefix)
return field.GetValue(suffix);
}
return null;
}
///
/// Create a derived type like PdfTextField or PdfCheckBox if possible.
/// If the actual cannot be guessed by PDFsharp the function returns an instance
/// of PdfGenericField.
///
PdfAcroField CreateAcroField(PdfDictionary dict)
{
string ft = dict.Elements.GetName(Keys.FT);
PdfAcroFieldFlags flags = (PdfAcroFieldFlags)dict.Elements.GetInteger(Keys.Ff);
switch (ft)
{
case "/Btn":
if ((flags & PdfAcroFieldFlags.Pushbutton) != 0)
return new PdfPushButtonField(dict);
if ((flags & PdfAcroFieldFlags.Radio) != 0)
return new PdfRadioButtonField(dict);
return new PdfCheckBoxField(dict);
case "/Tx":
return new PdfTextField(dict);
case "/Ch":
if ((flags & PdfAcroFieldFlags.Combo) != 0)
return new PdfComboBoxField(dict);
else
return new PdfListBoxField(dict);
case "/Sig":
return new PdfSignatureField(dict);
default:
return new PdfGenericField(dict);
}
}
}
///
/// Predefined keys of this dictionary.
/// The description comes from PDF 1.4 Reference.
///
public class Keys : KeysBase
{
// ReSharper disable InconsistentNaming
///
/// (Required for terminal fields; inheritable) The type of field that this dictionary
/// describes:
/// Btn Button
/// Tx Text
/// Ch Choice
/// Sig (PDF 1.3) Signature
/// Note: This entry may be present in a nonterminal field (one whose descendants
/// are themselves fields) in order to provide an inheritable FT value. However, a
/// nonterminal field does not logically have a type of its own; it is merely a container
/// for inheritable attributes that are intended for descendant terminal fields of
/// any type.
///
[KeyInfo(KeyType.Name | KeyType.Required)]
public const string FT = "/FT";
///
/// (Required if this field is the child of another in the field hierarchy; absent otherwise)
/// The field that is the immediate parent of this one (the field, if any, whose Kids array
/// includes this field). A field can have at most one parent; that is, it can be included
/// in the Kids array of at most one other field.
///
[KeyInfo(KeyType.Dictionary)]
public const string Parent = "/Parent";
///
/// (Optional) An array of indirect references to the immediate children of this field.
///
[KeyInfo(KeyType.Array | KeyType.Optional, typeof(PdfAcroFieldCollection))]
public const string Kids = "/Kids";
///
/// (Optional) The partial field name.
///
[KeyInfo(KeyType.TextString | KeyType.Optional)]
public const string T = "/T";
///
/// (Optional; PDF 1.3) An alternate field name, to be used in place of the actual
/// field name wherever the field must be identified in the user interface (such as
/// in error or status messages referring to the field). This text is also useful
/// when extracting the document’s contents in support of accessibility to disabled
/// users or for other purposes.
///
[KeyInfo(KeyType.TextString | KeyType.Optional)]
public const string TU = "/TU";
///
/// (Optional; PDF 1.3) The mapping name to be used when exporting interactive form field
/// data from the document.
///
[KeyInfo(KeyType.TextString | KeyType.Optional)]
public const string TM = "/TM";
///
/// (Optional; inheritable) A set of flags specifying various characteristics of the field.
/// Default value: 0.
///
[KeyInfo(KeyType.Integer | KeyType.Optional)]
public const string Ff = "/Ff";
///
/// (Optional; inheritable) The field’s value, whose format varies depending on
/// the field type; see the descriptions of individual field types for further information.
///
[KeyInfo(KeyType.Various | KeyType.Optional)]
public const string V = "/V";
///
/// (Optional; inheritable) The default value to which the field reverts when a
/// reset-form action is executed. The format of this value is the same as that of V.
///
[KeyInfo(KeyType.Various | KeyType.Optional)]
public const string DV = "/DV";
///
/// (Optional; PDF 1.2) An additional-actions dictionary defining the field’s behavior
/// in response to various trigger events. This entry has exactly the same meaning as
/// the AA entry in an annotation dictionary.
///
[KeyInfo(KeyType.Dictionary | KeyType.Optional)]
public const string AA = "/AA";
// ----- Additional entries to all fields containing variable text --------------------------
///
/// (Required; inheritable) A resource dictionary containing default resources
/// (such as fonts, patterns, or color spaces) to be used by the appearance stream.
/// At a minimum, this dictionary must contain a Font entry specifying the resource
/// name and font dictionary of the default font for displaying the field’s text.
///
[KeyInfo(KeyType.Dictionary | KeyType.Required)]
public const string DR = "/DR";
///
/// (Required; inheritable) The default appearance string, containing a sequence of
/// valid page-content graphics or text state operators defining such properties as
/// the field’s text size and color.
///
[KeyInfo(KeyType.String | KeyType.Required)]
public const string DA = "/DA";
///
/// (Optional; inheritable) A code specifying the form of quadding (justification)
/// to be used in displaying the text:
/// 0 Left-justified
/// 1 Centered
/// 2 Right-justified
/// Default value: 0 (left-justified).
///
[KeyInfo(KeyType.Integer | KeyType.Optional)]
public const string Q = "/Q";
// ReSharper restore InconsistentNaming
}
}
}