hexapdf 0.11.9 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE +1 -1
  4. data/examples/001-hello_world.rb +1 -1
  5. data/examples/002-graphics.rb +1 -1
  6. data/examples/003-arcs.rb +1 -1
  7. data/examples/004-optimizing.rb +1 -1
  8. data/examples/005-merging.rb +1 -1
  9. data/examples/006-standard_pdf_fonts.rb +1 -1
  10. data/examples/007-truetype.rb +1 -1
  11. data/examples/008-show_char_bboxes.rb +1 -1
  12. data/examples/009-text_layouter_alignment.rb +1 -1
  13. data/examples/010-text_layouter_inline_boxes.rb +1 -1
  14. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  15. data/examples/012-text_layouter_styling.rb +1 -1
  16. data/examples/013-text_layouter_shapes.rb +1 -1
  17. data/examples/014-text_in_polygon.rb +1 -1
  18. data/examples/015-boxes.rb +1 -1
  19. data/examples/016-frame_automatic_box_placement.rb +1 -1
  20. data/examples/017-frame_text_flow.rb +1 -1
  21. data/examples/018-composer.rb +1 -1
  22. data/examples/019-acro_form.rb +51 -0
  23. data/lib/hexapdf.rb +1 -1
  24. data/lib/hexapdf/cli.rb +3 -1
  25. data/lib/hexapdf/cli/batch.rb +1 -1
  26. data/lib/hexapdf/cli/command.rb +18 -9
  27. data/lib/hexapdf/cli/files.rb +1 -1
  28. data/lib/hexapdf/cli/form.rb +240 -0
  29. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +1 -1
  32. data/lib/hexapdf/cli/inspect.rb +1 -1
  33. data/lib/hexapdf/cli/merge.rb +1 -1
  34. data/lib/hexapdf/cli/modify.rb +1 -1
  35. data/lib/hexapdf/cli/optimize.rb +1 -1
  36. data/lib/hexapdf/cli/split.rb +1 -1
  37. data/lib/hexapdf/cli/watermark.rb +1 -1
  38. data/lib/hexapdf/composer.rb +2 -2
  39. data/lib/hexapdf/configuration.rb +66 -11
  40. data/lib/hexapdf/content.rb +3 -1
  41. data/lib/hexapdf/content/canvas.rb +5 -18
  42. data/lib/hexapdf/content/color_space.rb +111 -32
  43. data/lib/hexapdf/content/graphic_object.rb +1 -1
  44. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  45. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  46. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  47. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  48. data/lib/hexapdf/content/graphics_state.rb +1 -1
  49. data/lib/hexapdf/content/operator.rb +9 -9
  50. data/lib/hexapdf/content/parser.rb +18 -5
  51. data/lib/hexapdf/content/processor.rb +1 -1
  52. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  53. data/lib/hexapdf/data_dir.rb +1 -1
  54. data/lib/hexapdf/dictionary.rb +1 -1
  55. data/lib/hexapdf/dictionary_fields.rb +1 -1
  56. data/lib/hexapdf/document.rb +14 -5
  57. data/lib/hexapdf/document/files.rb +1 -1
  58. data/lib/hexapdf/document/fonts.rb +1 -1
  59. data/lib/hexapdf/document/images.rb +1 -1
  60. data/lib/hexapdf/document/pages.rb +3 -14
  61. data/lib/hexapdf/encryption.rb +1 -1
  62. data/lib/hexapdf/encryption/aes.rb +1 -1
  63. data/lib/hexapdf/encryption/arc4.rb +1 -1
  64. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  65. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  66. data/lib/hexapdf/encryption/identity.rb +1 -1
  67. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  68. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  69. data/lib/hexapdf/encryption/security_handler.rb +1 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
  71. data/lib/hexapdf/error.rb +1 -1
  72. data/lib/hexapdf/filter.rb +3 -3
  73. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  74. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  75. data/lib/hexapdf/filter/encryption.rb +1 -1
  76. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  77. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  78. data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
  79. data/lib/hexapdf/filter/predictor.rb +1 -1
  80. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  81. data/lib/hexapdf/font/cmap.rb +1 -1
  82. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  83. data/lib/hexapdf/font/cmap/writer.rb +1 -1
  84. data/lib/hexapdf/font/encoding.rb +1 -1
  85. data/lib/hexapdf/font/encoding/base.rb +1 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  87. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  88. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  89. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  90. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  91. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  92. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  93. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  94. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  95. data/lib/hexapdf/font/true_type.rb +1 -1
  96. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  97. data/lib/hexapdf/font/true_type/font.rb +1 -1
  98. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  99. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  100. data/lib/hexapdf/font/true_type/table.rb +1 -1
  101. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  102. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  103. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  104. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  105. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  106. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  107. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  108. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  109. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  114. data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
  115. data/lib/hexapdf/font/type1.rb +1 -1
  116. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  117. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  118. data/lib/hexapdf/font/type1/font.rb +1 -1
  119. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  120. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  121. data/lib/hexapdf/font/type1_wrapper.rb +67 -51
  122. data/lib/hexapdf/font_loader.rb +1 -1
  123. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  124. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  125. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  126. data/lib/hexapdf/image_loader.rb +1 -1
  127. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  128. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  129. data/lib/hexapdf/image_loader/png.rb +1 -1
  130. data/lib/hexapdf/importer.rb +2 -4
  131. data/lib/hexapdf/layout.rb +1 -1
  132. data/lib/hexapdf/layout/box.rb +1 -1
  133. data/lib/hexapdf/layout/frame.rb +1 -1
  134. data/lib/hexapdf/layout/image_box.rb +1 -1
  135. data/lib/hexapdf/layout/inline_box.rb +1 -1
  136. data/lib/hexapdf/layout/line.rb +1 -1
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +1 -1
  139. data/lib/hexapdf/layout/text_box.rb +1 -1
  140. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  141. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  142. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  143. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  144. data/lib/hexapdf/name_tree_node.rb +1 -1
  145. data/lib/hexapdf/number_tree_node.rb +1 -1
  146. data/lib/hexapdf/object.rb +2 -2
  147. data/lib/hexapdf/parser.rb +4 -3
  148. data/lib/hexapdf/pdf_array.rb +1 -1
  149. data/lib/hexapdf/rectangle.rb +31 -1
  150. data/lib/hexapdf/reference.rb +1 -1
  151. data/lib/hexapdf/revision.rb +2 -1
  152. data/lib/hexapdf/revisions.rb +1 -1
  153. data/lib/hexapdf/serializer.rb +1 -1
  154. data/lib/hexapdf/stream.rb +1 -1
  155. data/lib/hexapdf/task.rb +1 -1
  156. data/lib/hexapdf/task/dereference.rb +1 -1
  157. data/lib/hexapdf/task/optimize.rb +1 -1
  158. data/lib/hexapdf/tokenizer.rb +1 -1
  159. data/lib/hexapdf/type.rb +1 -1
  160. data/lib/hexapdf/type/acro_form.rb +7 -1
  161. data/lib/hexapdf/type/acro_form/appearance_generator.rb +401 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +300 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +220 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +157 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +186 -0
  167. data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
  168. data/lib/hexapdf/type/action.rb +1 -1
  169. data/lib/hexapdf/type/actions.rb +1 -1
  170. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  171. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  172. data/lib/hexapdf/type/actions/launch.rb +1 -1
  173. data/lib/hexapdf/type/actions/uri.rb +1 -1
  174. data/lib/hexapdf/type/annotation.rb +73 -3
  175. data/lib/hexapdf/type/annotations.rb +1 -1
  176. data/lib/hexapdf/type/annotations/link.rb +2 -2
  177. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  178. data/lib/hexapdf/type/annotations/text.rb +1 -1
  179. data/lib/hexapdf/type/annotations/widget.rb +239 -2
  180. data/lib/hexapdf/type/catalog.rb +21 -1
  181. data/lib/hexapdf/type/cid_font.rb +1 -1
  182. data/lib/hexapdf/type/embedded_file.rb +1 -1
  183. data/lib/hexapdf/type/file_specification.rb +1 -1
  184. data/lib/hexapdf/type/font.rb +18 -1
  185. data/lib/hexapdf/type/font_descriptor.rb +2 -2
  186. data/lib/hexapdf/type/font_simple.rb +1 -1
  187. data/lib/hexapdf/type/font_true_type.rb +1 -1
  188. data/lib/hexapdf/type/font_type0.rb +1 -1
  189. data/lib/hexapdf/type/font_type1.rb +16 -1
  190. data/lib/hexapdf/type/font_type3.rb +1 -1
  191. data/lib/hexapdf/type/form.rb +1 -1
  192. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  193. data/lib/hexapdf/type/icon_fit.rb +1 -1
  194. data/lib/hexapdf/type/image.rb +3 -1
  195. data/lib/hexapdf/type/info.rb +1 -1
  196. data/lib/hexapdf/type/names.rb +1 -1
  197. data/lib/hexapdf/type/object_stream.rb +1 -1
  198. data/lib/hexapdf/type/page.rb +1 -1
  199. data/lib/hexapdf/type/page_tree_node.rb +8 -11
  200. data/lib/hexapdf/type/resources.rb +16 -3
  201. data/lib/hexapdf/type/trailer.rb +2 -3
  202. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  203. data/lib/hexapdf/type/xref_stream.rb +1 -1
  204. data/lib/hexapdf/utils/bit_field.rb +38 -24
  205. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  206. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  207. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  208. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  209. data/lib/hexapdf/utils/object_hash.rb +1 -1
  210. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  211. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  212. data/lib/hexapdf/version.rb +2 -2
  213. data/lib/hexapdf/writer.rb +1 -1
  214. data/lib/hexapdf/xref_section.rb +1 -1
  215. data/test/hexapdf/content/common.rb +2 -2
  216. data/test/hexapdf/content/test_color_space.rb +71 -8
  217. data/test/hexapdf/content/test_operator.rb +22 -22
  218. data/test/hexapdf/content/test_parser.rb +14 -0
  219. data/test/hexapdf/document/test_fonts.rb +1 -1
  220. data/test/hexapdf/document/test_pages.rb +6 -6
  221. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  222. data/test/hexapdf/font/test_type1_wrapper.rb +32 -8
  223. data/test/hexapdf/test_document.rb +12 -0
  224. data/test/hexapdf/test_parser.rb +10 -0
  225. data/test/hexapdf/test_rectangle.rb +14 -0
  226. data/test/hexapdf/test_revision.rb +3 -0
  227. data/test/hexapdf/test_writer.rb +2 -2
  228. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
  229. data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
  230. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  231. data/test/hexapdf/type/acro_form/test_field.rb +124 -6
  232. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  233. data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
  234. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  235. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  236. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  237. data/test/hexapdf/type/test_annotation.rb +45 -0
  238. data/test/hexapdf/type/test_catalog.rb +18 -0
  239. data/test/hexapdf/type/test_font.rb +5 -0
  240. data/test/hexapdf/type/test_font_type1.rb +8 -0
  241. data/test/hexapdf/type/test_image.rb +7 -0
  242. data/test/hexapdf/type/test_page_tree_node.rb +20 -12
  243. data/test/hexapdf/type/test_resources.rb +20 -0
  244. data/test/hexapdf/type/test_trailer.rb +4 -0
  245. data/test/hexapdf/utils/test_bit_field.rb +13 -1
  246. data/test/test_helper.rb +1 -1
  247. metadata +37 -18
  248. data/lib/hexapdf/filter/dct_decode.rb +0 -60
