hexapdf 0.14.3 → 0.14.4
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 +25 -0
- data/lib/hexapdf/configuration.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +7 -2
- data/lib/hexapdf/error.rb +3 -3
- data/lib/hexapdf/filter.rb +1 -0
- data/lib/hexapdf/filter/crypt.rb +60 -0
- data/lib/hexapdf/font/type1/afm_parser.rb +2 -1
- data/lib/hexapdf/parser.rb +17 -5
- data/lib/hexapdf/serializer.rb +7 -1
- data/lib/hexapdf/tokenizer.rb +22 -3
- data/lib/hexapdf/type/xref_stream.rb +7 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/encryption/test_security_handler.rb +15 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +5 -0
- data/test/hexapdf/test_parser.rb +24 -0
- data/test/hexapdf/test_serializer.rb +3 -0
- data/test/hexapdf/test_tokenizer.rb +22 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_xref_stream.rb +7 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 958692ab2c53f74fe599c0ba8c9c046aa41b38d2bf840a47dec8f0e258fd86e0
|
4
|
+
data.tar.gz: b41d46ccb39d36d351cc143ba0afc145e785307b2a7ae90bc0f32d1ab76949af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c16e231aeeb12b55daf75a28a8e3918f6807127257655f42d1f3433c76cb25f0ea42daafc8dd20bd37099f9ef5f6f2a6a3e3b2468acf000c041452a32908e1ee
|
7
|
+
data.tar.gz: a9a1fff7c7ff699c2b48333d89fae1aa67cd7ce61003845f76e4855cfc2f2ad5969ae668359091cd3fa225ea9a8b99fb9883e28b07fe6fcd985c7f83b842969d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
## 0.14.4 - 2021-02-27
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for the Crypt filters
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
|
9
|
+
* [HexaPDF::MalformedPDFError] to make the `pos` argument optional
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
* Handling of invalid floating point numbers NaN, Inf and -Inf when serializing
|
14
|
+
* Processing of invalid PDF files containing NaN and Inf instead of numbers
|
15
|
+
* Bug in Type1 font AFM parser that occured if the file doesn't end with a new
|
16
|
+
line character
|
17
|
+
* Cross-reference table reconstruction to handle the case of an entry specifying
|
18
|
+
a non-existent indirect object
|
19
|
+
* Cross-reference table reconstruction to handle trailers specified by cross-
|
20
|
+
reference streams
|
21
|
+
* Cross-reference table reconstruction to use the set security handle for
|
22
|
+
decrypting indirect objects
|
23
|
+
* Parsing of cross-reference streams where data is missing
|
24
|
+
|
25
|
+
|
1
26
|
## 0.14.3 - 2021-02-16
|
2
27
|
|
3
28
|
### Fixed
|
@@ -393,7 +393,7 @@ module HexaPDF
|
|
393
393
|
DCTDecode: 'HexaPDF::Filter::PassThrough',
|
394
394
|
DCT: 'HexaPDF::Filter::PassThrough',
|
395
395
|
JPXDecode: 'HexaPDF::Filter::PassThrough',
|
396
|
-
Crypt:
|
396
|
+
Crypt: 'HexaPDF::Filter::Crypt',
|
397
397
|
Encryption: 'HexaPDF::Filter::Encryption',
|
398
398
|
},
|
399
399
|
'font.map' => {},
|
@@ -268,7 +268,7 @@ module HexaPDF
|
|
268
268
|
str.replace(string_algorithm.decrypt(key, str))
|
269
269
|
end
|
270
270
|
|
271
|
-
if obj.kind_of?(HexaPDF::Stream)
|
271
|
+
if obj.kind_of?(HexaPDF::Stream) && obj.raw_stream.filter[0] != :Crypt
|
272
272
|
unless string_algorithm == stream_algorithm
|
273
273
|
key = object_key(obj.oid, obj.gen, stream_algorithm)
|
274
274
|
end
|
@@ -300,7 +300,12 @@ module HexaPDF
|
|
300
300
|
obj.raw_stream.key == key && obj.raw_stream.algorithm == stream_algorithm
|
301
301
|
obj.raw_stream.undecrypted_fiber
|
302
302
|
else
|
303
|
-
|
303
|
+
filter = obj[:Filter]
|
304
|
+
if filter == :Crypt || (filter.kind_of?(PDFArray) && filter[0] == :Crypt)
|
305
|
+
result
|
306
|
+
else
|
307
|
+
stream_algorithm.encryption_fiber(key, result)
|
308
|
+
end
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
data/lib/hexapdf/error.rb
CHANGED
@@ -47,14 +47,14 @@ module HexaPDF
|
|
47
47
|
|
48
48
|
# Creates a new malformed PDF error object for the given exception message.
|
49
49
|
#
|
50
|
-
# The byte position where the error occured
|
51
|
-
def initialize(message, pos:)
|
50
|
+
# The byte position where the error occured can be given via the +pos+ argument.
|
51
|
+
def initialize(message, pos: nil)
|
52
52
|
super(message)
|
53
53
|
@pos = pos
|
54
54
|
end
|
55
55
|
|
56
56
|
def message # :nodoc:
|
57
|
-
"PDF malformed around position #{pos}: #{super}"
|
57
|
+
"PDF malformed#{pos ? "around position #{pos}" : ''}: #{super}"
|
58
58
|
end
|
59
59
|
|
60
60
|
end
|
data/lib/hexapdf/filter.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2014-2020 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/error'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Filter
|
41
|
+
|
42
|
+
# This filter module implements the Crypt filter. The only supported part is using the Identity
|
43
|
+
# filter.
|
44
|
+
module Crypt
|
45
|
+
|
46
|
+
# See HexaPDF::Filter
|
47
|
+
def self.decoder(source, options)
|
48
|
+
if !options || !options.key?(:Name) || options[:Name] == :Identity
|
49
|
+
source
|
50
|
+
else
|
51
|
+
raise FilterError, "Handling of Crypt filters besides Identity is not implemented"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
singleton_class.send(:alias_method, :encoder, :decoder)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -56,10 +56,12 @@ module HexaPDF
|
|
56
56
|
# PDF references are resolved using the associated Document object.
|
57
57
|
def initialize(io, document)
|
58
58
|
@io = io
|
59
|
-
|
59
|
+
on_correctable_error = document.config['parser.on_correctable_error'].curry[document]
|
60
|
+
@tokenizer = Tokenizer.new(io, on_correctable_error: on_correctable_error)
|
60
61
|
@document = document
|
61
62
|
@object_stream_data = {}
|
62
63
|
@reconstructed_revision = nil
|
64
|
+
@in_reconstruct_revision = false
|
63
65
|
retrieve_pdf_header_offset_and_version
|
64
66
|
end
|
65
67
|
|
@@ -94,7 +96,8 @@ module HexaPDF
|
|
94
96
|
|
95
97
|
@document.wrap(obj, oid: oid, gen: gen, stream: stream)
|
96
98
|
rescue HexaPDF::MalformedPDFError
|
97
|
-
reconstructed_revision.object(xref_entry)
|
99
|
+
reconstructed_revision.object(xref_entry) ||
|
100
|
+
@document.wrap(nil, oid: xref_entry.oid, gen: xref_entry.gen)
|
98
101
|
end
|
99
102
|
|
100
103
|
# Parses the indirect object at the specified offset.
|
@@ -389,6 +392,9 @@ module HexaPDF
|
|
389
392
|
# If the file contains multiple cross-reference sections, all objects will be put into a single
|
390
393
|
# cross-reference table, later objects overwriting prior ones.
|
391
394
|
def reconstruct_revision
|
395
|
+
return if @in_reconstruct_revision
|
396
|
+
@in_reconstruct_revision = true
|
397
|
+
|
392
398
|
raise unless @document.config['parser.try_xref_reconstruction']
|
393
399
|
msg = "#{$!} - trying cross-reference table reconstruction"
|
394
400
|
@document.config['parser.on_correctable_error'].call(@document, msg, @tokenizer.pos)
|
@@ -424,16 +430,22 @@ module HexaPDF
|
|
424
430
|
end
|
425
431
|
end
|
426
432
|
|
427
|
-
trailer&.delete(:Prev) # no need for this and may wreak havoc
|
428
433
|
if !trailer || trailer.empty?
|
429
|
-
|
434
|
+
_, trailer = load_revision(startxref_offset) rescue nil
|
435
|
+
unless trailer
|
436
|
+
@in_reconstruct_revision = false
|
437
|
+
raise_malformed("Could not reconstruct malformed PDF because trailer was not found", pos: 0)
|
438
|
+
end
|
430
439
|
end
|
440
|
+
trailer&.delete(:Prev) # no need for this and may wreak havoc
|
431
441
|
|
432
442
|
loader = lambda do |xref_entry|
|
433
443
|
obj, oid, gen, stream = parse_indirect_object(xref_entry.pos)
|
434
|
-
@document.wrap(obj, oid: oid, gen: gen, stream: stream)
|
444
|
+
obj = @document.wrap(obj, oid: oid, gen: gen, stream: stream)
|
445
|
+
@document.security_handler ? @document.security_handler.decrypt(obj) : obj
|
435
446
|
end
|
436
447
|
|
448
|
+
@in_reconstruct_revision = false
|
437
449
|
Revision.new(@document.wrap(trailer, type: :XXTrailer), xref_section: xref,
|
438
450
|
loader: loader)
|
439
451
|
end
|
data/lib/hexapdf/serializer.rb
CHANGED
@@ -191,7 +191,13 @@ module HexaPDF
|
|
191
191
|
#
|
192
192
|
# See: PDF1.7 s7.3.3
|
193
193
|
def serialize_float(obj)
|
194
|
-
-0.0001 < obj && obj < 0.0001 && obj != 0
|
194
|
+
if -0.0001 < obj && obj < 0.0001 && obj != 0
|
195
|
+
sprintf("%.6f", obj)
|
196
|
+
elsif obj.finite?
|
197
|
+
obj.round(6).to_s
|
198
|
+
else
|
199
|
+
raise HexaPDF::Error, "Can't serialize special floating point number #{obj}"
|
200
|
+
end
|
195
201
|
end
|
196
202
|
|
197
203
|
# The regexp matches all characters that need to be escaped and the substs hash contains the
|
data/lib/hexapdf/tokenizer.rb
CHANGED
@@ -73,11 +73,15 @@ module HexaPDF
|
|
73
73
|
# The IO object from the tokens are read.
|
74
74
|
attr_reader :io
|
75
75
|
|
76
|
-
# Creates a new tokenizer.
|
77
|
-
|
76
|
+
# Creates a new tokenizer for the given IO stream.
|
77
|
+
#
|
78
|
+
# If +on_correctable_error+ is set to an object responding to +call(msg, pos)+, errors for
|
79
|
+
# correctable situations are only raised if the return value of calling the object is +true+.
|
80
|
+
def initialize(io, on_correctable_error: nil)
|
78
81
|
@io = io
|
79
82
|
@ss = StringScanner.new(''.b)
|
80
83
|
@original_pos = -1
|
84
|
+
@on_correctable_error = on_correctable_error || proc { false }
|
81
85
|
self.pos = 0
|
82
86
|
end
|
83
87
|
|
@@ -180,7 +184,8 @@ module HexaPDF
|
|
180
184
|
end
|
181
185
|
else
|
182
186
|
unless allow_keyword
|
183
|
-
|
187
|
+
maybe_raise("Invalid object, got token #{token}", force: token !~ /^-?(nan|inf)$/i)
|
188
|
+
token = 0
|
184
189
|
end
|
185
190
|
end
|
186
191
|
end
|
@@ -428,6 +433,20 @@ module HexaPDF
|
|
428
433
|
true
|
429
434
|
end
|
430
435
|
|
436
|
+
# Calls the @on_correctable_error callable object with the given message and the current
|
437
|
+
# position. If the returned value is +true+, raises a HexaPDF::MalformedPDFError. Otherwise the
|
438
|
+
# error is corrected (by the caller) and tokenization continues.
|
439
|
+
#
|
440
|
+
# If the option +force+ is used, the callable object is not called and the error is raised
|
441
|
+
# immediately.
|
442
|
+
def maybe_raise(msg, force: false)
|
443
|
+
if force || @on_correctable_error.call(msg, pos)
|
444
|
+
error = HexaPDF::MalformedPDFError.new(msg, pos: pos)
|
445
|
+
error.set_backtrace(caller(1))
|
446
|
+
raise error
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
431
450
|
end
|
432
451
|
|
433
452
|
end
|
@@ -135,6 +135,13 @@ module HexaPDF
|
|
135
135
|
w1 = w[1]
|
136
136
|
w2 = w[2]
|
137
137
|
|
138
|
+
needed_bytes = (w0 + w1 + w2) * index.each_slice(2).sum(&:last)
|
139
|
+
|
140
|
+
if needed_bytes > data.size
|
141
|
+
raise HexaPDF::MalformedPDFError, "Cross-reference stream is missing data " \
|
142
|
+
"(#{needed_bytes} bytes needed, got #{data.size})"
|
143
|
+
end
|
144
|
+
|
138
145
|
index.each_slice(2) do |first_oid, number_of_entries|
|
139
146
|
first_oid.upto(first_oid + number_of_entries - 1) do |oid|
|
140
147
|
# Default for first field: type 1
|
data/lib/hexapdf/version.rb
CHANGED
@@ -297,6 +297,13 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
297
297
|
assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
|
298
298
|
end
|
299
299
|
|
300
|
+
it "defers handling encryption to a Crypt filter is specified" do
|
301
|
+
data = HexaPDF::StreamData.new(proc { 'mydata' }, filter: :Crypt)
|
302
|
+
obj = @document.wrap({}, oid: 1, stream: data)
|
303
|
+
@handler.decrypt(obj)
|
304
|
+
assert_equal('mydata', obj.stream)
|
305
|
+
end
|
306
|
+
|
300
307
|
it "doesn't decrypt XRef streams" do
|
301
308
|
@obj[:Type] = :XRef
|
302
309
|
assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
|
@@ -343,6 +350,14 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
343
350
|
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
344
351
|
end
|
345
352
|
|
353
|
+
it "defers encrypting to a Crypt filter if specified" do
|
354
|
+
@stream.set_filter(:Crypt)
|
355
|
+
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
356
|
+
|
357
|
+
@stream.set_filter([:Crypt])
|
358
|
+
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
359
|
+
end
|
360
|
+
|
346
361
|
it "doesn't encrypt the /Contents key of signature dictionaries" do
|
347
362
|
@obj[:Type] = :Sig
|
348
363
|
@obj[:Contents] = "test"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hexapdf/filter/crypt'
|
5
|
+
|
6
|
+
describe HexaPDF::Filter::Crypt do
|
7
|
+
before do
|
8
|
+
@obj = HexaPDF::Filter::Crypt
|
9
|
+
@source = Fiber.new { "hallo" }
|
10
|
+
end
|
11
|
+
|
12
|
+
it "works with the Identity filter" do
|
13
|
+
assert_equal(@source, @obj.decoder(@source, nil))
|
14
|
+
assert_equal(@source, @obj.encoder(@source, {})) # sic: 'encoder'
|
15
|
+
assert_equal(@source, @obj.decoder(@source, {Name: :Identity}))
|
16
|
+
end
|
17
|
+
|
18
|
+
it "fails if crypt filter name is not Identity" do
|
19
|
+
assert_raises(HexaPDF::FilterError) { @obj.decoder(@source, {Name: :Other}) }
|
20
|
+
end
|
21
|
+
end
|
@@ -39,6 +39,11 @@ describe HexaPDF::Font::Type1::AFMParser do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
it "parses until EOF if no end token is found" do
|
43
|
+
io = StringIO.new("StartFontMetrics 4.1\nFontName Test")
|
44
|
+
assert_equal('Test', HexaPDF::Font::Type1::AFMParser.parse(io).font_name)
|
45
|
+
end
|
46
|
+
|
42
47
|
it "extracts kerning and ligature information" do
|
43
48
|
metrics = FONT_TIMES.metrics
|
44
49
|
glyph = metrics.character_metrics[:f]
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -531,11 +531,25 @@ describe HexaPDF::Parser do
|
|
531
531
|
assert_equal(6, @parser.load_object(@xref).value)
|
532
532
|
end
|
533
533
|
|
534
|
+
it "uses a security handler for decrypting indirect objects if necessary" do
|
535
|
+
handler = Minitest::Mock.new
|
536
|
+
handler.expect(:decrypt, HexaPDF::Object.new(:result, oid: 1), [HexaPDF::Object])
|
537
|
+
@document.instance_variable_set(:@security_handler, handler)
|
538
|
+
create_parser("1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
|
539
|
+
assert_equal(:result, @parser.load_object(@xref).value)
|
540
|
+
assert(handler.verify)
|
541
|
+
end
|
542
|
+
|
534
543
|
it "ignores parts where the starting line is split across lines" do
|
535
544
|
create_parser("1 0 obj\n5\nendobj\n1 0\nobj\n6\nendobj\ntrailer\n<</Size 1>>")
|
536
545
|
assert_equal(5, @parser.load_object(@xref).value)
|
537
546
|
end
|
538
547
|
|
548
|
+
it "handles the case when the specified object had an xref entry but is not found" do
|
549
|
+
create_parser("3 0 obj\n5\nendobj\ntrailer\n<</Size 1>>")
|
550
|
+
assert(@parser.load_object(@xref).null?)
|
551
|
+
end
|
552
|
+
|
539
553
|
it "handles cases where the line contains an invalid string that exceeds the read buffer" do
|
540
554
|
create_parser("(1" << "(abc" * 32188 << "\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
|
541
555
|
assert_equal(6, @parser.load_object(@xref).value)
|
@@ -568,6 +582,16 @@ describe HexaPDF::Parser do
|
|
568
582
|
assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
|
569
583
|
end
|
570
584
|
|
585
|
+
it "tries the trailer specified at the startxref position if no other is found" do
|
586
|
+
create_parser("1 0 obj\n5\nendobj\nquack xref trailer <</Size 1/Prev 5>>\nstartxref\n22\n%%EOF")
|
587
|
+
assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
|
588
|
+
end
|
589
|
+
|
590
|
+
it "fails if no trailer is found and the trailer specified at the startxref position is not valid" do
|
591
|
+
create_parser("1 0 obj\n5\nendobj\nquack trailer <</Size 1>>\nstartxref\n22\n%%EOF")
|
592
|
+
assert_raises(HexaPDF::MalformedPDFError) { @parser.reconstructed_revision.trailer }
|
593
|
+
end
|
594
|
+
|
571
595
|
it "fails if no valid trailer is found" do
|
572
596
|
create_parser("1 0 obj\n5\nendobj")
|
573
597
|
assert_raises(HexaPDF::MalformedPDFError) { @parser.load_object(@xref) }
|
@@ -58,6 +58,9 @@ describe HexaPDF::Serializer do
|
|
58
58
|
assert_serialized("0.000005", 0.000005)
|
59
59
|
assert_serialized("-0.000005", -0.000005)
|
60
60
|
assert_serialized("0.0", 0.0)
|
61
|
+
assert_raises(HexaPDF::Error) { @serializer.serialize(0.0 / 0) }
|
62
|
+
assert_raises(HexaPDF::Error) { @serializer.serialize(1.0 / 0) }
|
63
|
+
assert_raises(HexaPDF::Error) { @serializer.serialize(-1.0 / 0) }
|
61
64
|
end
|
62
65
|
|
63
66
|
it "serializes numerics" do
|
@@ -55,4 +55,26 @@ describe HexaPDF::Tokenizer do
|
|
55
55
|
assert_equal(HexaPDF::Tokenizer::NO_MORE_TOKENS, @tokenizer.next_integer_or_keyword)
|
56
56
|
end
|
57
57
|
|
58
|
+
describe "can correct some problems" do
|
59
|
+
describe "invalid inf/-inf/nan/-nan tokens" do
|
60
|
+
it "turns them into zeros" do
|
61
|
+
count = 0
|
62
|
+
on_correctable_error = lambda do |msg, pos|
|
63
|
+
count += 1
|
64
|
+
assert_match(/Invalid object, got token/, msg)
|
65
|
+
assert_equal(8, pos) if count == 2
|
66
|
+
false
|
67
|
+
end
|
68
|
+
tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf -Inf NaN -nan"),
|
69
|
+
on_correctable_error: on_correctable_error)
|
70
|
+
assert_equal([0, 0, 0, 0], 4.times.map { tokenizer.next_object })
|
71
|
+
assert_equal(4, count)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "raises an error if configured so" do
|
75
|
+
tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf"), on_correctable_error: proc { true })
|
76
|
+
assert_raises(HexaPDF::MalformedPDFError) { tokenizer.next_object }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
58
80
|
end
|
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.14.
|
43
|
+
<</Producer(HexaPDF version 0.14.4)>>
|
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.14.
|
75
|
+
<</Producer(HexaPDF version 0.14.4)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -71,6 +71,13 @@ describe HexaPDF::Type::XRefStream do
|
|
71
71
|
assert(section[10, 0].in_use?)
|
72
72
|
assert_equal(250, section[10, 0].pos)
|
73
73
|
end
|
74
|
+
|
75
|
+
it "fails if there is not enough data available" do
|
76
|
+
@obj[:Index] = [1, 2, 10, 4]
|
77
|
+
@obj[:W] = [1, 2, 1]
|
78
|
+
@obj.stream = "abcd"
|
79
|
+
assert_raises(HexaPDF::MalformedPDFError) { @obj.xref_section }
|
80
|
+
end
|
74
81
|
end
|
75
82
|
|
76
83
|
describe "trailer" do
|
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.14.
|
4
|
+
version: 0.14.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -267,6 +267,7 @@ files:
|
|
267
267
|
- lib/hexapdf/filter.rb
|
268
268
|
- lib/hexapdf/filter/ascii85_decode.rb
|
269
269
|
- lib/hexapdf/filter/ascii_hex_decode.rb
|
270
|
+
- lib/hexapdf/filter/crypt.rb
|
270
271
|
- lib/hexapdf/filter/encryption.rb
|
271
272
|
- lib/hexapdf/filter/flate_decode.rb
|
272
273
|
- lib/hexapdf/filter/lzw_decode.rb
|
@@ -507,6 +508,7 @@ files:
|
|
507
508
|
- test/hexapdf/filter/common.rb
|
508
509
|
- test/hexapdf/filter/test_ascii85_decode.rb
|
509
510
|
- test/hexapdf/filter/test_ascii_hex_decode.rb
|
511
|
+
- test/hexapdf/filter/test_crypt.rb
|
510
512
|
- test/hexapdf/filter/test_encryption.rb
|
511
513
|
- test/hexapdf/filter/test_flate_decode.rb
|
512
514
|
- test/hexapdf/filter/test_lzw_decode.rb
|