hexapdf 0.12.1 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +130 -0
- data/examples/019-acro_form.rb +41 -4
- 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/cli/split.rb +74 -14
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/dictionary.rb +9 -6
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +41 -16
- 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/subsetter.rb +12 -3
- 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/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_layouter.rb +2 -2
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +52 -25
- data/lib/hexapdf/parser.rb +96 -4
- data/lib/hexapdf/pdf_array.rb +12 -5
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +34 -8
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/tokenizer.rb +4 -3
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +132 -28
- data/lib/hexapdf/type/acro_form/button_field.rb +21 -13
- data/lib/hexapdf/type/acro_form/choice_field.rb +68 -14
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +139 -14
- data/lib/hexapdf/type/acro_form/text_field.rb +70 -4
- 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/cid_font.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +4 -4
- data/lib/hexapdf/type/form.rb +15 -2
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +37 -13
- 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/object_hash.rb +0 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +6 -1
- 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/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +5 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/layout/test_style.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +12 -5
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +8 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +18 -10
- data/test/hexapdf/test_object.rb +71 -26
- data/test/hexapdf/test_parser.rb +171 -53
- data/test/hexapdf/test_pdf_array.rb +8 -1
- 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 +296 -38
- data/test/hexapdf/type/acro_form/test_button_field.rb +22 -2
- data/test/hexapdf/type/acro_form/test_choice_field.rb +92 -9
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +87 -15
- data/test/hexapdf/type/acro_form/test_text_field.rb +77 -1
- 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 +26 -1
- data/test/hexapdf/type/test_page.rb +45 -7
- 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_object_hash.rb +5 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- data/test/test_helper.rb +2 -0
- metadata +6 -11
@@ -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
|
data/lib/hexapdf/type/font.rb
CHANGED
@@ -102,7 +102,7 @@ module HexaPDF
|
|
102
102
|
|
103
103
|
# Parses and caches the ToUnicode CMap.
|
104
104
|
def to_unicode_cmap
|
105
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
62
|
-
document.wrap(
|
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
|
-
|
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
|
-
|
138
|
+
cache(:ucs2_cmap) do
|
139
139
|
encoding = self[:Encoding]
|
140
140
|
system_info = descendant_font[:CIDSystemInfo]
|
141
141
|
registry = system_info[:Registry]
|
data/lib/hexapdf/type/form.rb
CHANGED
@@ -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({
|
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.
|
@@ -126,13 +130,22 @@ module HexaPDF
|
|
126
130
|
# The canvas object is cached once it is created so that its graphics state is correctly
|
127
131
|
# retained without the need for parsing its contents.
|
128
132
|
#
|
133
|
+
# If the bounding box of the form XObject doesn't have its origin at (0, 0), the canvas origin
|
134
|
+
# is translated into the bottom left corner so that this detail doesn't matter when using the
|
135
|
+
# canvas. This means that the canvas' origin is always at the bottom left corner of the
|
136
|
+
# bounding box.
|
137
|
+
#
|
129
138
|
# *Note* that a canvas can only be retrieved for initially empty form XObjects!
|
130
139
|
def canvas
|
131
|
-
|
140
|
+
cache(:canvas) do
|
132
141
|
unless stream.empty?
|
133
142
|
raise HexaPDF::Error, "Cannot create a canvas for a form XObjects with contents"
|
134
143
|
end
|
144
|
+
|
135
145
|
canvas = Content::Canvas.new(self)
|
146
|
+
if box.left != 0 || box.bottom != 0
|
147
|
+
canvas.save_graphics_state.translate(box.left, box.bottom)
|
148
|
+
end
|
136
149
|
self.stream = canvas.stream_data
|
137
150
|
set_filter(:FlateDecode)
|
138
151
|
canvas
|
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,11 +353,26 @@ 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
|
359
369
|
# retained without the need for parsing its contents.
|
360
370
|
#
|
371
|
+
# If the media box of the page doesn't have its origin at (0, 0), the canvas origin is
|
372
|
+
# translated into the bottom left corner so that this detail doesn't matter when using the
|
373
|
+
# canvas. This means that the canvas' origin is always at the bottom left corner of the media
|
374
|
+
# box.
|
375
|
+
#
|
361
376
|
# type::
|
362
377
|
# Can either be
|
363
378
|
# * :page for getting the canvas for the page itself (only valid for initially empty pages)
|
@@ -368,22 +383,31 @@ module HexaPDF
|
|
368
383
|
raise ArgumentError, "Invalid value for 'type', expected: :page, :underlay or :overlay"
|
369
384
|
end
|
370
385
|
cache_key = "#{type}_canvas".intern
|
371
|
-
return
|
386
|
+
return cache(cache_key) if cached?(cache_key)
|
372
387
|
|
373
388
|
if type == :page && key?(:Contents)
|
374
389
|
raise HexaPDF::Error, "Cannot get the canvas for a page with contents"
|
375
390
|
end
|
376
391
|
|
392
|
+
create_canvas = lambda do
|
393
|
+
Content::Canvas.new(self).tap do |canvas|
|
394
|
+
media_box = box(:media)
|
395
|
+
if media_box.left != 0 || media_box.bottom != 0
|
396
|
+
canvas.translate(media_box.left, media_box.bottom)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
377
401
|
contents = self[:Contents]
|
378
402
|
if contents.nil?
|
379
|
-
page_canvas =
|
403
|
+
page_canvas = cache(:page_canvas, create_canvas.call)
|
380
404
|
self[:Contents] = document.add({Filter: :FlateDecode},
|
381
405
|
stream: page_canvas.stream_data)
|
382
406
|
end
|
383
407
|
|
384
408
|
if type == :overlay || type == :underlay
|
385
|
-
underlay_canvas =
|
386
|
-
overlay_canvas =
|
409
|
+
underlay_canvas = cache(:underlay_canvas, create_canvas.call)
|
410
|
+
overlay_canvas = cache(:overlay_canvas, create_canvas.call)
|
387
411
|
|
388
412
|
stream = HexaPDF::StreamData.new do
|
389
413
|
Fiber.yield(" q ")
|
@@ -396,18 +420,19 @@ module HexaPDF
|
|
396
420
|
underlay = document.add({Filter: :FlateDecode}, stream: stream)
|
397
421
|
|
398
422
|
stream = HexaPDF::StreamData.new do
|
399
|
-
Fiber.yield(" Q ")
|
423
|
+
Fiber.yield(" Q q ")
|
400
424
|
fiber = overlay_canvas.stream_data.fiber
|
401
425
|
while fiber.alive? && (data = fiber.resume)
|
402
426
|
Fiber.yield(data)
|
403
427
|
end
|
428
|
+
" Q "
|
404
429
|
end
|
405
430
|
overlay = document.add({Filter: :FlateDecode}, stream: stream)
|
406
431
|
|
407
432
|
self[:Contents] = [underlay, *self[:Contents], overlay]
|
408
433
|
end
|
409
434
|
|
410
|
-
|
435
|
+
cache(cache_key)
|
411
436
|
end
|
412
437
|
|
413
438
|
# Creates a Form XObject from the page's dictionary and contents for the given PDF document.
|
@@ -448,8 +473,7 @@ module HexaPDF
|
|
448
473
|
REQUIRED_INHERITABLE_FIELDS.each do |name|
|
449
474
|
next if self[name]
|
450
475
|
yield("Inheritable page field #{name} not set", name == :Resources)
|
451
|
-
|
452
|
-
self[:Resources].validate(&block)
|
476
|
+
resources.validate(&block) if name == :Ressources
|
453
477
|
end
|
454
478
|
end
|
455
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
|