@@ -0,0 +1,220 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/type/acro_form/variable_text_field'
38
+ require 'hexapdf/type/acro_form/appearance_generator'
39
+
40
+ module HexaPDF
41
+ module Type
42
+ module AcroForm
43
+
44
+ # AcroForm choice fields contain multiple text items of which one (or, if so flagged, more)
45
+ # may be selected.
46
+ #
47
+ # They are divided into scrollable list boxes and combo boxes. To create a list or combo box,
48
+ # use the appropriate convenience methods on the main Form instance
49
+ # (HexaPDF::Document#acro_form). By using those methods, everything needed is automatically
50
+ # set up.
51
+ #
52
+ # == Type Specific Field Flags
53
+ #
54
+ # :combo:: If set, the field represents a comb box.
55
+ #
56
+ # :edit:: If set, the combo box includes an editable text box for entering arbitrary values.
57
+ # Therefore the 'combo' flag also needs to be set.
58
+ #
59
+ # :sort:: The option items have to be sorted alphabetically. This flag is intended for PDF
60
+ # writers, not readers which should display the items in the order they appear.
61
+ #
62
+ # :multi_select:: If set, more than one item may be selected.
63
+ #
64
+ # :do_not_spell_check:: The text should not be spell-checked.
65
+ #
66
+ # :commit_on_sel_change:: If set, a new value should be commited as soon as a selection is
67
+ # made.
68
+ #
69
+ # See: PDF1.7 s12.7.4.4
70
+ class ChoiceField < VariableTextField
71
+
72
+ define_field :Opt, type: PDFArray
73
+ define_field :TI, type: Integer, default: 0
74
+ define_field :I, type: PDFArray, version: '1.4'
75
+
76
+ # Updated list of field flags.
77
+ FLAGS_BIT_MAPPING = superclass::FLAGS_BIT_MAPPING.merge(
78
+ {
79
+ combo: 17,
80
+ edit: 18,
81
+ sort: 19,
82
+ multi_select: 21,
83
+ do_not_spell_check: 22,
84
+ commit_on_sel_change: 26,
85
+ }
86
+ ).freeze
87
+
88
+ # Initializes the choice field to be a list box.
89
+ #
90
+ # This method should only be called directly after creating a new choice field because it
91
+ # doesn't completely reset the object.
92
+ def initialize_as_list_box
93
+ self[:V] = nil
94
+ unflag(:combo)
95
+ end
96
+
97
+ # Initializes the button field to be a combo box.
98
+ #
99
+ # This method should only be called directly after creating a new choice field because it
100
+ # doesn't completely reset the object.
101
+ def initialize_as_combo_box
102
+ self[:V] = nil
103
+ flag(:combo)
104
+ end
105
+
106
+ # Returns +true+ if this choice field represents a list box.
107
+ def list_box?
108
+ !combo_box?
109
+ end
110
+
111
+ # Returns +true+ if this choice field represents a combo box.
112
+ def combo_box?
113
+ flagged?(:combo)
114
+ end
115
+
116
+ # Returns the field value which represents the currently selected item(s).
117
+ #
118
+ # If no item is selected, +nil+ is returned. If multiple values are selected, the return
119
+ # value is an array of strings, otherwise it is just a string.
120
+ def field_value
121
+ process_value(self[:V])
122
+ end
123
+
124
+ # Sets the field value to the given string or array of strings.
125
+ def field_value=(value)
126
+ items = option_items
127
+ all_included = [value].flatten.all? {|v| items.include?(v) }
128
+ self[:V] = if (combo_box? && value.kind_of?(String) &&
129
+ (flagged?(:edit) || all_included)) ||
130
+ (list_box? && all_included &&
131
+ (value.kind_of?(String) || flagged?(:multi_select)))
132
+ value
133
+ else
134
+ @document.config['acro_form.on_invalid_value'].call(self, value)
135
+ end
136
+ end
137
+
138
+ # Returns the default field value.
139
+ #
140
+ # See: #field_value
141
+ def default_field_value
142
+ process_value(self[:DV])
143
+ end
144
+
145
+ # Sets the default field value.
146
+ #
147
+ # See: #field_value=
148
+ def default_field_value=(value)
149
+ items = option_items
150
+ self[:DV] = if [value].flatten.all? {|v| items.include?(v) }
151
+ value
152
+ else
153
+ @document.config['acro_form.on_invalid_value'].call(self, value)
154
+ end
155
+ end
156
+
157
+ # Returns the array with the available option items.
158
+ def option_items
159
+ key?(:Opt) ? process_value(self[:Opt]) : self[:Opt] ||= []
160
+ end
161
+
162
+ # Sets the array with the available option items to the given value.
163
+ def option_items=(value)
164
+ self[:Opt] = value
165
+ end
166
+
167
+ # Returns the concrete choice field type, either :list_box, :combo_box or
168
+ # :editable_combo_box.
169
+ def concrete_field_type
170
+ if combo_box?
171
+ flagged?(:edit) ? :editable_combo_box : :combo_box
172
+ else
173
+ :list_box
174
+ end
175
+ end
176
+
177
+ # Creates appropriate appearances for all widgets if they don't already exist.
178
+ #
179
+ # For information on how this is done see AppearanceGenerator.
180
+ #
181
+ # Note that an appearance for a choice field widget is *always* created even if there is an
182
+ # existing one to make sure the current field value is properly represented.
183
+ def create_appearances
184
+ appearance_generator_class = document.config.constantize('acro_form.appearance_generator')
185
+ each_widget do |widget|
186
+ if combo_box?
187
+ appearance_generator_class.new(widget).create_combo_box_appearances
188
+ else
189
+ raise HexaPDF::Error, "List boxes not yet supported"
190
+ end
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ # Uses the HexaPDF::DictionaryFields::StringConverter to process the value (a string or an
197
+ # array of strings) so that it contains only normalized strings.
198
+ def process_value(value)
199
+ value = value.value if value.kind_of?(PDFArray)
200
+ if value.kind_of?(Array)
201
+ value.map! {|item| DictionaryFields::StringConverter.convert(item, nil, nil) || item }
202
+ else
203
+ DictionaryFields::StringConverter.convert(value, nil, nil) || value
204
+ end
205
+ end
206
+
207
+ def perform_validation #:nodoc:
208
+ if field_type != :Ch
209
+ yield("Field /FT of AcroForm choie field has to be :Ch", true)
210
+ self[:FT] = :Ch
211
+ end
212
+
213
+ super
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+ end
220
+ end
@@ -4,7 +4,7 @@
4
4
  # This file is part of HexaPDF.
