hexapdf 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +35 -50
  5. data/VERSION +1 -1
  6. data/lib/hexapdf/cli.rb +4 -0
  7. data/lib/hexapdf/cli/command.rb +6 -2
  8. data/lib/hexapdf/cli/image2pdf.rb +141 -0
  9. data/lib/hexapdf/cli/info.rb +1 -1
  10. data/lib/hexapdf/cli/inspect.rb +32 -2
  11. data/lib/hexapdf/cli/modify.rb +1 -1
  12. data/lib/hexapdf/cli/optimize.rb +1 -1
  13. data/lib/hexapdf/cli/watermark.rb +130 -0
  14. data/lib/hexapdf/composer.rb +2 -2
  15. data/lib/hexapdf/configuration.rb +7 -1
  16. data/lib/hexapdf/content/canvas.rb +2 -2
  17. data/lib/hexapdf/content/graphic_object/arc.rb +2 -2
  18. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +2 -2
  19. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  20. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  21. data/lib/hexapdf/dictionary.rb +11 -3
  22. data/lib/hexapdf/dictionary_fields.rb +32 -3
  23. data/lib/hexapdf/document.rb +7 -3
  24. data/lib/hexapdf/document/files.rb +1 -1
  25. data/lib/hexapdf/document/fonts.rb +21 -1
  26. data/lib/hexapdf/document/pages.rb +2 -2
  27. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
  28. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  29. data/lib/hexapdf/font/true_type/table/head.rb +2 -2
  30. data/lib/hexapdf/font/true_type/table/os2.rb +4 -4
  31. data/lib/hexapdf/font/true_type_wrapper.rb +16 -16
  32. data/lib/hexapdf/font/type1_wrapper.rb +16 -16
  33. data/lib/hexapdf/font_loader.rb +2 -0
  34. data/lib/hexapdf/font_loader/from_configuration.rb +5 -0
  35. data/lib/hexapdf/font_loader/standard14.rb +5 -0
  36. data/lib/hexapdf/image_loader/png.rb +1 -1
  37. data/lib/hexapdf/layout/box.rb +2 -2
  38. data/lib/hexapdf/layout/image_box.rb +1 -1
  39. data/lib/hexapdf/layout/style.rb +50 -24
  40. data/lib/hexapdf/layout/text_box.rb +1 -1
  41. data/lib/hexapdf/layout/text_fragment.rb +2 -2
  42. data/lib/hexapdf/layout/text_layouter.rb +14 -10
  43. data/lib/hexapdf/name_tree_node.rb +3 -3
  44. data/lib/hexapdf/number_tree_node.rb +3 -3
  45. data/lib/hexapdf/pdf_array.rb +207 -0
  46. data/lib/hexapdf/rectangle.rb +12 -12
  47. data/lib/hexapdf/serializer.rb +1 -1
  48. data/lib/hexapdf/stream.rb +6 -4
  49. data/lib/hexapdf/task/optimize.rb +3 -3
  50. data/lib/hexapdf/type.rb +2 -0
  51. data/lib/hexapdf/type/acro_form.rb +51 -0
  52. data/lib/hexapdf/type/acro_form/field.rb +129 -0
  53. data/lib/hexapdf/type/acro_form/form.rb +124 -0
  54. data/lib/hexapdf/type/action.rb +1 -1
  55. data/lib/hexapdf/type/actions/go_to.rb +1 -1
  56. data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
  57. data/lib/hexapdf/type/actions/launch.rb +1 -1
  58. data/lib/hexapdf/type/annotation.rb +2 -2
  59. data/lib/hexapdf/type/annotations.rb +1 -0
  60. data/lib/hexapdf/type/annotations/link.rb +4 -15
  61. data/lib/hexapdf/type/annotations/markup_annotation.rb +2 -1
  62. data/lib/hexapdf/type/annotations/text.rb +3 -6
  63. data/lib/hexapdf/type/annotations/widget.rb +90 -0
  64. data/lib/hexapdf/type/catalog.rb +12 -9
  65. data/lib/hexapdf/type/cid_font.rb +3 -3
  66. data/lib/hexapdf/type/file_specification.rb +2 -2
  67. data/lib/hexapdf/type/font_descriptor.rb +5 -2
  68. data/lib/hexapdf/type/font_simple.rb +1 -1
  69. data/lib/hexapdf/type/font_type0.rb +1 -1
  70. data/lib/hexapdf/type/font_type3.rb +1 -1
  71. data/lib/hexapdf/type/form.rb +2 -2
  72. data/lib/hexapdf/type/graphics_state_parameter.rb +11 -6
  73. data/lib/hexapdf/type/icon_fit.rb +58 -0
  74. data/lib/hexapdf/type/image.rb +14 -8
  75. data/lib/hexapdf/type/info.rb +2 -1
  76. data/lib/hexapdf/type/page.rb +4 -4
  77. data/lib/hexapdf/type/page_tree_node.rb +3 -7
  78. data/lib/hexapdf/type/resources.rb +1 -1
  79. data/lib/hexapdf/type/trailer.rb +4 -4
  80. data/lib/hexapdf/type/viewer_preferences.rb +7 -4
  81. data/lib/hexapdf/type/xref_stream.rb +2 -2
  82. data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
  83. data/lib/hexapdf/version.rb +1 -1
  84. data/man/man1/hexapdf.1 +77 -8
  85. data/test/hexapdf/content/test_canvas.rb +2 -2
  86. data/test/hexapdf/content/test_processor.rb +3 -3
  87. data/test/hexapdf/document/test_files.rb +4 -4
  88. data/test/hexapdf/document/test_fonts.rb +13 -1
  89. data/test/hexapdf/document/test_images.rb +6 -6
  90. data/test/hexapdf/document/test_pages.rb +8 -8
  91. data/test/hexapdf/encryption/test_security_handler.rb +7 -7
  92. data/test/hexapdf/encryption/test_standard_security_handler.rb +5 -5
  93. data/test/hexapdf/font/test_true_type_wrapper.rb +2 -2
  94. data/test/hexapdf/font_loader/test_from_configuration.rb +4 -0
  95. data/test/hexapdf/font_loader/test_standard14.rb +10 -0
  96. data/test/hexapdf/image_loader/test_jpeg.rb +1 -1
  97. data/test/hexapdf/image_loader/test_png.rb +3 -3
  98. data/test/hexapdf/layout/test_box.rb +2 -2
  99. data/test/hexapdf/layout/test_frame.rb +1 -1
  100. data/test/hexapdf/layout/test_image_box.rb +1 -1
  101. data/test/hexapdf/layout/test_style.rb +18 -13
  102. data/test/hexapdf/layout/test_text_box.rb +1 -1
  103. data/test/hexapdf/layout/test_text_layouter.rb +11 -6
  104. data/test/hexapdf/task/test_dereference.rb +2 -2
  105. data/test/hexapdf/task/test_optimize.rb +11 -11
  106. data/test/hexapdf/test_composer.rb +1 -1
  107. data/test/hexapdf/test_dictionary.rb +10 -2
  108. data/test/hexapdf/test_dictionary_fields.rb +27 -3
  109. data/test/hexapdf/test_document.rb +16 -15
  110. data/test/hexapdf/test_importer.rb +4 -4
  111. data/test/hexapdf/test_object.rb +1 -1
  112. data/test/hexapdf/test_pdf_array.rb +162 -0
  113. data/test/hexapdf/test_rectangle.rb +3 -5
  114. data/test/hexapdf/test_serializer.rb +1 -1
  115. data/test/hexapdf/test_stream.rb +1 -0
  116. data/test/hexapdf/test_writer.rb +3 -3
  117. data/test/hexapdf/type/acro_form/test_field.rb +85 -0
  118. data/test/hexapdf/type/acro_form/test_form.rb +69 -0
  119. data/test/hexapdf/type/annotations/test_text.rb +2 -6
  120. data/test/hexapdf/type/annotations/test_widget.rb +24 -0
  121. data/test/hexapdf/type/test_annotation.rb +1 -1
  122. data/test/hexapdf/type/test_catalog.rb +1 -1
  123. data/test/hexapdf/type/test_cid_font.rb +3 -3
  124. data/test/hexapdf/type/test_font.rb +2 -2
  125. data/test/hexapdf/type/test_font_descriptor.rb +2 -1
  126. data/test/hexapdf/type/test_font_simple.rb +3 -3
  127. data/test/hexapdf/type/test_font_true_type.rb +6 -6
  128. data/test/hexapdf/type/test_font_type0.rb +5 -5
  129. data/test/hexapdf/type/test_font_type1.rb +8 -8
  130. data/test/hexapdf/type/test_font_type3.rb +4 -4
  131. data/test/hexapdf/type/test_image.rb +16 -12
  132. data/test/hexapdf/type/test_page.rb +11 -11
  133. data/test/hexapdf/type/test_page_tree_node.rb +20 -20
  134. data/test/hexapdf/type/test_resources.rb +6 -6
  135. data/test/hexapdf/type/test_trailer.rb +5 -2
  136. data/test/hexapdf/type/test_xref_stream.rb +1 -0
  137. data/test/hexapdf/utils/test_sorted_tree_node.rb +35 -35
  138. metadata +23 -7
  139. data/test/hexapdf/type/annotations/test_link.rb +0 -19
