hexapdf 0.32.2 → 0.34.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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -1
  3. data/README.md +9 -0
  4. data/examples/002-graphics.rb +15 -17
  5. data/examples/003-arcs.rb +9 -9
  6. data/examples/009-text_layouter_alignment.rb +1 -1
  7. data/examples/010-text_layouter_inline_boxes.rb +2 -2
  8. data/examples/011-text_layouter_line_wrapping.rb +1 -1
  9. data/examples/012-text_layouter_styling.rb +7 -7
  10. data/examples/013-text_layouter_shapes.rb +1 -1
  11. data/examples/014-text_in_polygon.rb +1 -1
  12. data/examples/015-boxes.rb +8 -7
  13. data/examples/016-frame_automatic_box_placement.rb +2 -2
  14. data/examples/017-frame_text_flow.rb +2 -1
  15. data/examples/018-composer.rb +1 -1
  16. data/examples/020-column_box.rb +2 -1
  17. data/examples/025-table_box.rb +46 -0
  18. data/examples/026-optional_content.rb +55 -0
  19. data/examples/027-composer_optional_content.rb +83 -0
  20. data/lib/hexapdf/cli/command.rb +12 -3
  21. data/lib/hexapdf/cli/fonts.rb +1 -1
  22. data/lib/hexapdf/cli/form.rb +5 -5
  23. data/lib/hexapdf/cli/inspect.rb +5 -7
  24. data/lib/hexapdf/composer.rb +106 -53
  25. data/lib/hexapdf/configuration.rb +65 -40
  26. data/lib/hexapdf/content/canvas.rb +445 -267
  27. data/lib/hexapdf/content/color_space.rb +72 -25
  28. data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
  31. data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
  32. data/lib/hexapdf/content/graphic_object.rb +6 -7
  33. data/lib/hexapdf/content/graphics_state.rb +54 -45
  34. data/lib/hexapdf/content/operator.rb +54 -54
  35. data/lib/hexapdf/content/parser.rb +2 -2
  36. data/lib/hexapdf/content/processor.rb +15 -15
  37. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  38. data/lib/hexapdf/content.rb +5 -0
  39. data/lib/hexapdf/dictionary.rb +7 -5
  40. data/lib/hexapdf/dictionary_fields.rb +43 -16
  41. data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
  42. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  43. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
  44. data/lib/hexapdf/digital_signature/signature.rb +6 -6
  45. data/lib/hexapdf/digital_signature/signatures.rb +13 -12
  46. data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
  47. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
  48. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
  49. data/lib/hexapdf/digital_signature/signing.rb +4 -0
  50. data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
  51. data/lib/hexapdf/digital_signature.rb +7 -2
  52. data/lib/hexapdf/document/destinations.rb +12 -11
  53. data/lib/hexapdf/document/files.rb +1 -1
  54. data/lib/hexapdf/document/fonts.rb +1 -1
  55. data/lib/hexapdf/document/layout.rb +170 -39
  56. data/lib/hexapdf/document/pages.rb +4 -3
  57. data/lib/hexapdf/document.rb +96 -55
  58. data/lib/hexapdf/encryption/aes.rb +5 -5
  59. data/lib/hexapdf/encryption/arc4.rb +1 -1
  60. data/lib/hexapdf/encryption/fast_aes.rb +2 -2
  61. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  62. data/lib/hexapdf/encryption/identity.rb +1 -1
  63. data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
  64. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  65. data/lib/hexapdf/encryption/security_handler.rb +31 -24
  66. data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
  67. data/lib/hexapdf/encryption.rb +7 -2
  68. data/lib/hexapdf/error.rb +18 -0
  69. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  70. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  71. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  72. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  73. data/lib/hexapdf/filter/pass_through.rb +1 -1
  74. data/lib/hexapdf/filter/predictor.rb +1 -1
  75. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  76. data/lib/hexapdf/filter.rb +55 -6
  77. data/lib/hexapdf/font/cmap/parser.rb +2 -2
  78. data/lib/hexapdf/font/cmap.rb +1 -1
  79. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  80. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  81. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
  82. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  83. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  84. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
  85. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  86. data/lib/hexapdf/font/invalid_glyph.rb +3 -0
  87. data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
  88. data/lib/hexapdf/font/type1_wrapper.rb +19 -4
  89. data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
  90. data/lib/hexapdf/font_loader/from_file.rb +5 -5
  91. data/lib/hexapdf/font_loader/standard14.rb +3 -3
  92. data/lib/hexapdf/font_loader.rb +3 -0
  93. data/lib/hexapdf/image_loader/jpeg.rb +2 -2
  94. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  95. data/lib/hexapdf/image_loader/png.rb +2 -2
  96. data/lib/hexapdf/image_loader.rb +1 -1
  97. data/lib/hexapdf/importer.rb +13 -0
  98. data/lib/hexapdf/layout/box.rb +32 -5
  99. data/lib/hexapdf/layout/box_fitter.rb +2 -2
  100. data/lib/hexapdf/layout/column_box.rb +20 -5
  101. data/lib/hexapdf/layout/frame.rb +53 -18
  102. data/lib/hexapdf/layout/image_box.rb +5 -0
  103. data/lib/hexapdf/layout/inline_box.rb +21 -9
  104. data/lib/hexapdf/layout/list_box.rb +50 -20
  105. data/lib/hexapdf/layout/page_style.rb +6 -5
  106. data/lib/hexapdf/layout/style.rb +64 -9
  107. data/lib/hexapdf/layout/table_box.rb +684 -0
  108. data/lib/hexapdf/layout/text_box.rb +12 -3
  109. data/lib/hexapdf/layout/text_fragment.rb +29 -3
  110. data/lib/hexapdf/layout/text_layouter.rb +32 -8
  111. data/lib/hexapdf/layout.rb +1 -0
  112. data/lib/hexapdf/name_tree_node.rb +1 -1
  113. data/lib/hexapdf/number_tree_node.rb +1 -1
  114. data/lib/hexapdf/object.rb +18 -7
  115. data/lib/hexapdf/parser.rb +7 -7
  116. data/lib/hexapdf/pdf_array.rb +1 -1
  117. data/lib/hexapdf/rectangle.rb +1 -1
  118. data/lib/hexapdf/reference.rb +1 -1
  119. data/lib/hexapdf/revision.rb +1 -1
  120. data/lib/hexapdf/revisions.rb +3 -3
  121. data/lib/hexapdf/serializer.rb +15 -15
  122. data/lib/hexapdf/stream.rb +5 -4
  123. data/lib/hexapdf/tokenizer.rb +14 -14
  124. data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
  125. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  126. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  127. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  128. data/lib/hexapdf/type/acro_form/form.rb +1 -1
  129. data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
  130. data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
  131. data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
  132. data/lib/hexapdf/type/acro_form.rb +1 -1
  133. data/lib/hexapdf/type/action.rb +1 -1
  134. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  135. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  136. data/lib/hexapdf/type/actions/launch.rb +1 -1
  137. data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
  138. data/lib/hexapdf/type/actions/uri.rb +1 -1
  139. data/lib/hexapdf/type/actions.rb +2 -1
  140. data/lib/hexapdf/type/annotation.rb +3 -3
  141. data/lib/hexapdf/type/annotations/link.rb +1 -1
  142. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  143. data/lib/hexapdf/type/annotations/text.rb +2 -3
  144. data/lib/hexapdf/type/annotations/widget.rb +2 -2
  145. data/lib/hexapdf/type/annotations.rb +1 -1
  146. data/lib/hexapdf/type/catalog.rb +11 -2
  147. data/lib/hexapdf/type/cid_font.rb +18 -4
  148. data/lib/hexapdf/type/embedded_file.rb +1 -1
  149. data/lib/hexapdf/type/file_specification.rb +2 -2
  150. data/lib/hexapdf/type/font_descriptor.rb +1 -1
  151. data/lib/hexapdf/type/font_simple.rb +2 -2
  152. data/lib/hexapdf/type/font_type0.rb +3 -3
  153. data/lib/hexapdf/type/font_type3.rb +1 -1
  154. data/lib/hexapdf/type/form.rb +76 -6
  155. data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
  156. data/lib/hexapdf/type/icon_fit.rb +1 -1
  157. data/lib/hexapdf/type/image.rb +1 -1
  158. data/lib/hexapdf/type/info.rb +1 -1
  159. data/lib/hexapdf/type/mark_information.rb +1 -1
  160. data/lib/hexapdf/type/names.rb +2 -2
  161. data/lib/hexapdf/type/object_stream.rb +2 -1
  162. data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
  163. data/lib/hexapdf/type/optional_content_group.rb +370 -0
  164. data/lib/hexapdf/type/optional_content_membership.rb +63 -0
  165. data/lib/hexapdf/type/optional_content_properties.rb +158 -0
  166. data/lib/hexapdf/type/outline.rb +1 -1
  167. data/lib/hexapdf/type/outline_item.rb +1 -1
  168. data/lib/hexapdf/type/page.rb +46 -21
  169. data/lib/hexapdf/type/page_label.rb +5 -9
  170. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  171. data/lib/hexapdf/type/resources.rb +1 -1
  172. data/lib/hexapdf/type/trailer.rb +2 -2
  173. data/lib/hexapdf/type/viewer_preferences.rb +1 -1
  174. data/lib/hexapdf/type/xref_stream.rb +2 -2
  175. data/lib/hexapdf/type.rb +4 -0
  176. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
  177. data/lib/hexapdf/version.rb +1 -1
  178. data/lib/hexapdf/writer.rb +4 -4
  179. data/lib/hexapdf/xref_section.rb +2 -2
  180. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
  181. data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
  182. data/test/hexapdf/content/test_canvas.rb +49 -1
  183. data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
  184. data/test/hexapdf/document/test_files.rb +2 -2
  185. data/test/hexapdf/document/test_layout.rb +105 -2
  186. data/test/hexapdf/document/test_pages.rb +6 -6
  187. data/test/hexapdf/encryption/test_security_handler.rb +12 -11
  188. data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
  189. data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
  190. data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
  191. data/test/hexapdf/layout/test_box.rb +14 -5
  192. data/test/hexapdf/layout/test_column_box.rb +65 -21
  193. data/test/hexapdf/layout/test_frame.rb +27 -15
  194. data/test/hexapdf/layout/test_image_box.rb +4 -0
  195. data/test/hexapdf/layout/test_inline_box.rb +17 -3
  196. data/test/hexapdf/layout/test_list_box.rb +84 -33
  197. data/test/hexapdf/layout/test_page_style.rb +3 -2
  198. data/test/hexapdf/layout/test_style.rb +60 -0
  199. data/test/hexapdf/layout/test_table_box.rb +728 -0
  200. data/test/hexapdf/layout/test_text_box.rb +26 -0
  201. data/test/hexapdf/layout/test_text_fragment.rb +33 -0
  202. data/test/hexapdf/layout/test_text_layouter.rb +36 -5
  203. data/test/hexapdf/test_composer.rb +10 -0
  204. data/test/hexapdf/test_dictionary.rb +10 -0
  205. data/test/hexapdf/test_dictionary_fields.rb +4 -1
  206. data/test/hexapdf/test_document.rb +5 -0
  207. data/test/hexapdf/test_filter.rb +8 -0
  208. data/test/hexapdf/test_importer.rb +9 -0
  209. data/test/hexapdf/test_object.rb +16 -5
  210. data/test/hexapdf/test_stream.rb +7 -0
  211. data/test/hexapdf/test_writer.rb +3 -3
  212. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
  213. data/test/hexapdf/type/acro_form/test_form.rb +4 -3
  214. data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
  215. data/test/hexapdf/type/test_catalog.rb +11 -0
  216. data/test/hexapdf/type/test_form.rb +119 -0
  217. data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
  218. data/test/hexapdf/type/test_optional_content_group.rb +158 -0
  219. data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
  220. data/test/hexapdf/type/test_page.rb +20 -6
  221. metadata +28 -8
