origami 2.0.0 → 2.0.1
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/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
|
|