eideticpdf 0.9.9
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/fonts/AntiqueOlive-Bold.afm +406 -0
- data/fonts/AntiqueOlive-Bold.inf +27 -0
- data/fonts/AntiqueOlive-Compact.afm +401 -0
- data/fonts/AntiqueOlive-Compact.inf +27 -0
- data/fonts/AntiqueOlive-Italic.afm +401 -0
- data/fonts/AntiqueOlive-Italic.inf +27 -0
- data/fonts/AntiqueOlive-Roman.afm +409 -0
- data/fonts/AntiqueOlive-Roman.inf +27 -0
- data/fonts/AvantGarde-Book.afm +667 -0
- data/fonts/AvantGarde-Book.inf +26 -0
- data/fonts/AvantGarde-BookOblique.afm +667 -0
- data/fonts/AvantGarde-BookOblique.inf +26 -0
- data/fonts/AvantGarde-Demi.afm +673 -0
- data/fonts/AvantGarde-Demi.inf +26 -0
- data/fonts/AvantGarde-DemiOblique.afm +673 -0
- data/fonts/AvantGarde-DemiOblique.inf +26 -0
- data/fonts/Bookman-Demi.afm +669 -0
- data/fonts/Bookman-Demi.inf +25 -0
- data/fonts/Bookman-DemiItalic.afm +669 -0
- data/fonts/Bookman-DemiItalic.inf +25 -0
- data/fonts/Bookman-Light.afm +643 -0
- data/fonts/Bookman-Light.inf +25 -0
- data/fonts/Bookman-LightItalic.afm +620 -0
- data/fonts/Bookman-LightItalic.inf +25 -0
- data/fonts/Clarendon-Bold.afm +412 -0
- data/fonts/Clarendon-Bold.inf +27 -0
- data/fonts/Clarendon-Light.afm +410 -0
- data/fonts/Clarendon-Light.inf +27 -0
- data/fonts/Clarendon.afm +410 -0
- data/fonts/Clarendon.inf +27 -0
- data/fonts/CooperBlack-Italic.afm +421 -0
- data/fonts/CooperBlack-Italic.inf +26 -0
- data/fonts/CooperBlack.afm +427 -0
- data/fonts/CooperBlack.inf +26 -0
- data/fonts/Coronet-Regular.afm +327 -0
- data/fonts/Coronet-Regular.inf +25 -0
- data/fonts/Courier-Bold.afm +342 -0
- data/fonts/Courier-Bold.inf +26 -0
- data/fonts/Courier-BoldOblique.afm +342 -0
- data/fonts/Courier-BoldOblique.inf +26 -0
- data/fonts/Courier-Oblique.afm +342 -0
- data/fonts/Courier-Oblique.inf +26 -0
- data/fonts/Courier.afm +342 -0
- data/fonts/Courier.inf +26 -0
- data/fonts/Eurostile.afm +419 -0
- data/fonts/Eurostile.inf +27 -0
- data/fonts/Goudy-ExtraBold.afm +412 -0
- data/fonts/Goudy-ExtraBold.inf +27 -0
- data/fonts/Helvetica-Bold.afm +2827 -0
- data/fonts/Helvetica-Bold.inf +26 -0
- data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/fonts/Helvetica-BoldOblique.inf +26 -0
- data/fonts/Helvetica-Condensed-Bold.afm +623 -0
- data/fonts/Helvetica-Condensed-Bold.inf +25 -0
- data/fonts/Helvetica-Condensed-BoldObl.afm +623 -0
- data/fonts/Helvetica-Condensed-BoldObl.inf +25 -0
- data/fonts/Helvetica-Condensed-Oblique.afm +528 -0
- data/fonts/Helvetica-Condensed-Oblique.inf +25 -0
- data/fonts/Helvetica-Condensed.afm +528 -0
- data/fonts/Helvetica-Condensed.inf +25 -0
- data/fonts/Helvetica-Narrow-Bold.afm +1439 -0
- data/fonts/Helvetica-Narrow-Bold.inf +26 -0
- data/fonts/Helvetica-Narrow-BoldOblique.afm +1439 -0
- data/fonts/Helvetica-Narrow-BoldOblique.inf +26 -0
- data/fonts/Helvetica-Narrow-Oblique.afm +1556 -0
- data/fonts/Helvetica-Narrow-Oblique.inf +26 -0
- data/fonts/Helvetica-Narrow.afm +1556 -0
- data/fonts/Helvetica-Narrow.inf +26 -0
- data/fonts/Helvetica-Oblique.afm +3051 -0
- data/fonts/Helvetica-Oblique.inf +26 -0
- data/fonts/Helvetica.afm +3051 -0
- data/fonts/Helvetica.inf +26 -0
- data/fonts/LetterGothic-Bold.afm +443 -0
- data/fonts/LetterGothic-Bold.inf +26 -0
- data/fonts/LetterGothic-BoldSlanted.afm +443 -0
- data/fonts/LetterGothic-BoldSlanted.inf +26 -0
- data/fonts/LetterGothic-Slanted.afm +443 -0
- data/fonts/LetterGothic-Slanted.inf +26 -0
- data/fonts/LetterGothic.afm +443 -0
- data/fonts/LetterGothic.inf +26 -0
- data/fonts/LubalinGraph-Book.afm +351 -0
- data/fonts/LubalinGraph-Book.inf +25 -0
- data/fonts/LubalinGraph-BookOblique.afm +351 -0
- data/fonts/LubalinGraph-BookOblique.inf +25 -0
- data/fonts/LubalinGraph-Demi.afm +351 -0
- data/fonts/LubalinGraph-Demi.inf +25 -0
- data/fonts/LubalinGraph-DemiOblique.afm +351 -0
- data/fonts/LubalinGraph-DemiOblique.inf +25 -0
- data/fonts/Marigold.afm +491 -0
- data/fonts/Marigold.inf +27 -0
- data/fonts/MonaLisa-Recut.afm +663 -0
- data/fonts/MonaLisa-Recut.inf +27 -0
- data/fonts/NewCenturySchlbk-Bold.afm +886 -0
- data/fonts/NewCenturySchlbk-Bold.inf +25 -0
- data/fonts/NewCenturySchlbk-BoldItalic.afm +1521 -0
- data/fonts/NewCenturySchlbk-BoldItalic.inf +26 -0
- data/fonts/NewCenturySchlbk-Italic.afm +1113 -0
- data/fonts/NewCenturySchlbk-Italic.inf +26 -0
- data/fonts/NewCenturySchlbk-Roman.afm +1067 -0
- data/fonts/NewCenturySchlbk-Roman.inf +26 -0
- data/fonts/Optima-Bold.afm +515 -0
- data/fonts/Optima-Bold.inf +27 -0
- data/fonts/Optima-BoldItalic.afm +712 -0
- data/fonts/Optima-BoldItalic.inf +26 -0
- data/fonts/Optima-Italic.afm +737 -0
- data/fonts/Optima-Italic.inf +26 -0
- data/fonts/Optima.afm +501 -0
- data/fonts/Optima.inf +27 -0
- data/fonts/Palatino-Bold.afm +675 -0
- data/fonts/Palatino-Bold.inf +26 -0
- data/fonts/Palatino-BoldItalic.afm +702 -0
- data/fonts/Palatino-BoldItalic.inf +26 -0
- data/fonts/Palatino-Italic.afm +700 -0
- data/fonts/Palatino-Italic.inf +26 -0
- data/fonts/Palatino-Roman.afm +718 -0
- data/fonts/Palatino-Roman.inf +26 -0
- data/fonts/StempelGaramond-Bold.afm +412 -0
- data/fonts/StempelGaramond-Bold.inf +27 -0
- data/fonts/StempelGaramond-BoldItalic.afm +413 -0
- data/fonts/StempelGaramond-BoldItalic.inf +27 -0
- data/fonts/StempelGaramond-Italic.afm +413 -0
- data/fonts/StempelGaramond-Italic.inf +27 -0
- data/fonts/StempelGaramond-Roman.afm +412 -0
- data/fonts/StempelGaramond-Roman.inf +27 -0
- data/fonts/Symbol.afm +213 -0
- data/fonts/Symbol.inf +27 -0
- data/fonts/Times-Bold.afm +2588 -0
- data/fonts/Times-Bold.inf +26 -0
- data/fonts/Times-BoldItalic.afm +2384 -0
- data/fonts/Times-BoldItalic.inf +26 -0
- data/fonts/Times-Italic.afm +2667 -0
- data/fonts/Times-Italic.inf +26 -0
- data/fonts/Times-Roman.afm +2419 -0
- data/fonts/Times-Roman.inf +26 -0
- data/fonts/Univers-Bold.afm +712 -0
- data/fonts/Univers-Bold.inf +27 -0
- data/fonts/Univers-BoldOblique.afm +712 -0
- data/fonts/Univers-BoldOblique.inf +27 -0
- data/fonts/Univers-Condensed.afm +403 -0
- data/fonts/Univers-Condensed.inf +27 -0
- data/fonts/Univers-CondensedOblique.afm +403 -0
- data/fonts/Univers-CondensedOblique.inf +27 -0
- data/fonts/Univers-Light.afm +737 -0
- data/fonts/Univers-Light.inf +27 -0
- data/fonts/Univers-LightOblique.afm +737 -0
- data/fonts/Univers-LightOblique.inf +27 -0
- data/fonts/Univers-Oblique.afm +656 -0
- data/fonts/Univers-Oblique.inf +27 -0
- data/fonts/Univers.afm +656 -0
- data/fonts/Univers.inf +27 -0
- data/fonts/ZapfChancery-MediumItalic.afm +842 -0
- data/fonts/ZapfChancery-MediumItalic.inf +26 -0
- data/fonts/ZapfDingbats.afm +225 -0
- data/fonts/ZapfDingbats.inf +26 -0
- data/lib/epdfafm.rb +334 -0
- data/lib/epdfdw.rb +710 -0
- data/lib/epdfk.rb +3142 -0
- data/lib/epdfo.rb +882 -0
- data/lib/epdfpw.rb +1749 -0
- data/lib/epdfs.rb +101 -0
- data/lib/epdfsw.rb +267 -0
- data/lib/epdft.rb +145 -0
- data/lib/epdftt.rb +458 -0
- data/test/pdf_tests.rb +16 -0
- data/test/test.rb +816 -0
- data/test/test_epdfafm.rb +202 -0
- data/test/test_epdfdw.rb +369 -0
- data/test/test_epdfk.rb +47 -0
- data/test/test_epdfo.rb +762 -0
- data/test/test_epdfpw.rb +129 -0
- data/test/test_epdfs.rb +54 -0
- data/test/test_epdfsw.rb +314 -0
- data/test/test_epdft.rb +198 -0
- data/test/test_epdftt.rb +34 -0
- data/test/test_helpers.rb +18 -0
- data/test/testimg.jpg +0 -0
- metadata +247 -0
data/lib/epdfo.rb
ADDED
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: ASCII-8BIT
|
|
3
|
+
#
|
|
4
|
+
# Created by Brent Rowland on 2007-07-13.
|
|
5
|
+
# Copyright (c) 2007, Eidetic Software. All rights reserved.
|
|
6
|
+
|
|
7
|
+
require 'epdfk'
|
|
8
|
+
|
|
9
|
+
module EideticPDF
|
|
10
|
+
module PdfObjects # :nodoc: all
|
|
11
|
+
class Header
|
|
12
|
+
VERSIONS = {
|
|
13
|
+
1.0 => "%PDF-1.0\n".freeze,
|
|
14
|
+
1.1 => "%PDF-1.1\n".freeze,
|
|
15
|
+
1.2 => "%PDF-1.2\n".freeze,
|
|
16
|
+
1.3 => "%PDF-1.3\n".freeze,
|
|
17
|
+
1.4 => "%PDF-1.4\n".freeze
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def initialize(version=1.3)
|
|
21
|
+
@version = version
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
VERSIONS[@version].dup
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class IndirectObjectRef
|
|
30
|
+
attr_reader :indirect_object
|
|
31
|
+
|
|
32
|
+
def initialize(indirect_object)
|
|
33
|
+
@indirect_object = indirect_object
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_s
|
|
37
|
+
@indirect_object.reference_string
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def eql?(other)
|
|
41
|
+
self.indirect_object.eql?(other.indirect_object)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def ==(other)
|
|
45
|
+
other.respond_to?(:indirect_object) && self.indirect_object == other.indirect_object
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class IndirectObject
|
|
50
|
+
attr_reader :seq, :gen
|
|
51
|
+
|
|
52
|
+
def initialize(seq, gen, obj=nil)
|
|
53
|
+
@seq, @gen, @obj = seq, gen, obj
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def header
|
|
57
|
+
"#{@seq} #{@gen} obj\n"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def body
|
|
61
|
+
@obj ? "#{@obj}\n" : ''
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def footer
|
|
65
|
+
"endobj\n"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_s
|
|
69
|
+
header + body + footer
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def reference_string
|
|
73
|
+
"#{@seq} #{@gen} R "
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def reference_object
|
|
77
|
+
IndirectObjectRef.new(self)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# direct objects
|
|
82
|
+
class PdfBoolean
|
|
83
|
+
attr_reader :value
|
|
84
|
+
|
|
85
|
+
def initialize(value)
|
|
86
|
+
@value = value
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_s
|
|
90
|
+
value ? 'true ' : 'false '
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ==(other)
|
|
94
|
+
other.respond_to?(:value) && self.value == other.value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class PdfNumber
|
|
99
|
+
attr_reader :value
|
|
100
|
+
|
|
101
|
+
def initialize(value)
|
|
102
|
+
@value = value
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def to_s
|
|
106
|
+
"#{value} "
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def eql?(other)
|
|
110
|
+
self.value.eql?(other.value)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def ==(other)
|
|
114
|
+
other.respond_to?(:value) && self.value == other.value
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class PdfInteger < PdfNumber
|
|
119
|
+
def initialize(value)
|
|
120
|
+
@value = value.to_i
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.ary(int_ary)
|
|
124
|
+
p_ary = int_ary.map do |i|
|
|
125
|
+
if i.respond_to?(:to_i)
|
|
126
|
+
PdfInteger.new(i.to_i)
|
|
127
|
+
elsif i.respond_to?(:to_ary)
|
|
128
|
+
PdfInteger.ary(i.to_ary)
|
|
129
|
+
else
|
|
130
|
+
i
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
PdfArray.new p_ary
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class PdfReal < PdfNumber
|
|
138
|
+
def initialize(value)
|
|
139
|
+
@value = value.to_f
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.ary(float_ary)
|
|
143
|
+
p_ary = float_ary.map do |f|
|
|
144
|
+
if f.respond_to?(:to_f)
|
|
145
|
+
PdfReal.new(f.to_f)
|
|
146
|
+
elsif f.respond_to?(:to_ary)
|
|
147
|
+
PdfReal.ary(f.to_ary)
|
|
148
|
+
else
|
|
149
|
+
f
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
PdfArray.new p_ary
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# text written between ()'s with '(', ')', and '\' escaped with '\'
|
|
157
|
+
class PdfString
|
|
158
|
+
attr_reader :value
|
|
159
|
+
|
|
160
|
+
def initialize(value)
|
|
161
|
+
@value = value.to_s
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def eql?(other)
|
|
165
|
+
self.value.eql?(other.value)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def ==(other)
|
|
169
|
+
other.respond_to?(:value) && self.value == other.value
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def to_s
|
|
173
|
+
"(#{PdfString.escape(value)}) "
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def self.escape(string)
|
|
177
|
+
string.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)')
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# TODO: What kind of changes are needed here?
|
|
181
|
+
def self.escape_wide(string)
|
|
182
|
+
string.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)')
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def self.ary(string_ary)
|
|
186
|
+
p_ary = string_ary.map do |s|
|
|
187
|
+
if s.respond_to?(:to_str)
|
|
188
|
+
PdfString.new(s.to_str)
|
|
189
|
+
elsif s.respond_to?(:to_ary)
|
|
190
|
+
PdfString.ary(s.to_ary)
|
|
191
|
+
else
|
|
192
|
+
s
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
PdfArray.new p_ary
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# name of PDF entity, written as /Name
|
|
200
|
+
class PdfName
|
|
201
|
+
attr_reader :name
|
|
202
|
+
|
|
203
|
+
def initialize(name)
|
|
204
|
+
@name = name.to_s
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def to_s
|
|
208
|
+
"/#{name} "
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def hash
|
|
212
|
+
self.to_s.hash
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def eql?(other)
|
|
216
|
+
self.name.eql?(other.name)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def ==(other)
|
|
220
|
+
other.respond_to?(:name) && self.name == other.name
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def self.ary(string_ary)
|
|
224
|
+
p_ary = string_ary.map do |s|
|
|
225
|
+
if s.respond_to?(:to_str)
|
|
226
|
+
PdfName.new(s.to_str)
|
|
227
|
+
elsif s.respond_to?(:to_ary)
|
|
228
|
+
PdfName.ary(s.to_ary)
|
|
229
|
+
else
|
|
230
|
+
s
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
PdfArray.new p_ary
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
class PdfArray
|
|
238
|
+
include Enumerable
|
|
239
|
+
|
|
240
|
+
attr_reader :values
|
|
241
|
+
attr_accessor :wrap
|
|
242
|
+
|
|
243
|
+
def initialize(values=[], wrap=0)
|
|
244
|
+
@values, @wrap = values, wrap
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def to_s
|
|
248
|
+
"[#{wrapped_values}] "
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def each
|
|
252
|
+
if wrap.zero?
|
|
253
|
+
yield values
|
|
254
|
+
else
|
|
255
|
+
0.step(values.size, wrap) { |i| yield values[i, wrap] }
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def eql?(other)
|
|
260
|
+
self.values.eql?(other.values) && self.wrap.eql?(other.wrap)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def ==(other)
|
|
264
|
+
(other.respond_to?(:values) && self.values == other.values) &&
|
|
265
|
+
(other.respond_to?(:wrap) && self.wrap == other.wrap)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
private
|
|
269
|
+
def wrapped_values
|
|
270
|
+
# return values.join if @wrap.nil? or @wrap.zero?
|
|
271
|
+
self.map { |segment| segment.join }.join("\n")
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
class PdfDictionary
|
|
276
|
+
def initialize(hash={})
|
|
277
|
+
@hash = {}
|
|
278
|
+
update(hash)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def [](key)
|
|
282
|
+
@hash[name_from_key(key)]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def []=(key, value)
|
|
286
|
+
@hash[name_from_key(key)] = value
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def update(other)
|
|
290
|
+
other.each_pair { |key, value| self[key] = value }
|
|
291
|
+
self
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def to_s
|
|
295
|
+
s = "<<\n"
|
|
296
|
+
# Sort the results consistently to be test-friendly.
|
|
297
|
+
s << @hash.keys.sort { |a,b| a.to_s <=> b.to_s }.map { |key| "#{key}#{@hash[key]}\n" }.join
|
|
298
|
+
s << ">>\n"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
private
|
|
302
|
+
def name_from_key(key)
|
|
303
|
+
key.is_a?(PdfName) ? key : PdfName.new(key)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
class PdfDictionaryObject < IndirectObject
|
|
308
|
+
def initialize(seq, gen, hash={})
|
|
309
|
+
super(seq, gen, PdfDictionary.new(hash))
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def dictionary
|
|
313
|
+
@obj
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def body
|
|
317
|
+
dictionary.to_s
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
class PdfStream < PdfDictionaryObject
|
|
322
|
+
attr_reader :stream
|
|
323
|
+
|
|
324
|
+
def initialize(seq, gen, stream=nil)
|
|
325
|
+
super(seq, gen)
|
|
326
|
+
@stream = (stream || '').dup
|
|
327
|
+
dictionary['Length'] = PdfInteger.new(length)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def length
|
|
331
|
+
stream.length
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def filter=(filter)
|
|
335
|
+
if filter.is_a?(PdfArray)
|
|
336
|
+
dictionary['Filter'] = filter
|
|
337
|
+
else
|
|
338
|
+
dictionary['Filter'] = PdfName.new(filter)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def body
|
|
343
|
+
dictionary['Length'] = PdfInteger.new(length)
|
|
344
|
+
"#{super}stream\n#{stream}endstream\n"
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
class PdfNull
|
|
349
|
+
def to_s
|
|
350
|
+
"null "
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
class InUseXRefEntry
|
|
355
|
+
def initialize(byte_offset, gen)
|
|
356
|
+
@byte_offset, @gen = byte_offset, gen
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def to_s
|
|
360
|
+
"%.10d %.5d n\n" % [@byte_offset, @gen]
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
class FreeXRefEntry < IndirectObject
|
|
365
|
+
attr_reader :seq, :gen
|
|
366
|
+
|
|
367
|
+
def to_s
|
|
368
|
+
"%.10d %.5d f\n" % [seq, gen]
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# sub-section of a cross-reference table
|
|
373
|
+
class XRefSubSection < Array
|
|
374
|
+
def initialize
|
|
375
|
+
self << FreeXRefEntry.new(0,65535)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def to_s
|
|
379
|
+
"#{self.first.seq} #{self.size}\n" << join
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# cross-reference table, allows quick access to any object in body
|
|
384
|
+
class XRefTable < Array
|
|
385
|
+
def size
|
|
386
|
+
self.inject(0) { |size, ary| size + ary.size }
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def to_s
|
|
390
|
+
"xref\n" << self.map { |entry| entry.to_s }.join
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# list of indirect objects
|
|
395
|
+
class Body < Array
|
|
396
|
+
def write_and_xref(s, xref_sub_section)
|
|
397
|
+
self.each do |indirect_object|
|
|
398
|
+
xref_sub_section << InUseXRefEntry.new(s.length, indirect_object.gen)
|
|
399
|
+
s << indirect_object.to_s
|
|
400
|
+
end
|
|
401
|
+
s
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def to_s
|
|
405
|
+
join
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
class Trailer < PdfDictionary
|
|
410
|
+
attr_accessor :xref_table_start
|
|
411
|
+
attr_reader :xref_table_size
|
|
412
|
+
|
|
413
|
+
def xref_table_size=(size)
|
|
414
|
+
@xref_table_size = size
|
|
415
|
+
self['Size'] = PdfInteger.new(size)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def root=(root)
|
|
419
|
+
self['Root'] = root.reference_object
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def to_s
|
|
423
|
+
s = "trailer\n"
|
|
424
|
+
s << super
|
|
425
|
+
s << "startxref\n"
|
|
426
|
+
s << "#{xref_table_start}\n"
|
|
427
|
+
s << "%%EOF\n"
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
class Rectangle < PdfArray
|
|
432
|
+
attr_reader :x1, :y1, :x2, :y2
|
|
433
|
+
|
|
434
|
+
def initialize(x1, y1, x2, y2)
|
|
435
|
+
super([x1, y1, x2, y2].map { |i| PdfInteger.new(i) })
|
|
436
|
+
@x1, @y1, @x2, @y2 = x1, y1, x2, y2
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# defines Type1, TrueType, etc font
|
|
441
|
+
class PdfFont < PdfDictionaryObject
|
|
442
|
+
def encoding=(encoding)
|
|
443
|
+
return if encoding == 'StandardEncoding'
|
|
444
|
+
encoding = PdfName.new(encoding) if encoding.is_a?(String)
|
|
445
|
+
dictionary['Encoding'] = encoding
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def widths=(widths)
|
|
449
|
+
dictionary['Widths'] = widths
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def font_descriptor=(font_descriptor)
|
|
453
|
+
dictionary['FontDescriptor'] = font_descriptor
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def initialize(seq, gen, sub_type, base_font, first_char, last_char, widths, font_descriptor)
|
|
457
|
+
super(seq, gen)
|
|
458
|
+
dictionary['Type'] = PdfName.new('Font')
|
|
459
|
+
dictionary['Subtype'] = PdfName.new(sub_type)
|
|
460
|
+
dictionary['BaseFont'] = PdfName.new(base_font)
|
|
461
|
+
dictionary['FirstChar'] = PdfInteger.new(first_char)
|
|
462
|
+
dictionary['LastChar'] = PdfInteger.new(last_char)
|
|
463
|
+
dictionary['Widths'] = widths.reference_object unless widths.nil?
|
|
464
|
+
dictionary['FontDescriptor'] = font_descriptor.reference_object unless font_descriptor.nil?
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def self.standard_encoding?(encoding)
|
|
468
|
+
PdfK::STANDARD_ENCODINGS.include?(encoding)
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
class PdfFontDescriptor < PdfDictionaryObject
|
|
473
|
+
def initialize(seq, gen,
|
|
474
|
+
font_name, flags, font_b_box, missing_width, stem_v, stem_h, italic_angle,
|
|
475
|
+
cap_height, x_height,
|
|
476
|
+
ascent, descent, leading,
|
|
477
|
+
max_width, avg_width)
|
|
478
|
+
super(seq, gen)
|
|
479
|
+
dictionary['Type'] = PdfName.new('FontDescriptor')
|
|
480
|
+
dictionary['FontName'] = PdfName.new(font_name)
|
|
481
|
+
dictionary['Flags'] = PdfInteger.new(flags)
|
|
482
|
+
dictionary['FontBBox'] = PdfArray.new(font_b_box.map { |i| PdfInteger.new(i) })
|
|
483
|
+
dictionary['MissingWidth'] = PdfInteger.new(missing_width)
|
|
484
|
+
dictionary['StemV'] = PdfInteger.new(stem_v) if stem_v
|
|
485
|
+
dictionary['StemH'] = PdfInteger.new(stem_h) if stem_h
|
|
486
|
+
dictionary['ItalicAngle'] = PdfReal.new(italic_angle)
|
|
487
|
+
dictionary['CapHeight'] = PdfInteger.new(cap_height)
|
|
488
|
+
dictionary['XHeight'] = PdfInteger.new(x_height)
|
|
489
|
+
dictionary['Ascent'] = PdfInteger.new(ascent)
|
|
490
|
+
dictionary['Descent'] = PdfInteger.new(descent)
|
|
491
|
+
dictionary['Leading'] = PdfInteger.new(leading)
|
|
492
|
+
dictionary['MaxWidth'] = PdfInteger.new(max_width)
|
|
493
|
+
dictionary['AvgWidth'] = PdfInteger.new(avg_width)
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
class PdfFontEncoding < PdfDictionaryObject
|
|
498
|
+
def initialize(seq, gen, base_encoding, differences)
|
|
499
|
+
super(seq, gen)
|
|
500
|
+
dictionary['Type'] = PdfName.new('Encoding')
|
|
501
|
+
dictionary['BaseEncoding'] = PdfName.new(base_encoding)
|
|
502
|
+
dictionary['Differences'] = differences
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# images and forms
|
|
507
|
+
class PdfXObject < PdfStream
|
|
508
|
+
def initialize(seq, gen, stream=nil)
|
|
509
|
+
super(seq, gen, stream)
|
|
510
|
+
dictionary['Type'] = PdfName.new('XObject')
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
class PdfImage < PdfXObject
|
|
515
|
+
attr_reader :width, :height
|
|
516
|
+
|
|
517
|
+
def initialize(seq, gen, stream=nil)
|
|
518
|
+
super(seq, gen, stream)
|
|
519
|
+
dictionary['Subtype'] = PdfName.new('Image')
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def body
|
|
523
|
+
dictionary['Length'] = PdfInteger.new(stream.length)
|
|
524
|
+
super
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def filter=(filter)
|
|
528
|
+
dictionary['Filter'] = PdfName.new(filter)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def filters=(filters)
|
|
532
|
+
dictionary['Filter'] = filters
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def width=(width)
|
|
536
|
+
@width = width
|
|
537
|
+
dictionary['Width'] = PdfInteger.new(width)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def height=(height)
|
|
541
|
+
@height = height
|
|
542
|
+
dictionary['Height'] = PdfInteger.new(height)
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def bits_per_component=(bits)
|
|
546
|
+
dictionary['BitsPerComponent'] = PdfInteger.new(bits)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def color_space=(color_space)
|
|
550
|
+
if color_space.is_a?(String)
|
|
551
|
+
dictionary['ColorSpace'] = PdfName.new(color_space)
|
|
552
|
+
else
|
|
553
|
+
# array or dictionary
|
|
554
|
+
dictionary['ColorSpace'] = color_space
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def decode=(decode)
|
|
559
|
+
dictionary['Decode'] = decode
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def interpolate=(interpolate)
|
|
563
|
+
dictionary['Interpolate'] = PdfBoolean.new(interpolate)
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def image_mask=(image_mask)
|
|
567
|
+
dictionary['ImageMask'] = PdfBoolean.new(image_mask)
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def intent=(intent)
|
|
571
|
+
dictionary['Intent'] = PdfName.new(intent)
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
class PdfAnnot < PdfDictionaryObject
|
|
576
|
+
def initialize(seq, gen, sub_type, rect)
|
|
577
|
+
super(seq, gen)
|
|
578
|
+
dictionary['Type'] = PdfName.new('Annot')
|
|
579
|
+
dictionary['Subtype'] = PdfName.new(sub_type)
|
|
580
|
+
dictionary['Rect'] = rect
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def border=(border)
|
|
584
|
+
dictionary['Border'] = PdfInteger.ary(border)
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def color=(color)
|
|
588
|
+
dictionary['C'] = PdfReal.ary(color)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def title=(title)
|
|
592
|
+
dictionary['T'] = PdfString.new(title)
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def mod_date=(mod_date)
|
|
596
|
+
dictionary['M'] = PdfString.new(mod_date.strftime("%Y%m%d%H%M%S"))
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def flags=(flags)
|
|
600
|
+
dictionary['F'] = PdfInteger.new(flags)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def highlight=(highlight)
|
|
604
|
+
dictionary['H'] = PdfName.new(highlights[highlight] || highlight)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def border_style=(border_style)
|
|
608
|
+
dictionary['BS'] = PdfDictionary.new(border_style)
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def appearance_dictionary=(appearance)
|
|
612
|
+
dictionary['AP'] = PdfDictionary.new(appearance)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def appearance_state=(state)
|
|
616
|
+
dictionary['AS'] = PdfName.new(state)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
private
|
|
620
|
+
def highlights
|
|
621
|
+
@highlights ||= {
|
|
622
|
+
:none => 'N',
|
|
623
|
+
:invert => 'I',
|
|
624
|
+
:outline => 'O',
|
|
625
|
+
:push => 'P'
|
|
626
|
+
}
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
class PdfTextAnnot < PdfAnnot
|
|
631
|
+
def initialize(seq, gen, rect, contents)
|
|
632
|
+
super(seq, gen, 'Text', rect)
|
|
633
|
+
dictionary['Contents'] = PdfString.new(contents)
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def open=(open)
|
|
637
|
+
dictionary['Open'] = PdfBoolean.new(open)
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
class PdfLinkAnnot < PdfAnnot
|
|
642
|
+
def initialize(seq, gen, rect)
|
|
643
|
+
super(seq, gen, 'Link', rect)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
def dest=(dest)
|
|
647
|
+
value = if dist.is_a?(String)
|
|
648
|
+
PdfName.new(dest)
|
|
649
|
+
elsif dist.is_a?(Array)
|
|
650
|
+
PdfArray.new(dest)
|
|
651
|
+
else
|
|
652
|
+
dest
|
|
653
|
+
end
|
|
654
|
+
dictionary['Dest'] = value
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def action=(action)
|
|
658
|
+
dictionary['A'] = action.is_a?(Hash) ? PdfDictionary.new(action) : action
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
class PdfMovieAnnot < PdfAnnot
|
|
663
|
+
def initialize(seq, gen, rect, movie)
|
|
664
|
+
# movie: Hash
|
|
665
|
+
super(seq, gen, 'Movie', rect)
|
|
666
|
+
dictionary['Movie'] = PdfDictionary.new(movie)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def activation=(activation)
|
|
670
|
+
# activation: Hash or boolean
|
|
671
|
+
dictionary['A'] = activation.is_a?(Hash) ? PdfDictionary.new(activation) : PdfBoolean.new(activation)
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
class PdfSoundAnnot < PdfAnnot
|
|
676
|
+
def initialize(seq, gen, rect, sound)
|
|
677
|
+
# sound: PdfStream
|
|
678
|
+
super(seq, gen, 'Sound', rect)
|
|
679
|
+
dictionary['Sound'] = sound
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
class PdfURIAction < PdfDictionary
|
|
684
|
+
def initialize(uri)
|
|
685
|
+
end
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
class PdfAnnotBorder < PdfDictionary
|
|
689
|
+
def initialize(sub_type)
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
# defines resources used by a page or collection of pages
|
|
694
|
+
class PdfResources < PdfDictionaryObject
|
|
695
|
+
def proc_set=(pdf_object)
|
|
696
|
+
# pdf_object: PdfArray or IndirectObjectRef
|
|
697
|
+
dictionary['ProcSet'] = pdf_object
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
def fonts
|
|
701
|
+
@fonts ||= PdfDictionary.new
|
|
702
|
+
dictionary['Font'] ||= @fonts
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def x_objects
|
|
706
|
+
@x_objects ||= PdfDictionary.new
|
|
707
|
+
dictionary['XObject'] ||= @x_objects
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# common elements between a page and a collection of pages
|
|
712
|
+
class PdfPageBase < PdfDictionaryObject
|
|
713
|
+
def initialize(seq, gen, parent=nil)
|
|
714
|
+
# parent: IndirectObjectRef
|
|
715
|
+
super(seq, gen)
|
|
716
|
+
dictionary['Parent'] = parent.reference_object unless parent.nil?
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def media_box=(media_box)
|
|
720
|
+
# media_box: Rectangle
|
|
721
|
+
dictionary['MediaBox'] = media_box
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def resources=(resources)
|
|
725
|
+
# resources: IndirectObjectRef
|
|
726
|
+
dictionary['Resources'] = resources.reference_object
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def crop_box=(crop_box)
|
|
730
|
+
# crop_box: Rectangle
|
|
731
|
+
dictionary['CropBox'] = crop_box
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def rotate=(rotate)
|
|
735
|
+
# rotate: integer
|
|
736
|
+
dictionary['Rotate'] = PdfInteger.new(rotate)
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def duration=(duration)
|
|
740
|
+
# duration: integer or float
|
|
741
|
+
dictionary['Dur'] = PdfNumber.new(duration)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def hidden=(hidden)
|
|
745
|
+
# hidden: boolean
|
|
746
|
+
dictionary['Hid'] = PdfBoolean.new(hidden)
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def transition=(transition)
|
|
750
|
+
# transition: hash
|
|
751
|
+
dictionary['Trans'] = PdfDictionary.new(transition)
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
def additional_actions=(additional_actions)
|
|
755
|
+
# additional_actions: hash
|
|
756
|
+
dictionary['AA'] = PdfDictionary.new(additional_actions)
|
|
757
|
+
end
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
# one page of a PDF document, not counting resources defined in a parent
|
|
761
|
+
class PdfPage < PdfPageBase
|
|
762
|
+
def initialize(seq, gen, parent)
|
|
763
|
+
super(seq, gen, parent)
|
|
764
|
+
dictionary['Type'] = PdfName.new('Page')
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def body
|
|
768
|
+
if contents.size > 1
|
|
769
|
+
dictionary['Contents'] = PdfArray.new(contents.map { |stream| stream.reference_object })
|
|
770
|
+
elsif contents.size == 1
|
|
771
|
+
dictionary['Contents'] = contents.first.reference_object
|
|
772
|
+
end
|
|
773
|
+
super
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
# PdfStream's
|
|
777
|
+
def contents
|
|
778
|
+
@contents ||= []
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def thumb=(thumb)
|
|
782
|
+
# thumb: stream
|
|
783
|
+
dictionary['Thumb'] = thumb.reference_object
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
def annots=(annots)
|
|
787
|
+
# annots: array of dictionary objects
|
|
788
|
+
(@annots ||= []).concat(annots)
|
|
789
|
+
dictionary['Annots'] = PdfArray.new(@annots.map { |annot| annot.reference_object })
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
def beads=(beads)
|
|
793
|
+
# beads: array of dictionary objects
|
|
794
|
+
dictionary['B'] = PdfArray.new(beads.map { |bead| bead.reference_object })
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
# collection of pages
|
|
799
|
+
class PdfPages < PdfPageBase
|
|
800
|
+
attr_reader :kids # array of refs to PdfPageBase
|
|
801
|
+
|
|
802
|
+
def initialize(seq, gen, parent=nil)
|
|
803
|
+
super(seq, gen, parent)
|
|
804
|
+
@kids = []
|
|
805
|
+
dictionary['Type'] = PdfName.new('Pages')
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
def to_s
|
|
809
|
+
dictionary['Count'] = PdfInteger.new(@kids.size)
|
|
810
|
+
dictionary['Kids'] = PdfArray.new(@kids.map { |page| page.reference_object })
|
|
811
|
+
super
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
class PdfOutlines < PdfDictionaryObject
|
|
816
|
+
def initialize(seq, gen)
|
|
817
|
+
super(seq, gen)
|
|
818
|
+
dictionary['Type'] = PdfName.new('Outlines')
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
def to_s
|
|
822
|
+
dictionary['Count'] = PdfInteger.new(0)
|
|
823
|
+
super
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
# root object of a PDF document, with pointers to other top-level objects
|
|
828
|
+
class PdfCatalog < PdfDictionaryObject
|
|
829
|
+
attr_reader :page_mode, :pages, :outlines
|
|
830
|
+
|
|
831
|
+
def initialize(seq, gen, page_mode=:use_none, pages=nil, outlines=nil)
|
|
832
|
+
super(seq, gen)
|
|
833
|
+
dictionary['Type'] = PdfName.new('Catalog')
|
|
834
|
+
@page_mode = page_mode
|
|
835
|
+
dictionary['PageMode'] = PdfName.new(PAGE_MODES[page_mode])
|
|
836
|
+
if pages
|
|
837
|
+
@pages = pages
|
|
838
|
+
dictionary['Pages'] = pages.reference_object
|
|
839
|
+
end
|
|
840
|
+
if outlines
|
|
841
|
+
@outlines = outlines
|
|
842
|
+
dictionary['Outlines'] = outlines.reference_object
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
def to_s
|
|
847
|
+
super
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
PAGE_MODES = {
|
|
851
|
+
:use_none => 'UseNone',
|
|
852
|
+
:use_outlines => 'UseOutlines',
|
|
853
|
+
:use_thumbs => 'UseThumbs',
|
|
854
|
+
:full_screen => 'FullScreen'
|
|
855
|
+
}.freeze
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
# root of object tree representing a PDF document
|
|
859
|
+
class PdfFile
|
|
860
|
+
attr_reader :header, :body, :trailer
|
|
861
|
+
|
|
862
|
+
def initialize
|
|
863
|
+
@header = Header.new
|
|
864
|
+
@body = Body.new
|
|
865
|
+
@trailer = Trailer.new
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
def to_s
|
|
869
|
+
xref_table = XRefTable.new
|
|
870
|
+
xref_sub_section = XRefSubSection.new
|
|
871
|
+
xref_table << xref_sub_section
|
|
872
|
+
|
|
873
|
+
s = @header.to_s
|
|
874
|
+
@body.write_and_xref(s, xref_sub_section)
|
|
875
|
+
@trailer.xref_table_start = s.length
|
|
876
|
+
@trailer.xref_table_size = xref_sub_section.size
|
|
877
|
+
s << xref_table.to_s
|
|
878
|
+
s << @trailer.to_s
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
end
|