class HexaPDF::Type::AcroForm::ChoiceField

AcroForm choice fields contain multiple text items of which one (or, if so flagged, more) may be selected.

They are divided into scrollable list boxes and combo boxes. To create a list or combo box, use the appropriate convenience methods on the main Form instance (HexaPDF::Document#acro_form). By using those methods, everything needed is automatically set up.

Type Specific Field Flags

:combo

If set, the field represents a comb box.

:edit

If set, the combo box includes an editable text box for entering arbitrary values. Therefore the 'combo' flag also needs to be set.

:sort

The option items have to be sorted alphabetically. This flag is intended for PDF writers, not readers which should display the items in the order they appear.

:multi_select

If set, more than one item may be selected.

:do_not_spell_check

The text should not be spell-checked.

:commit_on_sel_change

If set, a new value should be commited as soon as a selection is made.

See: PDF1.7 s12.7.4.4

Constants

FLAGS_BIT_MAPPING

Updated list of field flags.

Public Instance Methods

combo_box?() click to toggle source

Returns true if this choice field represents a combo box.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 111
def combo_box?
  flagged?(:combo)
end
concrete_field_type() click to toggle source

Returns the concrete choice field type, either :list_box, :combo_box or :editable_combo_box.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 209
def concrete_field_type
  if combo_box?
    flagged?(:edit) ? :editable_combo_box : :combo_box
  else
    :list_box
  end
end
create_appearances(force: false) click to toggle source

Creates appropriate appearances for all widgets if they don't already exist.

For information on how this is done see AppearanceGenerator.

Note that no new appearances are created if the dictionary fields involved in the creation of the appearance stream have not been changed between invocations.

By setting force to true the creation of the appearances can be forced.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 225
def create_appearances(force: false)
  current_appearance_state = [self[:V], self[:I], self[:Opt], self[:TI]]

  appearance_generator_class = document.config.constantize('acro_form.appearance_generator')
  each_widget do |widget|
    next if !force && widget.cached?(:appearance_state) &&
      widget.cache(:appearance_state) == current_appearance_state

    widget.cache(:appearance_state, current_appearance_state, update: true)
    if combo_box?
      appearance_generator_class.new(widget).create_combo_box_appearances
    else
      appearance_generator_class.new(widget).create_list_box_appearances
    end
  end
end
default_field_value() click to toggle source

Returns the default field value.

See: field_value

# File lib/hexapdf/type/acro_form/choice_field.rb, line 147
def default_field_value
  process_value(self[:DV])
end
default_field_value=(value) click to toggle source

Sets the default field value.

See: field_value=

# File lib/hexapdf/type/acro_form/choice_field.rb, line 154
def default_field_value=(value)
  items = option_items
  self[:DV] = if [value].flatten.all? {|v| items.include?(v) }
                value
              else
                @document.config['acro_form.on_invalid_value'].call(self, value)
              end
end
export_values() click to toggle source

Returns the export values of the option items.

If you need the display strings (as in most cases), use the option_items method.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 174
def export_values
  key?(:Opt) ? process_value(self[:Opt].map {|i| i.kind_of?(Array) ? i[0] : i }) : []
end
field_value() click to toggle source

Returns the field value which represents the currently selected item(s).

If no item is selected, nil is returned. If multiple values are selected, the return value is an array of strings, otherwise it is just a string.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 119
def field_value
  process_value(self[:V])
end
field_value=(value) click to toggle source

Sets the field value to the given string or array of strings.

The dictionary field /I is also modified to correctly represent the selected item(s).

# File lib/hexapdf/type/acro_form/choice_field.rb, line 126
def field_value=(value)
  items = option_items
  array_value = [value].flatten
  all_included = array_value.all? {|v| items.include?(v) }
  self[:V] = if (combo_box? && value.kind_of?(String) &&
                 (flagged?(:edit) || all_included))
               delete(:I)
               value
             elsif list_box? && all_included &&
                 (value.kind_of?(String) || flagged?(:multi_select))
               self[:I] = array_value.map {|val| items.index(val) }.sort!
               array_value.length == 1 ? value : array_value
             else
               @document.config['acro_form.on_invalid_value'].call(self, value)
             end
  update_widgets
end
initialize_as_combo_box() click to toggle source

Initializes the button field to be a combo box.

This method should only be called directly after creating a new choice field because it doesn't completely reset the object.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 100
def initialize_as_combo_box
  self[:V] = nil
  flag(:combo)
end
initialize_as_list_box() click to toggle source

Initializes the choice field to be a list box.

This method should only be called directly after creating a new choice field because it doesn't completely reset the object.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 91
def initialize_as_list_box
  self[:V] = nil
  unflag(:combo)
end
list_box?() click to toggle source

Returns true if this choice field represents a list box.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 106
def list_box?
  !combo_box?
end
list_box_top_index() click to toggle source

Returns the index of the first visible option item of a list box.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 194
def list_box_top_index
  self[:TI]
end
list_box_top_index=(index) click to toggle source

Makes the option item referred to via the given index the first visible option item of a list box.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 200
def list_box_top_index=(index)
  if index < 0 || !key?(:Opt) || index >= self[:Opt].length
    raise ArgumentError, "Index out of range for the set option items"
  end
  self[:TI] = index
end
option_items() click to toggle source

Returns the array with the available option items.

Note that this only returns the option items themselves! For getting the export values, the export_values method has to be used.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 167
def option_items
  key?(:Opt) ? process_value(self[:Opt].map {|i| i.kind_of?(Array) ? i[1] : i }) : []
end
option_items=(value) click to toggle source

Sets the array with the available option items to the given value.

Each entry in the array may either be a string representing the text to be displayed. Or an array of two strings where the first describes the export value (to be used when exporting form field data from the document) and the second is the display value.

See: option_items, export_values

# File lib/hexapdf/type/acro_form/choice_field.rb, line 185
def option_items=(value)
  self[:Opt] = if flagged?(:sort)
                 value.sort_by {|i| process_value(i.kind_of?(Array) ? i[1] : i) }
               else
                 value
               end
end
update_widgets() click to toggle source

Updates the widgets so that they reflect the current field value.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 243
def update_widgets
  create_appearances
end

Private Instance Methods

process_value(value) click to toggle source

Uses the HexaPDF::DictionaryFields::StringConverter to process the value (a string or an array of strings) so that it contains only normalized strings.

# File lib/hexapdf/type/acro_form/choice_field.rb, line 251
def process_value(value)
  value = value.value if value.kind_of?(PDFArray)
  if value.kind_of?(Array)
    value.map! {|item| DictionaryFields::StringConverter.convert(item, nil, nil) || item }
  else
    DictionaryFields::StringConverter.convert(value, nil, nil) || value
  end
end