@@ -117,10 +117,15 @@ describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
117
117
  assert_equal(20, result.size)
118
118
  [1, 3, 5, 7, 9, 11, 13, 17, 19].each do |index|
119
119
  assert_penalty(result[index],
120
- HexaPDF::Layout::TextLayouter::Penalty::MandatoryParagraphBreak.penalty)
120
+ HexaPDF::Layout::TextLayouter::Penalty::PARAGRAPH_BREAK)
121
+ assert_equal([], result[index].item.items)
122
+ assert(result[index].item.items.frozen?)
123
+ assert_same(frag.style, result[index].item.style)
121
124
  end
122
- assert_penalty(result[15],
123
- HexaPDF::Layout::TextLayouter::Penalty::MandatoryLineBreak.penalty)
125
+ assert_penalty(result[15], HexaPDF::Layout::TextLayouter::Penalty::LINE_BREAK)
126
+ assert_equal([], result[15].item.items)
127
+ assert(result[15].item.items.frozen?)
128
+ assert_same(frag.style, result[15].item.style)
124
129
  end
125
130
 
126
131
  it "insert a standard penalty after a hyphen" do
@@ -389,9 +394,9 @@ describe HexaPDF::Layout::TextLayouter do
389
394
 
390
395
  it "handles text indentation" do
