hexapdf 0.12.3 → 0.14.3

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +132 -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/dictionary.rb +12 -6
  13. data/lib/hexapdf/dictionary_fields.rb +2 -10
  14. data/lib/hexapdf/document.rb +41 -16
  15. data/lib/hexapdf/document/files.rb +0 -1
  16. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  17. data/lib/hexapdf/encryption/security_handler.rb +1 -0
  18. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
  19. data/lib/hexapdf/font/cmap.rb +1 -4
  20. data/lib/hexapdf/font/true_type/subsetter.rb +16 -3
  21. data/lib/hexapdf/font/true_type/table/head.rb +1 -0
  22. data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
  23. data/lib/hexapdf/font/true_type/table/post.rb +15 -10
  24. data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
  25. data/lib/hexapdf/font_loader/from_file.rb +18 -8
  26. data/lib/hexapdf/image_loader/png.rb +3 -2
  27. data/lib/hexapdf/importer.rb +3 -2
  28. data/lib/hexapdf/layout/line.rb +1 -1
  29. data/lib/hexapdf/layout/style.rb +23 -23
  30. data/lib/hexapdf/layout/text_layouter.rb +2 -2
  31. data/lib/hexapdf/layout/text_shaper.rb +3 -2
  32. data/lib/hexapdf/object.rb +52 -25
  33. data/lib/hexapdf/parser.rb +107 -7
  34. data/lib/hexapdf/pdf_array.rb +15 -5
  35. data/lib/hexapdf/revisions.rb +29 -21
  36. data/lib/hexapdf/serializer.rb +37 -10
  37. data/lib/hexapdf/task/optimize.rb +6 -4
  38. data/lib/hexapdf/tokenizer.rb +22 -0
  39. data/lib/hexapdf/type/acro_form/appearance_generator.rb +130 -27
  40. data/lib/hexapdf/type/acro_form/button_field.rb +5 -2
  41. data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
  42. data/lib/hexapdf/type/acro_form/field.rb +35 -5
  43. data/lib/hexapdf/type/acro_form/form.rb +139 -14
  44. data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
  45. data/lib/hexapdf/type/actions/uri.rb +3 -2
  46. data/lib/hexapdf/type/annotations/widget.rb +3 -4
  47. data/lib/hexapdf/type/catalog.rb +2 -2
  48. data/lib/hexapdf/type/cid_font.rb +1 -1
  49. data/lib/hexapdf/type/file_specification.rb +1 -1
  50. data/lib/hexapdf/type/font.rb +1 -1
  51. data/lib/hexapdf/type/font_simple.rb +4 -2
  52. data/lib/hexapdf/type/font_true_type.rb +6 -2
  53. data/lib/hexapdf/type/font_type0.rb +4 -4
  54. data/lib/hexapdf/type/form.rb +6 -2
  55. data/lib/hexapdf/type/image.rb +2 -2
  56. data/lib/hexapdf/type/page.rb +21 -12
  57. data/lib/hexapdf/type/page_tree_node.rb +29 -5
  58. data/lib/hexapdf/type/resources.rb +5 -0
  59. data/lib/hexapdf/type/trailer.rb +2 -3
  60. data/lib/hexapdf/utils/object_hash.rb +0 -1
  61. data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
  62. data/lib/hexapdf/version.rb +1 -1
  63. data/test/hexapdf/common_tokenizer_tests.rb +2 -2
  64. data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
  65. data/test/hexapdf/content/test_canvas.rb +3 -3
  66. data/test/hexapdf/content/test_color_space.rb +1 -1
  67. data/test/hexapdf/encryption/test_aes.rb +4 -4
  68. data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
  69. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  70. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  71. data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
  72. data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
  73. data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
  74. data/test/hexapdf/font_loader/test_from_file.rb +7 -0
  75. data/test/hexapdf/layout/test_text_layouter.rb +12 -5
  76. data/test/hexapdf/test_configuration.rb +2 -2
  77. data/test/hexapdf/test_dictionary.rb +8 -1
  78. data/test/hexapdf/test_dictionary_fields.rb +9 -2
  79. data/test/hexapdf/test_document.rb +18 -10
  80. data/test/hexapdf/test_object.rb +71 -26
  81. data/test/hexapdf/test_parser.rb +205 -51
  82. data/test/hexapdf/test_pdf_array.rb +8 -1
  83. data/test/hexapdf/test_revisions.rb +35 -0
  84. data/test/hexapdf/test_serializer.rb +7 -0
  85. data/test/hexapdf/test_tokenizer.rb +28 -0
  86. data/test/hexapdf/test_writer.rb +2 -2
  87. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +288 -35
  88. data/test/hexapdf/type/acro_form/test_button_field.rb +15 -0
  89. data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
  90. data/test/hexapdf/type/acro_form/test_field.rb +39 -0
  91. data/test/hexapdf/type/acro_form/test_form.rb +87 -15
  92. data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
  93. data/test/hexapdf/type/test_font_simple.rb +2 -1
  94. data/test/hexapdf/type/test_font_true_type.rb +6 -0
  95. data/test/hexapdf/type/test_form.rb +8 -1
  96. data/test/hexapdf/type/test_page.rb +8 -1
  97. data/test/hexapdf/type/test_page_tree_node.rb +42 -0
  98. data/test/hexapdf/type/test_resources.rb +6 -0
  99. data/test/hexapdf/utils/test_bit_field.rb +2 -0
  100. data/test/hexapdf/utils/test_object_hash.rb +5 -0
  101. data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
  102. data/test/test_helper.rb +2 -0
  103. metadata +6 -12
