hexapdf 0.12.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +126 -0
- data/examples/019-acro_form.rb +41 -4
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/split.rb +74 -14
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/dictionary.rb +4 -4
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +41 -16
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/encoding/base.rb +8 -0
- data/lib/hexapdf/font/encoding/difference_encoding.rb +6 -0
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/font/type1_wrapper.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_layouter.rb +2 -2
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +52 -25
- data/lib/hexapdf/parser.rb +87 -3
- data/lib/hexapdf/pdf_array.rb +11 -4
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +4 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
- data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
- data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +139 -14
- data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +4 -4
- data/lib/hexapdf/type/form.rb +15 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +37 -13
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/object_hash.rb +0 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +6 -1
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/font/encoding/test_base.rb +10 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +4 -3
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +3 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +159 -53
- data/test/hexapdf/test_pdf_array.rb +8 -1
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +296 -38
- data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +87 -15
- data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +26 -1
- data/test/hexapdf/type/test_page.rb +45 -7
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_object_hash.rb +5 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +2 -0
- metadata +6 -11
data/lib/hexapdf/type/trailer.rb
CHANGED
@@ -97,7 +97,7 @@ module HexaPDF
|
|
97
97
|
private
|
98
98
|
|
99
99
|
# Validates the trailer.
|
100
|
-
def perform_validation
|
100
|
+
def perform_validation(&block)
|
101
101
|
super
|
102
102
|
unless value[:ID]
|
103
103
|
msg = if value[:Encrypt]
|
@@ -111,8 +111,7 @@ module HexaPDF
|
|
111
111
|
|
112
112
|
unless value[:Root]
|
113
113
|
yield("A PDF document must have a Catalog dictionary", true)
|
114
|
-
|
115
|
-
value[:Root].validate {|message, correctable| yield(message, correctable) }
|
114
|
+
catalog.validate(&block)
|
116
115
|
end
|
117
116
|
|
118
117
|
if value[:Encrypt] && (!document.security_handler ||
|
@@ -143,12 +143,12 @@ module HexaPDF
|
|
143
143
|
stack.reverse_each.inject do |nested_node, node|
|
144
144
|
if (!nested_node[container_name] || nested_node[container_name].empty?) &&
|
145
145
|
(!nested_node[:Kids] || nested_node[:Kids].empty?)
|
146
|
-
node[:Kids].
|
146
|
+
node[:Kids].delete(nested_node)
|
147
147
|
document.delete(nested_node)
|
148
148
|
end
|
149
149
|
if !node[:Kids].empty? && node[:Limits]
|
150
|
-
node[:Limits][0] =
|
151
|
-
node[:Limits][1] =
|
150
|
+
node[:Limits][0] = node[:Kids][0][:Limits][0]
|
151
|
+
node[:Limits][1] = node[:Kids][-1][:Limits][1]
|
152
152
|
end
|
153
153
|
node
|
154
154
|
end
|
@@ -167,11 +167,11 @@ module HexaPDF
|
|
167
167
|
if node.key?(container_name)
|
168
168
|
index = find_in_leaf_node(node[container_name], key)
|
169
169
|
if node[container_name][index] == key
|
170
|
-
result =
|
170
|
+
result = node[container_name][index + 1]
|
171
171
|
end
|
172
172
|
elsif node.key?(:Kids)
|
173
173
|
index = find_in_intermediate_node(node[:Kids], key)
|
174
|
-
node =
|
174
|
+
node = node[:Kids][index]
|
175
175
|
break unless key >= node[:Limits][0] && key <= node[:Limits][1]
|
176
176
|
else
|
177
177
|
break
|
@@ -192,12 +192,12 @@ module HexaPDF
|
|
192
192
|
container_name = leaf_node_container_name
|
193
193
|
stack = [self]
|
194
194
|
until stack.empty?
|
195
|
-
node =
|
195
|
+
node = stack.pop
|
196
196
|
if node.key?(container_name)
|
197
197
|
data = node[container_name]
|
198
198
|
index = 0
|
199
199
|
while index < data.length
|
200
|
-
yield(data[index],
|
200
|
+
yield(data[index], data[index + 1])
|
201
201
|
index += 2
|
202
202
|
end
|
203
203
|
elsif node.key?(:Kids)
|
@@ -215,7 +215,7 @@ module HexaPDF
|
|
215
215
|
def path_to_key(node, key, stack)
|
216
216
|
return unless node.key?(:Kids)
|
217
217
|
index = find_in_intermediate_node(node[:Kids], key)
|
218
|
-
stack <<
|
218
|
+
stack << node[:Kids][index]
|
219
219
|
path_to_key(stack.last, key, stack)
|
220
220
|
end
|
221
221
|
|
@@ -226,7 +226,7 @@ module HexaPDF
|
|
226
226
|
right = array.length - 1
|
227
227
|
while left < right
|
228
228
|
mid = (left + right) / 2
|
229
|
-
limits =
|
229
|
+
limits = array[mid][:Limits]
|
230
230
|
if limits[1] < key
|
231
231
|
left = mid + 1
|
232
232
|
elsif limits[0] > key
|
@@ -295,7 +295,7 @@ module HexaPDF
|
|
295
295
|
node[container_name] = leaf_node[container_name].slice!(split_point..-1)
|
296
296
|
node[:Limits] = node[container_name].values_at(0, -2)
|
297
297
|
leaf_node[:Limits][1] = leaf_node[container_name][-2]
|
298
|
-
index = 1 + parent[:Kids].index {|o|
|
298
|
+
index = 1 + parent[:Kids].index {|o| o == leaf_node }
|
299
299
|
parent[:Kids].insert(index, node)
|
300
300
|
end
|
301
301
|
end
|
@@ -307,10 +307,10 @@ module HexaPDF
|
|
307
307
|
|
308
308
|
# All kids entries must be indirect objects
|
309
309
|
if key?(:Kids)
|
310
|
-
self[:Kids].
|
311
|
-
unless
|
312
|
-
|
313
|
-
|
310
|
+
self[:Kids].each_with_index do |kid, index|
|
311
|
+
unless kid.kind_of?(HexaPDF::Object) && kid.indirect?
|
312
|
+
yield("Child entries of sorted tree nodes must be indirect objects", true)
|
313
|
+
value[:Kids][index] = document.add(kid)
|
314
314
|
end
|
315
315
|
end
|
316
316
|
end
|
@@ -321,15 +321,18 @@ module HexaPDF
|
|
321
321
|
container = self[container_name]
|
322
322
|
if container.length.odd?
|
323
323
|
yield("Sorted tree leaf node contains odd number of entries", false)
|
324
|
+
return
|
324
325
|
end
|
325
326
|
index = 0
|
326
327
|
old = nil
|
327
328
|
while index < container.length
|
328
|
-
key =
|
329
|
+
key = container[index]
|
329
330
|
if !key.kind_of?(key_type)
|
330
331
|
yield("A key must be a #{key_type} object, not a #{key.class}", false)
|
332
|
+
return
|
331
333
|
elsif old && old > key
|
332
334
|
yield("Sorted tree leaf node entries are not correctly sorted", false)
|
335
|
+
return
|
333
336
|
end
|
334
337
|
old = key
|
335
338
|
index += 2
|
data/lib/hexapdf/version.rb
CHANGED
@@ -121,6 +121,11 @@ module CommonTokenizerTests
|
|
121
121
|
assert(token.kind_of?(HexaPDF::Tokenizer::Token))
|
122
122
|
end
|
123
123
|
|
124
|
+
it "next_token: should not fail when reading super long numbers" do
|
125
|
+
create_tokenizer("1" << "0" * 10_000)
|
126
|
+
assert_equal(10**10_000, @tokenizer.next_token)
|
127
|
+
end
|
128
|
+
|
124
129
|
it "next_object: works for all PDF object types, including array and dictionary" do
|
125
130
|
create_tokenizer(<<-EOF.chomp.gsub(/^ {8}/, ''))
|
126
131
|
true false null 123 34.5 (string) <4E6F76> /Name
|
@@ -157,7 +162,7 @@ module CommonTokenizerTests
|
|
157
162
|
end
|
158
163
|
|
159
164
|
it "returns the correct position on operations" do
|
160
|
-
create_tokenizer("hallo du"
|
165
|
+
create_tokenizer("hallo du" << " " * 50000 << "hallo du")
|
161
166
|
@tokenizer.next_token
|
162
167
|
assert_equal(5, @tokenizer.pos)
|
163
168
|
|
@@ -68,14 +68,14 @@ describe HexaPDF::Content::GraphicObject::Arc do
|
|
68
68
|
arc.max_curves = 4
|
69
69
|
curves = arc.curves
|
70
70
|
assert_equal(2, curves.size)
|
71
|
-
assert_curve_values([0, 1, p1: [1, 0.548584], p2: [0.548584, 1]], curves[0])
|
72
|
-
assert_curve_values([-1, 0, p1: [-0.548584, 1], p2: [-1, 0.548584]], curves[1])
|
71
|
+
assert_curve_values([0, 1, {p1: [1, 0.548584], p2: [0.548584, 1]}], curves[0])
|
72
|
+
assert_curve_values([-1, 0, {p1: [-0.548584, 1], p2: [-1, 0.548584]}], curves[1])
|
73
73
|
|
74
74
|
arc.configure(clockwise: true)
|
75
75
|
curves = arc.curves
|
76
76
|
assert_equal(2, curves.size)
|
77
|
-
assert_curve_values([0, -1, p1: [1, -0.548584], p2: [0.548584, -1]], curves[0])
|
78
|
-
assert_curve_values([-1, 0, p1: [-0.548584, -1], p2: [-1, -0.548584]], curves[1])
|
77
|
+
assert_curve_values([0, -1, {p1: [1, -0.548584], p2: [0.548584, -1]}], curves[0])
|
78
|
+
assert_curve_values([-1, 0, {p1: [-0.548584, -1], p2: [-1, -0.548584]}], curves[1])
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -531,7 +531,7 @@ describe HexaPDF::Content::Canvas do
|
|
531
531
|
end
|
532
532
|
|
533
533
|
it "invokes the polygon method when radius != 0" do
|
534
|
-
args = [0, 0, 10, 0, 10, 10, 0, 10, radius: 5]
|
534
|
+
args = [0, 0, 10, 0, 10, 10, 0, 10, {radius: 5}]
|
535
535
|
assert_method_invoked(@canvas, :polygon, args) do
|
536
536
|
@canvas.rectangle(0, 0, 10, 10, radius: 5)
|
537
537
|
end
|
@@ -631,7 +631,7 @@ describe HexaPDF::Content::Canvas do
|
|
631
631
|
|
632
632
|
describe "circle" do
|
633
633
|
it "uses arc for the hard work" do
|
634
|
-
assert_method_invoked(@canvas, :arc, [5, 6, a: 7]) do
|
634
|
+
assert_method_invoked(@canvas, :arc, [5, 6, {a: 7}]) do
|
635
635
|
@canvas.graphics_object = :path
|
636
636
|
@canvas.circle(5, 6, 7)
|
637
637
|
end
|
@@ -651,7 +651,7 @@ describe HexaPDF::Content::Canvas do
|
|
651
651
|
|
652
652
|
describe "ellipse" do
|
653
653
|
it "uses arc for the hard work" do
|
654
|
-
assert_method_invoked(@canvas, :ellipse, [5, 6, a: 7, b: 5, inclination: 10]) do
|
654
|
+
assert_method_invoked(@canvas, :ellipse, [5, 6, {a: 7, b: 5, inclination: 10}]) do
|
655
655
|
@canvas.ellipse(5, 6, a: 7, b: 5, inclination: 10)
|
656
656
|
end
|
657
657
|
end
|
@@ -155,7 +155,7 @@ describe HexaPDF::Content::ColorSpace::DeviceGray do
|
|
155
155
|
|
156
156
|
before do
|
157
157
|
@color_space = HexaPDF::Content::ColorSpace::DeviceGray.new
|
158
|
-
@color_space_family = @color_space_definition =
|
158
|
+
@color_space_family = @color_space_definition = :DeviceGray
|
159
159
|
@color = @color_space.default_color
|
160
160
|
@other_color = @color_space.color(128)
|
161
161
|
@colors = [128]
|
@@ -101,13 +101,13 @@ describe HexaPDF::Encryption::AES do
|
|
101
101
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
102
102
|
assert_equal('a' * 16, result)
|
103
103
|
|
104
|
-
f = Fiber.new { 'a' * 31
|
104
|
+
f = Fiber.new { 'a' * 31 << "\x00" }
|
105
105
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
106
|
-
assert_equal('a' * 15
|
106
|
+
assert_equal('a' * 15 << "\x00", result)
|
107
107
|
|
108
|
-
f = Fiber.new { 'a' * 29
|
108
|
+
f = Fiber.new { 'a' * 29 << "\x00\x01\x03" }
|
109
109
|
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
110
|
-
assert_equal('a' * 13
|
110
|
+
assert_equal('a' * 13 << "\x00\x01\x03", result)
|
111
111
|
end
|
112
112
|
|
113
113
|
it "fails on decryption if not enough bytes are provided" do
|
@@ -53,24 +53,24 @@ describe HexaPDF::Encryption::StandardEncryptionDictionary do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
describe HexaPDF::Encryption::StandardSecurityHandler do
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
test_files = Dir[File.join(TEST_DATA_DIR, 'standard-security-handler', '*.pdf')].sort
|
57
|
+
user_password = 'uhexapdf'
|
58
|
+
owner_password = 'ohexapdf'
|
59
59
|
|
60
|
-
|
60
|
+
minimal_doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
61
61
|
|
62
|
-
|
62
|
+
test_files.each do |file|
|
63
63
|
basename = File.basename(file)
|
64
64
|
it "can decrypt, encrypt and decrypt the encrypted file #{basename} with the user password" do
|
65
65
|
begin
|
66
66
|
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
67
|
-
decryption_opts: {password:
|
68
|
-
assert_equal(
|
67
|
+
decryption_opts: {password: user_password})
|
68
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
69
69
|
|
70
70
|
out = StringIO.new(''.b)
|
71
71
|
HexaPDF::Writer.new(doc, out).write
|
72
|
-
doc = HexaPDF::Document.new(io: out, decryption_opts: {password:
|
73
|
-
assert_equal(
|
72
|
+
doc = HexaPDF::Document.new(io: out, decryption_opts: {password: user_password})
|
73
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
74
74
|
rescue HexaPDF::EncryptionError => e
|
75
75
|
flunk("Error processing #{basename}: #{e}")
|
76
76
|
end
|
@@ -80,8 +80,8 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
80
80
|
it "can decrypt the encrypted file #{basename} with the owner password" do
|
81
81
|
begin
|
82
82
|
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
83
|
-
decryption_opts: {password:
|
84
|
-
assert_equal(
|
83
|
+
decryption_opts: {password: owner_password})
|
84
|
+
assert_equal(minimal_doc.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
|
85
85
|
rescue HexaPDF::EncryptionError => e
|
86
86
|
flunk("Error processing #{basename}: #{e}")
|
87
87
|
end
|
@@ -33,7 +33,7 @@ describe HexaPDF::Filter::ASCII85Decode do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it "ignores data after the EOD marker" do
|
36
|
-
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded
|
36
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << "~>abcdefg"))))
|
37
37
|
end
|
38
38
|
|
39
39
|
it "fails if the input contains invalid characters" do
|
@@ -24,7 +24,7 @@ describe HexaPDF::Filter::ASCIIHexDecode do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "ignores data after the EOD marker" do
|
27
|
-
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded
|
27
|
+
assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded << '4e6f7gzz'))))
|
28
28
|
end
|
29
29
|
|
30
30
|
it "assumes the missing char is '0' if the input length is odd" do
|
@@ -32,4 +32,14 @@ describe HexaPDF::Font::Encoding::Base do
|
|
32
32
|
assert_nil(@base.unicode(66))
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
describe "code" do
|
37
|
+
it "returns the code for an existing glyph name" do
|
38
|
+
assert_equal(65, @base.code(:A))
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns nil if the glyph name is not referenced" do
|
42
|
+
assert_nil(@base.code(:Unknown))
|
43
|
+
end
|
44
|
+
end
|
35
45
|
end
|
@@ -18,4 +18,12 @@ describe HexaPDF::Font::Encoding::DifferenceEncoding do
|
|
18
18
|
assert_equal(:B, @enc.name(66))
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
describe "code" do
|
23
|
+
it "takes the encoding differences into account" do
|
24
|
+
assert_equal(65, @enc.code(:A))
|
25
|
+
@enc.code_to_name[65] = :Known
|
26
|
+
assert_equal(65, @enc.code(:Known))
|
27
|
+
end
|
28
|
+
end
|
21
29
|
end
|
@@ -13,11 +13,12 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "can be used with an existing PDF object" do
|
16
|
-
font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: :
|
16
|
+
font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: {Differences: [65, :B]},
|
17
17
|
BaseFont: :"Times-Roman"})
|
18
18
|
wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES, pdf_object: font)
|
19
|
-
assert_equal([:
|
20
|
-
assert_equal("
|
19
|
+
assert_equal([:B, :E, :A, :S, :T], wrapper.decode_utf8("BEAST").map(&:name))
|
20
|
+
assert_equal("A", wrapper.encode(wrapper.glyph(:A)))
|
21
|
+
assert_equal("A", wrapper.encode(wrapper.glyph(:B)))
|
21
22
|
end
|
22
23
|
|
23
24
|
it "returns 1 for the scaling factor" do
|
@@ -537,7 +537,7 @@ describe HexaPDF::Layout::Style::LinkLayer do
|
|
537
537
|
end
|
538
538
|
|
539
539
|
it "does nothing if the context is not a page object" do
|
540
|
-
@canvas = HexaPDF::Document.new.add({Type: :XObject, Subtype: :Form}).canvas
|
540
|
+
@canvas = HexaPDF::Document.new.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 1, 1]}).canvas
|
541
541
|
assert_nil(call_link(dest: true))
|
542
542
|
end
|
543
543
|
|
@@ -591,25 +591,33 @@ describe HexaPDF::Layout::TextLayouter do
|
|
591
591
|
|
592
592
|
describe "horizontal alignment" do
|
593
593
|
before do
|
594
|
-
@items = boxes(*[[20, 20]] * 4)
|
594
|
+
@items = boxes(*[[20, 20]] * 4) + [glue(10), penalty(-5000, boxes(0).first.item)]
|
595
595
|
end
|
596
596
|
|
597
597
|
it "aligns the contents to the left" do
|
598
598
|
@style.align = :left
|
599
599
|
result = @layouter.fit(@items, 100, 100)
|
600
600
|
assert_equal(0, result.lines[0].x_offset)
|
601
|
+
assert_equal(80, result.lines[0].width)
|
602
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
603
|
+
assert_equal(0, result.lines[0].x_offset)
|
604
|
+
assert_equal(80, result.lines[0].width)
|
601
605
|
end
|
602
606
|
|
603
607
|
it "aligns the contents to the center" do
|
604
608
|
@style.align = :center
|
605
609
|
result = @layouter.fit(@items, 100, 100)
|
606
610
|
assert_equal(10, result.lines[0].x_offset)
|
611
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
612
|
+
assert_equal(10, result.lines[0].x_offset)
|
607
613
|
end
|
608
614
|
|
609
615
|
it "aligns the contents to the right" do
|
610
616
|
@style.align = :right
|
611
617
|
result = @layouter.fit(@items, 100, 100)
|
612
618
|
assert_equal(20, result.lines[0].x_offset)
|
619
|
+
result = @layouter.fit(@items, proc { 100 }, 100)
|
620
|
+
assert_equal(20, result.lines[0].x_offset)
|
613
621
|
end
|
614
622
|
end
|
615
623
|
|
@@ -674,10 +682,9 @@ describe HexaPDF::Layout::TextLayouter do
|
|
674
682
|
pos = [0, 0]
|
675
683
|
result.select! {|name, _| name == :set_text_matrix || name == :move_text_next_line }.
|
676
684
|
map! do |name, ops|
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
pos[1] -= leading
|
685
|
+
case name
|
686
|
+
when :set_text_matrix then pos = ops[-2, 2]
|
687
|
+
when :move_text_next_line then pos[1] -= leading
|
681
688
|
end
|
682
689
|
pos.dup
|
683
690
|
end
|
@@ -66,8 +66,8 @@ describe HexaPDF::Configuration do
|
|
66
66
|
assert_equal(HexaPDF, @config.constantize('test', 1))
|
67
67
|
end
|
68
68
|
|
69
|
-
def assert_constantize_error # :nodoc:
|
70
|
-
exp = assert_raises(HexaPDF::Error)
|
69
|
+
def assert_constantize_error(&block) # :nodoc:
|
70
|
+
exp = assert_raises(HexaPDF::Error, &block)
|
71
71
|
assert_match(/Error getting constant for configuration option/, exp.message)
|
72
72
|
end
|
73
73
|
|
@@ -14,7 +14,9 @@ describe HexaPDF::Dictionary do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def add(obj)
|
17
|
-
HexaPDF::Object
|
17
|
+
klass = HexaPDF::Object
|
18
|
+
klass = HexaPDF::Dictionary if obj.kind_of?(HexaPDF::Dictionary) || obj.kind_of?(Hash)
|
19
|
+
klass.new(obj, oid: 1)
|
18
20
|
end
|
19
21
|
|
20
22
|
def delete(_obj)
|
@@ -222,7 +222,7 @@ describe HexaPDF::DictionaryFields do
|
|
222
222
|
|
223
223
|
it "allows conversion to a Rectangle from an Array" do
|
224
224
|
doc = Minitest::Mock.new
|
225
|
-
doc.expect(:wrap, :data, [[0, 1, 2, 3], type: HexaPDF::Rectangle])
|
225
|
+
doc.expect(:wrap, :data, [[0, 1, 2, 3], {type: HexaPDF::Rectangle}])
|
226
226
|
@field.convert([0, 1, 2, 3], doc)
|
227
227
|
doc.verify
|
228
228
|
end
|
@@ -230,7 +230,7 @@ describe HexaPDF::DictionaryFields do
|
|
230
230
|
it "allows conversion to a Rectangle from a HexaPDF::PDFArray" do
|
231
231
|
data = HexaPDF::PDFArray.new([0, 1, 2, 3])
|
232
232
|
doc = Minitest::Mock.new
|
233
|
-
doc.expect(:wrap, :data, [data, type: HexaPDF::Rectangle])
|
233
|
+
doc.expect(:wrap, :data, [data, {type: HexaPDF::Rectangle}])
|
234
234
|
@field.convert(data, doc)
|
235
235
|
doc.verify
|
236
236
|
end
|