391
396
  items = boxes([20, 20], [20, 20], [20, 20]) +
392
- [HexaPDF::Layout::TextLayouter::Penalty::MandatoryParagraphBreak] +
397
+ [penalty(HexaPDF::Layout::TextLayouter::Penalty::PARAGRAPH_BREAK)] +
393
398
  boxes([40, 20]) + [glue(20)] +
394
- boxes(*([[20, 20]] * 4)) + [HexaPDF::Layout::TextLayouter::Penalty::MandatoryLineBreak] +
399
+ boxes(*([[20, 20]] * 4)) + [penalty(HexaPDF::Layout::TextLayouter::Penalty::LINE_BREAK)] +
395
400
  boxes(*([[20, 20]] * 4))
396
401
  @style.text_indent = 20
397
402
 
@@ -429,7 +434,7 @@ describe HexaPDF::Layout::TextLayouter do
429
434
  assert_equal(140, result.height)
430
435
  end
431
436
 
432
- it "handles empty lines" do
437
+ it "handles empty lines if the break penalties don't have an item" do
433
438
  items = boxes([20, 20]) + [penalty(-5000)] + boxes([30, 20]) + [penalty(-5000)] * 2 +
434
439
  boxes([20, 20]) + [penalty(-5000)] * 2
435
440
  result = @layouter.fit(items, 30, 100)
@@ -14,8 +14,8 @@ describe HexaPDF::Task::Dereference do
14
14
  len = @doc.add(5)
15
15
  str = @doc.add(@doc.wrap({Length: len}, stream: ''))
16
16
  @doc.trailer[:Test] = str
17
- pages = @doc.wrap(Type: :Pages)
18
- pages.add_page(@doc.wrap(Type: :Page))
17
+ pages = @doc.wrap({Type: :Pages})
18
+ pages.add_page(@doc.wrap({Type: :Page}))
19
19
  @doc.trailer[:Test2] = pages
20
20
  @doc.trailer[:InvalidRef] = HexaPDF::Reference.new(5000, 2)
21
21
 