5
5
  #
6
6
  # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
7
+ # Copyright (C) 2014-2020 Thomas Leitner
8
8
  #
9
9
  # HexaPDF is free software: you can redistribute it and/or modify it
10
10
  # under the terms of the GNU Affero General Public License version 3 as
@@ -35,24 +35,75 @@
35
35
  #++
36
36
 
37
37
  require 'hexapdf/dictionary'
38
+ require 'hexapdf/error'
39
+ require 'hexapdf/utils/bit_field'
40
+ require 'hexapdf/type/annotations'
38
41
 
39
42
  module HexaPDF
40
43
  module Type
41
44
  module AcroForm
42
45
 
43
- # Field dictionaries are used to define the properties of form fields of AcroForm objects.
46
+ # AcroForm field dictionaries are used to define the properties of form fields of AcroForm
47
+ # objects.
44
48
  #
45
49
  # Fields can be organized in a hierarchy using the /Kids and /Parent keys, for namespacing
46
50
  # purposes and to set default values. Those fields that have other fields as children are
47
51
  # called non-terminal fields, otherwise they are called terminal fields.
48
52
  #
53
+ # While field objects can be created manually, it is best to use the various +create_+ methods
54
+ # of the main Form object to create them so that all necessary things are set up correctly.
55
+ #
56
+ # == Field Types
57
+ #
58
+ # Subclasses are used to implement the specific AcroForm field types:
59
+ #
60
+ # * ButtonField implements the button fields (pushbuttons, check boxes and radio buttons)
61
+ # * TextField implements single or multiline text fields.
62
+ # * ChoiceField implements scrollable list boxes or (editable) combo boxes.
63
+ # * SignatureField implements signature fields.
64
+ #
65
+ # == Field Flags
66
+ #
67
+ # Various characteristics of a field can be changed by setting a certain flag. Some flags are
68
+ # defined for all types of field, some are specific to a certain type.
69
+ #
70
+ # The following flags apply to all fields:
71
+ #
72
+ # :read_only:: The field is read only which means the user can't change the value or interact
73
+ # with associated widget annotations.
74
+ #
75
+ # :required:: The field is required if the form is exported by a submit-form action.
76
+ #
77
+ # :no_export:: The field should *not* be exported by a submit-form action.
78
+ #
79
+ # == Field Type Implementation Notes
80
+ #
81
+ # If an AcroForm field type adds additional inheritable dictionary fields, it has to set the
82
+ # constant +INHERITABLE_FIELDS+ to all inheritable dictionary fields, including those from the
83
+ # superclass.
84
+ #
85
+ # Similarily, if additional flags are provided, the constant +FLAGS_BIT_MAPPING+ has to be set
86
+ # to combination of the superclass value of the constant and the mapping of flag names to bit
87
+ # indices.
88
+ #
49
89
  # See: PDF1.7 s12.7.3.1
