hexapdf 0.12.3 → 0.13.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/lib/hexapdf/cli/command.rb +4 -2
  4. data/lib/hexapdf/cli/image2pdf.rb +2 -1
  5. data/lib/hexapdf/cli/info.rb +51 -2
  6. data/lib/hexapdf/cli/inspect.rb +30 -8
  7. data/lib/hexapdf/cli/merge.rb +1 -1
  8. data/lib/hexapdf/configuration.rb +15 -0
  9. data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
  10. data/lib/hexapdf/dictionary.rb +4 -4
  11. data/lib/hexapdf/dictionary_fields.rb +1 -9
  12. data/lib/hexapdf/document.rb +31 -12
  13. data/lib/hexapdf/document/files.rb +0 -1
  14. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  15. data/lib/hexapdf/encryption/security_handler.rb +1 -0
  16. data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
  17. data/lib/hexapdf/font/cmap.rb +1 -4
  18. data/lib/hexapdf/font/true_type/table/head.rb +1 -0
  19. data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
  20. data/lib/hexapdf/image_loader/png.rb +3 -2
  21. data/lib/hexapdf/layout/line.rb +1 -1
  22. data/lib/hexapdf/layout/style.rb +23 -23
  23. data/lib/hexapdf/layout/text_shaper.rb +3 -2
  24. data/lib/hexapdf/object.rb +30 -25
  25. data/lib/hexapdf/parser.rb +65 -3
  26. data/lib/hexapdf/pdf_array.rb +9 -2
  27. data/lib/hexapdf/revisions.rb +29 -21
  28. data/lib/hexapdf/serializer.rb +1 -1
  29. data/lib/hexapdf/task/optimize.rb +6 -4
  30. data/lib/hexapdf/type/acro_form/choice_field.rb +4 -4
  31. data/lib/hexapdf/type/acro_form/field.rb +35 -5
  32. data/lib/hexapdf/type/acro_form/form.rb +6 -4
  33. data/lib/hexapdf/type/acro_form/text_field.rb +2 -1
  34. data/lib/hexapdf/type/actions/uri.rb +3 -2
  35. data/lib/hexapdf/type/annotations/widget.rb +3 -4
  36. data/lib/hexapdf/type/catalog.rb +2 -2
  37. data/lib/hexapdf/type/file_specification.rb +1 -1
  38. data/lib/hexapdf/type/font_simple.rb +3 -1
  39. data/lib/hexapdf/type/font_true_type.rb +6 -2
  40. data/lib/hexapdf/type/font_type0.rb +1 -1
  41. data/lib/hexapdf/type/form.rb +2 -1
  42. data/lib/hexapdf/type/image.rb +2 -2
  43. data/lib/hexapdf/type/page.rb +16 -7
  44. data/lib/hexapdf/type/page_tree_node.rb +29 -5
  45. data/lib/hexapdf/type/resources.rb +1 -0
  46. data/lib/hexapdf/type/trailer.rb +2 -3
  47. data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
  48. data/lib/hexapdf/version.rb +1 -1
  49. data/test/hexapdf/common_tokenizer_tests.rb +2 -2
  50. data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
  51. data/test/hexapdf/content/test_canvas.rb +3 -3
  52. data/test/hexapdf/content/test_color_space.rb +1 -1
  53. data/test/hexapdf/encryption/test_aes.rb +4 -4
  54. data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
  55. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  56. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  57. data/test/hexapdf/layout/test_text_layouter.rb +3 -4
  58. data/test/hexapdf/test_configuration.rb +2 -2
  59. data/test/hexapdf/test_dictionary.rb +3 -1
  60. data/test/hexapdf/test_dictionary_fields.rb +2 -2
  61. data/test/hexapdf/test_document.rb +4 -4
  62. data/test/hexapdf/test_object.rb +44 -26
  63. data/test/hexapdf/test_parser.rb +115 -55
  64. data/test/hexapdf/test_pdf_array.rb +7 -0
  65. data/test/hexapdf/test_revisions.rb +35 -0
  66. data/test/hexapdf/test_writer.rb +2 -2
  67. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
  68. data/test/hexapdf/type/acro_form/test_field.rb +39 -0
  69. data/test/hexapdf/type/acro_form/test_form.rb +4 -4
  70. data/test/hexapdf/type/acro_form/test_text_field.rb +2 -0
  71. data/test/hexapdf/type/test_font_simple.rb +2 -1
  72. data/test/hexapdf/type/test_font_true_type.rb +6 -0
  73. data/test/hexapdf/type/test_form.rb +1 -1
  74. data/test/hexapdf/type/test_page.rb +8 -1
  75. data/test/hexapdf/type/test_page_tree_node.rb +42 -0
  76. data/test/hexapdf/utils/test_bit_field.rb +2 -0
  77. data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
  78. metadata +5 -12