@@ -16,9 +16,9 @@ describe HexaPDF::Task::Optimize do
16
16
  @obj1 = @doc.add(@doc.wrap({Optional: :Optional}, type: TestType))
17
17
  @doc.trailer[:Test] = @doc.wrap(@obj1)
18
18
  @doc.revisions.add
19
- @obj2 = @doc.add(Type: :UsedEntry)
20
- @obj3 = @doc.add(Unused: @obj2)
21
- @obj4 = @doc.add(Test: :Test)
19
+ @obj2 = @doc.add({Type: :UsedEntry})
20
+ @obj3 = @doc.add({Unused: @obj2})
21
+ @obj4 = @doc.add({Test: :Test})
22
22
  @obj1[:Test] = @doc.wrap(@obj4, type: TestType)
23
23
  end
24
24
 
@@ -62,7 +62,7 @@ describe HexaPDF::Task::Optimize do
62
62
  end
63
63
 
64
64
  it "compacts and deletes object streams" do
65
- @doc.add(Type: :ObjStm)
65
+ @doc.add({Type: :ObjStm})
66
66
  @doc.task(:optimize, compact: true, object_streams: :delete)
67
67
  assert_no_objstms
68
68
  assert_default_deleted
@@ -85,8 +85,8 @@ describe HexaPDF::Task::Optimize do
85
85
 
86
86
  describe "object_streams" do
87
87
  it "generates object streams" do
88
- objstm = @doc.add(Type: :ObjStm)
89
- xref = @doc.add(Type: :XRef)
88
+ objstm = @doc.add({Type: :ObjStm})
89
+ xref = @doc.add({Type: :XRef})
90
90
  210.times { @doc.add(5) }
91
91
  @doc.task(:optimize, object_streams: :generate)
92
92
  assert_objstms_generated
@@ -97,8 +97,8 @@ describe HexaPDF::Task::Optimize do
97
97
  end
98
98
 
99
99
  it "deletes object and xref streams" do
100
- @doc.add(Type: :ObjStm)
101
- @doc.add(Type: :XRef)
100
+ @doc.add({Type: :ObjStm})
101
+ @doc.add({Type: :XRef})
102
102
  @doc.task(:optimize, object_streams: :delete, xref_streams: :delete)
103
103
  assert_no_objstms
104
104
  assert_no_xrefstms
@@ -106,7 +106,7 @@ describe HexaPDF::Task::Optimize do
106
106
  end
107
107
 
108
108
  it "deletes object and generates xref streams" do
109
- @doc.add(Type: :ObjStm)
109
+ @doc.add({Type: :ObjStm})
110
110
  @doc.task(:optimize, object_streams: :delete, xref_streams: :generate)
111
111
  assert_no_objstms
112
112
  assert_xrefstms_generated
@@ -122,13 +122,13 @@ describe HexaPDF::Task::Optimize do
122
122
  end
123
123
 
124
124
  it "reuses an xref stream in generatation mode" do
125
- @doc.add(Type: :XRef)
125
+ @doc.add({Type: :XRef})
126
126
  @doc.task(:optimize, xref_streams: :generate)
127
127
  assert_xrefstms_generated
128
128
  end
129
129
 
130
130
  it "deletes xref streams" do
131
- @doc.add(Type: :XRef)
131
+ @doc.add({Type: :XRef})
132
132
  @doc.task(:optimize, xref_streams: :delete)
133
133
  assert_no_xrefstms
134
134
  assert_default_deleted
@@ -181,7 +181,7 @@ describe HexaPDF::Composer do
181
181
 
182
182
  describe "draw_box" do
183
183
  def create_box(**kwargs)
184
- HexaPDF::Layout::Box.new(kwargs) {}
184
+ HexaPDF::Layout::Box.new(**kwargs) {}
185
185
  end
186
186
 
187
187
  it "draws the box if it completely fits" do
@@ -194,6 +194,7 @@ describe HexaPDF::Dictionary do
194
194
  describe "validate_fields" do
195
195
  before do
196
196
  @test_class.define_field(:Inherited, type: [Array, Symbol], required: true, indirect: false)
197
+ @test_class.define_field(:AllowedValues, type: Integer, allowed_values: [1, 5])
197
198
  @obj = @test_class.new({Array: [], Inherited: :symbol}, document: self)
