hexapdf 0.11.7 → 0.12.2

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 (255) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -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 +7 -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 +9 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +7 -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 +68 -52
  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 +2 -2
  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 +10 -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 +20 -5
  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/encryption/test_security_handler.rb +4 -0
  222. data/test/hexapdf/font/encoding/test_base.rb +10 -0
  223. data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
  224. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  225. data/test/hexapdf/font/test_type1_wrapper.rb +33 -8
  226. data/test/hexapdf/layout/test_style.rb +1 -1
  227. data/test/hexapdf/test_document.rb +12 -0
  228. data/test/hexapdf/test_parser.rb +10 -0
  229. data/test/hexapdf/test_rectangle.rb +14 -0
  230. data/test/hexapdf/test_revision.rb +3 -0
  231. data/test/hexapdf/test_serializer.rb +3 -3
  232. data/test/hexapdf/test_writer.rb +2 -2
  233. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
  234. data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
  235. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  236. data/test/hexapdf/type/acro_form/test_field.rb +124 -6
  237. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  238. data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
  239. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  240. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  241. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  242. data/test/hexapdf/type/test_annotation.rb +45 -0
  243. data/test/hexapdf/type/test_catalog.rb +18 -0
  244. data/test/hexapdf/type/test_font.rb +5 -0
  245. data/test/hexapdf/type/test_font_type1.rb +8 -0
  246. data/test/hexapdf/type/test_form.rb +18 -0
  247. data/test/hexapdf/type/test_image.rb +7 -0
  248. data/test/hexapdf/type/test_page.rb +37 -6
  249. data/test/hexapdf/type/test_page_tree_node.rb +20 -12
  250. data/test/hexapdf/type/test_resources.rb +20 -0
  251. data/test/hexapdf/type/test_trailer.rb +4 -0
  252. data/test/hexapdf/utils/test_bit_field.rb +13 -1
  253. data/test/test_helper.rb +1 -1
  254. metadata +38 -18
  255. data/lib/hexapdf/filter/dct_decode.rb +0 -60
@@ -7,64 +7,231 @@ require 'hexapdf/type/acro_form/form'
7
7
  describe HexaPDF::Type::AcroForm::Form do
8
8
  before do
9
9
  @doc = HexaPDF::Document.new
10
- @acroform = @doc.add({}, type: :XXAcroForm)
10
+ @acro_form = @doc.add({}, type: :XXAcroForm)
11
11
  end
12
12
 
13
13
  describe "signature flags" do
14
14
  before do
15
- @acroform[:SigFlags] = 3
15
+ @acro_form[:SigFlags] = 3
16
16
  end
17
17
 
18
18
  it "returns all signature flags" do
19
- assert_equal([:signatures_exist, :append_only], @acroform.signature_flags)
19
+ assert_equal([:signatures_exist, :append_only], @acro_form.signature_flags)
20
20
  end
21
21
 
22
22
  it "returns true if the given flag is set" do
23
- assert(@acroform.signature_flag?(:signatures_exist))
23
+ assert(@acro_form.signature_flag?(:signatures_exist))
24
24
  end
25
25
 
26
26
  it "raises an error if an unknown flag name is provided" do
27
- assert_raises(ArgumentError) { @acroform.signature_flag?(:non_exist) }
27
+ assert_raises(ArgumentError) { @acro_form.signature_flag?(:non_exist) }
28
28
  end
29
29
 
30
30
  it "sets the given flag bits" do
31
- @acroform[:SigFlags] = 0
32
- @acroform.signature_flag(:append_only)
33
- assert_equal([:append_only], @acroform.signature_flags)
34
- @acroform.signature_flag(:signatures_exist, clear_existing: true)
35
- assert_equal([:signatures_exist], @acroform.signature_flags)
31
+ @acro_form[:SigFlags] = 0
32
+ @acro_form.signature_flag(:append_only)
33
+ assert_equal([:append_only], @acro_form.signature_flags)
34
+ @acro_form.signature_flag(:signatures_exist, clear_existing: true)
35
+ assert_equal([:signatures_exist], @acro_form.signature_flags)
36
36
  end
37
37
  end
38
38
 
39
+ it "returns the root fields" do
40
+ assert_equal([], @acro_form.root_fields.value)
41
+ end
42
+
39
43
  it "finds the root fields" do
