hexapdf 0.11.9 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -8,7 +8,7 @@ describe HexaPDF::Type::Annotations::Text do
8
8
  before do
9
9
  @doc = HexaPDF::Document.new
10
10
  @doc.version = '1.5'
11
- @annot = HexaPDF::Type::Annotations::Text.new({Rect: [0, 0, 1, 1]}, document: @doc)
11
+ @annot = HexaPDF::Type::Annotations::Text.new({Rect: [0, 0, 1, 1]}, document: @doc, oid: 1)
12
12
  end
13
13
 
14
14
  describe "validation" do
@@ -22,3 +22,202 @@ describe HexaPDF::Type::Annotations::Widget::AppearanceCharacteristics do
22
22
  end
23
23
  end
24
24
  end
25
+
26
+ describe HexaPDF::Type::Annotations::Widget do
27
+ before do
28
+ @doc = HexaPDF::Document.new
29
+ @widget = @doc.wrap({Type: :Annot, Subtype: :Widget})
30
+ end
31
+
32
+ describe "form_field" do
33
+ it "works for the field and widget being the same object" do
34
+ @widget[:FT] = :Tx
35
+ @widget[:T] = 'field'
36
+ result = @widget.form_field
37
+ assert_kind_of(HexaPDF::Type::AcroForm::TextField, result)
38
+ assert_same(@widget.data, result.data)
39
+ end
40
+
41
+ it "works for a field with a parent field and the widget being the same object" do
42
+ @widget[:Parent] = {FT: :Tx, T: 'parent', Kids: [@widget]}
43
+ @widget[:T] = 'field'
44
+ result = @widget.form_field
45
+ assert_kind_of(HexaPDF::Type::AcroForm::TextField, result)
46
+ assert_same(@widget.data, result.data)
47
+ end
48
+
49
+ it "works for the widget being in the /Kids array of the field" do
50
+ @widget[:Parent] = {FT: :Tx, T: 'parent', Kids: [@widget]}
51
+ result = @widget.form_field
52
+ assert_kind_of(HexaPDF::Type::AcroForm::TextField, result)
53
+ refute_same(@widget.data, result.data)
54
+ end
55
+ end
56
+
57
+ describe "background_color" do
58
+ it "returns the current background color" do
59
+ assert_nil(@widget.background_color)
60
+ @widget[:MK] = {BG: [1]}
61
+ assert_equal([1], @widget.background_color.components)
62
+ end
63
+
64
+ it "sets the color and returns self" do
65
+ assert_same(@widget, @widget.background_color(51))
66
+ assert_equal([0.2], @widget.background_color.components)
67
+ end
68
+ end
69
+
70
+ describe "border_style" do
71
+ before do
72
+ @widget[:MK] = {BC: [1, 0, 1]}
73
+ @color = HexaPDF::Content::ColorSpace.prenormalized_device_color([1, 0, 1])
74
+ end
75
+
76
+ describe "getter" do
77
+ it "no /Border, /BS or /MK set" do
78
+ @widget.delete(:MK)
79
+ assert_equal([1, nil, :solid, 0, 0], @widget.border_style.to_a)
80
+ end
81
+
82
+ it "no /Border, /BS but with /MK empty" do
83
+ @widget[:MK].delete(:BC)
84
+ assert_equal([1, nil, :solid, 0, 0], @widget.border_style.to_a)
85
+ end
86
+
87
+ it "uses the color from /MK" do
88
+ assert_equal([1, @color, :solid, 0, 0], @widget.border_style.to_a)
89
+ @widget[:MK][:BC] = []
90
+ assert_equal([1, nil, :solid, 0, 0], @widget.border_style.to_a)
91
+ end
92
+
93
+ it "uses the data from /Border" do
94
+ @widget[:Border] = [1, 2, 3, [1, 2]]
95
+ assert_equal([3, @color, [1, 2], 1, 2], @widget.border_style.to_a)
96
+ end
97
+
98
+ it "uses the data from /BS, overriding /Border values" do
99
+ @widget[:Border] = [1, 2, 3, [1, 2]]
100
+ @widget[:BS] = {W: 5, S: :D, D: [5, 6]}
101
+ assert_equal([5, @color, [5, 6], 0, 0], @widget.border_style.to_a)
102
+
103
+ [[:S, :solid], [:D, [5, 6]], [:B, :beveled], [:I, :inset],
104
+ [:U, :underlined], [:Unknown, :solid]].each do |val, result|
105
+ @widget[:BS] = {S: val, D: [5, 6]}
106
+ assert_equal([1, @color, result, 0, 0], @widget.border_style.to_a)
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "setter" do
112
+ it "returns self" do
113
+ assert_equal(@widget, @widget.border_style(width: 1))
114
+ end
115
+
116
+ it "sets the color" do
117
+ @widget.border_style(color: [1.0, 51, 1.0])
118
+ assert_equal([1, 0.2, 1], @widget[:MK][:BC].value)
119
+
120
+ @widget.border_style(color: :transparent)
121
+ assert_equal([], @widget[:MK][:BC].value)
122
+ end
123
+
124
+ it "sets the width" do
125
+ @widget.border_style(width: 2)
126
+ assert_equal(2, @widget[:BS][:W])
127
+ end
128
+
129
+ it "sets the style" do
130
+ [[:solid, :S], [[5, 6], :D], [:beveled, :B], [:inset, :I], [:underlined, :U]].each do |val, r|
131
+ @widget.border_style(style: val)
132
+ assert_equal(r, @widget[:BS][:S])
133
+ assert_equal(val, @widget[:BS][:D].value) if r == :D
134
+ end
135
+ end
136
+
137
+ it "overrides all priorly set values" do
138
+ @widget.border_style(width: 3, style: :inset, color: [1])
139
+ @widget.border_style(width: 5)
140
+ border_style = @widget.border_style
141
+ assert_equal(:solid, border_style.style)
142
+ assert_equal([0], border_style.color.components)
143
+ end
144
+
145
+ it "raises an error for an unknown style" do
146
+ assert_raises(ArgumentError) { @widget.border_style(style: :unknown) }
147
+ end
148
+ end
149
+ end
150
+
151
+ describe "marker_style" do
152
+ before do
153
+ @chars = %w[4 l 8 u n H S]
154
+ @values = [:check, :circle, :cross, :diamond, :square, :star, 'S']
155
+ @widget[:Parent] = {FT: :Btn}
156
+ end
157
+
158
+ describe "style" do
159
+ it "returns the style" do
160
+ @chars.zip(@values) do |char, result|
161
+ @widget[:MK] = {CA: char}
162
+ assert_equal(result, @widget.marker_style.style)
163
+ end
164
+ end
165
+
166
+ it "returns the correct default button style depending on the field" do
167
+ @widget.form_field.initialize_as_check_box
168
+ assert_equal(:check, @widget.marker_style.style)
169
+ @widget.form_field.initialize_as_radio_button
170
+ assert_equal(:circle, @widget.marker_style.style)
171
+ end
172
+
173
+ it "sets the button style" do
174
+ @values.zip(@chars) do |argument, char|
175
+ @widget.marker_style(style: argument)
176
+ assert_equal(char, @widget[:MK][:CA])
177
+ end
178
+ end
179
+
180
+ it "fails if an invalid argument is provided" do
181
+ assert_raises(ArgumentError) { @widget.marker_style(style: 5) }
182
+ end
183
+ end
184
+
185
+ describe "size" do
186
+ it "returns the size" do
187
+ @widget.form_field[:DA] = "/F 15 Tf"
188
+ assert_equal(15, @widget.marker_style.size)
189
+ @widget[:DA] = "/F 10 Tf"
190
+ assert_equal(10, @widget.marker_style.size)
191
+ end
192
+
193
+ it "returns the default size if none is set" do
194
+ assert_equal(0, @widget.marker_style.size)
195
+ end
196
+
197
+ it "sets the given size" do
198
+ @widget.marker_style(size: 10)
199
+ assert_equal('/ZaDb 10 Tf 0.0 g', @widget[:DA])
200
+ end
201
+ end
202
+
203
+ describe "color" do
204
+ it "returns the color" do
205
+ @widget.form_field[:DA] = "0 1 0 rg"
206
+ assert_equal([0, 1, 0], @widget.marker_style.color.components)
207
+ @widget[:DA] = "0 0 1 rg"
208
+ assert_equal([0, 0, 1], @widget.marker_style.color.components)
209
+ end
210
+
211
+ it "returns the default color if none is set" do
212
+ assert_equal([0], @widget.marker_style.color.components)
213
+ end
214
+
215
+ it "sets the given color" do
216
+ @widget.marker_style(color: [1.0, 51, 1.0])
217
+ assert_equal([1, 0.2, 1], @widget.marker_style.color.components)
218
+ @widget.marker_style(color: [1.0, 20, 1.0, 1.0])
219
+ assert_equal([1, 0.2, 1, 1], @widget.marker_style.color.components)
220
+ end
221
+ end
222
+ end
223
+ end
@@ -4,12 +4,57 @@ require 'test_helper'
4
4
  require 'hexapdf/document'