@@ -53,9 +53,10 @@ module HexaPDF
53
53
 
54
54
  def perform_validation #:nodoc:
55
55
  super
56
- unless self[:URI].ascii_only?
56
+ uri = self[:URI]
57
+ if uri && !uri.ascii_only?
57
58
  yield("URIs have to contain ASCII characters only", true)
58
- uri = self[:URI].dup.force_encoding(Encoding::BINARY)
59
+ uri = uri.dup.force_encoding(Encoding::BINARY)
59
60
  uri.encode!(Encoding::US_ASCII, fallback: lambda {|c| "%#{c.ord.to_s(16).upcase}" })
60
61
  self[:URI] = uri
61
62
  end
@@ -307,10 +307,9 @@ module HexaPDF
307
307
  color = [0]
308
308
  if (da = self[:DA] || field[:DA])
309
309
  HexaPDF::Content::Parser.parse(da) do |obj, params|
310
- if obj == :rg || obj == :g || obj == :k
311
- color = params.dup
312
- elsif obj == :Tf
313
- size = params[1]
310
+ case obj
311
+ when :rg, :g, :k then color = params.dup
312
+ when :Tf then size = params[1]
314
313
  end
315
314
  end
316
315
  end
@@ -120,12 +120,12 @@ module HexaPDF
120
120
  private
121
121
 
122
122
  # Ensures that there is a valid page tree.
123
- def perform_validation
123
+ def perform_validation(&block)
124
124
  super
125
125
  unless key?(:Pages)
126
126
  yield("A PDF document needs a page tree", true)
127
127
  value[:Pages] = document.add({Type: :Pages})
128
- value[:Pages].validate {|msg, correctable| yield(msg, correctable) }
128
+ value[:Pages].validate(&block)
129
129
  end
130
130
  end
131
131
 
@@ -95,7 +95,7 @@ module HexaPDF
95
95
  #
96
96
  # See: PDF1.7 s9.7.4.3
97
97
  def widths
98
- document.cache(@data, :widths) do
98
+ cache(:widths) do
99
99
  result = {}
100
100
  index = 0
101
101
  array = self[:W] || []
@@ -220,7 +220,7 @@ module HexaPDF
220
220
 
221
221
  if document.catalog.key?(:Names) && document.catalog[:Names].key?(:EmbeddedFiles)
222
222
  tree = document.catalog[:Names][:EmbeddedFiles]
223
- tree.each_entry.find_all {|_, spec| document.deref(spec) == self }.each do |(name, _)|
223
+ tree.each_entry.find_all {|_, spec| spec == self }.each do |(name, _)|
224
224
  tree.delete_entry(name)
225
225
  end
226
226
  end
@@ -102,7 +102,7 @@ module HexaPDF
102
102
 
103
103
  # Parses and caches the ToUnicode CMap.
