origami 1.2.7 → 2.0.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.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,25 +1,20 @@
1
1
  =begin
2
2
 
3
- = File
4
- formats/fdf.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT 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/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
@@ -38,220 +33,312 @@ require 'origami/parsers/fdf'
38
33
 
39
34
  module Origami
40
35
 
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"
36
+ #
37
+ # Class representing an AcroForm Forms Data Format file.
38
+ #
39
+ class FDF
40
+
41
+ def self.read(path, options = {})
42
+ path = File.expand_path(path) if path.is_a?(::String)
43
+
44
+ FDF::Parser.new(options).parse(path)
68
45
  end
69
-
70
- FDF::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
46
 
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
47
+ class Header
48
+ MAGIC = /%FDF-(?<major>\d)\.(?<minor>\d)/
103
49
 
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
50
+ attr_accessor :major_version, :minor_version
51
+
52
+ #
53
+ # Creates a file header, with the given major and minor versions.
54
+ # _major_version_:: Major version.
55
+ # _minor_version_:: Minor version.
56
+ #
57
+ def initialize(major_version = 1, minor_version = 2)
58
+ @major_version, @minor_version = major_version, minor_version
59
+ end
60
+
61
+ def self.parse(stream) #:nodoc:
62
+ if not stream.scan(MAGIC).nil?
63
+ maj = stream['major'].to_i
64
+ min = stream['minor'].to_i
65
+ else
66
+ raise InvalidHeader, "Invalid header format"
67
+ end
68
+
69
+ stream.skip(REGEXP_WHITESPACES)
70
+
71
+ FDF::Header.new(maj, min)
72
+ end
73
+
74
+ def to_s
75
+ "%FDF-#{@major_version}.#{@minor_version}".b + EOL
76
+ end
77
+
78
+ def to_sym #:nodoc:
79
+ "#{@major_version}.#{@minor_version}".to_sym
80
+ end
81
+
82
+ def to_f #:nodoc:
83
+ to_sym.to_s.to_f
84
+ end
119
85
  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)
86
+
87
+ class Revision #:nodoc;
88
+ attr_accessor :document
89
+ attr_accessor :body, :xreftable
90
+ attr_reader :trailer
91
+
92
+ def initialize(fdf)
93
+ @document = fdf
94
+ @body = {}
95
+ @xreftable = nil
96
+ @trailer = nil
97
+ end
98
+
99
+ def trailer=(trl)
100
+ trl.document = @document
101
+ @trailer = trl
102
+ end
103
+
104
+ def each_object(&b)
105
+ @body.each_value(&b)
106
+ end
107
+
108
+ def objects
109
+ @body.values
110
+ end
126
111
  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
112
 
158
- lastno, brange = 0, 0
113
+ class JavaScript < Dictionary
114
+ include StandardObject
115
+
116
+ field :Before, :Type => [ String, Stream ]
117
+ field :After, :Type => [ String, Stream ]
118
+ field :AfterPermsReady, :Type => [ String, Stream ]
119
+ field :Doc, :Type => Array.of(Name, String)
120
+ end
159
121
 
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
122
+ class IconFit < Dictionary
123
+ include StandardObject
124
+
125
+ ALWAYS_SCALE = :A
126
+ SCALE_WHEN_BIGGER = :B
127
+ SCALE_WHEN_SMALLER = :S
128
+ NEVER_SCALE = :N
129
+
130
+ field :SW, :Type => Name
131
+ field :S, :Type => Name
132
+ field :A, :Type => Array.of(Number, length: 2)
133
+ field :FB, :Type => Boolean
168
134
  end
169
135
 
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
- }
136
+ class NamedPageReference < Dictionary
137
+ include StandardObject
138
+
139
+ field :Name, :Type => String, :Required => true
140
+ field :F, :Type => FileSpec
141
+ end
142
+
143
+ class Field < Dictionary
144
+ include StandardObject
145
+
146
+ field :Kids, :Type => Array.of(Field)
147
+ field :T, :Type => String, :Required => true
148
+ field :V, :Type => Dictionary
149
+ field :Ff, :Type => Integer
150
+ field :SetFf, :Type => Integer
151
+ field :ClrFf, :Type => Integer
152
+ field :F, :Type => Integer
153
+ field :SetF, :Type => Integer
154
+ field :ClrF, :Type => Integer
155
+ field :AP, :Type => Annotation::AppearanceDictionary
156
+ field :APRef, :Type => Dictionary
157
+ field :IF, :Type => IconFit
158
+ field :Opt, :Type => Array.of([String, Array.of(String, String)])
159
+ field :A, :Type => Action
160
+ field :AA, :Type => Annotation::AdditionalActions
161
+ field :RV, :Type => [ String, Stream ]
162
+ end
203
163
 
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
164
+ class Template < Dictionary
165
+ include StandardObject
166
+
167
+ field :TRef, :Type => NamedPageReference, :Required => true
168
+ field :Fields, :Type => Array.of(Field)
169
+ field :Rename, :Type => Boolean
226
170
  end
227
171
 
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:
172
+ class Page < Dictionary
173
+ include StandardObject
239
174
 
