Class: HexaPDF::Type::AcroForm::Form
- Inherits:
-
Dictionary
- Object
- Object
- Dictionary
- HexaPDF::Type::AcroForm::Form
- Extended by:
- Utils::BitField
- Defined in:
- lib/hexapdf/type/acro_form/form.rb
Overview
Represents the PDF’s interactive form dictionary. It is linked from the catalog dictionary via the /AcroForm entry.
Overview
An interactive form consists of fields which can be structured hierarchically and shown on pages by using Annotations::Widget annotations. This means one field can have zero, one or more visual representations on one or more pages. The fields at the bottom of the hierarchy which have no parent are called “root fields” and are stored in /Fields.
Each field in a form has a certain type which determines how it should be displayed and what a user can do with it. The most common type is “text field” which allows the user to enter one or more lines of text. There are also check boxes, radio buttons, list boxes and combo boxes.
Visual Appearance
The visual appearance of a field is normally provided by the application creating the PDF. This is done by generating the so called appearances for all widgets of a field. However, it is also possible to instruct the PDF reader application to generate the appearances on the fly using the /NeedAppearances key, see #need_appearances!.
HexaPDF uses the configuration option acro_form.create_appearance_streams
to determine whether appearances should automatically be generated.
See: PDF2.0 s12.7.3, Field, HexaPDF::Type::Annotations::Widget
Constant Summary
Constants included from DictionaryFields
DictionaryFields::Boolean, DictionaryFields::PDFByteString, DictionaryFields::PDFDate
Instance Attribute Summary
Attributes inherited from Object
#data, #document, #must_be_indirect
Instance Method Summary collapse
-
#create_appearances(force: false) ⇒ Object
Creates the appearances for all widgets of all terminal fields if they don’t exist.
-
#create_check_box(name) ⇒ Object
Creates a new check box with the given name and adds it to the form.
-
#create_comb_text_field(name, max_chars:, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new comb text field with the given name and adds it to the form.
-
#create_combo_box(name, option_items: nil, editable: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a combo box with the given name and adds it to the form.
-
#create_file_select_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new file select field with the given name and adds it to the form.
-
#create_list_box(name, option_items: nil, multi_select: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a list box with the given name and adds it to the form.
-
#create_multiline_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new multiline text field with the given name and adds it to the form.
-
#create_namespace_field(name) ⇒ Object
Creates an untyped namespace field for creating hierarchies.
-
#create_password_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new password field with the given name and adds it to the form.
-
#create_radio_button(name) ⇒ Object
Creates a radio button with the given name and adds it to the form.
-
#create_signature_field(name) ⇒ Object
Creates a signature field with the given name and adds it to the form.
-
#create_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new text field with the given name and adds it to the form.
-
#default_resources ⇒ Object
Returns the dictionary containing the default resources for form field appearance streams.
-
#delete_field(name_or_field) ⇒ Object
:call-seq: form.delete_field(name) form.delete_field(field).
-
#each_field(terminal_only: true) ⇒ Object
:call-seq: acroform.each_field(terminal_only: true) {|field| block} -> acroform acroform.each_field(terminal_only: true) -> Enumerator.
-
#field_by_name(name) ⇒ Object
Returns the field with the given
name
ornil
if no such field exists. -
#fill(data) ⇒ Object
Fills form fields with the values from the given
data
hash. -
#find_root_fields ⇒ Object
Returns an array with all root fields that were found in the PDF document.
-
#find_root_fields! ⇒ Object
Finds all root fields and sets /Fields appropriately.
-
#flatten(fields: nil, create_appearances: true) ⇒ Object
Flattens the whole interactive form or only the given fields, and returns the fields that couldn’t be flattened.
-
#need_appearances! ⇒ Object
Sets the /NeedAppearances field to
true
. -
#recalculate_fields ⇒ Object
Recalculates all form fields that have a calculate action applied (which are all fields listed in the /CO entry).
-
#root_fields ⇒ Object
Returns the PDFArray containing the root fields.
-
#set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0, font_color: 0) ⇒ Object
Sets the global default appearance string using the provided values or the default values which provide a sane default.
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, #must_be_indirect?, #null?, #oid, #oid=, #type, #validate, #value, #value=
Constructor Details
This class inherits a constructor from HexaPDF::Object
Instance Method Details
#create_appearances(force: false) ⇒ Object
Creates the appearances for all widgets of all terminal fields if they don’t exist.
If force
is true
, new appearances are created even if there are existing ones.
464 465 466 467 468 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 464 def create_appearances(force: false) each_field do |field| field.create_appearances(force: force) if field.respond_to?(:create_appearances) end end |
#create_check_box(name) ⇒ Object
Creates a new check box with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
Before a field value other than false
can be assigned to the check box, a widget needs to be created.
289 290 291 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 289 def create_check_box(name) create_field(name, :Btn, &:initialize_as_check_box) end |
#create_comb_text_field(name, max_chars:, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new comb text field with the given name and adds it to the form.
The max_chars
argument defines the maximum number of characters the comb text field can accommodate.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field, see #create_text_field for details.
237 238 239 240 241 242 243 244 245 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 237 def create_comb_text_field(name, max_chars:, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Tx) do |field| field.initialize_as_comb_text_field apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) field[:MaxLen] = max_chars end end |
#create_combo_box(name, option_items: nil, editable: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a combo box with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field:
option_items
-
Specifies the values of the list box.
editable
-
If set to
true
, the combo box allows entering an arbitrary value in addition to selecting one of the provided option items. font
,font_options
,font_size
andalign
-
See #create_text_field
322 323 324 325 326 327 328 329 330 331 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 322 def create_combo_box(name, option_items: nil, editable: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Ch) do |field| field.initialize_as_combo_box field.option_items = option_items if option_items field.flag(:edit) if editable apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#create_file_select_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new file select field with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field, see #create_text_field for details.
255 256 257 258 259 260 261 262 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 255 def create_file_select_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Tx) do |field| field.initialize_as_file_select_field apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#create_list_box(name, option_items: nil, multi_select: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a list box with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field:
option_items
-
Specifies the values of the list box.
multi_select
-
If set to
true
, the list box allows selecting multiple items instead of only one. font
,font_options
,font_size
andalign
-
See #create_text_field.
349 350 351 352 353 354 355 356 357 358 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 349 def create_list_box(name, option_items: nil, multi_select: nil, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Ch) do |field| field.initialize_as_list_box field.option_items = option_items if option_items field.flag(:multi_select) if multi_select apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#create_multiline_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new multiline text field with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field, see #create_text_field for details.
217 218 219 220 221 222 223 224 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 217 def create_multiline_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Tx) do |field| field.initialize_as_multiline_text_field apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#create_namespace_field(name) ⇒ Object
Creates an untyped namespace field for creating hierarchies.
Example:
form.create_namespace_field('text')
form.create_text_field('text.a1')
173 174 175 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 173 def create_namespace_field(name) create_field(name) end |
#create_password_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new password field with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field, see #create_text_field for details.
272 273 274 275 276 277 278 279 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 272 def create_password_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Tx) do |field| field.initialize_as_password_field apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#create_radio_button(name) ⇒ Object
Creates a radio button with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
Before a field value other than nil
can be assigned to the radio button, at least one widget needs to be created.
301 302 303 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 301 def (name) create_field(name, :Btn, &:initialize_as_radio_button) end |
#create_signature_field(name) ⇒ Object
Creates a signature field with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
365 366 367 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 365 def create_signature_field(name) create_field(name, :Sig) end |
#create_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) ⇒ Object
Creates a new text field with the given name and adds it to the form.
The name
may contain dots to signify a field hierarchy. If the parent fields don’t already exist, they are created as pure namespace fields (see #create_namespace_field). If the name
doesn’t contain dots, a top-level field is created.
The optional keyword arguments allow setting often used properties of the field:
font
-
The font that should be used for the text of the field. If not specified, it defaults to Helvetica.
font_options
-
A hash with font options like :variant that should be used. If not specified, it defaults to the empty hash.
font_size
-
The font size that should be used. If not specified, it defaults to 0 (= auto-sizing).
font_color
-
The font color that should be used. If not specified, it defaults to 0 (i.e. black).
align
-
The alignment of the text, either :left, :center or :right.
201 202 203 204 205 206 207 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 201 def create_text_field(name, font: nil, font_options: nil, font_size: nil, font_color: nil, align: nil) create_field(name, :Tx) do |field| apply_variable_text_properties(field, font: font, font_options: , font_size: font_size, font_color: font_color, align: align) end end |
#default_resources ⇒ Object
Returns the dictionary containing the default resources for form field appearance streams.
437 438 439 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 437 def default_resources self[:DR] ||= document.wrap({}, type: :XXResources) end |
#delete_field(name_or_field) ⇒ Object
:call-seq:
form.delete_field(name)
form.delete_field(field)
Deletes the field specified by the given name or via the given field object.
If the field is a signature field, the associated signature dictionary is also deleted.
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 376 def delete_field(name_or_field) field = (name_or_field.kind_of?(String) ? field_by_name(name_or_field) : name_or_field) document.delete(field[:V]) if field.field_type == :Sig to_delete = field.(direct_only: false).to_a document.pages.each do |page| next unless page.key?(:Annots) page_annots = page[:Annots].to_a - to_delete page[:Annots].value.replace(page_annots) end if field[:Parent] field[:Parent][:Kids].delete(field) else self[:Fields].delete(field) end to_delete.each {|| document.delete() } document.delete(field) end |
#each_field(terminal_only: true) ⇒ Object
:call-seq:
acroform.each_field(terminal_only: true) {|field| block} -> acroform
acroform.each_field(terminal_only: true) -> Enumerator
Yields all terminal fields or all fields, depending on the terminal_only
argument.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 126 def each_field(terminal_only: true) return to_enum(__method__, terminal_only: terminal_only) unless block_given? process_field_array = lambda do |array| array.each_with_index do |field, index| next if field.nil? unless field.respond_to?(:type) && field.type == :XXAcroFormField array[index] = field = Field.wrap(document, field) end if field.terminal_field? yield(field) else yield(field) unless terminal_only process_field_array.call(field[:Kids]) end end end process_field_array.call(root_fields) self end |
#field_by_name(name) ⇒ Object
Returns the field with the given name
or nil
if no such field exists.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 149 def field_by_name(name) fields = root_fields field = nil name.split('.').each do |part| field = nil fields&.each do |f| f = Field.wrap(document, f) next unless f[:T] == part field = f fields = field[:Kids] unless field.terminal_field? break end end field end |
#fill(data) ⇒ Object
Fills form fields with the values from the given data
hash.
The keys of the data
hash need to be full field names and the values are the respective values, usually in string form. It is possible to specify only some of the fields of the form.
What kind of values are supported for a field depends on the field type:
-
For fields containing text (single/multiline/comb text fields, file select fields, combo boxes and list boxes) the value needs to be a string and it is assigned as is.
-
For check boxes, the values “y”/“yes”/“t”/“true” are handled as assigning
true
to the field, the values “n”/“no”/“f”/“false” are handled as assigningfalse
to the field, and every other string value is assigned as is. See ButtonField#field_value= for details. -
For radio buttons the value needs to be a String or a Symbol representing the name of the radio button widget to select.
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 415 def fill(data) data.each do |field_name, value| field = field_by_name(field_name) raise HexaPDF::Error, "AcroForm field named '#{field_name}' not found" unless field case field.concrete_field_type when :single_line_text_field, :multiline_text_field, :comb_text_field, :file_select_field, :combo_box, :list_box, :editable_combo_box, :radio_button field.field_value = value when :check_box field.field_value = case value when /\A(?:y(es)?|t(rue)?)\z/ then true when /\A(?:n(o)?|f(alse)?)\z/ then false else value end else raise HexaPDF::Error, "AcroForm field type #{field.concrete_field_type} not yet supported" end end end |
#find_root_fields ⇒ Object
Returns an array with all root fields that were found in the PDF document.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 98 def find_root_fields result = [] document.pages.each do |page| page.each_annotation do |annot| if !annot.key?(:Parent) && annot.key?(:FT) result << Field.wrap(document, annot) elsif annot.key?(:Parent) field = annot[:Parent] field = field[:Parent] while field[:Parent] result << Field.wrap(document, field) end end end result end |
#find_root_fields! ⇒ Object
Finds all root fields and sets /Fields appropriately.
See: #find_root_fields
117 118 119 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 117 def find_root_fields! self[:Fields] = find_root_fields end |
#flatten(fields: nil, create_appearances: true) ⇒ Object
Flattens the whole interactive form or only the given fields, and returns the fields that couldn’t be flattened.
Flattening means making the appearance streams of the field widgets part of the respective page’s content stream and removing the fields themselves.
If the whole interactive form is flattened, the form object itself is also removed if all fields were flattened.
The create_appearances
argument controls whether missing appearances should automatically be created.
See: HexaPDF::Type::Page#flatten_annotations
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 483 def flatten(fields: nil, create_appearances: true) remove_form = fields.nil? fields ||= each_field.to_a if create_appearances fields.each {|field| field.create_appearances if field.respond_to?(:create_appearances) } end not_flattened = fields.map {|field| field.(direct_only: true).to_a }.flatten document.pages.each {|page| not_flattened = page.flatten_annotations(not_flattened) } not_flattened.map!(&:form_field) fields -= not_flattened fields.each do |field| (field[:Parent]&.[](:Kids) || self[:Fields]).delete(field) document.delete(field) end if remove_form && not_flattened.empty? document.catalog.delete(:AcroForm) document.delete(self) end not_flattened end |
#need_appearances! ⇒ Object
Sets the /NeedAppearances field to true
.
This will make PDF reader applications generate appropriate appearance streams based on the information stored in the fields and associated widgets.
457 458 459 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 457 def need_appearances! self[:NeedAppearances] = true end |
#recalculate_fields ⇒ Object
Recalculates all form fields that have a calculate action applied (which are all fields listed in the /CO entry).
If HexaPDF doesn’t support a calculation method or an error occurs during calculation, the field value is not updated.
Note that calculations are not done automatically when a form field’s value changes since it would lead to possibly many calls to this actions. So first fill in all field values and then call this method.
See: JavaScriptActions
519 520 521 522 523 524 525 526 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 519 def recalculate_fields self[:CO]&.each do |field| field = Field.wrap(document, field) next unless field && (calculation_action = field[:AA]&.[](:C)) result = JavaScriptActions.calculate(self, calculation_action) field.field_value = result if result end end |
#root_fields ⇒ Object
Returns the PDFArray containing the root fields.
93 94 95 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 93 def root_fields self[:Fields] ||= document.wrap([]) end |
#set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0, font_color: 0) ⇒ Object
Sets the global default appearance string using the provided values or the default values which provide a sane default.
See VariableTextField::create_appearance_string for information on the arguments.
445 446 447 448 449 450 451 |
# File 'lib/hexapdf/type/acro_form/form.rb', line 445 def set_default_appearance_string(font: 'Helvetica', font_options: {}, font_size: 0, font_color: 0) self[:DA] = VariableTextField.create_appearance_string(document, font: font, font_options: , font_size: font_size, font_color: font_color) end |