hexapdf 0.10.0 → 0.11.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.
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