hexapdf 0.12.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +126 -0
  3. data/examples/019-acro_form.rb +41 -4
  4. data/lib/hexapdf/cli/command.rb +4 -2
  5. data/lib/hexapdf/cli/image2pdf.rb +2 -1
  6. data/lib/hexapdf/cli/info.rb +51 -2
  7. data/lib/hexapdf/cli/inspect.rb +30 -8
  8. data/lib/hexapdf/cli/merge.rb +1 -1
  9. data/lib/hexapdf/cli/split.rb +74 -14
  10. data/lib/hexapdf/configuration.rb +15 -0
  11. data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
  12. data/lib/hexapdf/content/parser.rb +1 -1
  13. data/lib/hexapdf/dictionary.rb +4 -4
  14. data/lib/hexapdf/dictionary_fields.rb +1 -9
  15. data/lib/hexapdf/document.rb +41 -16
  16. data/lib/hexapdf/document/files.rb +0 -1
  17. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  18. data/lib/hexapdf/encryption/security_handler.rb +1 -0
  19. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
  20. data/lib/hexapdf/font/cmap.rb +1 -4
  21. data/lib/hexapdf/font/encoding/base.rb +8 -0
  22. data/lib/hexapdf/font/encoding/difference_encoding.rb +6 -0
  23. data/lib/hexapdf/font/true_type/table/head.rb +1 -0
  24. data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
  25. data/lib/hexapdf/font/type1_wrapper.rb +1 -1
  26. data/lib/hexapdf/image_loader/png.rb +3 -2
  27. data/lib/hexapdf/layout/line.rb +1 -1
  28. data/lib/hexapdf/layout/style.rb +23 -23
  29. data/lib/hexapdf/layout/text_layouter.rb +2 -2
  30. data/lib/hexapdf/layout/text_shaper.rb +3 -2
  31. data/lib/hexapdf/object.rb +52 -25
  32. data/lib/hexapdf/parser.rb +87 -3
  33. data/lib/hexapdf/pdf_array.rb +11 -4
  34. data/lib/hexapdf/revisions.rb +29 -21
  35. data/lib/hexapdf/serializer.rb +1 -1
  36. data/lib/hexapdf/task/optimize.rb +6 -4
  37. data/lib/hexapdf/tokenizer.rb +4 -3
  38. data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
  39. data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
  40. data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
  41. data/lib/hexapdf/type/acro_form/field.rb +35 -5
  42. data/lib/hexapdf/type/acro_form/form.rb +139 -14
  43. data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
  44. data/lib/hexapdf/type/actions/uri.rb +3 -2
  45. data/lib/hexapdf/type/annotations/widget.rb +3 -4
  46. data/lib/hexapdf/type/catalog.rb +2 -2
  47. data/lib/hexapdf/type/cid_font.rb +1 -1
  48. data/lib/hexapdf/type/file_specification.rb +1 -1
  49. data/lib/hexapdf/type/font.rb +1 -1
  50. data/lib/hexapdf/type/font_simple.rb +4 -2
  51. data/lib/hexapdf/type/font_true_type.rb +6 -2
  52. data/lib/hexapdf/type/font_type0.rb +4 -4
  53. data/lib/hexapdf/type/form.rb +15 -2
  54. data/lib/hexapdf/type/image.rb +2 -2
  55. data/lib/hexapdf/type/page.rb +37 -13
  56. data/lib/hexapdf/type/page_tree_node.rb +29 -5
  57. data/lib/hexapdf/type/resources.rb +1 -0
  58. data/lib/hexapdf/type/trailer.rb +2 -3
  59. data/lib/hexapdf/utils/object_hash.rb +0 -1
  60. data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
  61. data/lib/hexapdf/version.rb +1 -1
  62. data/test/hexapdf/common_tokenizer_tests.rb +6 -1
  63. data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
  64. data/test/hexapdf/content/test_canvas.rb +3 -3
  65. data/test/hexapdf/content/test_color_space.rb +1 -1
  66. data/test/hexapdf/encryption/test_aes.rb +4 -4
  67. data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
  68. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  69. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  70. data/test/hexapdf/font/encoding/test_base.rb +10 -0
  71. data/test/hexapdf/font/encoding/test_difference_encoding.rb +8 -0
  72. data/test/hexapdf/font/test_type1_wrapper.rb +4 -3
  73. data/test/hexapdf/layout/test_style.rb +1 -1
  74. data/test/hexapdf/layout/test_text_layouter.rb +12 -5
  75. data/test/hexapdf/test_configuration.rb +2 -2
  76. data/test/hexapdf/test_dictionary.rb +3 -1
  77. data/test/hexapdf/test_dictionary_fields.rb +2 -2
  78. data/test/hexapdf/test_document.rb +18 -10
  79. data/test/hexapdf/test_object.rb +71 -26
  80. data/test/hexapdf/test_parser.rb +159 -53
  81. data/test/hexapdf/test_pdf_array.rb +8 -1
  82. data/test/hexapdf/test_revisions.rb +35 -0
  83. data/test/hexapdf/test_writer.rb +2 -2
  84. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +296 -38
  85. data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
  86. data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
  87. data/test/hexapdf/type/acro_form/test_field.rb +39 -0
  88. data/test/hexapdf/type/acro_form/test_form.rb +87 -15
  89. data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
  90. data/test/hexapdf/type/test_font_simple.rb +2 -1
  91. data/test/hexapdf/type/test_font_true_type.rb +6 -0
  92. data/test/hexapdf/type/test_form.rb +26 -1
  93. data/test/hexapdf/type/test_page.rb +45 -7
  94. data/test/hexapdf/type/test_page_tree_node.rb +42 -0
  95. data/test/hexapdf/utils/test_bit_field.rb +2 -0
  96. data/test/hexapdf/utils/test_object_hash.rb +5 -0
  97. data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
  98. data/test/test_helper.rb +2 -0
  99. metadata +6 -11
