Class: HexaPDF::Type::AcroForm::TextField

Inherits:
VariableTextField show all
Defined in:
lib/hexapdf/type/acro_form/text_field.rb

Overview

AcroForm text fields provide a box or space to fill-in data entered from keyboard. The text may be restricted to a single line or can span multiple lines.

A special type of single-line text field is the comb text field. This type of field divides the existing space into /MaxLen equally spaced positions.

Type Specific Field Flags

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

:multiline

If set, the text field may contain multiple lines.

:password

The field is a password field. This changes the behaviour of the PDF reader application to not echo the input text and to not store it in the PDF file.

:file_select

The text field represents a file selection control where the input text is the path to a file.

:do_not_spell_check

The text should not be spell-checked.

:do_not_scroll

The text field should not scroll (horizontally for single-line fields and vertically for multiline fields) to accomodate more text than fits into the annotation rectangle. This means that no more text can be entered once the field is full.

:comb

The field is divided into /MaxLen equally spaced positions (so /MaxLen needs to be set). This is useful, for example, when entering things like social security numbers which always have the same length.

:rich_text

The field is a rich text field.

See: PDF2.0 s12.7.5.3

Constant Summary collapse

INHERITABLE_FIELDS =

All inheritable dictionary fields for text fields.

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

Updated list of field flags.

superclass::FLAGS_BIT_MAPPING.merge(
  {
    multiline: 12,
    password: 13,
    file_select: 20,
    do_not_spell_check: 22,
    do_not_scroll: 23,
    comb: 24,
    rich_text: 25,
  }
).freeze

Constants inherited from VariableTextField

VariableTextField::UNSET_ARG

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 VariableTextField

create_appearance_string, parse_appearance_string, #parse_default_appearance_string, #set_default_appearance_string, #text_alignment

Methods inherited from Field

#[], #alternate_field_name, #alternate_field_name=, #create_widget, #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

#comb_text_field?Boolean

Returns true if this field is a comb text field.

Returns:



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

def comb_text_field?
  flagged?(:comb) && !(flagged?(:file_select) || flagged?(:multiline) || flagged?(:password))
end

#concrete_field_typeObject

Returns the concrete text field type, either :single_line_text_field, :multiline_text_field, :password_field, :file_select_field, :comb_text_field or :rich_text_field.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 202

def concrete_field_type
  if flagged?(:multiline)
    :multiline_text_field
  elsif flagged?(:password)
    :password_field
  elsif flagged?(:file_select)
    :file_select_field
  elsif flagged?(:comb)
    :comb_text_field
  elsif flagged?(:rich_text)
    :rich_text_field
  else
    :single_line_text_field
  end
end

#create_appearances(force: false) ⇒ Object

Creates appropriate appearances for all widgets.

For information on how this is done see AppearanceGenerator.

Note that no new appearances are created if the field value hasn’t changed between invocations.

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



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 226

def create_appearances(force: false)
  current_value = field_value
  appearance_generator_class = document.config.constantize('acro_form.appearance_generator')
  each_widget do |widget|
    is_cached = widget.cached?(:last_value)
    unless force
      if is_cached && widget.cache(:last_value) == current_value
        next
      elsif !is_cached && widget.appearance?
        widget.cache(:last_value, current_value, update: true)
        next
      end
    end
    widget.cache(:last_value, current_value, update: true)
    appearance_generator_class.new(widget).create_text_appearances
  end
end

#default_field_valueObject

Returns the default field value.

See: #field_value



188
189
190
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 188

def default_field_value
  self[:DV].kind_of?(String) ? self[:DV] : self[:DV].stream
end

#default_field_value=(str) ⇒ Object

Sets the default field value.

See: #field_value=



195
196
197
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 195

def default_field_value=(str)
  self[:DV] = str
end

#field_valueObject

Returns the field value, i.e. the text contents of the field, or nil if no value is set.

Note that modifying the returned value *might not* modify the text contents in case it is stored as stream! So always use #field_value= to set the field value.



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

def field_value
  return unless value[:V]
  self[:V].kind_of?(String) ? self[:V] : self[:V].stream
end

#field_value=(str) ⇒ Object

Sets the field value, i.e. the text contents of the field, to the given string.

Note that for single line text fields, all whitespace characters are changed to simple spaces.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 169

def field_value=(str)
  if flagged?(:password)
    raise HexaPDF::Error, "Storing a field value for a password field is not allowed"
  elsif comb_text_field? && !key?(:MaxLen)
    raise HexaPDF::Error, "A comb text field need a valid /MaxLen value"
  elsif str && !str.kind_of?(String)
    str = @document.config['acro_form.on_invalid_value'].call(self, str)
  end
  str = str.gsub(/[[:space:]]/, ' ') if str && concrete_field_type == :single_line_text_field
  if key?(:MaxLen) && str && str.length > self[:MaxLen]
    raise HexaPDF::Error, "Value exceeds maximum allowed length of #{self[:MaxLen]}"
  end
  self[:V] = str
  update_widgets