50
90
  class Field < Dictionary
51
91
 
52
- define_type :XXAcroFormField
92
+ # Provides a #value method for hash that returns self so that a Hash can be used
93
+ # interchangably with a HexaPDF::Dictionary.
94
+ module HashRefinement
95
+ refine Hash do
96
+ def value
97
+ self
98
+ end
99
+ end
100
+ end
101
+
102
+ using HashRefinement
53
103
 
54
- # List of inheritable fields.
55
- INHERITABLE_FIELDS = [:FT, :Ff, :V, :DV]
104
+ extend Utils::BitField
105
+
106
+ define_type :XXAcroFormField
56
107
 
57
108
  define_field :FT, type: Symbol, allowed_values: [:Btn, :Tx, :Ch, :Sig]
58
109
  define_field :Parent, type: :XXAcroFormField
@@ -61,10 +112,49 @@ module HexaPDF
61
112
  define_field :TU, type: String, version: '1.3'
62
113
  define_field :TM, type: String, version: '1.3'
63
114
  define_field :Ff, type: Integer, default: 0
64
- define_field :V, type: [Symbol, String, Stream, PDFArray, Dictionary]
65
- define_field :DV, type: [Symbol, String, Stream, PDFArray, Dictionary]
115
+ define_field :V, type: [Dictionary, Symbol, String, Stream, PDFArray]
116
+ define_field :DV, type: [Dictionary, Symbol, String, Stream, PDFArray]
66
117
  define_field :AA, type: Dictionary, version: '1.2'
