hexapdf 0.11.9 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE +1 -1
  4. data/examples/001-hello_world.rb +1 -1
  5. data/examples/002-graphics.rb +1 -1
  6. data/examples/003-arcs.rb +1 -1
  7. data/examples/004-optimizing.rb +1 -1
  8. data/examples/005-merging.rb +1 -1
  9. data/examples/006-standard_pdf_fonts.rb +1 -1
  10. data/examples/007-truetype.rb +1 -1
  11. data/examples/008-show_char_bboxes.rb +1 -1
  12. data/examples/009-text_layouter_alignment.rb +1 -1
  13. data/examples/010-text_layouter_inline_boxes.rb +1 -1
  14. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  15. data/examples/012-text_layouter_styling.rb +1 -1
  16. data/examples/013-text_layouter_shapes.rb +1 -1
  17. data/examples/014-text_in_polygon.rb +1 -1
  18. data/examples/015-boxes.rb +1 -1
  19. data/examples/016-frame_automatic_box_placement.rb +1 -1
  20. data/examples/017-frame_text_flow.rb +1 -1
  21. data/examples/018-composer.rb +1 -1
  22. data/examples/019-acro_form.rb +51 -0
  23. data/lib/hexapdf.rb +1 -1
  24. data/lib/hexapdf/cli.rb +3 -1
  25. data/lib/hexapdf/cli/batch.rb +1 -1
  26. data/lib/hexapdf/cli/command.rb +18 -9
  27. data/lib/hexapdf/cli/files.rb +1 -1
  28. data/lib/hexapdf/cli/form.rb +240 -0
  29. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  30. data/lib/hexapdf/cli/images.rb +1 -1
  31. data/lib/hexapdf/cli/info.rb +1 -1
  32. data/lib/hexapdf/cli/inspect.rb +1 -1
  33. data/lib/hexapdf/cli/merge.rb +1 -1
  34. data/lib/hexapdf/cli/modify.rb +1 -1
  35. data/lib/hexapdf/cli/optimize.rb +1 -1
  36. data/lib/hexapdf/cli/split.rb +1 -1
  37. data/lib/hexapdf/cli/watermark.rb +1 -1
  38. data/lib/hexapdf/composer.rb +2 -2
  39. data/lib/hexapdf/configuration.rb +66 -11
  40. data/lib/hexapdf/content.rb +3 -1
  41. data/lib/hexapdf/content/canvas.rb +5 -18
  42. data/lib/hexapdf/content/color_space.rb +111 -32
  43. data/lib/hexapdf/content/graphic_object.rb +1 -1
  44. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  45. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  46. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  47. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  48. data/lib/hexapdf/content/graphics_state.rb +1 -1
  49. data/lib/hexapdf/content/operator.rb +9 -9
  50. data/lib/hexapdf/content/parser.rb +18 -5
  51. data/lib/hexapdf/content/processor.rb +1 -1
  52. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  53. data/lib/hexapdf/data_dir.rb +1 -1
  54. data/lib/hexapdf/dictionary.rb +1 -1
  55. data/lib/hexapdf/dictionary_fields.rb +1 -1
  56. data/lib/hexapdf/document.rb +14 -5
  57. data/lib/hexapdf/document/files.rb +1 -1
  58. data/lib/hexapdf/document/fonts.rb +1 -1
  59. data/lib/hexapdf/document/images.rb +1 -1
  60. data/lib/hexapdf/document/pages.rb +3 -14
  61. data/lib/hexapdf/encryption.rb +1 -1
  62. data/lib/hexapdf/encryption/aes.rb +1 -1
  63. data/lib/hexapdf/encryption/arc4.rb +1 -1
  64. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  65. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  66. data/lib/hexapdf/encryption/identity.rb +1 -1
  67. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  68. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  69. data/lib/hexapdf/encryption/security_handler.rb +1 -1
  70. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
  71. data/lib/hexapdf/error.rb +1 -1
  72. data/lib/hexapdf/filter.rb +3 -3
  73. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  74. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  75. data/lib/hexapdf/filter/encryption.rb +1 -1
  76. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  77. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  78. data/lib/hexapdf/filter/{jpx_decode.rb → pass_through.rb} +5 -5
  79. data/lib/hexapdf/filter/predictor.rb +1 -1
  80. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  81. data/lib/hexapdf/font/cmap.rb +1 -1
  82. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  83. data/lib/hexapdf/font/cmap/writer.rb +1 -1
  84. data/lib/hexapdf/font/encoding.rb +1 -1
  85. data/lib/hexapdf/font/encoding/base.rb +1 -1
  86. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  87. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  88. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  89. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  90. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  91. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  92. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  93. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  94. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  95. data/lib/hexapdf/font/true_type.rb +1 -1
  96. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  97. data/lib/hexapdf/font/true_type/font.rb +1 -1
  98. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  99. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  100. data/lib/hexapdf/font/true_type/table.rb +1 -1
  101. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  102. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  103. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  104. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  105. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  106. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  107. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  108. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  109. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  114. data/lib/hexapdf/font/true_type_wrapper.rb +54 -51
  115. data/lib/hexapdf/font/type1.rb +1 -1
  116. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  117. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  118. data/lib/hexapdf/font/type1/font.rb +1 -1
  119. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  120. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  121. data/lib/hexapdf/font/type1_wrapper.rb +67 -51
  122. data/lib/hexapdf/font_loader.rb +1 -1
  123. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  124. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  125. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  126. data/lib/hexapdf/image_loader.rb +1 -1
  127. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  128. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  129. data/lib/hexapdf/image_loader/png.rb +1 -1
  130. data/lib/hexapdf/importer.rb +2 -4
  131. data/lib/hexapdf/layout.rb +1 -1
  132. data/lib/hexapdf/layout/box.rb +1 -1
  133. data/lib/hexapdf/layout/frame.rb +1 -1
  134. data/lib/hexapdf/layout/image_box.rb +1 -1
  135. data/lib/hexapdf/layout/inline_box.rb +1 -1
  136. data/lib/hexapdf/layout/line.rb +1 -1
  137. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  138. data/lib/hexapdf/layout/style.rb +1 -1
  139. data/lib/hexapdf/layout/text_box.rb +1 -1
  140. data/lib/hexapdf/layout/text_fragment.rb +1 -1
  141. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  142. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  143. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  144. data/lib/hexapdf/name_tree_node.rb +1 -1
  145. data/lib/hexapdf/number_tree_node.rb +1 -1
  146. data/lib/hexapdf/object.rb +2 -2
  147. data/lib/hexapdf/parser.rb +4 -3
  148. data/lib/hexapdf/pdf_array.rb +1 -1
  149. data/lib/hexapdf/rectangle.rb +31 -1
  150. data/lib/hexapdf/reference.rb +1 -1
  151. data/lib/hexapdf/revision.rb +2 -1
  152. data/lib/hexapdf/revisions.rb +1 -1
  153. data/lib/hexapdf/serializer.rb +1 -1
  154. data/lib/hexapdf/stream.rb +1 -1
  155. data/lib/hexapdf/task.rb +1 -1
  156. data/lib/hexapdf/task/dereference.rb +1 -1
  157. data/lib/hexapdf/task/optimize.rb +1 -1
  158. data/lib/hexapdf/tokenizer.rb +1 -1
  159. data/lib/hexapdf/type.rb +1 -1
  160. data/lib/hexapdf/type/acro_form.rb +7 -1
  161. data/lib/hexapdf/type/acro_form/appearance_generator.rb +401 -0
  162. data/lib/hexapdf/type/acro_form/button_field.rb +300 -0
  163. data/lib/hexapdf/type/acro_form/choice_field.rb +220 -0
  164. data/lib/hexapdf/type/acro_form/field.rb +220 -17
  165. data/lib/hexapdf/type/acro_form/form.rb +157 -7
  166. data/lib/hexapdf/type/acro_form/text_field.rb +186 -0
  167. data/lib/hexapdf/type/acro_form/variable_text_field.rb +122 -0
  168. data/lib/hexapdf/type/action.rb +1 -1
  169. data/lib/hexapdf/type/actions.rb +1 -1
  170. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  171. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  172. data/lib/hexapdf/type/actions/launch.rb +1 -1
  173. data/lib/hexapdf/type/actions/uri.rb +1 -1
  174. data/lib/hexapdf/type/annotation.rb +73 -3
  175. data/lib/hexapdf/type/annotations.rb +1 -1
  176. data/lib/hexapdf/type/annotations/link.rb +2 -2
  177. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  178. data/lib/hexapdf/type/annotations/text.rb +1 -1
  179. data/lib/hexapdf/type/annotations/widget.rb +239 -2
  180. data/lib/hexapdf/type/catalog.rb +21 -1
  181. data/lib/hexapdf/type/cid_font.rb +1 -1
  182. data/lib/hexapdf/type/embedded_file.rb +1 -1
  183. data/lib/hexapdf/type/file_specification.rb +1 -1
  184. data/lib/hexapdf/type/font.rb +18 -1
  185. data/lib/hexapdf/type/font_descriptor.rb +2 -2
  186. data/lib/hexapdf/type/font_simple.rb +1 -1
  187. data/lib/hexapdf/type/font_true_type.rb +1 -1
  188. data/lib/hexapdf/type/font_type0.rb +1 -1
  189. data/lib/hexapdf/type/font_type1.rb +16 -1
  190. data/lib/hexapdf/type/font_type3.rb +1 -1
  191. data/lib/hexapdf/type/form.rb +1 -1
  192. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  193. data/lib/hexapdf/type/icon_fit.rb +1 -1
  194. data/lib/hexapdf/type/image.rb +3 -1
  195. data/lib/hexapdf/type/info.rb +1 -1
  196. data/lib/hexapdf/type/names.rb +1 -1
  197. data/lib/hexapdf/type/object_stream.rb +1 -1
  198. data/lib/hexapdf/type/page.rb +1 -1
  199. data/lib/hexapdf/type/page_tree_node.rb +8 -11
  200. data/lib/hexapdf/type/resources.rb +16 -3
  201. data/lib/hexapdf/type/trailer.rb +2 -3
  202. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  203. data/lib/hexapdf/type/xref_stream.rb +1 -1
  204. data/lib/hexapdf/utils/bit_field.rb +38 -24
  205. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  206. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  207. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  208. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  209. data/lib/hexapdf/utils/object_hash.rb +1 -1
  210. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  211. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  212. data/lib/hexapdf/version.rb +2 -2
  213. data/lib/hexapdf/writer.rb +1 -1
  214. data/lib/hexapdf/xref_section.rb +1 -1
  215. data/test/hexapdf/content/common.rb +2 -2
  216. data/test/hexapdf/content/test_color_space.rb +71 -8
  217. data/test/hexapdf/content/test_operator.rb +22 -22
  218. data/test/hexapdf/content/test_parser.rb +14 -0
  219. data/test/hexapdf/document/test_fonts.rb +1 -1
  220. data/test/hexapdf/document/test_pages.rb +6 -6
  221. data/test/hexapdf/font/test_true_type_wrapper.rb +10 -7
  222. data/test/hexapdf/font/test_type1_wrapper.rb +32 -8
  223. data/test/hexapdf/test_document.rb +12 -0
  224. data/test/hexapdf/test_parser.rb +10 -0
  225. data/test/hexapdf/test_rectangle.rb +14 -0
  226. data/test/hexapdf/test_revision.rb +3 -0
  227. data/test/hexapdf/test_writer.rb +2 -2
  228. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +515 -0
  229. data/test/hexapdf/type/acro_form/test_button_field.rb +276 -0
  230. data/test/hexapdf/type/acro_form/test_choice_field.rb +137 -0
  231. data/test/hexapdf/type/acro_form/test_field.rb +124 -6
  232. data/test/hexapdf/type/acro_form/test_form.rb +189 -22
  233. data/test/hexapdf/type/acro_form/test_text_field.rb +119 -0
  234. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +77 -0
  235. data/test/hexapdf/type/annotations/test_text.rb +1 -1
  236. data/test/hexapdf/type/annotations/test_widget.rb +199 -0
  237. data/test/hexapdf/type/test_annotation.rb +45 -0
  238. data/test/hexapdf/type/test_catalog.rb +18 -0
  239. data/test/hexapdf/type/test_font.rb +5 -0
  240. data/test/hexapdf/type/test_font_type1.rb +8 -0
  241. data/test/hexapdf/type/test_image.rb +7 -0
  242. data/test/hexapdf/type/test_page_tree_node.rb +20 -12
  243. data/test/hexapdf/type/test_resources.rb +20 -0
  244. data/test/hexapdf/type/test_trailer.rb +4 -0
  245. data/test/hexapdf/utils/test_bit_field.rb +13 -1
  246. data/test/test_helper.rb +1 -1
  247. metadata +37 -18
  248. data/lib/hexapdf/filter/dct_decode.rb +0 -60