40
- @doc.pages.add[:Annots] = [{FT: :Tx1}, {FT: :Tx2, Parent: {FT: :Tx3}}]
44
+ @doc.pages.add[:Annots] = [{FT: :Tx}, {FT: :Tx2, Parent: {FT: :Tx3}}]
41
45
  @doc.pages.add[:Annots] = [{Subtype: :Widget}]
42
46
  @doc.pages.add
43
47
 
44
- result = [{FT: :Tx1}, {FT: :Tx3}]
45
- assert_equal(result, @acroform.find_root_fields.map(&:value))
46
- refute(@acroform.key?(:Fields))
48
+ result = [{FT: :Tx}, {FT: :Tx3}]
49
+ root_fields = @acro_form.find_root_fields
50
+ assert_equal(result, root_fields.map(&:value))
51
+ assert_kind_of(HexaPDF::Type::AcroForm::TextField, root_fields[0])
52
+ refute(@acro_form.key?(:Fields))
47
53
 
48
- @acroform.find_root_fields!
49
- assert_equal(result, @acroform[:Fields].value.map(&:value))
54
+ @acro_form.find_root_fields!
55
+ assert_equal(result, @acro_form[:Fields].value.map(&:value))
50
56
  end
51
57
 
52
58
  describe "each_field" do
53
59
  before do
