hexapdf 0.32.2 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
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: []