hexapdf 0.12.3 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/dictionary.rb +4 -4
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +31 -12
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +30 -25
- data/lib/hexapdf/parser.rb +65 -3
- data/lib/hexapdf/pdf_array.rb +9 -2
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/type/acro_form/choice_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +6 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -1
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +3 -1
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/form.rb +2 -1
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +16 -7
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +3 -4
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +3 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +4 -4
- data/test/hexapdf/test_object.rb +44 -26
- data/test/hexapdf/test_parser.rb +115 -55
- data/test/hexapdf/test_pdf_array.rb +7 -0
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +4 -4
- data/test/hexapdf/type/acro_form/test_text_field.rb +2 -0
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +1 -1
- data/test/hexapdf/type/test_page.rb +8 -1
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- metadata +5 -12
data/lib/hexapdf/serializer.rb
CHANGED
@@ -129,9 +129,10 @@ module HexaPDF
|
|
129
129
|
xref_stream = false
|
130
130
|
objects_to_delete = []
|
131
131
|
rev.each do |obj|
|
132
|
-
|
132
|
+
case obj.type
|
133
|
+
when :ObjStm
|
133
134
|
objects_to_delete << obj
|
134
|
-
|
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
|
-
|
154
|
+
case obj.type
|
155
|
+
when :XRef
|
154
156
|
xref_stream = true
|
155
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
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 ||
|
283
|
+
if !allow_embedded || embedded_widget? || (key?(:Kids) && !self[:Kids].empty?)
|
279
284
|
kids = self[:Kids] ||= []
|
280
|
-
kids << extract_widget if
|
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
|
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
|
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({
|
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)
|
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
|
-
|
56
|
+
uri = self[:URI]
|
57
|
+
if uri && !uri.ascii_only?
|
57
58
|
yield("URIs have to contain ASCII characters only", true)
|
58
|
-
uri =
|
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
|
-
|
311
|
-
|
312
|
-
|
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
|
data/lib/hexapdf/type/catalog.rb
CHANGED
@@ -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
|
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|
|
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
|
-
|
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
|
-
|
52
|
-
|
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
|
data/lib/hexapdf/type/form.rb
CHANGED
@@ -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({
|
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.
|
data/lib/hexapdf/type/image.rb
CHANGED
@@ -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, = *
|
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 =
|
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
|
data/lib/hexapdf/type/page.rb
CHANGED
@@ -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 <<
|
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({
|
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
|
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
|
-
|
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
|
175
|
+
index = parent[:Kids].index(page)
|
176
176
|
|
177
177
|
if index
|
178
|
-
ancestors =
|
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
|
282
|
+
add_page.validate(&block)
|
259
283
|
end
|
260
284
|
end
|
261
285
|
|
data/lib/hexapdf/type/trailer.rb
CHANGED
@@ -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
|
-
|
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].
|
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] =
|
151
|
-
node[: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 =
|
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 =
|
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 =
|
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],
|
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 <<
|
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 =
|
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|
|
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].
|
311
|
-
unless
|
312
|
-
|
313
|
-
|
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 =
|
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
|