67
118
 
119
+ # The inheritable dictionary fields common to all AcroForm field types.
120
+ INHERITABLE_FIELDS = [:FT, :Ff, :V, :DV].freeze
121
+
122
+ ##
123
+ # :method: flags
124
+ #
125
+ # Returns an array of flag names representing the set bit flags.
126
+ #
127
+
128
+ ##
129
+ # :method: flagged?
130
+ # :call-seq:
131
+ # flagged?(flag)
132
+ #
133
+ # Returns +true+ if the given flag is set. The argument can either be the flag name or the
134
+ # bit index.
135
+ #
136
+
137
+ ##
138
+ # :method: flag
139
+ # :call-seq:
140
+ # flag(*flags, clear_existing: false)
141
+ #
142
+ # Sets the given flags, given as flag names or bit indices. If +clear_existing+ is +true+,
143
+ # all prior flags will be cleared.
144
+ #
145
+ bit_field(:flags, {read_only: 0, required: 1, no_export: 2},
146
+ lister: "flags", getter: "flagged?", setter: "flag", unsetter: "unflag",
147
+ value_getter: "self[:Ff]", value_setter: "self[:Ff]")
148
+
149
+ # Treats +name+ as an inheritable dictionary field and resolves its value for the AcroForm
150
+ # field +field+.
151
+ def self.inherited_value(field, name)
152
+ while field.value[name].nil? && (parent = field[:Parent])
153
+ field = parent
154
+ end
155
+ field.value[name].nil? ? nil : field[name]
156
+ end
157
+
68
158
  # Form fields must always be indirect objects.
