origami 1.0.2
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.
- data/COPYING.LESSER +165 -0
- data/README +77 -0
- data/VERSION +1 -0
- data/bin/config/pdfcop.conf.yml +237 -0
- data/bin/gui/about.rb +46 -0
- data/bin/gui/config.rb +132 -0
- data/bin/gui/file.rb +385 -0
- data/bin/gui/hexdump.rb +74 -0
- data/bin/gui/hexview.rb +91 -0
- data/bin/gui/imgview.rb +72 -0
- data/bin/gui/menu.rb +392 -0
- data/bin/gui/properties.rb +132 -0
- data/bin/gui/signing.rb +635 -0
- data/bin/gui/textview.rb +107 -0
- data/bin/gui/treeview.rb +409 -0
- data/bin/gui/walker.rb +282 -0
- data/bin/gui/xrefs.rb +79 -0
- data/bin/pdf2graph +121 -0
- data/bin/pdf2ruby +353 -0
- data/bin/pdfcocoon +104 -0
- data/bin/pdfcop +455 -0
- data/bin/pdfdecompress +104 -0
- data/bin/pdfdecrypt +95 -0
- data/bin/pdfencrypt +112 -0
- data/bin/pdfextract +221 -0
- data/bin/pdfmetadata +123 -0
- data/bin/pdfsh +13 -0
- data/bin/pdfwalker +7 -0
- data/bin/shell/.irbrc +104 -0
- data/bin/shell/console.rb +136 -0
- data/bin/shell/hexdump.rb +83 -0
- data/origami.rb +36 -0
- data/origami/3d.rb +239 -0
- data/origami/acroform.rb +321 -0
- data/origami/actions.rb +299 -0
- data/origami/adobe/fdf.rb +259 -0
- data/origami/adobe/ppklite.rb +489 -0
- data/origami/annotations.rb +775 -0
- data/origami/array.rb +187 -0
- data/origami/boolean.rb +101 -0
- data/origami/catalog.rb +486 -0
- data/origami/destinations.rb +213 -0
- data/origami/dictionary.rb +188 -0
- data/origami/docmdp.rb +96 -0
- data/origami/encryption.rb +1293 -0
- data/origami/export.rb +283 -0
- data/origami/file.rb +222 -0
- data/origami/filters.rb +250 -0
- data/origami/filters/ascii.rb +189 -0
- data/origami/filters/ccitt.rb +515 -0
- data/origami/filters/crypt.rb +47 -0
- data/origami/filters/dct.rb +61 -0
- data/origami/filters/flate.rb +112 -0
- data/origami/filters/jbig2.rb +63 -0
- data/origami/filters/jpx.rb +53 -0
- data/origami/filters/lzw.rb +195 -0
- data/origami/filters/predictors.rb +276 -0
- data/origami/filters/runlength.rb +117 -0
- data/origami/font.rb +209 -0
- data/origami/functions.rb +93 -0
- data/origami/graphics.rb +33 -0
- data/origami/graphics/colors.rb +191 -0
- data/origami/graphics/instruction.rb +126 -0
- data/origami/graphics/path.rb +154 -0
- data/origami/graphics/patterns.rb +180 -0
- data/origami/graphics/state.rb +164 -0
- data/origami/graphics/text.rb +224 -0
- data/origami/graphics/xobject.rb +493 -0
- data/origami/header.rb +90 -0
- data/origami/linearization.rb +318 -0
- data/origami/metadata.rb +114 -0
- data/origami/name.rb +170 -0
- data/origami/null.rb +75 -0
- data/origami/numeric.rb +188 -0
- data/origami/obfuscation.rb +233 -0
- data/origami/object.rb +527 -0
- data/origami/outline.rb +59 -0
- data/origami/page.rb +559 -0
- data/origami/parser.rb +268 -0
- data/origami/parsers/fdf.rb +45 -0
- data/origami/parsers/pdf.rb +27 -0
- data/origami/parsers/pdf/linear.rb +113 -0
- data/origami/parsers/ppklite.rb +86 -0
- data/origami/pdf.rb +1144 -0
- data/origami/reference.rb +113 -0
- data/origami/signature.rb +474 -0
- data/origami/stream.rb +575 -0
- data/origami/string.rb +416 -0
- data/origami/trailer.rb +173 -0
- data/origami/webcapture.rb +87 -0
- data/origami/xfa.rb +3027 -0
- data/origami/xreftable.rb +447 -0
- data/templates/patterns.rb +66 -0
- data/templates/widgets.rb +173 -0
- data/templates/xdp.rb +92 -0
- data/tests/dataset/test.dummycrt +28 -0
- data/tests/dataset/test.dummykey +27 -0
- data/tests/tc_actions.rb +32 -0
- data/tests/tc_annotations.rb +85 -0
- data/tests/tc_pages.rb +37 -0
- data/tests/tc_pdfattach.rb +24 -0
- data/tests/tc_pdfencrypt.rb +110 -0
- data/tests/tc_pdfnew.rb +32 -0
- data/tests/tc_pdfparse.rb +98 -0
- data/tests/tc_pdfsig.rb +37 -0
- data/tests/tc_streams.rb +129 -0
- data/tests/ts_pdf.rb +45 -0
- metadata +193 -0
data/origami/actions.rb
ADDED
@@ -0,0 +1,299 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
actions.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
This file is part of Origami, PDF manipulation framework for Ruby
|
8
|
+
Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
|
9
|
+
All right reserved.
|
10
|
+
|
11
|
+
Origami is free software: you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU Lesser General Public License as published by
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
Origami is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
GNU Lesser General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU Lesser General Public License
|
22
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
module Origami
|
27
|
+
|
28
|
+
class PDF
|
29
|
+
|
30
|
+
#
|
31
|
+
# Lookup script in the scripts name directory.
|
32
|
+
#
|
33
|
+
def get_script_by_name(name)
|
34
|
+
resolve_name Names::Root::JAVASCRIPT, name
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Calls block for each named JavaScript script.
|
39
|
+
#
|
40
|
+
def each_named_script(&b)
|
41
|
+
each_name(Names::Root::JAVASCRIPT, &b)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Class representing an action to launch in a PDF.
|
47
|
+
#
|
48
|
+
class Action < Dictionary
|
49
|
+
|
50
|
+
include StandardObject
|
51
|
+
|
52
|
+
field :Type, :Type => Name, :Default => :Action
|
53
|
+
field :S, :Type => Name, :Required => true
|
54
|
+
field :Next, :Type => [ Array, Dictionary ], :Version => "1.2"
|
55
|
+
|
56
|
+
#
|
57
|
+
# Class representing a action going to a destination in the current document.
|
58
|
+
#
|
59
|
+
class GoTo < Action
|
60
|
+
|
61
|
+
field :S, :Type => Name, :Default => :GoTo, :Required => true
|
62
|
+
field :D, :Type => [ Array, Name, ByteString ], :Required => true
|
63
|
+
|
64
|
+
#
|
65
|
+
# Creates a new GoTo Action.
|
66
|
+
# _hash_:: A hash of options to set for this jump.
|
67
|
+
#
|
68
|
+
def initialize(hash = {})
|
69
|
+
|
70
|
+
if hash.is_a? Destination
|
71
|
+
super(:S => :GoTo, :D => hash)
|
72
|
+
else
|
73
|
+
super(hash)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Class representing an action launching an URL.
|
82
|
+
#
|
83
|
+
class URI < Action
|
84
|
+
|
85
|
+
field :S, :Type => Name, :Default => :URI, :Required => true
|
86
|
+
field :URI, :Type => ByteString, :Required => true
|
87
|
+
field :IsMap, :Type => Boolean, :Default => false
|
88
|
+
|
89
|
+
#
|
90
|
+
# Creates a new URI Action.
|
91
|
+
# _uri_:: The URI to launch.
|
92
|
+
# _ismap_::
|
93
|
+
#
|
94
|
+
def initialize(uri, ismap = false)
|
95
|
+
super(:URI => uri, :IsMap => ismap)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Class representing a JavaScript Action.
|
102
|
+
#
|
103
|
+
class JavaScript < Action
|
104
|
+
|
105
|
+
field :S, :Type => Name, :Default => :JavaScript, :Required => true
|
106
|
+
field :JS, :Type => [ Stream, String ], :Required => true
|
107
|
+
|
108
|
+
#
|
109
|
+
# Creates a new JavaScript Action.
|
110
|
+
# _script_:: The script to be executed.
|
111
|
+
#
|
112
|
+
def initialize(script)
|
113
|
+
super(:JS => script)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Class representing an Action which run a command on the current system.
|
120
|
+
#
|
121
|
+
class Launch < Action
|
122
|
+
|
123
|
+
field :S, :Type => Name, :Default => :Launch, :Required => true
|
124
|
+
field :F, :Type => [ ByteString, Dictionary ]
|
125
|
+
field :Win, :Type => Dictionary
|
126
|
+
field :Mac, :Type => Object
|
127
|
+
field :Unix, :Type => Object
|
128
|
+
field :NewWindow, :Type => Boolean
|
129
|
+
|
130
|
+
#
|
131
|
+
# Dictionary for passing parameter to Windows applications during Launch.
|
132
|
+
#
|
133
|
+
class WindowsLaunchParams < Dictionary
|
134
|
+
|
135
|
+
include StandardObject
|
136
|
+
|
137
|
+
field :F, :Type => ByteString, :Required => true
|
138
|
+
field :D, :Type => ByteString
|
139
|
+
field :O, :Type => ByteString, :Default => "open"
|
140
|
+
field :P, :Type => ByteString
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Class representing a Named Action.
|
147
|
+
# Named actions are predefined GoTo actions.
|
148
|
+
#
|
149
|
+
class Named < Action
|
150
|
+
|
151
|
+
NEXTPAGE = :NextPage
|
152
|
+
PREVPAGE = :PrevPage
|
153
|
+
FIRSTPAGE = :FirstPage
|
154
|
+
LASTPAGE = :LastPage
|
155
|
+
PRINT = :Print
|
156
|
+
|
157
|
+
field :S, :Type => Name, :Default => :Named, :Required => true
|
158
|
+
field :N, :Type => Name, :Required => true
|
159
|
+
|
160
|
+
def initialize(type)
|
161
|
+
super(:N => type)
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# Class representing a GoTo Action to an external file.
|
168
|
+
#
|
169
|
+
class GoToR < Action
|
170
|
+
|
171
|
+
field :S, :Type => Name, :Default => :GoToR, :Required => true
|
172
|
+
field :F, :Type => [ ByteString, Dictionary ], :Required => true
|
173
|
+
field :D, :Type => [ Array, Name, ByteString ], :Required => true
|
174
|
+
field :NewWindow, :Type => Boolean, :Version => "1.2"
|
175
|
+
|
176
|
+
#
|
177
|
+
# Creates a new GoTo remote Action.
|
178
|
+
# _file_:: A FileSpec describing the file.
|
179
|
+
# _dest_:: A Destination in the file.
|
180
|
+
# _newwindow_:: Specifies whether the file has to be opened in a new window.
|
181
|
+
#
|
182
|
+
def initialize(file, dest = Destination::GlobalFit.new(0), newwindow = false)
|
183
|
+
super(:F => file, :D => dest, :NewWindow => newwindow)
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Class representing a GoTo Action to an embedded pdf file.
|
190
|
+
#
|
191
|
+
class GoToE < Action
|
192
|
+
|
193
|
+
field :S, :Type => Name, :Default => :GoToE, :Required => true
|
194
|
+
field :F, :Type => [ Dictionary, ByteString ]
|
195
|
+
field :D, :Type => [ Array, Name, ByteString ], :Required => true
|
196
|
+
field :NewWindow, :Type => Boolean
|
197
|
+
field :T, :Type => Dictionary
|
198
|
+
|
199
|
+
#
|
200
|
+
# A class representing a target for a GoToE to an embedded file.
|
201
|
+
#
|
202
|
+
class EmbeddedTarget < Dictionary
|
203
|
+
|
204
|
+
include StandardObject
|
205
|
+
|
206
|
+
module Relationship
|
207
|
+
PARENT = :P
|
208
|
+
CHILD = :C
|
209
|
+
end
|
210
|
+
|
211
|
+
field :R, :Type => Name, :Required => true
|
212
|
+
field :N, :Type => ByteString
|
213
|
+
field :P, :Type => [ Integer, ByteString ]
|
214
|
+
field :A, :Type => [ Integer, ByteString ]
|
215
|
+
field :T, :Type => Dictionary
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
def initialize(filename, dest, newwindow = false)
|
220
|
+
super(:T => EmbeddedTarget.new(:R => :C, :N => filename), :D => dest, :NewWindow => newwindow)
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# (PDF 1.2) Send data to a uniform resource locator. p703
|
227
|
+
#
|
228
|
+
class SubmitForm < Action
|
229
|
+
|
230
|
+
module Flags
|
231
|
+
INCLUDEEXCLUDE = 1 << 0
|
232
|
+
INCLUDENOVALUEFIELDS = 1 << 1
|
233
|
+
EXPORTFORMAT = 1 << 2
|
234
|
+
GETMETHOD = 1 << 3
|
235
|
+
SUBMITCOORDINATES = 1 << 4
|
236
|
+
XFDF = 1 << 5
|
237
|
+
INCLUDEAPPENDSAVES = 1 << 6
|
238
|
+
INCLUDEANNOTATIONS = 1 << 7
|
239
|
+
SUBMITPDF = 1 << 8
|
240
|
+
CANONICALFORMAT = 1 << 9
|
241
|
+
EXCLNONUSERANNOTS = 1 << 10
|
242
|
+
EXCLFKEY = 1 << 11
|
243
|
+
EMBEDFORM = 1 << 12
|
244
|
+
end
|
245
|
+
|
246
|
+
field :S, :Type => Name, :Default => :SubmitForm, :Required => true
|
247
|
+
field :F, :Type => Dictionary
|
248
|
+
field :Fields, :Type => Array
|
249
|
+
field :Flags, :Type => Integer, :Default => 0
|
250
|
+
|
251
|
+
def initialize(url, fields = [], flags = 0)
|
252
|
+
if not url.is_a? FileSpec
|
253
|
+
url = FileSpec.new(:FS => :URL, :F => url)
|
254
|
+
end
|
255
|
+
|
256
|
+
super(:F => url, :Fields => fields, :Flags => flags)
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
class ImportData < Action
|
262
|
+
|
263
|
+
field :S, :Type => Name, :Default => :ImportData, :Required => true
|
264
|
+
field :F, :Type => Dictionary, :Required => true
|
265
|
+
|
266
|
+
def initialize(file)
|
267
|
+
if not file.is_a? FileSpec
|
268
|
+
file = FileSpec.new(:FS => :File, :F => file)
|
269
|
+
end
|
270
|
+
|
271
|
+
super(:F => file)
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
class RichMediaExecute < Action
|
277
|
+
|
278
|
+
field :S, :Type => Name, :Default => :RichMediaExecute, :Version => "1.7", :ExtensionLevel => 3, :Required => true
|
279
|
+
field :TA, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
|
280
|
+
field :TI, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
|
281
|
+
field :CMD, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
|
282
|
+
|
283
|
+
class Command < Dictionary
|
284
|
+
include StandardObject
|
285
|
+
|
286
|
+
field :Type, :Type => Name, :Default => :RichMediaCommand, :Version => "1.7", :ExtensionLevel => 3
|
287
|
+
field :C, :Type => String, :Version => "1.7", :ExtensionLevel => 3, :Required => true
|
288
|
+
field :A, :Type => Object, :Version => "1.7", :ExtensionLevel => 3
|
289
|
+
end
|
290
|
+
|
291
|
+
def initialize(annotation, command, *params)
|
292
|
+
super(:TA => annotation, :CMD => Command.new(:C => command, :A => params))
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= File
|
4
|
+
adobe/fdf.rb
|
5
|
+
|
6
|
+
= Info
|
7
|
+
This file is part of Origami, PDF manipulation framework for Ruby
|
8
|
+
Copyright (C) 2010 Guillaume Delugr� <guillaume@security-labs.org>
|
9
|
+
All right reserved.
|
10
|
+
|
11
|
+
Origami is free software: you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU Lesser General Public License as published by
|
13
|
+
the Free Software Foundation, either version 3 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
Origami is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
GNU Lesser General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU Lesser General Public License
|
22
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
require 'origami/object'
|
27
|
+
require 'origami/name'
|
28
|
+
require 'origami/dictionary'
|
29
|
+
require 'origami/reference'
|
30
|
+
require 'origami/boolean'
|
31
|
+
require 'origami/numeric'
|
32
|
+
require 'origami/string'
|
33
|
+
require 'origami/array'
|
34
|
+
require 'origami/trailer'
|
35
|
+
require 'origami/xreftable'
|
36
|
+
|
37
|
+
module Origami
|
38
|
+
|
39
|
+
module Adobe
|
40
|
+
|
41
|
+
#
|
42
|
+
# Class representing an AcroForm Forms Data Format file.
|
43
|
+
#
|
44
|
+
class FDF
|
45
|
+
|
46
|
+
class Header
|
47
|
+
|
48
|
+
MAGIC = /\A%FDF-(\d)\.(\d)/
|
49
|
+
|
50
|
+
attr_accessor :majorversion, :minorversion
|
51
|
+
|
52
|
+
#
|
53
|
+
# Creates a file header, with the given major and minor versions.
|
54
|
+
# _majorversion_:: Major version.
|
55
|
+
# _minorversion_:: Minor version.
|
56
|
+
#
|
57
|
+
def initialize(majorversion = 2, minorversion = 1)
|
58
|
+
@majorversion, @minorversion = majorversion, minorversion
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.parse(stream) #:nodoc:
|
62
|
+
|
63
|
+
if not stream.scan(MAGIC).nil?
|
64
|
+
maj = stream[1].to_i
|
65
|
+
min = stream[2].to_i
|
66
|
+
else
|
67
|
+
raise InvalidHeader, "Invalid header format"
|
68
|
+
end
|
69
|
+
|
70
|
+
PPKLite::Header.new(maj,min)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
"%FDF-#{@majorversion}.#{@minorversion}" + EOL
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_sym #:nodoc:
|
78
|
+
"#{@majorversion}.#{@minorversion}".to_sym
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_f #:nodoc:
|
82
|
+
to_sym.to_s.to_f
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class Revision #:nodoc;
|
88
|
+
attr_accessor :pdf
|
89
|
+
attr_accessor :body, :xreftable, :trailer
|
90
|
+
|
91
|
+
def initialize(adbk)
|
92
|
+
@pdf = adbk
|
93
|
+
@body = {}
|
94
|
+
@xreftable = nil
|
95
|
+
@trailer = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def trailer=(trl)
|
99
|
+
trl.pdf = @pdf
|
100
|
+
@trailer = trl
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_accessor :header, :revisions
|
105
|
+
|
106
|
+
def initialize #:nodoc:
|
107
|
+
@header = FDF::Header.new
|
108
|
+
@revisions = [ Revision.new(self) ]
|
109
|
+
@revisions.first.trailer = Trailer.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def objects
|
113
|
+
def append_subobj(root, objset)
|
114
|
+
if objset.find{ |o| o.object_id == root.object_id }.nil?
|
115
|
+
objset << root
|
116
|
+
if root.is_a?(Array) or root.is_a?(Dictionary)
|
117
|
+
root.each { |subobj| append_subobj(subobj, objset) unless subobj.is_a?(Reference) }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
objset = []
|
123
|
+
@revisions.first.body.values.each do |object|
|
124
|
+
unless object.is_a?(Reference)
|
125
|
+
append_subobj(object, objset)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
objset
|
130
|
+
end
|
131
|
+
|
132
|
+
def <<(object)
|
133
|
+
|
134
|
+
object.set_indirect(true)
|
135
|
+
|
136
|
+
if object.no.zero?
|
137
|
+
maxno = 1
|
138
|
+
while get_object(maxno) do maxno = maxno.succ end
|
139
|
+
|
140
|
+
object.generation = 0
|
141
|
+
object.no = maxno
|
142
|
+
end
|
143
|
+
|
144
|
+
@revisions.first.body[object.reference] = object
|
145
|
+
|
146
|
+
object.reference
|
147
|
+
end
|
148
|
+
|
149
|
+
def Catalog
|
150
|
+
get_object(@trailer.Root)
|
151
|
+
end
|
152
|
+
|
153
|
+
def save(filename)
|
154
|
+
|
155
|
+
bin = ""
|
156
|
+
bin << @header.to_s
|
157
|
+
|
158
|
+
lastno, brange = 0, 0
|
159
|
+
|
160
|
+
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
|
161
|
+
xrefsection = XRef::Section.new
|
162
|
+
|
163
|
+
@revisions.first.body.values.sort.each { |obj|
|
164
|
+
if (obj.no - lastno).abs > 1
|
165
|
+
xrefsection << XRef::Subsection.new(brange, xrefs)
|
166
|
+
brange = obj.no
|
167
|
+
xrefs.clear
|
168
|
+
end
|
169
|
+
|
170
|
+
xrefs << XRef.new(bin.size, obj.generation, XRef::USED)
|
171
|
+
lastno = obj.no
|
172
|
+
|
173
|
+
bin << obj.to_s
|
174
|
+
}
|
175
|
+
|
176
|
+
xrefsection << XRef::Subsection.new(brange, xrefs)
|
177
|
+
|
178
|
+
@xreftable = xrefsection
|
179
|
+
@trailer ||= Trailer.new
|
180
|
+
@trailer.Size = rev.body.size + 1
|
181
|
+
@trailer.startxref = bin.size
|
182
|
+
|
183
|
+
bin << @xreftable.to_s
|
184
|
+
bin << @trailer.to_s
|
185
|
+
|
186
|
+
fd = File.open(filename, "w").binmode
|
187
|
+
fd << bin
|
188
|
+
fd.close
|
189
|
+
|
190
|
+
show_entries
|
191
|
+
end
|
192
|
+
alias saveas save
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def rebuildxrefs #:nodoc:
|
197
|
+
|
198
|
+
startxref = @header.to_s.size
|
199
|
+
|
200
|
+
@revisions.first.body.values.each { |object|
|
201
|
+
startxref += object.to_s.size
|
202
|
+
}
|
203
|
+
|
204
|
+
@xreftable = buildxrefs(@revisions.first.body)
|
205
|
+
|
206
|
+
@trailer ||= Trailer.new
|
207
|
+
@trailer.Size = @revisions.first.body.size + 1
|
208
|
+
@trailer.startxref = startxref
|
209
|
+
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
def buildxrefs(objects) #:nodoc:
|
214
|
+
|
215
|
+
lastno = 0
|
216
|
+
brange = 0
|
217
|
+
|
218
|
+
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
|
219
|
+
|
220
|
+
xrefsection = XRef::Section.new
|
221
|
+
objects.sort.each { |object|
|
222
|
+
if (object.no - lastno).abs > 1
|
223
|
+
xrefsection << XRef::Subsection.new(brange, xrefs)
|
224
|
+
brange = object.no
|
225
|
+
xrefs.clear
|
226
|
+
end
|
227
|
+
|
228
|
+
xrefs << XRef.new(get_object_offset(object.no, object.generation), object.generation, XRef::USED)
|
229
|
+
|
230
|
+
lastno = object.no
|
231
|
+
}
|
232
|
+
|
233
|
+
xrefsection << XRef::Subsection.new(brange, xrefs)
|
234
|
+
|
235
|
+
xrefsection
|
236
|
+
end
|
237
|
+
|
238
|
+
def get_object_offset(no,generation) #:nodoc:
|
239
|
+
|
240
|
+
bodyoffset = @header.to_s.size
|
241
|
+
|
242
|
+
objectoffset = bodyoffset
|
243
|
+
|
244
|
+
@revisions.first.body.values.each { |object|
|
245
|
+
if object.no == no and object.generation == generation then return objectoffset
|
246
|
+
else
|
247
|
+
objectoffset += object.to_s.size
|
248
|
+
end
|
249
|
+
}
|
250
|
+
|
251
|
+
nil
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|