@@ -0,0 +1,112 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/type/optional_content_configuration'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Type::OptionalContentConfiguration do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @oc_config = @doc.optional_content.default_configuration
11
+ @ocg = @doc.optional_content.ocg('Test')
12
+ end
13
+
14
+ describe "ocg_state" do
15
+ it "defaults to the base state if nothing specific is set" do
16
+ assert_equal(:on, @oc_config.ocg_state(@ocg))
17
+ @oc_config[:BaseState] = :OFF
18
+ assert_equal(:off, @oc_config.ocg_state(@ocg))
19
+ @oc_config[:BaseState] = :Unchanged
20
+ assert_nil(@oc_config.ocg_state(@ocg))
21
+ end
22
+
23
+ it "returns :on if the OCG is in the /ON key" do
24
+ @oc_config[:ON] = [@ocg]
25
+ [:ON, :OFF, :Unchanged].each do |base_state|
26
+ @oc_config[:BaseState] = base_state
27
+ assert_equal(:on, @oc_config.ocg_state(@ocg))
28
+ end
29
+ end
30
+
31
+ it "returns :off if the OCG is in the /OFF key" do
32
+ @oc_config[:OFF] = [@ocg]
33
+ [:ON, :OFF, :Unchanged].each do |base_state|
34
+ @oc_config[:BaseState] = base_state
35
+ assert_equal(:off, @oc_config.ocg_state(@ocg))
36
+ end
37
+ end
38
+
39
+ it "adds the OCG to the respective dictionary key if a state is given" do
40
+ [[:ON, :OFF], [:OFF, :ON]].each do |state, other_state|
41
+ @oc_config[other_state] = [@ocg]
42
+ @oc_config.ocg_state(@ocg, state.downcase)
43
+ assert_equal([], @oc_config[other_state].value)
44
+ assert_equal([@ocg], @oc_config[state].value)
45
+ @oc_config.ocg_state(@ocg, state)
46
+ assert_equal([@ocg], @oc_config[state].value)
47
+ end
48
+ end
49
+
50
+ it "fails if an invalid state is given" do
51
+ assert_raises(ArgumentError) { @oc_config.ocg_state(@ocg, :unknwo) }
52
+ end
53
+ end
54
+
55
+ it "returns whether a given ocg is on" do
56
+ assert(@oc_config.ocg_on?(@ocg))
57
+ @oc_config[:OFF] = [@ocg]
58
+ refute(@oc_config.ocg_on?(@ocg))
59
+ end
60
+
61
+ describe "add_ocg_to_ui" do
62
+ it "adds the ocg to the top level" do
63
+ @oc_config.add_ocg_to_ui(@ocg)
64
+ @oc_config.add_ocg_to_ui(@ocg)
65
+ assert_equal([@ocg, @ocg], @oc_config[:Order].value)
66
+ end
67
+
68
+ it "adds the ocg under an existing label" do
69
+ @oc_config[:Order] = [:ocg1, ['Test'], :ocg2]
70
+ @oc_config.add_ocg_to_ui(@ocg, path: 'Test')
71
+ @oc_config.add_ocg_to_ui(@ocg, path: 'Test')
72
+ assert_equal([:ocg1, ['Test', @ocg, @ocg], :ocg2], @oc_config[:Order].value)
73
+ end
74
+
75
+ it "adds the ocg under a new label" do
76
+ @oc_config[:Order] = []
77
+ @oc_config.add_ocg_to_ui(@ocg, path: 'Test')
78
+ @oc_config.add_ocg_to_ui(@ocg, path: 'Test')
79
+ @oc_config.add_ocg_to_ui(@ocg, path: 'Test2')
80
+ assert_equal([['Test', @ocg, @ocg], ['Test2', @ocg]], @oc_config[:Order].value)
81
+ end
82
+
83
+ it "adds the ocg under an existing ocg" do
84
+ @oc_config[:Order] = [:ocg1, :ocg2]
85
+ @oc_config.add_ocg_to_ui(@ocg, path: :ocg2)
86
+ @oc_config.add_ocg_to_ui(@ocg, path: :ocg2)
87
+ assert_equal([:ocg1, :ocg2, [@ocg, @ocg]], @oc_config[:Order].value)
88
+ end
89
+
90
+ it "adds the ocg under a new ocg" do
91
+ @oc_config[:Order] = []
92
+ @oc_config.add_ocg_to_ui(@ocg, path: :ocg1)
93
+ @oc_config.add_ocg_to_ui(@ocg, path: :ocg1)
94
+ @oc_config.add_ocg_to_ui(@ocg, path: :ocg2)
95
+ assert_equal([:ocg1, [@ocg, @ocg], :ocg2, [@ocg]], @oc_config[:Order].value)
96
+ end
97
+
98
+ it "adds the ocg under an existing multi-level path" do
99
+ @oc_config[:Order] = [:ocg1, ['Test', :ocg2, [:ocg4, ['Test2', :ocg5]]], :ocg3]
100
+ @oc_config.add_ocg_to_ui(@ocg, path: ['Test', :ocg2, 'Test2'])
101
+ assert_equal([:ocg1, ['Test', :ocg2, [:ocg4, ['Test2', :ocg5, @ocg]]], :ocg3],
102
+ @oc_config[:Order].value)
103
+ end
104
+
105
+ it "adds the ocg under a new multi-level path" do
106
+ @oc_config[:Order] = [:ocg1, ['Test', :ocg2]]
107
+ @oc_config.add_ocg_to_ui(@ocg, path: ['Test2', :ocg3, 'Test3'])
108
+ assert_equal([:ocg1, ['Test', :ocg2], ['Test2', :ocg3, [['Test3', @ocg]]]],
109
+ @oc_config[:Order].value)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,158 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/type/optional_content_group'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Type::OptionalContentGroup do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @ocg = @doc.add({Type: :OCG, Name: 'OCG'})
11
+ end
12
+
13
+ it "resolves all referenced type classes" do
14
+ hash = {
15
+ Usage: {
16
+ CreatorInfo: {},
17
+ Language: {},
18
+ Export: {},
19
+ Zoom: {},
20
+ Print: {},
21
+ View: {},
22
+ User: {},
23
+ PageElement: {}
24
+ }
25
+ }
26
+ ocg = @doc.add(hash, type: :OCG)
27
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup, ocg)
28
+ ocu = ocg[:Usage]
29
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage, ocu)
30
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::CreatorInfo,
31
+ ocu[:CreatorInfo])
32
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Language,
33
+ ocu[:Language])
34
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Export,
35
+ ocu[:Export])
36
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Zoom,
37
+ ocu[:Zoom])
38
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Print,
39
+ ocu[:Print])
40
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::View,
41
+ ocu[:View])
42
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::User,
43
+ ocu[:User])
44
+ assert_kind_of(HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::PageElement,
45
+ ocu[:PageElement])
46
+ end
47
+
48
+ it "must always be an indirect object" do
49
+ assert(@ocg.must_be_indirect?)
50
+ end
51
+
52
+ it "returns the name" do
53
+ assert_equal('OCG', @ocg.name)
54
+ @ocg.name('Other')
55
+ assert_equal('Other', @ocg.name)
56
+ end
57
+
58
+ describe "intent" do
59
+ it "can be ask whether the intent is :View" do
60
+ assert(@ocg.intent_view?)
61
+ @ocg[:Intent] = :Design
62
+ refute(@ocg.intent_view?)
63
+ end
64
+
65
+ it "can be ask whether the intent is :Design" do
66
+ refute(@ocg.intent_design?)
67
+ @ocg[:Intent] = :Design
68
+ assert(@ocg.intent_design?)
69
+ end
70
+
71
+ it "can apply one or more intents" do
72
+ @ocg.apply_intent(:View)
73
+ @ocg.apply_intent(:Design)
74
+ assert(@ocg.intent_view?)
75
+ assert(@ocg.intent_design?)
76
+ end
77
+ end
78
+
79
+ describe "managing the OCG's default configuration" do
80
+ it "can be asked whether it is on by default" do
81
+ assert(@ocg.on?)
82
+ @doc.optional_content.default_configuration[:OFF] = [@ocg]
83
+ refute(@ocg.on?)
84
+ end
85
+
86
+ it "can set its default state to on" do
87
+ @doc.optional_content.default_configuration[:OFF] = [@ocg]
88
+ @ocg.on!
89
+ assert(@ocg.on?)
90
+ end
91
+
92
+ it "can set its default state to off" do
93
+ @ocg.off!
94
+ refute(@ocg.on?)
95
+ end
96
+
97
+ it "can add itself to the UI" do
98
+ @ocg.add_to_ui(path: 'Test')
99
+ assert_equal([['Test', @ocg]], @doc.optional_content.default_configuration[:Order].value)
100
+ end
101
+ end
102
+
103
+ it "can set and return the creator info usage entry" do
104
+ refute(@ocg.creator_info)
105
+ dict = @ocg.creator_info("HexaPDF", :Technical)
106
+ assert_equal({Creator: "HexaPDF", Subtype: :Technical}, dict.value)
107
+ assert_raises(ArgumentError) { @ocg.creator_info("HexaPDF") }
108
+ end
109
+
110
+ it "can set and return the language usage entry" do
111
+ refute(@ocg.language)
112
+ dict = @ocg.language('de')
113
+ assert_equal({Lang: "de", Preferred: :OFF}, dict.value)
114
+ @ocg.language('de', preferred: true)
115
+ assert_equal({Lang: "de", Preferred: :ON}, @ocg.language.value)
116
+ end
117
+
118
+ it "can set and return the export state usage entry" do
119
+ refute(@ocg.export_state)
120
+ assert(@ocg.export_state(true))
121
+ assert(@ocg.export_state)
122
+ end
123
+
124
+ it "can set and return the view state usage entry" do
125
+ refute(@ocg.view_state)
126
+ assert(@ocg.view_state(true))
127
+ assert(@ocg.view_state)
128
+ end
129
+
130
+ it "can set and return the print state usage entry" do
131
+ refute(@ocg.print_state)
132
+ dict = @ocg.print_state(true)
133
+ assert_equal({PrintState: :ON, Subtype: nil}, dict.value)
134
+ @ocg.print_state(true, subtype: :Watermark)
135
+ assert_equal({PrintState: :ON, Subtype: :Watermark}, @ocg.print_state.value)
136
+ end
137
+
138
+ it "can set and return the zoom usage entry" do
139
+ refute(@ocg.zoom)
140
+ dict = @ocg.zoom(min: 2.0)
141
+ assert_equal({min: 2.0, max: nil}, dict.value)
142
+ assert_equal({min: nil, max: 3.0}, @ocg.zoom(max: 3.0).value)
143
+ assert_equal({min: 1.0, max: 3.0}, @ocg.zoom(min: 1.0, max: 3.0).value)
144
+ end
145
+
146
+ it "can set and return the intended user usage entry" do
147
+ refute(@ocg.intended_user)
148
+ dict = @ocg.intended_user(:Ind, 'Me')
149
+ assert_equal({Type: :Ind, Name: "Me"}, dict.value)
150
+ end
151
+
152
+ it "can set and return the page element usage entry" do
153
+ refute(@ocg.page_element)
154
+ assert_equal(:HF, @ocg.page_element(:HF))
155
+ @ocg.page_element(:L)
156
+ assert_equal(:L, @ocg.page_element)
157
+ end
158
+ end
@@ -0,0 +1,109 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/type/optional_content_properties'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Type::OptionalContentProperties do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @oc = @doc.optional_content
11
+ end
12
+
13
+ describe "add_ocg" do
14
+ it "adds a given OCG object" do
15
+ ocg = @doc.add({Type: :OCG, Name: 'test'})
16
+ assert_same(ocg, @oc.add_ocg(ocg))
17
+ assert_equal([ocg], @oc[:OCGs])
18
+ end
19
+
20
+ it "doesn't add an OCG if it has been added before" do
21
+ ocg = @doc.add({Type: :OCG, Name: 'test'})
22
+ @oc.add_ocg(ocg)
23
+ @oc.add_ocg(ocg)
24
+ assert_equal([ocg], @oc[:OCGs])
25
+ end
26
+
27
+ it "creates a new OCG object with the given name and adds it" do
28
+ ocg = @oc.add_ocg('Test')
29
+ assert_equal([ocg], @oc[:OCGs])
30
+ end
31
+ end
32
+
33
+ describe "ocg" do
34
+ it "returns the first OCG with the given name, regardless of the create argument" do
35
+ ocg1 = @oc.add_ocg('Test')
36
+ _ocg2 = @oc.add_ocg('Test')
37
+ assert_same(ocg1, @oc.ocg('Test', create: false))
38
+ assert_same(ocg1, @oc.ocg('Test', create: true))
39
+ end
40
+
41
+ it "returns nil if no OCG with the given name exists and create is false" do
42
+ assert_nil(@oc.ocg('Other', create: false))
43
+ @oc.add_ocg('Test')
44
+ assert_nil(@oc.ocg('Other', create: false))
45
+ end
46
+
47
+ it "creates an OCG with the given name if none is found and create is true" do
48
+ ocg = @oc.ocg('Test')
49
+ assert_same(ocg, @oc.ocg('Test'))
50
+ assert_equal([ocg], @oc[:OCGs])
51
+ end
52
+ end
53
+
54
+ describe "ocgs" do
55
+ it "returns the list of the known optional content groups, with duplicates removed" do
56
+ ocg1 = @oc.add_ocg(@oc.add_ocg('Test'))
57
+ @oc[:OCGs] << nil
58
+ ocg2 = @oc.add_ocg('Test')
59
+ ocg3 = @oc.add_ocg('Other')
60
+ assert_equal([ocg1, ocg2, ocg3], @oc.ocgs)
61
+ end
62
+ end
63
+
64
+ describe "create_ocmd" do
65
+ it "creates the optional content membership dictionary for the given OCGs" do
66
+ ocmd = @oc.create_ocmd(@oc.ocg('Test'))
67
+ assert_equal({Type: :OCMD, OCGs: [@oc.ocg('Test')], P: :AnyOn}, ocmd.value)
68
+
69
+ ocmd = @oc.create_ocmd([@oc.ocg('Test'), @oc.ocg('Test2')], policy: :any_off)
70
+ assert_equal({Type: :OCMD, OCGs: [@oc.ocg('Test'), @oc.ocg('Test2')], P: :AnyOff}, ocmd.value)
71
+ end
72
+
73
+ it "fails if the policy is invalid" do
74
+ error = assert_raises(ArgumentError) { @oc.create_ocmd(:ocg, policy: :unknown) }
75
+ assert_match(/Invalid OCMD.*unknown/, error.message)
76
+ end
77
+ end
78
+
79
+ describe "default_configuration" do
80
+ it "returns an existing dictionary" do
81
+ dict = @oc.default_configuration
82
+ assert_same(@oc[:D], dict)
83
+ assert_kind_of(HexaPDF::Type::OptionalContentConfiguration, dict)
84
+ end
85
+
86
+ it "sets and returns a default configuration dictionary if none is set" do
87
+ @oc.delete(:D)
88
+ assert_equal({Creator: 'HexaPDF'}, @oc.default_configuration.value)
89
+ end
90
+
91
+ it "sets the default configuration dictionary to the given value" do
92
+ d_before = @oc[:D]
93
+ d_new = @oc.default_configuration(Creator: 'Test')
94
+ refute_same(d_before, d_new)
95
+ assert_same(@oc[:D], d_new)
96
+ assert_equal({Creator: 'Test'}, d_new.value)
97
+ end
98
+ end
99
+
100
+ describe "perform_validation" do
101
+ it "creates the /D entry if it is not set" do
102
+ @oc.delete(:D)
103
+ refute(@oc.validate(auto_correct: false))
104
+ refute(@oc.key?(:D))
105
+ assert(@oc.validate(auto_correct: true))
106
+ assert_equal({Creator: 'HexaPDF'}, @oc[:D].value)
107
+ end
108
+ end
109
+ end
@@ -557,7 +557,7 @@ describe HexaPDF::Type::Page do
557
557
  @page = @doc.pages.add