198
199
  end
199
200
 
@@ -241,6 +242,13 @@ describe HexaPDF::Dictionary do
241
242
  assert(@obj.validate(auto_correct: true))
242
243
  end
243
244
 
245
+ it "checks whether the value is an allowed one" do
246
+ @obj.value[:AllowedValues] = 7
247
+ refute(@obj.validate(auto_correct: false))
248
+ @obj.value[:AllowedValues] = 1
249
+ assert(@obj.validate(auto_correct: false))
250
+ end
251
+
244
252
  it "checks whether a field needs to be indirect w/wo auto_correct" do
245
253
  @obj.value[:Inherited] = HexaPDF::Object.new(:test, oid: 1)
246
254
  refute(@obj.validate(auto_correct: false))
@@ -252,7 +260,7 @@ describe HexaPDF::Dictionary do
252
260
  assert(@obj.validate(auto_correct: true))
253
261
  assert_equal(1, @obj.value[:TestClass].oid)
254
262
 
255
- @obj.value[:TestClass] = HexaPDF::Object.new(Inherited: :symbol)
263
+ @obj.value[:TestClass] = HexaPDF::Object.new({Inherited: :symbol})
256
264
  assert(@obj.validate(auto_correct: true))
257
265
  assert_equal(1, @obj.value[:TestClass].oid)
258
266
  end
@@ -319,7 +327,7 @@ describe HexaPDF::Dictionary do
319
327
  describe "empty?" do
320
328
  it "returns true if the dictionary contains no entries" do
321
329
  assert(HexaPDF::Dictionary.new({}).empty?)
322
- refute(HexaPDF::Dictionary.new(x: 5).empty?)
330
+ refute(HexaPDF::Dictionary.new({x: 5}).empty?)
323
331
  end
324
332
  end
325
333
  end
@@ -11,7 +11,9 @@ describe HexaPDF::DictionaryFields do
11
11
 
12
12
  describe "Field" do
13
13
  before do
14
- @field = self.class::Field.new([:Integer, self.class::PDFByteString], true, 500, false, '1.2')
14
+ @field = self.class::Field.new([:Integer, self.class::PDFByteString], required: true,
15
+ default: 500, indirect: false, allowed_values: [500, 1],
16
+ version: '1.2')
15
17
  HexaPDF::GlobalConfiguration['object.type_map'][:Integer] = Integer
16
18
  end
17
19
 
@@ -25,6 +27,7 @@ describe HexaPDF::DictionaryFields do
25
27
  assert_equal(500, @field.default)
26
28
  assert_equal(false, @field.indirect)
27
29
  assert_equal('1.2', @field.version)
30
+ assert_equal([500, 1], @field.allowed_values)
28
31
  end
29
32
 
30
33
  it "maps string types to constants" do
@@ -71,7 +74,7 @@ describe HexaPDF::DictionaryFields do
71
74
 
72
75
  it "allows conversion from a Dictionary" do
73
76
  @doc.expect(:wrap, :data, [HexaPDF::Dictionary, Hash])
74
- @field.convert(HexaPDF::Dictionary.new(Test: :value), @doc)
77
+ @field.convert(HexaPDF::Dictionary.new({Test: :value}), @doc)
75
78
  @doc.verify
76
79
  end
77
80
 
@@ -86,7 +89,28 @@ describe HexaPDF::DictionaryFields do
86
89
  it "doesn't allow conversion to a Stream subclass from Hash or Dictionary" do
87
90
  @field = self.class::Field.new(HexaPDF::Stream)
88
91
  refute(@field.convert({}, @doc))
89
- refute(@field.convert(HexaPDF::Dictionary.new(Test: :value), @doc))
92
+ refute(@field.convert(HexaPDF::Dictionary.new({Test: :value}), @doc))
93
+ end
94
+
95
+ it "doesn't allow conversion from nil" do
96
+ refute(@field.convert(nil, @doc))
97
+ end
98
+ end
99
+
100
+ describe "ArrayConverter" do
101
+ before do
102
+ @field = self.class::Field.new(HexaPDF::PDFArray)
103
+ @doc = Minitest::Mock.new
104
+ end
105
+
106
+ it "additionally adds Array as allowed type" do
107
+ assert(@field.type.include?(Array))
108
+ end
109
+
110
+ it "allows conversion from an array" do
111
+ @doc.expect(:wrap, :data, [[1, 2], {type: HexaPDF::PDFArray}])
112
+ @field.convert([1, 2], @doc)
113
+ @doc.verify
90
114
  end