@@ -243,7 +243,7 @@ module HexaPDF
243
243
  else
244
244
  obj.dup
245
245
  end
246
- obj.gsub!(/[\(\)\\\r]/n, STRING_ESCAPE_MAP)
246
+ obj.gsub!(/[()\\\r]/n, STRING_ESCAPE_MAP)
247
247
  "(#{obj})"
248
248
  end
249
249
 
@@ -129,9 +129,10 @@ module HexaPDF
129
129
  xref_stream = false
130
130
  objects_to_delete = []
131
131
  rev.each do |obj|
132
- if obj.type == :ObjStm
132
+ case obj.type
133
+ when :ObjStm
133
134
  objects_to_delete << obj
134
- elsif obj.type == :XRef
135
+ when :XRef
135
136
  xref_stream = true
136
137
  objects_to_delete << obj if xref_streams == :delete
137
138
  else
@@ -150,9 +151,10 @@ module HexaPDF
150
151
  objstms = [doc.wrap({Type: :ObjStm})]
151
152
  old_objstms = []
152
153
  rev.each do |obj|
153
- if obj.type == :XRef
154
+ case obj.type
155
+ when :XRef
154
156
  xref_stream = true
155
- elsif obj.type == :ObjStm
157
+ when :ObjStm
156
158
  old_objstms << obj
157
159
  end
158
160
  delete_fields_with_defaults(obj)
@@ -148,10 +148,10 @@ module HexaPDF
148
148
  def default_field_value=(value)
149
149
  items = option_items
150
150
  self[:DV] = if [value].flatten.all? {|v| items.include?(v) }
151
- value
152
- else
153
- @document.config['acro_form.on_invalid_value'].call(self, value)
154
- end
151
+ value
152
+ else
153
+ @document.config['acro_form.on_invalid_value'].call(self, value)
154
+ end
155
155
  end
156
156
 
157
157
  # Returns the array with the available option items.
@@ -236,6 +236,11 @@ module HexaPDF
236
236
  kids.nil? || kids.empty? || kids.none? {|kid| kid.key?(:T) }
237
237
  end
238
238
 
239
+ # Returns +true+ if the field contains an embedded widget.
240
+ def embedded_widget?
241
+ key?(:Subtype)
242
+ end
243
+
239
244
  # :call-seq:
240
245
  # field.each_widget {|widget| block} -> field
241
246
  # field.each_widget -> Enumerator
@@ -245,7 +250,7 @@ module HexaPDF
245
250
  # See: HexaPDF::Type::Annotations::Widget
246
251
  def each_widget # :yields: widget
247
252
  return to_enum(__method__) unless block_given?
248
- if self[:Subtype]
253
+ if embedded_widget?
249
254
  yield(document.wrap(self))
250
255
  elsif terminal_field?
251
256
  self[:Kids]&.each {|kid| yield(document.wrap(kid)) }
@@ -275,9 +280,9 @@ module HexaPDF
275
280
 
276
281
  widget_data = {Type: :Annot, Subtype: :Widget, Rect: [0, 0, 0, 0], **values}
277
282
 
278
- if !allow_embedded || key?(:Subtype) || (key?(:Kids) && !self[:Kids].empty?)
283
+ if !allow_embedded || embedded_widget? || (key?(:Kids) && !self[:Kids].empty?)
279
284
  kids = self[:Kids] ||= []
280
- kids << extract_widget if key?(:Subtype)
285
+ kids << extract_widget if embedded_widget?
281
286
  widget = document.add(widget_data)
282
287
  widget[:Parent] = self
283
288
  self[:Kids] << widget
@@ -291,6 +296,31 @@ module HexaPDF
291
296
  widget
292
297
  end
293
298
 
