hexapdf 0.11.9 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,276 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../../content/common'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/type/acro_form/button_field'
7
+
8
+ describe HexaPDF::Type::AcroForm::ButtonField do
9
+ before do
10
+ @doc = HexaPDF::Document.new
11
+ @field = @doc.add({FT: :Btn, T: 'button'}, type: :XXAcroFormField, subtype: :Btn)
12
+ end
13
+
14
+ it "can be initialized as push button" do
15
+ @field.initialize_as_push_button
16
+ assert_nil(@field[:V])
17
+ assert(@field.push_button?)
18
+ end
19
+
20
+ it "can be initialized as check box" do
21
+ @field.initialize_as_check_box
22
+ assert_equal(:Off, @field[:V])
23
+ assert(@field.check_box?)
24
+ end
25
+
26
+ it "can be initialized as radio button" do
27
+ @field.initialize_as_radio_button
28
+ assert_equal(:Off, @field[:V])
29
+ assert_equal(1 << 15, @field[:Ff])
30
+ end
31
+
32
+ describe "push button" do
33
+ before do
34
+ @field.flag(:push_button)
35
+ end
36
+
37
+ it "can be asked whether it is a push button field" do
38
+ @field.flag(:push_button)
39
+ assert(@field.push_button?)
40
+ end
41
+
42
+ it "always returns nil when getting the field value" do
43
+ @field[:V] = :test
44
+ assert_nil(@field.field_value)
45
+ end
46
+
47
+ it "doesn't set a field value" do
48
+ @field.field_value = :test
49
+ assert_nil(@field[:V])
50
+ end
51
+
52
+ it "returns the correct concrete field type" do
53
+ assert_equal(:push_button, @field.concrete_field_type)
54
+ end
55
+
56
+ it "applies sensible default values when creating a widget" do
57
+ widget = @field.create_widget(@doc.pages.add)
58
+ border_style = widget.border_style
59
+ assert_equal([0], border_style.color.components)
60
+ assert_equal(1, border_style.width)
61
+ assert_equal(:beveled, border_style.style)
62
+ assert_equal([0.5], widget.background_color.components)
63
+ end
64
+ end
65
+
66
+ describe "check box" do
67
+ before do
68
+ @field.unflag(:push_button)
69
+ @field.unflag(:radio)
70
+ end
71
+
72
+ it "can be asked whether it is a check box field" do
73
+ assert(@field.check_box?)
74
+ end
75
+
76
+ it "returns a correct field value" do
77
+ refute(@field.field_value)
78
+ @field[:V] = :Off
79
+ refute(@field.field_value)
80
+ @field[:V] = :Yes
81
+ assert(@field.field_value)
82
+ end
83
+
84
+ it "sets a correct field value" do
85
+ @field.field_value = true
86
+ assert_equal(:Yes, @field[:V])
87
+ @field.field_value = false
88
+ assert_equal(:Off, @field[:V])
89
+ end
90
+
91
+ it "returns the correct concrete field type" do
92
+ assert_equal(:check_box, @field.concrete_field_type)
93
+ end
94
+
95
+ it "updates the widgets after setting the field value" do
96
+ widget = @field.create_widget(@doc.pages.add)
97
+ @field.field_value = true
98
+ assert_equal(:Yes, widget[:AS])
99
+ end
100
+
101
+ it "can determine the name of the on state" do
102
+ assert_equal(:Yes, @field.check_box_on_name)
103
+ widget = @field.create_widget(@doc.pages.add)
104
+ assert_equal(:Yes, @field.check_box_on_name)
105
+ widget[:AP][:N].delete(:Yes)
106
+ widget[:AP][:N][:On] = nil
107
+ assert_equal(:On, @field.check_box_on_name)
108
+ end
109
+
110
+ it "applies sensible default values when creating a widget" do
111
+ widget = @field.create_widget(@doc.pages.add)
112
+ assert_equal({Yes: nil, Off: nil}, widget[:AP][:N].value)
113
+ border_style = widget.border_style
114
+ assert_equal([0], border_style.color.components)
115
+ assert_equal(1, border_style.width)
116
+ assert_equal(:solid, border_style.style)
117
+ assert_equal([1], widget.background_color.components)
118
+ assert_equal(:check, widget.marker_style.style)
119
+ end
120
+ end
121
+
122
+ describe "radio button" do
123
+ before do
124
+ @field.unflag(:push_button)
125
+ @field.flag(:radio)
126
+ end
127
+
128
+ it "can be asked whether it is a radio button field" do
129
+ assert(@field.radio_button?)
130
+ end
131
+
132
+ it "returns a correct field value" do
133
+ assert_nil(@field.field_value)
134
+ @field[:V] = :Off
135
+ assert_nil(@field.field_value)
136
+ @field[:V] = :name
137
+ assert_equal(:name, @field.field_value)
138
+ end
139
+
140
+ it "sets a correct field value" do
141
+ @field.create_widget(@doc.pages.add, value: :button1)
142
+
143
+ @field.field_value = :button1
144
+ assert_equal(:button1, @field[:V])
145
+ @field.field_value = nil
146
+ assert_equal(:Off, @field[:V])
147
+ assert_raises(HexaPDF::Error) { @field.field_value = :unknown }
148
+ end
149
+
150
+ it "returns the correct concrete field type" do
151
+ assert_equal(:radio_button, @field.concrete_field_type)
152
+ end
153
+
154
+ it "updates the widgets after setting the field value" do
155
+ widget = @field.create_widget(@doc.pages.add, value: :Test)
156
+ @field.field_value = :Test
157
+ assert_equal(:Test, widget[:AS])
158
+ end
159
+
160
+ it "returns an array of possible values" do
161
+ @field.create_widget(@doc.pages.add, value: :Test)
162
+ @field.create_widget(@doc.pages.add, value: :x)
163
+ @field.create_widget(@doc.pages.add, value: :y)
164
+ assert_equal([:Test, :x, :y], @field.radio_button_values)
165
+ end
166
+
167
+ it "applies sensible default values when creating a widget" do
168
+ widget = @field.create_widget(@doc.pages.add, value: 'test')
169
+ border_style = widget.border_style
170
+ assert_equal([0], border_style.color.components)
171
+ assert_equal(1, border_style.width)
172
+ assert_equal(:solid, border_style.style)
173
+ assert_equal([1], widget.background_color.components)
174
+ assert_equal(:circle, widget.marker_style.style)
175
+ end
176
+
177
+ it "always creates standalone widgets" do
178
+ refute_same(@field.data, @field.create_widget(@doc.pages.add, value: 'test'))
179
+ end
180
+
181
+ it "fails if the value argument is not provided for create_widget" do
182
+ assert_raises(ArgumentError) { @field.create_widget(@doc.pages.add) }
183
+ end
184
+ end
185
+
186
+ it "returns a default field value" do
187
+ assert_method_invoked(@field, :normalized_field_value, [:DV]) do
188
+ @field.default_field_value
189
+ end
190
+ end
191
+
192
+ it "sets a default field value" do
193
+ assert_method_invoked(@field, :normalized_field_value_set, [:DV, :value]) do
194
+ @field.default_field_value = :value
195
+ end
196
+ end
197
+
198
+ it "resolves /Opt as inheritable field" do
199
+ @field[:Parent] = {Opt: 5}
200
+ assert_equal(5, @field[:Opt])
201
+
202
+ @field[:Opt] = 6
203
+ assert_equal(6, @field[:Opt])
204
+ end
205
+
206
+ describe "create_appearances" do
207
+ it "works for check boxes" do
208
+ @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
209
+ @field.create_appearances
210
+ assert(@field[:AP][:N][:Yes])
211
+ end
212
+
213
+ it "works for radio buttons" do
214
+ @field.initialize_as_radio_button
215
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0], value: :test)
216
+ @field.create_appearances
217
+ assert(widget[:AP][:N][:test])
218
+ end
219
+
220
+ it "won't generate appearances if they already exist" do
221
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
222
+ @field.create_appearances
223
+ yes = widget.appearance.normal_appearance[:Yes]
224
+ off = widget.appearance.normal_appearance[:Off]
225
+ widget.appearance.normal_appearance.delete(:Off)
226
+ @field.create_appearances
227
+ assert_same(yes, widget.appearance.normal_appearance[:Yes])
228
+ refute_same(off, widget.appearance.normal_appearance[:Off])
229
+ end
230
+
231
+ it "fails for unsupported button types" do
232
+ @field.flag(:push_button)
233
+ @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
234
+ assert_raises(HexaPDF::Error) { @field.create_appearances }
235
+ end
236
+
237
+ it "uses the configuration option acro_form.appearance_generator" do
238
+ @doc.config['acro_form.appearance_generator'] = 'NonExistent'
239
+ assert_raises(Exception) { @field.create_appearances }
240
+ end
241
+ end
242
+
243
+ describe "update_widgets" do
244
+ it "does nothing for push buttons" do
245
+ @field.flag(:push_button)
246
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
247
+ @field.update_widgets
248
+ assert_nil(widget[:AS])
249
+ end
250
+
251
+ it "sets the /AS entry correctly" do
252
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
253
+ assert_nil(widget[:AS])
254
+ @field.update_widgets
255
+ assert_equal(:Off, widget[:AS])
256
+
257
+ @field[:V] = :Yes
258
+ @field.update_widgets
259
+ assert_equal(:Yes, widget[:AS])
260
+ end
261
+ end
262
+
263
+ describe "validation" do
264
+ it "checks the value of the /FT field" do
265
+ @field.delete(:FT)
266
+ refute(@field.validate(auto_correct: false))
267
+ assert(@field.validate)
268
+ assert_equal(:Btn, @field.field_type)
269
+ end
270
+
271
+ it "sets the field value to :Off it it is not set" do
272
+ assert(@field.validate)
273
+ assert_equal(:Off, @field[:V])
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,137 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/acro_form/choice_field'
6
+
7
+ describe HexaPDF::Type::AcroForm::ChoiceField do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @field = @doc.add({FT: :Ch, T: 'choice'}, type: :XXAcroFormField, subtype: :Ch)
11
+ end
12
+
13
+ it "can be initialized as list box" do
14
+ @field.initialize_as_list_box
15
+ assert_nil(@field[:V])
16
+ assert(@field.list_box?)
17
+ end
18
+
19
+ it "can be initialized as combo box" do
20
+ @field.initialize_as_combo_box
21
+ assert_nil(@field[:V])
22
+ assert(@field.combo_box?)
23
+ end
24
+
25
+ describe "field_value" do
26
+ it "returns the correct Unicode string value" do
27
+ @field[:V] = "H\xe4llo".b
28
+ assert_equal("Hällo", @field.field_value)
29
+ end
30
+
31
+ it "returns an array of Unicode string values" do
32
+ @field[:V] = ["H\xe4llo".b, "\xFE\xFF".b << "Óthér".encode('UTF-16BE').b]
33
+ assert_equal(["Hällo", "Óthér"], @field.field_value)
34
+ end
35
+ end
36
+
37
+ describe "field_value=" do
38
+ before do
39
+ @field.option_items = ["test", "other"]
40
+ end
41
+
42
+ describe "combo_box" do
43
+ before do
44
+ @field.initialize_as_combo_box
45
+ end
46
+
47
+ it "can set the value for an uneditable combo box" do
48
+ @field.field_value = 'test'
49
+ assert_equal("test", @field[:V])
50
+ end
51
+
52
+ it "can set the value for an editable combo box" do
53
+ @field.flag(:edit)
54
+ @field.field_value = 'another'
55
+ assert_equal("another", @field[:V])
56
+ end
57
+
58
+ it "fails if mulitple values are provided for a combo box" do
59
+ assert_raises(HexaPDF::Error) { @field.field_value = ['a', 'b'] }
60
+ end
61
+
62
+ it "fails if an unlisted value is specified for an uneditable combo box" do
63
+ assert_raises(HexaPDF::Error) { @field.field_value = 'a' }
64
+ end
65
+ end
66
+
67
+ describe "list_box" do
68
+ before do
69
+ @field.initialize_as_list_box
70
+ end
71
+
72
+ it "can set a single value" do
73
+ @field.field_value = 'test'
74
+ assert_equal("test", @field[:V])
75
+ end
76
+
77
+ it "can set a multiple values if the list box is a multi-select" do
78
+ @field.flag(:multi_select)
79
+ @field.field_value = ['test', 'other']
80
+ assert_equal(['test', 'other'], @field[:V].value)
81
+ end
82
+
83
+ it "fails if mulitple values are provided but the list box is not a multi-select" do
84
+ assert_raises(HexaPDF::Error) { @field.field_value = ['a', 'b'] }
85
+ end
86
+
87
+ it "fails if an unlisted value is specified" do
88
+ assert_raises(HexaPDF::Error) { @field.field_value = 'a' }
89
+ end
90
+ end
91
+ end
92
+
93
+ it "sets and returns the default field value" do
94
+ @field.option_items = ["hällo"]
95
+ @field.default_field_value = 'hällo'
96
+ assert_equal('hällo', @field.default_field_value)
97
+ assert_raises(HexaPDF::Error) { @field.default_field_value = 'unknown' }
98
+ end
99
+
100
+ it "sets and returns the array with the option items" do
101
+ assert_equal([], @field.option_items)
102
+ @field.option_items = ["H\xe4llo".b, "\xFE\xFF".b << "Töne".encode('UTF-16BE').b]
103
+ assert_equal(["Hällo", "Töne"], @field.option_items)
104
+ end
105
+
106
+ it "returns the correct concrete field type" do
107
+ assert_equal(:list_box, @field.concrete_field_type)
108
+ @field.initialize_as_combo_box
109
+ assert_equal(:combo_box, @field.concrete_field_type)
110
+ end
111
+
112
+ describe "create_appearances" do
113
+ before do
114
+ @widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
115
+ end
116
+
117
+ it "works for combo box fields" do
118
+ @field.initialize_as_combo_box
119
+ @field.set_default_appearance_string
120
+ @field.create_appearances
121
+ assert(@field[:AP][:N])
122
+ end
123
+
124
+ it "fails for list boxes" do
125
+ assert_raises(HexaPDF::Error) { @field.create_appearances }
126
+ end
127
+ end
128
+
129
+ describe "validation" do
130
+ it "checks the value of the /FT field" do
131
+ @field.delete(:FT)
132
+ refute(@field.validate(auto_correct: false))
133
+ assert(@field.validate)
134
+ assert_equal(:Ch, @field.field_type)
135
+ end
136
+ end
137
+ end
@@ -4,6 +4,15 @@ require 'test_helper'
4
4
  require 'hexapdf/document'