91
115
 
92
116
  it "doesn't allow conversion from nil" do
@@ -282,7 +282,8 @@ describe HexaPDF::Document do
282
282
  it "uses a suitable default type if no special type is specified" do
283
283
  assert_instance_of(HexaPDF::Object, @doc.wrap(5))
284
284
  assert_instance_of(HexaPDF::Stream, @doc.wrap({a: 5}, stream: ''))
285
- assert_instance_of(HexaPDF::Dictionary, @doc.wrap(a: 5))
285
+ assert_instance_of(HexaPDF::Dictionary, @doc.wrap({a: 5}))
286
+ assert_instance_of(HexaPDF::PDFArray, @doc.wrap([1, 2]))
286
287
  end
287
288
 
288
289
  it "returns an object of type HexaPDF::Object" do
@@ -327,12 +328,12 @@ describe HexaPDF::Document do
327
328
  end
328
329
 
329
330
  it "uses the type/subtype information in the hash that should be wrapped" do
330
- assert_kind_of(@myclass, @doc.wrap(Type: :MyClass))
331
- refute_kind_of(@myclass2, @doc.wrap(Subtype: :TheSecond))
332
- refute_kind_of(@myclass2, @doc.wrap(Subtype: :Global))
333
- assert_kind_of(@myclass2, @doc.wrap(Subtype: :Global, Test: "true"))
334
- assert_kind_of(@myclass2, @doc.wrap(Type: :MyClass, S: :TheSecond))
335
- assert_kind_of(@myclass, @doc.wrap(Type: :MyClass, Subtype: :TheThird))
331
+ assert_kind_of(@myclass, @doc.wrap({Type: :MyClass}))
332
+ refute_kind_of(@myclass2, @doc.wrap({Subtype: :TheSecond}))
333
+ refute_kind_of(@myclass2, @doc.wrap({Subtype: :Global}))
334
+ assert_kind_of(@myclass2, @doc.wrap({Subtype: :Global, Test: "true"}))
335
+ assert_kind_of(@myclass2, @doc.wrap({Type: :MyClass, S: :TheSecond}))
336
+ assert_kind_of(@myclass, @doc.wrap({Type: :MyClass, Subtype: :TheThird}))
336
337
  end
337
338
 
338
339
  it "respects the given type/subtype arguments" do
@@ -367,14 +368,14 @@ describe HexaPDF::Document do
367
368
 
368
369
  it "recursively unwraps hashes" do
369
370
  assert_equal({a: 5, b: 10, c: [200], d: [200]},
370
- @io_doc.unwrap(a: 5, b: HexaPDF::Reference.new(1, 0),
371
- c: [HexaPDF::Reference.new(2, 0)],
372
- d: [HexaPDF::Reference.new(2, 0)]))
371
+ @io_doc.unwrap({a: 5, b: HexaPDF::Reference.new(1, 0),
372
+ c: [HexaPDF::Reference.new(2, 0)],
373
+ d: [HexaPDF::Reference.new(2, 0)]}))
373
374
  end
374
375
 
375
376
  it "recursively unwraps PDF objects" do
376
- assert_equal({a: 10}, @io_doc.unwrap(@io_doc.wrap(a: HexaPDF::Reference.new(1, 0))))
377
- value = {a: HexaPDF::Object.new(b: HexaPDF::Object.new(10))}
377
+ assert_equal({a: 10}, @io_doc.unwrap(@io_doc.wrap({a: HexaPDF::Reference.new(1, 0)})))
378
+ value = {a: HexaPDF::Object.new({b: HexaPDF::Object.new(10)})}
378
379
  assert_equal({a: {b: 10}}, @doc.unwrap(value))
379
380
  end
380
381
 
@@ -383,7 +384,7 @@ describe HexaPDF::Document do
383
384
  obj2 = @doc.add({})
384
385
  obj1.value[2] = obj2
385
386
  obj2.value[1] = obj1