54
- @acroform[:Fields] = [
55
- {FT: :Tx1},
56
- {FT: :Tx2, Kids: [{Subtype: :Widget}]},
57
- {FT: :Tx3, Kids: [{FT: :Tx4}, {FT: :Tx5, Kids: [{FT: :Tx6}]}]},
60
+ @acro_form[:Fields] = [
61
+ {T: :Tx1},
62
+ {T: :Tx2, Kids: [{Subtype: :Widget}]},
63
+ {T: :Tx3, FT: :Tx, Kids: [{T: :Tx4}, {T: :Tx5, Kids: [{T: :Tx6}]}]},
58
64
  ]
65
+ @acro_form[:Fields][2][:Kids][0][:Parent] = @acro_form[:Fields][2]
66
+ @acro_form[:Fields][2][:Kids][1][:Parent] = @acro_form[:Fields][2]
67
+ @acro_form[:Fields][2][:Kids][1][:Kids][0][:Parent] = @acro_form[:Fields][2][:Kids][1]
59
68
  end
60
69
 
61
70
  it "iterates over all terminal fields" do
62
- assert_equal([:Tx1, :Tx2, :Tx4, :Tx6], @acroform.each_field.map {|h| h[:FT] })
71
+ assert_equal([:Tx1, :Tx2, :Tx4, :Tx6], @acro_form.each_field.map {|h| h[:T] })
63
72
  end
64
73
 
65
74
  it "iterates over all fields" do
66
75
  assert_equal([:Tx1, :Tx2, :Tx3, :Tx4, :Tx5, :Tx6],
67
- @acroform.each_field(terminal_only: false).map {|h| h[:FT] })
76
+ @acro_form.each_field(terminal_only: false).map {|h| h[:T] })
77
+ end
78
+
79
+ it "converts the fields into their proper types if possible" do
80
+ assert_kind_of(HexaPDF::Type::AcroForm::TextField, @acro_form.each_field.to_a.last)
81
+ end
82
+ end
83
+
84
+ describe "field_by_name" do
85
+ before do
86
+ @acro_form[:Fields] = [
87
+ {T: "root only", Kids: [{Subtype: :Widget}]},
88
+ {T: "children", Kids: [{T: "child", FT: :Btn}, {T: "sub", Kids: [{T: "child"}]}]},
89
+ ]
90
+ end
91
+
92
+ it "works for root fields" do
93
+ assert(@acro_form.field_by_name("root only"))
94
+ end
95
+
96
+ it "works for 1st level children" do
97
+ assert(@acro_form.field_by_name("children.child"))
98
+ end
99
+
100
+ it "works for children on any level" do
101
+ assert(@acro_form.field_by_name("children.sub.child"))
102
+ end
103
+
104
+ it "returns nil for unknown fields" do
105
+ assert_nil(@acro_form.field_by_name("non root field"))
106
+ assert_nil(@acro_form.field_by_name("root only.no child"))
107
+ assert_nil(@acro_form.field_by_name("root only.no"))
108
+ assert_nil(@acro_form.field_by_name("children.no child"))
109
+ assert_nil(@acro_form.field_by_name("children.sub.no child"))
110
+ end
111
+
112
+ it "returns the correct field class" do
113
+ assert_kind_of(HexaPDF::Type::AcroForm::ButtonField, @acro_form.field_by_name('children.child'))
114
+ end
115
+ end
116
+
117
+ describe "create fields" do
118
+ describe "handles the general case" do
119
+ it "works for names with a dot" do
120
+ @acro_form[:Fields] = [{T: "root"}]
121
+ field = @acro_form.create_text_field("root.field")
122
+ assert_equal('root.field', field.full_field_name)
123
+ assert_equal([field], @acro_form[:Fields][0][:Kids])
124
+ end
125
+
126
+ it "works for names without a dot" do
127
+ field = @acro_form.create_text_field("field")
128
+ assert_equal('field', field.full_field_name)
129
+ assert([field], @acro_form[:Fields])
130
+ end
131
+
132
+ it "fails if the parent field is not found" do
133
+ assert_raises(HexaPDF::Error) { @acro_form.create_text_field("root.field") }
134
+ end
135
+ end
136
+
137
+ it "creates a text field" do
138
+ field = @acro_form.create_text_field("field")
139
+ assert_equal(:Tx, field.field_type)
140
+ end
141
+
142
+ it "creates a check box" do
143
+ field = @acro_form.create_check_box("field")
144
+ assert(field.check_box?)
145
+ end
146
+
147
+ it "creates a radio button" do
148
+ field = @acro_form.create_radio_button("field")
149
+ assert(field.radio_button?)
150
+ end
151
+
152
+ it "creates a combo box" do
153
+ field = @acro_form.create_combo_box("field")
154
+ assert(field.combo_box?)
155
+ end
156
+
157
+ it "creates a list box" do
158
+ field = @acro_form.create_list_box("field")
159
+ assert(field.list_box?)
160
+ end
161
+ end
162
+
163
+ it "returns the default resources" do
164
+ assert_kind_of(HexaPDF::Type::Resources, @acro_form.default_resources)
165
+ end
166
+
167
+ describe "set_default_appearance_string" do
168
+ it "uses sane default values if no arguments are provided" do
169
+ @acro_form.set_default_appearance_string
170
+ assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
171
+ font = @acro_form.default_resources.font(:F1)
172
+ assert(font)
173
+ assert_equal(:Helvetica, font[:BaseFont])
174
+ end
175
+
176
+ it "allows specifying the used font and font size" do
177
+ @acro_form.set_default_appearance_string(font: 'Times', font_size: 10)
178
+ assert_equal("0 g /F1 10 Tf", @acro_form[:DA])
179
+ assert_equal(:'Times-Roman', @acro_form.default_resources.font(:F1)[:BaseFont])
180
+ end
181
+ end
182
+
183
+ it "sets the /NeedAppearances key" do
184
+ @acro_form.need_appearances!
185
+ assert(@acro_form[:NeedAppearances])
186
+ end
187
+
188
+ it "creates the appearances of all field widgets if necessary" do
189
+ tf = @acro_form.create_text_field('test')
190
+ tf.set_default_appearance_string
191
+ tf.create_widget(@doc.pages.add)
192
+ cb = @acro_form.create_check_box('test2')
193
+ cb.create_widget(@doc.pages.add)
194
+ @acro_form.create_appearances
195
+ assert(tf.each_widget.all? {|w| w.appearance.normal_appearance.kind_of?(HexaPDF::Stream) })
196
+ assert(cb.each_widget.all? {|w| w.appearance.normal_appearance[:Yes].kind_of?(HexaPDF::Stream) })
197
+ end
198
+
199
+ describe "perform_validation" do
200
+ it "checks whether the /DR field is available when /DA is set" do
201
+ @acro_form[:DA] = 'test'
202
+ refute(@acro_form.validate)
203
+ end
204
+
205
+ it "checks whether the font used in /DA is available in /DR" do
206
+ @acro_form[:DA] = '/F2 0 Tf /F1 0 Tf'
207
+ refute(@acro_form.validate {|msg, c| assert_match(/DR must also be present/, msg) })
208
+ @acro_form.default_resources[:Font] = {}
209
+ refute(@acro_form.validate {|msg, c| assert_match(/font.*is not.*resource/, msg) })
210
+ @acro_form.default_resources[:Font][:F1] = :yes
211
+ assert(@acro_form.validate)
212
+ end
213
+
214
+ it "set the default appearance string, though optional, to a valid value to avoid problems" do
215
+ assert(@acro_form.validate)
216
+ assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
217
+ end
218
+
219
+ describe "automatically creates the terminal fields; appearances" do
220
+ before do
221
+ @cb = @acro_form.create_check_box('test2')
222
+ @cb.create_widget(@doc.pages.add)
223
+ end
224
+
225
+ it "does this if the configuration option is true" do
226
+ assert(@acro_form.validate)
227
+ assert_kind_of(HexaPDF::Stream, @cb[:AP][:N][:Yes])
228
+ end
229
+
230
+ it "does nothing if the configuration option is false" do
231
+ @doc.config['acro_form.create_appearances'] = false
232
+ assert(@acro_form.validate)
233
+ refute_kind_of(HexaPDF::Stream, @cb[:AP][:N][:Yes])
234
+ end
68
235
  end
