hexapdf 0.24.0 → 0.24.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/lib/hexapdf/cli/command.rb +2 -2
- data/lib/hexapdf/cli/info.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document.rb +7 -5
- data/lib/hexapdf/object.rb +7 -4
- data/lib/hexapdf/parser.rb +1 -1
- data/lib/hexapdf/revisions.rb +3 -0
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +2 -2
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/test_utils.rb +114 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +0 -1
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +11 -21
- data/test/hexapdf/content/test_canvas.rb +0 -1
- data/test/hexapdf/content/test_parser.rb +1 -2
- data/test/hexapdf/encryption/test_aes.rb +9 -9
- data/test/hexapdf/encryption/test_arc4.rb +2 -2
- data/test/hexapdf/encryption/test_security_handler.rb +1 -1
- data/test/hexapdf/filter/common.rb +0 -1
- data/test/hexapdf/filter/test_predictor.rb +0 -1
- data/test/hexapdf/font/true_type/common.rb +4 -8
- data/test/hexapdf/font/true_type/table/common.rb +18 -16
- data/test/hexapdf/font/true_type/test_font.rb +1 -1
- data/test/hexapdf/font/true_type/test_table.rb +4 -4
- data/test/hexapdf/layout/test_box.rb +0 -1
- data/test/hexapdf/layout/test_column_box.rb +0 -1
- data/test/hexapdf/layout/test_image_box.rb +0 -1
- data/test/hexapdf/layout/test_list_box.rb +0 -1
- data/test/hexapdf/layout/test_style.rb +0 -1
- data/test/hexapdf/layout/test_text_box.rb +0 -1
- data/test/hexapdf/layout/test_text_fragment.rb +0 -1
- data/test/hexapdf/layout/test_text_layouter.rb +1 -2
- data/test/hexapdf/test_composer.rb +0 -1
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_parser.rb +12 -5
- data/test/hexapdf/test_stream.rb +0 -2
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +0 -1
- data/test/hexapdf/type/acro_form/test_button_field.rb +0 -1
- data/test/hexapdf/type/acro_form/test_text_field.rb +0 -1
- data/test/hexapdf/type/signature/common.rb +55 -53
- data/test/hexapdf/type/test_form.rb +3 -4
- data/test/hexapdf/type/test_page.rb +1 -2
- data/test/test_helper.rb +1 -39
- metadata +4 -5
- data/test/hexapdf/content/common.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d7da895c4baff0d0e5114a6c423f3f6ca3de02004807d7f5a81c19e404edff1
|
4
|
+
data.tar.gz: 89c6dd4709077bf6c6ccb710addeccfdf23a4a75d37dda4357f36c25d5f8865d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5400d99a41fec977ba734bf487af5ce847bdea1f762a0f2df98b45f20952ee4059b08b75ff76ddb3e87db2191f1f7d18b01d9c1d8911b44fbdfc54ea2faa478
|
7
|
+
data.tar.gz: f70679b7844a27d2da4f208900309b1820c39174923ab7d0645edbeddaa60455bd359a6e355c254e8764de013078d199846ec5ac443de3d39dd1abc4ad93c890
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## 0.24.1 - 2022-08-11
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::TestUtils] module that contains helper methods useful for testing
|
6
|
+
various parts of HexaPDF
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
* All applicable places to only load the current version of PDF objects, to
|
11
|
+
avoid possible inconsistencies when working with files containing multiple
|
12
|
+
revisions
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
|
16
|
+
* Parsing of streams with an invalid length value that led to a parsing error
|
17
|
+
* [HexaPDF::Object#==] to only allow comparing simple values to non-indirect
|
18
|
+
objects and not also other HexaPDF::Object instances
|
19
|
+
|
20
|
+
|
1
21
|
## 0.24.0 - 2022-08-01
|
2
22
|
|
3
23
|
### Added
|
data/lib/hexapdf/cli/command.rb
CHANGED
@@ -244,7 +244,7 @@ module HexaPDF
|
|
244
244
|
compress_pages: @out_options.compress_pages,
|
245
245
|
prune_page_resources: @out_options.prune_page_resources)
|
246
246
|
if @out_options.streams != :preserve || @out_options.optimize_fonts
|
247
|
-
doc.each
|
247
|
+
doc.each do |obj|
|
248
248
|
optimize_stream(obj)
|
249
249
|
optimize_font(obj)
|
250
250
|
end
|
@@ -352,7 +352,7 @@ module HexaPDF
|
|
352
352
|
def remove_unused_pages(doc)
|
353
353
|
retained = doc.pages.each_with_object({}) {|page, h| h[page.data] = true }
|
354
354
|
retained[doc.pages.root.data] = true
|
355
|
-
doc.each
|
355
|
+
doc.each do |obj|
|
356
356
|
next unless obj.kind_of?(HexaPDF::Dictionary)
|
357
357
|
if (obj.type == :Pages || obj.type == :Page) && !retained.key?(obj.data)
|
358
358
|
doc.delete(obj)
|
data/lib/hexapdf/cli/info.rb
CHANGED
@@ -103,7 +103,7 @@ module HexaPDF
|
|
103
103
|
"#{correctable ? '(correctable)' : ''}"
|
104
104
|
end
|
105
105
|
doc.trailer.validate(auto_correct: true, &validation_block)
|
106
|
-
doc.each(
|
106
|
+
doc.each(only_loaded: false) do |obj|
|
107
107
|
indirect_object = obj
|
108
108
|
obj.validate(auto_correct: true, &validation_block)
|
109
109
|
end
|
data/lib/hexapdf/cli/optimize.rb
CHANGED
@@ -90,7 +90,7 @@ module HexaPDF
|
|
90
90
|
end
|
91
91
|
doc.catalog[:Pages] = page_tree
|
92
92
|
|
93
|
-
doc.each
|
93
|
+
doc.each do |obj, revision|
|
94
94
|
next unless obj.kind_of?(HexaPDF::Dictionary)
|
95
95
|
if (obj.type == :Pages || obj.type == :Page) && !retained.key?(obj.data)
|
96
96
|
revision.delete(obj)
|
@@ -88,7 +88,7 @@ module HexaPDF
|
|
88
88
|
# Note that only real images are yielded which means, for example, that images used as soft
|
89
89
|
# mask are not.
|
90
90
|
def each(&block)
|
91
|
-
images = @document.each
|
91
|
+
images = @document.each.select do |obj|
|
92
92
|
next unless obj.kind_of?(HexaPDF::Dictionary)
|
93
93
|
obj[:Subtype] == :Image && !obj[:ImageMask]
|
94
94
|
end
|
data/lib/hexapdf/document.rb
CHANGED
@@ -386,8 +386,10 @@ module HexaPDF
|
|
386
386
|
#
|
387
387
|
# Yields every object and the revision it is in.
|
388
388
|
#
|
389
|
-
# If +only_current+ is +true+, only the current version of each object is yielded, otherwise
|
390
|
-
#
|
389
|
+
# If +only_current+ is +true+, only the current version of each object is yielded, otherwise all
|
390
|
+
# objects from all revisions. *Note* that it is normally not necessary or useful to retrieve all
|
391
|
+
# objects from all revisions and if it is still done that care has to be taken to avoid an
|
392
|
+
# invalid document state.
|
391
393
|
#
|
392
394
|
# If +only_loaded+ is +true+, only the already loaded objects are yielded.
|
393
395
|
#
|
@@ -602,15 +604,15 @@ module HexaPDF
|
|
602
604
|
signatures.add(file_or_io, handler, signature: signature, write_options: write_options)
|
603
605
|
end
|
604
606
|
|
605
|
-
# Validates all objects, or, if +only_loaded+ is +true+, only loaded objects, with
|
606
|
-
# auto-correction, and returns +true+ if everything is fine.
|
607
|
+
# Validates all current objects, or, if +only_loaded+ is +true+, only loaded objects, with
|
608
|
+
# optional auto-correction, and returns +true+ if everything is fine.
|
607
609
|
#
|
608
610
|
# If a block is given, it is called on validation problems.
|
609
611
|
#
|
610
612
|
# See HexaPDF::Object#validate for more information.
|
611
613
|
def validate(auto_correct: true, only_loaded: false, &block) #:yield: msg, correctable, object
|
612
614
|
result = trailer.validate(auto_correct: auto_correct, &block)
|
613
|
-
each(
|
615
|
+
each(only_loaded: only_loaded) do |obj|
|
614
616
|
result &&= obj.validate(auto_correct: auto_correct, &block)
|
615
617
|
end
|
616
618
|
result
|
data/lib/hexapdf/object.rb
CHANGED
@@ -333,12 +333,15 @@ module HexaPDF
|
|
333
333
|
(oid == other.oid ? gen <=> other.gen : oid <=> other.oid)
|
334
334
|
end
|
335
335
|
|
336
|
-
# Returns +true+
|
337
|
-
#
|
338
|
-
#
|
336
|
+
# Returns +true+ in the following cases:
|
337
|
+
#
|
338
|
+
# * The other object is an Object and wraps the same #data structure.
|
339
|
+
# * The other object is a Reference with the same oid/gen.
|
340
|
+
# * This object is not indirect and the other object is not an Object and equal to the value of
|
341
|
+
# this object.
|
339
342
|
def ==(other)
|
340
343
|
(other.kind_of?(Object) && data == other.data) || (other.kind_of?(Reference) && other == self) ||
|
341
|
-
(!indirect? && other == data.value)
|
344
|
+
(!indirect? && !other.kind_of?(Object) && other == data.value)
|
342
345
|
end
|
343
346
|
|
344
347
|
# Returns +true+ if the other object references the same PDF object as this object.
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -172,7 +172,7 @@ module HexaPDF
|
|
172
172
|
end
|
173
173
|
@tokenizer.pos = pos + length rescue pos
|
174
174
|
|
175
|
-
tok = @tokenizer.next_token
|
175
|
+
tok = @tokenizer.next_token rescue nil
|
176
176
|
unless tok.kind_of?(Tokenizer::Token) && tok == 'endstream'
|
177
177
|
maybe_raise("Invalid stream length, keyword endstream not found", pos: @tokenizer.pos)
|
178
178
|
@tokenizer.pos = pos
|
data/lib/hexapdf/revisions.rb
CHANGED
@@ -233,6 +233,9 @@ module HexaPDF
|
|
233
233
|
# * Additionally, there may also be objects with the same object number but different
|
234
234
|
# generation numbers in different revisions, e.g. one object with oid/gen [3,0] and one with
|
235
235
|
# oid/gen [3,1].
|
236
|
+
#
|
237
|
+
# *Note* that setting +only_current+ to +false+ is normally not necessary and should not be
|
238
|
+
# done. If it is still done, one has to take care to avoid an invalid document state.
|
236
239
|
def each_object(only_current: true, only_loaded: false, &block)
|
237
240
|
unless block_given?
|
238
241
|
return to_enum(__method__, only_current: only_current, only_loaded: only_loaded)
|
@@ -72,7 +72,7 @@ module HexaPDF
|
|
72
72
|
else
|
73
73
|
dereference_all(@doc.trailer)
|
74
74
|
@result = []
|
75
|
-
@doc.each
|
75
|
+
@doc.each do |obj|
|
76
76
|
if !@seen.key?(obj.data) && obj.type != :ObjStm && obj.type != :XRef
|
77
77
|
@result << obj
|
78
78
|
elsif obj.kind_of?(HexaPDF::Stream) && (val = obj.value[:Length]) &&
|
@@ -92,7 +92,7 @@ module HexaPDF
|
|
92
92
|
elsif xref_streams != :preserve
|
93
93
|
process_xref_streams(doc, xref_streams)
|
94
94
|
else
|
95
|
-
doc.each(
|
95
|
+
doc.each(&method(:delete_fields_with_defaults))
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -193,7 +193,7 @@ module HexaPDF
|
|
193
193
|
def self.process_xref_streams(doc, method)
|
194
194
|
case method
|
195
195
|
when :delete
|
196
|
-
doc.each
|
196
|
+
doc.each do |obj, rev|
|
197
197
|
if obj.type == :XRef
|
198
198
|
rev.delete(obj)
|
199
199
|
else
|
data/lib/hexapdf/task.rb
CHANGED
@@ -58,7 +58,7 @@ module HexaPDF
|
|
58
58
|
#
|
59
59
|
# doc = HexaPDF::Document.new
|
60
60
|
# doc.config['task.map'][:validate] = lambda do |doc|
|
61
|
-
# doc.each
|
61
|
+
# doc.each {|obj| obj.validate || raise "Invalid object #{obj}"}
|
62
62
|
# end
|
63
63
|
module Task
|
64
64
|
|
@@ -0,0 +1,114 @@
|
|
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-2022 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/content/processor'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
|
41
|
+
# Contains various helper methods for testing HexaPDF
|
42
|
+
module TestUtils
|
43
|
+
|
44
|
+
# Can be used to to record operators parsed from content streams.
|
45
|
+
class OperatorRecorder < HexaPDF::Content::Processor
|
46
|
+
|
47
|
+
undef :paint_xobject
|
48
|
+
|
49
|
+
attr_reader :recorded_ops
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
super
|
53
|
+
operators.clear
|
54
|
+
@recorded_ops = []
|
55
|
+
end
|
56
|
+
|
57
|
+
def respond_to_missing?(*)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def method_missing(msg, *params)
|
62
|
+
@recorded_ops << (params.empty? ? [msg] : [msg, params])
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
# Asserts that the content string contains the operators.
|
68
|
+
def assert_operators(content, operators, only_names: false, range: 0..-1)
|
69
|
+
processor = OperatorRecorder.new
|
70
|
+
HexaPDF::Content::Parser.new.parse(content, processor)
|
71
|
+
result = processor.recorded_ops[range]
|
72
|
+
result.map!(&:first) if only_names
|
73
|
+
assert_equal(operators, result)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Asserts that the method +name+ of +object+ gets invoked with the +expected_values+ when
|
77
|
+
# executing the block. +expected_values+ should contain arrays of arguments, one array for each
|
78
|
+
# invocation of the method.
|
79
|
+
def assert_method_invoked(object, name, *expected_values, check_block: false)
|
80
|
+
args = []
|
81
|
+
block = []
|
82
|
+
object.define_singleton_method(name) {|*la, &lb| args << la; block << lb }
|
83
|
+
yield
|
84
|
+
assert_equal(expected_values, args, "Incorrect arguments for #{object.class}##{name}")
|
85
|
+
block.each do |block_arg|
|
86
|
+
assert_kind_of(Proc, block_arg, "Missing block for #{object.class}##{name}") if check_block
|
87
|
+
end
|
88
|
+
ensure
|
89
|
+
object.singleton_class.send(:remove_method, name)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Creates a fiber that yields the given string in +len+ length parts.
|
93
|
+
def feeder(string, len = string.length)
|
94
|
+
Fiber.new do
|
95
|
+
until string.empty?
|
96
|
+
Fiber.yield(string.slice!(0, len).force_encoding('BINARY'))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Collects the result from the HexaPDF::Filter source into a binary string.
|
102
|
+
def collector(source)
|
103
|
+
str = ''.b
|
104
|
+
while source.alive? && (data = source.resume)
|
105
|
+
str << data
|
106
|
+
end
|
107
|
+
str
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
Minitest::Spec.include(HexaPDF::TestUtils)
|
data/lib/hexapdf/version.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
|
-
require_relative '../common'
|
5
4
|
require 'hexapdf/document'
|
6
5
|
require 'hexapdf/content'
|
7
6
|
require 'hexapdf/content/graphic_object'
|
@@ -40,13 +39,6 @@ describe HexaPDF::Content::GraphicObject::SolidArc do
|
|
40
39
|
end
|
41
40
|
|
42
41
|
describe "draw" do
|
43
|
-
def operators(content)
|
44
|
-
processor = TestHelper::OperatorRecorder.new
|
45
|
-
parser = HexaPDF::Content::Parser.new
|
46
|
-
parser.parse(content, processor)
|
47
|
-
processor.recorded_ops
|
48
|
-
end
|
49
|
-
|
50
42
|
before do
|
51
43
|
@doc = HexaPDF::Document.new
|
52
44
|
@doc.config['graphic_object.arc.max_curves'] = 4
|
@@ -56,31 +48,29 @@ describe HexaPDF::Content::GraphicObject::SolidArc do
|
|
56
48
|
|
57
49
|
it "draws a disk" do
|
58
50
|
@canvas.draw(:solid_arc)
|
59
|
-
|
60
|
-
|
61
|
-
|
51
|
+
assert_operators(@canvas.contents,
|
52
|
+
[:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
53
|
+
only_names: true)
|
62
54
|
end
|
63
55
|
|
64
56
|
it "draws a sector" do
|
65
57
|
@canvas.draw(:solid_arc, end_angle: 90)
|
66
|
-
|
67
|
-
|
68
|
-
ops.map(&:first))
|
58
|
+
assert_operators(@canvas.contents, [:move_to, :line_to, :curve_to, :close_subpath],
|
59
|
+
only_names: true)
|
69
60
|
end
|
70
61
|
|
71
62
|
it "draws an annulus" do
|
72
63
|
@canvas.draw(:solid_arc, inner_a: 5, inner_b: 5)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
64
|
+
assert_operators(@canvas.contents,
|
65
|
+
[:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath,
|
66
|
+
:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
67
|
+
only_names: true)
|
77
68
|
end
|
78
69
|
|
79
70
|
it "draws an annular sector" do
|
80
71
|
@canvas.draw(:solid_arc, inner_a: 5, inner_b: 5, end_angle: 90)
|
81
|
-
|
82
|
-
|
83
|
-
ops.map(&:first))
|
72
|
+
assert_operators(@canvas.contents, [:move_to, :curve_to, :line_to, :curve_to, :close_subpath],
|
73
|
+
only_names: true)
|
84
74
|
end
|
85
75
|
end
|
86
76
|
end
|
@@ -4,7 +4,6 @@ require 'test_helper'
|
|
4
4
|
require 'hexapdf/content/parser'
|
5
5
|
require 'hexapdf/content/processor'
|
6
6
|
require_relative '../common_tokenizer_tests'
|
7
|
-
require_relative 'common'
|
8
7
|
|
9
8
|
describe HexaPDF::Content::Tokenizer do
|
10
9
|
include CommonTokenizerTests
|
@@ -16,7 +15,7 @@ end
|
|
16
15
|
|
17
16
|
describe HexaPDF::Content::Parser do
|
18
17
|
before do
|
19
|
-
@processor =
|
18
|
+
@processor = HexaPDF::TestUtils::OperatorRecorder.new
|
20
19
|
@parser = HexaPDF::Content::Parser.new
|
21
20
|
end
|
22
21
|
|
@@ -67,7 +67,7 @@ describe HexaPDF::Encryption::AES do
|
|
67
67
|
it "returns the padded result with IV on encryption_fiber" do
|
68
68
|
@padding_data.each do |data|
|
69
69
|
result = @algorithm_class.encryption_fiber('some key' * 2, Fiber.new { data[:plain] })
|
70
|
-
result =
|
70
|
+
result = collector(result)
|
71
71
|
assert_equal(data[:length], result.length)
|
72
72
|
assert_equal(data[:cipher_padding][-16, 16], result[-16, 16])
|
73
73
|
end
|
@@ -77,14 +77,14 @@ describe HexaPDF::Encryption::AES do
|
|
77
77
|
@padding_data.each do |data|
|
78
78
|
result = @algorithm_class.decryption_fiber('some key' * 2,
|
79
79
|
Fiber.new { 'iv' * 8 + data[:cipher_padding] })
|
80
|
-
result =
|
80
|
+
result = collector(result)
|
81
81
|
assert_equal(data[:plain], result)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
it "encryption works with multiple yielded strings" do
|
86
86
|
f = Fiber.new { Fiber.yield('a' * 40); Fiber.yield('test'); "b" * 20 }
|
87
|
-
result =
|
87
|
+
result = collector(@algorithm_class.encryption_fiber('some key' * 2, f))
|
88
88
|
assert_equal('a' * 40 << 'test' << 'b' * 20, result[16..-17])
|
89
89
|
end
|
90
90
|
|
@@ -96,32 +96,32 @@ describe HexaPDF::Encryption::AES do
|
|
96
96
|
Fiber.yield('a' * 20)
|
97
97
|
8.chr * 8
|
98
98
|
end
|
99
|
-
result =
|
99
|
+
result = collector(@algorithm_class.decryption_fiber('some key' * 2, f))
|
100
100
|
assert_equal('a' * 40, result)
|
101
101
|
end
|
102
102
|
|
103
103
|
it "decryption works if the padding is invalid" do
|
104
104
|
f = Fiber.new { 'a' * 16 }
|
105
|
-
result =
|
105
|
+
result = collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
106
106
|
assert_equal('', result)
|
107
107
|
|
108
108
|
f = Fiber.new { 'a' * 32 }
|
109
|
-
result =
|
109
|
+
result = collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
110
110
|
assert_equal('a' * 16, result)
|
111
111
|
|
112
112
|
f = Fiber.new { 'a' * 31 << "\x00" }
|
113
|
-
result =
|
113
|
+
result = collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
114
114
|
assert_equal('a' * 15 << "\x00", result)
|
115
115
|
|
116
116
|
f = Fiber.new { 'a' * 29 << "\x00\x01\x03" }
|
117
|
-
result =
|
117
|
+
result = collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
118
118
|
assert_equal('a' * 13 << "\x00\x01\x03", result)
|
119
119
|
end
|
120
120
|
|
121
121
|
it "fails on decryption if not enough bytes are provided" do
|
122
122
|
[4, 20, 40].each do |length|
|
123
123
|
assert_raises(HexaPDF::EncryptionError) do
|
124
|
-
|
124
|
+
collector(@algorithm_class.decryption_fiber('some' * 4,
|
125
125
|
Fiber.new { 'a' * length }))
|
126
126
|
end
|
127
127
|
end
|
@@ -31,9 +31,9 @@ describe HexaPDF::Encryption::ARC4 do
|
|
31
31
|
it "correctly uses klass.encryption_fiber and klass.decryption_fiber" do
|
32
32
|
f = Fiber.new { Fiber.yield('first'); Fiber.yield(''); 'second' }
|
33
33
|
assert_equal('mykeyfirstsecond',
|
34
|
-
|
34
|
+
collector(@algorithm_class.encryption_fiber('mykey', f)))
|
35
35
|
f = Fiber.new { Fiber.yield('first'); 'second' }
|
36
36
|
assert_equal('mykeyfirstsecond',
|
37
|
-
|
37
|
+
collector(@algorithm_class.decryption_fiber('mykey', f)))
|
38
38
|
end
|
39
39
|
end
|
@@ -344,7 +344,7 @@ describe HexaPDF::Encryption::SecurityHandler do
|
|
344
344
|
end
|
345
345
|
|
346
346
|
it "encrypts streams" do
|
347
|
-
result =
|
347
|
+
result = collector(@handler.encrypt_stream(@stream))
|
348
348
|
@stream.stream = HexaPDF::StreamData.new(proc { result })
|
349
349
|
assert_equal('string', @handler.decrypt(@stream).stream)
|
350
350
|
end
|
@@ -7,7 +7,6 @@ require 'test_helper'
|
|
7
7
|
# The filter object needs to be available in the @obj variable and the @all_test_cases variable
|
8
8
|
# needs to hold an array of test cases, i.e. [decoded, encoded] objects.
|
9
9
|
module CommonFilterTests
|
10
|
-
include TestHelper
|
11
10
|
|
12
11
|
TEST_BIG_STR = ''.b
|
13
12
|
TEST_BIG_STR << [rand(2**32)].pack('N') while TEST_BIG_STR.length < 2**16
|
@@ -2,16 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'hexapdf/font/true_type/table'
|
4
4
|
|
5
|
-
|
5
|
+
class TrueTypeTestTable < HexaPDF::Font::TrueType::Table
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
attr_reader :data
|
10
|
-
|
11
|
-
def parse_table
|
12
|
-
@data = io.read(directory_entry.length)
|
13
|
-
end
|
7
|
+
attr_reader :data
|
14
8
|
|
9
|
+
def parse_table
|
10
|
+
@data = io.read(directory_entry.length)
|
15
11
|
end
|
16
12
|
|
17
13
|
end
|
@@ -4,24 +4,26 @@ require 'test_helper'
|
|
4
4
|
require 'stringio'
|
5
5
|
require 'hexapdf/font/true_type'
|
6
6
|
|
7
|
-
module
|
7
|
+
module HexaPDF
|
8
|
+
module TestUtils
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
def set_up_stub_true_type_font(initial_data = ''.b, register_vars: true)
|
11
|
+
font = ::Object.new
|
12
|
+
font.define_singleton_method(:io) { @io ||= StringIO.new(initial_data) }
|
13
|
+
font.define_singleton_method(:config) { @config ||= {} }
|
14
|
+
entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('mock', 0, 0, initial_data.length)
|
15
|
+
@font, @entry = font, entry if register_vars
|
16
|
+
[font, entry]
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def create_table(name, data = nil, standalone: false)
|
20
|
+
font, entry = standalone ? set_up_stub_true_type_font(register_vars: false) : [@font, @entry]
|
21
|
+
if data
|
22
|
+
font.io.string = data
|
23
|
+
entry.length = font.io.length
|
24
|
+
end
|
25
|
+
HexaPDF::Font::TrueType::Table.const_get(name).new(font, entry)
|
23
26
|
end
|
24
|
-
HexaPDF::Font::TrueType::Table.const_get(name).new(font, entry)
|
25
|
-
end
|
26
27
|
|
28
|
+
end
|
27
29
|
end
|
@@ -10,7 +10,7 @@ describe HexaPDF::Font::TrueType::Font do
|
|
10
10
|
@io = StringIO.new("TEST\x00\x01\x00\x00\x00\x00\x00\x00" \
|
11
11
|
"TEST----\x00\x00\x00\x1C\x00\x00\x00\x05ENTRY".b)
|
12
12
|
@font = HexaPDF::Font::TrueType::Font.new(@io)
|
13
|
-
@font.config['font.true_type.table_mapping'][:TEST] =
|
13
|
+
@font.config['font.true_type.table_mapping'][:TEST] = TrueTypeTestTable.name
|
14
14
|
end
|
15
15
|
|
16
16
|
describe "[]" do
|
@@ -15,7 +15,7 @@ describe HexaPDF::Font::TrueType::Table do
|
|
15
15
|
|
16
16
|
describe "initialize" do
|
17
17
|
it "reads the data from the associated file" do
|
18
|
-
table =
|
18
|
+
table = TrueTypeTestTable.new(@file, @entry)
|
19
19
|
assert_equal(@file.io.string, table.data)
|
20
20
|
end
|
21
21
|
end
|
@@ -25,7 +25,7 @@ describe HexaPDF::Font::TrueType::Table do
|
|
25
25
|
@file.io.string = 254.chr * 17 + 0.chr * 3
|
26
26
|
@entry.checksum = (0xfefefefe * 4 + 0xfe000000) % 2**32
|
27
27
|
@entry.length = @file.io.string.length
|
28
|
-
table =
|
28
|
+
table = TrueTypeTestTable.new(@file, @entry)
|
29
29
|
assert(table.checksum_valid?)
|
30
30
|
end
|
31
31
|
end
|
@@ -34,14 +34,14 @@ describe HexaPDF::Font::TrueType::Table do
|
|
34
34
|
it "works for unsigned values" do
|
35
35
|
@file.io.string = [1, 20480].pack('nn')
|
36
36
|
@entry.length = @file.io.string.length
|
37
|
-
table =
|
37
|
+
table = TrueTypeTestTable.new(@file, @entry)
|
38
38
|
assert_equal(1 + Rational(20480, 65536), table.send(:read_fixed))
|
39
39
|
end
|
40
40
|
|
41
41
|
it "works for signed values" do
|
42
42
|
@file.io.string = [-1, 20480].pack('nn')
|
43
43
|
@entry.length = @file.io.string.length
|
44
|
-
table =
|
44
|
+
table = TrueTypeTestTable.new(@file, @entry)
|
45
45
|
assert_equal(-1 + Rational(20480, 65536), table.send(:read_fixed))
|
46
46
|
end
|
47
47
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/layout'
|
5
5
|
require 'hexapdf/document'
|
6
|
-
require_relative "../content/common"
|
7
6
|
|
8
7
|
module TestTextLayouterHelpers
|
9
8
|
def boxes(*dims)
|
@@ -707,7 +706,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
707
706
|
|
708
707
|
describe "Result#draw" do
|
709
708
|
def assert_positions(content, positions)
|
710
|
-
processor =
|
709
|
+
processor = HexaPDF::TestUtils::OperatorRecorder.new
|
711
710
|
HexaPDF::Content::Parser.new.parse(content, processor)
|
712
711
|
result = processor.recorded_ops
|
713
712
|
leading = (result.select {|name, _| name == :set_leading } || [0]).map(&:last).flatten.first
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -196,7 +196,7 @@ describe HexaPDF::Object do
|
|
196
196
|
refute_equal(obj, 5)
|
197
197
|
obj.data.oid = 0
|
198
198
|
assert_equal(obj, 5)
|
199
|
-
|
199
|
+
refute_equal(obj, HexaPDF::Object.new(5))
|
200
200
|
end
|
201
201
|
|
202
202
|
it "works correctly as hash key, is interchangable in this regard with Reference objects" do
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -86,19 +86,19 @@ describe HexaPDF::Parser do
|
|
86
86
|
it "handles keyword stream followed only by CR without LF" do
|
87
87
|
create_parser("1 0 obj<</Length 2>> stream\r12\nendstream endobj")
|
88
88
|
*, stream = @parser.parse_indirect_object
|
89
|
-
assert_equal('12',
|
89
|
+
assert_equal('12', collector(stream.fiber))
|
90
90
|
end
|
91
91
|
|
92
92
|
it "handles keyword stream followed by space and CR or LF" do
|
93
93
|
create_parser("1 0 obj<</Length 2>> stream \n12\nendstream endobj")
|
94
94
|
*, stream = @parser.parse_indirect_object
|
95
|
-
assert_equal('12',
|
95
|
+
assert_equal('12', collector(stream.fiber))
|
96
96
|
end
|
97
97
|
|
98
98
|
it "handles keyword stream followed by space and CR LF" do
|
99
99
|
create_parser("1 0 obj<</Length 2>> stream \r\n12\nendstream endobj")
|
100
100
|
*, stream = @parser.parse_indirect_object
|
101
|
-
assert_equal('12',
|
101
|
+
assert_equal('12', collector(stream.fiber))
|
102
102
|
end
|
103
103
|
|
104
104
|
it "handles invalid indirect object value consisting of number followed by endobj without space" do
|
@@ -117,7 +117,14 @@ describe HexaPDF::Parser do
|
|
117
117
|
create_parser("1 0 obj<</Length 4>> stream\n12endstream endobj")
|
118
118
|
obj, _, _, stream = @parser.parse_indirect_object
|
119
119
|
assert_equal(2, obj[:Length])
|
120
|
-
assert_equal('12',
|
120
|
+
assert_equal('12', collector(stream.fiber))
|
121
|
+
end
|
122
|
+
|
123
|
+
it "recovers from an incorrect stream length value which leads to a parsing error" do
|
124
|
+
create_parser("1 0 obj<</Length 2>> stream\n12(ab\nendstream endobj")
|
125
|
+
obj, _, _, stream = @parser.parse_indirect_object
|
126
|
+
assert_equal(5, obj[:Length])
|
127
|
+
assert_equal('12(ab', collector(stream.fiber))
|
121
128
|
end
|
122
129
|
|
123
130
|
it "recovers from an invalid stream length value" do
|
@@ -125,7 +132,7 @@ describe HexaPDF::Parser do
|
|
125
132
|
@document.add([5], oid: 2)
|
126
133
|
obj, _, _, stream = @parser.parse_indirect_object
|
127
134
|
assert_equal(2, obj[:Length])
|
128
|
-
assert_equal('12',
|
135
|
+
assert_equal('12', collector(stream.fiber))
|
129
136
|
end
|
130
137
|
|
131
138
|
it "works even if the keyword endobj is missing or mangled" do
|
data/test/hexapdf/test_stream.rb
CHANGED
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.24.
|
43
|
+
<</Producer(HexaPDF version 0.24.1)>>
|
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.24.
|
75
|
+
<</Producer(HexaPDF version 0.24.1)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -1,71 +1,73 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
|
3
|
-
module
|
3
|
+
module HexaPDF
|
4
|
+
module TestUtils
|
4
5
|
|
5
|
-
|
6
|
+
class Certificates
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def ca_key
|
9
|
+
@ca_key ||= OpenSSL::PKey::RSA.new(512)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def ca_certificate
|
13
|
+
@ca_certificate ||=
|
14
|
+
begin
|
15
|
+
ca_name = OpenSSL::X509::Name.parse('/C=AT/O=HexaPDF/CN=HexaPDF Test Root CA')
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
ca_cert = OpenSSL::X509::Certificate.new
|
18
|
+
ca_cert.serial = 0
|
19
|
+
ca_cert.version = 2
|
20
|
+
ca_cert.not_before = Time.now - 86400
|
21
|
+
ca_cert.not_after = Time.now + 86400
|
22
|
+
ca_cert.public_key = ca_key.public_key
|
23
|
+
ca_cert.subject = ca_name
|
24
|
+
ca_cert.issuer = ca_name
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
27
|
+
extension_factory.subject_certificate = ca_cert
|
28
|
+
extension_factory.issuer_certificate = ca_cert
|
29
|
+
ca_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
30
|
+
ca_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
|
31
|
+
ca_cert.add_extension(extension_factory.create_extension('keyUsage', 'cRLSign,keyCertSign', true))
|
32
|
+
ca_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
ca_cert
|
35
|
+
end
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def signer_key
|
39
|
+
@signer_key ||= OpenSSL::PKey::RSA.new(512)
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
def signer_certificate
|
43
|
+
@signer_certificate ||=
|
44
|
+
begin
|
45
|
+
name = OpenSSL::X509::Name.parse('/CN=signer/DC=gettalong')
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
signer_cert = OpenSSL::X509::Certificate.new
|
48
|
+
signer_cert.serial = 2
|
49
|
+
signer_cert.version = 2
|
50
|
+
signer_cert.not_before = Time.now - 86400
|
51
|
+
signer_cert.not_after = Time.now + 86400
|
52
|
+
signer_cert.public_key = signer_key.public_key
|
53
|
+
signer_cert.subject = name
|
54
|
+
signer_cert.issuer = ca_certificate.subject
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
57
|
+
extension_factory.subject_certificate = signer_cert
|
58
|
+
extension_factory.issuer_certificate = ca_certificate
|
59
|
+
signer_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
|
60
|
+
signer_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE'))
|
61
|
+
signer_cert.add_extension(extension_factory.create_extension('keyUsage', 'digitalSignature'))
|
62
|
+
signer_cert.sign(ca_key, OpenSSL::Digest.new('SHA1'))
|
63
|
+
|
64
|
+
signer_cert
|
65
|
+
end
|
66
|
+
end
|
62
67
|
|
63
|
-
signer_cert
|
64
|
-
end
|
65
68
|
end
|
66
69
|
|
67
70
|
end
|
68
|
-
|
69
71
|
end
|
70
72
|
|
71
|
-
CERTIFICATES =
|
73
|
+
CERTIFICATES = HexaPDF::TestUtils::Certificates.new
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
|
-
require_relative '../content/common'
|
5
4
|
require 'hexapdf/document'
|
6
5
|
require 'hexapdf/type/form'
|
7
6
|
|
@@ -70,7 +69,7 @@ describe HexaPDF::Type::Form do
|
|
70
69
|
describe "process_contents" do
|
71
70
|
it "parses the contents and processes it" do
|
72
71
|
@form.stream = '10 w'
|
73
|
-
processor =
|
72
|
+
processor = HexaPDF::TestUtils::OperatorRecorder.new
|
74
73
|
@form.process_contents(processor)
|
75
74
|
assert_equal([[:set_line_width, [10]]], processor.recorded_ops)
|
76
75
|
assert_nil(@form[:Resources])
|
@@ -82,7 +81,7 @@ describe HexaPDF::Type::Form do
|
|
82
81
|
|
83
82
|
it "uses the provided resources if it has no resources itself" do
|
84
83
|
resources = @doc.wrap({}, type: :XXResources)
|
85
|
-
processor =
|
84
|
+
processor = HexaPDF::TestUtils::OperatorRecorder.new
|
86
85
|
@form.process_contents(processor, original_resources: resources)
|
87
86
|
assert_same(resources, processor.resources)
|
88
87
|
end
|
@@ -91,7 +90,7 @@ describe HexaPDF::Type::Form do
|
|
91
90
|
describe "canvas" do
|
92
91
|
# Asserts that the form's contents contains the operators.
|
93
92
|
def assert_operators(form, operators)
|
94
|
-
processor =
|
93
|
+
processor = HexaPDF::TestUtils::OperatorRecorder.new
|
95
94
|
form.process_contents(processor)
|
96
95
|
assert_equal(operators, processor.recorded_ops)
|
97
96
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
|
-
require_relative '../content/common'
|
5
4
|
require 'stringio'
|
6
5
|
require 'hexapdf/document'
|
7
6
|
require 'hexapdf/type/page'
|
@@ -27,7 +26,7 @@ describe HexaPDF::Type::Page do
|
|
27
26
|
|
28
27
|
# Asserts that the page's contents contains the operators.
|
29
28
|
def assert_page_operators(page, operators)
|
30
|
-
processor =
|
29
|
+
processor = HexaPDF::TestUtils::OperatorRecorder.new
|
31
30
|
page.process_contents(processor)
|
32
31
|
assert_equal(operators, processor.recorded_ops)
|
33
32
|
end
|
data/test/test_helper.rb
CHANGED
@@ -14,6 +14,7 @@ gem 'minitest'
|
|
14
14
|
require 'minitest/autorun'
|
15
15
|
require 'fiber'
|
16
16
|
require 'zlib'
|
17
|
+
require 'hexapdf/test_utils'
|
17
18
|
|
18
19
|
TEST_DATA_DIR = File.join(__dir__, 'data')
|
19
20
|
MINIMAL_PDF = File.binread(File.join(TEST_DATA_DIR, 'minimal.pdf')).freeze
|
@@ -21,42 +22,3 @@ MINIMAL_PDF = File.binread(File.join(TEST_DATA_DIR, 'minimal.pdf')).freeze
|
|
21
22
|
Minitest::Test.make_my_diffs_pretty!
|
22
23
|
|
23
24
|
ENV['TZ'] = 'UTC'
|
24
|
-
|
25
|
-
module TestHelper
|
26
|
-
|
27
|
-
# Asserts that the method +name+ of +object+ gets invoked with the +expected_values+ when
|
28
|
-
# executing the block. +expected_values+ should contain arrays of arguments, one array for each
|
29
|
-
# invocation of the method.
|
30
|
-
def assert_method_invoked(object, name, *expected_values, check_block: false)
|
31
|
-
args = []
|
32
|
-
block = []
|
33
|
-
object.define_singleton_method(name) {|*la, &lb| args << la; block << lb }
|
34
|
-
yield
|
35
|
-
assert_equal(expected_values, args, "Incorrect arguments for #{object.class}##{name}")
|
36
|
-
block.each do |block_arg|
|
37
|
-
assert_kind_of(Proc, block_arg, "Missing block for #{object.class}##{name}") if check_block
|
38
|
-
end
|
39
|
-
ensure
|
40
|
-
object.singleton_class.send(:remove_method, name)
|
41
|
-
end
|
42
|
-
|
43
|
-
module_function
|
44
|
-
|
45
|
-
def feeder(string, len = string.length)
|
46
|
-
Fiber.new do
|
47
|
-
until string.empty?
|
48
|
-
Fiber.yield(string.slice!(0, len).force_encoding('BINARY'))
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def collector(source)
|
54
|
-
str = ''.force_encoding('BINARY')
|
55
|
-
while source.alive? && (data = source.resume)
|
56
|
-
str << data
|
57
|
-
end
|
58
|
-
str
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
Minitest::Spec.include(TestHelper)
|
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.24.
|
4
|
+
version: 0.24.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -379,6 +379,7 @@ files:
|
|
379
379
|
- lib/hexapdf/task.rb
|
380
380
|
- lib/hexapdf/task/dereference.rb
|
381
381
|
- lib/hexapdf/task/optimize.rb
|
382
|
+
- lib/hexapdf/test_utils.rb
|
382
383
|
- lib/hexapdf/tokenizer.rb
|
383
384
|
- lib/hexapdf/type.rb
|
384
385
|
- lib/hexapdf/type/acro_form.rb
|
@@ -513,7 +514,6 @@ files:
|
|
513
514
|
- test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf
|
514
515
|
- test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf
|
515
516
|
- test/hexapdf/common_tokenizer_tests.rb
|
516
|
-
- test/hexapdf/content/common.rb
|
517
517
|
- test/hexapdf/content/graphic_object/test_arc.rb
|
518
518
|
- test/hexapdf/content/graphic_object/test_endpoint_arc.rb
|
519
519
|
- test/hexapdf/content/graphic_object/test_geom2d.rb
|
@@ -698,8 +698,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
698
698
|
- !ruby/object:Gem::Version
|
699
699
|
version: '0'
|
700
700
|
requirements: []
|
701
|
-
|
702
|
-
rubygems_version: 2.7.6.2
|
701
|
+
rubygems_version: 3.2.32
|
703
702
|
signing_key:
|
704
703
|
specification_version: 4
|
705
704
|
summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
require 'hexapdf/content/processor'
|
5
|
-
|
6
|
-
module TestHelper
|
7
|
-
|
8
|
-
# Can be used to to record operators parsed from content streams.
|
9
|
-
class OperatorRecorder < HexaPDF::Content::Processor
|
10
|
-
|
11
|
-
undef :paint_xobject
|
12
|
-
|
13
|
-
attr_reader :recorded_ops
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
super
|
17
|
-
operators.clear
|
18
|
-
@recorded_ops = []
|
19
|
-
end
|
20
|
-
|
21
|
-
def respond_to_missing?(*)
|
22
|
-
true
|
23
|
-
end
|
24
|
-
|
25
|
-
def method_missing(msg, *params)
|
26
|
-
@recorded_ops << (params.empty? ? [msg] : [msg, params])
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
# Asserts that the content string contains the operators.
|
32
|
-
def assert_operators(content, operators, only_names: false, range: 0..-1)
|
33
|
-
processor = TestHelper::OperatorRecorder.new
|
34
|
-
HexaPDF::Content::Parser.new.parse(content, processor)
|
35
|
-
result = processor.recorded_ops[range]
|
36
|
-
result.map!(&:first) if only_names
|
37
|
-
assert_equal(operators, result)
|
38
|
-
end
|
39
|
-
end
|