hexapdf 0.26.2 → 0.28.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 +115 -1
- data/README.md +1 -1
- data/examples/013-text_layouter_shapes.rb +8 -8
- data/examples/016-frame_automatic_box_placement.rb +3 -3
- data/examples/017-frame_text_flow.rb +3 -3
- data/examples/019-acro_form.rb +14 -3
- data/examples/020-column_box.rb +3 -3
- data/examples/023-images.rb +30 -0
- data/lib/hexapdf/cli/info.rb +5 -1
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/cli/split.rb +8 -8
- data/lib/hexapdf/cli/watermark.rb +2 -2
- data/lib/hexapdf/configuration.rb +3 -2
- data/lib/hexapdf/content/canvas.rb +8 -3
- data/lib/hexapdf/dictionary.rb +4 -17
- data/lib/hexapdf/document/destinations.rb +42 -5
- data/lib/hexapdf/document/signatures.rb +265 -48
- data/lib/hexapdf/document.rb +6 -10
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/importer.rb +35 -27
- data/lib/hexapdf/layout/list_box.rb +1 -5
- data/lib/hexapdf/object.rb +5 -0
- data/lib/hexapdf/parser.rb +14 -0
- data/lib/hexapdf/revision.rb +15 -12
- data/lib/hexapdf/revisions.rb +7 -1
- data/lib/hexapdf/tokenizer.rb +15 -9
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +174 -128
- data/lib/hexapdf/type/acro_form/button_field.rb +5 -3
- data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/field.rb +11 -5
- data/lib/hexapdf/type/acro_form/form.rb +61 -8
- data/lib/hexapdf/type/acro_form/signature_field.rb +2 -0
- data/lib/hexapdf/type/acro_form/text_field.rb +12 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -0
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +14 -0
- data/lib/hexapdf/type/object_stream.rb +2 -2
- data/lib/hexapdf/type/outline.rb +19 -1
- data/lib/hexapdf/type/outline_item.rb +72 -14
- data/lib/hexapdf/type/page.rb +95 -64
- data/lib/hexapdf/type/resources.rb +13 -17
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +16 -2
- data/lib/hexapdf/type/signature.rb +10 -0
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +5 -3
- data/test/hexapdf/content/test_canvas.rb +5 -0
- data/test/hexapdf/document/test_destinations.rb +41 -0
- data/test/hexapdf/document/test_pages.rb +2 -2
- data/test/hexapdf/document/test_signatures.rb +139 -19
- data/test/hexapdf/encryption/test_aes.rb +1 -1
- data/test/hexapdf/filter/test_predictor.rb +0 -1
- data/test/hexapdf/layout/test_box.rb +2 -1
- data/test/hexapdf/layout/test_column_box.rb +1 -1
- data/test/hexapdf/layout/test_list_box.rb +1 -1
- data/test/hexapdf/test_document.rb +2 -8
- data/test/hexapdf/test_importer.rb +27 -6
- data/test/hexapdf/test_parser.rb +19 -2
- data/test/hexapdf/test_revision.rb +15 -14
- data/test/hexapdf/test_revisions.rb +63 -12
- data/test/hexapdf/test_stream.rb +1 -1
- data/test/hexapdf/test_tokenizer.rb +10 -1
- data/test/hexapdf/test_writer.rb +11 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +135 -56
- data/test/hexapdf/type/acro_form/test_button_field.rb +6 -1
- data/test/hexapdf/type/acro_form/test_choice_field.rb +4 -0
- data/test/hexapdf/type/acro_form/test_field.rb +4 -4
- data/test/hexapdf/type/acro_form/test_form.rb +65 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +4 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +13 -0
- data/test/hexapdf/type/signature/common.rb +54 -0
- data/test/hexapdf/type/signature/test_adbe_pkcs7_detached.rb +21 -0
- data/test/hexapdf/type/test_catalog.rb +5 -2
- data/test/hexapdf/type/test_font_true_type.rb +20 -0
- data/test/hexapdf/type/test_object_stream.rb +2 -1
- data/test/hexapdf/type/test_outline.rb +4 -1
- data/test/hexapdf/type/test_outline_item.rb +62 -1
- data/test/hexapdf/type/test_page.rb +103 -45
- data/test/hexapdf/type/test_page_tree_node.rb +4 -2
- data/test/hexapdf/type/test_resources.rb +0 -5
- data/test/hexapdf/type/test_signature.rb +8 -0
- data/test/test_helper.rb +1 -1
- metadata +61 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 874e09b094ea4e793d1d123cfbaded6d1cc5ba93af3b57587e9faf402786a30f
|
4
|
+
data.tar.gz: c1eed6a778936cd360b4f1878a18abc3e5727c1f36b5eab4838a9a72817dff7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b66a7587a239acbeb9ebbb20f851b2fa7738c5a4c2f8a95ae7ae3d7419b84ae37b5d1fb48a6b7023bff0df3e1728c395b5351c9c42c05707fabd5a1722e2b88a
|
7
|
+
data.tar.gz: 62ac7d070bb8ae3426685af497a047096dd32dc16a102baa961512652222b388198561776fc1227be69bdd0713183d91fa25e411262eec34a29227aae9723d5c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,117 @@
|
|
1
|
+
## 0.28.0 - 2022-12-30
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::Type::AcroForm::AppearanceGenerator#create_push_button_appearances]
|
6
|
+
to allow customizing the behaviour
|
7
|
+
* [HexaPDF::Parser#linearized?] for determining whether a document is linearized
|
8
|
+
* Information on linearization to `hexapdf info` output
|
9
|
+
* Support for `AFNumber_Format` Javascript method to the form field appearance
|
10
|
+
generator
|
11
|
+
* Support for using fully embedded, simple TrueType fonts for drawing operations
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
* **Breaking change**: `HexaPDF::Revision#reset_objects` has been removed
|
16
|
+
* **Breaking change**: Method signature of [HexaPDF::Importer::for] has been
|
17
|
+
changed
|
18
|
+
* **Breaking change**: [HexaPDF::Type::AcroForm::Field#each_widget] now has the
|
19
|
+
default value of the argument `direct_only` set to `true` instead of `false`
|
20
|
+
* [HexaPDF::Revision#each_modified_object] to allow deleting the modified
|
21
|
+
objects from the active objects' container
|
22
|
+
* [HexaPDF::Revision#each_modified_object] to allow ignoring added object and
|
23
|
+
cross-reference stream objects
|
24
|
+
* [HexaPDF::Revisions::from_io] to merge the two revisions of a linearized PDF
|
25
|
+
* [HexaPDF::Importer] and [HexaPDF::Document#import] to make working with them
|
26
|
+
easier by allowing the import of arbitrary objects
|
27
|
+
* `HexaPDF::Type::AcroForm::Form#perform_validation` to combine fields with the
|
28
|
+
same name
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
|
32
|
+
* [HexaPDF::Type::AcroForm::AppearanceGenerator#create_check_box_appearances] to
|
33
|
+
correctly handle a field value of `nil`
|
34
|
+
* Return value of `#type` method for all AcroForm field classes
|
35
|
+
* [HexaPDF::Type::Page#flatten_annotations] to work correctly in case no
|
36
|
+
annotations are on the page
|
37
|
+
* [HexaPDF::Type::AcroForm::ButtonField#create_appearances] to avoid creating
|
38
|
+
appearances in case of as-yet unresolved references to existing appearances
|
39
|
+
* [HexaPDF::Type::AcroForm::TextField#create_appearances] to avoid creating
|
40
|
+
appearances in case of pre-existing ones
|
41
|
+
* `HexaPDF::Tokenizer#parse_number` to treat invalid indirect object references
|
42
|
+
with an object number of 0 as null values
|
43
|
+
* [HexaPDF::Type::AcroForm::AppearanceGenerator] to handle empty appearance
|
44
|
+
characteristics dictionary marker style strings
|
45
|
+
* Writing of encrypted files containing two or more revisions
|
46
|
+
* Generation of object streams to never allow storing the catalog object to
|
47
|
+
avoid problems with certain viewers
|
48
|
+
* `HexaPDF::Type::Outline#perform_validation` to not show validation error when
|
49
|
+
`/Count` is zero
|
50
|
+
* Writing of documents with two or more revisions in non-incremental mode when
|
51
|
+
`optimize: true` is used and the original document used cross-reference tables
|
52
|
+
* [HexaPDF::Type::AcroForm::AppearanceGenerator] to take a widget's rotation
|
53
|
+
value into account
|
54
|
+
* [HexaPDF::Type::Page#flatten_annotations] to correctly flatten all
|
55
|
+
annotations, including ones with custom rotations
|
56
|
+
* [HexaPDF::Type::Page#rotate] to also rotate annotations
|
57
|
+
|
58
|
+
|
59
|
+
## 0.27.0 - 2022-11-18
|
60
|
+
|
61
|
+
### Added
|
62
|
+
|
63
|
+
* Support for timestamp signatures through the
|
64
|
+
[HexaPDF::Document::Signatures::TimestampHandler]
|
65
|
+
* [HexaPDF::Document::Destinations#resolve] for resolving destination values
|
66
|
+
* [HexaPDF::Document::Destinations::Destination#value] to return the destination
|
67
|
+
array
|
68
|
+
* Support for verifying document timestamp signatures
|
69
|
+
* [HexaPDF::Document::Signatures::DefaultHandler#signature_size] to support
|
70
|
+
setting custom signature sizes
|
71
|
+
* [HexaPDF::Document::Signatures::DefaultHandler#external_signing] to support
|
72
|
+
signing via custom mechanisms
|
73
|
+
* [HexaPDF::Document::Signatures::embed_signature] to enable asynchronous
|
74
|
+
external signing
|
75
|
+
|
76
|
+
### Changed
|
77
|
+
|
78
|
+
* **Breaking change**: The crop box is now used instead of the media box in most
|
79
|
+
cases to be in line with the specification
|
80
|
+
* [HexaPDF::Document::Signatures::DefaultHandler] to allow setting the used
|
81
|
+
signature method
|
82
|
+
* **Breaking change**: [HexaPDF::Document::Signatures::DefaultHandler#sign]
|
83
|
+
needs to accept the IO object and the byte range instead of just the data
|
84
|
+
* **Breaking change**: Enhanced support for outline items with new methods
|
85
|
+
`#level` and `#destination_page` as well as changes to `#add` and `#each_item`
|
86
|
+
* **Breaking change**: Removed `#filter_name` and `#sub_filter_name` from
|
87
|
+
[HexaPDF::Document::Signatures::DefaultHandler]
|
88
|
+
* `HexaPDF::Type::Resources#perform_validation` to not add a default procedure
|
89
|
+
set since this feature is deprecated
|
90
|
+
|
91
|
+
### Fixed
|
92
|
+
|
93
|
+
* [HexaPDF::Document::Destinations::Destination::new] to also accept a hash
|
94
|
+
* [HexaPDF::Type::Catalog] auto-conversion of /Outlines to correct class
|
95
|
+
* [HexaPDF::Type::AcroForm::Form#flatten] to return the unflattened form fields
|
96
|
+
instead of the widgets
|
97
|
+
* [HexaPDF::Writer#write_incremental] to set the /Version in the catalog
|
98
|
+
dictionary when necessary
|
99
|
+
* [HexaPDF::Importer#import] to always return an imported object with the same
|
100
|
+
class as the argument
|
101
|
+
* [HexaPDF::Type::OutlineItem] to always be an indirect object
|
102
|
+
* `HexaPDF::Tokenizer#parse_number` to handle references correctly in all cases
|
103
|
+
* [HexaPDF::Type::Page#rotate] to correctly flatten all page boxes
|
104
|
+
* [HexaPDF::Document::Signatures#add] to raise an error if the reserved space
|
105
|
+
for the signature is not enough
|
106
|
+
* `HexaPDF::Type::AcroForm::Form#perform_validation` to fix broken /Parent
|
107
|
+
entries and to remove invalid objects from the field hierarchy
|
108
|
+
* `HexaPDF::Type::OutlineItem#perform_validation` bug where a missing /Count key
|
109
|
+
was deemed invalid
|
110
|
+
* [HexaPDF::Revisions::from_io] to use the correct /Prev offset when revisions
|
111
|
+
have been merged
|
112
|
+
* Handling of indirect objects with invalid values for more situations
|
113
|
+
|
114
|
+
|
1
115
|
## 0.26.2 - 2022-10-22
|
2
116
|
|
3
117
|
### Added
|
@@ -180,7 +294,7 @@
|
|
180
294
|
### Added
|
181
295
|
|
182
296
|
- [HexaPDF::Composer#create_stamp] for creating a form Xobject
|
183
|
-
-
|
297
|
+
- `HexaPDF::Revision#reset_objects` for deleting all live loaded and added
|
184
298
|
objects
|
185
299
|
- Support for removing or flattening annotations to the `hexapdf modify` command
|
186
300
|
- Option to CLI command `hexapdf form` to allow generation of a template file
|
data/README.md
CHANGED
@@ -91,7 +91,7 @@ with example graphics and PDF files and tightly integrated into the rest of the
|
|
91
91
|
## Requirements and Installation
|
92
92
|
|
93
93
|
Since HexaPDF is written in Ruby, a working Ruby installation is needed - see the
|
94
|
-
[official installation documentation][rbinstall] for details. Note that you need Ruby version 2.
|
94
|
+
[official installation documentation][rbinstall] for details. Note that you need Ruby version 2.6 or
|
95
95
|
higher as prior versions are not supported!
|
96
96
|
|
97
97
|
HexaPDF works on all Ruby implementations that are CRuby compatible, e.g. TruffleRuby, and on any
|
@@ -73,14 +73,14 @@ canvas.circle(0, circle_top - radius, radius).stroke
|
|
73
73
|
# Center: full circle
|
74
74
|
layouter.style.align = :justify
|
75
75
|
result = layouter.fit(items, circle, radius * 2)
|
76
|
-
result.draw(canvas, page.box
|
77
|
-
canvas.circle(page.box
|
76
|
+
result.draw(canvas, page.box.width / 2.0 - radius, circle_top)
|
77
|
+
canvas.circle(page.box.width / 2.0, circle_top - radius, radius).stroke
|
78
78
|
|
79
79
|
# Right: left half circle
|
80
80
|
layouter.style.align = :right
|
81
81
|
result = layouter.fit(items, left_half_circle, radius * 2)
|
82
|
-
result.draw(canvas, page.box
|
83
|
-
canvas.circle(page.box
|
82
|
+
result.draw(canvas, page.box.width - radius, circle_top)
|
83
|
+
canvas.circle(page.box.width, circle_top - radius, radius).stroke
|
84
84
|
|
85
85
|
|
86
86
|
########################################################################
|
@@ -115,7 +115,7 @@ canvas.polyline(0, diamond_top, diamond_width, diamond_top - diamond_width,
|
|
115
115
|
# Center: full diamond
|
116
116
|
layouter.style.align = :justify
|
117
117
|
result = layouter.fit(items, full_diamond, 2 * diamond_width)
|
118
|
-
left = page.box
|
118
|
+
left = page.box.width / 2.0 - diamond_width
|
119
119
|
result.draw(canvas, left, diamond_top)
|
120
120
|
canvas.polyline(left + diamond_width, diamond_top,
|
121
121
|
left + 2 * diamond_width, diamond_top - diamond_width,
|
@@ -125,7 +125,7 @@ canvas.polyline(left + diamond_width, diamond_top,
|
|
125
125
|
# Right: left half diamond
|
126
126
|
layouter.style.align = :right
|
127
127
|
result = layouter.fit(items, left_half_diamond, 2 * diamond_width)
|
128
|
-
middle = page.box
|
128
|
+
middle = page.box.width
|
129
129
|
result.draw(canvas, middle - diamond_width, diamond_top)
|
130
130
|
canvas.polyline(middle, diamond_top,
|
131
131
|
middle - diamond_width, diamond_top - diamond_width,
|
@@ -144,7 +144,7 @@ sine_wave = lambda do |height, line_height|
|
|
144
144
|
end
|
145
145
|
layouter.style.align = :justify
|
146
146
|
result = layouter.fit(items, sine_wave, sine_wave_height)
|
147
|
-
middle = page.box
|
147
|
+
middle = page.box.width / 2.0
|
148
148
|
result.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)
|
149
149
|
|
150
150
|
########################################################################
|
@@ -170,7 +170,7 @@ end
|
|
170
170
|
layouter.style.align = :justify
|
171
171
|
result = layouter.fit(items, house, 200)
|
172
172
|
|
173
|
-
middle = page.box
|
173
|
+
middle = page.box.width / 2.0
|
174
174
|
result.draw(canvas, middle - (outer_width / 2), house_top)
|
175
175
|
|
176
176
|
doc.write("text_layouter_shapes.pdf", optimize: true)
|
@@ -20,11 +20,11 @@ include HexaPDF::Layout
|
|
20
20
|
|
21
21
|
doc = HexaPDF::Document.new
|
22
22
|
page = doc.pages.add
|
23
|
-
|
23
|
+
page_box = page.box
|
24
24
|
canvas = page.canvas
|
25
25
|
|
26
|
-
frame = Frame.new(
|
27
|
-
|
26
|
+
frame = Frame.new(page_box.left + 20, page_box.bottom + 20,
|
27
|
+
page_box.width - 40, page_box.height - 40)
|
28
28
|
|
29
29
|
box_counter = 1
|
30
30
|
draw_box = lambda do |**args|
|
@@ -20,9 +20,9 @@ include HexaPDF::Utils::GraphicsHelpers
|
|
20
20
|
doc = HexaPDF::Document.new
|
21
21
|
|
22
22
|
page = doc.pages.add
|
23
|
-
|
24
|
-
frame = Frame.new(
|
25
|
-
|
23
|
+
page_box = page.box
|
24
|
+
frame = Frame.new(page_box.left + 20, page_box.bottom + 20,
|
25
|
+
page_box.width - 40, page_box.height - 40)
|
26
26
|
|
27
27
|
boxes = []
|
28
28
|
boxes << doc.layout.image_box(File.join(__dir__, 'machupicchu.jpg'),
|
data/examples/019-acro_form.rb
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
# This example show-cases how to create the various form field types and their
|
7
7
|
# possible standard appearances.
|
8
8
|
#
|
9
|
+
# Note the 'number format' text field which uses a JavaScript function for
|
10
|
+
# formatting a number.
|
11
|
+
#
|
9
12
|
# Usage:
|
10
13
|
# : `ruby acro_form.rb`
|
11
14
|
#
|
@@ -42,13 +45,21 @@ rb = form.create_radio_button("Radio")
|
|
42
45
|
end
|
43
46
|
rb.field_value = :button0
|
44
47
|
|
45
|
-
canvas.text("Text fields", at: [50,
|
48
|
+
canvas.text("Text fields", at: [50, 480])
|
46
49
|
|
47
|
-
canvas.text("Single line", at: [70,
|
50
|
+
canvas.text("Single line", at: [70, 450])
|
48
51
|
tx = form.create_text_field("Single Line", font_size: 16)
|
49
|
-
widget = tx.create_widget(page, Rect: [200,
|
52
|
+
widget = tx.create_widget(page, Rect: [200, 445, 500, 465])
|
50
53
|
tx.field_value = "A sample test string!"
|
51
54
|
|
55
|
+
canvas.text("Number format", at: [70, 420])
|
56
|
+
tx = form.create_text_field("Number format", font_size: 16)
|
57
|
+
widget = tx.create_widget(page, Rect: [200, 415, 500, 435])
|
58
|
+
widget[:AA] = {
|
59
|
+
F: {S: :JavaScript, JS: 'AFNumber_Format(2, 2, 0, 0, "EUR ", true);'},
|
60
|
+
}
|
61
|
+
tx.field_value = "123456,789"
|
62
|
+
|
52
63
|
canvas.text("Multiline", at: [70, 390])
|
53
64
|
tx = form.create_multiline_text_field("Multiline", font_size: 0, align: :right)
|
54
65
|
widget = tx.create_widget(page, Rect: [200, 325, 500, 405])
|
data/examples/020-column_box.rb
CHANGED
@@ -15,9 +15,9 @@ require 'hexapdf'
|
|
15
15
|
|
16
16
|
doc = HexaPDF::Document.new
|
17
17
|
page = doc.pages.add
|
18
|
-
|
19
|
-
frame = HexaPDF::Layout::Frame.new(
|
20
|
-
|
18
|
+
page_box = page.box
|
19
|
+
frame = HexaPDF::Layout::Frame.new(page_box.left + 20, page_box.bottom + 20,
|
20
|
+
page_box.width - 40, page_box.height - 40)
|
21
21
|
|
22
22
|
boxes = []
|
23
23
|
5.times do
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# # Images
|
2
|
+
#
|
3
|
+
# This example shows how to embed images into a PDF document, directly on a
|
4
|
+
# page's canvas and through the high-level [HexaPDF::Composer].
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# : `ruby images.rb`
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'hexapdf'
|
11
|
+
|
12
|
+
file = File.join(__dir__, 'machupicchu.jpg')
|
13
|
+
|
14
|
+
doc = HexaPDF::Document.new
|
15
|
+
# Image only added to PDF once though used multiple times
|
16
|
+
canvas = doc.pages.add.canvas
|
17
|
+
canvas.image(file, at: [100, 500]) # auto-size based on image size
|
18
|
+
canvas.image(file, at: [100, 300], width: 100) # height based on w/h ratio
|
19
|
+
canvas.image(file, at: [300, 300], height: 100) # width based on w/h ratio
|
20
|
+
canvas.image(file, at: [100, 100], width: 300, height: 100)
|
21
|
+
|
22
|
+
HexaPDF::Composer.create('images.pdf') do |composer|
|
23
|
+
composer.image(file) # fill current rectangular region
|
24
|
+
composer.image(file, width: 100) # height based on w/h ratio
|
25
|
+
composer.image(file, height: 100) # width based on w/h ratio
|
26
|
+
composer.image(file, width: 300, height: 100)
|
27
|
+
|
28
|
+
# Add the page created above as second page
|
29
|
+
composer.document.pages << composer.document.import(doc.pages[0])
|
30
|
+
end
|
data/lib/hexapdf/cli/info.rb
CHANGED
@@ -131,6 +131,10 @@ module HexaPDF
|
|
131
131
|
output_line("Encrypted", "yes (no or wrong password given)")
|
132
132
|
end
|
133
133
|
|
134
|
+
if doc.revisions.parser.linearized?
|
135
|
+
output_line("Linearized", "yes")
|
136
|
+
end
|
137
|
+
|
134
138
|
signatures = doc.signatures.to_a
|
135
139
|
unless signatures.empty?
|
136
140
|
nr_sigs = signatures.count
|
@@ -186,7 +190,7 @@ module HexaPDF
|
|
186
190
|
end
|
187
191
|
|
188
192
|
def output_line(header, text) #:nodoc:
|
189
|
-
puts(
|
193
|
+
puts("#{header}:".ljust(COLUMN_WIDTH) << text.to_s)
|
190
194
|
end
|
191
195
|
|
192
196
|
end
|
data/lib/hexapdf/cli/inspect.rb
CHANGED
@@ -335,9 +335,9 @@ module HexaPDF
|
|
335
335
|
# - The signature dictionary if this revision was signed
|
336
336
|
# - The byte offset from the start of the file to the end of the revision
|
337
337
|
def revision_information
|
338
|
-
signatures = @doc.signatures.
|
338
|
+
signatures = @doc.signatures.to_h do |sig|
|
339
339
|
[@doc.revisions.find {|rev| rev.object(sig) == sig }, sig]
|
340
|
-
end
|
340
|
+
end
|
341
341
|
io = @doc.revisions.parser.io
|
342
342
|
|
343
343
|
startxrefs = @doc.revisions.map {|rev| rev.trailer[:Prev] }
|
data/lib/hexapdf/cli/split.rb
CHANGED
@@ -114,7 +114,7 @@ module HexaPDF
|
|
114
114
|
end
|
115
115
|
|
116
116
|
doc.pages.each do |page|
|
117
|
-
out = out_files[page_size_name(page.box
|
117
|
+
out = out_files[page_size_name(page.box.value)]
|
118
118
|
out.pages.add(out.import(page))
|
119
119
|
end
|
120
120
|
|
@@ -125,18 +125,18 @@ module HexaPDF
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
-
# Tries to retrieve a page size name based on the
|
128
|
+
# Tries to retrieve a page size name based on the given page box. If this is not possible, the
|
129
129
|
# returned page size name consists of width x height.
|
130
|
-
def page_size_name(
|
130
|
+
def page_size_name(box)
|
131
131
|
@page_name_cache ||= {}
|
132
|
-
return @page_name_cache[
|
132
|
+
return @page_name_cache[box] if @page_name_cache.key?(box)
|
133
133
|
|
134
|
-
paper_size = HexaPDF::Type::Page::PAPER_SIZE.find do |_name,
|
135
|
-
|
134
|
+
paper_size = HexaPDF::Type::Page::PAPER_SIZE.find do |_name, paper_box|
|
135
|
+
paper_box.each_with_index.all? {|entry, index| (entry - paper_box[index]).abs < 5 }
|
136
136
|
end
|
137
137
|
|
138
|
-
@page_name_cache[
|
139
|
-
paper_size ? paper_size[0] : sprintf("%.0fx%.0f", *
|
138
|
+
@page_name_cache[box] =
|
139
|
+
paper_size ? paper_size[0] : sprintf("%.0fx%.0f", *box.values_at(2, 3))
|
140
140
|
end
|
141
141
|
|
142
142
|
end
|
@@ -95,8 +95,8 @@ module HexaPDF
|
|
95
95
|
doc.pages.each do |page|
|
96
96
|
index = indices.next
|
97
97
|
xobject = xobject_map[index] ||= doc.import(watermark.pages[index].to_form_xobject)
|
98
|
-
pw = page.box
|
99
|
-
ph = page.box
|
98
|
+
pw = page.box.width.to_f
|
99
|
+
ph = page.box.height.to_f
|
100
100
|
xw = xobject.width.to_f
|
101
101
|
xh = xobject.height.to_f
|
102
102
|
canvas = page.canvas(type: @type)
|
@@ -422,8 +422,7 @@ module HexaPDF
|
|
422
422
|
'encryption.filter_map' => {
|
423
423
|
Standard: 'HexaPDF::Encryption::StandardSecurityHandler',
|
424
424
|
},
|
425
|
-
'encryption.sub_filter_map' => {
|
426
|
-
},
|
425
|
+
'encryption.sub_filter_map' => {},
|
427
426
|
'filter.map' => {
|
428
427
|
ASCIIHexDecode: 'HexaPDF::Filter::ASCIIHexDecode',
|
429
428
|
AHx: 'HexaPDF::Filter::ASCIIHexDecode',
|
@@ -488,11 +487,13 @@ module HexaPDF
|
|
488
487
|
},
|
489
488
|
'signature.signing_handler' => {
|
490
489
|
default: 'HexaPDF::Document::Signatures::DefaultHandler',
|
490
|
+
timestamp: 'HexaPDF::Document::Signatures::TimestampHandler',
|
491
491
|
},
|
492
492
|
'signature.sub_filter_map' => {
|
493
493
|
'adbe.x509.rsa_sha1': 'HexaPDF::Type::Signature::AdbeX509RsaSha1',
|
494
494
|
'adbe.pkcs7.detached': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
495
495
|
'ETSI.CAdES.detached': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
496
|
+
'ETSI.RFC3161': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
496
497
|
},
|
497
498
|
'task.map' => {
|
498
499
|
optimize: 'HexaPDF::Task::Optimize',
|
@@ -1626,17 +1626,22 @@ module HexaPDF
|
|
1626
1626
|
end
|
1627
1627
|
return obj if obj.width == 0 || obj.height == 0
|
1628
1628
|
|
1629
|
+
left, bottom = *at
|
1629
1630
|
width, height = calculate_dimensions(obj.width, obj.height,
|
1630
1631
|
rwidth: width, rheight: height)
|
1631
1632
|
if obj[:Subtype] != :Image
|
1632
1633
|
width /= obj.box.width.to_f
|
1633
1634
|
height /= obj.box.height.to_f
|
1634
|
-
|
1635
|
-
|
1635
|
+
left -= obj.box.left
|
1636
|
+
bottom -= obj.box.bottom
|
1636
1637
|
end
|
1637
1638
|
|
1638
|
-
|
1639
|
+
if left == 0 && bottom == 0 && width == 1 && height == 1
|
1639
1640
|
invoke1(:Do, resources.add_xobject(obj))
|
1641
|
+
else
|
1642
|
+
transform(width, 0, 0, height, left, bottom) do
|
1643
|
+
invoke1(:Do, resources.add_xobject(obj))
|
1644
|
+
end
|
1640
1645
|
end
|
1641
1646
|
|
1642
1647
|
obj
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -108,11 +108,7 @@ module HexaPDF
|
|
108
108
|
# The ancestor classes are also searched for such a field entry if none is found for the
|
109
109
|
# current class.
|
110
110
|
def self.field(name)
|
111
|
-
|
112
|
-
@fields[name]
|
113
|
-
elsif superclass.respond_to?(:field)
|
114
|
-
superclass.field(name)
|
115
|
-
end
|
111
|
+
@fields&.[](name) || superclass.field(name)
|
116
112
|
end
|
117
113
|
|
118
114
|
# :call-seq:
|
@@ -259,22 +255,13 @@ module HexaPDF
|
|
259
255
|
end
|
260
256
|
end
|
261
257
|
|
262
|
-
# Iterates over all currently set fields and those that are required.
|
263
|
-
def each_set_key_or_required_field #:yields: name, field
|
264
|
-
value.keys.each {|name| yield(name, self.class.field(name)) }
|
265
|
-
self.class.each_field do |name, field|
|
266
|
-
yield(name, field) if field.required? && !value.key?(name)
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
258
|
# Performs validation tasks based on the currently set keys and defined fields.
|
271
259
|
def perform_validation(&block)
|
272
260
|
super
|
273
|
-
|
274
|
-
|
261
|
+
self.class.each_field do |name, field|
|
262
|
+
next unless field.required? || value.key?(name)
|
275
263
|
|
276
|
-
|
277
|
-
next if field.nil?
|
264
|
+
obj = key?(name) ? self[name] : nil
|
278
265
|
|
279
266
|
# Check that required fields are set
|
280
267
|
if field.required? && obj.nil?
|
@@ -107,8 +107,7 @@ module HexaPDF
|
|
107
107
|
# not changing it from the current value.
|
108
108
|
class Destination
|
109
109
|
|
110
|
-
|
111
|
-
TYPE_MAPPING = {
|
110
|
+
TYPE_MAPPING = { #:nodoc:
|
112
111
|
XYZ: :xyz,
|
113
112
|
Fit: :fit_page,
|
114
113
|
FitH: :fit_page_horizontal,
|
@@ -119,8 +118,7 @@ module HexaPDF
|
|
119
118
|
FitBV: :fit_bounding_box_vertical,
|
120
119
|
}
|
121
120
|
|
122
|
-
|
123
|
-
REVERSE_TYPE_MAPPING = Hash[*TYPE_MAPPING.flatten.reverse]
|
121
|
+
REVERSE_TYPE_MAPPING = Hash[*TYPE_MAPPING.flatten.reverse] #:nodoc:
|
124
122
|
|
125
123
|
# Returns +true+ if the destination is valid.
|
126
124
|
def self.valid?(destination)
|
@@ -132,7 +130,11 @@ module HexaPDF
|
|
132
130
|
# Creates a new Destination for the given +destination+ which may be an explicit destination
|
133
131
|
# array or a dictionary with a /D entry (as allowed for a named destination).
|
134
132
|
def initialize(destination)
|
135
|
-
@destination =
|
133
|
+
@destination = if destination.kind_of?(HexaPDF::Dictionary) || destination.kind_of?(Hash)
|
134
|
+
destination[:D]
|
135
|
+
else
|
136
|
+
destination
|
137
|
+
end
|
136
138
|
end
|
137
139
|
|
138
140
|
# Returns +true+ if the destination references a destination in a remote document.
|
@@ -212,6 +214,11 @@ module HexaPDF
|
|
212
214
|
self.class.valid?(@destination)
|
213
215
|
end
|
214
216
|
|
217
|
+
# Returns the wrapped destination array.
|
218
|
+
def value
|
219
|
+
@destination
|
220
|
+
end
|
221
|
+
|
215
222
|
end
|
216
223
|
|
217
224
|
include Enumerable
|
@@ -447,6 +454,36 @@ module HexaPDF
|
|
447
454
|
destinations.delete_entry(name)
|
448
455
|
end
|
449
456
|
|
457
|
+
# :call-seq:
|
458
|
+
# destinations.resolve(string_name) -> destination or nil
|
459
|
+
# destinations.resolve(symbol_name) -> destination or nil
|
460
|
+
# destinations.resolve(dest_array) -> destination or nil
|
461
|
+
#
|
462
|
+
# Resolves the given value to a valid destination object, if possible, or otherwise returns
|
463
|
+
# +nil+.
|
464
|
+
#
|
465
|
+
# * If the given value is a string, it is treated as a destination name and looked up in the
|
466
|
+
# destination name tree.
|
467
|
+
#
|
468
|
+
# * If the given value is a symbol, it is treated as an old-style destination name and looked
|
469
|
+
# up in the destination dictionary.
|
470
|
+
#
|
471
|
+
# * If the given value is an array, it is treated as a destination array itself.
|
472
|
+
def resolve(value)
|
473
|
+
result = case value
|
474
|
+
when String
|
475
|
+
destinations.find_entry(value)
|
476
|
+
when PDFArray
|
477
|
+
value.value
|
478
|
+
when Array
|
479
|
+
value
|
480
|
+
when Symbol
|
481
|
+
@document.catalog[:Dests]&.[](value)
|
482
|
+
end
|
483
|
+
result = Destination.new(result) if result
|
484
|
+
result&.valid? ? result : nil
|
485
|
+
end
|
486
|
+
|
450
487
|
# :call-seq:
|
451
488
|
# destinations[name] -> destination
|
452
489
|
#
|