104
104
  def to_unicode_cmap
105
- document.cache(@data, :to_unicode_cmap) do
105
+ cache(:to_unicode_cmap) do
106
106
  if key?(:ToUnicode)
107
107
  HexaPDF::Font::CMap.parse(self[:ToUnicode].stream)
108
108
  else
@@ -57,7 +57,7 @@ module HexaPDF
57
57
  #
58
58
  # Note that the encoding is cached internally when accessed the first time.
59
59
  def encoding
60
- document.cache(@data, :encoding) do
60
+ cache(:encoding) do
61
61
  case (val = self[:Encoding])
62
62
  when Symbol
63
63
  encoding = HexaPDF::Font::Encoding.for_name(val)
@@ -170,7 +170,9 @@ module HexaPDF
170
170
  [:FirstChar, :LastChar, :Widths].each do |field|
171
171
  yield("Required field #{field} is not set", false) if self[field].nil?
172
172
  end
173
- if self[:Widths].length != (self[:LastChar] - self[:FirstChar] + 1)
173
+
174
+ if key?(:Widths) && key?(:LastChar) && key?(:FirstChar) &&
175
+ self[:Widths].length != (self[:LastChar] - self[:FirstChar] + 1)
174
176
  yield("Invalid number of entries in field Widths", false)
175
177
  end
176
178
  end
@@ -48,8 +48,12 @@ module HexaPDF
48
48
  private
49
49
 
50
50
  def perform_validation
51
- super
52
- yield("Required field FontDescriptor is not set", false) if self[:FontDescriptor].nil?
51
+ std_font = FontType1::StandardFonts.standard_font?(self[:BaseFont])
52
+ super(ignore_missing_font_fields: std_font)
53
+
54
+ if self[:FontDescriptor].nil? && !std_font
55
+ yield("Required field FontDescriptor is not set", false)
56
+ end
53
57
  end
54
58
 
55
59
  end
@@ -58,8 +58,8 @@ module HexaPDF
58
58
 
59
59
  # Returns the CID font of this type 0 font.
60
60
  def descendant_font
61
- document.cache(@data, :descendant_font) do
62
- document.wrap(document.deref(self[:DescendantFonts][0]))
61
+ cache(:descendant_font) do
62
+ document.wrap(self[:DescendantFonts][0])
63
63
  end
64
64
  end
65
65
 
@@ -116,7 +116,7 @@ module HexaPDF
116
116
  #
117
117
  # Note that the CMap is cached internally when accessed the first time.
118
118
  def cmap
119
- document.cache(@data, :cmap) do
119
+ cache(:cmap) do
120
120
  val = self[:Encoding]
121
121
  if val.kind_of?(Symbol)
122
122
  HexaPDF::Font::CMap.for_name(val.to_s)
@@ -135,7 +135,7 @@ module HexaPDF
135
135
  #
136
136
  # See: PDF1.7 s9.10.2
137
137
  def ucs2_cmap
138
- document.cache(@data, :ucs2_cmap) do
138
+ cache(:ucs2_cmap) do
139
139
  encoding = self[:Encoding]
140
140
  system_info = descendant_font[:CIDSystemInfo]
141
141
  registry = system_info[:Registry]
@@ -94,14 +94,18 @@ module HexaPDF
94
94
 
95
95
  # Replaces the contents of the form XObject with the given string.
96
96
  #
97
+ # This also clears the cache to avoid returning invalid objects.
98
+ #
97
99
  # Note: This is the same as #stream= but here for interface compatibility with Page.
98
100
  def contents=(data)
99
101
  self.stream = data
102
+ clear_cache
100
103
  end
101
104
 
102
105
  # Returns the resource dictionary which is automatically created if it doesn't exist.
103
106
  def resources
104
- self[:Resources] ||= document.wrap({}, type: :XXResources)
107
+ self[:Resources] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
108
+ type: :XXResources)
105
109
  end
106
110
 
107
111
  # Processes the content stream of the form XObject with the given processor object.
@@ -133,7 +137,7 @@ module HexaPDF
133
137
  #
134
138
  # *Note* that a canvas can only be retrieved for initially empty form XObjects!
135
139
  def canvas