5
5
  require 'hexapdf/type/annotation'
6
6
 
7
+ describe HexaPDF::Type::Annotation::AppearanceDictionary do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @ap = @doc.add({N: :n, D: :d, R: :r}, type: :XXAppearanceDictionary)
11
+ end
12
+
13
+ it "resolves the normal appearance" do
14
+ assert_equal(:n, @ap.normal_appearance)
15
+ end
16
+
17
+ it "resolves the rollover appearance" do
18
+ assert_equal(:r, @ap.rollover_appearance)
19
+ @ap.delete(:R)
20
+ assert_equal(:n, @ap.rollover_appearance)
21
+ end
22
+
23
+ it "resolves the down appearance" do
24
+ assert_equal(:d, @ap.down_appearance)
25
+ @ap.delete(:D)
26
+ assert_equal(:n, @ap.down_appearance)
27
+ end
28
+ end
29
+
7
30
  describe HexaPDF::Type::Annotation do
8
31
  before do
9
32
  @doc = HexaPDF::Document.new
10
33
  @annot = @doc.add({Type: :Annot, F: 0b100011})
11
34
  end
12
35
 
36
+ it "must always be indirect" do
37
+ @annot.must_be_indirect = false
38
+ assert(@annot.must_be_indirect?)
39
+ end
40
+
41
+ it "returns the appearance dictionary" do
42
+ @annot[:AP] = :yes
43
+ assert_equal(:yes, @annot.appearance)
44
+ end
45
+
46
+ it "checks whether an appearance exists" do
47
+ refute(@annot.appearance?)
48
+ @annot[:AP] = {N: {}}
49
+ refute(@annot.appearance?)
50
+ @annot[:AP][:N] = @doc.wrap({}, stream: '')
51
+ assert(@annot.appearance?)
52
+ @annot[:AP][:N] = {okay: @doc.wrap({}, stream: '')}
53
+ assert(@annot.appearance?)
54
+ @annot[:AP][:N][:Off] = :other
55
+ refute(@annot.appearance?)
56
+ end
57
+
13
58
  describe "flags" do