5
5
  require 'hexapdf/type/acro_form/field'
6
6
 
7
+ describe HexaPDF::Type::AcroForm::Field::HashRefinement do
8
+ using HexaPDF::Type::AcroForm::Field::HashRefinement
9
+
10
+ it "returns self when calling value" do
11
+ x = {}
12
+ assert_same(x, x.value)
13
+ end
14
+ end
15
+
7
16
  describe HexaPDF::Type::AcroForm::Field do
8
17
  before do
9
18
  @doc = HexaPDF::Document.new
@@ -24,6 +33,14 @@ describe HexaPDF::Type::AcroForm::Field do
24
33
  assert_equal(:Ch, @field[:FT])
25
34
  end
26
35
 
36
+ it "has convenience methods for accessing the field flags" do
37
+ assert_equal([], @field.flags)
38
+ refute(@field.flagged?(:required))
39
+ @field.flag(:required, 2)
40
+ assert(@field.flagged?(2))
41
+ assert_equal(6, @field[:Ff])
42
+ end
43
+
27
44
  it "returns the field type" do
28
45
  assert_nil(@field.field_type)
29
46
 
@@ -31,17 +48,42 @@ describe HexaPDF::Type::AcroForm::Field do
31
48
  assert_equal(:Tx, @field.field_type)