136
- document.cache(@data, :canvas) do
140
+ cache(:canvas) do
137
141
  unless stream.empty?
138
142
  raise HexaPDF::Error, "Cannot create a canvas for a form XObjects with contents"
139
143
  end
@@ -150,7 +150,7 @@ module HexaPDF
150
150
  color_space, = *self[:ColorSpace]
151
151
  if color_space == :Indexed
152
152
  result.indexed = true
153
- color_space, = *document.deref(self[:ColorSpace][1])
153
+ color_space, = *self[:ColorSpace][1]
154
154
  end
155
155
  case color_space
156
156
  when :DeviceRGB, :CalRGB
@@ -240,7 +240,7 @@ module HexaPDF
240
240
  end
241
241
 
242
242
  if color_type == ImageLoader::PNG::INDEXED
243
- palette_data = document.deref(self[:ColorSpace][3])
243
+ palette_data = self[:ColorSpace][3]
244
244
  palette_data = palette_data.stream unless palette_data.kind_of?(String)
245
245
  palette = ''.b
246
246
  if info.color_space == :rgb
@@ -304,7 +304,7 @@ module HexaPDF
304
304
  def contents
305
305
  Array(self[:Contents]).each_with_object("".b) do |content_stream, content|
306
306
  content << " " unless content.empty?
307
- content << document.deref(content_stream).stream
307
+ content << content_stream.stream
308
308
  end
309
309
  end
310
310
 
@@ -323,10 +323,11 @@ module HexaPDF
323
323
  end
324
324
  end
325
325
 
326
- # Returns the possibly inherited resource dictionary which is automatically created if it
326
+ # Returns the, possibly inherited, resource dictionary which is automatically created if it
327
327
  # doesn't exist.
328
328
  def resources
329
- self[:Resources] ||= document.wrap({}, type: :XXResources)
329
+ self[:Resources] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
330
+ type: :XXResources)
330
331
  end
331
332
 
332
333
  # Processes the content streams associated with the page with the given processor object.
@@ -344,8 +345,7 @@ module HexaPDF
344
345
  node = self
345
346
  while (parent_node = node[:Parent])
346
347
  parent_node[:Kids].each do |kid|
347
- kid = document.deref(kid)
348
- break if kid.data == node.data
348
+ break if kid == node
349
349
  idx += (kid.type == :Page ? 1 : kid[:Count])
350
350
  end
351
351
  node = parent_node
@@ -353,6 +353,16 @@ module HexaPDF
353
353
  idx
354
354
  end
355
355
 
356
+ # Returns all parent nodes of the page up to the root of the page tree.
357
+ #
358
+ # The direct parent is the first node in the array and the root node the last.
359
+ def ancestor_nodes
360
+ parent = self[:Parent]
361
+ result = [parent]
362
+ result << parent while (parent = parent[:Parent])
363
+ result
364
+ end
365
+
356
366
  # Returns the requested type of canvas for the page.
357
367
  #
358
368
  # The canvas object is cached once it is created so that its graphics state is correctly
@@ -373,7 +383,7 @@ module HexaPDF
373
383
  raise ArgumentError, "Invalid value for 'type', expected: :page, :underlay or :overlay"
374
384
  end
375
385
  cache_key = "#{type}_canvas".intern
376
- return document.cache(@data, cache_key) if document.cached?(@data, cache_key)
386
+ return cache(cache_key) if cached?(cache_key)
377
387
 
378
388
  if type == :page && key?(:Contents)
379
389
  raise HexaPDF::Error, "Cannot get the canvas for a page with contents"
@@ -390,14 +400,14 @@ module HexaPDF
390
400
 
391
401
  contents = self[:Contents]
392
402
  if contents.nil?
393
- page_canvas = document.cache(@data, :page_canvas, create_canvas.call)
403
+ page_canvas = cache(:page_canvas, create_canvas.call)
394
404
  self[:Contents] = document.add({Filter: :FlateDecode},
395
405
  stream: page_canvas.stream_data)
396
406
  end
397
407
 
398
408
  if type == :overlay || type == :underlay
