hexapdf 0.24.0 → 0.24.1
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 +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
|