Class: HexaPDF::Type::AcroForm::AppearanceGenerator
- Inherits:
-
Object
- Object
- HexaPDF::Type::AcroForm::AppearanceGenerator
- Defined in:
- lib/hexapdf/type/acro_form/appearance_generator.rb
Overview
The AppearanceGenerator class provides methods for generating and updating the appearance streams of form fields.
The only method needed is #create_appearances since this method determines to what field the widget belongs and therefore which appearance should be generated.
The visual appearance of a field is constructed using information from the field itself as well as information from the widget. See the documentation for the individual methods which information is used in which way.
By default, any existing appearances are overwritten and the :print
flag is set on the widget so that the field appearance will appear on print-outs.
The visual appearances are chosen to be similar to those used by Adobe Acrobat and others. By subclassing and overriding the necessary methods it is possible to define custom appearances.
See: PDF2.0 s12.5.5, s12.7
Instance Method Summary collapse
-
#create_appearances ⇒ Object
Creates the appropriate appearances for the widget.
-
#create_check_box_appearances ⇒ Object
(also: #create_radio_button_appearances)
Creates the appropriate appearances for check boxes and radio buttons.
-
#create_push_button_appearances ⇒ Object
Creates the appropriate appearances for push buttons.
-
#create_text_appearances ⇒ Object
(also: #create_combo_box_appearances, #create_list_box_appearances)
Creates the appropriate appearances for text fields, combo box fields and list box fields.
-
#initialize(widget) ⇒ AppearanceGenerator
constructor
Creates a new instance for the given
widget
.
Constructor Details
#initialize(widget) ⇒ AppearanceGenerator
Creates a new instance for the given widget
.
69 70 71 72 73 |
# File 'lib/hexapdf/type/acro_form/appearance_generator.rb', line 69 def initialize() @widget = @field = .form_field @document = .document end |
Instance Method Details
#create_appearances ⇒ Object
Creates the appropriate appearances for the widget.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/hexapdf/type/acro_form/appearance_generator.rb', line 76 def create_appearances case @field.field_type when :Btn if @field. else create_check_box_appearances end when :Tx, :Ch create_text_appearances else raise HexaPDF::Error, "Unsupported field type #{@field.field_type}" end end |
#create_check_box_appearances ⇒ Object Also known as:
Creates the appropriate appearances for check boxes and radio buttons.
The unchecked box or unselected radio button is always represented by the appearance with the key /Off. If there is more than one other key besides the /Off key, the first one is used for the appearance of the checked box or selected radio button.
For unchecked boxes an empty rectangle is drawn. Similarly, for unselected radio buttons an empty circle (if the marker is :circle) or rectangle is drawn. When checked or selected, a symbol from the ZapfDingbats font is placed inside. How this is exactly done depends on the following values:
-
The widget’s rectangle /Rect must be defined. If the height and/or width of the rectangle are zero, they are based on the configuration option
acro_form.default_font_size
and widget’s border width. In such a case the rectangle is appropriately updated. -
The line width, style and color of the cirle/rectangle are taken from the widget’s border style. See HexaPDF::Type::Annotations::Widget#border_style.
-
The background color is determined by the widget’s background color. See HexaPDF::Type::Annotations::Widget#background_color.
-
The symbol (marker) as well as its size and color are determined by the marker style of the widget. See HexaPDF::Type::Annotations::Widget#marker_style for details.
Examples:
# check box: default appearance
.border_style(color: 0)
.background_color(1)
.marker_style(style: :check, size: 0, color: 0)
# check box: no visible rectangle, gray background, cross mark when checked
.border_style(color: :transparent, width: 2)
.background_color(0.7)
.marker_style(style: :cross)
# radio button: default appearance
.border_style(color: 0)
.background_color(1)
.marker_style(style: :circle, size: 0, color: 0)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/hexapdf/type/acro_form/appearance_generator.rb', line 132 def create_check_box_appearances normal_appearance = @widget.appearance_dict&.normal_appearance if !normal_appearance.kind_of?(HexaPDF::Dictionary) || normal_appearance.kind_of?(HexaPDF::Stream) (@widget[:AP] ||= {})[:N] = {Off: nil} normal_appearance = @widget[:AP][:N] normal_appearance[@field.field_value&.to_sym || :Yes] = nil end on_name = (normal_appearance.value.keys - [:Off]).first unless on_name on_name = @field.field_value&.to_sym || :Yes normal_appearance[on_name] = nil end @widget[:AS] = (@field[:V] == on_name ? on_name : :Off) @widget.flag(:print) @widget.unflag(:hidden) border_style = @widget.border_style marker_style = @widget.marker_style circular = @field. && marker_style.style == :circle default_font_size = @document.config['acro_form.default_font_size'] rect = @widget[:Rect] rect.width = default_font_size + 2 * border_style.width if rect.width == 0 rect.height = default_font_size + 2 * border_style.width if rect.height == 0 width, height, matrix = perform_rotation(rect.width, rect.height) off_form = @widget.appearance_dict.normal_appearance[:Off] = @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height], Matrix: matrix}) apply_background_and_border(border_style, off_form.canvas, circular: circular) on_form = @widget.appearance_dict.normal_appearance[on_name] = @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height], Matrix: matrix}) canvas = on_form.canvas apply_background_and_border(border_style, canvas, circular: circular) canvas.save_graphics_state do draw_marker(canvas, width, height, border_style.width, marker_style) end end |
#create_push_button_appearances ⇒ Object
Creates the appropriate appearances for push buttons.
This is currently a dummy implementation raising an error.
180 181 182 |
# File 'lib/hexapdf/type/acro_form/appearance_generator.rb', line 180 def raise HexaPDF::Error, "Push button appearance generation not yet supported" end |
#create_text_appearances ⇒ Object Also known as: create_combo_box_appearances, create_list_box_appearances
Creates the appropriate appearances for text fields, combo box fields and list box fields.
The following describes how the appearance is built:
-
The font, font size and font color are taken from the associated field’s default appearance string. See VariableTextField.
If the font is not usable by HexaPDF (which may be due to a variety of reasons, e.g. no associated information in the form’s default resources), the font specified by the configuration option
acro_form.fallback_font
will be used. -
The widget’s rectangle /Rect must be defined. If the height is zero, it is auto-sized based on the font size. If additionally the font size is zero, a font size of
acro_form.default_font_size
is used. If the width is zero, theacro_form.text_field.default_width
value is used. In such cases the rectangle is appropriately updated. -
The line width, style and color of the rectangle are taken from the widget’s border style. See HexaPDF::Type::Annotations::Widget#border_style.
-
The background color is determined by the widget’s background color. See HexaPDF::Type::Annotations::Widget#background_color.
Note: Rich text fields are currently not supported!
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/hexapdf/type/acro_form/appearance_generator.rb', line 208 def create_text_appearances default_resources = @document.acro_form.default_resources font, font_size, font_color = retrieve_font_information(default_resources) style = HexaPDF::Layout::Style.new(font: font, font_size: font_size, fill_color: font_color) border_style = @widget.border_style padding = [1, border_style.width].max @widget[:AS] = :N @widget.flag(:print) @widget.unflag(:hidden) rect = @widget[:Rect] rect.width = @document.config['acro_form.text_field.default_width'] if rect.width == 0 if rect.height == 0 style.font_size = (font_size == 0 ? @document.config['acro_form.default_font_size'] : font_size) rect.height = style.scaled_y_max - style.scaled_y_min + 2 * padding end width, height, matrix = perform_rotation(rect.width, rect.height) form = (@widget[:AP] ||= {})[:N] ||= @document.add({Type: :XObject, Subtype: :Form}) # Wrap existing object in Form class in case the PDF writer didn't include the /Subtype # key or the type of the object is wrong; we can do this since we know this has to be a # Form object unless form.type == :XObject && form[:Subtype] == :Form form = @document.wrap(form, type: :XObject, subtype: :Form) end form.value.replace({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height], Matrix: matrix, Resources: HexaPDF::Object.deep_copy(default_resources)}) form.contents = '' canvas = form.canvas apply_background_and_border(border_style, canvas) canvas.marked_content_sequence(:Tx) do if @field.field_value || @field.concrete_field_type == :list_box canvas.save_graphics_state do canvas.rectangle(padding, padding, width - 2 * padding, height - 2 * padding).clip_path.end_path case @field.concrete_field_type when :multiline_text_field draw_multiline_text(canvas, width, height, style, padding) when :list_box draw_list_box(canvas, width, height, style, padding) else draw_single_line_text(canvas, width, height, style, padding) end end end end end |