399
- underlay_canvas = document.cache(@data, :underlay_canvas, create_canvas.call)
400
- overlay_canvas = document.cache(@data, :overlay_canvas, create_canvas.call)
409
+ underlay_canvas = cache(:underlay_canvas, create_canvas.call)
410
+ overlay_canvas = cache(:overlay_canvas, create_canvas.call)
401
411
 
402
412
  stream = HexaPDF::StreamData.new do
403
413
  Fiber.yield(" q ")
@@ -422,7 +432,7 @@ module HexaPDF
422
432
  self[:Contents] = [underlay, *self[:Contents], overlay]
423
433
  end
424
434
 
425
- document.cache(@data, cache_key)
435
+ cache(cache_key)
426
436
  end
427
437
 
428
438
  # Creates a Form XObject from the page's dictionary and contents for the given PDF document.
@@ -463,8 +473,7 @@ module HexaPDF
463
473
  REQUIRED_INHERITABLE_FIELDS.each do |name|
464
474
  next if self[name]
465
475
  yield("Inheritable page field #{name} not set", name == :Resources)
466
- self[:Resources] = {}
467
- self[:Resources].validate(&block)
476
+ resources.validate(&block) if name == :Ressources
468
477
  end
469
478
  end
470
479
 
@@ -172,11 +172,10 @@ module HexaPDF
172
172
  return unless page && !page.null? && page[:Parent]
173
173
 
174
174
  parent = page[:Parent]
175
- index = parent[:Kids].index {|kid| kid.data == page.data }
175
+ index = parent[:Kids].index(page)
176
176
 
177
177
  if index
178
- ancestors = [parent]
179
- ancestors << parent while (parent = parent[:Parent])
178
+ ancestors = page.ancestor_nodes
180
179
  return nil unless ancestors.include?(self)
181
180
 
182
181
  page[:Parent][:Kids].delete_at(index)
@@ -188,6 +187,31 @@ module HexaPDF
188
187
  end
189
188
  end
190
189
 
190
+ # :call-seq:
191
+ # pages.move_page(page, to_index)
192
+ # pages.move_page(index, to_index)
193
+ #
194
+ # Moves the given page or the page at the position specified by the zero-based index to the
195
+ # +to_index+ position.
196
+ #
197
+ # If the page that should be moved, doesn't exist or is invalid, an error is raised.
198
+ #
199
+ # Negative indices count backwards from the end, i.e. -1 is the last page. When using a
200
+ # negative index, the page will be moved after that element. So using an index of -1 will
201
+ # move the page after the last page.
202
+ def move_page(page, to_index)
203
+ page = self.page(page) if page.kind_of?(Integer)
204
+ if page.nil? || page.null? || !page[:Parent] ||
205
+ !(ancestors = page.ancestor_nodes).include?(self)
206
+ raise HexaPDF::Error, "The page to be moved doesn't exist in this page tree"
207
+ end
208
+
209
+ parent = page[:Parent]
210
+ insert_page(to_index, page)
211
+ ancestors.each {|node| node[:Count] -= 1 }
212
+ parent[:Kids].delete(page)
213
+ end
214
+
191
215
  # :call-seq:
192
216
  # pages.each_page {|page| block } -> pages
193
217
  # pages.each_page -> Enumerator
@@ -222,7 +246,7 @@ module HexaPDF
222
246
  # Ensures that the /Count and /Parent fields of the whole page tree are set up correctly and
223
247
  # that there is at least one page node. This is therefore only done for the root node of the
224
248
  # page tree!
225
- def perform_validation
249
+ def perform_validation(&block)
226
250
  super
227
251
  return if key?(:Parent)
228
252
 
@@ -255,7 +279,7 @@ module HexaPDF
255
279
 
256
280
  if self[:Count] == 0
257
281
  yield("A PDF document needs at least one page", true)
258
- add_page.validate {|msg, correctable| yield(msg, correctable) }
282
+ add_page.validate(&block)
259
283
  end
260
284
  end
261
285
 
@@ -219,8 +219,13 @@ 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
225
+ if val.kind_of?(Symbol)
226
+ yield("Procedure set is a single value instead of an Array", true)
227
+ val = value[:ProcSet] = [val]
228
+ end
224
229
  val.reject! do |name|
225
230
  case name
226
231
  when :PDF, :Text, :ImageB, :ImageC, :ImageI
@@ -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