@@ -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
@@ -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)
@@ -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
@@ -155,36 +155,44 @@ describe HexaPDF::Type::PageTreeNode do
155
155
  end
156
156
 
157
157
  it "deletes the correct page by index" do
158
- assert_equal(@pages[2], @root.delete_page(2))
158
+ @root.delete_page(2)
159
159
  assert_equal(2, @kid12.page_count)
160
160
  assert_equal(4, @kid1.page_count)
161
161
  assert_equal(7, @root.page_count)
162
- assert_nil(@pages[2][:Parent])
162
+ assert(@pages[2].null?)
163
163
 
164
- assert_equal(@pages[5], @root.delete_page(4))
164
+ @root.delete_page(4)
165
165
  assert_equal(6, @root.page_count)
166
- assert_nil(@pages[5][:Parent])
166
+ assert(@pages[5].null?)
167
167
  end
168
168
 
169
169
  it "deletes the given page" do
170
- assert_equal(@pages[2], @root.delete_page(@pages[2]))
171
- assert_equal(@pages[5], @root.delete_page(@pages[5]))
170
+ @root.delete_page(@pages[2])
171
+ assert(@pages[2].null?)
172
+ @root.delete_page(@pages[5])
173
+ assert(@pages[5].null?)
172
174
  end
173
175
 
