hexapdf 0.12.0 → 0.14.0
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 +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
|