hexapdf 0.17.1 → 0.17.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 +1024 -0
  3. data/LICENSE +29 -0
  4. data/README.md +129 -0
  5. data/Rakefile +109 -0
  6. data/agpl-3.0.txt +661 -0
  7. data/examples/001-hello_world.rb +16 -0
  8. data/examples/002-graphics.rb +275 -0
  9. data/examples/003-arcs.rb +50 -0
  10. data/examples/004-optimizing.rb +23 -0
  11. data/examples/005-merging.rb +27 -0
  12. data/examples/006-standard_pdf_fonts.rb +73 -0
  13. data/examples/007-truetype.rb +42 -0
  14. data/examples/008-show_char_bboxes.rb +55 -0
  15. data/examples/009-text_layouter_alignment.rb +47 -0
  16. data/examples/010-text_layouter_inline_boxes.rb +64 -0
  17. data/examples/011-text_layouter_line_wrapping.rb +57 -0
  18. data/examples/012-text_layouter_styling.rb +122 -0
  19. data/examples/013-text_layouter_shapes.rb +176 -0
  20. data/examples/014-text_in_polygon.rb +60 -0
  21. data/examples/015-boxes.rb +76 -0
  22. data/examples/016-frame_automatic_box_placement.rb +90 -0
  23. data/examples/017-frame_text_flow.rb +60 -0
  24. data/examples/018-composer.rb +44 -0
  25. data/examples/019-acro_form.rb +88 -0
  26. data/examples/emoji-smile.png +0 -0
  27. data/examples/emoji-wink.png +0 -0
  28. data/examples/machupicchu.jpg +0 -0
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
  31. data/lib/hexapdf/version.rb +1 -1
  32. data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
  33. data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
  34. data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
  35. data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
  36. data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
  37. data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
  38. data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
  39. data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
  40. data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
  41. data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
  42. data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
  43. data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
  44. data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
  45. data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
  46. data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
  47. data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
  48. data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
  49. data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
  50. data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
  51. data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
  52. data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
  53. data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
  54. data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
  55. data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
  56. data/test/data/fonts/Ubuntu-Title.ttf +0 -0
  57. data/test/data/images/cmyk.jpg +0 -0
  58. data/test/data/images/fillbytes.jpg +0 -0
  59. data/test/data/images/gray.jpg +0 -0
  60. data/test/data/images/greyscale-1bit.png +0 -0
  61. data/test/data/images/greyscale-2bit.png +0 -0
  62. data/test/data/images/greyscale-4bit.png +0 -0
  63. data/test/data/images/greyscale-8bit.png +0 -0
  64. data/test/data/images/greyscale-alpha-8bit.png +0 -0
  65. data/test/data/images/greyscale-trns-8bit.png +0 -0
  66. data/test/data/images/greyscale-with-gamma1.0.png +0 -0
  67. data/test/data/images/greyscale-with-gamma1.5.png +0 -0
  68. data/test/data/images/indexed-1bit.png +0 -0
  69. data/test/data/images/indexed-2bit.png +0 -0
  70. data/test/data/images/indexed-4bit.png +0 -0
  71. data/test/data/images/indexed-8bit.png +0 -0
  72. data/test/data/images/indexed-alpha-4bit.png +0 -0
  73. data/test/data/images/indexed-alpha-8bit.png +0 -0
  74. data/test/data/images/rgb.jpg +0 -0
  75. data/test/data/images/truecolour-8bit.png +0 -0
  76. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  77. data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
  78. data/test/data/images/truecolour-srgb-8bit.png +0 -0
  79. data/test/data/images/ycck.jpg +0 -0
  80. data/test/data/minimal.pdf +44 -0
  81. data/test/data/standard-security-handler/README +9 -0
  82. data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
  83. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
  84. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
  85. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
  86. data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
  87. data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
  88. data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
  89. data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
  90. data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
  91. data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
  92. data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
  93. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
  94. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
  95. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
  96. data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
  97. data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
  98. data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
  99. data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
  100. data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
  101. data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
  102. data/test/hexapdf/common_tokenizer_tests.rb +236 -0
  103. data/test/hexapdf/content/common.rb +39 -0
  104. data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
  105. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
  106. data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
  107. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
  108. data/test/hexapdf/content/test_canvas.rb +1279 -0
  109. data/test/hexapdf/content/test_color_space.rb +176 -0
  110. data/test/hexapdf/content/test_graphics_state.rb +151 -0
  111. data/test/hexapdf/content/test_operator.rb +619 -0
  112. data/test/hexapdf/content/test_parser.rb +99 -0
  113. data/test/hexapdf/content/test_processor.rb +163 -0
  114. data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
  115. data/test/hexapdf/document/test_files.rb +72 -0
  116. data/test/hexapdf/document/test_fonts.rb +60 -0
  117. data/test/hexapdf/document/test_images.rb +72 -0
  118. data/test/hexapdf/document/test_pages.rb +130 -0
  119. data/test/hexapdf/encryption/common.rb +87 -0
  120. data/test/hexapdf/encryption/test_aes.rb +129 -0
  121. data/test/hexapdf/encryption/test_arc4.rb +39 -0
  122. data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
  123. data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
  124. data/test/hexapdf/encryption/test_identity.rb +21 -0
  125. data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
  126. data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
  127. data/test/hexapdf/encryption/test_security_handler.rb +380 -0
  128. data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
  129. data/test/hexapdf/filter/common.rb +53 -0
  130. data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
  131. data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
  132. data/test/hexapdf/filter/test_crypt.rb +21 -0
  133. data/test/hexapdf/filter/test_encryption.rb +24 -0
  134. data/test/hexapdf/filter/test_flate_decode.rb +44 -0
  135. data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
  136. data/test/hexapdf/filter/test_predictor.rb +219 -0
  137. data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
  138. data/test/hexapdf/font/cmap/test_parser.rb +102 -0
  139. data/test/hexapdf/font/cmap/test_writer.rb +66 -0
  140. data/test/hexapdf/font/encoding/test_base.rb +45 -0
  141. data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
  142. data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
  143. data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
  144. data/test/hexapdf/font/test_cmap.rb +104 -0
  145. data/test/hexapdf/font/test_encoding.rb +27 -0
  146. data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
  147. data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
  148. data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
  149. data/test/hexapdf/font/true_type/common.rb +17 -0
  150. data/test/hexapdf/font/true_type/table/common.rb +27 -0
  151. data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
  152. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
  153. data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
  154. data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
  155. data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
  156. data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
  157. data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
  158. data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
  159. data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
  160. data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
  161. data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
  162. data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
  163. data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
  164. data/test/hexapdf/font/true_type/test_builder.rb +42 -0
  165. data/test/hexapdf/font/true_type/test_font.rb +116 -0
  166. data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
  167. data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
  168. data/test/hexapdf/font/true_type/test_table.rb +48 -0
  169. data/test/hexapdf/font/type1/common.rb +6 -0
  170. data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
  171. data/test/hexapdf/font/type1/test_font.rb +104 -0
  172. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  173. data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
  174. data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
  175. data/test/hexapdf/font_loader/test_from_file.rb +36 -0
  176. data/test/hexapdf/font_loader/test_standard14.rb +33 -0
  177. data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
  178. data/test/hexapdf/image_loader/test_pdf.rb +47 -0
  179. data/test/hexapdf/image_loader/test_png.rb +259 -0
  180. data/test/hexapdf/layout/test_box.rb +154 -0
  181. data/test/hexapdf/layout/test_frame.rb +350 -0
  182. data/test/hexapdf/layout/test_image_box.rb +73 -0
  183. data/test/hexapdf/layout/test_inline_box.rb +71 -0
  184. data/test/hexapdf/layout/test_line.rb +206 -0
  185. data/test/hexapdf/layout/test_style.rb +790 -0
  186. data/test/hexapdf/layout/test_text_box.rb +140 -0
  187. data/test/hexapdf/layout/test_text_fragment.rb +375 -0
  188. data/test/hexapdf/layout/test_text_layouter.rb +758 -0
  189. data/test/hexapdf/layout/test_text_shaper.rb +62 -0
  190. data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
  191. data/test/hexapdf/task/test_dereference.rb +51 -0
  192. data/test/hexapdf/task/test_optimize.rb +162 -0
  193. data/test/hexapdf/test_composer.rb +258 -0
  194. data/test/hexapdf/test_configuration.rb +93 -0
  195. data/test/hexapdf/test_data_dir.rb +32 -0
  196. data/test/hexapdf/test_dictionary.rb +340 -0
  197. data/test/hexapdf/test_dictionary_fields.rb +269 -0
  198. data/test/hexapdf/test_document.rb +641 -0
  199. data/test/hexapdf/test_filter.rb +100 -0
  200. data/test/hexapdf/test_importer.rb +106 -0
  201. data/test/hexapdf/test_object.rb +258 -0
  202. data/test/hexapdf/test_parser.rb +645 -0
  203. data/test/hexapdf/test_pdf_array.rb +169 -0
  204. data/test/hexapdf/test_rectangle.rb +73 -0
  205. data/test/hexapdf/test_reference.rb +50 -0
  206. data/test/hexapdf/test_revision.rb +188 -0
  207. data/test/hexapdf/test_revisions.rb +196 -0
  208. data/test/hexapdf/test_serializer.rb +195 -0
  209. data/test/hexapdf/test_stream.rb +274 -0
  210. data/test/hexapdf/test_tokenizer.rb +80 -0
  211. data/test/hexapdf/test_type.rb +18 -0
  212. data/test/hexapdf/test_writer.rb +140 -0
  213. data/test/hexapdf/test_xref_section.rb +61 -0
  214. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
  215. data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
  216. data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
  217. data/test/hexapdf/type/acro_form/test_field.rb +259 -0
  218. data/test/hexapdf/type/acro_form/test_form.rb +357 -0
  219. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  220. data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
  221. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
  222. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  223. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  224. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  225. data/test/hexapdf/type/annotations/test_text.rb +34 -0
  226. data/test/hexapdf/type/annotations/test_widget.rb +225 -0
  227. data/test/hexapdf/type/test_annotation.rb +97 -0
  228. data/test/hexapdf/type/test_catalog.rb +48 -0
  229. data/test/hexapdf/type/test_cid_font.rb +61 -0
  230. data/test/hexapdf/type/test_file_specification.rb +141 -0
  231. data/test/hexapdf/type/test_font.rb +67 -0
  232. data/test/hexapdf/type/test_font_descriptor.rb +61 -0
  233. data/test/hexapdf/type/test_font_simple.rb +176 -0
  234. data/test/hexapdf/type/test_font_true_type.rb +31 -0
  235. data/test/hexapdf/type/test_font_type0.rb +120 -0
  236. data/test/hexapdf/type/test_font_type1.rb +142 -0
  237. data/test/hexapdf/type/test_font_type3.rb +26 -0
  238. data/test/hexapdf/type/test_form.rb +120 -0
  239. data/test/hexapdf/type/test_image.rb +261 -0
  240. data/test/hexapdf/type/test_info.rb +9 -0
  241. data/test/hexapdf/type/test_object_stream.rb +117 -0
  242. data/test/hexapdf/type/test_page.rb +598 -0
  243. data/test/hexapdf/type/test_page_tree_node.rb +315 -0
  244. data/test/hexapdf/type/test_resources.rb +209 -0
  245. data/test/hexapdf/type/test_trailer.rb +116 -0
  246. data/test/hexapdf/type/test_xref_stream.rb +143 -0
  247. data/test/hexapdf/utils/test_bit_field.rb +63 -0
  248. data/test/hexapdf/utils/test_bit_stream.rb +69 -0
  249. data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
  250. data/test/hexapdf/utils/test_lru_cache.rb +22 -0
  251. data/test/hexapdf/utils/test_object_hash.rb +120 -0
  252. data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
  253. data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
  254. data/test/test_helper.rb +58 -0
  255. metadata +263 -3