386
- assert_raises(HexaPDF::Error) { @doc.unwrap(a: obj1) }
387
+ assert_raises(HexaPDF::Error) { @doc.unwrap({a: obj1}) }
387
388
  end
388
389
  end
389
390
 
@@ -444,7 +445,7 @@ describe HexaPDF::Document do
444
445
  end
445
446
 
446
447
  it "validates indirect objects" do
447
- obj = @doc.add(Type: :Catalog)
448
+ obj = @doc.add({Type: :Catalog})
448
449
  refute(@doc.validate(auto_correct: false))
449
450
 
450
451
  called = false
@@ -462,7 +463,7 @@ describe HexaPDF::Document do
462
463
  doc = HexaPDF::Document.new
463
464
  doc.pages.add.delete(:Resources)
464
465
  page = doc.pages.add
465
- page[:Annots] = [doc.add(Type: :Annot, Subtype: :Link, Rect: [0, 0, 1, 1], H: :Z)]
466
+ page[:Annots] = [doc.add({Type: :Annot, Subtype: :Link, Rect: [0, 0, 1, 1], H: :Z})]
466
467
  doc.write(io, validate: false)
467
468
  doc = HexaPDF::Document.new(io: io)
468
469
  doc.pages[0] # force loading of the first page
@@ -22,10 +22,10 @@ describe HexaPDF::Importer do
22
22
  before do
23
23
  @source = HexaPDF::Document.new
24
24
  obj = @source.add("test")
25
- @hash = @source.wrap(key: "value")
26
- @obj = @source.add(hash: @hash, array: ["one", "two"],
27
- ref: HexaPDF::Reference.new(obj.oid, obj.gen),
28
- others: [:symbol, 5, 5.5, nil, true, false])
25
+ @hash = @source.wrap({key: "value"})
26
+ @obj = @source.add({hash: @hash, array: ["one", "two"],
27
+ ref: HexaPDF::Reference.new(obj.oid, obj.gen),
28
+ others: [:symbol, 5, 5.5, nil, true, false]})
29
29
  @source.pages.add
30
30
  @source.pages.root[:Rotate] = 90
31
31
  @dest = HexaPDF::Document.new
@@ -167,7 +167,7 @@ describe HexaPDF::Object do
167
167
 
168
168
  describe "deep_copy" do
169
169
  it "creates an independent object" do
170
- obj = HexaPDF::Object.new(a: "mystring", b: HexaPDF::Reference.new(1, 0), c: 5)
170
+ obj = HexaPDF::Object.new({a: "mystring", b: HexaPDF::Reference.new(1, 0), c: 5})
171
171
  copy = obj.deep_copy
172
172
  refute_equal(copy, obj)
173
173
  assert_equal(copy.value, obj.value)
