hexapdf 0.26.2 → 0.27.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 +56 -0
- 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/020-column_box.rb +3 -3
- data/lib/hexapdf/cli/split.rb +7 -7
- data/lib/hexapdf/cli/watermark.rb +2 -2
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/dictionary.rb +3 -12
- data/lib/hexapdf/document/destinations.rb +42 -5
- data/lib/hexapdf/document/signatures.rb +265 -48
- data/lib/hexapdf/importer.rb +3 -0
- data/lib/hexapdf/parser.rb +1 -0
- data/lib/hexapdf/revisions.rb +3 -1
- data/lib/hexapdf/tokenizer.rb +2 -2
- data/lib/hexapdf/type/acro_form/form.rb +28 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/outline.rb +18 -0
- data/lib/hexapdf/type/outline_item.rb +72 -14
- data/lib/hexapdf/type/page.rb +56 -35
- 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 +3 -0
- data/test/hexapdf/document/test_destinations.rb +41 -0
- data/test/hexapdf/document/test_signatures.rb +139 -19
- data/test/hexapdf/test_importer.rb +14 -0
- data/test/hexapdf/test_parser.rb +2 -2
- data/test/hexapdf/test_revisions.rb +20 -12
- data/test/hexapdf/test_tokenizer.rb +11 -1
- data/test/hexapdf/test_writer.rb +11 -3
- data/test/hexapdf/type/acro_form/test_form.rb +47 -0
- data/test/hexapdf/type/signature/common.rb +52 -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_outline.rb +1 -1
- data/test/hexapdf/type/test_outline_item.rb +62 -1
- data/test/hexapdf/type/test_page.rb +41 -20
- 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 +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57b852a0648f47b5e3443e9b8aa4480a7a8cd187085c0067baf86af14cee7d9a
|
4
|
+
data.tar.gz: 6e28f748f1d8e089b6585748e2f56c3adf79669cf9b00d0d9fe8d83a74e97063
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1d2cd75344a3fc9cd54f0dafa6a0fd23b9821d67f54581090a6db2820f7e04a0989da01d7b5f9c558e02bb57cf254f4ebaab791b950d4dacc1e02a29c5f3844
|
7
|
+
data.tar.gz: ea758cdeb96d8282e3c6581785b690b8d79e9bee318a7cf80bfb4201b47fc0f1d721a78b5c703b32f20b8671211b604e78f83dec6768187321fef46e5dac3a81
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,59 @@
|
|
1
|
+
## 0.27.0 - 2022-11-18
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for timestamp signatures through the
|
6
|
+
[HexaPDF::Document::Signatures::TimestampHandler]
|
7
|
+
* [HexaPDF::Document::Destinations#resolve] for resolving destination values
|
8
|
+
* [HexaPDF::Document::Destinations::Destination#value] to return the destination
|
9
|
+
array
|
10
|
+
* Support for verifying document timestamp signatures
|
11
|
+
* [HexaPDF::Document::Signatures::DefaultHandler#signature_size] to support
|
12
|
+
setting custom signature sizes
|
13
|
+
* [HexaPDF::Document::Signatures::DefaultHandler#external_signing] to support
|
14
|
+
signing via custom mechanisms
|
15
|
+
* [HexaPDF::Document::Signatures::embed_signature] to enable asynchronous
|
16
|
+
external signing
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
* **Breaking change**: The crop box is now used instead of the media box in most
|
21
|
+
cases to be in line with the specification
|
22
|
+
* [HexaPDF::Document::Signatures::DefaultHandler] to allow setting the used
|
23
|
+
signature method
|
24
|
+
* **Breaking change**: [HexaPDF::Document::Signatures::DefaultHandler#sign]
|
25
|
+
needs to accept the IO object and the byte range instead of just the data
|
26
|
+
* **Breaking change**: Enhanced support for outline items with new methods
|
27
|
+
`#level` and `#destination_page` as well as changes to `#add` and `#each_item`
|
28
|
+
* **Breaking change**: Removed `#filter_name` and `#sub_filter_name` from
|
29
|
+
[HexaPDF::Document::Signatures::DefaultHandler]
|
30
|
+
* `HexaPDF::Type::Resources#perform_validation` to not add a default procedure
|
31
|
+
set since this feature is deprecated
|
32
|
+
|
33
|
+
### Fixed
|
34
|
+
|
35
|
+
* [HexaPDF::Document::Destinations::Destination::new] to also accept a hash
|
36
|
+
* [HexaPDF::Type::Catalog] auto-conversion of /Outlines to correct class
|
37
|
+
* [HexaPDF::Type::AcroForm::Form#flatten] to return the unflattened form fields
|
38
|
+
instead of the widgets
|
39
|
+
* [HexaPDF::Writer#write_incremental] to set the /Version in the catalog
|
40
|
+
dictionary when necessary
|
41
|
+
* [HexaPDF::Importer#import] to always return an imported object with the same
|
42
|
+
class as the argument
|
43
|
+
* [HexaPDF::Type::OutlineItem] to always be an indirect object
|
44
|
+
* `HexaPDF::Tokenizer#parse_number` to handle references correctly in all cases
|
45
|
+
* [HexaPDF::Type::Page#rotate] to correctly flatten all page boxes
|
46
|
+
* [HexaPDF::Document::Signatures#add] to raise an error if the reserved space
|
47
|
+
for the signature is not enough
|
48
|
+
* `HexaPDF::Type::AcroForm::Form#perform_validation` to fix broken /Parent
|
49
|
+
entries and to remove invalid objects from the field hierarchy
|
50
|
+
* `HexaPDF::Type::OutlineItem#perform_validation` bug where a missing /Count key
|
51
|
+
was deemed invalid
|
52
|
+
* [HexaPDF::Revisions::from_io] to use the correct /Prev offset when revisions
|
53
|
+
have been merged
|
54
|
+
* Handling of indirect objects with invalid values for more situations
|
55
|
+
|
56
|
+
|
1
57
|
## 0.26.2 - 2022-10-22
|
2
58
|
|
3
59
|
### Added
|
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/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
|
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
134
|
paper_size = HexaPDF::Type::Page::PAPER_SIZE.find do |_name, box|
|
135
|
-
box.each_with_index.all? {|entry, index| (entry -
|
135
|
+
box.each_with_index.all? {|entry, index| (entry - 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)
|
@@ -488,11 +488,13 @@ module HexaPDF
|
|
488
488
|
},
|
489
489
|
'signature.signing_handler' => {
|
490
490
|
default: 'HexaPDF::Document::Signatures::DefaultHandler',
|
491
|
+
timestamp: 'HexaPDF::Document::Signatures::TimestampHandler',
|
491
492
|
},
|
492
493
|
'signature.sub_filter_map' => {
|
493
494
|
'adbe.x509.rsa_sha1': 'HexaPDF::Type::Signature::AdbeX509RsaSha1',
|
494
495
|
'adbe.pkcs7.detached': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
495
496
|
'ETSI.CAdES.detached': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
497
|
+
'ETSI.RFC3161': 'HexaPDF::Type::Signature::AdbePkcs7Detached',
|
496
498
|
},
|
497
499
|
'task.map' => {
|
498
500
|
optimize: 'HexaPDF::Task::Optimize',
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -259,22 +259,13 @@ module HexaPDF
|
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
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
262
|
# Performs validation tasks based on the currently set keys and defined fields.
|
271
263
|
def perform_validation(&block)
|
272
264
|
super
|
273
|
-
|
274
|
-
|
265
|
+
self.class.each_field do |name, field|
|
266
|
+
next unless field.required? || value.key?(name)
|
275
267
|
|
276
|
-
|
277
|
-
next if field.nil?
|
268
|
+
obj = key?(name) ? self[name] : nil
|
278
269
|
|
279
270
|
# Check that required fields are set
|
280
271
|
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
|
#
|