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,370 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2023 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/dictionary'
38
+
39
+ module HexaPDF
40
+ module Type
41
+
42
+ # Represents an optional content group (OCG).
43
+ #
44
+ # An optional content group represents graphics that can be made visible or invisible
45
+ # dynamically by the PDF processor. These graphics may reside in any content stream and don't
46
+ # need to be consecutive with respect to the drawing order.
47
+ #
48
+ # Most PDF viewers call this feature "layers" since it is often used to show/hide parts of
49
+ # drawings or maps.
50
+ #
51
+ # == Intent and Usage
52
+ #
53
+ # An OCG may be assigned an intent (defaults to :View) and usage information. This allows one to
54
+ # specify in more detail how an OCG may be used (e.g. to only show the content when a certain
55
+ # zoom level is active).
56
+ #
57
+ # See: PDF2.0 s8.11.2
58
+ class OptionalContentGroup < Dictionary
59
+
60
+ # Represents an optional content group's usage dictionary which describes how the content
61
+ # controlled by the group should be used.
62
+ #
63
+ # See: PDF2.0 s8.11.4.4
64
+ class OptionalContentUsage < Dictionary
65
+
66
+ # The dictionary used as value for the /CreatorInfo key.
67
+ #
68
+ # See: PDF2.0 s8.11.4.4
69
+ class CreatorInfo < Dictionary
70
+ define_type :XXOCUsageCreatorInfo
71
+ define_field :Creator, type: String, required: true
72
+ define_field :Subtype, type: Symbol, required: true
73
+ end
74
+
75
+ # The dictionary used as value for the /Language key.
76
+ #
77
+ # See: PDF2.0 s8.11.4.4
78
+ class Language < Dictionary
79
+ define_type :XXOCUsageLanguage
80
+ define_field :Lang, type: String, required: true
81
+ define_field :Preferred, type: Symbol, default: :OFF, allowed_values: [:ON, :OFF]
82
+ end
83
+
84
+ # The dictionary used as value for the /Export key.
85
+ #
86
+ # See: PDF2.0 s8.11.4.4
87
+ class Export < Dictionary
88
+ define_type :XXOCUsageExport
89
+ define_field :ExportState, type: Symbol, required: true, allowed_values: [:ON, :OFF]
90
+ end
91
+
92
+ # The dictionary used as value for the /Zoom key.
93
+ #
94
+ # See: PDF2.0 s8.11.4.4
95
+ class Zoom < Dictionary
96
+ define_type :XXOCUsageZoom
97
+ define_field :min, type: Numeric, default: 0
98
+ define_field :max, type: Numeric
99
+ end
100
+
101
+ # The dictionary used as value for the /Print key.
102
+ #
103
+ # See: PDF2.0 s8.11.4.4
104
+ class Print < Dictionary
105
+ define_type :XXOCUsagePrint
106
+ define_field :Subtype, type: Symbol
107
+ define_field :PrintState, type: Symbol, allowed_values: [:ON, :OFF]
108
+ end
109
+
110
+ # The dictionary used as value for the /View key.
111
+ #
112
+ # See: PDF2.0 s8.11.4.4
113
+ class View < Dictionary
114
+ define_type :XXOCUsageView
115
+ define_field :ViewState, type: Symbol, required: true, allowed_values: [:ON, :OFF]
116
+ end
117
+
118
+ # The dictionary used as value for the /User key.
119
+ #
120
+ # See: PDF2.0 s8.11.4.4
121
+ class User < Dictionary
122
+ define_type :XXOCUsageUser
123
+ define_field :Type, type: Symbol, required: true, allowed_values: [:Ind, :Ttl, :Org]
124
+ define_field :Name, type: [String, PDFArray], required: true
125
+ end
126
+
127
+ # The dictionary used as value for the /PageElement key.
128
+ #
129
+ # See: PDF2.0 s8.11.4.4
130
+ class PageElement < Dictionary
131
+ define_type :XXOCUsagePageElement
132
+ define_field :Subtype, type: Symbol, required: true, allowed_values: [:HF, :FG, :BG, :L]
133
+ end
134
+
135
+ define_type :XXOCUsage
136
+
137
+ define_field :CreatorInfo, type: :XXOCUsageCreatorInfo
138
+ define_field :Language, type: :XXOCUsageLanguage
139
+ define_field :Export, type: :XXOCUsageExport
140
+ define_field :Zoom, type: :XXOCUsageZoom
141
+ define_field :Print, type: :XXOCUsagePrint
142
+ define_field :View, type: :XXOCUsageView
143
+ define_field :User, type: :XXOCUsageUser
144
+ define_field :PageElement, type: :XXOCUsagePageElement
145
+
146
+ end
147
+
148
+ define_type :OCG
149
+
150
+ define_field :Type, type: Symbol, required: true, default: type
151
+ define_field :Name, type: String, required: true
152
+ define_field :Intent, type: [Symbol, PDFArray], default: :View
153
+ define_field :Usage, type: :XXOCUsage
154
+
155
+ # Returns +true+ since optional content group dictionaries objects must always be indirect.
156
+ def must_be_indirect?
157
+ true
158
+ end
159
+
160
+ # :call-seq:
161
+ # ocg.name -> name
162
+ # ocg.name(value) -> value
163
+ #
164
+ # Returns the name of the OCG if no argument is given. Otherwise sets the name to the given
165
+ # value.
166
+ def name(value = nil)
167
+ if value
168
+ self[:Name] = value
169
+ else
170
+ self[:Name]
171
+ end
172
+ end
173
+
174
+ # Applies the given intent (:View, :Design or a custom intent) to the OCG.
175
+ def apply_intent(intent)
176
+ self[:Intent] = key?(:Intent) ? Array(self[:Intent]) : []
177
+ self[:Intent] << intent
178
+ end
179
+
180
+ # Returns +true+ if this OCG has an intent of :View.
181
+ def intent_view?
182
+ Array(self[:Intent]).include?(:View)
183
+ end
184
+
185
+ # Returns +true+ if this OCG has an intent of :Design.
186
+ def intent_design?
187
+ Array(self[:Intent]).include?(:Design)
188
+ end
189
+
190
+ # Returns +true+ if the OCG is set to on in the default configuration (see
191
+ # OptionalContentProperties#default_configuration).
192
+ def on?
193
+ document.optional_content.default_configuration.ocg_on?(self)
194
+ end
195
+
196
+ # Sets the state of the OCG to on in the default configuration (see
197
+ # OptionalContentProperties#default_configuration).
198
+ def on!
199
+ document.optional_content.default_configuration.ocg_state(self, :on)
200
+ end
201
+
202
+ # Sets the state of the OCG to off in the default configuration (see
203
+ # OptionalContentProperties#default_configuration).
204
+ def off!
205
+ document.optional_content.default_configuration.ocg_state(self, :off)
206
+ end
207
+
208
+ # Adds the OCG to the PDF processor's user interface in the default configuration (see
209
+ # OptionalContentProperties#default_configuration), either at the top-level or under the given
210
+ # hierarchical +path+ but always as the last item.
211
+ def add_to_ui(path: nil)
212
+ document.optional_content.default_configuration.add_ocg_to_ui(self, path: path)
213
+ end
214
+
215
+ # :call-seq:
216
+ # ocg.creator_info -> creator_info or nil
217
+ # ocg.creator_info(creator, subtype) -> creator_info
218
+ #
219
+ # Returns the creator info dictionary (see OptionalContentUsage::CreatorInfo) or +nil+ if no
220
+ # argument is given. Otherwise sets the creator info using the given values.
221
+ #
222
+ # The creator info dictionary is used to store application-specific data. The string +creator+
223
+ # specifies the application that created the group and the symbol +subtype+ defines the type
224
+ # of content controlled by the OCG (for example :Artwork for graphic design applications or
225
+ # :Technical for technical designs such as plans).
226
+ def creator_info(creator = nil, subtype = nil)
227
+ if creator && subtype
228
+ self[:Usage] ||= {}
229
+ self[:Usage][:CreatorInfo] = {Creator: creator, Subtype: subtype}
230
+ elsif creator || subtype
231
+ raise ArgumentError, "Missing argument, both creator and subtype are needed"
232
+ end
233
+ self[:Usage]&.[](:CreatorInfo)
234
+ end
235
+
236
+ # :call-seq:
237
+ # ocg.language -> language_info or nil
238
+ # ocg.language(lang, preferred: false) -> language_info
239
+ #
240
+ # Returns the language dictionary (see OptionalContentUsage::Language) or +nil+ if no argument
241
+ # is given. Otherwise sets the langauge using the given values.
242
+ #
243
+ # The language dictionary describes the language of the content controlled by the OCG. The
244
+ # string +lang+ needs to be a language tag as defined in BCP 47 (e.g. 'en' or 'de-AT'). If
245
+ # +preferred+ is +true+, this dictionary is preferred if there is only a partial match
246
+ def language(lang = nil, preferred: false)
247
+ if lang
248
+ self[:Usage] ||= {}
249
+ self[:Usage][:Language] = {Lang: lang, Preferred: (preferred ? :ON : :OFF)}
250
+ end
251
+ self[:Usage]&.[](:Language)
252
+ end
253
+
254
+ # :call-seq:
255
+ # ocg.export_state -> true or false
256
+ # ocg.export_state(state) -> state
257
+ #
258
+ # Returns the export state if no argument is given. Otherwise sets the export state using the
259
+ # given value.
260
+ #
261
+ # The export state indicates the recommended state of the content when the PDF document is
262
+ # saved to a format that does not support optional content (e.g. a raster image format). If
263
+ # +state+ is +true+, the content controlled by the OCG will be visible.
264
+ def export_state(state = nil)
265
+ if state
266
+ self[:Usage] ||= {}
267
+ self[:Usage][:Export] = {ExportState: (state ? :ON : :OFF)}
268
+ end
269
+ self[:Usage]&.[](:Export)&.[](:ExportState) == :ON
270
+ end
271
+
272
+ # :call-seq:
273
+ # ocg.view_state -> true or false
274
+ # ocg.view_state(state) -> state
275
+ #
276
+ # Returns the view state if no argument is given. Otherwise sets the view state using the
277
+ # given value.
278
+ #
279
+ # The view state indicates the state of the content when the PDF document is first opened. If
280
+ # +state+ is +true+, the content controlled by the OCG will be visible.
281
+ def view_state(state = nil)
282
+ if state
283
+ self[:Usage] ||= {}
284
+ self[:Usage][:View] = {ViewState: (state ? :ON : :OFF)}
285
+ end
286
+ self[:Usage]&.[](:View)&.[](:ViewState) == :ON
287
+ end
288
+
289
+ # :call-seq:
290
+ # ocg.print_state -> print_state or nil
291
+ # ocg.print_state(state, subtype: nil) -> print_state
292
+ #
293
+ # Returns the print state (see OptionalContentUsage::Print) or +nil+ if no argument is given.
294
+ # Otherwise sets the print state using the given values.
295
+ #
296
+ # The print state indicates the state of the content when the PDF document is printed. If
297
+ # +state+ is +true+, the content controlled by the OCG will be printed. The symbol +subtype+
298
+ # may optionally specify the kind of content controlled by the OCG (e.g. :Trapping or
299
+ # :Watermark).
300
+ def print_state(state = nil, subtype: nil)
301
+ if state
302
+ self[:Usage] ||= {}
303
+ self[:Usage][:Print] = {PrintState: (state ? :ON : :OFF), Subtype: subtype}
304
+ end
305
+ self[:Usage]&.[](:Print)
306
+ end
307
+
308
+ # :call-seq:
309
+ # ocg.zoom -> zoom_dict or nil
310
+ # ocg.zoom(min: nil, max: nil) -> zoom_dict
311
+ #
312
+ # Returns the zoom dictionary (see OptionalContentUsage::Zoom) or +nil+ if no argument is
313
+ # given. Otherwise sets the zoom range using the given values.
314
+ #
315
+ # The zoom range specifies the magnifications at which the content in the OCG is visible.
316
+ # Either +min+ or +max+ or both can be specified as magnification factors (i.e. 1.0 means
317
+ # viewing at 100%):
318
+ #
319
+ # * If +min+ is specified but +max+ isn't, the maximum possible magnification factor of the
320
+ # PDF processor is used for +max+.
321
+ #
322
+ # * If +max+ is specified but +min+ isn't, the default value of 0 for +min+ is used.
323
+ def zoom(min: nil, max: nil)
324
+ if min || max
325
+ self[:Usage] ||= {}
326
+ self[:Usage][:Zoom] = {min: min, max: max}
327
+ end
328
+ self[:Usage]&.[](:Zoom)
329
+ end
330
+
331
+ # :call-seq:
332
+ # ocg.intended_user -> user_dict or nil
333
+ # ocg.intended_user(type, name) -> user_dict
334
+ #
335
+ # Returns the user dictionary (see OptionalContentUsage::User) or +nil+ if no argument is
336
+ # given. Otherwise sets the user information using the given values.
337
+ #
338
+ # The information specifies one or more users for whom this OCG is primarily intended. The
339
+ # symbol +type+ can either be :Ind (individual), :Ttl (title or position) or :Org
340
+ # (organisation). The argument +name+ can either be a single name or an array of names.
341
+ def intended_user(type = nil, name = nil)
342
+ if type && name
343
+ self[:Usage] ||= {}
344
+ self[:Usage][:User] = {Type: type, Name: name}
345
+ end
346
+ self[:Usage]&.[](:User)
347
+ end
348
+
349
+ # :call-seq:
350
+ # ocg.page_element -> element_type or nil
351
+ # ocg.page_element(subtype) -> element_type
352
+ #
353
+ # Returns the page element type if no argument is given. Otherwise sets the page element type
354
+ # using the given value.
355
+ #
356
+ # When set, the page element declares that the OCG contains a pagination artificat. The symbol
357
+ # argument +subtype+ can either be :HF (header/footer), :FG (foreground image or graphics),
358
+ # :BG (background image or graphics), or :L (logo).
359
+ def page_element(subtype = nil)
360
+ if subtype
361
+ self[:Usage] ||= {}
362
+ self[:Usage][:PageElement] = {Subtype: subtype}
363
+ end
364
+ self[:Usage]&.[](:PageElement)&.[](:Subtype)
365
+ end
366
+
367
+ end
368
+
369
+ end
370
+ end
@@ -0,0 +1,63 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2023 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/dictionary'
38
+
39
+ module HexaPDF
40
+ module Type
41
+
42
+ # Represents an optional content membership dictionary.
43
+ #
44
+ # A membership dictionary allows more complex visibility policies, like:
45
+ #
46
+ # * Content that should be visible when a certain optional content group is off instead of on.
47
+ # * Content that should be visible when all of a number of OCGs are on.
48
+ #
49
+ # See: PDF2.0 s8.11.2.2
50
+ class OptionalContentMembership < Dictionary
51
+
52
+ define_type :OCMD
53
+
54
+ define_field :Type, type: Symbol, required: true, default: type
55
+ define_field :OCGs, type: [:OCG, PDFArray]
56
+ define_field :P, type: Symbol, default: :AnyOn,
57
+ allowed_values: [:AllOn, :AnyOn, :AnyOff, :AllOff]
58
+ define_field :VE, type: PDFArray
59
+
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,158 @@
1
+ # -*- encoding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # This file is part of HexaPDF.
5
+ #
6
+ # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
+ # Copyright (C) 2014-2023 Thomas Leitner
8
+ #
9
+ # HexaPDF is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU Affero General Public License version 3 as
11
+ # published by the Free Software Foundation with the addition of the
12
+ # following permission added to Section 15 as permitted in Section 7(a):
13
+ # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
+ # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
+ # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
+ #
17
+ # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
+ # License for more details.
21
+ #
22
+ # You should have received a copy of the GNU Affero General Public License
23
+ # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
+ #
25
+ # The interactive user interfaces in modified source and object code
26
+ # versions of HexaPDF must display Appropriate Legal Notices, as required
27
+ # under Section 5 of the GNU Affero General Public License version 3.
28
+ #
29
+ # In accordance with Section 7(b) of the GNU Affero General Public
30
+ # License, a covered work must retain the producer line in every PDF that
31
+ # is created or manipulated using HexaPDF.
32
+ #
33
+ # If the GNU Affero General Public License doesn't fit your need,
34
+ # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
+ #++
36
+
37
+ require 'hexapdf/dictionary'
38
+
39
+ module HexaPDF
40
+ module Type
41
+
42
+ # Represents an optional content properties dictionary.
43
+ #
44
+ # This dictionary is the value of the /OCProperties key in the document catalog and needs to
45
+ # exist for optional content to be usable by a PDF processor.
46
+ #
47
+ # In HexaPDF it provides the main entry point for working with optional content.
48
+ #
49
+ # See: PDF2.0 s8.11.4.2
50
+ class OptionalContentProperties < Dictionary
51
+
52
+ define_type :XXOCProperties
53
+
54
+ define_field :OCGs, type: PDFArray, default: [], required: true
55
+ define_field :D, type: :XXOCConfiguration, required: true
56
+ define_field :Configs, type: PDFArray
57
+
58
+ # :call-seq:
59
+ # optional_content.add_ocg(name) -> ocg
60
+ # optional_content.add_ocg(ocg) -> ocg
61
+ #
62
+ # Adds the given optional content group to the list of known OCGs and returns it. If a string
63
+ # is provided, an optional content group with that name is created before adding it.
64
+ #
65
+ # See: #ocg, OptionalContentGroup
66
+ def add_ocg(name_or_dict)
67
+ ocg = if name_or_dict.kind_of?(Dictionary)
68
+ name_or_dict
69
+ else
70
+ document.add({Type: :OCG, Name: name_or_dict})
71
+ end
72
+ self[:OCGs] << ocg unless self[:OCGs].include?(ocg)
73
+ ocg
74
+ end
75
+
76
+ # :call-seq:
77
+ # optional_content.ocg(name, create: true) -> ocg or +nil+
78
+ #
79
+ # Returns the first found optional content group with the given +name+.
80
+ #
81
+ # If no optional content group with the given +name+ exists but the optional argument +create+
82
+ # is +true+, a new OCG with the given +name+ is created and returned. Otherwise +nil+ is
83
+ # returned.
84
+ #
85
+ # See: #add_ocg
86
+ def ocg(name, create: true)
87
+ self[:OCGs].find {|ocg| ocg.name == name } || (create && add_ocg(name) || nil)
88
+ end
89
+
90
+ # Returns the list of known optional content group objects, with duplicates removed.
91
+ def ocgs
92
+ self[:OCGs].uniq.compact
93
+ end
94
+
95
+
96
+ OCMD_POLICY_MAPPING = {any_on: :AnyOn, AnyOn: :AnyOn, any_off: :AnyOff, # :nodoc:
97
+ AnyOff: :AnyOff, all_off: :AllOff, AllOff: :AllOff}
98
+
99
+ # Creates an optional content membership dictionary containing the given optional content
100
+ # group(s).
101
+ #
102
+ # The optional argument +policy+ specifies the visibility policy:
103
+ #
104
+ # :any_on/:AnyOn:: Content is visible if any of the OCGs are on.
105
+ # :any_off/:AnyOff:: Content is visible if any of the OCGs are off.
106
+ # :all_on/:AllOn:: Content is only visible if all OCGs are on.
107
+ # :all_off/:AllOff:: Content is only visible if all OCGs are off.
108
+ #
109
+ # See: OptionalContentMembership
110
+ def create_ocmd(ocgs, policy: :any_on)
111
+ policy = OCMD_POLICY_MAPPING.fetch(policy) do
112
+ raise ArgumentError, "Invalid OCMD policy #{policy} specified"
113
+ end
114
+ document.wrap({Type: :OCMD, OCGs: Array(ocgs), P: policy})
115
+ end
116
+
117
+ # :call-seq:
118
+ # optional_content.default_configuration -> config_dict
119
+ # optional_content.default_configuration(hash) -> config_dict
120
+ #
121
+ # Returns the default optional content configuration dictionary if no argument is given.
122
+ # Otherwise sets the the default optional content configuration to the given hash value.
123
+ #
124
+ # The default configuration defines the initial state of the optional content groups and how
125
+ # those states may be changed by a PDF processor.
126
+ #
127
+ # Example:
128
+ #
129
+ # optional_content.default_configuration(
130
+ # Name: 'My Configuration',
131
+ # OFF: [ocg1],
132
+ # Order: [ocg_all, [ocg1, ocg2, ocg3]]
133
+ # )
134
+ #
135
+ # See: OptionalContentConfiguration
136
+ def default_configuration(hash = nil)
137
+ if hash
138
+ self[:D] = hash
139
+ else
140
+ self[:D] ||= {Creator: 'HexaPDF'}
141
+ end
142
+ self[:D]
143
+ end
144
+
145
+ private
146
+
147
+ def perform_validation(&block) # :nodoc:
148
+ unless key?(:D)
149
+ yield('The OptionalContentProperties dictionary needs a default configuration', true)
150
+ self[:D] = {Creator: 'HexaPDF'}
151
+ end
152
+ super
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+ end
@@ -80,7 +80,7 @@ module HexaPDF
80
80
  # # Copying all the pages so that the references work.
81
81
  # doc.pages.each {|page| target.pages << target.import(page) }
82
82
  #
83
- # See: PDF1.7 s12.3.3
83
+ # See: PDF2.0 s12.3.3
84
84
  class Outline < Dictionary
85
85
 
86
86
  define_type :Outlines
@@ -60,7 +60,7 @@ module HexaPDF
60
60
  # Since many dictionary keys need to be kept up-to-date when manipulating the outline item tree,
61
61
  # it is not recommended to manually do this but to rely on the provided convenience methods.
62
62
  #
63
- # See: PDF1.7 s12.3.3
63
+ # See: PDF2.0 s12.3.3
64
64
  class OutlineItem < Dictionary
65
65
 
66
66
  extend Utils::BitField