@@ -219,6 +219,7 @@ module HexaPDF
219
219
  super
220
220
  val = self[:ProcSet]
221
221
  if !val
222
+ yield("No procedure set specified", true)
222
223
  self[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
223
224
  else
224
225
  val.reject! do |name|
@@ -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
- value[:Root] = document.add({Type: :Catalog})
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 ||
@@ -125,7 +125,6 @@ module HexaPDF
125
125
  def oids
126
126
  @oids.keys
127
127
  end
128
- private :oids
129
128
 
130
129
  end
131
130
 
@@ -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].delete_at(node[:Kids].index {|n| document.deref(n) == nested_node })
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] = document.deref(node[:Kids][0])[:Limits][0]
151
- node[:Limits][1] = document.deref(node[:Kids][-1])[: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 = document.deref(node[container_name][index + 1])
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 = document.deref(node[:Kids][index])
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 = document.deref(stack.pop)
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], document.deref(data[index + 1]))
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 << document.deref(node[:Kids][index])
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 = document.deref(array[mid])[: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| document.deref(o) == leaf_node }
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].each do |kid|
311
- unless (kid.kind_of?(HexaPDF::Object) && kid.indirect?) ||
312
- kid.kind_of?(HexaPDF::Reference)
313
- yield("Child entries of sorted tree nodes must be indirect objects", false)
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 = document.unwrap(container[index])
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
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.12.0'
40
+ VERSION = '0.14.0'
41
41
 
42
42
  end
@@ -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" + " " * 50000 + "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 = :DeviceGray
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 + "\x00" }
104
+ f = Fiber.new { 'a' * 31 << "\x00" }
105
105
  result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
106
- assert_equal('a' * 15 + "\x00", result)
106
+ assert_equal('a' * 15 << "\x00", result)
107
107
 
108
- f = Fiber.new { 'a' * 29 + "\x00\x01\x03" }
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 + "\x00\x01\x03", result)
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
- TEST_FILES = Dir[File.join(TEST_DATA_DIR, 'standard-security-handler', '*.pdf')].sort
57
- USER_PASSWORD = 'uhexapdf'
58
- OWNER_PASSWORD = 'ohexapdf'
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
- MINIMAL_DOC = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
60
+ minimal_doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
61
61
 
62
- TEST_FILES.each do |file|
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: USER_PASSWORD})
68
- assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
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: USER_PASSWORD})
73
- assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
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: OWNER_PASSWORD})
84
- assert_equal(MINIMAL_DOC.trailer[:Info][:ModDate], doc.trailer[:Info][:ModDate])
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.dup + "~>abcdefg"))))
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 + '4e6f7gzz'))))
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: :WinAnsiEncoding,
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([:T, :e, :s, :t], wrapper.decode_utf8("Test").map(&:name))
20
- assert_equal("a", wrapper.encode(wrapper.glyph(:a)))
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
- if name == :set_text_matrix
678
- pos = ops[-2, 2]
679
- elsif name == :move_text_next_line
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) { yield }
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.new(obj, oid: 1)
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