origami 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/bin/gui/config.rb +2 -1
  4. data/bin/gui/file.rb +118 -240
  5. data/bin/gui/gtkhex.rb +5 -5
  6. data/bin/gui/hexview.rb +20 -16
  7. data/bin/gui/imgview.rb +1 -1
  8. data/bin/gui/menu.rb +138 -158
  9. data/bin/gui/properties.rb +46 -48
  10. data/bin/gui/signing.rb +183 -214
  11. data/bin/gui/textview.rb +1 -1
  12. data/bin/gui/treeview.rb +13 -7
  13. data/bin/gui/walker.rb +102 -71
  14. data/bin/gui/xrefs.rb +1 -1
  15. data/bin/pdf2ruby +3 -3
  16. data/bin/pdfcop +18 -11
  17. data/bin/pdfextract +14 -5
  18. data/bin/pdfmetadata +3 -3
  19. data/bin/shell/console.rb +8 -8
  20. data/bin/shell/hexdump.rb +4 -4
  21. data/examples/attachments/nested_document.rb +1 -1
  22. data/examples/javascript/hello_world.rb +3 -3
  23. data/lib/origami.rb +0 -1
  24. data/lib/origami/acroform.rb +3 -3
  25. data/lib/origami/array.rb +1 -3
  26. data/lib/origami/boolean.rb +1 -3
  27. data/lib/origami/catalog.rb +3 -9
  28. data/lib/origami/destinations.rb +2 -2
  29. data/lib/origami/dictionary.rb +15 -29
  30. data/lib/origami/encryption.rb +334 -692
  31. data/lib/origami/extensions/fdf.rb +3 -2
  32. data/lib/origami/extensions/ppklite.rb +5 -9
  33. data/lib/origami/filespec.rb +2 -2
  34. data/lib/origami/filters.rb +54 -36
  35. data/lib/origami/filters/ascii.rb +67 -49
  36. data/lib/origami/filters/ccitt.rb +4 -236
  37. data/lib/origami/filters/ccitt/tables.rb +267 -0
  38. data/lib/origami/filters/crypt.rb +1 -1
  39. data/lib/origami/filters/dct.rb +0 -1
  40. data/lib/origami/filters/flate.rb +3 -43
  41. data/lib/origami/filters/lzw.rb +62 -99
  42. data/lib/origami/filters/predictors.rb +135 -105
  43. data/lib/origami/filters/runlength.rb +34 -22
  44. data/lib/origami/graphics.rb +2 -2
  45. data/lib/origami/graphics/colors.rb +89 -63
  46. data/lib/origami/graphics/path.rb +14 -14
  47. data/lib/origami/graphics/patterns.rb +31 -33
  48. data/lib/origami/graphics/render.rb +0 -1
  49. data/lib/origami/graphics/state.rb +9 -9
  50. data/lib/origami/graphics/text.rb +17 -17
  51. data/lib/origami/graphics/xobject.rb +102 -92
  52. data/lib/origami/javascript.rb +91 -68
  53. data/lib/origami/linearization.rb +22 -20
  54. data/lib/origami/metadata.rb +1 -1
  55. data/lib/origami/name.rb +1 -3
  56. data/lib/origami/null.rb +1 -3
  57. data/lib/origami/numeric.rb +3 -13
  58. data/lib/origami/object.rb +100 -72
  59. data/lib/origami/page.rb +24 -28
  60. data/lib/origami/parser.rb +34 -51
  61. data/lib/origami/parsers/fdf.rb +2 -2
  62. data/lib/origami/parsers/pdf.rb +41 -18
  63. data/lib/origami/parsers/pdf/lazy.rb +83 -46
  64. data/lib/origami/parsers/pdf/linear.rb +19 -10
  65. data/lib/origami/parsers/ppklite.rb +1 -1
  66. data/lib/origami/pdf.rb +150 -206
  67. data/lib/origami/reference.rb +4 -6
  68. data/lib/origami/signature.rb +76 -48
  69. data/lib/origami/stream.rb +69 -63
  70. data/lib/origami/string.rb +2 -19
  71. data/lib/origami/trailer.rb +25 -22
  72. data/lib/origami/version.rb +1 -1
  73. data/lib/origami/xfa.rb +6 -4
  74. data/lib/origami/xreftable.rb +29 -29
  75. data/test/test_annotations.rb +16 -38
  76. data/test/test_pdf_attachment.rb +1 -1
  77. data/test/test_pdf_parse.rb +1 -1
  78. data/test/test_xrefs.rb +2 -2
  79. metadata +4 -4
  80. data/lib/origami/export.rb +0 -247
@@ -42,7 +42,7 @@ module Origami
42
42
  @refno, @refgen = refno, refgen
43
43
  end
44
44
 
45
- def self.parse(stream, parser = nil) #:nodoc:
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.to_s}"
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 self.
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
@@ -18,12 +18,7 @@
18
18
 
19
19
  =end
20
20
 
21
- begin
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 do |ca| store.add_cert(ca) end
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
- stream = StringScanner.new(self.original_data)
57
- stream.pos = digsig[:Contents].file_offset
58
- Object.typeof(stream).parse(stream)
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
- data = self.original_data[s1,l1] + self.original_data[s2,l2]
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 'adbe.pkcs7.sha1'
81
- p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
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
- signfield_size = -> (crt, pkey, certs) do
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 = -> (crt, pkey, certs) do
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::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
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.has_key?(:SigFlags) and
267
- (self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
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::APPENDONLY
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
@@ -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 + "\\r?\\n", "endstream" ] #:nodoc:
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
- @@cast_fingerprints = {}
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.to_h)
144
+ self.guess_type(dictionary).new('', dictionary)
143
145
  else
144
- Stream.new('', dictionary.to_h)
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.add_type_info(typeclass, key, value) #:nodoc:
165
- if not @@cast_fingerprints.has_key?(typeclass) and typeclass.superclass != Stream and
166
- @@cast_fingerprints.has_key?(typeclass.superclass)
167
- @@cast_fingerprints[typeclass] = @@cast_fingerprints[typeclass.superclass].dup
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
- @@cast_fingerprints[typeclass] ||= {}
171
- @@cast_fingerprints[typeclass][key.to_o] = value.to_o
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
- @@cast_fingerprints.each_pair do |klass, keys|
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
- unless filters.include?(:FlateDecode) or filters.include?(:LZWDecode)
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.to_h)
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 self.decoded?
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 self.encoded?
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! if @objects.nil?
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.to_s} "
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 (obj_doc = object.document).nil?
584
- # Remove the previous instance if the object is indirect to avoid duplicates.
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! if @objects.nil?
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! if @objects.nil?
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
- ind = 0
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! if @objects.nil?
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! if @objects.nil?
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! if @objects.nil?
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! if @objects.nil?
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! if @objects.nil?
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? (first_object_offset + offsets[i]) and offsets[i] >= 0
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.set_indirect(true) # object is indirect
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
- @objects[nums[i]] = embeddedobj
728
+
729
+ store_object(embeddedobj)
724
730
  end
725
731
  end
726
732