69
236
  end
70
237
  end
@@ -0,0 +1,119 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../../content/common'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/type/acro_form/text_field'
7
+
8
+ describe HexaPDF::Type::AcroForm::TextField do
9
+ before do
10
+ @doc = HexaPDF::Document.new
11
+ @field = @doc.add({FT: :Tx}, type: :XXAcroFormField, subtype: :Tx)
12
+ end
13
+
14
+ it "resolves /MaxLen as inheritable field" do
15
+ assert_nil(@field[:MaxLen])
16
+
17
+ @field[:Parent] = {MaxLen: 5}
18
+ assert_equal(5, @field[:MaxLen])
19
+
20
+ @field[:MaxLen] = 6
21
+ assert_equal(6, @field[:MaxLen])
22
+ end
23
+
24
+ describe "field_value" do
25
+ it "handles unset values" do
26
+ assert_nil(@field.field_value)
27
+ end
28
+
29
+ it "handles string values" do
30
+ @field[:V] = "str"
31
+ assert_equal("str", @field.field_value)
32
+ end
33
+
34
+ it "handles stream values" do
35
+ @field[:V] = @doc.wrap({}, stream: "str")
36
+ assert_equal("str", @field.field_value)
37
+ end
38
+ end
39
+
40
+ describe "field_value=" do
41
+ it "sets the field to the given value" do
42
+ @field.field_value = 'str'
43
+ assert_equal('str', @field.field_value)
44
+ end
45
+
46
+ it "converts whitespace characters to simple spaces for single line text fields" do
47
+ @field.field_value = "str\ning"
48
+ assert_equal('str ing', @field.field_value)
49
+ end
50
+
51
+ it "updates the widgets to reflect the changed value" do
52
+ widget = @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
53
+ @field.set_default_appearance_string
54
+ @field.field_value = 'str'
55
+ assert(widget[:AP][:N])
56
+ end
57
+
58
+ it "fails if the :password flag is set" do
59
+ @field.flag(:password)
60
+ assert_raises(HexaPDF::Error) { @field.field_value = 'test' }
61
+ end
62
+ end
63
+
64
+ it "sets and returns the default field value" do
65
+ @field.default_field_value = 'hallo'
66
+ assert_equal('hallo', @field.default_field_value)
67
+ end
68
+
69
+ it "returns the correct concrete field type" do
70
+ assert_equal(:single_line_text_field, @field.concrete_field_type)
71
+ @field.flag(:multiline, clear_existing: true)
72
+ assert_equal(:multiline_text_field, @field.concrete_field_type)
73
+ @field.flag(:password, clear_existing: true)
74
+ assert_equal(:password_field, @field.concrete_field_type)
75
+ @field.flag(:file_select, clear_existing: true)
76
+ assert_equal(:file_select_field, @field.concrete_field_type)
77
+ @field.flag(:comb, clear_existing: true)
78
+ assert_equal(:comb_text_field, @field.concrete_field_type)
79
+ @field.flag(:rich_text, clear_existing: true)
80
+ assert_equal(:rich_text_field, @field.concrete_field_type)
81
+ end
82
+
83
+ describe "create_appearances" do
84
+ it "creates the needed streams" do
85
+ @doc.acro_form(create: true)
86
+ @field.create_widget(@doc.pages.add, Rect: [0, 0, 0, 0])
87
+ @field.set_default_appearance_string
88
+ @field.create_appearances
89
+ assert(@field[:AP][:N])
90
+ end
91
+
92
+ it "uses the configuration option acro_form.appearance_generator" do
93
+ @doc.config['acro_form.appearance_generator'] = 'NonExistent'
94
+ assert_raises(Exception) { @field.create_appearances }
95
+ end
96
+ end
97
+
98
+ describe "validation" do
99
+ it "checks the value of the /FT field" do
100
+ @field.delete(:FT)
101
+ refute(@field.validate(auto_correct: false))
102
+ assert(@field.validate)
103
+ assert_equal(:Tx, @field.field_type)
104
+ end
105
+
106
+ it "checks that the field value has a valid type" do
107
+ assert(@field.validate) # no field value
108
+ @field[:V] = :sym
109
+ refute(@field.validate)
110
+ end
111
+
112
+ it "checks the field value against /MaxLen" do
113
+ @field[:V] = 'Test'
114
+ assert(@field.validate)
115
+ @field[:MaxLen] = 2
116
+ refute(@field.validate)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,77 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/acro_form/variable_text_field'
6
+
7
+ describe HexaPDF::Type::AcroForm::VariableTextField do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @doc.acro_form(create: true).set_default_appearance_string
11
+ @field = @doc.add({}, type: HexaPDF::Type::AcroForm::VariableTextField)
12
+ end
13
+
14
+ describe "text_alignment" do
15
+ it "returns the alignment value for displaying text" do
16
+ assert_equal(:left, @field.text_alignment)
17
+ @field[:Q] = 1
18
+ assert_equal(:center, @field.text_alignment)
19
+ @field[:Q] = 2
20
+ assert_equal(:right, @field.text_alignment)
21
+ end
22
+
23
+ it "sets the alignment value for displaying text to a given value" do
24
+ @field.text_alignment(:center)
25
+ assert_equal(1, @field[:Q])
26
+ @field.text_alignment(:right)
27
+ assert_equal(2, @field[:Q])
28
+ @field.text_alignment(:left)
29
+ assert_equal(0, @field[:Q])
30
+ assert_raises(ArgumentError) { @field.text_alignment(:unknown) }
31
+ end
32
+ end
33
+
34
+ describe "set_default_appearance_string" do
35
+ it "creates the AcroForm object if it doesn't exist" do
36
+ @doc.catalog.delete(:AcroForm)
37
+ @field.set_default_appearance_string
38
+ assert(@doc.acro_form)
39
+ end
40
+
41
+ it "uses sane default values if no arguments are provided" do
42
+ @field.set_default_appearance_string
43
+ assert_equal("0 g /F1 0 Tf", @field[:DA])
44
+ font = @doc.acro_form.default_resources.font(:F1)
45
+ assert(font)
46
+ assert_equal(:Helvetica, font[:BaseFont])
47
+ end
48
+
49
+ it "allows specifying the used font and font size" do
50
+ @field.set_default_appearance_string(font: 'Times', font_size: 10)
51
+ assert_equal("0 g /F2 10 Tf", @field[:DA])
52
+ assert_equal(:'Times-Roman', @doc.acro_form.default_resources.font(:F2)[:BaseFont])
53
+ end
54
+ end
55
+
56
+ describe "parse_default_appearance_string" do
57
+ it "parses the default appearance string of the field" do
58
+ @field[:DA] = "1 g /F1 20 Tf 5 w /F2 10 Tf"
59
+ assert_equal([:F2, 10], @field.parse_default_appearance_string)
60
+ end
61
+
62
+ it "uses the default appearance string of a parent field" do
63
+ parent = @doc.add({DA: "/F1 15 Tf"}, type: :XXAcroFormField)
64
+ @field[:Parent] = parent
65
+ assert_equal([:F1, 15], @field.parse_default_appearance_string)
66
+ end
67
+
68
+ it "uses the global default appearance string" do
69
+ assert_equal([:F1, 0], @field.parse_default_appearance_string)
70
+ end
71
+
72
+ it "fails if no /DA value is set" do
73
+ @doc.acro_form.delete(:DA)
74
+ assert_raises(HexaPDF::Error) { @field.parse_default_appearance_string }
75
+ end
76
+ end
77
+ end