hexapdf 0.26.2 → 0.28.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 +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
|
#
|