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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -1
- data/README.md +9 -0
- data/examples/002-graphics.rb +15 -17
- data/examples/003-arcs.rb +9 -9
- data/examples/009-text_layouter_alignment.rb +1 -1
- data/examples/010-text_layouter_inline_boxes.rb +2 -2
- data/examples/011-text_layouter_line_wrapping.rb +1 -1
- data/examples/012-text_layouter_styling.rb +7 -7
- data/examples/013-text_layouter_shapes.rb +1 -1
- data/examples/014-text_in_polygon.rb +1 -1
- data/examples/015-boxes.rb +8 -7
- data/examples/016-frame_automatic_box_placement.rb +2 -2
- data/examples/017-frame_text_flow.rb +2 -1
- data/examples/018-composer.rb +1 -1
- data/examples/020-column_box.rb +2 -1
- data/examples/025-table_box.rb +46 -0
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +12 -3
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +5 -7
- data/lib/hexapdf/composer.rb +106 -53
- data/lib/hexapdf/configuration.rb +65 -40
- data/lib/hexapdf/content/canvas.rb +445 -267
- data/lib/hexapdf/content/color_space.rb +72 -25
- data/lib/hexapdf/content/graphic_object/arc.rb +57 -24
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -23
- data/lib/hexapdf/content/graphic_object/geom2d.rb +47 -6
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +58 -36
- data/lib/hexapdf/content/graphic_object.rb +6 -7
- data/lib/hexapdf/content/graphics_state.rb +54 -45
- data/lib/hexapdf/content/operator.rb +54 -54
- data/lib/hexapdf/content/parser.rb +2 -2
- data/lib/hexapdf/content/processor.rb +15 -15
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +5 -0
- data/lib/hexapdf/dictionary.rb +7 -5
- data/lib/hexapdf/dictionary_fields.rb +43 -16
- data/lib/hexapdf/digital_signature/cms_handler.rb +2 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +2 -3
- data/lib/hexapdf/digital_signature/signature.rb +6 -6
- data/lib/hexapdf/digital_signature/signatures.rb +13 -12
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +14 -5
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -4
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +4 -4
- data/lib/hexapdf/digital_signature/signing.rb +4 -0
- data/lib/hexapdf/digital_signature/verification_result.rb +3 -4
- data/lib/hexapdf/digital_signature.rb +7 -2
- data/lib/hexapdf/document/destinations.rb +12 -11
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/layout.rb +170 -39
- data/lib/hexapdf/document/pages.rb +4 -3
- data/lib/hexapdf/document.rb +96 -55
- data/lib/hexapdf/encryption/aes.rb +5 -5
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +11 -21
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +31 -24
- data/lib/hexapdf/encryption/standard_security_handler.rb +45 -36
- data/lib/hexapdf/encryption.rb +7 -2
- data/lib/hexapdf/error.rb +18 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +55 -6
- data/lib/hexapdf/font/cmap/parser.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +3 -3
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +3 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +17 -4
- data/lib/hexapdf/font/type1_wrapper.rb +19 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +5 -2
- data/lib/hexapdf/font_loader/from_file.rb +5 -5
- data/lib/hexapdf/font_loader/standard14.rb +3 -3
- data/lib/hexapdf/font_loader.rb +3 -0
- data/lib/hexapdf/image_loader/jpeg.rb +2 -2
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +13 -0
- data/lib/hexapdf/layout/box.rb +32 -5
- data/lib/hexapdf/layout/box_fitter.rb +2 -2
- data/lib/hexapdf/layout/column_box.rb +20 -5
- data/lib/hexapdf/layout/frame.rb +53 -18
- data/lib/hexapdf/layout/image_box.rb +5 -0
- data/lib/hexapdf/layout/inline_box.rb +21 -9
- data/lib/hexapdf/layout/list_box.rb +50 -20
- data/lib/hexapdf/layout/page_style.rb +6 -5
- data/lib/hexapdf/layout/style.rb +64 -9
- data/lib/hexapdf/layout/table_box.rb +684 -0
- data/lib/hexapdf/layout/text_box.rb +12 -3
- data/lib/hexapdf/layout/text_fragment.rb +29 -3
- data/lib/hexapdf/layout/text_layouter.rb +32 -8
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +18 -7
- data/lib/hexapdf/parser.rb +7 -7
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -3
- data/lib/hexapdf/serializer.rb +15 -15
- data/lib/hexapdf/stream.rb +5 -4
- data/lib/hexapdf/tokenizer.rb +14 -14
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +22 -22
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +2 -1
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +2 -3
- data/lib/hexapdf/type/annotations/widget.rb +2 -2
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +11 -2
- data/lib/hexapdf/type/cid_font.rb +18 -4
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +3 -3
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +76 -6
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +46 -21
- data/lib/hexapdf/type/page_label.rb +5 -9
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +2 -2
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +4 -4
- data/lib/hexapdf/xref_section.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +11 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +7 -0
- data/test/hexapdf/content/test_canvas.rb +49 -1
- data/test/hexapdf/digital_signature/test_signatures.rb +22 -0
- data/test/hexapdf/document/test_files.rb +2 -2
- data/test/hexapdf/document/test_layout.rb +105 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/encryption/test_security_handler.rb +12 -11
- data/test/hexapdf/encryption/test_standard_security_handler.rb +35 -23
- data/test/hexapdf/font/test_true_type_wrapper.rb +18 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +15 -1
- data/test/hexapdf/layout/test_box.rb +14 -5
- data/test/hexapdf/layout/test_column_box.rb +65 -21
- data/test/hexapdf/layout/test_frame.rb +27 -15
- data/test/hexapdf/layout/test_image_box.rb +4 -0
- data/test/hexapdf/layout/test_inline_box.rb +17 -3
- data/test/hexapdf/layout/test_list_box.rb +84 -33
- data/test/hexapdf/layout/test_page_style.rb +3 -2
- data/test/hexapdf/layout/test_style.rb +60 -0
- data/test/hexapdf/layout/test_table_box.rb +728 -0
- data/test/hexapdf/layout/test_text_box.rb +26 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +36 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_dictionary_fields.rb +4 -1
- data/test/hexapdf/test_document.rb +5 -0
- data/test/hexapdf/test_filter.rb +8 -0
- data/test/hexapdf/test_importer.rb +9 -0
- data/test/hexapdf/test_object.rb +16 -5
- data/test/hexapdf/test_stream.rb +7 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +13 -5
- data/test/hexapdf/type/acro_form/test_form.rb +4 -3
- data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +20 -6
- 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
|
data/lib/hexapdf/type/outline.rb
CHANGED
|
@@ -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:
|
|
63
|
+
# See: PDF2.0 s12.3.3
|
|
64
64
|
class OutlineItem < Dictionary
|
|
65
65
|
|
|
66
66
|
extend Utils::BitField
|