240
- bodyoffset = @header.to_s.size
241
-
242
- objectoffset = bodyoffset
175
+ field :Templates, :Type => Array.of(Template), :Required => true
176
+ field :Info, :Type => Dictionary
177
+ end
178
+
179
+ class Annotation < Origami::Annotation
180
+ field :Page, :Type => Integer, :Required => true
181
+ end
243
182
 
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
183
+ class Dictionary < Origami::Dictionary
184
+ include StandardObject
185
+
186
+ field :F, :Type => FileSpec
187
+ field :ID, :Type => Array.of(String, length: 2)
188
+ field :Fields, :Type => Array.of(FDF::Field)
189
+ field :Status, :Type => String
190
+ field :Pages, :Type => Array.of(FDF::Page)
191
+ field :Encoding, :Type => Name
192
+ field :Annots, :Type => Array.of(FDF::Annotation)
193
+ field :Differences, :Type => Stream
194
+ field :Target, :Type => String
195
+ field :EmbeddedFDFs, :Type => Array.of(FileSpec)
196
+ field :JavaScript, :Type => JavaScript
248
197
  end
249
- }
250
-
251
- nil
198
+
199
+ class Catalog < Dictionary
200
+ include StandardObject
201
+
202
+ field :Version, :Type => Name
203
+ field :FDF, :Type => FDF::Dictionary, :Required => true
204
+ end
205
+
206
+ attr_accessor :header, :revisions
207
+
208
+ def initialize(parser = nil) #:nodoc:
209
+ @header = FDF::Header.new
210
+ @revisions = [ Revision.new(self) ]
211
+ @revisions.first.trailer = Trailer.new
212
+
213
+ init if parser.nil?
214
+ end
215
+
216
+ def <<(object)
217
+ object.set_indirect(true)
218
+ object.set_document(self)
219
+
220
+ if object.no.zero?
221
+ maxno = 1
222
+ maxno = maxno.succ while get_object(maxno)
223
+
224
+ object.generation = 0
225
+ object.no = maxno
226
+ end
227
+
228
+ @revisions.first.body[object.reference] = object
229
+
230
+ object.reference
231
+ end
232
+ alias insert <<
233
+
234
+ def get_object(no, generation = 0) #:nodoc:
235
+ case no
236
+ when Reference
237
+ target = no
238
+ when ::Integer
239
+ target = Reference.new(no, generation)
240
+ when Origami::Object
241
+ return no
242
+ end
243
+
244
+ @revisions.first.body[target]
245
+ end
246
+
247
+ def indirect_objects
248
+ @revisions.inject([]) do |set, rev| set.concat(rev.objects) end
249
+ end
250
+ alias root_objects indirect_objects
251
+
252
+ def cast_object(reference, type, parser = nil) #:nodoc:
253
+ @revisions.each do |rev|
254
+ if rev.body.include?(reference) and type < rev.body[reference].class
255
+ rev.body[reference] = rev.body[reference].cast_to(type, parser)
256
+
257
+ rev.body[reference]
258
+ else
259
+ nil
260
+ end
261
+ end
262
+ end
263
+
264
+ def Catalog
265
+ get_object(@revisions.first.trailer.Root)
266
+ end
267
+
268
+ def save(path)
269
+ bin = "".b
270
+ bin << @header.to_s
271
+
272
+ lastno, brange = 0, 0
273
+
274
+ xrefs = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
275
+ xrefsection = XRef::Section.new
276
+
277
+ @revisions.first.body.values.sort.each { |obj|
278
+ if (obj.no - lastno).abs > 1
279
+ xrefsection << XRef::Subsection.new(brange, xrefs)
280
+ brange = obj.no
281
+ xrefs.clear
282
+ end
283
+
284
+ xrefs << XRef.new(bin.size, obj.generation, XRef::USED)
285
+ lastno = obj.no
286
+
287
+ obj.pre_build
288
+
289
+ bin << obj.to_s
290
+
291
+ obj.post_build
292
+ }
293
+
294
+ xrefsection << XRef::Subsection.new(brange, xrefs)
295
+
296
+ @xreftable = xrefsection
297
+ @trailer ||= Trailer.new
298
+ @trailer.Size = @revisions.first.body.size + 1
299
+ @trailer.startxref = bin.size
300
+
301
+ bin << @xreftable.to_s
302
+ bin << @trailer.to_s
303
+
304
+ if path.respond_to?(:write)
305
+ io = path
306
+ else
307
+ path = File.expand_path(path)
308
+ io = File.open(path, "wb", encoding: 'binary')
309
+ close = true
310
+ end
311
+
312
+ begin
313
+ io.write(bin)
314
+ ensure
315
+ io.close if close
316
+ end
317
+
318
+ self
319
+ end
320
+
321
+ private
322
+
323
+ def init
324
+ catalog = Catalog.new(:FDF => FDF::Dictionary.new)
325
+
326
+ @revisions.first.trailer.Root = self.insert(catalog)
327
+ end
328
+
329
+ def get_object_offset(no,generation) #:nodoc:
330
+ bodyoffset = @header.to_s.size
331
+ objectoffset = bodyoffset
332
+
333
+ @revisions.first.body.values.each { |object|
334
+ if object.no == no and object.generation == generation then return objectoffset
335
+ else
336
+ objectoffset += object.to_s.size
337
+ end
338
+ }
339
+
340
+ nil
341
+ end
342
+
252
343
  end
253
-
254
- end
255
-
256
344
  end
257
-