hexapdf 0.29.0 → 0.30.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 +18 -0
- data/lib/hexapdf/dictionary_fields.rb +7 -2
- data/lib/hexapdf/document/pages.rb +35 -18
- data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
- data/lib/hexapdf/type/font_simple.rb +14 -2
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_pages.rb +25 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -2
- data/test/hexapdf/test_dictionary_fields.rb +9 -4
- data/test/hexapdf/test_writer.rb +6 -6
- data/test/hexapdf/type/test_font_simple.rb +18 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 139f1864e4decd05c57468dc3b326a9cc4e749f1d35d4b5dadb3e0549215afd6
|
4
|
+
data.tar.gz: e7a0d34979abe6738a084dec2334f449ebc1db3eb479dd5f2374a81eeabd83d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 339c38737585eafdf447f7516a52ba7ce40b7e2e476336a2404e97a97645d5043b2956835d874296841b8b68d5935c53d97244ac90a2fd621d0471ce85ae8809
|
7
|
+
data.tar.gz: 4a4c843a32859c639fdf724ca1c6492fa3d387a1c8a92335a2341e82de42a5b9bc30663a3226c1cc0f2ca116dd638071e24b595b323e64fa1e0342d8681199bd
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
## 0.30.0 - 2023-01-13
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::Document::Pages#create] for creating a page object without adding it
|
6
|
+
to the page tree
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
* `HexaPDF::Type::FontSimple#perform_validation` to correct /Widths fields in
|
11
|
+
case it has an invalid number of entries
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
* [HexaPDF::DictionaryFields::DateConverter] to handle invalid months, day,
|
16
|
+
hour, minute and second values
|
17
|
+
|
18
|
+
|
1
19
|
## 0.29.0 - 2023-01-30
|
2
20
|
|
3
21
|
### Added
|
@@ -305,8 +305,13 @@ module HexaPDF
|
|
305
305
|
else
|
306
306
|
(m[7] == '-' ? -1 : 1) * (m[8].to_i * 3600 + m[9].to_i * 60).clamp(0, 86399)
|
307
307
|
end
|
308
|
-
|
309
|
-
|
308
|
+
begin
|
309
|
+
Time.new(m[1].to_i, (m[2] ? m[2].to_i : 1), (m[3] ? m[3].to_i : 1),
|
310
|
+
m[4].to_i, m[5].to_i, m[6].to_i, utc_offset)
|
311
|
+
rescue ArgumentError
|
312
|
+
Time.new(m[1].to_i, m[2].to_i.clamp(1, 12), m[3].to_i.clamp(1, 31),
|
313
|
+
m[4].to_i.clamp(0, 23), m[5].to_i.clamp(0, 59), m[6].to_i.clamp(0, 59), utc_offset)
|
314
|
+
end
|
310
315
|
end
|
311
316
|
|
312
317
|
end
|
@@ -75,28 +75,45 @@ module HexaPDF
|
|
75
75
|
@document.catalog.pages
|
76
76
|
end
|
77
77
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
78
|
+
# Creates a page object and returns it *without* adding it to the page tree.
|
79
|
+
#
|
80
|
+
# +media_box+::
|
81
|
+
# If this argument is +nil+/not specified, the value is taken from the configuration
|
82
|
+
# option 'page.default_media_box'.
|
83
|
+
#
|
84
|
+
# If the resulting value is an array with four numbers (specifying the media box), the new
|
85
|
+
# page will have these exact dimensions.
|
82
86
|
#
|
83
|
-
#
|
87
|
+
# If the value is a symbol, it is taken as a reference to a pre-defined media box in
|
88
|
+
# HexaPDF::Type::Page::PAPER_SIZE. The +orientation+ can then be used to specify the page
|
89
|
+
# orientation.
|
84
90
|
#
|
85
|
-
#
|
86
|
-
# 'page.
|
91
|
+
# +orientation+::
|
92
|
+
# If this argument is not specified, it is taken from 'page.default_media_orientation'. It
|
93
|
+
# is only used if +media_box+ is a symbol and not an array.
|
94
|
+
def create(media_box: nil, orientation: nil)
|
95
|
+
media_box ||= @document.config['page.default_media_box']
|
96
|
+
orientation ||= @document.config['page.default_media_orientation']
|
97
|
+
box = if media_box.kind_of?(Array)
|
98
|
+
media_box
|
99
|
+
else
|
100
|
+
Type::Page.media_box(media_box, orientation: orientation)
|
101
|
+
end
|
102
|
+
@document.add({Type: :Page, MediaBox: box})
|
103
|
+
end
|
104
|
+
|
105
|
+
# :call-seq:
|
106
|
+
# pages.add -> new_page
|
107
|
+
# pages.add(page) -> page
|
108
|
+
# pages.add(media_box, orientation: nil) -> new_page
|
87
109
|
#
|
88
|
-
#
|
89
|
-
# page will have these dimensions.
|
110
|
+
# Adds the given page or a new empty page at the end and returns it.
|
90
111
|
#
|
91
|
-
# If
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
page = @document.add({Type: :Page, MediaBox: page})
|
97
|
-
elsif page.kind_of?(Symbol)
|
98
|
-
box = Type::Page.media_box(page, orientation: orientation)
|
99
|
-
page = @document.add({Type: :Page, MediaBox: box})
|
112
|
+
# If called with a page object as argument, that page object is used. Otherwise #create is
|
113
|
+
# called with the arguments +media_box+ and +orientation+ to create a new page.
|
114
|
+
def add(page = nil, orientation: nil)
|
115
|
+
unless page.kind_of?(HexaPDF::Type::Page)
|
116
|
+
page = create(media_box: page, orientation: orientation)
|
100
117
|
end
|
101
118
|
@document.catalog.pages.add_page(page)
|
102
119
|
end
|
@@ -324,10 +324,10 @@ module HexaPDF
|
|
324
324
|
def prepare_decryption(password: '', check_permissions: true)
|
325
325
|
if dict[:Filter] != :Standard
|
326
326
|
raise(HexaPDF::UnsupportedEncryptionError,
|
327
|
-
"Invalid /Filter value for standard security handler")
|
327
|
+
"Invalid /Filter value #{dict[:Filter]} for standard security handler")
|
328
328
|
elsif ![2, 3, 4, 6].include?(dict[:R])
|
329
329
|
raise(HexaPDF::UnsupportedEncryptionError,
|
330
|
-
"Invalid /R value for standard security handler")
|
330
|
+
"Invalid /R value #{dict[:R]} for standard security handler")
|
331
331
|
elsif dict[:R] <= 4 && !document.trailer[:ID].kind_of?(PDFArray)
|
332
332
|
document.trailer[:ID] = ['', '']
|
333
333
|
end
|
@@ -171,9 +171,21 @@ module HexaPDF
|
|
171
171
|
yield("Required field #{field} is not set", false) if self[field].nil?
|
172
172
|
end
|
173
173
|
|
174
|
+
widths = self[:Widths]
|
174
175
|
if key?(:Widths) && key?(:LastChar) && key?(:FirstChar) &&
|
175
|
-
|
176
|
-
yield("Invalid number of entries in field Widths",
|
176
|
+
widths.length != (self[:LastChar] - self[:FirstChar] + 1)
|
177
|
+
yield("Invalid number of entries in field Widths", true)
|
178
|
+
difference = self[:LastChar] - self[:FirstChar] + 1 - widths.length
|
179
|
+
if difference > 0
|
180
|
+
missing_value = if widths.count(widths[0]) == widths.length
|
181
|
+
widths[0]
|
182
|
+
else
|
183
|
+
self[:FontDescriptor]&.[](:MissingWidth) || 0
|
184
|
+
end
|
185
|
+
difference.times { widths << missing_value }
|
186
|
+
else
|
187
|
+
widths.slice!(difference, -difference)
|
188
|
+
end
|
177
189
|
end
|
178
190
|
end
|
179
191
|
|
data/lib/hexapdf/version.rb
CHANGED
@@ -14,6 +14,31 @@ describe HexaPDF::Document::Pages do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
describe "create" do
|
18
|
+
it "uses the defaults from the configuration for missing arguments" do
|
19
|
+
page = @doc.pages.create
|
20
|
+
assert_equal([0, 0, 595, 842], page.box(:media).value)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "allows specifying a reference to a predefined page size" do
|
24
|
+
page = @doc.pages.create(media_box: :A3)
|
25
|
+
assert_equal([0, 0, 842, 1191], page.box(:media).value)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "allows specifying the orientation for a predefined page size" do
|
29
|
+
page = @doc.pages.create(media_box: :A4, orientation: :landscape)
|
30
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
31
|
+
|
32
|
+
page = @doc.pages.create(orientation: :landscape)
|
33
|
+
assert_equal([0, 0, 842, 595], page.box(:media).value)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "allows using a media box array" do
|
37
|
+
page = @doc.pages.create(media_box: [0, 0, 12, 24], orientation: :landscape)
|
38
|
+
assert_equal([0, 0, 12, 24], page.box(:media).value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
17
42
|
describe "add" do
|
18
43
|
it "adds a new empty page when no page is given" do
|
19
44
|
page = @doc.pages.add
|
@@ -213,14 +213,14 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
213
213
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
214
214
|
@handler.set_up_decryption({Filter: :NonStandard, V: 2})
|
215
215
|
end
|
216
|
-
assert_match(/Invalid \/Filter/i, exp.message)
|
216
|
+
assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
|
217
217
|
end
|
218
218
|
|
219
219
|
it "fails if the /R value is incorrect" do
|
220
220
|
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
221
221
|
@handler.set_up_decryption({Filter: :Standard, V: 2, R: 5})
|
222
222
|
end
|
223
|
-
assert_match(/Invalid \/R/i, exp.message)
|
223
|
+
assert_match(/Invalid \/R value 5/i, exp.message)
|
224
224
|
end
|
225
225
|
|
226
226
|
it "fails if the supplied password is invalid" do
|
@@ -188,10 +188,15 @@ describe HexaPDF::DictionaryFields do
|
|
188
188
|
["D:19981223-08'00'", [1998, 12, 23, 00, 00, 00, "-08:00"]],
|
189
189
|
["D:199812-08'00'", [1998, 12, 01, 00, 00, 00, "-08:00"]],
|
190
190
|
["D:1998-08'00'", [1998, 01, 01, 00, 00, 00, "-08:00"]],
|
191
|
-
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
192
|
-
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], #
|
193
|
-
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], #
|
194
|
-
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], #
|
191
|
+
["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
192
|
+
["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
|
193
|
+
["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # TZ hour too large
|
194
|
+
["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # TZ min too large
|
195
|
+
["D:19982423195210-08'00'", [1998, 12, 23, 19, 52, 10, "-08:00"]], # months too large
|
196
|
+
["D:19981273195210-08'00'", [1998, 12, 31, 19, 52, 10, "-08:00"]], # day too large
|
197
|
+
["D:19981223275210-08'00'", [1998, 12, 23, 23, 52, 10, "-08:00"]], # hour too large
|
198
|
+
["D:19981223197710-08'00'", [1998, 12, 23, 19, 59, 10, "-08:00"]], # minute too large
|
199
|
+
["D:19981223195280-08'00'", [1998, 12, 23, 19, 52, 59, "-08:00"]], # seconds too large
|
195
200
|
].each do |str, data|
|
196
201
|
obj = @field.convert(str, self)
|
197
202
|
assert_equal(Time.new(*data), obj, "date str used: #{str}")
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
40
40
|
219
|
41
41
|
%%EOF
|
42
42
|
3 0 obj
|
43
|
-
<</Producer(HexaPDF version 0.
|
43
|
+
<</Producer(HexaPDF version 0.30.0)>>
|
44
44
|
endobj
|
45
45
|
xref
|
46
46
|
3 1
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
72
72
|
141
|
73
73
|
%%EOF
|
74
74
|
6 0 obj
|
75
|
-
<</Producer(HexaPDF version 0.
|
75
|
+
<</Producer(HexaPDF version 0.30.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -164,9 +164,9 @@ describe HexaPDF::Writer do
|
|
164
164
|
|
165
165
|
document = HexaPDF::Document.new(io: io)
|
166
166
|
assert_equal(3, document.revisions.count)
|
167
|
-
assert_equal(1, document.revisions.all[0].object(
|
168
|
-
assert_equal(2, document.revisions.all[1].object(
|
169
|
-
assert_equal(3, document.revisions.all[2].object(
|
167
|
+
assert_equal(1, document.revisions.all[0].object(3)[:Kids].length)
|
168
|
+
assert_equal(2, document.revisions.all[1].object(3)[:Kids].length)
|
169
|
+
assert_equal(3, document.revisions.all[2].object(3)[:Kids].length)
|
170
170
|
end
|
171
171
|
|
172
172
|
it "creates an xref stream if no xref stream is in a revision but object streams are" do
|
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
|
|
214
214
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
215
215
|
endobj
|
216
216
|
5 0 obj
|
217
|
-
<</Producer(HexaPDF version 0.
|
217
|
+
<</Producer(HexaPDF version 0.30.0)>>
|
218
218
|
endobj
|
219
219
|
4 0 obj
|
220
220
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|
@@ -15,7 +15,7 @@ describe HexaPDF::Type::FontSimple do
|
|
15
15
|
EOF
|
16
16
|
font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
|
17
17
|
FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
|
18
|
-
Descent: -100, CapHeight: 800, StemV: 20})
|
18
|
+
MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
|
19
19
|
@font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
|
20
20
|
BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
|
21
21
|
FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
|
@@ -130,9 +130,7 @@ describe HexaPDF::Type::FontSimple do
|
|
130
130
|
end
|
131
131
|
|
132
132
|
it "returns the /MissingWidth of a /FontDescriptor if available and the width was not found" do
|
133
|
-
assert_equal(
|
134
|
-
@font[:FontDescriptor][:MissingWidth] = 99
|
135
|
-
assert_equal(99, @font.width(0))
|
133
|
+
assert_equal(500, @font.width(0))
|
136
134
|
end
|
137
135
|
|
138
136
|
it "returns 0 for a missing code point when FontDescriptor is not available" do
|
@@ -169,8 +167,22 @@ describe HexaPDF::Type::FontSimple do
|
|
169
167
|
end
|
170
168
|
|
171
169
|
it "validates the lengths of the /Widths field" do
|
172
|
-
@font[:Widths] = [65]
|
173
|
-
|
170
|
+
@font[:Widths] = [65, 65]
|
171
|
+
assert(@font.validate)
|
172
|
+
assert_equal([65, 65, 65], @font[:Widths])
|
173
|
+
|
174
|
+
@font[:Widths] = [65, 70]
|
175
|
+
assert(@font.validate)
|
176
|
+
assert_equal([65, 70, 500], @font[:Widths])
|
177
|
+
|
178
|
+
@font[:Widths] = [65, 70]
|
179
|
+
@font.delete(:FontDescriptor)
|
180
|
+
assert(@font.validate)
|
181
|
+
assert_equal([65, 70, 0], @font[:Widths])
|
182
|
+
|
183
|
+
@font[:Widths] = [65, 65, 70, 90, 100]
|
184
|
+
assert(@font.validate)
|
185
|
+
assert_equal([65, 65, 70], @font[:Widths])
|
174
186
|
end
|
175
187
|
end
|
176
188
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.30.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|