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.
- 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
|