69
159
  def must_be_indirect?
70
160
  true
@@ -72,15 +162,13 @@ module HexaPDF
72
162
 
73
163
  # Returns the value for the entry +name+.
74
164
  #
75
- # If +name+ is an inheritable value and the value has not been set on this field object, its
165
+ # If +name+ is an inheritable field and the value has not been set on this field object, its
76
166
  # value is retrieved from the parent fields.
77
167
  #
78
168
  # See: Dictionary#[]
79
169
  def [](name)
80
- if value[name].nil? && INHERITABLE_FIELDS.include?(name)
81
- field = self
82
- field = field[:Parent] while field.value[name].nil? && field[:Parent]
83
- field == self || field.value[name].nil? ? super : field[name]
170
+ if value[name].nil? && self.class::INHERITABLE_FIELDS.include?(name)
171
+ self.class.inherited_value(self, name) || super
84
172
  else
85
173
  super
86
174
  end
@@ -88,30 +176,145 @@ module HexaPDF
88
176
 
89
177
  # Returns the type of the field, either :Btn (pushbuttons, check boxes, radio buttons), :Tx
90
178
  # (text fields), :Ch (scrollable list boxes, combo boxes) or :Sig (signature fields).
179
+ #
180
+ # Also see #concrete_field_type
91
181
  def field_type