558
558
  @appearance = @doc.add({Type: :XObject, Subtype: :Form, BBox: [-10, -5, 50, 20]}, stream: "")
559
559
  @annot1 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [100, 100, 160, 125], AP: {N: @appearance}})
560
- @annot2 = @doc.add({Rect: [10, 10, 70, 35], AP: {N: @appearance}})
560
+ @annot2 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [10, 10, 70, 35], AP: {N: @appearance}})
561
561
  @page[:Annots] = [@annot1, @annot2]
562
562
  @canvas = @page.canvas(type: :overlay)
563
563
  end
@@ -591,6 +591,18 @@ describe HexaPDF::Type::Page do
591
591
  assert(@annot2.null?)
592
592
  end
593
593
 
594
+ it "gracefully handles invalid /Annot key values" do
595
+ @page[:Annots] << nil << @doc.add({}, stream: '')
596
+ result = @page.flatten_annotations
597
+ assert(result.empty?)
598
+ assert(@annot1.null?)
599
+ assert(@annot2.null?)
600
+
601
+ @page[:Annots] = @doc.add({}, stream: '')
602
+ result = @page.flatten_annotations
603
+ assert(result.empty?)
604
+ end
605
+
594
606
  it "only deletes the widget annotation of a form field even if it is embedded in the field object" do