32
49
  end
33
50
 
51
+ it "returns the concrete field type" do
52
+ assert_nil(@field.concrete_field_type)
53
+
54
+ @field[:FT] = :Tx
55
+ assert_equal(:text_field, @field.concrete_field_type)
56
+ @field[:FT] = :Btn
57
+ assert_equal(:button_field, @field.concrete_field_type)
58
+ @field[:FT] = :Ch
59
+ assert_equal(:choice_field, @field.concrete_field_type)
60
+ @field[:FT] = :Sig
61
+ assert_equal(:signature_field, @field.concrete_field_type)
62
+ end
63
+
64
+ it "returns the field name" do
65
+ assert_nil(@field.field_name)
66
+ @field[:T] = 'test'
67
+ assert_equal('test', @field.field_name)
68
+ end
69
+
34
70
  it "returns the full name of the field" do
35
- assert_nil(@field.full_name)
71
+ assert_nil(@field.full_field_name)
36
72
 
37
73
  @field[:T] = "Test"
38
- assert_equal("Test", @field.full_name)
74
+ assert_equal("Test", @field.full_field_name)
39
75
 
40
76
  @field[:Parent] = {}
41
- assert_equal("Test", @field.full_name)
77
+ assert_equal("Test", @field.full_field_name)
42
78
 
