hexapdf 0.33.0 → 0.34.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -1
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +7 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +2 -4
- data/lib/hexapdf/composer.rb +2 -1
- data/lib/hexapdf/configuration.rb +21 -1
- data/lib/hexapdf/content/canvas.rb +52 -0
- data/lib/hexapdf/content/operator.rb +2 -0
- data/lib/hexapdf/dictionary.rb +1 -0
- data/lib/hexapdf/dictionary_fields.rb +1 -2
- data/lib/hexapdf/digital_signature/verification_result.rb +1 -2
- data/lib/hexapdf/document/layout.rb +3 -0
- data/lib/hexapdf/document/pages.rb +1 -1
- data/lib/hexapdf/document.rb +7 -0
- data/lib/hexapdf/encryption/ruby_aes.rb +10 -20
- data/lib/hexapdf/layout/box.rb +23 -3
- data/lib/hexapdf/layout/column_box.rb +2 -1
- data/lib/hexapdf/layout/frame.rb +23 -6
- data/lib/hexapdf/layout/inline_box.rb +20 -9
- data/lib/hexapdf/layout/list_box.rb +34 -20
- data/lib/hexapdf/layout/page_style.rb +2 -1
- data/lib/hexapdf/layout/style.rb +46 -6
- data/lib/hexapdf/layout/table_box.rb +9 -7
- data/lib/hexapdf/layout/text_box.rb +9 -2
- data/lib/hexapdf/layout/text_fragment.rb +28 -2
- data/lib/hexapdf/layout/text_layouter.rb +21 -5
- data/lib/hexapdf/stream.rb +1 -2
- data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions.rb +1 -0
- data/lib/hexapdf/type/annotations/text.rb +1 -2
- data/lib/hexapdf/type/catalog.rb +10 -1
- data/lib/hexapdf/type/cid_font.rb +15 -1
- data/lib/hexapdf/type/form.rb +75 -5
- 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/page.rb +27 -11
- data/lib/hexapdf/type/page_label.rb +4 -8
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +0 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +49 -0
- data/test/hexapdf/document/test_layout.rb +7 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/layout/test_box.rb +13 -4
- data/test/hexapdf/layout/test_frame.rb +13 -1
- data/test/hexapdf/layout/test_inline_box.rb +17 -8
- data/test/hexapdf/layout/test_list_box.rb +48 -31
- data/test/hexapdf/layout/test_style.rb +10 -0
- data/test/hexapdf/layout/test_table_box.rb +32 -26
- data/test/hexapdf/layout/test_text_box.rb +8 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +32 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_document.rb +4 -0
- data/test/hexapdf/test_writer.rb +3 -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 +2 -2
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00aaef323df26b1a17c6e4ee39787c92037e8ae887713b28c3674962fcb6ac26
|
4
|
+
data.tar.gz: a46ecda2514bc5c044d115370f66402fc22ad3f8c8dfa1741e057b6849a6b952
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a15e468bdc852fd4c16286ba0e4829fcd3fb3be64aa48c429246216fbfe1c856cb8e155bfefa83c47b99246e7161f20a1de54769aa222dbcdfdb25f8be777643
|
7
|
+
data.tar.gz: d75048ff9d77601d93893cceb0c73673410695e3ea381e17d27de8c92d7de3d29d2ed467e44df6362dd28d3ee374a8e5710e5f1323393ee2e4104cdcecef1112
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
## 0.34.0 - 2023-10-22
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for optional content groups (layers)
|
6
|
+
* Support for reference XObjects
|
7
|
+
* Basic support for group XObjects
|
8
|
+
* [HexaPDF::Layout::Style#fill_horizontal] for allowing a text fragment to fill
|
9
|
+
the remaining space of line
|
10
|
+
* [HexaPDF::Layout::TextFragment#text] and [HexaPDF::Layout::TextBox#text] for
|
11
|
+
retrieving the text represented by the stored items
|
12
|
+
* [HexaPDF::Content::Canvas#pos] for retrieving untransformed positions
|
13
|
+
* [HexaPDF::Type::CIDFont::CIDSystemInfo] type class
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
* [HexaPDF::Composer#draw_box] to return the last drawn box
|
18
|
+
* [HexaPDF::Layout::Style::LinkLayer] to support arbitrary actions
|
19
|
+
* [HexaPDF::Layout::Frame::new] (and adapted other layout classes) to accept a
|
20
|
+
context argument (a page or Form XObject instance)
|
21
|
+
* [HexaPDF::Layout::ListBox] to use its 'fill_color' style property for the item
|
22
|
+
marker color
|
23
|
+
* [HexaPDF::Layout::Frame::FitResult#draw] to use optional content groups for
|
24
|
+
debug output
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
|
28
|
+
* [HexaPDF::Document::Pages#add_labelling_range] to add a correct entry for
|
29
|
+
the default range starting at page 1
|
30
|
+
* [HexaPDF::Type::Page#flatten_annotations] to correctly handle scaled
|
31
|
+
appearances
|
32
|
+
* Using an unknown style name in [HexaPDF:Document::Layout] method by providing
|
33
|
+
a useful error message
|
34
|
+
* [HexaPDF::Layout::Box::new] to ensure that the properties attribute is always
|
35
|
+
a hash
|
36
|
+
* [HexaPDF::Layout::ListBox] to work correctly if the marker height is larger
|
37
|
+
than the item content height
|
38
|
+
* [HexaPDF::Dictionary] setting default values on wrong classes in certain
|
39
|
+
situations
|
40
|
+
|
41
|
+
|
1
42
|
## 0.33.0 - 2023-08-02
|
2
43
|
|
3
44
|
### Added
|
@@ -39,7 +80,7 @@
|
|
39
80
|
|
40
81
|
### Fixed
|
41
82
|
|
42
|
-
**Breaking change**: [HexaPDF::Object::make_direct] now needs the document
|
83
|
+
* **Breaking change**: [HexaPDF::Object::make_direct] now needs the document
|
43
84
|
instance as second argument to correctly resolve references
|
44
85
|
* [HexaPDF::Layout::ColumnBox], [HexaPDF::Layout::ListBox] and
|
45
86
|
[HexaPDF::Layout::ImageBox] to correctly respond to `#empty?`
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# # Optional Content (a.k.a. Layers)
|
2
|
+
#
|
3
|
+
# This example shows how to create and assign optional content groups (OCGs) to
|
4
|
+
# parts of the content of a page.
|
5
|
+
#
|
6
|
+
# Four OCGs are created: Squares, Black, Blue, and Orange. The Squares one is
|
7
|
+
# applied to everything, the others to the respectively colored squares.
|
8
|
+
#
|
9
|
+
# When viewed in a compatible viewer, the "Optional Content" or "Layers" panel
|
10
|
+
# can be used to switch the layers on and off, resulting in the respective
|
11
|
+
# squares appearing or disappearing. Initially, the blue one is not shown, only
|
12
|
+
# the black and orange ones.
|
13
|
+
#
|
14
|
+
# Additionally, if supported by a viewer and if the visibility hasn't been
|
15
|
+
# manually changed, the OCGs for the squares are also configured to only be
|
16
|
+
# visible at certain zoom levels. For example, the black one is only visible up
|
17
|
+
# to a zoom level of 100%.
|
18
|
+
#
|
19
|
+
# Usage:
|
20
|
+
# : `ruby optional_content.rb`
|
21
|
+
#
|
22
|
+
require 'hexapdf'
|
23
|
+
|
24
|
+
doc = HexaPDF::Document.new
|
25
|
+
|
26
|
+
ocg = doc.optional_content.ocg('Squares')
|
27
|
+
ocg1 = doc.optional_content.ocg('Black')
|
28
|
+
ocg1.zoom(max: 1)
|
29
|
+
ocg1.add_to_ui(path: ocg)
|
30
|
+
ocg2 = doc.optional_content.ocg('Blue')
|
31
|
+
ocg2.zoom(min: 1, max: 2)
|
32
|
+
ocg2.add_to_ui(path: ocg)
|
33
|
+
ocg2.off!
|
34
|
+
ocg3 = doc.optional_content.ocg('Orange')
|
35
|
+
ocg3.zoom(min: 2, max: 20)
|
36
|
+
ocg3.add_to_ui(path: ocg)
|
37
|
+
|
38
|
+
canvas = doc.pages.add([0, 0, 200, 200]).canvas
|
39
|
+
canvas.optional_content(ocg) do
|
40
|
+
canvas.optional_content(ocg1) do
|
41
|
+
canvas.fill_color('black').rectangle(20, 80, 100, 100).fill
|
42
|
+
end
|
43
|
+
canvas.optional_content(ocg2) do
|
44
|
+
canvas.fill_color('hp-blue').rectangle(50, 50, 100, 100).fill
|
45
|
+
end
|
46
|
+
canvas.optional_content(ocg3) do
|
47
|
+
canvas.fill_color('hp-orange').rectangle(80, 20, 100, 100).fill
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
doc.optional_content.default_configuration[:AS] = [
|
52
|
+
{Event: :View, Category: [:Zoom], OCGs: [ocg1, ocg2, ocg3]}
|
53
|
+
]
|
54
|
+
|
55
|
+
doc.write('optional_content.pdf')
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# # Composer - Optional Content
|
2
|
+
#
|
3
|
+
# This example shows how to use the optional content feature to create a quiz
|
4
|
+
# where the answers can be individually shown and hidden. There is also a link
|
5
|
+
# after the questions to toggle all answers.
|
6
|
+
#
|
7
|
+
# Note: To provide the "All answers" layer switch functionality we need to make
|
8
|
+
# use of optional content membership dictionaries. However, this PDF feature is
|
9
|
+
# not supported by all PDF viewers. To enable the "All answers" switch in this
|
10
|
+
# example, use `a1m`, `a2m`, and `a3m` instead of `a1`, `a2`, and `a3` when
|
11
|
+
# defining the optional content for a box.
|
12
|
+
#
|
13
|
+
# Usage:
|
14
|
+
# : `ruby composer_optional_content.rb`
|
15
|
+
#
|
16
|
+
require 'hexapdf'
|
17
|
+
|
18
|
+
HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
|
19
|
+
composer.style(:question, font_size: 16, margin: [0, 0, 16], fill_color: 'hp-blue')
|
20
|
+
composer.style(:answer, font: 'ZapfDingbats', fill_color: "green")
|
21
|
+
|
22
|
+
all = composer.document.optional_content.ocg('All answers')
|
23
|
+
a1 = composer.document.optional_content.ocg('Answer 1')
|
24
|
+
a1m = composer.document.optional_content.create_ocmd([a1, all], policy: :any_on)
|
25
|
+
a2 = composer.document.optional_content.ocg('Answer 2')
|
26
|
+
a2m = composer.document.optional_content.create_ocmd([a2, all], policy: :any_on)
|
27
|
+
a3 = composer.document.optional_content.ocg('Answer 3')
|
28
|
+
a3m = composer.document.optional_content.create_ocmd([a3, all], policy: :any_on)
|
29
|
+
|
30
|
+
composer.text('The Great Ruby Quiz', align: :center, margin: [0, 0, 24],
|
31
|
+
font: ['Helvetica', variant: :bold], font_size: 24)
|
32
|
+
|
33
|
+
composer.list(item_type: :decimal, item_spacing: 32, style: :question) do |listing|
|
34
|
+
listing.multiple do |item|
|
35
|
+
item.text('Who created Ruby?', style: :question)
|
36
|
+
item.column(columns: 3, gaps: 5) do |cols|
|
37
|
+
cols.list(item_type: :decimal) do |answers|
|
38
|
+
answers.text('Guido van Rossum')
|
39
|
+
answers.multiple do |answer|
|
40
|
+
answer.text('Yukihiro “Matz” Matsumoto', position: :float)
|
41
|
+
answer.text("\u{a0}\u{a0}4", style: :answer,
|
42
|
+
properties: {'optional_content' => a1})
|
43
|
+
end
|
44
|
+
answers.text('Rob Pike')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
listing.multiple do |item|
|
50
|
+
item.text('When was Ruby created?', style: :question)
|
51
|
+
item.column(columns: 3, gaps: 5) do |cols|
|
52
|
+
cols.list(item_type: :decimal) do |answers|
|
53
|
+
answers.text('1991')
|
54
|
+
answers.text('1992')
|
55
|
+
answers.multiple do |answer|
|
56
|
+
answer.text('1993', position: :float)
|
57
|
+
answer.text("\u{a0}\u{a0}4", style: :answer,
|
58
|
+
properties: {'optional_content' => a2})
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
listing.multiple do |item|
|
65
|
+
item.text('What is the best PDF library for Ruby?', style: :question)
|
66
|
+
answer = composer.document.layout.text('There are several PDF libraries for ' \
|
67
|
+
'Ruby but the best is HexaPDF! :)',
|
68
|
+
width: 400,
|
69
|
+
properties: {'optional_content' => a3})
|
70
|
+
item.formatted_text([{box: answer}], border: {width: [0, 0, 1]})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
action = composer.document.wrap({Type: :Action, S: :SetOCGState})
|
75
|
+
action.add_state_change(:toggle, [a1, a2, a3])
|
76
|
+
composer.text("Click to toggle answers", border: {width: 1, color: "red"},
|
77
|
+
position_hint: :right, padding: 2, overlays: [[:link, action: action]])
|
78
|
+
|
79
|
+
composer.document.optional_content.default_configuration(
|
80
|
+
BaseState: :OFF,
|
81
|
+
Order: [all, a1, a2, a3],
|
82
|
+
)
|
83
|
+
end
|
data/lib/hexapdf/cli/command.rb
CHANGED
@@ -53,9 +53,15 @@ module HexaPDF
|
|
53
53
|
module Extensions #:nodoc:
|
54
54
|
def help_banner #:nodoc:
|
55
55
|
"hexapdf #{HexaPDF::VERSION} - Versatile PDF Manipulation Tool\n" \
|
56
|
-
"Copyright (c) 2014-
|
56
|
+
"Copyright (c) 2014-2023 Thomas Leitner; licensed under the AGPLv3\n\n" \
|
57
57
|
"#{format(usage, indent: 7)}\n\n"
|
58
58
|
end
|
59
|
+
|
60
|
+
def help #:nodoc:
|
61
|
+
super << format("See https://hexapdf.gettalong.org/documentation/hexapdf.1.html " \
|
62
|
+
"for the full manual page with examples.", indent: 0)
|
63
|
+
end
|
64
|
+
|
59
65
|
end
|
60
66
|
|
61
67
|
include Extensions
|
data/lib/hexapdf/cli/fonts.rb
CHANGED
@@ -127,7 +127,7 @@ module HexaPDF
|
|
127
127
|
page.resources[:Font]&.each(&font_proc)
|
128
128
|
page.resources[:XObject]&.each do |_, xobj|
|
129
129
|
next unless xobj[:Subtype] == :Form
|
130
|
-
xobj.
|
130
|
+
xobj.resources[:Font]&.each(&font_proc)
|
131
131
|
end
|
132
132
|
page.each_annotation do |annotation|
|
133
133
|
appearance = annotation.appearance
|
data/lib/hexapdf/cli/inspect.rb
CHANGED
@@ -93,11 +93,9 @@ module HexaPDF
|
|
93
93
|
|
94
94
|
private
|
95
95
|
|
96
|
-
# :nodoc:
|
97
|
-
COMMAND_LIST = %w[object recursive stream raw-stream xref catalog trailer pages
|
96
|
+
COMMAND_LIST = %w[object recursive stream raw-stream xref catalog trailer pages # :nodoc:
|
98
97
|
page-count search quit help]
|
99
|
-
# :nodoc:
|
100
|
-
RELINE_COMPLETION_PROC = proc do |s|
|
98
|
+
RELINE_COMPLETION_PROC = proc do |s| # :nodoc:
|
101
99
|
if s.empty?
|
102
100
|
COMMAND_DESCRIPTIONS.map {|cmd, desc| cmd.ljust(35) << desc }
|
103
101
|
else
|
data/lib/hexapdf/composer.rb
CHANGED
@@ -401,7 +401,7 @@ module HexaPDF
|
|
401
401
|
@document.layout.box_creation_method?(name) || super
|
402
402
|
end
|
403
403
|
|
404
|
-
# Draws the given HexaPDF::Layout::Box.
|
404
|
+
# Draws the given HexaPDF::Layout::Box and returns the last drawn box.
|
405
405
|
#
|
406
406
|
# The box is drawn into the current frame if possible. If it doesn't fit, the box is split. If
|
407
407
|
# it still doesn't fit, a new region of the frame is determined and then the process starts
|
@@ -433,6 +433,7 @@ module HexaPDF
|
|
433
433
|
end
|
434
434
|
end
|
435
435
|
end
|
436
|
+
box
|
436
437
|
end
|
437
438
|
|
438
439
|
# Creates a stamp (Form XObject) which can be used like an image multiple times on a single page
|
@@ -268,7 +268,8 @@ module HexaPDF
|
|
268
268
|
#
|
269
269
|
# The value needs to be an object that responds to \#call(character, font_wrapper) where
|
270
270
|
# +character+ is the Unicode character for the missing glyph and returns a substitute glyph to
|
271
|
-
# be used instead.
|
271
|
+
# be used instead. This substitute glyph needs to be from the same font, i.e. it needs to be
|
272
|
+
# created through the provided +font_wrapper+ instance.
|
272
273
|
#
|
273
274
|
# The +font_wrapper+ argument is the used font wrapper object, e.g.
|
274
275
|
# HexaPDF::Font::TrueTypeWrapper. To access the HexaPDF::Document instance from which this hook
|
@@ -609,6 +610,23 @@ module HexaPDF
|
|
609
610
|
XXOutlineItem: 'HexaPDF::Type::OutlineItem',
|
610
611
|
PageLabel: 'HexaPDF::Type::PageLabel',
|
611
612
|
XXMarkInformation: 'HexaPDF::Type::MarkInformation',
|
613
|
+
OCG: 'HexaPDF::Type::OptionalContentGroup',
|
614
|
+
OCMD: 'HexaPDF::Type::OptionalContentMembership',
|
615
|
+
XXOCUsage: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage',
|
616
|
+
XXOCUsageCreatorInfo: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::CreatorInfo',
|
617
|
+
XXOCUsageLanguage: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Language',
|
618
|
+
XXOCUsageExport: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Export',
|
619
|
+
XXOCUsageZoom: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Zoom',
|
620
|
+
XXOCUsagePrint: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::Print',
|
621
|
+
XXOCUsageView: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::View',
|
622
|
+
XXOCUsageUser: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::User',
|
623
|
+
XXOCUsagePageElement: 'HexaPDF::Type::OptionalContentGroup::OptionalContentUsage::PageElement',
|
624
|
+
XXOCProperties: 'HexaPDF::Type::OptionalContentProperties',
|
625
|
+
XXOCConfiguration: 'HexaPDF::Type::OptionalContentConfiguration',
|
626
|
+
XXOCUsageApplication: 'HexaPDF::Type::OptionalContentConfiguration::UsageApplication',
|
627
|
+
XXReference: 'HexaPDF::Type::Form::Reference',
|
628
|
+
XXCIDSystemInfo: 'HexaPDF::Type::CIDFont::CIDSystemInfo',
|
629
|
+
Group: 'HexaPDF::Type::Form::Group',
|
612
630
|
},
|
613
631
|
'object.subtype_map' => {
|
614
632
|
nil => {
|
@@ -623,6 +641,7 @@ module HexaPDF
|
|
623
641
|
GoToR: 'HexaPDF::Type::Actions::GoToR',
|
624
642
|
Launch: 'HexaPDF::Type::Actions::Launch',
|
625
643
|
URI: 'HexaPDF::Type::Actions::URI',
|
644
|
+
SetOCGState: 'HexaPDF::Type::Actions::SetOCGState',
|
626
645
|
Text: 'HexaPDF::Type::Annotations::Text',
|
627
646
|
Link: 'HexaPDF::Type::Annotations::Link',
|
628
647
|
Widget: 'HexaPDF::Type::Annotations::Widget',
|
@@ -644,6 +663,7 @@ module HexaPDF
|
|
644
663
|
GoToR: 'HexaPDF::Type::Actions::GoToR',
|
645
664
|
Launch: 'HexaPDF::Type::Actions::Launch',
|
646
665
|
URI: 'HexaPDF::Type::Actions::URI',
|
666
|
+
SetOCGState: 'HexaPDF::Type::Actions::SetOCGState',
|
647
667
|
},
|
648
668
|
Annot: {
|
649
669
|
Text: 'HexaPDF::Type::Annotations::Text',
|
@@ -317,6 +317,14 @@ module HexaPDF
|
|
317
317
|
@context.resources
|
318
318
|
end
|
319
319
|
|
320
|
+
# Returns the position (x,y) transformed by the current transformation matrix.
|
321
|
+
#
|
322
|
+
# The resulting position should be interpreted in terms of the coordinate system of the
|
323
|
+
# context object (e.g. the page or Form XObject).
|
324
|
+
def pos(x, y)
|
325
|
+
graphics_state.ctm.evaluate(x, y)
|
326
|
+
end
|
327
|
+
|
320
328
|
# :call-seq:
|
321
329
|
# canvas.save_graphics_state => canvas
|
322
330
|
# canvas.save_graphics_state { block } => canvas
|
@@ -2483,6 +2491,50 @@ module HexaPDF
|
|
2483
2491
|
self
|
2484
2492
|
end
|
2485
2493
|
|
2494
|
+
# :call-seq:
|
2495
|
+
# canvas.optional_content(ocg, &block) -> canvas
|
2496
|
+
# canvas.optional_content(name, use_existing_ocg: true, &block) -> canvas
|
2497
|
+
#
|
2498
|
+
# Inserts an optional content sequence. Returns +self+.
|
2499
|
+
#
|
2500
|
+
# An optional content sequence marks part of the content stream as belonging to the given
|
2501
|
+
# optional content group. See HexaPDF::Type::OptionalContentProperties for details.
|
2502
|
+
#
|
2503
|
+
# If the first argument is already an optional content group dictionary, it is used.
|
2504
|
+
# Otherwise, the first argument needs to be the name of the optional content group. In that
|
2505
|
+
# case, the +use_existing_ocg+ specifies whether the first found optional content group with
|
2506
|
+
# that name should be used or whether a new OCG should always be created.
|
2507
|
+
#
|
2508
|
+
# If invoked without a block, a corresponding call to #end_optional_content must be done.
|
2509
|
+
# Otherwise the optional content sequence automatically ends when the block is finished.
|
2510
|
+
#
|
2511
|
+
# Examples:
|
2512
|
+
#
|
2513
|
+
# canvas.optional_content('Hints')
|
2514
|
+
# # Other instructions
|
2515
|
+
# canvas.end_optional_content
|
2516
|
+
#
|
2517
|
+
# canvas.optional_content('Hints', use_existing_ocg: false) do
|
2518
|
+
# # Other instructions
|
2519
|
+
# end
|
2520
|
+
#
|
2521
|
+
# See: PDF2.0 s8.11, #end_optional_content, HexaPDF::Type::OptionalContentProperties
|
2522
|
+
def optional_content(ocg, use_existing_ocg: true, &block)
|
2523
|
+
ocg = if ocg.kind_of?(HexaPDF::Dictionary) || !use_existing_ocg
|
2524
|
+
context.document.optional_content.add_ocg(ocg)
|
2525
|
+
else
|
2526
|
+
context.document.optional_content.ocg(ocg, create: true)
|
2527
|
+
end
|
2528
|
+
marked_content_sequence(:OC, property_list: ocg, &block)
|
2529
|
+
end
|
2530
|
+
|
2531
|
+
# Ends an optional content sequence and returns +self+.
|
2532
|
+
#
|
2533
|
+
# See #optional_content for details.
|
2534
|
+
#
|
2535
|
+
# See: PDF2.0 s8.11
|
2536
|
+
alias :end_optional_content :end_marked_content_sequence
|
2537
|
+
|
2486
2538
|
# Creates and returns a color object from the given color specification. See #stroke_color for
|
2487
2539
|
# details on the possible color specifications.
|
2488
2540
|
#
|
@@ -1001,6 +1001,8 @@ module HexaPDF
|
|
1001
1001
|
|
1002
1002
|
# Mapping of operator names to their default operator implementations.
|
1003
1003
|
DEFAULT_OPERATORS = {
|
1004
|
+
BX: NoArgumentOperator.new('BX'),
|
1005
|
+
EX: NoArgumentOperator.new('EX'),
|
1004
1006
|
q: SaveGraphicsState.new,
|
1005
1007
|
Q: RestoreGraphicsState.new,
|
1006
1008
|
cm: ConcatenateMatrix.new,
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -249,6 +249,7 @@ module HexaPDF
|
|
249
249
|
# Sets all required fields that have no current value but a default value to their respective
|
250
250
|
# default value.
|
251
251
|
def set_required_fields_with_defaults
|
252
|
+
return if (type = value[:Type]) && self.class.type != type
|
252
253
|
self.class.each_field do |name, field|
|
253
254
|
if !key?(name) && field.required? && field.default?
|
254
255
|
value[name] = field.default
|
@@ -313,8 +313,7 @@ module HexaPDF
|
|
313
313
|
[String, Time, Date, DateTime]
|
314
314
|
end
|
315
315
|
|
316
|
-
# :nodoc:
|
317
|
-
DATE_RE = /\AD:(\d{4})(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([Z+-])?(?:(\d+)(?:'|'(\d+)'?|\z)?)?\z/n
|
316
|
+
DATE_RE = /\AD:(\d{4})(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([Z+-])?(?:(\d+)(?:'|'(\d+)'?|\z)?)?\z/n # :nodoc:
|
318
317
|
|
319
318
|
# Checks if the given object is a string and converts into a Time object if possible.
|
320
319
|
# Otherwise returns +nil+.
|
@@ -40,8 +40,7 @@ module HexaPDF
|
|
40
40
|
# Holds the result of verifying a signature.
|
41
41
|
class VerificationResult
|
42
42
|
|
43
|
-
# :nodoc:
|
44
|
-
MESSAGE_SORT_MAP = {
|
43
|
+
MESSAGE_SORT_MAP = { # :nodoc:
|
45
44
|
info: {warning: 1, error: 1, info: 0},
|
46
45
|
warning: {info: -1, error: 1, warning: 0},
|
47
46
|
error: {info: -1, warning: -1, error: 0},
|
@@ -607,6 +607,9 @@ module HexaPDF
|
|
607
607
|
# Finally, a default font is set if necessary to ensure that the style object works in all
|
608
608
|
# cases.
|
609
609
|
def retrieve_style(style, properties = nil)
|
610
|
+
if style.kind_of?(Symbol) && !@styles.key?(style)
|
611
|
+
raise HexaPDF::Error, "Style #{style} not defined"
|
612
|
+
end
|
610
613
|
style = HexaPDF::Layout::Style.create(@styles[style] || style || @styles[:base])
|
611
614
|
style = style.dup.update(**properties) unless properties.nil? || properties.empty?
|
612
615
|
style.font('Times') unless style.font?
|
@@ -246,7 +246,7 @@ module HexaPDF
|
|
246
246
|
|
247
247
|
labels = @document.catalog.page_labels(create: true)
|
248
248
|
labels.add_entry(start_index, page_label)
|
249
|
-
labels.add_entry(0, {S: :
|
249
|
+
labels.add_entry(0, {S: :D}) unless labels.find_entry(0)
|
250
250
|
|
251
251
|
page_label
|
252
252
|
end
|
data/lib/hexapdf/document.rb
CHANGED
@@ -538,6 +538,13 @@ module HexaPDF
|
|
538
538
|
catalog.outline
|
539
539
|
end
|
540
540
|
|
541
|
+
# Returns the main object for working with optional content (a.k.a. layers).
|
542
|
+
#
|
543
|
+
# See: Type::Catalog#optional_content
|
544
|
+
def optional_content
|
545
|
+
catalog.optional_content
|
546
|
+
end
|
547
|
+
|
541
548
|
# Executes the given task and returns its result.
|
542
549
|
#
|
543
550
|
# Tasks provide an extensible way for performing operations on a PDF document without
|
@@ -92,9 +92,8 @@ module HexaPDF
|
|
92
92
|
|
93
93
|
private
|
94
94
|
|
95
|
-
# :nodoc:
|
96
95
|
# Rijndael S-box
|
97
|
-
SBOX = [
|
96
|
+
SBOX = [ # :nodoc:
|
98
97
|
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
99
98
|
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
100
99
|
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
@@ -113,9 +112,8 @@ module HexaPDF
|
|
113
112
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
114
113
|
].freeze
|
115
114
|
|
116
|
-
# :nodoc:
|
117
115
|
# Inverse of the Rijndael S-box
|
118
|
-
INV_SBOX = [
|
116
|
+
INV_SBOX = [ # :nodoc:
|
119
117
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
120
118
|
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
121
119
|
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
@@ -134,12 +132,10 @@ module HexaPDF
|
|
134
132
|
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
135
133
|
].freeze
|
136
134
|
|
137
|
-
# :nodoc:
|
138
|
-
RCON = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c].freeze
|
135
|
+
RCON = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c].freeze # :nodoc:
|
139
136
|
|
140
|
-
# :nodoc:
|
141
137
|
# Precomputed Galois multiplication table for multiplication with 2 in GF(2^8)
|
142
|
-
G2MULT = [
|
138
|
+
G2MULT = [ # :nodoc:
|
143
139
|
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
|
144
140
|
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
|
145
141
|
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
|
@@ -158,9 +154,8 @@ module HexaPDF
|
|
158
154
|
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
|
159
155
|
].freeze
|
160
156
|
|
161
|
-
# :nodoc:
|
162
157
|
# Precomputed Galois multiplication table for multiplication with 3 in GF(2^8)
|
163
|
-
G3MULT = [
|
158
|
+
G3MULT = [ # :nodoc:
|
164
159
|
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
|
165
160
|
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
|
166
161
|
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
|
@@ -179,9 +174,8 @@ module HexaPDF
|
|
179
174
|
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
|
180
175
|
].freeze
|
181
176
|
|
182
|
-
# :nodoc:
|
183
177
|
# Precomputed Galois multiplication table for multiplication with 9 in GF(2^8)
|
184
|
-
G9MULT = [
|
178
|
+
G9MULT = [ # :nodoc:
|
185
179
|
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
|
186
180
|
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
|
187
181
|
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
|
@@ -200,9 +194,8 @@ module HexaPDF
|
|
200
194
|
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
|
201
195
|
].freeze
|
202
196
|
|
203
|
-
# :nodoc:
|
204
197
|
# Precomputed Galois multiplication table for multiplication with 11 in GF(2^8)
|
205
|
-
G11MULT = [
|
198
|
+
G11MULT = [ # :nodoc:
|
206
199
|
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
|
207
200
|
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
|
208
201
|
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
|
@@ -221,9 +214,8 @@ module HexaPDF
|
|
221
214
|
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
|
222
215
|
].freeze
|
223
216
|
|
224
|
-
# :nodoc:
|
225
217
|
# Precomputed Galois multiplication table for multiplication with 13 in GF(2^8)
|
226
|
-
G13MULT = [
|
218
|
+
G13MULT = [ # :nodoc:
|
227
219
|
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
|
228
220
|
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
|
229
221
|
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
|
@@ -242,9 +234,8 @@ module HexaPDF
|
|
242
234
|
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
|
243
235
|
].freeze
|
244
236
|
|
245
|
-
# :nodoc:
|
246
237
|
# Precomputed Galois multiplication table for multiplication with 14 in GF(2^8)
|
247
|
-
G14MULT = [
|
238
|
+
G14MULT = [ # :nodoc:
|
248
239
|
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
|
249
240
|
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
|
250
241
|
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
|
@@ -263,9 +254,8 @@ module HexaPDF
|
|
263
254
|
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
|
264
255
|
].freeze
|
265
256
|
|
266
|
-
# :nodoc:
|
267
257
|
# Number of rounds needed in various parts of the algorithm, depends on the key size
|
268
|
-
NUMBER_OF_ROUNDS = {16 => 10, 24 => 12, 32 => 14}.freeze
|
258
|
+
NUMBER_OF_ROUNDS = {16 => 10, 24 => 12, 32 => 14}.freeze # :nodoc:
|
269
259
|
|
270
260
|
# KeyExpansion step
|
271
261
|
#
|
data/lib/hexapdf/layout/box.rb
CHANGED
@@ -127,10 +127,24 @@ module HexaPDF
|
|
127
127
|
#
|
128
128
|
# This can be used to store arbitrary information on boxes for later use. For example, a
|
129
129
|
# generic style layer could use one or more custom properties for its work.
|
130
|
+
#
|
131
|
+
# The Box class itself uses the following properties:
|
132
|
+
#
|
133
|
+
# optional_content::
|
134
|
+
#
|
135
|
+
# If this property is set, it needs to be an optional content group dictionary, a String
|
136
|
+
# defining an (optionally existing) optional content group dictionary, or an optional
|
137
|
+
# content membership dictionary.
|
138
|
+
#
|
139
|
+
# The whole content of the box, i.e. including padding, border, background..., is
|
140
|
+
# wrapped with the appropriate commands so that the optional content group or membership
|
141
|
+
# dictionary specifies whether the content is shown or not.
|
142
|
+
#
|
143
|
+
# See: HexaPDF::Type::OptionalContentProperties
|
130
144
|
attr_reader :properties
|
131
145
|
|
132
146
|
# :call-seq:
|
133
|
-
# Box.new(width: 0, height: 0, style: nil, properties:
|
147
|
+
# Box.new(width: 0, height: 0, style: nil, properties: nil) {|canv, box| block} -> box
|
134
148
|
#
|
135
149
|
# Creates a new Box object with the given width and height that uses the provided block when
|
136
150
|
# it is asked to draw itself on a canvas (see #draw).
|
@@ -138,11 +152,11 @@ module HexaPDF
|
|
138
152
|
# Since the final location of the box is not known beforehand, the drawing operations inside
|
139
153
|
# the block should draw inside the rectangle (0, 0, content_width, content_height) - note that
|
140
154
|
# the width and height of the box may not be known beforehand.
|
141
|
-
def initialize(width: 0, height: 0, style: nil, properties:
|
155
|
+
def initialize(width: 0, height: 0, style: nil, properties: nil, &block)
|
142
156
|
@width = @initial_width = width
|
143
157
|
@height = @initial_height = height
|
144
158
|
@style = Style.create(style)
|
145
|
-
@properties = properties
|
159
|
+
@properties = properties || {}
|
146
160
|
@draw_block = block
|
147
161
|
@fit_successful = false
|
148
162
|
@split_box = false
|
@@ -220,6 +234,10 @@ module HexaPDF
|
|
220
234
|
# instance variable to +nil+ or a valid block. This is useful to avoid unnecessary set-up
|
221
235
|
# operations when the block does nothing.
|
222
236
|
def draw(canvas, x, y)
|
237
|
+
if (oc = properties['optional_content'])
|
238
|
+
canvas.optional_content(oc)
|
239
|
+
end
|
240
|
+
|
223
241
|
if style.background_color? && style.background_color
|
224
242
|
canvas.save_graphics_state do
|
225
243
|
canvas.opacity(fill_alpha: style.background_alpha).
|
@@ -233,6 +251,8 @@ module HexaPDF
|
|
233
251
|
draw_content(canvas, x + reserved_width_left, y + reserved_height_bottom)
|
234
252
|
|
235
253
|
style.overlays.draw(canvas, x, y, self) if style.overlays?
|
254
|
+
|
255
|
+
canvas.end_optional_content if oc
|
236
256
|
end
|
237
257
|
|
238
258
|
# Returns +true+ if no drawing operations are performed.
|
@@ -177,7 +177,8 @@ module HexaPDF
|
|
177
177
|
[column_left, column_bottom + height])
|
178
178
|
shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection)
|
179
179
|
end
|
180
|
-
column_frame = Frame.new(column_left, column_bottom, column_width, height,
|
180
|
+
column_frame = Frame.new(column_left, column_bottom, column_width, height,
|
181
|
+
shape: shape, context: frame.context)
|
181
182
|
@box_fitter << column_frame
|
182
183
|
end
|
183
184
|
|