174
176
  it "allows deleting a page from an intermediary node" do
175
- assert_equal(@pages[2], @kid1.delete_page(@pages[2]))
177
+ @kid1.delete_page(@pages[2])
176
178
  assert_equal(7, @root.page_count)
177
179
  end
178
180
 
179
181
  it "does nothing if the page index is not valid" do
180
- assert_nil(@root.delete_page(20))
181
- assert_nil(@root.delete_page(-20))
182
+ @root.delete_page(20)
183
+ @root.delete_page(-20)
182
184
  assert_equal(8, @root.page_count)
183
185
  end
184
186
 
185
- it "does nothing if the page is not in its parent's /Kids array" do
187
+ it "does nothing if the page has already been deleted" do
188
+ @root.delete_page(@pages[2])
189
+ @root.delete_page(@pages[2])
190
+ assert_equal(7, @root.page_count)
191
+ end
192
+
193
+ it "fails if the page is not in its parent's /Kids array" do
186
194
  @kid12[:Kids].delete_at(0)
187
- assert_nil(@root.delete_page(@pages[2]))
195
+ assert_raises(HexaPDF::Error) { @root.delete_page(@pages[2]) }
188
196
  assert_equal(8, @root.page_count)
189
197
  end
190
198
 
@@ -193,7 +201,7 @@ describe HexaPDF::Type::PageTreeNode do
193
201
  page = @doc.add({Type: :Page, Parent: pages})
