class HexaPDF::Type::AcroForm::Field
AcroForm
field dictionaries are used to define the properties of form fields of AcroForm
objects.
Fields can be organized in a hierarchy using the /Kids and /Parent keys, for namespacing purposes and to set default values. Those fields that have other fields as children are called non-terminal fields, otherwise they are called terminal fields.
While field objects can be created manually, it is best to use the various create_
methods of the main Form
object to create them so that all necessary things are set up correctly.
Field
Types¶ ↑
Subclasses are used to implement the specific AcroForm
field types:
-
ButtonField
implements the button fields (pushbuttons, check boxes and radio buttons) -
TextField
implements single or multiline text fields. -
ChoiceField
implements scrollable list boxes or (editable) combo boxes. -
SignatureField
implements signature fields.
Field
Flags¶ ↑
Various characteristics of a field can be changed by setting a certain flag. Some flags are defined for all types of field, some are specific to a certain type.
The following flags apply to all fields:
- :read_only
-
The field is read only which means the user can't change the value or interact with associated widget annotations.
- :required
-
The field is required if the form is exported by a submit-form action.
- :no_export
-
The field should not be exported by a submit-form action.
Field
Type
Implementation Notes¶ ↑
If an AcroForm
field type adds additional inheritable dictionary fields, it has to set the constant INHERITABLE_FIELDS
to all inheritable dictionary fields, including those from the superclass.
Similarily, if additional flags are provided, the constant FLAGS_BIT_MAPPING
has to be set to combination of the superclass value of the constant and the mapping of flag names to bit indices.
See: PDF1.7 s12.7.3.1
Constants
- INHERITABLE_FIELDS
The inheritable dictionary fields common to all
AcroForm
field types.- WIDGET_FIELDS
An array of all widget annotation field names.
Public Class Methods
Treats name
as an inheritable dictionary field and resolves its value for the AcroForm
field field
.
# File lib/hexapdf/type/acro_form/field.rb, line 150 def self.inherited_value(field, name) while field.value[name].nil? && (parent = field[:Parent]) field = parent end field.value[name].nil? ? nil : field[name] end
Public Instance Methods
Returns the value for the entry name
.
If name
is an inheritable field and the value has not been set on this field object, its value is retrieved from the parent fields.
See: Dictionary#[]
HexaPDF::Dictionary#[]
# File lib/hexapdf/type/acro_form/field.rb, line 168 def [](name) if value[name].nil? && self.class::INHERITABLE_FIELDS.include?(name) self.class.inherited_value(self, name) || super else super end end
Returns the alternate field name that should be used for display purposes (e.g. Acrobat shows this as tool tip).
# File lib/hexapdf/type/acro_form/field.rb, line 220 def alternate_field_name self[:TU] end
Sets the alternate field name.
# File lib/hexapdf/type/acro_form/field.rb, line 227 def alternate_field_name=(value) self[:TU] = value end
Returns the concrete field type (:button_field, :text_field, :choice_field or :signature_field) or nil
is no field type is set.
In constrast to field_type
this method also considers the field flags and not just the field type. This means that subclasses can return a more concrete name for the field type.
Also see field_type
# File lib/hexapdf/type/acro_form/field.rb, line 191 def concrete_field_type case self[:FT] when :Btn then :button_field when :Tx then :text_field when :Ch then :choice_field when :Sig then :signature_field else nil end end
Creates a new widget annotation for this form field (must be a terminal field!) on the given page
, adding the values
to the created widget annotation oject.
If allow_embedded
is false
, embedding the first widget in the field itself is not allowed.
The values
argument should at least include :Rect for setting the visible area of the widget.
If the field already has an embedded widget, i.e. field and widget are the same PDF object, its widget data is extracted to a new PDF object and stored in the /Kids field, together with the new widget annotation. Note that this means that a possible reference to the formerly embedded widget (=this field) is not valid anymore!
See: HexaPDF::Type::Annotations::Widget
# File lib/hexapdf/type/acro_form/field.rb, line 275 def create_widget(page, allow_embedded: true, **values) unless terminal_field? raise HexaPDF::Error, "Widgets can only be added to terminal fields" end widget_data = {Type: :Annot, Subtype: :Widget, Rect: [0, 0, 0, 0], **values} if !allow_embedded || embedded_widget? || (key?(:Kids) && !self[:Kids].empty?) kids = self[:Kids] ||= [] kids << extract_widget if embedded_widget? widget = document.add(widget_data) widget[:Parent] = self self[:Kids] << widget else value.update(widget_data) widget = document.wrap(self) end (page[:Annots] ||= []) << widget widget end
Deletes the given widget annotation object from this field, the page it appears on and the document.
If the given widget is not a widget of this field, nothing is done.
# File lib/hexapdf/type/acro_form/field.rb, line 302 def delete_widget(widget) widget = if embedded_widget? && self == widget widget elsif terminal_field? (widget_index = self[:Kids]&.index(widget)) && widget end return unless widget document.pages.each do |page| break if page[:Annots]&.delete(widget) # See comment in #extract_widget end if embedded_widget? WIDGET_FIELDS.each {|key| delete(key) } document.revisions.each {|revision| break if revision.update(self)} else self[:Kids].delete_at(widget_index) document.delete(widget) end end
Yields each widget, i.e. visual representation, of this field.
See: HexaPDF::Type::Annotations::Widget
# File lib/hexapdf/type/acro_form/field.rb, line 250 def each_widget # :yields: widget return to_enum(__method__) unless block_given? if embedded_widget? yield(document.wrap(self)) elsif terminal_field? self[:Kids]&.each {|kid| yield(document.wrap(kid)) } end self end
Returns true
if the field contains an embedded widget.
# File lib/hexapdf/type/acro_form/field.rb, line 239 def embedded_widget? key?(:Subtype) end
Returns the name of the field or nil
if no name is set.
# File lib/hexapdf/type/acro_form/field.rb, line 202 def field_name self[:T] end
Returns the type of the field, either :Btn (pushbuttons, check boxes, radio buttons), :Tx (text fields), :Ch (scrollable list boxes, combo boxes) or :Sig (signature fields).
Also see concrete_field_type
# File lib/hexapdf/type/acro_form/field.rb, line 180 def field_type self[:FT] end
Sets the given flags, given as flag names or bit indices. If clear_existing
is true
, all prior flags will be cleared.
# File lib/hexapdf/type/acro_form/field.rb, line 144 bit_field(:flags, {read_only: 0, required: 1, no_export: 2}, lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag", value_getter: "self[:Ff]", value_setter: "self[:Ff]")
Returns true
if the given flag is set. The argument can either be the flag name or the bit index.
# File lib/hexapdf/type/acro_form/field.rb, line 128
Returns an array of flag names representing the set bit flags.
# File lib/hexapdf/type/acro_form/field.rb, line 122
Returns the full name of the field or nil
if no name is set.
The full name of a field is constructed using the full name of the parent field, a period and the field name of the field.
# File lib/hexapdf/type/acro_form/field.rb, line 210 def full_field_name if key?(:Parent) [self[:Parent].full_field_name, field_name].compact.join('.') else field_name end end
Form
fields must always be indirect objects.
# File lib/hexapdf/type/acro_form/field.rb, line 158 def must_be_indirect? true end
Returns true
if this is a terminal field.
# File lib/hexapdf/type/acro_form/field.rb, line 232 def terminal_field? kids = self[:Kids] # PDF 2.0 s12.7.4.2 clarifies how to do check for fields since PDF 1.7 isn't clear kids.nil? || kids.empty? || kids.none? {|kid| kid.key?(:T) } end
Private Instance Methods
Returns a new dictionary object with all the widget annotation data that is stored directly in the field and adjust the references accordingly. If the field doesn't have any widget data, nil
is returned.
# File lib/hexapdf/type/acro_form/field.rb, line 332 def extract_widget return unless embedded_widget? data = WIDGET_FIELDS.each_with_object({}) do |key, hash| hash[key] = delete(key) if key?(key) end widget = document.add(data, type: :Annot) widget[:Parent] = self document.pages.each do |page| if page.key?(:Annots) && (index = page[:Annots].index(self)) page[:Annots][index] = widget break # Each annotation dictionary may only appear on one page, see PDF1.7 12.5.2 end end widget end