595
607
  form = @doc.acro_form(create: true)
596
608
  field = form.create_text_field('test')
@@ -631,7 +643,7 @@ describe HexaPDF::Type::Page do
631
643
  [:restore_graphics_state]])
632
644
  end
633
645
 
634
- it "ignores annotations without appearane stream" do
646
+ it "ignores annotations without appearance stream" do
635
647
  @annot1.delete(:AP)
636
648
  result = @page.flatten_annotations
637
649
  assert_equal([@annot1], result)
@@ -694,24 +706,26 @@ describe HexaPDF::Type::Page do
694
706
  @appearance[:Matrix] = [0.707106, 0.707106, -0.707106, 0.707106, 10, 30]
695
707
  @page.flatten_annotations
696
708
  assert_operators(@canvas.contents,
697
- [:concatenate_matrix, [0.998269, 0.0, 0.0, 0.415946, 111.21318, 80.60659]],
709
+ [:concatenate_matrix, [0.998269, 0.0, 0.0, 0.415946, 111.193776, 91.933396]],
698
710
  range: 1)
699
711
  end
700
712
 
701
713
  it "scales the appearance to fit into the annotations's rectangle" do
702
714
  @annot1[:Rect] = [100, 100, 130, 150]
703
715
  @page.flatten_annotations