14
59
  it "returns all flags" do
15
60
  assert_equal([:invisible, :hidden, :no_view], @annot.flags)
@@ -21,6 +21,24 @@ describe HexaPDF::Type::Catalog do
21
21
  assert_equal(:Pages, pages.type)
22
22
  end
23
23
 
24
+ describe "acro_form" do
25
+ it "returns an existing form object" do
26
+ @catalog[:AcroForm] = :test
27
+ assert_equal(:test, @catalog.acro_form)
28
+ end
29
+
30
+ it "returns an existing form object even if create: true" do
31
+ @catalog[:AcroForm] = :test
32
+ assert_equal(:test, @catalog.acro_form(create: true))
33
+ end
34
+
35
+ it "creates a new AcroForm object with defaults if create: true" do
36
+ form = @catalog.acro_form(create: true)
37
+ assert_kind_of(HexaPDF::Type::AcroForm::Form, form)
38
+ assert(form[:DA])
39
+ end
40
+ end
41
+
24
42
  describe "validation" do
25
43
  it "creates the page tree if necessary" do
26
44
  refute(@catalog.validate(auto_correct: false))
@@ -17,6 +17,11 @@ describe HexaPDF::Type::Font do
17
17
  @font = @doc.add({Type: :Font, BaseFont: :TestFont, FontDescriptor: fd, ToUnicode: cmap})
18
18
  end
19
19
 
20
+ it "allows setting and returning a font wrapper object" do
21
+ @font.font_wrapper = :fake_wrapper
22
+ assert_equal(:fake_wrapper, @font.font_wrapper)
23
+ end
24
+
20
25
  it "must always be an indirect" do
21
26
  assert(@font.must_be_indirect?)
22
27
  end
@@ -161,8 +161,9 @@ describe HexaPDF::Type::FontSimple do
161
161
  end
162
162
 
163
163
  describe "validation" do
164
+ before { assert(@font.validate) }
165
+
164
166
  it "validates the existence of required keys" do