194
202
  pages[:Kids] << page
195
203
 
196
- assert_nil(@root.delete_page(page))
204
+ @root.delete_page(page)
197
205
  assert_equal(8, @root.page_count)
198
206
  end
199
207
  end
@@ -25,6 +25,10 @@ describe HexaPDF::Type::Resources do
25
25
  assert_kind_of(HexaPDF::Content::ColorSpace::Universal, @res.color_space(:CSName))
26
26
  end
27
27
 
28
+ it "works for the Pattern color space" do
29
+ assert_kind_of(HexaPDF::Content::ColorSpace::Universal, @res.color_space(:Pattern))
30
+ end
31
+
28
32
  it "returns the universal color space for unknown color spaces, with resolved references" do
29
33
  data = @doc.add({Some: :data})
30
34
  @res[:ColorSpace] = {CSName: [:SomeUnknownColorSpace,
@@ -168,6 +172,22 @@ describe HexaPDF::Type::Resources do
168
172
  end
169
173
  end
170
174
 
175
+ describe "pattern" do
176
+ it "invokes the object_getter method" do
177
+ assert_method_invoked(@res, :object_getter, [:Pattern, :test]) do
178
+ @res.pattern(:test)
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "add_pattern" do
184
+ it "invokes the object_setter method" do
185
+ assert_method_invoked(@res, :object_setter, [:Pattern, 'P', :test]) do
186
+ @res.add_pattern(:test)
187
+ end
188
+ end
189
+ end
190
+
171
191
  describe "validation" do
172
192
  it "assigns the default value if ProcSet is not set" do
173
193
  @res.validate
@@ -9,12 +9,16 @@ describe HexaPDF::Type::Trailer do
9
9
  before do
10
10
  @doc = Object.new
11
11
  @doc.instance_variable_set(:@version, '1.2')
12
+
12
13
  def (@doc).version=(v); @version = v; end
14
+
13
15
  def (@doc).deref(obj); obj; end
16
+
14
17
  def (@doc).wrap(obj, *)
15
18
  (obj.kind_of?(Array) ? HexaPDF::PDFArray : HexaPDF::Dictionary).
16
19
  new(obj, oid: (obj.oid rescue 0))
17
20
  end
21
+
18
22
  root = HexaPDF::Dictionary.new({}, oid: 3)
19
23
  @obj = HexaPDF::Type::Trailer.new({Size: 10, Root: root}, document: @doc)
20
24
  end