704
- assert_operators(@canvas.contents, [:concatenate_matrix, [0.5, 0, 0, 2, 110, 105]], range: 1)
716
+ assert_operators(@canvas.contents, [:concatenate_matrix, [0.5, 0, 0, 2, 105, 110]], range: 1)
705
717
  end
706
718
  end
707
719
 
708
720
  it "yields each annotation" do
709
721
  page = @doc.pages.add
710
722
  annot1 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [100, 100, 160, 125]})
711
- annot2 = @doc.add({Rect: [10, 10, 70, 35]})
712
- page[:Annots] = [annot1, nil, annot2]
723
+ annot2 = @doc.add({Subtype: :Unknown, Rect: [10, 10, 70, 35]})
724
+ not_an_annot = @doc.add({}, stream: '')
725
+ page[:Annots] = [not_an_annot, annot1, nil, annot2]
713
726
 
714
727
  annotations = page.each_annotation.to_a
728
+ assert_equal(2, annotations.size)
715
729
  assert_equal([100, 100, 160, 125], annotations[0][:Rect])
716
730
  assert_equal(:Annot, annotations[0].type)
717
731
  assert_equal(:Annot, annotations[1].type)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.2
4
+ version: 0.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-06 00:00:00.000000000 Z
11
+ date: 2023-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -36,14 +36,20 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.3'
39
+ version: '0.4'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.4.1
40
43
  type: :runtime