end

#file_select_field?Boolean

Returns true if this field is a file select field.

Returns:



152
153
154
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 152

def file_select_field?
  flagged?(:file_select) && !(flagged?(:password) || flagged?(:multiline) || flagged?(:comb))
end

#initialize_as_comb_text_fieldObject

Initializes the text field to be a comb text field.

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



112
113
114
115
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 112

def initialize_as_comb_text_field
  flag(:comb)
  unflag(:file_select, :multiline, :password)
end

#initialize_as_file_select_fieldObject

Initializes the text field to be a file select field.

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



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

def initialize_as_file_select_field
  flag(:file_select)
  unflag(:comb, :multiline, :password)
end

#initialize_as_multiline_text_fieldObject

Initializes the text field to be a multiline text field.

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



103
104
105
106
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 103

def initialize_as_multiline_text_field
  flag(:multiline)
  unflag(:file_select, :comb, :password)
end

#initialize_as_password_fieldObject

Initializes the text field to be a password field.

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



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

def initialize_as_password_field
  delete(:V)
  flag(:password)
  unflag(:comb, :multiline, :file_select)
end

#multiline_text_field?Boolean

Returns true if this field is a multiline text field.

Returns:



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

def multiline_text_field?
  flagged?(:multiline) && !(flagged?(:file_select) || flagged?(:comb) || flagged?(:password))
end

#password_field?Boolean

Returns true if this field is a password field.

Returns:



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

def password_field?
  flagged?(:password) && !(flagged?(:file_select) || flagged?(:multiline) || flagged?(:comb))
end

#set_calculate_action(type, fields: nil) ⇒ Object

Sets the specified JavaScript calculate action on the field.

This action is executed by a viewer when any field’s value changes so as to recalculate the value of this field. Usually, the field is also flagged as read only to avoid a user changing the value manually.

Note that HexaPDF *doesn’t* automatically recalculate field values, use Form#recalculate_fields to manually kick off recalculation.

The argument type can be one of the following:

:sum

Sums the values of the given fields.

:average

Calculates the average value of the given fields.

:product

Multiplies the values of the given fields.

:min

Uses the minimum value of the given fields.

:max

Uses the maximum value of the given fields.

:sfn

Uses the Simplified Field Notation for calculating the field’s value. This allows for more complex calculations involving addition, subtraction, multiplication and division. Field values are specified by using the full field names which should not contain spaces or punctuation characters except point.

The fields argument needs to contain a string with the SFN calculation rule.

Here are some examples:

field1 + field2 - field3
(field.1 + field.2) * (field.3 - field.4)

Note: Setting this action appends the field to the main Form’s /CO entry which specifies the calculation order. Rearrange the entries as needed.



322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 322

def set_calculate_action(type, fields: nil)
  action_string = case type
                  when :sum, :average, :product, :min, :max
                    JavaScriptActions.af_simple_calculate_action(type, fields)
                  when :sfn
                    JavaScriptActions.simplified_field_notation_action(document.acro_form, fields)
                  else
                    raise ArgumentError, "Invalid value for type argument: #{type.inspect}"
                  end
  self[:AA] ||= {}
  self[:AA][:C] = {S: :JavaScript, JS: action_string}
  (document.acro_form[:CO] ||= []) << self
end

#set_format_action(type, **arguments) ⇒ Object

Sets the specified JavaScript format action on the field’s widgets.

This action is executed when the field value needs to be formatted for rendering in the appearance streams of the associated widgets.

The argument type can be one of the following:

:number

Assumes that the field value is a number and formats it according to the given arguments. See JavaScriptActions.af_number_format_action for details on the arguments.

:percent

Assumes that the field value is a number and formats it as percentage (where 1=100% and 0=0%). See JavaScriptActions.af_percent_format_action for details on the arguments.

:time

Assumes that the field value is a string with a time value and formats it according to the given argument. See JavaScriptActions.af_time_format_action for details on the arguments.



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 269

def set_format_action(type, **arguments)
  action_string = case type
                  when :number then JavaScriptActions.af_number_format_action(**arguments)
                  when :percent then JavaScriptActions.af_percent_format_action(**arguments)
                  when :time then JavaScriptActions.af_time_format_action(**arguments)
                  else
                    raise ArgumentError, "Invalid value for type argument: #{type.inspect}"
                  end
  self[:AA] ||= {}
  self[:AA][:F] = {S: :JavaScript, JS: action_string}
end

#update_widgetsObject

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



245
246
247
# File 'lib/hexapdf/type/acro_form/text_field.rb', line 245

def update_widgets
  create_appearances(force: true)
end