origami-docspring 2.2.0 → 2.3.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 +18 -0
- data/examples/attachments/attachment.rb +7 -8
- data/examples/attachments/nested_document.rb +6 -5
- data/examples/encryption/encryption.rb +5 -4
- data/examples/events/events.rb +7 -6
- data/examples/flash/flash.rb +10 -9
- data/examples/forms/javascript.rb +14 -13
- data/examples/forms/xfa.rb +67 -66
- data/examples/javascript/hello_world.rb +6 -5
- data/examples/javascript/js_emulation.rb +26 -26
- data/examples/loop/goto.rb +12 -11
- data/examples/loop/named.rb +17 -16
- data/examples/signature/signature.rb +11 -11
- data/examples/uri/javascript.rb +25 -24
- data/examples/uri/open-uri.rb +5 -4
- data/examples/uri/submitform.rb +11 -10
- data/lib/origami/3d.rb +330 -334
- data/lib/origami/acroform.rb +267 -268
- data/lib/origami/actions.rb +266 -278
- data/lib/origami/annotations.rb +659 -670
- data/lib/origami/array.rb +192 -196
- data/lib/origami/boolean.rb +66 -70
- data/lib/origami/catalog.rb +360 -363
- data/lib/origami/collections.rb +132 -133
- data/lib/origami/compound.rb +125 -129
- data/lib/origami/destinations.rb +226 -237
- data/lib/origami/dictionary.rb +155 -154
- data/lib/origami/encryption.rb +967 -923
- data/lib/origami/extensions/fdf.rb +270 -275
- data/lib/origami/extensions/ppklite.rb +323 -328
- data/lib/origami/filespec.rb +170 -173
- data/lib/origami/filters/ascii.rb +162 -167
- data/lib/origami/filters/ccitt/tables.rb +248 -252
- data/lib/origami/filters/ccitt.rb +309 -312
- data/lib/origami/filters/crypt.rb +31 -34
- data/lib/origami/filters/dct.rb +47 -50
- data/lib/origami/filters/flate.rb +57 -60
- data/lib/origami/filters/jbig2.rb +50 -53
- data/lib/origami/filters/jpx.rb +40 -43
- data/lib/origami/filters/lzw.rb +151 -155
- data/lib/origami/filters/predictors.rb +250 -255
- data/lib/origami/filters/runlength.rb +111 -115
- data/lib/origami/filters.rb +319 -325
- data/lib/origami/font.rb +173 -177
- data/lib/origami/functions.rb +62 -66
- data/lib/origami/graphics/colors.rb +203 -208
- data/lib/origami/graphics/instruction.rb +79 -81
- data/lib/origami/graphics/path.rb +141 -144
- data/lib/origami/graphics/patterns.rb +156 -160
- data/lib/origami/graphics/render.rb +51 -47
- data/lib/origami/graphics/state.rb +144 -142
- data/lib/origami/graphics/text.rb +185 -188
- data/lib/origami/graphics/xobject.rb +818 -804
- data/lib/origami/graphics.rb +25 -26
- data/lib/origami/header.rb +63 -65
- data/lib/origami/javascript.rb +718 -651
- data/lib/origami/linearization.rb +284 -285
- data/lib/origami/metadata.rb +156 -135
- data/lib/origami/name.rb +98 -100
- data/lib/origami/null.rb +49 -51
- data/lib/origami/numeric.rb +133 -135
- data/lib/origami/obfuscation.rb +180 -182
- data/lib/origami/object.rb +634 -631
- data/lib/origami/optionalcontent.rb +147 -149
- data/lib/origami/outline.rb +46 -48
- data/lib/origami/outputintents.rb +76 -77
- data/lib/origami/page.rb +637 -596
- data/lib/origami/parser.rb +214 -221
- data/lib/origami/parsers/fdf.rb +44 -45
- data/lib/origami/parsers/pdf/lazy.rb +147 -154
- data/lib/origami/parsers/pdf/linear.rb +104 -109
- data/lib/origami/parsers/pdf.rb +109 -107
- data/lib/origami/parsers/ppklite.rb +44 -46
- data/lib/origami/pdf.rb +886 -896
- data/lib/origami/reference.rb +116 -120
- data/lib/origami/signature.rb +617 -625
- data/lib/origami/stream.rb +560 -558
- data/lib/origami/string.rb +366 -368
- data/lib/origami/template/patterns.rb +50 -52
- data/lib/origami/template/widgets.rb +111 -114
- data/lib/origami/trailer.rb +153 -157
- data/lib/origami/tree.rb +55 -57
- data/lib/origami/version.rb +19 -19
- data/lib/origami/webcapture.rb +87 -90
- data/lib/origami/xfa/config.rb +409 -414
- data/lib/origami/xfa/connectionset.rb +113 -117
- data/lib/origami/xfa/datasets.rb +38 -42
- data/lib/origami/xfa/localeset.rb +33 -37
- data/lib/origami/xfa/package.rb +49 -52
- data/lib/origami/xfa/pdf.rb +54 -59
- data/lib/origami/xfa/signature.rb +33 -37
- data/lib/origami/xfa/sourceset.rb +34 -38
- data/lib/origami/xfa/stylesheet.rb +35 -39
- data/lib/origami/xfa/template.rb +1630 -1634
- data/lib/origami/xfa/xdc.rb +33 -37
- data/lib/origami/xfa/xfa.rb +132 -123
- data/lib/origami/xfa/xfdf.rb +34 -38
- data/lib/origami/xfa/xmpmeta.rb +34 -38
- data/lib/origami/xfa.rb +50 -53
- data/lib/origami/xreftable.rb +462 -462
- data/lib/origami.rb +37 -38
- data/test/test_actions.rb +22 -20
- data/test/test_annotations.rb +54 -52
- data/test/test_forms.rb +23 -21
- data/test/test_native_types.rb +82 -78
- data/test/test_object_tree.rb +25 -24
- data/test/test_pages.rb +43 -41
- data/test/test_pdf.rb +2 -0
- data/test/test_pdf_attachment.rb +23 -21
- data/test/test_pdf_create.rb +16 -15
- data/test/test_pdf_encrypt.rb +69 -66
- data/test/test_pdf_parse.rb +131 -129
- data/test/test_pdf_parse_lazy.rb +53 -53
- data/test/test_pdf_sign.rb +67 -67
- data/test/test_streams.rb +145 -143
- data/test/test_xrefs.rb +46 -45
- metadata +64 -8
data/lib/origami/stream.rb
CHANGED
@@ -1,705 +1,707 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# This file is part of Origami, PDF manipulation framework for Ruby
|
5
|
+
# Copyright (C) 2016 Guillaume Delugré.
|
6
|
+
#
|
7
|
+
# Origami is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Origami is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
2
20
|
|
3
|
-
|
4
|
-
Copyright (C) 2016 Guillaume Delugré.
|
21
|
+
require 'strscan'
|
5
22
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
(at your option) any later version.
|
23
|
+
module Origami
|
24
|
+
class InvalidStreamObjectError < InvalidObjectError # :nodoc:
|
25
|
+
end
|
10
26
|
|
11
|
-
|
12
|
-
|
13
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
-
GNU Lesser General Public License for more details.
|
27
|
+
# Forward declaration.
|
28
|
+
class FileSpec < Dictionary; end
|
15
29
|
|
16
|
-
|
17
|
-
|
30
|
+
#
|
31
|
+
# Class representing a PDF Stream Object.
|
32
|
+
# Streams can be used to hold any kind of data, especially binary data.
|
33
|
+
#
|
34
|
+
class Stream
|
35
|
+
include Origami::Object
|
36
|
+
include StandardObject
|
37
|
+
include FieldAccessor
|
38
|
+
include Enumerable
|
39
|
+
extend TypeGuessing
|
18
40
|
|
19
|
-
=
|
41
|
+
TOKENS = ["stream" + WHITECHARS_NORET + "(\\r\\n|\\r|\\n)", "endstream"] # :nodoc:
|
20
42
|
|
21
|
-
|
43
|
+
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
|
44
|
+
@@regexp_close = Regexp.new(TOKENS.last)
|
22
45
|
|
23
|
-
|
46
|
+
#
|
47
|
+
# Actually only 5 first ones are implemented,
|
48
|
+
# other ones are mainly about image data processing (JPEG, JPEG2000 ...)
|
49
|
+
#
|
50
|
+
DEFINED_FILTERS = %i[
|
51
|
+
ASCIIHexDecode
|
52
|
+
ASCII85Decode
|
53
|
+
LZWDecode
|
54
|
+
FlateDecode
|
55
|
+
RunLengthDecode
|
56
|
+
|
57
|
+
CCITTFaxDecode
|
58
|
+
JBIG2Decode
|
59
|
+
DCTDecode
|
60
|
+
JPXDecode
|
61
|
+
|
62
|
+
AHx
|
63
|
+
A85
|
64
|
+
LZW
|
65
|
+
Fl
|
66
|
+
RL
|
67
|
+
CCF
|
68
|
+
DCT
|
69
|
+
]
|
70
|
+
|
71
|
+
attr_reader :dictionary
|
72
|
+
|
73
|
+
field :Length, Type: Integer, Required: true
|
74
|
+
field :Filter, Type: [Name, Array.of(Name)]
|
75
|
+
field :DecodeParms, Type: [Dictionary, Array.of(Dictionary)]
|
76
|
+
field :F, Type: FileSpec, Version: "1.2"
|
77
|
+
field :FFilter, Type: [Name, Array.of(Name)], Version: "1.2"
|
78
|
+
field :FDecodeParms, Type: [Dictionary, Array.of(Dictionary)], Version: "1.2"
|
79
|
+
field :DL, Type: Integer, Version: "1.5"
|
24
80
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Class representing a PDF Stream Object.
|
33
|
-
# Streams can be used to hold any kind of data, especially binary data.
|
34
|
-
#
|
35
|
-
class Stream
|
36
|
-
include Origami::Object
|
37
|
-
include StandardObject
|
38
|
-
include FieldAccessor
|
39
|
-
include Enumerable
|
40
|
-
extend TypeGuessing
|
41
|
-
|
42
|
-
TOKENS = [ "stream" + WHITECHARS_NORET + "(\\r\\n|\\r|\\n)" , "endstream" ] #:nodoc:
|
43
|
-
|
44
|
-
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
|
45
|
-
@@regexp_close = Regexp.new(TOKENS.last)
|
46
|
-
|
47
|
-
#
|
48
|
-
# Actually only 5 first ones are implemented,
|
49
|
-
# other ones are mainly about image data processing (JPEG, JPEG2000 ...)
|
50
|
-
#
|
51
|
-
DEFINED_FILTERS = %i[
|
52
|
-
ASCIIHexDecode
|
53
|
-
ASCII85Decode
|
54
|
-
LZWDecode
|
55
|
-
FlateDecode
|
56
|
-
RunLengthDecode
|
57
|
-
|
58
|
-
CCITTFaxDecode
|
59
|
-
JBIG2Decode
|
60
|
-
DCTDecode
|
61
|
-
JPXDecode
|
62
|
-
|
63
|
-
AHx
|
64
|
-
A85
|
65
|
-
LZW
|
66
|
-
Fl
|
67
|
-
RL
|
68
|
-
CCF
|
69
|
-
DCT
|
70
|
-
]
|
71
|
-
|
72
|
-
attr_reader :dictionary
|
73
|
-
|
74
|
-
field :Length, :Type => Integer, :Required => true
|
75
|
-
field :Filter, :Type => [ Name, Array.of(Name) ]
|
76
|
-
field :DecodeParms, :Type => [ Dictionary, Array.of(Dictionary) ]
|
77
|
-
field :F, :Type => FileSpec, :Version => "1.2"
|
78
|
-
field :FFilter, :Type => [ Name, Array.of(Name) ], :Version => "1.2"
|
79
|
-
field :FDecodeParms, :Type => [ Dictionary, Array.of(Dictionary) ], :Version => "1.2"
|
80
|
-
field :DL, :Type => Integer, :Version => "1.5"
|
81
|
-
|
82
|
-
#
|
83
|
-
# Creates a new PDF Stream.
|
84
|
-
# _data_:: The Stream uncompressed data.
|
85
|
-
# _dictionary_:: A hash representing the Stream attributes.
|
86
|
-
#
|
87
|
-
def initialize(data = "", dictionary = {})
|
88
|
-
super()
|
89
|
-
|
90
|
-
set_indirect(true)
|
91
|
-
|
92
|
-
@encoded_data = nil
|
93
|
-
@dictionary, @data = Dictionary.new(dictionary), data
|
94
|
-
@dictionary.parent = self
|
95
|
-
end
|
81
|
+
#
|
82
|
+
# Creates a new PDF Stream.
|
83
|
+
# _data_:: The Stream uncompressed data.
|
84
|
+
# _dictionary_:: A hash representing the Stream attributes.
|
85
|
+
#
|
86
|
+
def initialize(data = "", dictionary = {})
|
87
|
+
super()
|
96
88
|
|
97
|
-
|
98
|
-
@dictionary = dict
|
99
|
-
@dictionary.parent = self
|
100
|
-
end
|
89
|
+
set_indirect(true)
|
101
90
|
|
102
|
-
|
103
|
-
|
91
|
+
@encoded_data = nil
|
92
|
+
@dictionary, @data = Dictionary.new(dictionary), data
|
93
|
+
@dictionary.parent = self
|
94
|
+
end
|
104
95
|
|
105
|
-
|
106
|
-
|
96
|
+
def dictionary=(dict)
|
97
|
+
@dictionary = dict
|
98
|
+
@dictionary.parent = self
|
99
|
+
end
|
107
100
|
|
108
|
-
|
109
|
-
|
101
|
+
def pre_build
|
102
|
+
encode!
|
110
103
|
|
111
|
-
|
112
|
-
|
104
|
+
super
|
105
|
+
end
|
113
106
|
|
114
|
-
|
115
|
-
|
116
|
-
dictionary = Dictionary.parse(scanner, parser)
|
117
|
-
return dictionary if not scanner.skip(@@regexp_open)
|
118
|
-
|
119
|
-
length = dictionary[:Length]
|
120
|
-
if not length.is_a?(Integer)
|
121
|
-
raw_data = scanner.scan_until(@@regexp_close)
|
122
|
-
if raw_data.nil?
|
123
|
-
raise InvalidStreamObjectError,
|
124
|
-
"Stream shall end with a 'endstream' statement"
|
125
|
-
end
|
126
|
-
else
|
127
|
-
length = length.value
|
128
|
-
raw_data = scanner.peek(length)
|
129
|
-
scanner.pos += length
|
130
|
-
|
131
|
-
if not ( unmatched = scanner.scan_until(@@regexp_close) )
|
132
|
-
raise InvalidStreamObjectError,
|
133
|
-
"Stream shall end with a 'endstream' statement"
|
134
|
-
end
|
135
|
-
|
136
|
-
raw_data << unmatched
|
137
|
-
end
|
138
|
-
|
139
|
-
stm =
|
140
|
-
if Origami::OPTIONS[:enable_type_guessing]
|
141
|
-
self.guess_type(dictionary).new('', dictionary)
|
142
|
-
else
|
143
|
-
Stream.new('', dictionary)
|
144
|
-
end
|
145
|
-
|
146
|
-
raw_data.chomp!(TOKENS.last)
|
147
|
-
|
148
|
-
if raw_data[-1,1] == "\n"
|
149
|
-
if raw_data[-2,1] == "\r"
|
150
|
-
raw_data = raw_data[0, raw_data.size - 2]
|
151
|
-
else
|
152
|
-
raw_data = raw_data[0, raw_data.size - 1]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
#raw_data.chomp! if length.is_a?(Integer) and length < raw_data.length
|
156
|
-
|
157
|
-
stm.encoded_data = raw_data
|
158
|
-
stm.file_offset = dictionary.file_offset
|
159
|
-
|
160
|
-
stm
|
161
|
-
end
|
107
|
+
def post_build
|
108
|
+
self.Length = @encoded_data.length
|
162
109
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
case filters
|
171
|
-
when NilClass then 0
|
172
|
-
when Array then filters.length
|
173
|
-
else
|
174
|
-
1
|
175
|
-
end
|
176
|
-
end unless block_given?
|
177
|
-
|
178
|
-
return if filters.nil?
|
179
|
-
|
180
|
-
if filters.is_a?(Array)
|
181
|
-
filters.each do |filter| yield(filter) end
|
182
|
-
else
|
183
|
-
yield(filters)
|
184
|
-
end
|
185
|
-
|
186
|
-
self
|
187
|
-
end
|
110
|
+
super
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.parse(stream, parser = nil) # :nodoc:
|
114
|
+
scanner = Parser.init_scanner(stream)
|
115
|
+
dictionary = Dictionary.parse(scanner, parser)
|
116
|
+
return dictionary if !scanner.skip(@@regexp_open)
|
188
117
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
118
|
+
length = dictionary[:Length]
|
119
|
+
if !length.is_a?(Integer)
|
120
|
+
raw_data = scanner.scan_until(@@regexp_close)
|
121
|
+
if raw_data.nil?
|
122
|
+
raise InvalidStreamObjectError,
|
123
|
+
"Stream shall end with a 'endstream' statement"
|
194
124
|
end
|
125
|
+
else
|
126
|
+
length = length.value
|
127
|
+
raw_data = scanner.peek(length)
|
128
|
+
scanner.pos += length
|
195
129
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
def set_predictor(predictor, colors: 1, bitspercomponent: 8, columns: 1)
|
201
|
-
filters = self.filters
|
130
|
+
if !(unmatched = scanner.scan_until(@@regexp_close))
|
131
|
+
raise InvalidStreamObjectError,
|
132
|
+
"Stream shall end with a 'endstream' statement"
|
133
|
+
end
|
202
134
|
|
203
|
-
|
204
|
-
|
205
|
-
raise InvalidStreamObjectError, 'Predictor functions can only be used with Flate or LZW filters'
|
206
|
-
end
|
135
|
+
raw_data << unmatched
|
136
|
+
end
|
207
137
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
138
|
+
stm =
|
139
|
+
if Origami::OPTIONS[:enable_type_guessing]
|
140
|
+
guess_type(dictionary).new('', dictionary)
|
141
|
+
else
|
142
|
+
Stream.new('', dictionary)
|
143
|
+
end
|
213
144
|
|
214
|
-
|
145
|
+
raw_data.chomp!(TOKENS.last)
|
215
146
|
|
216
|
-
|
147
|
+
if raw_data[-1, 1] == "\n"
|
148
|
+
raw_data = if raw_data[-2, 1] == "\r"
|
149
|
+
raw_data[0, raw_data.size - 2]
|
150
|
+
else
|
151
|
+
raw_data[0, raw_data.size - 1]
|
217
152
|
end
|
153
|
+
end
|
154
|
+
# raw_data.chomp! if length.is_a?(Integer) and length < raw_data.length
|
218
155
|
|
219
|
-
|
220
|
-
|
156
|
+
stm.encoded_data = raw_data
|
157
|
+
stm.file_offset = dictionary.file_offset
|
221
158
|
|
222
|
-
|
223
|
-
|
224
|
-
cast.file_offset = self.file_offset
|
159
|
+
stm
|
160
|
+
end
|
225
161
|
|
226
|
-
|
227
|
-
|
162
|
+
#
|
163
|
+
# Iterates over each Filter in the Stream.
|
164
|
+
#
|
165
|
+
def each_filter
|
166
|
+
filters = self.Filter
|
228
167
|
|
229
|
-
|
230
|
-
|
168
|
+
unless block_given?
|
169
|
+
return enum_for(__method__) do
|
170
|
+
case filters
|
171
|
+
when NilClass then 0
|
172
|
+
when Array then filters.length
|
173
|
+
else
|
174
|
+
1
|
175
|
+
end
|
231
176
|
end
|
177
|
+
end
|
232
178
|
|
233
|
-
|
234
|
-
# Returns the uncompressed stream content.
|
235
|
-
#
|
236
|
-
def data
|
237
|
-
self.decode! unless decoded?
|
179
|
+
return if filters.nil?
|
238
180
|
|
239
|
-
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
# Sets the uncompressed stream content.
|
245
|
-
# _str_:: The new uncompressed data.
|
246
|
-
#
|
247
|
-
def data=(str)
|
248
|
-
@encoded_data = nil
|
249
|
-
@data = str
|
250
|
-
end
|
251
|
-
alias decoded_data= data=
|
181
|
+
if filters.is_a?(Array)
|
182
|
+
filters.each do |filter| yield(filter) end
|
183
|
+
else
|
184
|
+
yield(filters)
|
185
|
+
end
|
252
186
|
|
253
|
-
|
254
|
-
|
255
|
-
#
|
256
|
-
def encoded_data
|
257
|
-
self.encode! unless encoded?
|
187
|
+
self
|
188
|
+
end
|
258
189
|
|
259
|
-
|
260
|
-
|
190
|
+
#
|
191
|
+
# Returns an Array of Filters for this Stream.
|
192
|
+
#
|
193
|
+
def filters
|
194
|
+
each_filter.to_a
|
195
|
+
end
|
261
196
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
@data = nil
|
269
|
-
end
|
197
|
+
#
|
198
|
+
# Set predictor type for the current Stream.
|
199
|
+
# Applies only for LZW and FlateDecode filters.
|
200
|
+
#
|
201
|
+
def set_predictor(predictor, colors: 1, bitspercomponent: 8, columns: 1)
|
202
|
+
filters = self.filters
|
270
203
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
self.decrypt! if self.is_a?(Encryption::EncryptedStream)
|
276
|
-
return if decoded?
|
204
|
+
layer = filters.index(:FlateDecode) or filters.index(:LZWDecode)
|
205
|
+
if layer.nil?
|
206
|
+
raise InvalidStreamObjectError, 'Predictor functions can only be used with Flate or LZW filters'
|
207
|
+
end
|
277
208
|
|
278
|
-
|
279
|
-
|
209
|
+
params = Filter::LZW::DecodeParms.new
|
210
|
+
params[:Predictor] = predictor
|
211
|
+
params[:Colors] = colors if colors != 1
|
212
|
+
params[:BitsPerComponent] = bitspercomponent if bitspercomponent != 8
|
213
|
+
params[:Columns] = columns if columns != 1
|
280
214
|
|
281
|
-
|
282
|
-
@data.freeze
|
215
|
+
set_decode_params(layer, params)
|
283
216
|
|
284
|
-
|
285
|
-
|
217
|
+
self
|
218
|
+
end
|
286
219
|
|
287
|
-
|
288
|
-
|
289
|
-
raise Filter::Error, "Crypt filter must be the first filter" unless layer.zero?
|
220
|
+
def cast_to(type, _parser = nil)
|
221
|
+
assert_cast_type(type)
|
290
222
|
|
291
|
-
|
292
|
-
|
293
|
-
|
223
|
+
cast = type.new("", dictionary.copy)
|
224
|
+
cast.encoded_data = encoded_data.dup
|
225
|
+
cast.file_offset = file_offset
|
294
226
|
|
295
|
-
|
296
|
-
|
297
|
-
rescue Filter::Error => error
|
298
|
-
@data = error.decoded_data
|
299
|
-
raise
|
300
|
-
end
|
301
|
-
end
|
227
|
+
transfer_attributes(cast)
|
228
|
+
end
|
302
229
|
|
303
|
-
|
304
|
-
|
230
|
+
def value # :nodoc:
|
231
|
+
self
|
232
|
+
end
|
305
233
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
234
|
+
#
|
235
|
+
# Returns the uncompressed stream content.
|
236
|
+
#
|
237
|
+
def data
|
238
|
+
decode! unless decoded?
|
311
239
|
|
312
|
-
|
313
|
-
|
240
|
+
@data
|
241
|
+
end
|
242
|
+
alias_method :decoded_data, :data
|
314
243
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
244
|
+
#
|
245
|
+
# Sets the uncompressed stream content.
|
246
|
+
# _str_:: The new uncompressed data.
|
247
|
+
#
|
248
|
+
def data=(str)
|
249
|
+
@encoded_data = nil
|
250
|
+
@data = str
|
251
|
+
end
|
252
|
+
alias_method :decoded_data=, :data=
|
319
253
|
|
320
|
-
|
321
|
-
|
322
|
-
|
254
|
+
#
|
255
|
+
# Returns the raw compressed stream content.
|
256
|
+
#
|
257
|
+
def encoded_data
|
258
|
+
encode! unless encoded?
|
323
259
|
|
324
|
-
|
325
|
-
|
326
|
-
end
|
260
|
+
@encoded_data
|
261
|
+
end
|
327
262
|
|
328
|
-
|
329
|
-
|
263
|
+
#
|
264
|
+
# Sets the raw compressed stream content.
|
265
|
+
# _str_:: the new raw data.
|
266
|
+
#
|
267
|
+
def encoded_data=(str)
|
268
|
+
@encoded_data = str
|
269
|
+
@data = nil
|
270
|
+
end
|
330
271
|
|
331
|
-
|
272
|
+
#
|
273
|
+
# Uncompress the stream data.
|
274
|
+
#
|
275
|
+
def decode!
|
276
|
+
decrypt! if is_a?(Encryption::EncryptedStream)
|
277
|
+
return if decoded?
|
332
278
|
|
333
|
-
|
334
|
-
|
279
|
+
filters = self.filters
|
280
|
+
dparams = decode_params
|
335
281
|
|
336
|
-
|
337
|
-
|
282
|
+
@data = @encoded_data.dup
|
283
|
+
@data.freeze
|
338
284
|
|
339
|
-
|
340
|
-
|
341
|
-
content << self.encoded_data
|
342
|
-
content << eol << TOKENS.last
|
285
|
+
filters.each_with_index do |filter, layer|
|
286
|
+
params = dparams[layer].is_a?(Dictionary) ? dparams[layer] : {}
|
343
287
|
|
344
|
-
|
345
|
-
|
288
|
+
# Handle Crypt filters.
|
289
|
+
if filter == :Crypt
|
290
|
+
raise Filter::Error, "Crypt filter must be the first filter" unless layer.zero?
|
346
291
|
|
347
|
-
|
348
|
-
|
292
|
+
# Skip the Crypt filter.
|
293
|
+
next
|
349
294
|
end
|
350
295
|
|
351
|
-
|
352
|
-
|
296
|
+
begin
|
297
|
+
@data = decode_data(@data, filter, params)
|
298
|
+
rescue Filter::Error => error
|
299
|
+
@data = error.decoded_data
|
300
|
+
raise
|
353
301
|
end
|
302
|
+
end
|
354
303
|
|
355
|
-
|
356
|
-
|
357
|
-
end
|
304
|
+
self
|
305
|
+
end
|
358
306
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
307
|
+
#
|
308
|
+
# Compress the stream data.
|
309
|
+
#
|
310
|
+
def encode!
|
311
|
+
return if encoded?
|
363
312
|
|
364
|
-
|
365
|
-
|
366
|
-
end
|
367
|
-
alias has_key? key?
|
313
|
+
filters = self.filters
|
314
|
+
dparams = decode_params
|
368
315
|
|
369
|
-
|
370
|
-
|
371
|
-
|
316
|
+
@encoded_data = @data.dup
|
317
|
+
(filters.length - 1).downto(0) do |layer|
|
318
|
+
params = dparams[layer].is_a?(Dictionary) ? dparams[layer] : {}
|
319
|
+
filter = filters[layer]
|
372
320
|
|
373
|
-
|
321
|
+
# Handle Crypt filters.
|
322
|
+
if filter == :Crypt
|
323
|
+
raise Filter::Error, "Crypt filter must be the first filter" unless layer.zero?
|
374
324
|
|
375
|
-
|
376
|
-
|
325
|
+
# Skip the Crypt filter.
|
326
|
+
next
|
377
327
|
end
|
378
328
|
|
379
|
-
|
380
|
-
|
381
|
-
end
|
329
|
+
@encoded_data = encode_data(@encoded_data, filter, params)
|
330
|
+
end
|
382
331
|
|
383
|
-
|
384
|
-
params = self.DecodeParms
|
332
|
+
self.Length = @encoded_data.length
|
385
333
|
|
386
|
-
|
387
|
-
|
388
|
-
when NilClass then 0
|
389
|
-
when Array then params.length
|
390
|
-
else
|
391
|
-
1
|
392
|
-
end
|
393
|
-
end unless block_given?
|
334
|
+
self
|
335
|
+
end
|
394
336
|
|
395
|
-
|
337
|
+
def to_s(indent: 1, tab: "\t", eol: $/) # :nodoc:
|
338
|
+
content = +""
|
396
339
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
end
|
340
|
+
content << @dictionary.to_s(indent: indent, tab: tab)
|
341
|
+
content << "stream" + eol
|
342
|
+
content << encoded_data
|
343
|
+
content << eol << TOKENS.last
|
402
344
|
|
403
|
-
|
404
|
-
|
345
|
+
super(content, eol: eol)
|
346
|
+
end
|
405
347
|
|
406
|
-
|
407
|
-
|
408
|
-
|
348
|
+
def [](key) # :nodoc:
|
349
|
+
@dictionary[key]
|
350
|
+
end
|
409
351
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
@dictionary[:DecodeParms] = dparms = []
|
414
|
-
end
|
352
|
+
def []=(key, val) # :nodoc:
|
353
|
+
@dictionary[key] = val
|
354
|
+
end
|
415
355
|
|
416
|
-
|
417
|
-
|
418
|
-
|
356
|
+
def each_key(&b) # :nodoc:
|
357
|
+
@dictionary.each_key(&b)
|
358
|
+
end
|
419
359
|
|
420
|
-
|
421
|
-
|
360
|
+
def each_pair(&b) # :nodoc
|
361
|
+
@dictionary.each_pair(&b)
|
362
|
+
end
|
363
|
+
alias_method :each, :each_pair
|
422
364
|
|
423
|
-
|
424
|
-
|
365
|
+
def key?(name)
|
366
|
+
@dictionary.key?(name)
|
367
|
+
end
|
368
|
+
alias_method :has_key?, :key?
|
425
369
|
|
426
|
-
|
427
|
-
|
428
|
-
|
370
|
+
def keys
|
371
|
+
@dictionary.keys
|
372
|
+
end
|
429
373
|
|
430
|
-
|
431
|
-
mod = filter_module(filter)
|
374
|
+
private
|
432
375
|
|
433
|
-
|
376
|
+
def decoded? # :nodoc:
|
377
|
+
!@data.nil?
|
378
|
+
end
|
434
379
|
|
435
|
-
|
436
|
-
|
437
|
-
|
380
|
+
def encoded? # :nodoc:
|
381
|
+
!@encoded_data.nil?
|
382
|
+
end
|
438
383
|
|
439
|
-
|
384
|
+
def each_decode_params
|
385
|
+
params = self.DecodeParms
|
386
|
+
|
387
|
+
unless block_given?
|
388
|
+
return enum_for(__method__) do
|
389
|
+
case params
|
390
|
+
when NilClass then 0
|
391
|
+
when Array then params.length
|
392
|
+
else
|
393
|
+
1
|
394
|
+
end
|
440
395
|
end
|
396
|
+
end
|
441
397
|
|
442
|
-
|
443
|
-
unless name.is_a?(Name)
|
444
|
-
raise InvalidObjectStreamObjectError, "Filter has invalid type #{name.type}"
|
445
|
-
end
|
398
|
+
return if params.nil?
|
446
399
|
|
447
|
-
|
448
|
-
|
449
|
-
|
400
|
+
if params.is_a?(Array)
|
401
|
+
params.each do |param| yield(param) end
|
402
|
+
else
|
403
|
+
yield(params)
|
404
|
+
end
|
450
405
|
|
451
|
-
|
452
|
-
end
|
406
|
+
self
|
453
407
|
end
|
454
408
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
class ExternalStream < Stream
|
409
|
+
def decode_params
|
410
|
+
each_decode_params.to_a
|
411
|
+
end
|
459
412
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
413
|
+
def set_decode_params(layer, params) # :nodoc:
|
414
|
+
dparms = self.DecodeParms
|
415
|
+
unless dparms.is_a? ::Array
|
416
|
+
@dictionary[:DecodeParms] = dparms = []
|
417
|
+
end
|
418
|
+
|
419
|
+
if layer > dparms.length - 1
|
420
|
+
dparms.concat(::Array.new(layer - dparms.length + 1, Null.new))
|
421
|
+
end
|
422
|
+
|
423
|
+
dparms[layer] = params
|
424
|
+
@dictionary[:DecodeParms] = dparms.first if dparms.length == 1
|
425
|
+
|
426
|
+
self
|
464
427
|
end
|
465
428
|
|
466
|
-
|
429
|
+
def decode_data(data, filter, params) # :nodoc:
|
430
|
+
filter_module(filter).decode(data, params)
|
467
431
|
end
|
468
432
|
|
469
|
-
#
|
470
|
-
|
471
|
-
#
|
472
|
-
class ObjectStream < Stream
|
473
|
-
include Enumerable
|
433
|
+
def encode_data(data, filter, params) # :nodoc:
|
434
|
+
mod = filter_module(filter)
|
474
435
|
|
475
|
-
|
476
|
-
OBJ = 1 #:nodoc:
|
436
|
+
encoded = mod.encode(data, params)
|
477
437
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
field :Extends, :Type => ObjectStream
|
438
|
+
if %i[ASCIIHexDecode ASCII85Decode AHx A85].include?(filter.value)
|
439
|
+
encoded << mod::EOD
|
440
|
+
end
|
482
441
|
|
483
|
-
|
484
|
-
|
485
|
-
# _dictionary_:: A hash of attributes to set to the Stream.
|
486
|
-
# _raw_data_:: The Stream data.
|
487
|
-
#
|
488
|
-
def initialize(raw_data = "", dictionary = {})
|
489
|
-
super
|
442
|
+
encoded
|
443
|
+
end
|
490
444
|
|
491
|
-
|
492
|
-
|
445
|
+
def filter_module(name)
|
446
|
+
unless name.is_a?(Name)
|
447
|
+
raise InvalidObjectStreamObjectError, "Filter has invalid type #{name.type}"
|
448
|
+
end
|
493
449
|
|
494
|
-
|
495
|
-
|
450
|
+
unless DEFINED_FILTERS.include?(name.value)
|
451
|
+
raise InvalidStreamObjectError, "Invalid filter : #{name}"
|
452
|
+
end
|
496
453
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
454
|
+
Filter.const_get(name.value.to_s.sub(/Decode$/, ""))
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
#
|
459
|
+
# Class representing an external Stream.
|
460
|
+
#
|
461
|
+
class ExternalStream < Stream
|
462
|
+
def initialize(filespec, hash = {})
|
463
|
+
hash[:F] = filespec
|
464
|
+
super('', hash)
|
465
|
+
end
|
466
|
+
end
|
501
467
|
|
502
|
-
|
503
|
-
|
468
|
+
class InvalidObjectStreamObjectError < InvalidStreamObjectError # :nodoc:
|
469
|
+
end
|
504
470
|
|
505
|
-
|
506
|
-
|
471
|
+
#
|
472
|
+
# Class representing a Stream containing other Objects.
|
473
|
+
#
|
474
|
+
class ObjectStream < Stream
|
475
|
+
include Enumerable
|
507
476
|
|
508
|
-
|
509
|
-
|
510
|
-
obj.set_indirect(true)
|
511
|
-
obj.no = num
|
512
|
-
end
|
477
|
+
NUM = 0 # :nodoc:
|
478
|
+
OBJ = 1 # :nodoc:
|
513
479
|
|
514
|
-
|
480
|
+
field :Type, Type: Name, Default: :ObjStm, Required: true, Version: "1.5"
|
481
|
+
field :N, Type: Integer, Required: true
|
482
|
+
field :First, Type: Integer, Required: true
|
483
|
+
field :Extends, Type: ObjectStream
|
515
484
|
|
516
|
-
|
517
|
-
|
485
|
+
#
|
486
|
+
# Creates a new Object Stream.
|
487
|
+
# _dictionary_:: A hash of attributes to set to the Stream.
|
488
|
+
# _raw_data_:: The Stream data.
|
489
|
+
#
|
490
|
+
def initialize(raw_data = "", dictionary = {})
|
491
|
+
super
|
518
492
|
|
519
|
-
|
520
|
-
|
493
|
+
@objects = nil
|
494
|
+
end
|
521
495
|
|
522
|
-
|
523
|
-
|
524
|
-
# _object_:: The Object to append.
|
525
|
-
#
|
526
|
-
def <<(object)
|
527
|
-
unless object.generation == 0
|
528
|
-
raise InvalidObjectError, "Cannot store an object with generation > 0 in an ObjectStream"
|
529
|
-
end
|
496
|
+
def pre_build # :nodoc:
|
497
|
+
load!
|
530
498
|
|
531
|
-
|
532
|
-
|
533
|
-
|
499
|
+
prolog = +""
|
500
|
+
data = +""
|
501
|
+
objoff = 0
|
502
|
+
@objects.to_a.sort.each do |num, obj|
|
503
|
+
obj.set_indirect(false)
|
504
|
+
obj.objstm_offset = objoff
|
534
505
|
|
535
|
-
|
536
|
-
|
537
|
-
raise InvalidObjectError, "The ObjectStream must be added to a document before inserting objects"
|
538
|
-
end
|
506
|
+
prolog << "#{num} #{objoff} "
|
507
|
+
objdata = "#{obj} "
|
539
508
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
509
|
+
objoff += objdata.size
|
510
|
+
data << objdata
|
511
|
+
obj.set_indirect(true)
|
512
|
+
obj.no = num
|
513
|
+
end
|
544
514
|
|
545
|
-
|
515
|
+
self.data = prolog + data
|
546
516
|
|
547
|
-
|
548
|
-
|
517
|
+
@dictionary[:N] = @objects.size
|
518
|
+
@dictionary[:First] = prolog.size
|
549
519
|
|
550
|
-
|
551
|
-
|
552
|
-
alias insert <<
|
520
|
+
super
|
521
|
+
end
|
553
522
|
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
523
|
+
#
|
524
|
+
# Adds a new Object to this Stream.
|
525
|
+
# _object_:: The Object to append.
|
526
|
+
#
|
527
|
+
def <<(object)
|
528
|
+
unless object.generation == 0
|
529
|
+
raise InvalidObjectError, "Cannot store an object with generation > 0 in an ObjectStream"
|
530
|
+
end
|
559
531
|
|
560
|
-
|
561
|
-
|
532
|
+
if object.is_a?(Stream)
|
533
|
+
raise InvalidObjectError, "Cannot store a Stream in an ObjectStream"
|
534
|
+
end
|
562
535
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
@objects.to_a.sort.index { |num, _| num == no }
|
568
|
-
end
|
536
|
+
# We must have an associated document to generate new object numbers.
|
537
|
+
if @document.nil?
|
538
|
+
raise InvalidObjectError, "The ObjectStream must be added to a document before inserting objects"
|
539
|
+
end
|
569
540
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
def extract(no)
|
575
|
-
load!
|
541
|
+
# The object already belongs to a document.
|
542
|
+
unless object.document.nil?
|
543
|
+
object = import_object_from_document(object)
|
544
|
+
end
|
576
545
|
|
577
|
-
|
578
|
-
end
|
546
|
+
load!
|
579
547
|
|
580
|
-
|
581
|
-
|
582
|
-
# _index_:: The Object index in the ObjectStream.
|
583
|
-
#
|
584
|
-
def extract_by_index(index)
|
585
|
-
load!
|
548
|
+
object.no, object.generation = @document.allocate_new_object_number if object.no == 0
|
549
|
+
store_object(object)
|
586
550
|
|
587
|
-
|
588
|
-
|
551
|
+
Reference.new(object.no, 0)
|
552
|
+
end
|
553
|
+
alias_method :insert, :<<
|
589
554
|
|
590
|
-
|
591
|
-
|
555
|
+
#
|
556
|
+
# Deletes Object _no_.
|
557
|
+
#
|
558
|
+
def delete(no)
|
559
|
+
load!
|
592
560
|
|
593
|
-
|
594
|
-
|
595
|
-
# _no_:: The Object number.
|
596
|
-
#
|
597
|
-
def include?(no)
|
598
|
-
load!
|
561
|
+
@objects.delete(no)
|
562
|
+
end
|
599
563
|
|
600
|
-
|
601
|
-
|
564
|
+
#
|
565
|
+
# Returns the index of Object _no_.
|
566
|
+
#
|
567
|
+
def index(no)
|
568
|
+
@objects.to_a.sort.index { |num, _| num == no }
|
569
|
+
end
|
602
570
|
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
571
|
+
#
|
572
|
+
# Returns a given decompressed object contained in the Stream.
|
573
|
+
# _no_:: The Object number.
|
574
|
+
#
|
575
|
+
def extract(no)
|
576
|
+
load!
|
608
577
|
|
609
|
-
|
610
|
-
|
611
|
-
alias each_object each
|
578
|
+
@objects[no]
|
579
|
+
end
|
612
580
|
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
581
|
+
#
|
582
|
+
# Returns a given decompressed object by index.
|
583
|
+
# _index_:: The Object index in the ObjectStream.
|
584
|
+
#
|
585
|
+
def extract_by_index(index)
|
586
|
+
load!
|
618
587
|
|
619
|
-
|
620
|
-
|
588
|
+
raise TypeError, "index must be an integer" unless index.is_a?(::Integer)
|
589
|
+
raise IndexError, "index #{index} out of range" if (index < 0) || (index >= @objects.size)
|
621
590
|
|
622
|
-
|
623
|
-
|
624
|
-
#
|
625
|
-
def objects
|
626
|
-
load!
|
591
|
+
@objects.to_a.sort[index][1]
|
592
|
+
end
|
627
593
|
|
628
|
-
|
629
|
-
|
594
|
+
#
|
595
|
+
# Returns whether a specific object is contained in this stream.
|
596
|
+
# _no_:: The Object number.
|
597
|
+
#
|
598
|
+
def include?(no)
|
599
|
+
load!
|
630
600
|
|
631
|
-
|
601
|
+
@objects.include?(no)
|
602
|
+
end
|
632
603
|
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
def import_object_from_document(object)
|
639
|
-
obj_doc = object.document
|
604
|
+
#
|
605
|
+
# Iterates over each object in the stream.
|
606
|
+
#
|
607
|
+
def each(&b)
|
608
|
+
load!
|
640
609
|
|
641
|
-
|
642
|
-
|
643
|
-
|
610
|
+
@objects.values.each(&b)
|
611
|
+
end
|
612
|
+
alias_method :each_object, :each
|
644
613
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
614
|
+
#
|
615
|
+
# Returns the number of objects contained in the stream.
|
616
|
+
#
|
617
|
+
def length
|
618
|
+
raise InvalidObjectStreamObjectError, "Invalid number of objects" unless self.N.is_a?(Integer)
|
649
619
|
|
650
|
-
|
651
|
-
|
620
|
+
self.N.to_i
|
621
|
+
end
|
652
622
|
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
623
|
+
#
|
624
|
+
# Returns the array of inner objects.
|
625
|
+
#
|
626
|
+
def objects
|
627
|
+
load!
|
657
628
|
|
658
|
-
|
659
|
-
|
629
|
+
@objects.values
|
630
|
+
end
|
631
|
+
|
632
|
+
private
|
633
|
+
|
634
|
+
#
|
635
|
+
# Preprocess the object in case it already belongs to a document.
|
636
|
+
# If the document is the same as the current object stream, remove the duplicate object from our document.
|
637
|
+
# If the object comes from another document, use the export method to create a version without references.
|
638
|
+
#
|
639
|
+
def import_object_from_document(object)
|
640
|
+
obj_doc = object.document
|
660
641
|
|
661
|
-
|
662
|
-
|
642
|
+
# Remove the previous instance if the object is indirect to avoid duplicates.
|
643
|
+
if obj_doc.equal?(@document)
|
644
|
+
@document.delete_object(object.reference) if object.indirect?
|
663
645
|
|
664
|
-
|
646
|
+
# Otherwise, create a exported version of the object.
|
647
|
+
else
|
648
|
+
object = object.export
|
649
|
+
end
|
665
650
|
|
666
|
-
|
667
|
-
|
651
|
+
object
|
652
|
+
end
|
668
653
|
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
654
|
+
def store_object(object) # :nodoc:
|
655
|
+
object.set_indirect(true) # all stored objects are indirect.
|
656
|
+
object.parent = self # set this stream as the parent.
|
657
|
+
object.set_document(@document) # inherit document information.
|
673
658
|
|
674
|
-
|
675
|
-
|
676
|
-
offsets << Integer.parse(data).to_i
|
677
|
-
end
|
659
|
+
@objects[object.no] = object
|
660
|
+
end
|
678
661
|
|
679
|
-
|
680
|
-
|
681
|
-
raise InvalidObjectStreamObjectError, "Invalid offset '#{offsets[i]} for object #{nums[i]}"
|
682
|
-
end
|
662
|
+
def load! # :nodoc:
|
663
|
+
return unless @objects.nil?
|
683
664
|
|
684
|
-
|
685
|
-
type = Object.typeof(data)
|
686
|
-
raise InvalidObjectStreamObjectError,
|
687
|
-
"Bad embedded object format in object stream" if type.nil?
|
665
|
+
decode!
|
688
666
|
|
689
|
-
|
690
|
-
|
691
|
-
embeddedobj.objstm_offset = offsets[i]
|
667
|
+
@objects = {}
|
668
|
+
return if @data.empty?
|
692
669
|
|
693
|
-
|
694
|
-
|
695
|
-
|
670
|
+
data = StringScanner.new(@data)
|
671
|
+
nums = []
|
672
|
+
offsets = []
|
673
|
+
first_offset = first_object_offset
|
696
674
|
|
697
|
-
|
698
|
-
|
699
|
-
|
675
|
+
length.times do
|
676
|
+
nums << Integer.parse(data).to_i
|
677
|
+
offsets << Integer.parse(data).to_i
|
678
|
+
end
|
679
|
+
|
680
|
+
length.times do |i|
|
681
|
+
unless (0...@data.size).cover?(first_object_offset + offsets[i]) && (offsets[i] >= 0)
|
682
|
+
raise InvalidObjectStreamObjectError, "Invalid offset '#{offsets[i]} for object #{nums[i]}"
|
683
|
+
end
|
700
684
|
|
701
|
-
|
685
|
+
data.pos = first_offset + offsets[i]
|
686
|
+
type = Object.typeof(data)
|
687
|
+
if type.nil?
|
688
|
+
raise InvalidObjectStreamObjectError,
|
689
|
+
"Bad embedded object format in object stream"
|
702
690
|
end
|
691
|
+
|
692
|
+
embeddedobj = type.parse(data)
|
693
|
+
embeddedobj.no = nums[i] # object number
|
694
|
+
embeddedobj.objstm_offset = offsets[i]
|
695
|
+
|
696
|
+
store_object(embeddedobj)
|
697
|
+
end
|
703
698
|
end
|
704
699
|
|
700
|
+
def first_object_offset # :nodoc:
|
701
|
+
raise InvalidObjectStreamObjectError, "Invalid First offset" unless self.First.is_a?(Integer)
|
702
|
+
raise InvalidObjectStreamObjectError, "Negative object offset" if self.First < 0
|
703
|
+
|
704
|
+
self.First.to_i
|
705
|
+
end
|
706
|
+
end
|
705
707
|
end
|