41
44
  prerelease: false
42
45
  version_requirements: !ruby/object:Gem::Requirement
43
46
  requirements:
44
47
  - - "~>"
45
48
  - !ruby/object:Gem::Version
46
- version: '0.3'
49
+ version: '0.4'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.4.1
47
53
  - !ruby/object:Gem::Dependency
48
54
  name: openssl
49
55
  requirement: !ruby/object:Gem::Requirement
@@ -292,6 +298,9 @@ files:
292
298
  - examples/022-outline.rb
293
299
  - examples/023-images.rb
294
300
  - examples/024-digital-signatures.rb
301
+ - examples/025-table_box.rb
302
+ - examples/026-optional_content.rb
303
+ - examples/027-composer_optional_content.rb
295
304
  - examples/emoji-smile.png
296
305
  - examples/emoji-wink.png
297
306
  - examples/machupicchu.jpg
@@ -430,6 +439,7 @@ files:
430
439
  - lib/hexapdf/layout/numeric_refinements.rb
431
440
  - lib/hexapdf/layout/page_style.rb
432
441
  - lib/hexapdf/layout/style.rb
442
+ - lib/hexapdf/layout/table_box.rb
433
443
  - lib/hexapdf/layout/text_box.rb
434
444
  - lib/hexapdf/layout/text_fragment.rb