92
182
  self[:FT]
93
183
  end
94
184
 
185
+ # Returns the concrete field type (:button_field, :text_field, :choice_field or
186
+ # :signature_field) or +nil+ is no field type is set.
187
+ #
188
+ # In constrast to #field_type this method also considers the field flags and not just the
189
+ # field type. This means that subclasses can return a more concrete name for the field type.
190
+ #
191
+ # Also see #field_type
192
+ def concrete_field_type
193
+ case self[:FT]
194
+ when :Btn then :button_field
195
+ when :Tx then :text_field
196
+ when :Ch then :choice_field
197
+ when :Sig then :signature_field
198
+ else nil
199
+ end
200
+ end
201
+
202
+ # Returns the name of the field or +nil+ if no name is set.
203
+ def field_name
204
+ self[:T]
205
+ end
206
+
95
207
  # Returns the full name of the field or +nil+ if no name is set.
96
208
  #
97
209
  # The full name of a field is constructed using the full name of the parent field, a period
98
- # and the partial name of the field.
99
- def full_name
210
+ # and the field name of the field.
211
+ def full_field_name
100
212
  if key?(:Parent)
101
- [self[:Parent].full_name, self[:T]].compact.join('.')
213
+ [self[:Parent].full_field_name, field_name].compact.join('.')
102
214
  else
103
- self[:T]
215
+ field_name
104
216
  end
105
217
  end
106
218
 
219
+ # Returns the alternate field name that should be used for display purposes (e.g. Acrobat
220
+ # shows this as tool tip).
221
+ def alternate_field_name
222
+ self[:TU]
223
+ end
224
+
225
+ # Sets the alternate field name.
226
+ #
227
+ # See #alternate_field_name
228
+ def alternate_field_name=(value)
229
+ self[:TU] = value
230
+ end
231
+
107
232
  # Returns +true+ if this is a terminal field.
108
233
  def terminal_field?