299
+ # Deletes the given widget annotation object from this field, the page it appears on and the
300
+ # document.
301
+ #
302
+ # If the given widget is not a widget of this field, nothing is done.
303
+ def delete_widget(widget)
304
+ widget = if embedded_widget? && self == widget
305
+ widget
306
+ elsif terminal_field?
307
+ (widget_index = self[:Kids]&.index(widget)) && widget
308
+ end
309
+
310
+ return unless widget
311
+
312
+ document.pages.each do |page|
313
+ break if page[:Annots]&.delete(widget) # See comment in #extract_widget
314
+ end
315
+
316
+ if embedded_widget?
317
+ WIDGET_FIELDS.each {|key| delete(key) }
318
+ else
319
+ self[:Kids].delete_at(widget_index)
320
+ document.delete(widget)
321
+ end
322
+ end
323
+
294
324
  private
295
325
 
296
326
  # An array of all widget annotation field names.
@@ -300,14 +330,14 @@ module HexaPDF
300
330
  # directly in the field and adjust the references accordingly. If the field doesn't have any
301
331
  # widget data, +nil+ is returned.
302
332
  def extract_widget
303
- return unless key?(:Subtype)
333
+ return unless embedded_widget?
304
334
  data = WIDGET_FIELDS.each_with_object({}) do |key, hash|
305
335
  hash[key] = delete(key) if key?(key)
306
336
  end
307
337
  widget = document.add(data, type: :Annot)
308
338
  widget[:Parent] = self
309
339
  document.pages.each do |page|
310
- if page.key?(:Annots) && (index = page[:Annots].index {|annot| annot.data == self.data })
340
+ if page.key?(:Annots) && (index = page[:Annots].index(self))
311
341
  page[:Annots][index] = widget
312
342
  break # Each annotation dictionary may only appear on one page, see PDF1.7 12.5.2
313
343
  end
@@ -191,7 +191,8 @@ module HexaPDF
191
191
 
192
192
  # Returns the dictionary containing the default resources for form field appearance streams.
193
193
  def default_resources
194
- self[:DR] ||= document.wrap({}, type: :XXResources)
194
+ self[:DR] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
195
+ type: :XXResources)
195
196
  end
196
197
 
197
198
  # Sets the global default appearance string using the provided values.
@@ -249,14 +250,15 @@ module HexaPDF
249
250
  end
250
251
 
251
252
  def perform_validation # :nodoc:
253
+ super
254
+
252
255
  if (da = self[:DA])
253
256
  unless self[:DR]
254
257
  yield("When the field /DA is present, the field /DR must also be present")
258
+ return
255
259
  end
256
260
  font_name = nil
257
- HexaPDF::Content::Parser.parse(da) do |obj, params|
258
- font_name = params[0] if obj == :Tf
259
- end
261
+ HexaPDF::Content::Parser.parse(da) {|obj, params| font_name = params[0] if obj == :Tf }
260
262
  if font_name && !(self[:DR][:Font] && self[:DR][:Font][font_name])
261
263
  yield("The font specified in /DA is not in the /DR resource dictionary")
262
264
  end
@@ -173,8 +173,9 @@ module HexaPDF
173
173
 
174
174
  if self[:V] && !(self[:V].kind_of?(String) || self[:V].kind_of?(HexaPDF::Stream))
175
175
  yield("Text field doesn't contain text but #{self[:V].class} object")
176
+ return
176
177
  end
177
- if (max_len = self[:MaxLen]) && field_value.length > max_len
178
+ if (max_len = self[:MaxLen]) && field_value && field_value.length > max_len
178
179
  yield("Text contents of field '#{full_field_name}' is too long")
179
180
  end
180
181
  end
@@ -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
 
@@ -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
@@ -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
@@ -59,7 +59,7 @@ module HexaPDF
59
59
  # Returns the CID font of this type 0 font.
60
60
  def descendant_font
61
61
  document.cache(@data, :descendant_font) do
62
- document.wrap(document.deref(self[:DescendantFonts][0]))
62
+ document.wrap(self[:DescendantFonts][0])
63
63
  end
64
64
  end
65
65
 
@@ -101,7 +101,8 @@ module HexaPDF
101
101
 
102
102
  # Returns the resource dictionary which is automatically created if it doesn't exist.
103
103
  def resources
104
- self[:Resources] ||= document.wrap({}, type: :XXResources)
104
+ self[:Resources] ||= document.wrap({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]},
105
+ type: :XXResources)
105
106
  end
106
107
 
107
108
  # Processes the content stream of the form XObject with the given processor object.
@@ -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
@@ -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,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 ||
@@ -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