435
445
  - lib/hexapdf/layout/text_layouter.rb
@@ -466,6 +476,7 @@ files:
466
476
  - lib/hexapdf/type/actions/go_to.rb
467
477
  - lib/hexapdf/type/actions/go_to_r.rb
468
478
  - lib/hexapdf/type/actions/launch.rb
479
+ - lib/hexapdf/type/actions/set_ocg_state.rb
469
480
  - lib/hexapdf/type/actions/uri.rb
470
481
  - lib/hexapdf/type/annotation.rb
471
482
  - lib/hexapdf/type/annotations.rb
@@ -492,6 +503,10 @@ files:
492
503
  - lib/hexapdf/type/mark_information.rb
493
504
  - lib/hexapdf/type/names.rb
494
505
  - lib/hexapdf/type/object_stream.rb
506
+ - lib/hexapdf/type/optional_content_configuration.rb
507
+ - lib/hexapdf/type/optional_content_group.rb
508
+ - lib/hexapdf/type/optional_content_membership.rb
509
+ - lib/hexapdf/type/optional_content_properties.rb
495
510
  - lib/hexapdf/type/outline.rb
496
511
  - lib/hexapdf/type/outline_item.rb
497
512
  - lib/hexapdf/type/page.rb
@@ -682,6 +697,7 @@ files:
682
697
  - test/hexapdf/layout/test_list_box.rb
