Class: HexaPDF::Type::AcroForm::Field
- Inherits:
-
Dictionary
- Object
- Object
- Dictionary
- HexaPDF::Type::AcroForm::Field
- Extended by:
- Utils::BitField
- Defined in:
- lib/hexapdf/type/acro_form/field.rb
Overview
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.
Also see the class description of the subclasses for additional, type specific field flags.
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: PDF2.0 s12.7.4.1
Direct Known Subclasses
Defined Under Namespace
Modules: HashRefinement
Constant Summary collapse
- INHERITABLE_FIELDS =
The inheritable dictionary fields common to all AcroForm field types.
[:FT, :Ff, :V, :DV].freeze
Constants included from DictionaryFields
DictionaryFields::Boolean, DictionaryFields::PDFByteString, DictionaryFields::PDFDate
Instance Attribute Summary
Attributes inherited from Object
#data, #document, #must_be_indirect
Class Method Summary collapse
-
.inherited_value(field, name) ⇒ Object
Treats
name
as an inheritable dictionary field and resolves its value for the AcroForm fieldfield
. -
.wrap(document, field) ⇒ Object
Wraps the given
field
object inside the correct field class and returns the wrapped object.
Instance Method Summary collapse
-
#[](name) ⇒ Object
Returns the value for the entry
name
. -
#alternate_field_name ⇒ Object
Returns the alternate field name that should be used for display purposes (e.g. Acrobat shows this as tool tip).
-
#alternate_field_name=(value) ⇒ Object
Sets the alternate field name.
-
#concrete_field_type ⇒ Object
Returns the concrete field type (:button_field, :text_field, :choice_field or :signature_field) or
nil
is no field type is set. -
#create_widget(page, allow_embedded: true, **values) ⇒ Object
Creates a new widget annotation for this form field (must be a terminal field!) on the given
page
, adding thevalues
to the created widget annotation object. -
#delete_widget(widget) ⇒ Object
Deletes the given widget annotation object from this field, the page it appears on and the document.
-
#each_widget(direct_only: true, &block) ⇒ Object
:call-seq: field.each_widget(direct_only: true) {|widget| block} -> field field.each_widget(direct_only: true) -> Enumerator.
-
#embedded_widget? ⇒ Boolean
Returns
true
if the field contains an embedded widget. -
#field_name ⇒ Object
Returns the name of the field or
nil
if no name is set. -
#field_type ⇒ Object
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).
-
#flags ⇒ Object
:method: flag :call-seq: flag(*flags, clear_existing: false).
-
#form_field ⇒ Object
Returns self.
-
#full_field_name ⇒ Object
Returns the full name of the field or
nil
if no name is set. -
#must_be_indirect? ⇒ Boolean
Form fields must always be indirect objects.
-
#terminal_field? ⇒ Boolean
Returns
true
if this is a terminal field.
Methods included from Utils::BitField
Methods inherited from Dictionary
#[]=, define_field, define_type, #delete, #each, each_field, #empty?, field, #key?, #to_hash, type, #type
Methods inherited from Object
#<=>, #==, #cache, #cached?, #clear_cache, deep_copy, #deep_copy, #document?, #eql?, field, #gen, #gen=, #hash, #indirect?, #initialize, #inspect, make_direct, #null?, #oid, #oid=, #type, #validate, #value, #value=
Constructor Details
This class inherits a constructor from HexaPDF::Object
Class Method Details
.inherited_value(field, name) ⇒ Object
Treats name
as an inheritable dictionary field and resolves its value for the AcroForm field field
.
159 160 161 162 163 164 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 159 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 |
.wrap(document, field) ⇒ Object
Wraps the given field
object inside the correct field class and returns the wrapped object.
168 169 170 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 168 def self.wrap(document, field) document.wrap(field, type: :XXAcroFormField, subtype: inherited_value(field, :FT)) end |
Instance Method Details
#[](name) ⇒ Object
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#[]
183 184 185 186 187 188 189 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 183 def [](name) if value[name].nil? && self.class::INHERITABLE_FIELDS.include?(name) self.class.inherited_value(self, name) || super else super end end |
#alternate_field_name ⇒ Object
Returns the alternate field name that should be used for display purposes (e.g. Acrobat shows this as tool tip).
235 236 237 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 235 def alternate_field_name self[:TU] end |
#alternate_field_name=(value) ⇒ Object
Sets the alternate field name.
See #alternate_field_name
242 243 244 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 242 def alternate_field_name=(value) self[:TU] = value end |
#concrete_field_type ⇒ Object
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
206 207 208 209 210 211 212 213 214 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 206 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 |
#create_widget(page, allow_embedded: true, **values) ⇒ Object
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 object.
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
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 323 def (page, allow_embedded: true, **values) unless terminal_field? raise HexaPDF::Error, "Widgets can only be added to terminal fields" end = {Type: :Annot, Subtype: :Widget, Rect: [0, 0, 0, 0], **values} if ! || || (key?(:Kids) && !self[:Kids].empty?) kids = self[:Kids] ||= [] kids << if = document.add() [:Parent] = self self[:Kids] << else value.update() = document.wrap(self) end .flag(:print) [:P] = page (page[:Annots] ||= []) << end |
#delete_widget(widget) ⇒ Object
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.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 352 def () = if && self == elsif terminal_field? ( = self[:Kids]&.index()) && end return unless document.pages.each do |page| break if page[:Annots]&.delete() # See comment in #extract_widget end if WIDGET_FIELDS.each {|key| delete(key) } document.revisions.each {|revision| break if revision.update(self) } else self[:Kids].delete_at() document.delete() end end |
#each_widget(direct_only: true, &block) ⇒ Object
:call-seq:
field.each_widget(direct_only: true) {|widget| block} -> field
field.each_widget(direct_only: true) -> Enumerator
Yields each widget, i.e. visual representation, of this field.
Widgets can be associated to the field in three ways:
-
The widget can be embedded in the field itself.
-
One or more widgets are defined as children of this field.
-
Widgets of *another field instance with the same full field name*.
With the default of direct_only
being true
, only the usual cases 1 and 2 are handled/ If case 3 also needs to be handled, set direct_only
to false
or run the validation on the main AcroForm object (HexaPDF::Document#acro_form) before using this method (this will reduce case 3 to case 2).
Note: Setting direct_only
to false
will have a severe performance impact since all fields of the form have to be searched to check whether there is another field with the same full field name.
See: HexaPDF::Type::Annotations::Widget
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 288 def (direct_only: true, &block) # :yields: widget return to_enum(__method__, direct_only: direct_only) unless block_given? if 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.(direct_only: true, &block) end end self end |
#embedded_widget? ⇒ Boolean
Returns true
if the field contains an embedded widget.
262 263 264 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 262 def key?(:Subtype) end |
#field_name ⇒ Object
Returns the name of the field or nil
if no name is set.
217 218 219 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 217 def field_name self[:T] end |
#field_type ⇒ Object
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
195 196 197 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 195 def field_type self[:FT] end |
#flags ⇒ Object
:method: flag :call-seq:
flag(*flags, clear_existing: false)
Sets the given flags, given as flag names or bit indices. If clear_existing
is true
, all prior flags will be cleared.
See the class description for a list of available flags.
153 154 155 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 153 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]") |
#form_field ⇒ Object
Returns self.
This method is only here to make it easier to get the form field when the object may either be a form field or a field widget.
257 258 259 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 257 def form_field self end |
#full_field_name ⇒ Object
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.
225 226 227 228 229 230 231 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 225 def full_field_name if key?(:Parent) [self[:Parent].full_field_name, field_name].compact.join('.') else field_name end end |
#must_be_indirect? ⇒ Boolean
Form fields must always be indirect objects.
173 174 175 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 173 def must_be_indirect? true end |
#terminal_field? ⇒ Boolean
Returns true
if this is a terminal field.
247 248 249 250 251 |
# File 'lib/hexapdf/type/acro_form/field.rb', line 247 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 |