hexapdf 0.12.3 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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