165
- assert(@font.validate)
166
167
  @font.delete(:FirstChar)
167
168
  refute(@font.validate)
168
169
  end
@@ -16,6 +16,12 @@ describe HexaPDF::Type::FontTrueType do
16
16
  end
17
17
 
18
18
  describe "validation" do
19
+ it "ignores some missing fields if the font name is one of the standard PDF fonts" do
20
+ @font[:BaseFont] = :'Arial,Bold'
21
+ [:FirstChar, :LastChar, :Widths, :FontDescriptor].each {|field| @font.delete(field) }
22
+ assert(@font.validate)
23
+ end
24
+
19
25
  it "requires that the FontDescriptor key is set" do
20
26
  assert(@font.validate)
21
27
  @font.delete(:FontDescriptor)
@@ -57,6 +57,14 @@ describe HexaPDF::Type::FontType1 do
57
57
  FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]})
58
58
  end
59
59
 
60
+ it "can create a usable font wrapper for the standard fonts" do
61
+ wrapper = @font.font_wrapper
62
+ assert(wrapper)
63
+ assert_same(@font, wrapper.pdf_object)
64
+ assert_equal(@font[:BaseFont], wrapper.wrapped_font.font_name.intern)
65
+ assert_same(wrapper, @font.font_wrapper)
66
+ end
67
+
60
68
  describe "encoding" do
61
69
  it "returns the the standard font's encoding" do
62
70
  @font.delete(:Encoding)
@@ -49,7 +49,7 @@ describe HexaPDF::Type::Form do
49
49
  it "creates the resource dictionary if it is not found" do
50
50
  resources = @form.resources
51
51
  assert_equal(:XXResources, resources.type)
52
- assert_equal({}, resources.value)
52
+ assert_equal({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
53
53
  end
54
54
 
55
55
  it "returns the already used resource dictionary" do
@@ -82,9 +82,27 @@ describe HexaPDF::Type::Form do
82
82
  end
83
83
 
84
84
  describe "canvas" do
85
+ # Asserts that the form's contents contains the operators.
86
+ def assert_operators(form, operators)
87
+ processor = TestHelper::OperatorRecorder.new
88
+ form.process_contents(processor)
89
+ assert_equal(operators, processor.recorded_ops)
90
+ end
91
+
85
92
  it "always returns the same Canvas instance" do
93
+ @form[:BBox] = [0, 0, 100, 100]
86
94
  canvas = @form.canvas
87
95
  assert_same(canvas, @form.canvas)
96
+ assert_operators(@form, [])
97
+ end
98
+
99
+ it "always moves the origin to the bottom left corner of the bounding box" do
100
+ @form[:BBox] = [-10, -5, 100, 300]
101
+ @form.canvas.line_width = 5
102
+ assert_operators(@form, [[:save_graphics_state],
103
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
104
+ [:set_line_width, [5]],
105
+ [:restore_graphics_state]])
88
106
  end
89
107
 
90
108
  it "fails if the form XObject already has data" do
@@ -120,6 +120,12 @@ describe HexaPDF::Type::Image do
120
120
  assert_equal(:other, info.color_space)
121
121
  assert_equal(-1, info.components)
122
122
  end
123
+
124
+ it "processes the SMask entry" do
125
+ @image[:SMask] = :something
126
+ info = @image.info
127
+ refute(info.writable)
128
+ end
123
129
  end
124
130
 
125
131
  describe "write" do
@@ -182,6 +188,7 @@ describe HexaPDF::Type::Image do
182
188
  end
183
189
 
184
190
  Dir.glob(File.join(TEST_DATA_DIR, 'images', '*.png')).each do |png_file|
191
+ next if png_file =~ /alpha/
185
192
  it "writes #{File.basename(png_file)} correctly as PNG file" do
186
193
  image = @doc.images.add(png_file)
187
194
  if png_file =~ /greyscale-1bit.png/ # force use of arrays for one image
@@ -277,7 +277,7 @@ describe HexaPDF::Type::Page do
277
277
  page = @doc.add({Type: :Page, Parent: @doc.pages.root})
278
278
  resources = page.resources
279
279
  assert_equal(:XXResources, resources.type)
280
- assert_equal({}, resources.value)
280
+ assert_equal({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
281
281
  end
282
282
 
283
283
  it "returns the already used resource dictionary" do
@@ -319,6 +319,13 @@ describe HexaPDF::Type::Page do
319
319
  end
320
320
  end
321
321
 
322
+ it "returns all ancestor page tree nodes of a page" do
323
+ root = @doc.add({Type: :Pages})
324
+ kid = @doc.add({Type: :Pages, Parent: root})
325
+ page = @doc.add({Type: :Page, Parent: kid})
326
+ assert_equal([kid, root], page.ancestor_nodes)
327
+ end
328
+
322
329
  describe "canvas" do
323
330
  before do
324
331
  @page = @doc.pages.add
@@ -331,31 +338,36 @@ describe HexaPDF::Type::Page do
331
338
  @page.canvas(type: :overlay).line_width = 5
332
339
  assert_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
333
340
  [:save_graphics_state], [:set_line_width, [10]],
334
- [:restore_graphics_state], [:set_line_width, [5]]])
341
+ [:restore_graphics_state], [:save_graphics_state],
342
+ [:set_line_width, [5]], [:restore_graphics_state]])
335
343
 