@@ -0,0 +1,162 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/pdf_array'
5
+ require 'hexapdf/reference'
6
+ require 'hexapdf/dictionary'
7
+
8
+ describe HexaPDF::PDFArray do
9
+ def deref(obj)
10
+ if obj.kind_of?(HexaPDF::Reference)
11
+ HexaPDF::Object.new('deref', oid: obj.oid, gen: obj.gen)
12
+ else
13
+ obj
14
+ end
15
+ end
16
+
17
+ def add(obj)
18
+ HexaPDF::Object.new(obj, oid: 1)
19
+ end
20
+
21
+ def delete(_obj)
22
+ end
23
+
24
+ def wrap(obj, type:)
25
+ type.new(obj, document: self)
26
+ end
27
+
28
+ before do
29
+ @array = HexaPDF::PDFArray.new([1, HexaPDF::Object.new(:data), HexaPDF::Reference.new(1, 0),
30
+ HexaPDF::Dictionary.new({a: 'b'})], document: self)
31
+ end
32
+
33
+ describe "after_data_change" do
34
+ it "uses an empty array if nil is provided" do
35
+ array = HexaPDF::PDFArray.new(nil)
36
+ assert_equal([], array.value)
37
+ end
38
+ it "fails if the value is not an array" do
39
+ assert_raises(ArgumentError) { HexaPDF::PDFArray.new(:Name) }
40
+ end
41
+ end
42
+
43
+ describe "[]" do
44
+ it "allows retrieving values by index" do
45
+ assert_equal(1, @array[0])
46
+ end
47
+
48
+ it "allows retrieving values by start/length" do
49
+ assert_equal([1, :data], @array[0, 2])
50
+ end
51
+
52
+ it "allows retrieving values by range" do
53
+ assert_equal([1, :data], @array[0..1])
54
+ end
55
+
56
+ it "fetches the value out of a HexaPDF::Object" do
57
+ assert_equal(:data, @array[1])
58
+ end
59
+
60
+ it "resolves references and stores the resolved object in place of the reference" do
61
+ assert_equal('deref', @array[2])
62
+ assert_kind_of(HexaPDF::Object, @array.value[2])
63
+ end
64
+ end
65
+
66
+ describe "[]=" do
67
+ it "directly stores the value if the stored value is no HexaPDF::Object" do
68
+ @array[0] = 2
69
+ assert_equal(2, @array.value[0])
70
+ end
71
+
72
+ it "stores the value in an existing HexaPDF::Object but only if it is not such an object" do
73
+ @array[1] = [4, 5]
74
+ assert_equal([4, 5], @array.value[1].value)
75
+
76
+ @array[1] = temp = HexaPDF::Object.new(:other)
77
+ assert_equal(temp, @array.value[1])
78
+ end
79
+
80
+ it "doesn't store the value inside the existing object for subclasses of HexaPDF::Object" do
81
+ @array[3] = [4, 5]
82
+ assert_equal([4, 5], @array.value[3])
83
+ end
84
+
85
+ it "doesn't store the value inside for HexaPDF::Reference objects" do
86
+ @array[1] = HexaPDF::Reference.new(5, 0)
87
+ assert_kind_of(HexaPDF::Reference, @array.value[1])
88
+ end
89
+ end
90
+
91
+ it "allows getting multiple values at once" do
92
+ assert_equal([1, :data, @array[3]], @array.values_at(0, 1, 3))
93
+ end
94
+
95
+ it "allows adding values to the end" do
96
+ @array << 5
97
+ assert_equal(5, @array[4])
98
+ end
99
+
100
+ it "allows inserting values like Array#insert" do
101
+ @array.insert(1, :a, :b)
102
+ assert_equal([1, :a, :b, :data], @array[0, 4])
103
+ end
104
+
105
+ it "allows deleting values at a certain index" do
106
+ @array.delete_at(2)
107
+ assert_equal([1, :data, @array[2]], @array[0, 5])
108
+ end
109
+
110
+ describe "slice!" do
111
+ it "allows deleting a single element" do
112
+ @array.slice!(2)
113
+ assert_equal([1, :data, @array[2]], @array[0, 5])
114
+ end
115
+
116
+ it "allows deleting elements given by start/length" do
117
+ @array.slice!(1, 2)
118
+ assert_equal([1, @array[1]], @array[0, 5])
119
+ end
120
+
121
+ it "allows deleting elements given a range" do
122
+ @array.slice!(1..2)
123
+ assert_equal([1, @array[1]], @array[0, 5])
124
+ end
125
+ end
126
+
127
+ it "allows deleting elements that are selected using a block" do
128
+ @array.reject! {|item| item == :data }
129
+ assert_equal([1, "deref", @array[2]], @array[0, 5])
130
+ end
131
+
132
+ describe "index" do
133
+ it "allows getting the index of an element" do
134
+ assert_equal(2, @array.index("deref"))
135
+ end
136
+
137
+ it "allows getting the index of the first element for which a block returns true" do
138
+ assert_equal(2, @array.index {|item| item == "deref" })
139
+ end
140
+ end
141
+
142
+ it "returns the length of the array" do
143
+ assert_equal(4, @array.length)
144
+ end
145
+
146
+ it "allows checking for emptiness" do
147
+ refute(@array.empty?)
148
+ @array.slice!(0, 5)
149
+ assert(@array.empty?)
150
+ end
151
+
152
+ describe "each" do
153
+ it "iterates over all elements in the dictionary" do
154
+ data = [1, :data, "deref", @array[3]]
155
+ @array.each {|value| assert_equal(data.shift, value) }
156
+ end
157
+ end
158
+
159
+ it "can be converted to a simple array" do
160
+ assert_equal(@array.value, @array.to_ary)
161
+ end
162
+ end