class HexaPDF::Type::AcroForm::Field
- See: PDF2.0 s12.7.4.1
indices.
to combination of the superclass value of the constant and the mapping of flag names to bit
Similarily, if additional flags are provided, the constantFLAGS_BIT_MAPPING
has to be set
superclass.
constantINHERITABLE_FIELDS
to all inheritable dictionary fields, including those from the
If an AcroForm field type adds additional inheritable dictionary fields, it has to set the
== Field Type Implementation Notes
Also see the class description of the subclasses for additional, type specific field flags.
:no_export - The field should not be exported by a submit-form action.
:required - The field is required if the form is exported by a submit-form action.
with associated widget annotations.
:read_only -
The field is read only which means the user can’t change the value or interact
The following flags apply to all fields:
defined for all types of field, some are specific to a certain type.
Various characteristics of a field can be changed by setting a certain flag. Some flags are
== Field Flags
* SignatureField implements signature fields.
* ChoiceField implements scrollable list boxes or (editable) combo boxes.
* TextField implements single or multiline text fields.
* ButtonField implements the button fields (pushbuttons, check boxes and radio buttons)
Subclasses are used to implement the specific AcroForm field types:
== Field Types
of the main Form object to create them so that all necessary things are set up correctly.
While field objects can be created manually, it is best to use the variouscreate_
methods
called non-terminal fields, otherwise they are called terminal fields.
purposes and to set default values. Those fields that have other fields as children are
Fields can be organized in a hierarchy using the /Kids and /Parent keys, for namespacing
objects.
AcroForm field dictionaries are used to define the properties of form fields of AcroForm
- The field is required if the form is exported by a submit-form action.
- The field should not be exported by a submit-form action.
def self.inherited_value(field, name)
Treats +name+ as an inheritable dictionary field and resolves its value for the AcroForm
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
def self.wrap(document, field)
Wraps the given +field+ object inside the correct field class and returns the wrapped
def self.wrap(document, field) document.wrap(field, type: :XXAcroFormField, subtype: inherited_value(field, :FT)) end
def [](name)
value is retrieved from the parent fields.
If +name+ is an inheritable field and the value has not been set on this field object, its
Returns the value for the entry +name+.
def [](name) if value[name].nil? && self.class::INHERITABLE_FIELDS.include?(name) self.class.inherited_value(self, name) || super else super end end
def alternate_field_name
Returns the alternate field name that should be used for display purposes (e.g. Acrobat
def alternate_field_name self[:TU] end
def alternate_field_name=(value)
Sets the alternate field name.
def alternate_field_name=(value) self[:TU] = value end
def concrete_field_type
field type. This means that subclasses can return a more concrete name for the field type.
In constrast to #field_type this method also considers the field flags and not just the
:signature_field) or +nil+ is no field type is set.
Returns the concrete field type (:button_field, :text_field, :choice_field or
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
def create_widget(page, allow_embedded: true, **values)
the formerly embedded widget (=this field) is not valid anymore!
together with the new widget annotation. Note that this means that a possible reference to
object, its widget data is extracted to a new PDF object and stored in the /Kids field,
If the field already has an embedded widget, i.e. field and widget are the same PDF
widget.
The +values+ argument should at least include :Rect for setting the visible area of the
allowed.
If +allow_embedded+ is +false+, embedding the first widget in the field itself is not
given +page+, adding the +values+ to the created widget annotation object.
Creates a new widget annotation for this form field (must be a terminal field!) on the
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 widget.flag(:print) widget[:P] = page (page[:Annots] ||= []) << widget widget end
def delete_widget(widget)
document.
Deletes the given widget annotation object from this field, the page it appears on and the
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
def each_widget(direct_only: true, &block) # :yields: widget
See: HexaPDF::Type::Annotations::Widget
same full field name.
fields of the form have to be searched to check whether there is another field with the
*Note*: Setting +direct_only+ to +false+ will have a severe performance impact since all
reduce case 3 to case 2).
the main AcroForm object (HexaPDF::Document#acro_form) before using this method (this will
If case 3 also needs to be handled, set +direct_only+ to +false+ or run the validation on
With the default of +direct_only+ being +true+, only the usual cases 1 and 2 are handled/
3. Widgets of *another field instance with the same full field name*.
2. One or more widgets are defined as children of this field.
1. The widget can be embedded in the field itself.
Widgets can be associated to the field in three ways:
Yields each widget, i.e. visual representation, of this field.
field.each_widget(direct_only: true) -> Enumerator
field.each_widget(direct_only: true) {|widget| block} -> field
:call-seq:
def each_widget(direct_only: true, &block) # :yields: widget return to_enum(__method__, direct_only: direct_only) unless block_given? if embedded_widget? yield(document.wrap(self)) elsif terminal_field? self[:Kids]&.each {|kid| yield(document.wrap(kid)) } end unless direct_only my_name = full_field_name document.acro_form&.each_field do |field| next if field.full_field_name != my_name || field == self field.each_widget(direct_only: true, &block) end end self end
def embedded_widget?
def embedded_widget? key?(:Subtype) end
def extract_widget
directly in the field and adjust the references accordingly. If the field doesn't have any
Returns a new dictionary object with all the widget annotation data that is stored
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 PDF2.0 12.5.2 end end document.revisions.current.update(self) widget end
def field_name
def field_name self[:T] end
def field_type
(text fields), :Ch (scrollable list boxes, combo boxes) or :Sig (signature fields).
Returns the type of the field, either :Btn (pushbuttons, check boxes, radio buttons), :Tx
def field_type self[:FT] end
def form_field
This method is only here to make it easier to get the form field when the object may
Returns self.
def form_field self end
def full_field_name
The full name of a field is constructed using the full name of the parent field, a period
Returns the full name of the field or +nil+ if no name is set.
def full_field_name if key?(:Parent) [self[:Parent].full_field_name, field_name].compact.join('.') else field_name end end
def must_be_indirect?
def must_be_indirect? true end
def perform_validation #:nodoc:
def perform_validation #:nodoc: super if terminal_field? && field_type.nil? yield("/FT is required for terminal fields") end if key?(:T) && self[:T].include?('.') yield("/T shall not contain a period") end end
def terminal_field?
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