683
698
  - test/hexapdf/layout/test_page_style.rb
684
699
  - test/hexapdf/layout/test_style.rb
700
+ - test/hexapdf/layout/test_table_box.rb
685
701
  - test/hexapdf/layout/test_text_box.rb
686
702
  - test/hexapdf/layout/test_text_fragment.rb
687
703
  - test/hexapdf/layout/test_text_layouter.rb
@@ -719,6 +735,7 @@ files:
719
735
  - test/hexapdf/type/acro_form/test_text_field.rb
720
736
  - test/hexapdf/type/acro_form/test_variable_text_field.rb
721
737
  - test/hexapdf/type/actions/test_launch.rb
738
+ - test/hexapdf/type/actions/test_set_ocg_state.rb
722
739
  - test/hexapdf/type/actions/test_uri.rb
723
740
  - test/hexapdf/type/annotations/test_markup_annotation.rb
724
741
  - test/hexapdf/type/annotations/test_text.rb
@@ -739,6 +756,9 @@ files:
739
756
  - test/hexapdf/type/test_info.rb
740
757
  - test/hexapdf/type/test_names.rb
741
758
  - test/hexapdf/type/test_object_stream.rb
759
+ - test/hexapdf/type/test_optional_content_configuration.rb
760
+ - test/hexapdf/type/test_optional_content_group.rb
761
+ - test/hexapdf/type/test_optional_content_properties.rb
742
762
  - test/hexapdf/type/test_outline.rb
743
763
  - test/hexapdf/type/test_outline_item.rb
744
764
  - test/hexapdf/type/test_page.rb
@@ -760,7 +780,7 @@ licenses:
760
780
  - AGPL-3.0
761
781
  - Nonstandard
762
782
  metadata: {}
763
- post_install_message:
783
+ post_install_message:
764
784
  rdoc_options: []
765
785
  require_paths:
766
786
  - lib
@@ -775,8 +795,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
775
795
  - !ruby/object:Gem::Version
776
796
  version: '0'
777
797
  requirements: []
778
- rubygems_version: 3.4.1
779
- signing_key:
798
+ rubygems_version: 3.4.10
799
+ signing_key:
780
800
  specification_version: 4
781
801
  summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
782
802
  test_files: []