hexapdf 0.11.9 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -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 +22 -11
  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 +3 -2
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +52 -3
  32. data/lib/hexapdf/cli/inspect.rb +31 -9
  33. data/lib/hexapdf/cli/merge.rb +2 -2
  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 +81 -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 +4 -4
  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 +5 -5
  55. data/lib/hexapdf/dictionary_fields.rb +2 -10
  56. data/lib/hexapdf/document.rb +45 -17
  57. data/lib/hexapdf/document/files.rb +1 -2
  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 +2 -2
  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 +2 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -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 +2 -5
  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 +2 -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 +3 -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 +4 -3
  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 +2 -2
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +24 -24
  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 +4 -3
  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 +32 -27
  147. data/lib/hexapdf/parser.rb +69 -6
  148. data/lib/hexapdf/pdf_array.rb +10 -3
  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 +30 -22
  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 +7 -5
  158. data/lib/hexapdf/tokenizer.rb +5 -4
  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 +405 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +305 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +250 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +159 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +187 -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 +4 -3
  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 +238 -2
  180. data/lib/hexapdf/type/catalog.rb +23 -3
  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 +2 -2
  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 +4 -2
  187. data/lib/hexapdf/type/font_true_type.rb +7 -3
  188. data/lib/hexapdf/type/font_type0.rb +2 -2
  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 +12 -2
  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 +5 -3
  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 +36 -12
  199. data/lib/hexapdf/type/page_tree_node.rb +37 -16
  200. data/lib/hexapdf/type/resources.rb +17 -3
  201. data/lib/hexapdf/type/trailer.rb +4 -6
  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 +19 -16
  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/common_tokenizer_tests.rb +6 -1
  216. data/test/hexapdf/content/common.rb +2 -2
  217. data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
  218. data/test/hexapdf/content/test_canvas.rb +3 -3
  219. data/test/hexapdf/content/test_color_space.rb +71 -8
  220. data/test/hexapdf/content/test_operator.rb +22 -22
  221. data/test/hexapdf/content/test_parser.rb +14 -0
  222. data/test/hexapdf/document/test_fonts.rb +1 -1
  223. data/test/hexapdf/document/test_pages.rb +6 -6
  224. data/test/hexapdf/encryption/test_aes.rb +4 -4
  225. data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
  226. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  227. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  228. data/test/hexapdf/font/encoding/test_base.rb +10 -0
  229. data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
  230. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  231. data/test/hexapdf/font/test_type1_wrapper.rb +33 -8
  232. data/test/hexapdf/layout/test_style.rb +1 -1
  233. data/test/hexapdf/layout/test_text_layouter.rb +3 -4
  234. data/test/hexapdf/test_configuration.rb +2 -2
  235. data/test/hexapdf/test_dictionary.rb +3 -1
  236. data/test/hexapdf/test_dictionary_fields.rb +2 -2
  237. data/test/hexapdf/test_document.rb +16 -4
  238. data/test/hexapdf/test_object.rb +44 -26
  239. data/test/hexapdf/test_parser.rb +125 -55
  240. data/test/hexapdf/test_pdf_array.rb +7 -0
  241. data/test/hexapdf/test_rectangle.rb +14 -0
  242. data/test/hexapdf/test_revision.rb +3 -0
  243. data/test/hexapdf/test_revisions.rb +35 -0
  244. data/test/hexapdf/test_writer.rb +2 -2
  245. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +521 -0
  246. data/test/hexapdf/type/acro_form/test_button_field.rb +281 -0
  247. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  248. data/test/hexapdf/type/acro_form/test_field.rb +163 -6
  249. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  250. data/test/hexapdf/type/acro_form/test_text_field.rb +121 -0
  251. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  252. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  253. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  254. data/test/hexapdf/type/test_annotation.rb +45 -0
  255. data/test/hexapdf/type/test_catalog.rb +18 -0
  256. data/test/hexapdf/type/test_font.rb +5 -0
  257. data/test/hexapdf/type/test_font_simple.rb +2 -1
  258. data/test/hexapdf/type/test_font_true_type.rb +6 -0
  259. data/test/hexapdf/type/test_font_type1.rb +8 -0
  260. data/test/hexapdf/type/test_form.rb +19 -1
  261. data/test/hexapdf/type/test_image.rb +7 -0
  262. data/test/hexapdf/type/test_page.rb +45 -7
  263. data/test/hexapdf/type/test_page_tree_node.rb +62 -12
  264. data/test/hexapdf/type/test_resources.rb +20 -0
  265. data/test/hexapdf/type/test_trailer.rb +4 -0
  266. data/test/hexapdf/utils/test_bit_field.rb +15 -1
  267. data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
  268. data/test/test_helper.rb +1 -1
  269. metadata +34 -21
  270. 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({Fields: []}, 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
+ assert_equal([], @acro_form[:Fields].value)
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| assert_match(/DR must also be present/, msg) })
208
+ @acro_form.default_resources[:Font] = {}
209
+ refute(@acro_form.validate {|msg| 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,121 @@
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
+ @field[:V] = nil
118
+ assert(@field.validate)
119
+ end
120
+ end
121
+ 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