43
79
  @field[:Parent] = {T: 'Parent'}
44
- assert_equal("Parent.Test", @field.full_name)
80
+ assert_equal("Parent.Test", @field.full_field_name)
81
+ end
82
+
83
+ it "allows setting and retrieving the alternate field name" do
84
+ @field.alternate_field_name = 'Alternate'
85
+ assert_equal('Alternate', @field.alternate_field_name)
86
+ assert_equal('Alternate', @field[:TU])
45
87
  end
46
88
 
47
89
  it "returns whether the field is a terminal field" do
@@ -53,10 +95,86 @@ describe HexaPDF::Type::AcroForm::Field do
53
95
  @field[:Kids] = [{Subtype: :Widget}]
54
96
  assert(@field.terminal_field?)
55
97
 
56
- @field[:Kids] = [{FT: :Tx}]
98
+ @field[:Kids] = [{FT: :Tx, T: 'name'}]
57
99
  refute(@field.terminal_field?)
58
100
  end
59
101
 
102
+ describe "each_widget" do
103
+ it "yields a wrapped instance of self if a single widget is embedded" do
104
+ @field[:Subtype] = :Widget
105
+ @field[:Rect] = [0, 0, 0, 0]
106
+ widgets = @field.each_widget.to_a
107
+ assert_kind_of(HexaPDF::Type::Annotations::Widget, *widgets)
108
+ assert_same(@field.data, widgets.first.data)
109
+ end
110
+
111
+ it "yields all widgets in the /Kids array" do
112
+ @field[:Kids] = [{Subtype: :Widget, Rect: [0, 0, 0, 0], X: 1}]
113
+ widgets = @field.each_widget.to_a
114
+ assert_kind_of(HexaPDF::Type::Annotations::Widget, *widgets)
115
+ assert_equal(1, widgets.first[:X])
116
+ end
117
+
118
+ it "yields nothing if no widgets are defined" do
119
+ assert_equal([], @field.each_widget.to_a)
120
+ end
121
+ end
122
+
123
+ describe "create_widget" do
124
+ before do
125
+ @page = @doc.pages.add
126
+ end
127
+
128
+ it "sets all required widget keys" do
129
+ widget = @field.create_widget(@page)
130
+ assert_equal(:Annot, widget.type)
131
+ assert_equal(:Widget, widget[:Subtype])
132
+ assert_equal([0, 0, 0, 0], widget[:Rect])
133
+ end
134
+
135
+ it "sets the additionally specified keys on the widget" do
136
+ widget = @field.create_widget(@page, X: 5)
137
+ assert_equal(5, widget[:X])
138
+ end
139
+
140
+ it "adds the new widget to the given page's annotations" do
141
+ widget = @field.create_widget(@page)
142
+ assert_equal([widget], @page[:Annots].value)
143
+ end
144
+
145
+ it "populates the field with the widget data if there is no widget" do
146
+ widget = @field.create_widget(@page)
147
+ assert_same(widget.data, @field.data)
148
+ assert_nil(@field[:Kids])
149
+ end
150
+
151
+ it "creates a standalone widget if embedding is not allowed" do
152
+ refute_same(@field.data, @field.create_widget(@page, allow_embedded: false).data)
153
+ end
154
+
155
+ it "extracts an embedded widget into a standalone object if necessary" do
156
+ widget1 = @field.create_widget(@page, Rect: [1, 2, 3, 4])
157
+ widget2 = @field.create_widget(@doc.pages.add, Rect: [2, 1, 4, 3])
158
+ kids = @field[:Kids]
159
+
160
+ assert_equal(2, kids.length)
161
+ refute_same(widget1, kids[0])
162
+ assert_same(widget2, kids[1])
163
+ assert_nil(@field[:Rect])
164
+ assert_equal({Rect: [1, 2, 3, 4], Type: :Annot, Subtype: :Widget, Parent: @field},
165
+ kids[0].value)
166
+ assert_equal([2, 1, 4, 3], kids[1][:Rect].value)
167
+
168
+ refute_equal([widget1], @page[:Annots].value)
169
+ assert_equal([kids[0]], @page[:Annots].value)
170
+ end
171
+
172
+ it "fails if called on a non-terminal field" do
173
+ @field[:Kids] = [{T: 'name'}]
174
+ assert_raises(HexaPDF::Error) { @field.create_widget(@page) }
175
+ end
176
+ end
177
+
60
178
  describe "perform_validation" do
61
179
  before do
62
180
  @field[:FT] = :Tx
@@ -68,7 +186,7 @@ describe HexaPDF::Type::AcroForm::Field do
68
186
  @field.delete(:FT)
69
187
  refute(@field.validate)
70
188
 
71
- @field[:Kids] = [{}]
189
+ @field[:Kids] = [{T: 'name'}]
72
190
  assert(@field.validate)
73
191
  end
74
192