class HexaPDF::Type::AcroForm::ButtonField

See: PDF2.0 s12.7.4.2
on or off in unison.
:radios_in_unison
A group of radio buttons with the same value for the on state will turn
:push_button
The field represents a pushbutton without a permanent value.
box. Additionally, the :pushbutton flag needs to be clear.
:radio
If this flag is set, the field is a set of radio buttons. Otherwise it is a check
names.
work-around is to use multiple check box widgets with different on
Note: This deselectiong is not implemented in any tested PDF viewer. A
button deselects it.
needs to be selected at all times. Otherwise, clicking on the selected
:no_toggle_to_off

Only used with radio buttons fields. If this flag is set, one button
== Type Specific Field Flags
no check box may be selected.
case the check box fields acts like a radio button group, with the additional feature that
Otherwise only one of those widgets will be toggled on while the others are off. In such a
those widgets have the same value, they will all be toggled on or off simultaneously.
Check boxes can be toggled on and off. One check box field may have multiple widgets. If
for this feature.
no_toggle_to_off field flag, no PDF viewer implements that; one needs to use check boxes
otherwise the widgets with the same value represent the same thing. Although there is the
may be selected at all times. Each widget must have a different value to be distinguishable;
group. Of the radio button group only one radio button (= widget of the radio button field)
Radio buttons are widgets of a single radio button field. This is also called a radio button
everything needed is automatically set up.
methods on the main Form instance (HexaPDF::Document#acro_form). By using those methods,
To create a push button, check box or radio button field, use the appropriate convenience
of these are represented with this class.
They are divided into push buttons (things to click on), check boxes and radio buttons. All
AcroForm button fields represent interactive controls to be used with the mouse.

def allowed_values

widget.
Note that this will only return useful values if there is at least one correctly set-up

value for check boxes or radio buttons.
Returns the array of Symbol values (minus the /Off value) that can be used for the field
def allowed_values
  (each_widget.with_object([]) do |widget, result|
     keys = widget.appearance_dict&.normal_appearance&.value&.keys
     result.concat(keys) if keys
   end - [:Off]).uniq
end

def check_box?

Returns +true+ if this button field represents a check box.
def check_box?
  !push_button? && !flagged?(:radio)
end

def concrete_field_type

Returns the concrete button field type, either :push_button, :check_box or :radio_button.
def concrete_field_type
  if push_button?
    :push_button
  elsif radio_button?
    :radio_button
  else
    :check_box
  end
end

def create_appearances(force: false)

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

AppearanceGenerator for the details.
The created appearance streams depend on the actual type of the button field. See

Creates appropriate appearances for all widgets if they don't already exist.
def create_appearances(force: false)
  appearance_generator_class = document.config.constantize('acro_form.appearance_generator')
  each_widget do |widget|
    normal_appearance = widget.appearance_dict&.normal_appearance
    next if !force && normal_appearance &&
      ((!push_button? && normal_appearance.value.length == 2 &&
        normal_appearance.each.all? {|_, v| v.kind_of?(HexaPDF::Stream) }) ||
       (push_button? && normal_appearance.kind_of?(HexaPDF::Stream)))
    if check_box?
      appearance_generator_class.new(widget).create_check_box_appearances
    elsif radio_button?
      appearance_generator_class.new(widget).create_radio_button_appearances
    else
      appearance_generator_class.new(widget).create_push_button_appearances
    end
  end
end

def create_widget(page, defaults: true, value: nil, **values)

See: Field#create_widget, AppearanceGenerator button field methods

be used.
The +value+ is optional for check box fields; if not specified, the default of :Yes will

be used with #field_value= to set this specific widget of the radio button set to on.
the value (a Symbol or an object responding to +#to_sym+) this widget represents. It can
If the widget is created for a radio button field, the +value+ argument needs to set to

default appearance.
If +defaults+ is +true+, then default values will be set on the widget so that it uses a

Creates a widget for the button field.
def create_widget(page, defaults: true, value: nil, **values)
  super(page, allow_embedded: !radio_button?, **values).tap do |widget|
    value = :Yes if check_box? && value.nil?
    if radio_button? || check_box?
      unless value.respond_to?(:to_sym)
        raise ArgumentError, "Argument 'value' has to be provided for radio buttons " \
          "and needs to respond to #to_sym"
      end
      widget[:AP] = {N: {value.to_sym => nil, Off: nil}}
    end
    next unless defaults
    widget.border_style(color: 0, width: 1, style: (push_button? ? :beveled : :solid))
    widget.background_color(push_button? ? 0.5 : 255)
    widget.marker_style(style: check_box? ? :check : :circle) unless push_button?
  end
end

def default_field_value

See: #field_value

Returns the default field value.
def default_field_value
  normalized_field_value(:DV)
end

def default_field_value=(value)

See: #field_value=

Sets the default field value.
def default_field_value=(value)
  normalized_field_value_set(:DV, value)
end

def field_value

Symbol) of the specific radio button that is selected is returned.
Radio buttons:: If no radio button is selected, +nil+ is returned. Otherwise the value (a

checked is returned. Otherwise +nil+ is returned.
Check boxes:: For check boxes that are checked the value of the specific check box that is

Push buttons:: They don't have a value, so +nil+ is always returned.

Returns the field value which depends on the concrete type.
def field_value
  normalized_field_value(:V)
end

def field_value=(value)

button that should be turned on.
the value (a Symbol or an object responding to +#to_sym+) of a radio
Radio buttons:: To turn all radio buttons off, provide +nil+ as value. Otherwise provide

toggled on.
an object responding to +#to_sym+) of the check box widget that should be
i.e. toggling it to the on state. Otherwise provide the value (a Symbol or
there is only one possible value, +true+ may be used for checking the box,
Check boxes:: Provide +nil+ or +false+ as value to toggle all check box widgets off. If

nothing is stored for them (e.g a no-op).
Push buttons:: Since push buttons don't store any value, the given value is ignored and

Sets the field value which depends on the concrete type.
def field_value=(value)
  normalized_field_value_set(:V, value)
end

def initialize_as_check_box

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

Initializes the button field to be a check box.
def initialize_as_check_box
  self[:V] = :Off
  unflag(:push_button)
  unflag(:radio)
end

def initialize_as_push_button

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

Initializes the button field to be a push button.
def initialize_as_push_button
  self[:V] = nil
  flag(:push_button)
  unflag(:radio)
end

def initialize_as_radio_button

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

Initializes the button field to be a radio button.
def initialize_as_radio_button
  self[:V] = :Off
  unflag(:push_button)
  flag(:radio)
end

def normalized_field_value(key)

See #field_value for details.

Returns the normalized field value for the given key which can be :V or :DV.
def normalized_field_value(key)
  if push_button?
    nil
  else
    self[key] == :Off ? nil : self[key]
  end
end

def normalized_field_value_set(key, value)

See #field_value= for details.

transformed into the expected value depending on the specific field type.
Sets the key, either :V or :DV, to the value. The given normalized value is first
def normalized_field_value_set(key, value)
  return if push_button?
  av = allowed_values
  self[key] = if value.nil? || value == :Off
                :Off
              elsif check_box?
                if value == false
                  :Off
                elsif value == true && av.size == 1
                  av[0]
                elsif av.include?(value.to_sym)
                  value.to_sym
                else
                  @document.config['acro_form.on_invalid_value'].call(self, value)
                end
              elsif av.include?(value.to_sym)
                value.to_sym
              else
                @document.config['acro_form.on_invalid_value'].call(self, value)
              end
  update_widgets
end

def perform_validation #:nodoc:

:nodoc:
def perform_validation #:nodoc:
  if field_type != :Btn
    yield("Field /FT of AcroForm button field has to be :Btn", true)
    self[:FT] = :Btn
  end
  super
  unless key?(:V)
    yield("Button field has no value set, defaulting to :Off", true)
    self[:V] = :Off
  end
end

def push_button?

Returns +true+ if this button field represents a push button.
def push_button?
  flagged?(:push_button)
end

def radio_button?

Returns +true+ if this button field represents a radio button set.
def radio_button?
  !push_button? && flagged?(:radio)
end

def update_widgets

Updates the widgets so that they reflect the current field value.
def update_widgets
  return if push_button?
  create_appearances
  value = self[:V]
  each_widget do |widget|
    widget[:AS] = (widget.appearance_dict&.normal_appearance&.key?(value) ? value : :Off)
  end
end