origami 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/bin/gui/config.rb +2 -1
- data/bin/gui/file.rb +118 -240
- data/bin/gui/gtkhex.rb +5 -5
- data/bin/gui/hexview.rb +20 -16
- data/bin/gui/imgview.rb +1 -1
- data/bin/gui/menu.rb +138 -158
- data/bin/gui/properties.rb +46 -48
- data/bin/gui/signing.rb +183 -214
- data/bin/gui/textview.rb +1 -1
- data/bin/gui/treeview.rb +13 -7
- data/bin/gui/walker.rb +102 -71
- data/bin/gui/xrefs.rb +1 -1
- data/bin/pdf2ruby +3 -3
- data/bin/pdfcop +18 -11
- data/bin/pdfextract +14 -5
- data/bin/pdfmetadata +3 -3
- data/bin/shell/console.rb +8 -8
- data/bin/shell/hexdump.rb +4 -4
- data/examples/attachments/nested_document.rb +1 -1
- data/examples/javascript/hello_world.rb +3 -3
- data/lib/origami.rb +0 -1
- data/lib/origami/acroform.rb +3 -3
- data/lib/origami/array.rb +1 -3
- data/lib/origami/boolean.rb +1 -3
- data/lib/origami/catalog.rb +3 -9
- data/lib/origami/destinations.rb +2 -2
- data/lib/origami/dictionary.rb +15 -29
- data/lib/origami/encryption.rb +334 -692
- data/lib/origami/extensions/fdf.rb +3 -2
- data/lib/origami/extensions/ppklite.rb +5 -9
- data/lib/origami/filespec.rb +2 -2
- data/lib/origami/filters.rb +54 -36
- data/lib/origami/filters/ascii.rb +67 -49
- data/lib/origami/filters/ccitt.rb +4 -236
- data/lib/origami/filters/ccitt/tables.rb +267 -0
- data/lib/origami/filters/crypt.rb +1 -1
- data/lib/origami/filters/dct.rb +0 -1
- data/lib/origami/filters/flate.rb +3 -43
- data/lib/origami/filters/lzw.rb +62 -99
- data/lib/origami/filters/predictors.rb +135 -105
- data/lib/origami/filters/runlength.rb +34 -22
- data/lib/origami/graphics.rb +2 -2
- data/lib/origami/graphics/colors.rb +89 -63
- data/lib/origami/graphics/path.rb +14 -14
- data/lib/origami/graphics/patterns.rb +31 -33
- data/lib/origami/graphics/render.rb +0 -1
- data/lib/origami/graphics/state.rb +9 -9
- data/lib/origami/graphics/text.rb +17 -17
- data/lib/origami/graphics/xobject.rb +102 -92
- data/lib/origami/javascript.rb +91 -68
- data/lib/origami/linearization.rb +22 -20
- data/lib/origami/metadata.rb +1 -1
- data/lib/origami/name.rb +1 -3
- data/lib/origami/null.rb +1 -3
- data/lib/origami/numeric.rb +3 -13
- data/lib/origami/object.rb +100 -72
- data/lib/origami/page.rb +24 -28
- data/lib/origami/parser.rb +34 -51
- data/lib/origami/parsers/fdf.rb +2 -2
- data/lib/origami/parsers/pdf.rb +41 -18
- data/lib/origami/parsers/pdf/lazy.rb +83 -46
- data/lib/origami/parsers/pdf/linear.rb +19 -10
- data/lib/origami/parsers/ppklite.rb +1 -1
- data/lib/origami/pdf.rb +150 -206
- data/lib/origami/reference.rb +4 -6
- data/lib/origami/signature.rb +76 -48
- data/lib/origami/stream.rb +69 -63
- data/lib/origami/string.rb +2 -19
- data/lib/origami/trailer.rb +25 -22
- data/lib/origami/version.rb +1 -1
- data/lib/origami/xfa.rb +6 -4
- data/lib/origami/xreftable.rb +29 -29
- data/test/test_annotations.rb +16 -38
- data/test/test_pdf_attachment.rb +1 -1
- data/test/test_pdf_parse.rb +1 -1
- data/test/test_xrefs.rb +2 -2
- metadata +4 -4
- data/lib/origami/export.rb +0 -247
data/lib/origami/reference.rb
CHANGED
@@ -42,7 +42,7 @@ module Origami
|
|
42
42
|
@refno, @refgen = refno, refgen
|
43
43
|
end
|
44
44
|
|
45
|
-
def self.parse(stream,
|
45
|
+
def self.parse(stream, _parser = nil) #:nodoc:
|
46
46
|
offset = stream.pos
|
47
47
|
|
48
48
|
if stream.scan(@@regexp).nil?
|
@@ -68,7 +68,7 @@ module Origami
|
|
68
68
|
target = doc.get_object(self)
|
69
69
|
|
70
70
|
if target.nil? and not Origami::OPTIONS[:ignore_bad_references]
|
71
|
-
raise InvalidReferenceError, "Cannot resolve reference : #{self
|
71
|
+
raise InvalidReferenceError, "Cannot resolve reference : #{self}"
|
72
72
|
end
|
73
73
|
|
74
74
|
target or Null.new
|
@@ -104,13 +104,11 @@ module Origami
|
|
104
104
|
end
|
105
105
|
|
106
106
|
#
|
107
|
-
# Returns
|
107
|
+
# Returns the referenced object value.
|
108
108
|
#
|
109
109
|
def value
|
110
|
-
self
|
110
|
+
self.solve.value
|
111
111
|
end
|
112
|
-
|
113
|
-
def self.native_type ; Reference end
|
114
112
|
end
|
115
113
|
|
116
114
|
end
|
data/lib/origami/signature.rb
CHANGED
@@ -18,12 +18,7 @@
|
|
18
18
|
|
19
19
|
=end
|
20
20
|
|
21
|
-
|
22
|
-
require 'openssl' if Origami::OPTIONS[:use_openssl]
|
23
|
-
rescue LoadError
|
24
|
-
Origami::OPTIONS[:use_openssl] = false
|
25
|
-
end
|
26
|
-
|
21
|
+
require 'openssl'
|
27
22
|
require 'digest/sha1'
|
28
23
|
|
29
24
|
module Origami
|
@@ -38,48 +33,29 @@ module Origami
|
|
38
33
|
# If no argument is passed, embedded certificates are treated as trusted.
|
39
34
|
#
|
40
35
|
def verify(trusted_certs: [])
|
41
|
-
unless Origami::OPTIONS[:use_openssl]
|
42
|
-
fail "OpenSSL is not present or has been disabled."
|
43
|
-
end
|
44
|
-
|
45
36
|
digsig = self.signature
|
37
|
+
digsig = digsig.cast_to(Signature::DigitalSignature) unless digsig.is_a?(Signature::DigitalSignature)
|
46
38
|
|
47
39
|
unless digsig[:Contents].is_a?(String)
|
48
40
|
raise SignatureError, "Invalid digital signature contents"
|
49
41
|
end
|
50
42
|
|
51
43
|
store = OpenSSL::X509::Store.new
|
52
|
-
trusted_certs.each
|
44
|
+
trusted_certs.each { |ca| store.add_cert(ca) }
|
53
45
|
flags = 0
|
54
46
|
flags |= OpenSSL::PKCS7::NOVERIFY if trusted_certs.empty?
|
55
47
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
endofsig_offset = stream.pos
|
60
|
-
stream.terminate
|
61
|
-
|
62
|
-
s1,l1,s2,l2 = digsig.ByteRange
|
63
|
-
if s1.value != 0 or
|
64
|
-
(s2.value + l2.value) != self.original_data.size or
|
65
|
-
(s1.value + l1.value) != digsig[:Contents].file_offset or
|
66
|
-
s2.value != endofsig_offset
|
67
|
-
|
68
|
-
raise SignatureError, "Invalid signature byte range"
|
69
|
-
end
|
48
|
+
data = extract_signed_data(digsig)
|
49
|
+
signature = digsig[:Contents]
|
50
|
+
subfilter = digsig.SubFilter.value
|
70
51
|
|
71
|
-
|
52
|
+
case subfilter
|
53
|
+
when Signature::DigitalSignature::PKCS7_DETACHED
|
54
|
+
Signature.verify_pkcs7_detached_signature(data, signature, store, flags)
|
72
55
|
|
73
|
-
case digsig.SubFilter.value.to_s
|
74
|
-
when 'adbe.pkcs7.detached'
|
75
|
-
flags |= OpenSSL::PKCS7::DETACHED
|
76
|
-
p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
|
77
|
-
raise SignatureError, "Not a PKCS7 detached signature" unless p7.detached?
|
78
|
-
p7.verify([], store, data, flags)
|
79
56
|
|
80
|
-
when
|
81
|
-
|
82
|
-
p7.verify([], store, nil, flags) and p7.data == Digest::SHA1.digest(data)
|
57
|
+
when Signature::DigitalSignature::PKCS7_SHA1
|
58
|
+
Signature.verify_pkcs7_sha1_signature(data, signature, store, flags)
|
83
59
|
|
84
60
|
else
|
85
61
|
raise NotImplementedError, "Unsupported method #{digsig.SubFilter}"
|
@@ -107,10 +83,6 @@ module Origami
|
|
107
83
|
contact: nil,
|
108
84
|
reason: nil)
|
109
85
|
|
110
|
-
unless Origami::OPTIONS[:use_openssl]
|
111
|
-
fail "OpenSSL is not present or has been disabled."
|
112
|
-
end
|
113
|
-
|
114
86
|
unless certificate.is_a?(OpenSSL::X509::Certificate)
|
115
87
|
raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
|
116
88
|
end
|
@@ -140,7 +112,7 @@ module Origami
|
|
140
112
|
end
|
141
113
|
|
142
114
|
when 'adbe.pkcs7.sha1'
|
143
|
-
|
115
|
+
signfield_size = -> (crt, pkey, certs) do
|
144
116
|
OpenSSL::PKCS7.sign(
|
145
117
|
crt,
|
146
118
|
pkey,
|
@@ -151,7 +123,7 @@ module Origami
|
|
151
123
|
end
|
152
124
|
|
153
125
|
when 'adbe.x509.rsa_sha1'
|
154
|
-
signfield_size = -> (
|
126
|
+
signfield_size = -> (_crt, pkey, _certs) do
|
155
127
|
pkey.private_encrypt(
|
156
128
|
Digest::SHA1.digest('')
|
157
129
|
).size
|
@@ -172,7 +144,7 @@ module Origami
|
|
172
144
|
annotation.V = digsig
|
173
145
|
add_fields(annotation)
|
174
146
|
self.Catalog.AcroForm.SigFlags =
|
175
|
-
InteractiveForm::SigFlags::
|
147
|
+
InteractiveForm::SigFlags::SIGNATURES_EXIST | InteractiveForm::SigFlags::APPEND_ONLY
|
176
148
|
|
177
149
|
digsig.Type = :Sig #:nodoc:
|
178
150
|
digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, ca]) #:nodoc:
|
@@ -263,8 +235,8 @@ module Origami
|
|
263
235
|
def signed?
|
264
236
|
begin
|
265
237
|
self.Catalog.AcroForm.is_a?(Dictionary) and
|
266
|
-
self.Catalog.AcroForm.
|
267
|
-
(self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::
|
238
|
+
self.Catalog.AcroForm.SigFlags.is_a?(Integer) and
|
239
|
+
(self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURES_EXIST != 0)
|
268
240
|
rescue InvalidReferenceError
|
269
241
|
false
|
270
242
|
end
|
@@ -275,9 +247,6 @@ module Origami
|
|
275
247
|
# _rights_:: list of rights defined in UsageRights::Rights
|
276
248
|
#
|
277
249
|
def enable_usage_rights(cert, pkey, *rights)
|
278
|
-
unless Origami::OPTIONS[:use_openssl]
|
279
|
-
fail "OpenSSL is not present or has been disabled."
|
280
|
-
end
|
281
250
|
|
282
251
|
signfield_size = -> (crt, key, ca) do
|
283
252
|
OpenSSL::PKCS7.sign(
|
@@ -301,7 +270,7 @@ module Origami
|
|
301
270
|
digsig = Signature::DigitalSignature.new.set_indirect(true)
|
302
271
|
|
303
272
|
self.Catalog.AcroForm ||= InteractiveForm.new
|
304
|
-
#self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::
|
273
|
+
#self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPEND_ONLY
|
305
274
|
|
306
275
|
digsig.Type = :Sig #:nodoc:
|
307
276
|
digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, []]) #:nodoc:
|
@@ -390,6 +359,34 @@ module Origami
|
|
390
359
|
|
391
360
|
raise SignatureError, "Cannot find digital signature"
|
392
361
|
end
|
362
|
+
|
363
|
+
private
|
364
|
+
|
365
|
+
#
|
366
|
+
# Verifies the ByteRange field of a digital signature and returned the signed data.
|
367
|
+
#
|
368
|
+
def extract_signed_data(digsig)
|
369
|
+
# Computes the boundaries of the Contents field.
|
370
|
+
start_sig = digsig[:Contents].file_offset
|
371
|
+
|
372
|
+
stream = StringScanner.new(self.original_data)
|
373
|
+
stream.pos = digsig[:Contents].file_offset
|
374
|
+
Object.typeof(stream).parse(stream)
|
375
|
+
end_sig = stream.pos
|
376
|
+
stream.terminate
|
377
|
+
|
378
|
+
r1, r2 = digsig.ranges
|
379
|
+
if r1.begin != 0 or
|
380
|
+
r2.end != self.original_data.size or
|
381
|
+
r1.end != start_sig or
|
382
|
+
r2.begin != end_sig
|
383
|
+
|
384
|
+
raise SignatureError, "Invalid signature byte range"
|
385
|
+
end
|
386
|
+
|
387
|
+
self.original_data[r1] + self.original_data[r2]
|
388
|
+
end
|
389
|
+
|
393
390
|
end
|
394
391
|
|
395
392
|
class Perms < Dictionary
|
@@ -402,6 +399,21 @@ module Origami
|
|
402
399
|
|
403
400
|
module Signature
|
404
401
|
|
402
|
+
# Verifies a PKCS7 detached signature.
|
403
|
+
def self.verify_pkcs7_detached_signature(data, signature, store, flags)
|
404
|
+
pkcs7 = OpenSSL::PKCS7.new(signature)
|
405
|
+
raise SignatureError, "Not a PKCS7 detached signature" unless pkcs7.detached?
|
406
|
+
|
407
|
+
flags |= OpenSSL::PKCS7::DETACHED
|
408
|
+
pkcs7.verify([], store, data, flags)
|
409
|
+
end
|
410
|
+
|
411
|
+
# Verifies a PKCS7-SHA1 signature.
|
412
|
+
def self.verify_pkcs7_sha1_signature(data, signature, store, flags)
|
413
|
+
pkcs7 = OpenSSL::PKCS7.new(signature)
|
414
|
+
pkcs7.verify([], store, nil, flags) and pkcs7.data == Digest::SHA1.digest(data)
|
415
|
+
end
|
416
|
+
|
405
417
|
#
|
406
418
|
# Class representing a signature which can be embedded in DigitalSignature dictionary.
|
407
419
|
# It must be a direct object.
|
@@ -494,6 +506,10 @@ module Origami
|
|
494
506
|
class DigitalSignature < Dictionary
|
495
507
|
include StandardObject
|
496
508
|
|
509
|
+
PKCS1_RSA_SHA1 = :"adbe.x509.rsa_sha1"
|
510
|
+
PKCS7_SHA1 = :"adbe.pkcs7.sha1"
|
511
|
+
PKCS7_DETACHED = :"adbe.pkcs7.detached"
|
512
|
+
|
497
513
|
field :Type, :Type => Name, :Default => :Sig
|
498
514
|
field :Filter, :Type => Name, :Default => :"Adobe.PPKLite", :Required => true
|
499
515
|
field :SubFilter, :Type => Name
|
@@ -537,6 +553,18 @@ module Origami
|
|
537
553
|
output(content)
|
538
554
|
end
|
539
555
|
|
556
|
+
def ranges
|
557
|
+
byte_range = self.ByteRange
|
558
|
+
|
559
|
+
unless byte_range.is_a?(Array) and byte_range.length == 4 and byte_range.all? {|i| i.is_a?(Integer) }
|
560
|
+
raise SignatureError, "Invalid ByteRange field value"
|
561
|
+
end
|
562
|
+
|
563
|
+
byte_range.map(&:to_i).each_slice(2).map do |start, length|
|
564
|
+
(start...start + length)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
540
568
|
def signature_offset #:nodoc:
|
541
569
|
indent, tab = 1, "\t"
|
542
570
|
content = "#{no} #{generation} obj" + EOL + TOKENS.first + EOL
|
data/lib/origami/stream.rb
CHANGED
@@ -35,14 +35,16 @@ module Origami
|
|
35
35
|
class Stream
|
36
36
|
include Origami::Object
|
37
37
|
include StandardObject
|
38
|
+
include FieldAccessor
|
38
39
|
using TypeConversion
|
39
40
|
|
40
|
-
TOKENS = [ "stream" + WHITECHARS_NORET
|
41
|
+
TOKENS = [ "stream" + WHITECHARS_NORET + "(\\r\\n|\\r|\\n)" , "endstream" ] #:nodoc:
|
41
42
|
|
42
43
|
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
|
43
44
|
@@regexp_close = Regexp.new(TOKENS.last)
|
44
45
|
|
45
|
-
@@
|
46
|
+
@@type_signatures = {}
|
47
|
+
@@type_keys = []
|
46
48
|
|
47
49
|
#
|
48
50
|
# Actually only 5 first ones are implemented,
|
@@ -139,9 +141,9 @@ module Origami
|
|
139
141
|
|
140
142
|
stm =
|
141
143
|
if Origami::OPTIONS[:enable_type_guessing]
|
142
|
-
self.guess_type(dictionary).new('', dictionary
|
144
|
+
self.guess_type(dictionary).new('', dictionary)
|
143
145
|
else
|
144
|
-
Stream.new('', dictionary
|
146
|
+
Stream.new('', dictionary)
|
145
147
|
end
|
146
148
|
|
147
149
|
raw_data.chomp!(TOKENS.last)
|
@@ -161,20 +163,24 @@ module Origami
|
|
161
163
|
stm
|
162
164
|
end
|
163
165
|
|
164
|
-
def self.
|
165
|
-
|
166
|
-
|
167
|
-
|
166
|
+
def self.add_type_signature(key, value) #:nodoc:
|
167
|
+
key, value = key.to_o, value.to_o
|
168
|
+
|
169
|
+
# Inherit the superclass type information.
|
170
|
+
if not @@type_signatures.key?(self) and @@type_signatures.key?(self.superclass)
|
171
|
+
@@type_signatures[self] = @@type_signatures[self.superclass].dup
|
168
172
|
end
|
169
173
|
|
170
|
-
@@
|
171
|
-
@@
|
174
|
+
@@type_signatures[self] ||= {}
|
175
|
+
@@type_signatures[self][key] = value
|
176
|
+
|
177
|
+
@@type_keys.push(key) unless @@type_keys.include?(key)
|
172
178
|
end
|
173
179
|
|
174
180
|
def self.guess_type(hash) #:nodoc:
|
175
181
|
best_type = self
|
176
182
|
|
177
|
-
@@
|
183
|
+
@@type_signatures.each_pair do |klass, keys|
|
178
184
|
next unless klass < best_type
|
179
185
|
|
180
186
|
best_type = klass if keys.all? { |k,v| hash[k] == v }
|
@@ -223,12 +229,11 @@ module Origami
|
|
223
229
|
def set_predictor(predictor, colors: 1, bitspercomponent: 8, columns: 1)
|
224
230
|
filters = self.filters
|
225
231
|
|
226
|
-
|
232
|
+
layer = filters.index(:FlateDecode) or filters.index(:LZWDecode)
|
233
|
+
if layer.nil?
|
227
234
|
raise InvalidStreamObjectError, 'Predictor functions can only be used with Flate or LZW filters'
|
228
235
|
end
|
229
236
|
|
230
|
-
layer = filters.index(:FlateDecode) or filters.index(:LZWDecode)
|
231
|
-
|
232
237
|
params = Filter::LZW::DecodeParms.new
|
233
238
|
params[:Predictor] = predictor
|
234
239
|
params[:Colors] = colors if colors != 1
|
@@ -243,7 +248,7 @@ module Origami
|
|
243
248
|
def cast_to(type, _parser = nil)
|
244
249
|
super(type)
|
245
250
|
|
246
|
-
cast = type.new("", self.dictionary.
|
251
|
+
cast = type.new("", self.dictionary.copy)
|
247
252
|
cast.encoded_data = self.encoded_data.dup
|
248
253
|
cast.no, cast.generation = self.no, self.generation
|
249
254
|
cast.set_indirect(true)
|
@@ -261,7 +266,7 @@ module Origami
|
|
261
266
|
# Returns the uncompressed stream content.
|
262
267
|
#
|
263
268
|
def data
|
264
|
-
self.decode! unless
|
269
|
+
self.decode! unless decoded?
|
265
270
|
|
266
271
|
@data
|
267
272
|
end
|
@@ -281,7 +286,7 @@ module Origami
|
|
281
286
|
# Returns the raw compressed stream content.
|
282
287
|
#
|
283
288
|
def encoded_data
|
284
|
-
self.encode! unless
|
289
|
+
self.encode! unless encoded?
|
285
290
|
|
286
291
|
@encoded_data
|
287
292
|
end
|
@@ -407,19 +412,8 @@ module Origami
|
|
407
412
|
end
|
408
413
|
alias has_key? key?
|
409
414
|
|
410
|
-
def self.native_type ; Stream end
|
411
|
-
|
412
415
|
private
|
413
416
|
|
414
|
-
def method_missing(field, *args) #:nodoc:
|
415
|
-
if field.to_s[-1,1] == '='
|
416
|
-
self[field.to_s[0..-2].to_sym] = args.first
|
417
|
-
else
|
418
|
-
obj = self[field];
|
419
|
-
obj.is_a?(Reference) ? obj.solve : obj
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
417
|
def decoded? #:nodoc:
|
424
418
|
not @data.nil?
|
425
419
|
end
|
@@ -534,7 +528,7 @@ module Origami
|
|
534
528
|
end
|
535
529
|
|
536
530
|
def pre_build #:nodoc:
|
537
|
-
load!
|
531
|
+
load!
|
538
532
|
|
539
533
|
prolog = ""
|
540
534
|
data = ""
|
@@ -545,7 +539,7 @@ module Origami
|
|
545
539
|
obj.objstm_offset = objoff
|
546
540
|
|
547
541
|
prolog << "#{num} #{objoff} "
|
548
|
-
objdata = "#{obj
|
542
|
+
objdata = "#{obj} "
|
549
543
|
|
550
544
|
objoff += objdata.size
|
551
545
|
data << objdata
|
@@ -580,23 +574,14 @@ module Origami
|
|
580
574
|
end
|
581
575
|
|
582
576
|
# The object already belongs to a document.
|
583
|
-
unless
|
584
|
-
|
585
|
-
if obj_doc.equal?(@document)
|
586
|
-
@document.delete_object(object.reference) if object.indirect?
|
587
|
-
else
|
588
|
-
object = object.export
|
589
|
-
end
|
577
|
+
unless object.document.nil?
|
578
|
+
object = import_object_from_document(object)
|
590
579
|
end
|
591
580
|
|
592
|
-
load!
|
581
|
+
load!
|
593
582
|
|
594
583
|
object.no, object.generation = @document.allocate_new_object_number if object.no == 0
|
595
|
-
|
596
|
-
object.set_indirect(true) # object is indirect
|
597
|
-
object.parent = self # set this stream as the parent
|
598
|
-
object.set_document(@document) # indirect objects need pdf information
|
599
|
-
@objects[object.no] = object
|
584
|
+
store_object(object)
|
600
585
|
|
601
586
|
Reference.new(object.no, 0)
|
602
587
|
end
|
@@ -606,7 +591,7 @@ module Origami
|
|
606
591
|
# Deletes Object _no_.
|
607
592
|
#
|
608
593
|
def delete(no)
|
609
|
-
load!
|
594
|
+
load!
|
610
595
|
|
611
596
|
@objects.delete(no)
|
612
597
|
end
|
@@ -615,14 +600,7 @@ module Origami
|
|
615
600
|
# Returns the index of Object _no_.
|
616
601
|
#
|
617
602
|
def index(no)
|
618
|
-
|
619
|
-
@objects.to_a.sort.each do |num, obj|
|
620
|
-
return ind if num == no
|
621
|
-
|
622
|
-
ind = ind + 1
|
623
|
-
end
|
624
|
-
|
625
|
-
nil
|
603
|
+
@objects.to_a.sort.index { |num, _| num == no }
|
626
604
|
end
|
627
605
|
|
628
606
|
#
|
@@ -630,7 +608,7 @@ module Origami
|
|
630
608
|
# _no_:: The Object number.
|
631
609
|
#
|
632
610
|
def extract(no)
|
633
|
-
load!
|
611
|
+
load!
|
634
612
|
|
635
613
|
@objects[no]
|
636
614
|
end
|
@@ -640,7 +618,7 @@ module Origami
|
|
640
618
|
# _index_:: The Object index in the ObjectStream.
|
641
619
|
#
|
642
620
|
def extract_by_index(index)
|
643
|
-
load!
|
621
|
+
load!
|
644
622
|
|
645
623
|
raise TypeError, "index must be an integer" unless index.is_a?(::Integer)
|
646
624
|
raise IndexError, "index #{index} out of range" if index < 0 or index >= @objects.size
|
@@ -653,7 +631,7 @@ module Origami
|
|
653
631
|
# _no_:: The Object number.
|
654
632
|
#
|
655
633
|
def include?(no)
|
656
|
-
load!
|
634
|
+
load!
|
657
635
|
|
658
636
|
@objects.include?(no)
|
659
637
|
end
|
@@ -662,7 +640,7 @@ module Origami
|
|
662
640
|
# Iterates over each object in the stream.
|
663
641
|
#
|
664
642
|
def each(&b)
|
665
|
-
load!
|
643
|
+
load!
|
666
644
|
|
667
645
|
@objects.values.each(&b)
|
668
646
|
end
|
@@ -681,14 +659,44 @@ module Origami
|
|
681
659
|
# Returns the array of inner objects.
|
682
660
|
#
|
683
661
|
def objects
|
684
|
-
load!
|
662
|
+
load!
|
685
663
|
|
686
664
|
@objects.values
|
687
665
|
end
|
688
666
|
|
689
667
|
private
|
690
668
|
|
669
|
+
#
|
670
|
+
# Preprocess the object in case it already belongs to a document.
|
671
|
+
# If the document is the same as the current object stream, remove the duplicate object from our document.
|
672
|
+
# If the object comes from another document, use the export method to create a version without references.
|
673
|
+
#
|
674
|
+
def import_object_from_document(object)
|
675
|
+
obj_doc = object.document
|
676
|
+
|
677
|
+
# Remove the previous instance if the object is indirect to avoid duplicates.
|
678
|
+
if obj_doc.equal?(@document)
|
679
|
+
@document.delete_object(object.reference) if object.indirect?
|
680
|
+
|
681
|
+
# Otherwise, create a exported version of the object.
|
682
|
+
else
|
683
|
+
object = object.export
|
684
|
+
end
|
685
|
+
|
686
|
+
object
|
687
|
+
end
|
688
|
+
|
689
|
+
def store_object(object) #:nodoc:
|
690
|
+
object.set_indirect(true) # all stored objects are indirect.
|
691
|
+
object.parent = self # set this stream as the parent.
|
692
|
+
object.set_document(@document) # inherit document information.
|
693
|
+
|
694
|
+
@objects[object.no] = object
|
695
|
+
end
|
696
|
+
|
691
697
|
def load! #:nodoc:
|
698
|
+
return unless @objects.nil?
|
699
|
+
|
692
700
|
decode!
|
693
701
|
|
694
702
|
@objects = {}
|
@@ -705,7 +713,7 @@ module Origami
|
|
705
713
|
end
|
706
714
|
|
707
715
|
self.length.times do |i|
|
708
|
-
unless (0...@data.size).cover?
|
716
|
+
unless (0...@data.size).cover?(first_object_offset + offsets[i]) and offsets[i] >= 0
|
709
717
|
raise InvalidObjectStreamObjectError, "Invalid offset '#{offsets[i]} for object #{nums[i]}"
|
710
718
|
end
|
711
719
|
|
@@ -715,12 +723,10 @@ module Origami
|
|
715
723
|
"Bad embedded object format in object stream" if type.nil?
|
716
724
|
|
717
725
|
embeddedobj = type.parse(data)
|
718
|
-
embeddedobj.
|
719
|
-
embeddedobj.no = nums[i] # object number
|
720
|
-
embeddedobj.parent = self # set this stream as the parent
|
721
|
-
embeddedobj.set_document(@document) # indirect objects need pdf information
|
726
|
+
embeddedobj.no = nums[i] # object number
|
722
727
|
embeddedobj.objstm_offset = offsets[i]
|
723
|
-
|
728
|
+
|
729
|
+
store_object(embeddedobj)
|
724
730
|
end
|
725
731
|
end
|
726
732
|
|