@@ -0,0 +1,259 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/acro_form/field'
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
+
16
+ describe HexaPDF::Type::AcroForm::Field do
17
+ before do
18
+ @doc = HexaPDF::Document.new
19
+ @field = @doc.add({}, type: :XXAcroFormField)
20
+ @doc.acro_form(create: true).root_fields << @field
21
+ end
22
+
23
+ it "must always be an indirect object" do
24
+ assert(@field.must_be_indirect?)
25
+ end
26
+
27
+ it "resolves inherited field values" do
28
+ assert_nil(@field[:FT])
29
+
30
+ @field[:Parent] = {FT: :Tx}
31
+ assert_equal(:Tx, @field[:FT])
32
+
33
+ @field[:FT] = :Ch
34
+ assert_equal(:Ch, @field[:FT])
35
+ end
36
+
37
+ it "has convenience methods for accessing the field flags" do
38
+ assert_equal([], @field.flags)
39
+ refute(@field.flagged?(:required))
40
+ @field.flag(:required, 2)
41
+ assert(@field.flagged?(2))
42
+ assert_equal(6, @field[:Ff])
43
+ end
44
+
45
+ it "returns the field type" do
46
+ assert_nil(@field.field_type)
47
+
48
+ @field[:FT] = :Tx
49
+ assert_equal(:Tx, @field.field_type)
50
+ end
51
+
52
+ it "returns the concrete field type" do
53
+ assert_nil(@field.concrete_field_type)
54
+
55
+ @field[:FT] = :Tx
56
+ assert_equal(:text_field, @field.concrete_field_type)
57
+ @field[:FT] = :Btn
58
+ assert_equal(:button_field, @field.concrete_field_type)
59
+ @field[:FT] = :Ch
60
+ assert_equal(:choice_field, @field.concrete_field_type)
61
+ @field[:FT] = :Sig
62
+ assert_equal(:signature_field, @field.concrete_field_type)
63
+ end
64
+
65
+ it "returns the field name" do
66
+ assert_nil(@field.field_name)
67
+ @field[:T] = 'test'
68
+ assert_equal('test', @field.field_name)
69
+ end
70
+
71
+ it "returns the full name of the field" do
72
+ assert_nil(@field.full_field_name)
73
+
74
+ @field[:T] = "Test"
75
+ assert_equal("Test", @field.full_field_name)
76
+
77
+ @field[:Parent] = {}
78
+ assert_equal("Test", @field.full_field_name)
79
+
80
+ @field[:Parent] = {T: 'Parent'}
81
+ assert_equal("Parent.Test", @field.full_field_name)
82
+ end
83
+
84
+ it "allows setting and retrieving the alternate field name" do
85
+ @field.alternate_field_name = 'Alternate'
86
+ assert_equal('Alternate', @field.alternate_field_name)
87
+ assert_equal('Alternate', @field[:TU])
88
+ end
89
+
90
+ it "returns whether the field is a terminal field" do
91
+ assert(@field.terminal_field?)
92
+
93
+ @field[:Kids] = []
94
+ assert(@field.terminal_field?)
95
+
96
+ @field[:Kids] = [{Subtype: :Widget}]
97
+ assert(@field.terminal_field?)
98
+
99
+ @field[:Kids] = [{FT: :Tx, T: 'name'}]
100
+ refute(@field.terminal_field?)
101
+ end
102
+
103
+ it "can check whether a widget is embedded in the field" do
104
+ refute(@field.embedded_widget?)
105
+ @field[:Subtype] = :Wdiget
106
+ assert(@field.embedded_widget?)
107
+ end
108
+
109
+ describe "each_widget" do
110
+ it "yields a wrapped instance of self if a single widget is embedded" do
111
+ @field[:Subtype] = :Widget
112
+ @field[:Rect] = [0, 0, 0, 0]
113
+ widgets = @field.each_widget.to_a
114
+ assert_kind_of(HexaPDF::Type::Annotations::Widget, *widgets)
115
+ assert_same(@field.data, widgets.first.data)
116
+ end
117
+
118
+ it "yields all widgets in the /Kids array" do
119
+ @field[:Kids] = [{Subtype: :Widget, Rect: [0, 0, 0, 0], X: 1}]
120
+ widgets = @field.each_widget.to_a
121
+ assert_kind_of(HexaPDF::Type::Annotations::Widget, *widgets)
122
+ assert_equal(1, widgets.first[:X])
123
+ end
124
+
125
+ it "yields all widgets of other fields with the same full field name" do
126
+ @field[:T] = 'a'
127
+ @doc.acro_form.root_fields <<
128
+ @doc.add({T: "b", Subtype: :Widget, Rect: [0, 0, 0, 0]}, type: :XXAcroFormField) <<
129
+ @doc.add({T: "a", X: 1, Subtype: :Widget, Rect: [0, 0, 0, 0]}, type: :XXAcroFormField)
130
+
131
+ widgets = @field.each_widget.to_a
132
+ assert_kind_of(HexaPDF::Type::Annotations::Widget, *widgets)
133
+ assert_equal(1, widgets.first[:X])
134
+ end
135
+
136
+ it "yields nothing if no widgets are defined" do
137
+ assert_equal([], @field.each_widget.to_a)
138
+ end
139
+ end
140
+
141
+ describe "create_widget" do
142
+ before do
143
+ @page = @doc.pages.add
144
+ end
145
+
146
+ it "sets all required widget keys" do
147
+ widget = @field.create_widget(@page)
148
+ assert_equal(:Annot, widget.type)
149
+ assert_equal(:Widget, widget[:Subtype])
150
+ assert_equal([0, 0, 0, 0], widget[:Rect])
151
+ end
152
+
153
+ it "sets the additionally specified keys on the widget" do
154
+ widget = @field.create_widget(@page, X: 5)
155
+ assert_equal(5, widget[:X])
156
+ end
157
+
158
+ it "adds the new widget to the given page's annotations" do
159
+ widget = @field.create_widget(@page)
160
+ assert_equal([widget], @page[:Annots].value)
161
+ end
162
+
163
+ it "populates the field with the widget data if there is no widget" do
164
+ widget = @field.create_widget(@page)
165
+ assert_same(widget.data, @field.data)
166
+ assert_nil(@field[:Kids])
167
+ end
168
+
169
+ it "creates a standalone widget if embedding is not allowed" do
170
+ refute_same(@field.data, @field.create_widget(@page, allow_embedded: false).data)
171
+ end
172
+
173
+ it "extracts an embedded widget into a standalone object if necessary" do
174
+ widget1 = @field.create_widget(@page, Rect: [1, 2, 3, 4])
175
+ widget2 = @field.create_widget(@doc.pages.add, Rect: [2, 1, 4, 3])
176
+ kids = @field[:Kids]
177
+
178
+ assert_equal(2, kids.length)
179
+ refute_same(widget1, kids[0])
180
+ assert_same(widget2, kids[1])
181
+ assert_nil(@field[:Rect])
182
+ assert_equal({Rect: [1, 2, 3, 4], Type: :Annot, Subtype: :Widget, Parent: @field},
183
+ kids[0].value)
184
+ assert_equal([2, 1, 4, 3], kids[1][:Rect].value)
185
+
186
+ refute_equal([widget1], @page[:Annots].value)
187
+ assert_equal([kids[0]], @page[:Annots].value)
188
+ end
189
+
190
+ it "fails if called on a non-terminal field" do
191
+ @field[:Kids] = [{T: 'name'}]
192
+ assert_raises(HexaPDF::Error) { @field.create_widget(@page) }
193
+ end
194
+ end
195
+
196
+ describe "delete_widget" do
197
+ before do
198
+ @page = @doc.pages.add
199
+ end
200
+
201
+ it "does nothing if the provided widget doesn't belong to the field" do
202
+ wrong_widget = @doc.add({Subtype: :Widget})
203
+
204
+ @field.create_widget(@page)
205
+ @field.delete_widget(wrong_widget)
206
+ assert_equal(:Widget, @field[:Subtype])
207
+
208
+ @field.create_widget(@page)
209
+ @field.delete_widget(wrong_widget)
210
+ assert_equal(2, @field[:Kids].size)
211
+ end
212
+
213
+ it "deletes the widget if it is embedded" do
214
+ widget = @field.create_widget(@page)
215
+ @doc.revisions.current.update(widget)
216
+ assert_same(widget, @doc.object(widget))
217
+ refute_same(@field, @doc.object(@field))
218
+
219
+ @field.delete_widget(widget)
220
+ refute(@field.key?(:Subtype))
221
+ assert(@page[:Annots].empty?)
222
+ assert_same(@field, @doc.object(@field))
223
+ end
224
+
225
+ it "deletes the widget if it is not embedded" do
226
+ @field.create_widget(@page)
227
+ widget2 = @field.create_widget(@page)
228
+ @field.delete_widget(widget2)
229
+ assert_equal(1, @field[:Kids].size)
230
+ assert_equal(@field[:Kids].value, @page[:Annots].value)
231
+ end
232
+ end
233
+
234
+ describe "perform_validation" do
235
+ before do
236
+ @field[:FT] = :Tx
237
+ end
238
+
239
+ it "requires the /FT key to be present for terminal fields" do
240
+ assert(@field.validate)
241
+
242
+ @field.delete(:FT)
243
+ refute(@field.validate)
244
+
245
+ @field[:Kids] = [{T: 'name'}]
246
+ assert(@field.validate)
247
+ end
248
+
249
+ it "doesn't allow periods in partial field names" do
250
+ assert(@field.validate)
251
+
252
+ @field[:T] = "Test"
253
+ assert(@field.validate)
254
+
255
+ @field[:T] = "Te.st"
256
+ refute(@field.validate)
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,357 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/acro_form/form'
6
+
7
+ describe HexaPDF::Type::AcroForm::Form do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @acro_form = @doc.add({Fields: []}, type: :XXAcroForm)
11
+ end
12
+
13
+ describe "signature flags" do
14
+ before do
15
+ @acro_form[:SigFlags] = 3
16
+ end
17
+
18
+ it "returns all signature flags" do
19
+ assert_equal([:signatures_exist, :append_only], @acro_form.signature_flags)
20
+ end
21
+
22
+ it "returns true if the given flag is set" do
23
+ assert(@acro_form.signature_flag?(:signatures_exist))
24
+ end
25
+
26
+ it "raises an error if an unknown flag name is provided" do
27
+ assert_raises(ArgumentError) { @acro_form.signature_flag?(:non_exist) }
28
+ end
29
+
30
+ it "sets the given flag bits" do
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
+ end
37
+ end
38
+
39
+ it "returns the root fields" do
40
+ assert_equal([], @acro_form.root_fields.value)
41
+ end
42
+
43
+ it "finds the root fields" do
44
+ @doc.pages.add[:Annots] = [{FT: :Tx}, {FT: :Tx2, Parent: {FT: :Tx3}}]
45
+ @doc.pages.add[:Annots] = [{Subtype: :Widget}]
46
+ @doc.pages.add
47
+
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)
53
+
54
+ @acro_form.find_root_fields!
55
+ assert_equal(result, @acro_form[:Fields].value.map(&:value))
56
+ end
57
+
58
+ describe "each_field" do
59
+ before do
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}]}]},
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]
68
+ end
69
+
70
+ it "iterates over all terminal fields" do
71
+ assert_equal([:Tx1, :Tx2, :Tx4, :Tx6], @acro_form.each_field.map {|h| h[:T] })
72
+ end
73
+
74
+ it "iterates over all fields" do
75
+ assert_equal([:Tx1, :Tx2, :Tx3, :Tx4, :Tx5, :Tx6],
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
+ before do
119
+ @acro_form = @doc.acro_form(create: true)
120
+ end
121
+
122
+ describe "handles the general case" do
123
+ it "works for names with a dot" do
124
+ @acro_form[:Fields] = [{T: "root"}]
125
+ field = @acro_form.create_text_field("root.field")
126
+ assert_equal('root.field', field.full_field_name)
127
+ assert_equal([field], @acro_form[:Fields][0][:Kids])
128
+ end
129
+
130
+ it "works for names without a dot" do
131
+ field = @acro_form.create_text_field("field")
132
+ assert_equal('field', field.full_field_name)
133
+ assert([field], @acro_form[:Fields])
134
+ end
135
+
136
+ it "fails if the parent field is not found" do
137
+ assert_raises(HexaPDF::Error) { @acro_form.create_text_field("root.field") }
138
+ end
139
+ end
140
+
141
+ def applies_variable_text_properties(method, **args)
142
+ field = @acro_form.send(method, "field", **args, font: 'Times')
143
+ font_name, font_size = field.parse_default_appearance_string
144
+ assert_equal(:'Times-Roman', @acro_form.default_resources.font(font_name)[:BaseFont])
145
+ assert_equal(0, font_size)
146
+
147
+ field = @acro_form.send(method, "field", **args, font_options: {variant: :bold})
148
+ font_name, font_size = field.parse_default_appearance_string
149
+ assert_equal(:'Helvetica-Bold', @acro_form.default_resources.font(font_name)[:BaseFont])
150
+
151
+ field = @acro_form.send(method, "field", **args, font_size: 10)
152
+ font_name, font_size = field.parse_default_appearance_string
153
+ assert_equal(:Helvetica, @acro_form.default_resources.font(font_name)[:BaseFont])
154
+ assert_equal(10, font_size)
155
+
156
+ field = @acro_form.send(method, "field", **args, font: 'Courier', font_size: 10, align: :center)
157
+ font_name, font_size = field.parse_default_appearance_string
158
+ assert_equal(:Courier, @acro_form.default_resources.font(font_name)[:BaseFont])
159
+ assert_equal(10, font_size)
160
+ assert_equal(:center, field.text_alignment)
161
+ end
162
+
163
+ it "creates a text field" do
164
+ field = @acro_form.create_text_field("field")
165
+ assert_equal(:Tx, field.field_type)
166
+ applies_variable_text_properties(:create_text_field)
167
+ end
168
+
169
+ it "creates a multiline text field" do
170
+ field = @acro_form.create_multiline_text_field("field")
171
+ assert_equal(:Tx, field.field_type)
172
+ assert(field.multiline_text_field?)
173
+ applies_variable_text_properties(:create_multiline_text_field)
174
+ end
175
+
176
+ it "creates a comb text field" do
177
+ field = @acro_form.create_comb_text_field("field", max_chars: 9)
178
+ assert_equal(:Tx, field.field_type)
179
+ assert_equal(9, field[:MaxLen])
180
+ assert(field.comb_text_field?)
181
+ applies_variable_text_properties(:create_comb_text_field, max_chars: 9)
182
+ end
183
+
184
+ it "creates a password field" do
185
+ field = @acro_form.create_password_field("field")
186
+ assert_equal(:Tx, field.field_type)
187
+ assert(field.password_field?)
188
+ applies_variable_text_properties(:create_password_field)
189
+ end
190
+
191
+ it "creates a file select field" do
192
+ field = @acro_form.create_file_select_field("field")
193
+ assert_equal(:Tx, field.field_type)
194
+ assert(field.file_select_field?)
195
+ applies_variable_text_properties(:create_file_select_field)
196
+ end
197
+
198
+ it "creates a check box" do
199
+ field = @acro_form.create_check_box("field")
200
+ assert(field.check_box?)
201
+ end
202
+
203
+ it "creates a radio button" do
204
+ field = @acro_form.create_radio_button("field")
205
+ assert(field.radio_button?)
206
+ end
207
+
208
+ it "creates a combo box" do
209
+ field = @acro_form.create_combo_box("field", option_items: ['a', 'b', 'c'], editable: true)
210
+ assert(field.combo_box?)
211
+ assert_equal(['a', 'b', 'c'], field.option_items)
212
+ assert(field.flagged?(:edit))
213
+ applies_variable_text_properties(:create_combo_box)
214
+ end
215
+
216
+ it "creates a list box" do
217
+ field = @acro_form.create_list_box("field", option_items: ['a', 'b', 'c'], multi_select: true)
218
+ assert(field.list_box?)
219
+ assert_equal(['a', 'b', 'c'], field.option_items)
220
+ assert(field.flagged?(:multi_select))
221
+ applies_variable_text_properties(:create_list_box)
222
+ end
223
+ end
224
+
225
+ it "returns the default resources" do
226
+ assert_kind_of(HexaPDF::Type::Resources, @acro_form.default_resources)
227
+ end
228
+
229
+ describe "set_default_appearance_string" do
230
+ it "uses sane default values if no arguments are provided" do
231
+ @acro_form.set_default_appearance_string
232
+ assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
233
+ font = @acro_form.default_resources.font(:F1)
234
+ assert(font)
235
+ assert_equal(:Helvetica, font[:BaseFont])
236
+ end
237
+
238
+ it "allows specifying the used font and font size" do
239
+ @acro_form.set_default_appearance_string(font: 'Times', font_size: 10)
240
+ assert_equal("0 g /F1 10 Tf", @acro_form[:DA])
241
+ assert_equal(:'Times-Roman', @acro_form.default_resources.font(:F1)[:BaseFont])
242
+ end
243
+ end
244
+
245
+ it "sets the /NeedAppearances key" do
246
+ @acro_form.need_appearances!
247
+ assert(@acro_form[:NeedAppearances])
248
+ end
249
+
250
+ describe "create_appearances" do
251
+ before do
252
+ @tf = @acro_form.create_text_field('test')
253
+ @tf.set_default_appearance_string
254
+ @tf.create_widget(@doc.pages.add)
255
+ @cb = @acro_form.create_check_box('test2')
256
+ @cb.create_widget(@doc.pages.add)
257
+ end
258
+
259
+ it "creates the appearances of all field widgets if necessary" do
260
+ @acro_form.create_appearances
261
+ assert(@tf.each_widget.all? {|w| w.appearance_dict.normal_appearance.kind_of?(HexaPDF::Stream) })
262
+ assert(@cb.each_widget.all? {|w| w.appearance_dict.normal_appearance[:Yes].kind_of?(HexaPDF::Stream) })
263
+ end
264
+
265
+ it "force the creation of appearances if force is true" do
266
+ @acro_form.create_appearances
267
+ text_stream = @tf[:AP][:N].raw_stream
268
+ @acro_form.create_appearances
269
+ assert_same(text_stream, @tf[:AP][:N].raw_stream)
270
+ @acro_form.create_appearances(force: true)
271
+ refute_same(text_stream, @tf[:AP][:N].raw_stream)
272
+ end
273
+ end
274
+
275
+ describe "flatten" do
276
+ before do
277
+ @acro_form.root_fields << @doc.wrap({T: 'test'})
278
+ @tf = @acro_form.create_text_field('textfields')
279
+ @tf.set_default_appearance_string
280
+ @tf[:V] = 'Test'
281
+ @tf.create_widget(@doc.pages.add)
282
+ @cb = @acro_form.create_check_box('test.checkbox')
283
+ @cb.create_widget(@doc.pages[0])
284
+ @cb.create_widget(@doc.pages.add)
285
+ end
286
+
287
+ it "creates the missing appearances if instructed to do so" do
288
+ assert_equal(3, @acro_form.flatten(create_appearances: false).size)
289
+ assert_equal(0, @acro_form.flatten(create_appearances: true).size)
290
+ end
291
+
292
+ it "flattens the whole interactive form" do
293
+ result = @acro_form.flatten
294
+ assert(result.empty?)
295
+ assert(@tf.null?)
296
+ assert(@cb.null?)
297
+ assert(@acro_form.null?)
298
+ refute(@doc.catalog.key?(:AcroForm))
299
+ end
300
+
301
+ it "flattens the given fields" do
302
+ result = @acro_form.flatten(fields: [@cb])
303
+ assert(result.empty?)
304
+ assert(@cb.null?)
305
+ refute(@tf.null?)
306
+ refute(@acro_form.null?)
307
+ assert(@doc.catalog.key?(:AcroForm))
308
+ end
309
+
310
+ it "doesn't delete the form object if not all fields were flattened" do
311
+ @acro_form.create_appearances
312
+ @tf.delete(:AP)
313
+ result = @acro_form.flatten(create_appearances: false)
314
+ assert_equal(1, result.size)
315
+ assert(@doc.catalog.key?(:AcroForm))
316
+ end
317
+ end
318
+
319
+ describe "perform_validation" do
320
+ it "checks whether the /DR field is available when /DA is set" do
321
+ @acro_form[:DA] = 'test'
322
+ refute(@acro_form.validate)
323
+ end
324
+
325
+ it "checks whether the font used in /DA is available in /DR" do
326
+ @acro_form[:DA] = '/F2 0 Tf /F1 0 Tf'
327
+ refute(@acro_form.validate {|msg| assert_match(/DR must also be present/, msg) })
328
+ @acro_form.default_resources[:Font] = {}
329
+ refute(@acro_form.validate {|msg| assert_match(/font.*is not.*resource/, msg) })
330
+ @acro_form.default_resources[:Font][:F1] = :yes
331
+ assert(@acro_form.validate)
332
+ end
333
+
334
+ it "set the default appearance string, though optional, to a valid value to avoid problems" do
335
+ assert(@acro_form.validate)
336
+ assert_equal("0 g /F1 0 Tf", @acro_form[:DA])
337
+ end
338
+
339
+ describe "automatically creates the terminal fields; appearances" do
340
+ before do
341
+ @cb = @acro_form.create_check_box('test2')
342
+ @cb.create_widget(@doc.pages.add)
343
+ end
344
+
345
+ it "does this if the configuration option is true" do
346
+ assert(@acro_form.validate)
347
+ assert_kind_of(HexaPDF::Stream, @cb[:AP][:N][:Yes])
348
+ end
349
+
350
+ it "does nothing if the configuration option is false" do
351
+ @doc.config['acro_form.create_appearances'] = false
352
+ assert(@acro_form.validate)
353
+ refute_kind_of(HexaPDF::Stream, @cb[:AP][:N][:Yes])
354
+ end
355
+ end
356
+ end
357
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/acro_form/signature_field'
6
+
7
+ describe HexaPDF::Type::AcroForm::SignatureField::LockDictionary do
8
+ it "validates the presence of the /Fields key" do
9
+ doc = HexaPDF::Document.new
10
+ obj = HexaPDF::Type::AcroForm::SignatureField::LockDictionary.new({Action: :All}, document: doc)
11
+ assert(obj.validate)
12
+ obj[:Action] = :Include
13
+ refute(obj.validate)
14
+ end
15
+ end
16
+
17
+ describe HexaPDF::Type::AcroForm::SignatureField do
18
+ before do
19
+ @doc = HexaPDF::Document.new
20
+ @field = @doc.wrap({}, type: :XXAcroFormField, subtype: :Sig)
21
+ end
22
+
23
+ it "sets the field value" do
24
+ @field.field_value = {Empty: :True}
25
+ assert_equal({Empty: :True}, @field[:V].value)
26
+ end
27
+
28
+ it "gets the field value" do
29
+ @field[:V] = {Empty: :True}
30
+ assert_equal({Empty: :True}, @field.field_value.value)
31
+ end
32
+
33
+ it "validates the value of the /FT field" do
34
+ refute(@field.validate(auto_correct: false))
35
+ assert(@field.validate)
36
+ assert_equal(:Sig, @field.field_type)
37
+ end
38
+ end