Class: HexaPDF::Type::AcroForm::ButtonField

Inherits:
Field show all
Defined in:
lib/hexapdf/type/acro_form/button_field.rb

Overview

AcroForm button fields represent interactive controls to be used with the mouse.

They are divided into push buttons (things to click on), check boxes and radio buttons. All of these are represented with this class.

To create a push button, check box or radio button field, use the appropriate convenience methods on the main Form instance (HexaPDF::Document#acro_form). By using those methods, everything needed is automatically set up.

Radio buttons are widgets of a single radio button field. This is also called a radio button group. Of the radio button group only one radio button (= widget of the radio button field) may be selected at all times. Each widget must have a different value to be distinguishable; otherwise the widgets with the same value represent the same thing. Although there is the no_toggle_to_off field flag, no PDF viewer implements that; one needs to use check boxes for this feature.

Check boxes can be toggled on and off. One check box field may have multiple widgets. If those widgets have the same value, they will all be toggled on or off simultaneously. Otherwise only one of those widgets will be toggled on while the others are off. In such a case the check box fields acts like a radio button group, with the additional feature that no check box may be selected.

Type Specific Field Flags

See the class description for Field for the general field flags.

:no_toggle_to_off

Only used with radio buttons fields. If this flag is set, one button needs to be selected at all times. Otherwise, clicking on the selected button deselects it.

Note: This deselectiong is not implemented in any tested PDF viewer. A work-around is to use multiple check box widgets with different on names.

:radio

If this flag is set, the field is a set of radio buttons. Otherwise it is a check box. Additionally, the :pushbutton flag needs to be clear.

:push_button

The field represents a pushbutton without a permanent value.

:radios_in_unison

A group of radio buttons with the same value for the on state will turn on or off in unison.

See: PDF2.0 s12.7.4.2

Constant Summary collapse

INHERITABLE_FIELDS =

All inheritable dictionary fields for button fields.

(superclass::INHERITABLE_FIELDS + [:Opt]).freeze
FLAGS_BIT_MAPPING =

Updated list of field flags.

superclass::FLAGS_BIT_MAPPING.merge(
  {
    no_toggle_to_off: 14,
    radio: 15,
    push_button: 16,
    radios_in_unison: 25,
  }
).freeze

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

Methods inherited from Field

#[], #alternate_field_name, #alternate_field_name=, #delete_widget, #each_widget, #embedded_widget?, #field_name, #field_type, #flags, #form_field, #full_field_name, inherited_value, #must_be_indirect?, #terminal_field?, wrap

Methods included from Utils::BitField

#bit_field

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

#allowed_valuesObject

Returns the array of Symbol values (minus the /Off value) that can be used for the field value for check boxes or radio buttons.

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



214
215
216
217
218
219
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 214

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

#check_box?Boolean

Returns true if this button field represents a check box.

Returns:



142
143
144
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 142

def check_box?
  !push_button? && !flagged?(:radio)
end

#concrete_field_typeObject

Returns the concrete button field type, either :push_button, :check_box or :radio_button.



199
200
201
202
203
204
205
206
207
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 199

def concrete_field_type
  if push_button?
    :push_button
  elsif radio_button?
    :radio_button
  else
    :check_box
  end
end

#create_appearances(force: false) ⇒ Object

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

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

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



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 257

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

#create_widget(page, defaults: true, value: nil, **values) ⇒ Object

Creates a widget for the button field.

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

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

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

See: Field#create_widget, AppearanceGenerator button field methods



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 234

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

#default_field_valueObject

Returns the default field value.

See: #field_value



187
188
189
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 187

def default_field_value
  normalized_field_value(:DV)
end

#default_field_value=(value) ⇒ Object

Sets the default field value.

See: #field_value=



194
195
196
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 194

def default_field_value=(value)
  normalized_field_value_set(:DV, value)
end

#field_valueObject

Returns the field value which depends on the concrete type.

Push buttons

They don’t have a value, so nil is always returned.

Check boxes

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

Radio buttons

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



160
161
162
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 160

def field_value
  normalized_field_value(:V)
end

#field_value=(value) ⇒ Object

Sets the field value which depends on the concrete type.

Push buttons

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

Check boxes

Provide nil or false as value to toggle all check box widgets off. If true is provided, all check box widgets with the same name as the first one are toggled on. Otherwise provide the value (a Symbol or an object responding to #to_sym) of the check box widget that should be toggled on.

Radio buttons

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

Note that in most cases the field needs to already have widgets because the value is checked against the possibly allowed values which depend on the existing widgets.



180
181
182
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 180

def field_value=(value)
  normalized_field_value_set(:V, value)
end

#initialize_as_check_boxObject

Initializes the button field to be a check box.

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



120
121
122
123
124
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 120

def initialize_as_check_box
  self[:V] = :Off
  unflag(:push_button)
  unflag(:radio)
end

#initialize_as_push_buttonObject

Initializes the button field to be a push button.

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



110
111
112
113
114
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 110

def initialize_as_push_button
  self[:V] = nil
  flag(:push_button)
  unflag(:radio)
end

#initialize_as_radio_buttonObject

Initializes the button field to be a radio button.

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



130
131
132
133
134
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 130

def initialize_as_radio_button
  self[:V] = :Off
  unflag(:push_button)
  flag(:radio)
end

#push_button?Boolean

Returns true if this button field represents a push button.

Returns:



137
138
139
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 137

def push_button?
  flagged?(:push_button)
end

#radio_button?Boolean

Returns true if this button field represents a radio button set.

Returns:



147
148
149
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 147

def radio_button?
  !push_button? && flagged?(:radio)
end

#update_widgetsObject

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



276
277
278
279
280
281
282
283
# File 'lib/hexapdf/type/acro_form/button_field.rb', line 276

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