109
234
  kids = self[:Kids]
110
- kids.nil? || kids.empty? || kids.any? {|kid| kid[:Subtype] == :Widget }
235
+ # PDF 2.0 s12.7.4.2 clarifies how to do check for fields since PDF 1.7 isn't clear
236
+ kids.nil? || kids.empty? || kids.none? {|kid| kid.key?(:T) }
237
+ end
238
+
239
+ # :call-seq:
240
+ # field.each_widget {|widget| block} -> field
241
+ # field.each_widget -> Enumerator
242
+ #
243
+ # Yields each widget, i.e. visual representation, of this field.
244
+ #
245
+ # See: HexaPDF::Type::Annotations::Widget
246
+ def each_widget # :yields: widget
247
+ return to_enum(__method__) unless block_given?
248
+ if self[:Subtype]
249
+ yield(document.wrap(self))
250
+ elsif terminal_field?
251
+ self[:Kids]&.each {|kid| yield(document.wrap(kid)) }
252
+ end
253
+ self
254
+ end
255
+
256
+ # Creates a new widget annotation for this form field (must be a terminal field!) on the
257
+ # given +page+, adding the +values+ to the created widget annotation oject.
258
+ #
259
+ # If +allow_embedded+ is +false+, embedding the first widget in the field itself is not
260
+ # allowed.
261
+ #
262
+ # The +values+ argument should at least include :Rect for setting the visible area of the
263
+ # widget.
264
+ #
265
+ # If the field already has an embedded widget, i.e. field and widget are the same PDF
266
+ # object, its widget data is extracted to a new PDF object and stored in the /Kids field,
267
+ # together with the new widget annotation. Note that this means that a possible reference to
268
+ # the formerly embedded widget (=this field) is not valid anymore!
269
+ #
270
+ # See: HexaPDF::Type::Annotations::Widget
271
+ def create_widget(page, allow_embedded: true, **values)
272
+ unless terminal_field?
273
+ raise HexaPDF::Error, "Widgets can only be added to terminal fields"
274
+ end
275
+
276
+ widget_data = {Type: :Annot, Subtype: :Widget, Rect: [0, 0, 0, 0], **values}
277
+
278
+ if !allow_embedded || key?(:Subtype) || (key?(:Kids) && !self[:Kids].empty?)
279
+ kids = self[:Kids] ||= []
280
+ kids << extract_widget if key?(:Subtype)
281
+ widget = document.add(widget_data)
282
+ widget[:Parent] = self
283
+ self[:Kids] << widget
284
+ else
285
+ value.update(widget_data)
286
+ widget = document.wrap(self)
287
+ end
288
+
289
+ (page[:Annots] ||= []) << widget
290
+
291
+ widget
111
292
  end
112
293
 
113
294
  private
114
295
 
296
+ # An array of all widget annotation field names.
297
+ WIDGET_FIELDS = HexaPDF::Type::Annotations::Widget.each_field.map(&:first).uniq - [:Parent]
298
+
299
+ # Returns a new dictionary object with all the widget annotation data that is stored
300
+ # directly in the field and adjust the references accordingly. If the field doesn't have any
301
+ # widget data, +nil+ is returned.
302
+ def extract_widget
303
+ return unless key?(:Subtype)
304
+ data = WIDGET_FIELDS.each_with_object({}) do |key, hash|
305
+ hash[key] = delete(key) if key?(key)
306
+ end
307
+ widget = document.add(data, type: :Annot)
308
+ widget[:Parent] = self
309
+ document.pages.each do |page|
310
+ if page.key?(:Annots) && (index = page[:Annots].index {|annot| annot.data == self.data })
311
+ page[:Annots][index] = widget
312
+ break # Each annotation dictionary may only appear on one page, see PDF1.7 12.5.2
313
+ end
314
+ end
315
+ widget
316
+ end
317
+
115
318
  def perform_validation #:nodoc:
116
319
  super
117
320
  if terminal_field? && field_type.nil?