336
344
  @page.canvas(type: :underlay).line_width = 2
337
345
  assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
338
346
  [:restore_graphics_state], [:save_graphics_state],
339
347
  [:set_line_width, [10]],
340
- [:restore_graphics_state], [:set_line_width, [5]]])
348
+ [:restore_graphics_state], [:save_graphics_state],
349
+ [:set_line_width, [5]], [:restore_graphics_state]])
341
350
  end
342
351
 
343
352
  it "works correctly if invoked on an empty page, using type :underlay in first invocation" do
344
353
  @page.canvas(type: :underlay).line_width = 2
345
354
  assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
355
+ [:restore_graphics_state], [:save_graphics_state],
346
356
  [:restore_graphics_state], [:save_graphics_state],
347
357
  [:restore_graphics_state]])
348
358
 
349
359
  @page.canvas.line_width = 10
350
360
  assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
351
361
  [:restore_graphics_state], [:save_graphics_state],
352
- [:set_line_width, [10]], [:restore_graphics_state]])
362
+ [:set_line_width, [10]], [:restore_graphics_state],
363
+ [:save_graphics_state], [:restore_graphics_state]])
353
364
 
354
365
  @page.canvas(type: :overlay).line_width = 5
355
366
  assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
356
367
  [:restore_graphics_state], [:save_graphics_state],
357
368
  [:set_line_width, [10]],
358
- [:restore_graphics_state], [:set_line_width, [5]]])
369
+ [:restore_graphics_state], [:save_graphics_state],
370
+ [:set_line_width, [5]], [:restore_graphics_state]])
359
371
  end
360
372
 
361
373
  it "works correctly if invoked on a page with existing contents" do
@@ -364,13 +376,39 @@ describe HexaPDF::Type::Page do
364
376
  @page.canvas(type: :overlay).line_width = 5
365
377
  assert_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
366
378
  [:save_graphics_state], [:set_line_width, [10]],
367
- [:restore_graphics_state], [:set_line_width, [5]]])
379
+ [:restore_graphics_state],
380
+ [:save_graphics_state], [:set_line_width, [5]],
381
+ [:restore_graphics_state]])
368
382
 
369
383
  @page.canvas(type: :underlay).line_width = 2
370
384
  assert_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
371
385
  [:restore_graphics_state], [:save_graphics_state],
372
386
  [:set_line_width, [10]],
373
- [:restore_graphics_state], [:set_line_width, [5]]])
387
+ [:restore_graphics_state],
388
+ [:save_graphics_state], [:set_line_width, [5]],
389
+ [:restore_graphics_state]])
390
+ end
391
+
392
+ it "works correctly if the page has its origin not at (0,0)" do
393
+ @page.box(:media, [-10, -5, 100, 300])
394
+ @page.canvas(type: :underlay).line_width = 2
395
+ @page.canvas(type: :page).line_width = 2
396
+ @page.canvas(type: :overlay).line_width = 2
397
+
398
+ assert_operators(@page, [[:save_graphics_state],
399
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
400
+ [:set_line_width, [2]],
401
+ [:restore_graphics_state],
402
+
403
+ [:save_graphics_state],
404
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
405
+ [:set_line_width, [2]],
406
+ [:restore_graphics_state],
407
+
408
+ [:save_graphics_state],
409
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
410
+ [:set_line_width, [2]],
411
+ [:restore_graphics_state]])
374
412
  end
375
413
 
376
414
  it "fails if the page canvas is requested for a page with existing contents" do