origamindee 3.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +89 -0
- data/COPYING.LESSER +165 -0
- data/README.md +131 -0
- data/bin/config/pdfcop.conf.yml +236 -0
- data/bin/pdf2pdfa +87 -0
- data/bin/pdf2ruby +333 -0
- data/bin/pdfcop +476 -0
- data/bin/pdfdecompress +97 -0
- data/bin/pdfdecrypt +91 -0
- data/bin/pdfencrypt +113 -0
- data/bin/pdfexplode +223 -0
- data/bin/pdfextract +277 -0
- data/bin/pdfmetadata +143 -0
- data/bin/pdfsh +12 -0
- data/bin/shell/console.rb +128 -0
- data/bin/shell/hexdump.rb +59 -0
- data/bin/shell/irbrc +69 -0
- data/examples/README.md +34 -0
- data/examples/attachments/attachment.rb +38 -0
- data/examples/attachments/nested_document.rb +51 -0
- data/examples/encryption/encryption.rb +28 -0
- data/examples/events/events.rb +72 -0
- data/examples/flash/flash.rb +37 -0
- data/examples/flash/helloworld.swf +0 -0
- data/examples/forms/javascript.rb +54 -0
- data/examples/forms/xfa.rb +115 -0
- data/examples/javascript/hello_world.rb +22 -0
- data/examples/javascript/js_emulation.rb +54 -0
- data/examples/loop/goto.rb +32 -0
- data/examples/loop/named.rb +33 -0
- data/examples/signature/signature.rb +65 -0
- data/examples/uri/javascript.rb +56 -0
- data/examples/uri/open-uri.rb +21 -0
- data/examples/uri/submitform.rb +47 -0
- data/lib/origami/3d.rb +364 -0
- data/lib/origami/acroform.rb +321 -0
- data/lib/origami/actions.rb +318 -0
- data/lib/origami/annotations.rb +711 -0
- data/lib/origami/array.rb +242 -0
- data/lib/origami/boolean.rb +90 -0
- data/lib/origami/catalog.rb +418 -0
- data/lib/origami/collections.rb +144 -0
- data/lib/origami/compound.rb +161 -0
- data/lib/origami/destinations.rb +252 -0
- data/lib/origami/dictionary.rb +192 -0
- data/lib/origami/encryption.rb +1084 -0
- data/lib/origami/extensions/fdf.rb +347 -0
- data/lib/origami/extensions/ppklite.rb +422 -0
- data/lib/origami/filespec.rb +197 -0
- data/lib/origami/filters/ascii.rb +211 -0
- data/lib/origami/filters/ccitt/tables.rb +267 -0
- data/lib/origami/filters/ccitt.rb +357 -0
- data/lib/origami/filters/crypt.rb +38 -0
- data/lib/origami/filters/dct.rb +54 -0
- data/lib/origami/filters/flate.rb +69 -0
- data/lib/origami/filters/jbig2.rb +57 -0
- data/lib/origami/filters/jpx.rb +47 -0
- data/lib/origami/filters/lzw.rb +170 -0
- data/lib/origami/filters/predictors.rb +292 -0
- data/lib/origami/filters/runlength.rb +129 -0
- data/lib/origami/filters.rb +364 -0
- data/lib/origami/font.rb +196 -0
- data/lib/origami/functions.rb +79 -0
- data/lib/origami/graphics/colors.rb +230 -0
- data/lib/origami/graphics/instruction.rb +98 -0
- data/lib/origami/graphics/path.rb +182 -0
- data/lib/origami/graphics/patterns.rb +174 -0
- data/lib/origami/graphics/render.rb +62 -0
- data/lib/origami/graphics/state.rb +149 -0
- data/lib/origami/graphics/text.rb +225 -0
- data/lib/origami/graphics/xobject.rb +918 -0
- data/lib/origami/graphics.rb +38 -0
- data/lib/origami/header.rb +75 -0
- data/lib/origami/javascript.rb +713 -0
- data/lib/origami/linearization.rb +330 -0
- data/lib/origami/metadata.rb +172 -0
- data/lib/origami/name.rb +135 -0
- data/lib/origami/null.rb +65 -0
- data/lib/origami/numeric.rb +181 -0
- data/lib/origami/obfuscation.rb +245 -0
- data/lib/origami/object.rb +760 -0
- data/lib/origami/optionalcontent.rb +183 -0
- data/lib/origami/outline.rb +54 -0
- data/lib/origami/outputintents.rb +85 -0
- data/lib/origami/page.rb +722 -0
- data/lib/origami/parser.rb +269 -0
- data/lib/origami/parsers/fdf.rb +56 -0
- data/lib/origami/parsers/pdf/lazy.rb +176 -0
- data/lib/origami/parsers/pdf/linear.rb +122 -0
- data/lib/origami/parsers/pdf.rb +118 -0
- data/lib/origami/parsers/ppklite.rb +57 -0
- data/lib/origami/pdf.rb +1108 -0
- data/lib/origami/reference.rb +134 -0
- data/lib/origami/signature.rb +702 -0
- data/lib/origami/stream.rb +705 -0
- data/lib/origami/string.rb +444 -0
- data/lib/origami/template/patterns.rb +56 -0
- data/lib/origami/template/widgets.rb +151 -0
- data/lib/origami/trailer.rb +190 -0
- data/lib/origami/tree.rb +62 -0
- data/lib/origami/version.rb +23 -0
- data/lib/origami/webcapture.rb +100 -0
- data/lib/origami/xfa/config.rb +453 -0
- data/lib/origami/xfa/connectionset.rb +146 -0
- data/lib/origami/xfa/datasets.rb +49 -0
- data/lib/origami/xfa/localeset.rb +42 -0
- data/lib/origami/xfa/package.rb +59 -0
- data/lib/origami/xfa/pdf.rb +73 -0
- data/lib/origami/xfa/signature.rb +42 -0
- data/lib/origami/xfa/sourceset.rb +43 -0
- data/lib/origami/xfa/stylesheet.rb +44 -0
- data/lib/origami/xfa/template.rb +1691 -0
- data/lib/origami/xfa/xdc.rb +42 -0
- data/lib/origami/xfa/xfa.rb +146 -0
- data/lib/origami/xfa/xfdf.rb +43 -0
- data/lib/origami/xfa/xmpmeta.rb +43 -0
- data/lib/origami/xfa.rb +62 -0
- data/lib/origami/xreftable.rb +557 -0
- data/lib/origami.rb +47 -0
- data/test/dataset/calc.pdf +85 -0
- data/test/dataset/crypto.pdf +36 -0
- data/test/dataset/empty.pdf +49 -0
- data/test/test_actions.rb +27 -0
- data/test/test_annotations.rb +68 -0
- data/test/test_forms.rb +30 -0
- data/test/test_native_types.rb +83 -0
- data/test/test_object_tree.rb +33 -0
- data/test/test_pages.rb +60 -0
- data/test/test_pdf.rb +20 -0
- data/test/test_pdf_attachment.rb +34 -0
- data/test/test_pdf_create.rb +24 -0
- data/test/test_pdf_encrypt.rb +102 -0
- data/test/test_pdf_parse.rb +134 -0
- data/test/test_pdf_parse_lazy.rb +69 -0
- data/test/test_pdf_sign.rb +97 -0
- data/test/test_streams.rb +184 -0
- data/test/test_xrefs.rb +67 -0
- metadata +280 -0
@@ -0,0 +1,713 @@
|
|
1
|
+
=begin
|
2
|
+
|
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/>.
|
18
|
+
|
19
|
+
=end
|
20
|
+
|
21
|
+
module Origami
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'v8'
|
25
|
+
|
26
|
+
class PDF
|
27
|
+
|
28
|
+
module JavaScript
|
29
|
+
module Platforms
|
30
|
+
WINDOWS = "WIN"
|
31
|
+
UNIX = "UNIX"
|
32
|
+
MAC = "MAC"
|
33
|
+
end
|
34
|
+
|
35
|
+
module Viewers
|
36
|
+
ADOBE_READER = "Reader"
|
37
|
+
end
|
38
|
+
|
39
|
+
class Error < Origami::Error; end
|
40
|
+
|
41
|
+
class MissingArgError < Error
|
42
|
+
def initialize; super("Missing required argument.") end
|
43
|
+
end
|
44
|
+
|
45
|
+
class TypeError < Error
|
46
|
+
def initialize; super("Incorrect argument type.") end
|
47
|
+
end
|
48
|
+
|
49
|
+
class InvalidArgsError < Error
|
50
|
+
def initialize; super("Incorrect arguments.") end
|
51
|
+
end
|
52
|
+
|
53
|
+
class NotAllowedError < Error
|
54
|
+
def initialize; super("Security settings prevent access to this property or method.") end
|
55
|
+
end
|
56
|
+
|
57
|
+
class HelpError < Error
|
58
|
+
def initialize; super("Help") end
|
59
|
+
end
|
60
|
+
|
61
|
+
class GeneralError < Error
|
62
|
+
def initialize; super("Operation failed.") end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Arg
|
66
|
+
attr_reader :name, :type, :required, :default
|
67
|
+
|
68
|
+
def initialize(declare = {})
|
69
|
+
@name = declare[:name]
|
70
|
+
@type = declare[:type]
|
71
|
+
@required = declare[:required]
|
72
|
+
@default = declare[:default]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.[](declare = {})
|
76
|
+
self.new(declare)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.inspect(obj)
|
80
|
+
case obj
|
81
|
+
when V8::Function then "function #{obj.name}"
|
82
|
+
when V8::Array then obj.to_a.inspect
|
83
|
+
when V8::Object
|
84
|
+
"{#{obj.to_a.map{|k,v| "#{k}:#{Arg.inspect(v)}"}.join(', ')}}"
|
85
|
+
else
|
86
|
+
obj.inspect
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class AcrobatObject
|
92
|
+
def initialize(engine)
|
93
|
+
@engine = engine
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.check_method_args(args, def_args)
|
97
|
+
if args.first.is_a?(V8::Object)
|
98
|
+
check_method_named_args(args.first, def_args)
|
99
|
+
else
|
100
|
+
check_method_ordered_args(args, def_args)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.check_method_named_args(object, def_args)
|
105
|
+
members = object.entries.map {|k, _| k}
|
106
|
+
argv = []
|
107
|
+
def_args.each do |def_arg|
|
108
|
+
raise MissingArgError if def_arg.required and not members.include?(def_arg.name)
|
109
|
+
|
110
|
+
if members.include?(def_arg.name)
|
111
|
+
arg = object[def_arg.name]
|
112
|
+
raise TypeError if def_arg.type and not arg.is_a?(def_arg.type)
|
113
|
+
else
|
114
|
+
arg = def_arg.default
|
115
|
+
end
|
116
|
+
|
117
|
+
argv.push(arg)
|
118
|
+
end
|
119
|
+
|
120
|
+
argv
|
121
|
+
end
|
122
|
+
private_class_method :check_method_named_args
|
123
|
+
|
124
|
+
def self.check_method_ordered_args(args, def_args)
|
125
|
+
def_args.each_with_index do |def_arg, index|
|
126
|
+
raise MissingArgError if def_arg.required and index >= args.length
|
127
|
+
raise TypeError if def_arg.type and not args[index].is_a?(def_arg.type)
|
128
|
+
|
129
|
+
args.push(def_arg.default) if index >= args.length
|
130
|
+
end
|
131
|
+
|
132
|
+
args
|
133
|
+
end
|
134
|
+
private_class_method :check_method_ordered_args
|
135
|
+
|
136
|
+
def self.acro_method(name, *def_args, &b)
|
137
|
+
define_method(name) do |*args|
|
138
|
+
if @engine.options[:log_method_calls]
|
139
|
+
@engine.options[:console].puts(
|
140
|
+
"LOG: #{self.class}.#{name}(#{args.map{|arg| Arg.inspect(arg)}.join(',')})"
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
args = AcrobatObject.check_method_args(args, def_args)
|
145
|
+
self.instance_exec(*args, &b) if b
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.acro_method_protected(name, *def_args, &b)
|
150
|
+
define_method(name) do |*args|
|
151
|
+
if @engine.options[:log_method_calls]
|
152
|
+
@engine.options[:console].puts(
|
153
|
+
"LOG: #{self.class}.#{name}(#{args.map{|arg| arg.inspect}.join(',')})"
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
unless @engine.privileged?
|
158
|
+
raise NotAllowedError, "Security settings prevent access to this property or method."
|
159
|
+
end
|
160
|
+
|
161
|
+
args = AcrobatObject.check_method_args(args, def_args)
|
162
|
+
self.instance_exec(*args, &b) if b
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_s
|
167
|
+
"[object #{self.class.to_s.split('::').last}]"
|
168
|
+
end
|
169
|
+
alias inspect to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
class AcroTimer < AcrobatObject
|
173
|
+
def initialize(engine, timeout, code, repeat)
|
174
|
+
@thr = Thread.start(engine, timeout, code, repeat) do
|
175
|
+
loop do
|
176
|
+
sleep(timeout / 1000.0)
|
177
|
+
engine.exec(code.to_s)
|
178
|
+
break if not repeat
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class TimeOut < AcroTimer
|
185
|
+
def initialize(engine, timeout, code)
|
186
|
+
super(engine, timeout, code, false)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Interval < AcroTimer
|
191
|
+
def initialize(engine, timeout, code)
|
192
|
+
super(engine, timeout, code, true)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class ReadStream < AcrobatObject
|
197
|
+
def initialize(engine, data)
|
198
|
+
super(engine)
|
199
|
+
|
200
|
+
@data = data
|
201
|
+
end
|
202
|
+
|
203
|
+
acro_method 'read', Arg[name: 'nBytes', type: Numeric, required: true] do |nBytes|
|
204
|
+
@data.slice!(0, nBytes).unpack("H*")[0]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class Acrohelp < AcrobatObject; end
|
209
|
+
|
210
|
+
class Global < AcrobatObject
|
211
|
+
def initialize(engine)
|
212
|
+
super(engine)
|
213
|
+
|
214
|
+
@vars = {}
|
215
|
+
end
|
216
|
+
|
217
|
+
def []=(name, value)
|
218
|
+
@vars[name] ||= {callbacks: []}
|
219
|
+
@vars[name][:value] = value
|
220
|
+
@vars[name][:callbacks].each do |callback|
|
221
|
+
callback.call(value)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def [](name)
|
226
|
+
@vars[name][:value] if @vars.include?(name)
|
227
|
+
end
|
228
|
+
|
229
|
+
acro_method 'setPersistent',
|
230
|
+
Arg[name: 'cVariable', required: true],
|
231
|
+
Arg[name: 'bPersist', required: true] do |cVariable, _bPersist|
|
232
|
+
|
233
|
+
raise GeneralError unless @vars.include?(cVariable)
|
234
|
+
end
|
235
|
+
|
236
|
+
acro_method 'subscribe',
|
237
|
+
Arg[name: 'cVariable', required: true],
|
238
|
+
Arg[name: 'fCallback', type: V8::Function, require: true] do |cVariable, fCallback|
|
239
|
+
|
240
|
+
if @vars.include?(cVariable)
|
241
|
+
@vars[cVariable][:callbacks].push(fCallback)
|
242
|
+
fCallback.call(@vars[cVariable][:value])
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class Doc < AcrobatObject
|
248
|
+
attr_reader :info
|
249
|
+
attr_accessor :disclosed
|
250
|
+
attr_reader :hidden
|
251
|
+
|
252
|
+
attr_reader :app, :acrohelp, :global, :console, :util
|
253
|
+
|
254
|
+
class Info < AcrobatObject
|
255
|
+
def initialize(engine, doc)
|
256
|
+
super(engine)
|
257
|
+
|
258
|
+
@doc = doc
|
259
|
+
end
|
260
|
+
|
261
|
+
def title; @doc.title.to_s end
|
262
|
+
def author; @doc.author.to_s end
|
263
|
+
def subject; @doc.subject.to_s end
|
264
|
+
def keywords; @doc.keywords.to_s end
|
265
|
+
def creator; @doc.creator.to_s end
|
266
|
+
def creationDate; @doc.creation_date.to_s end
|
267
|
+
def modDate; @doc.mod_date.to_s end
|
268
|
+
end
|
269
|
+
|
270
|
+
def initialize(*args)
|
271
|
+
engine, pdf = args # XXX: Bypass therubyracer bug #238. Temporary.
|
272
|
+
super(engine)
|
273
|
+
|
274
|
+
@pdf = pdf
|
275
|
+
@disclosed = false
|
276
|
+
@hidden = false
|
277
|
+
@info = Info.new(@engine, pdf)
|
278
|
+
|
279
|
+
@app = JavaScript::App.new(@engine)
|
280
|
+
@acrohelp = JavaScript::Acrohelp.new(@engine)
|
281
|
+
@global = JavaScript::Global.new(@engine)
|
282
|
+
@console = JavaScript::Console.new(@engine)
|
283
|
+
@util = JavaScript::Util.new(@engine)
|
284
|
+
end
|
285
|
+
|
286
|
+
### PROPERTIES ###
|
287
|
+
|
288
|
+
def numFields
|
289
|
+
fields = @pdf.fields
|
290
|
+
|
291
|
+
fields.size
|
292
|
+
end
|
293
|
+
|
294
|
+
def numPages; @pdf.pages.size end
|
295
|
+
|
296
|
+
def title; @info.title end
|
297
|
+
def author; @info.author end
|
298
|
+
def subject; @info.subject end
|
299
|
+
def keywords; @info.keywords end
|
300
|
+
def creator; @info.creator end
|
301
|
+
def creationDate; @info.creationDate end
|
302
|
+
def modDate; @info.modDate end
|
303
|
+
|
304
|
+
def metadata
|
305
|
+
meta = @pdf.Catalog.Metadata
|
306
|
+
|
307
|
+
(meta.data if meta.is_a?(Stream)).to_s
|
308
|
+
end
|
309
|
+
|
310
|
+
def filesize; @pdf.original_filesize end
|
311
|
+
def path; @pdf.original_filename.to_s end
|
312
|
+
def documentFileName; File.basename(self.path) end
|
313
|
+
def URL; "file://#{self.path}" end
|
314
|
+
def baseURL; '' end
|
315
|
+
|
316
|
+
def dataObjects
|
317
|
+
data_objs = []
|
318
|
+
@pdf.each_attachment do |name, file_desc|
|
319
|
+
if file_desc and file_desc.EF and (f = file_desc.EF.F)
|
320
|
+
data_objs.push Data.new(@engine, name, f.data.size) if f.is_a?(Stream)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
data_objs
|
325
|
+
end
|
326
|
+
|
327
|
+
### METHODS ###
|
328
|
+
|
329
|
+
acro_method 'closeDoc'
|
330
|
+
|
331
|
+
acro_method 'getDataObject',
|
332
|
+
Arg[name: 'cName', type: ::String, required: true] do |cName|
|
333
|
+
|
334
|
+
file_desc = @pdf.resolve_name(Names::EMBEDDED_FILES, cName)
|
335
|
+
|
336
|
+
if file_desc and file_desc.EF and (f = file_desc.EF.F)
|
337
|
+
Data.new(@engine, cName, f.data.size) if f.is_a?(Stream)
|
338
|
+
else
|
339
|
+
raise TypeError
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
acro_method 'getDataObjectContents',
|
344
|
+
Arg[name: 'cName', type: ::String, required: true],
|
345
|
+
Arg[name: 'bAllowAuth', default: false] do |cName, _bAllowAuth|
|
346
|
+
|
347
|
+
file_desc = @pdf.resolve_name(Names::EMBEDDED_FILES, cName)
|
348
|
+
|
349
|
+
if file_desc and file_desc.EF and (f = file_desc.EF.F)
|
350
|
+
ReadStream.new(@engine, f.data) if f.is_a?(Stream)
|
351
|
+
else
|
352
|
+
raise TypeError
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
acro_method 'exportDataObject',
|
357
|
+
Arg[name: 'cName', type: ::String, required: true],
|
358
|
+
Arg[name: 'cDIPath' ],
|
359
|
+
Arg[name: 'bAllowAuth'],
|
360
|
+
Arg[name: 'nLaunch'] do |cName, _cDIPath, _bAllowAuth, _nLaunch|
|
361
|
+
|
362
|
+
file_desc = @pdf.resolve_name(Names::EMBEDDED_FILES, cName)
|
363
|
+
|
364
|
+
if file_desc and file_desc.EF and (f = file_desc.EF.F)
|
365
|
+
else
|
366
|
+
raise TypeError
|
367
|
+
end
|
368
|
+
|
369
|
+
raise TypeError if f.nil?
|
370
|
+
end
|
371
|
+
|
372
|
+
acro_method 'getField',
|
373
|
+
Arg[name: 'cName', type: ::Object, required: true] do |cName|
|
374
|
+
|
375
|
+
field = @pdf.get_field(cName)
|
376
|
+
|
377
|
+
Field.new(@engine, field) if field
|
378
|
+
end
|
379
|
+
|
380
|
+
acro_method 'getNthFieldName',
|
381
|
+
Arg[name: 'nIndex', type: ::Object, required: true] do |nIndex|
|
382
|
+
|
383
|
+
nIndex =
|
384
|
+
case nIndex
|
385
|
+
when false then 0
|
386
|
+
when true then 1
|
387
|
+
else
|
388
|
+
@engine.parseInt.call(nIndex)
|
389
|
+
end
|
390
|
+
|
391
|
+
raise TypeError if (nIndex.is_a?(Float) and nIndex.nan?) or nIndex < 0
|
392
|
+
fields = @pdf.fields
|
393
|
+
|
394
|
+
if fields and nIndex <= fields.size - 1
|
395
|
+
Field.new(@engine, fields.take(nIndex + 1).last).name.to_s
|
396
|
+
else
|
397
|
+
""
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
class App < AcrobatObject
|
403
|
+
|
404
|
+
def platform; @engine.options[:platform] end
|
405
|
+
def viewerType; @engine.options[:viewerType] end
|
406
|
+
def viewerVariation; @engine.options[:viewerVariation] end
|
407
|
+
def viewerVersion; @engine.options[:viewerVersion] end
|
408
|
+
|
409
|
+
def activeDocs; [] end
|
410
|
+
|
411
|
+
### METHODS ###
|
412
|
+
|
413
|
+
acro_method 'setInterval',
|
414
|
+
Arg[name: 'cExpr', required: true],
|
415
|
+
Arg[name: 'nMilliseconds', type: Numeric, required: true] do |cExpr, nMilliseconds|
|
416
|
+
|
417
|
+
Interval.new(@engine, nMilliseconds, cExpr)
|
418
|
+
end
|
419
|
+
|
420
|
+
acro_method 'setTimeOut',
|
421
|
+
Arg[name: 'cExpr', required: true],
|
422
|
+
Arg[name: 'nMilliseconds', type: Numeric, required: true] do |cExpr, nMilliseconds|
|
423
|
+
|
424
|
+
TimeOut.new(@engine, nMilliseconds, cExpr)
|
425
|
+
end
|
426
|
+
|
427
|
+
acro_method 'clearInterval',
|
428
|
+
Arg[name: 'oInterval', type: Interval, required: true] do |oInterval|
|
429
|
+
|
430
|
+
oInterval.instance_variable_get(:@thr).terminate
|
431
|
+
nil
|
432
|
+
end
|
433
|
+
|
434
|
+
acro_method 'clearTimeOut',
|
435
|
+
Arg[name: 'oInterval', type: TimeOut, required: true] do |oInterval|
|
436
|
+
|
437
|
+
oInterval.instance_variable_get(:@thr).terminate
|
438
|
+
nil
|
439
|
+
end
|
440
|
+
|
441
|
+
acro_method_protected 'addMenuItem'
|
442
|
+
acro_method_protected 'addSubMenu'
|
443
|
+
acro_method 'addToolButton'
|
444
|
+
acro_method_protected 'beginPriv'
|
445
|
+
acro_method 'beep'
|
446
|
+
acro_method_protected 'browseForDoc'
|
447
|
+
acro_method_protected 'endPriv'
|
448
|
+
end
|
449
|
+
|
450
|
+
class Console < AcrobatObject
|
451
|
+
def println(*args)
|
452
|
+
raise MissingArgError unless args.length > 0
|
453
|
+
|
454
|
+
@engine.options[:console].puts(args.first.to_s)
|
455
|
+
end
|
456
|
+
|
457
|
+
acro_method 'show'
|
458
|
+
acro_method 'clear'
|
459
|
+
acro_method 'hide'
|
460
|
+
end
|
461
|
+
|
462
|
+
class Util < AcrobatObject
|
463
|
+
acro_method 'streamFromString',
|
464
|
+
Arg[name: 'cString', type: ::Object, required: true],
|
465
|
+
Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |cString, _cCharset|
|
466
|
+
|
467
|
+
ReadStream.new(@engine, cString.to_s)
|
468
|
+
end
|
469
|
+
|
470
|
+
acro_method 'stringFromStream',
|
471
|
+
Arg[name: 'oStream', type: ReadStream, required: true],
|
472
|
+
Arg[name: 'cCharset', type: ::Object, default: 'utf-8'] do |oStream, _cCharset|
|
473
|
+
|
474
|
+
oStream.instance_variable_get(:@data).dup
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
class Field < AcrobatObject
|
479
|
+
def initialize(engine, field)
|
480
|
+
super(engine)
|
481
|
+
|
482
|
+
@field = field
|
483
|
+
end
|
484
|
+
|
485
|
+
def doc; Doc.new(@field.document) end
|
486
|
+
def name
|
487
|
+
(@field.T.value if @field.has_key?(:T)).to_s
|
488
|
+
end
|
489
|
+
|
490
|
+
def value
|
491
|
+
@field.V.value if @field.has_key?(:V)
|
492
|
+
end
|
493
|
+
|
494
|
+
def valueAsString
|
495
|
+
self.value.to_s
|
496
|
+
end
|
497
|
+
|
498
|
+
def type
|
499
|
+
return '' unless @field.key?(:FT)
|
500
|
+
|
501
|
+
type_name =
|
502
|
+
case @field.FT.value
|
503
|
+
when PDF::Field::Type::BUTTON
|
504
|
+
button_type
|
505
|
+
|
506
|
+
when PDF::Field::Type::TEXT then 'text'
|
507
|
+
when PDF::Field::Type::SIGNATURE then 'signature'
|
508
|
+
when PDF::Field::Type::CHOICE
|
509
|
+
choice_type
|
510
|
+
end
|
511
|
+
|
512
|
+
type_name.to_s
|
513
|
+
end
|
514
|
+
|
515
|
+
private
|
516
|
+
|
517
|
+
def button_type
|
518
|
+
return if @field.key?(:Ff) and not @field.Ff.is_a?(Integer)
|
519
|
+
|
520
|
+
flags = @field.Ff.to_i
|
521
|
+
|
522
|
+
if (flags & Annotation::Widget::Button::Flags::PUSHBUTTON) != 0
|
523
|
+
'button'
|
524
|
+
elsif (flags & Annotation::Widget::Button::Flags::RADIO) != 0
|
525
|
+
'radiobox'
|
526
|
+
else
|
527
|
+
'checkbox'
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
def choice_type
|
532
|
+
return if @field.key?(:Ff) and not @field.Ff.is_a?(Integer)
|
533
|
+
|
534
|
+
if (@field.Ff.to_i & Annotation::Widget::Choice::Flags::COMBO) != 0
|
535
|
+
'combobox'
|
536
|
+
else
|
537
|
+
'listbox'
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
class Data < AcrobatObject
|
543
|
+
attr_reader :name, :path, :size
|
544
|
+
attr_reader :creationDate, :modDate
|
545
|
+
attr_reader :description, :MIMEType
|
546
|
+
|
547
|
+
def initialize(engine, name, size, **metadata)
|
548
|
+
super(engine)
|
549
|
+
|
550
|
+
@name, @size = name, size
|
551
|
+
|
552
|
+
@path, @creationDate, @modDate,
|
553
|
+
@description, @MIMEType = metadata.values_at(:path, :creationDate, :modDate, :description, :MIMEType)
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
class JavaScript::EngineError < Origami::Error; end
|
559
|
+
|
560
|
+
class JavaScript::Engine
|
561
|
+
attr_reader :doc
|
562
|
+
attr_reader :context
|
563
|
+
attr_reader :options
|
564
|
+
attr_reader :privileged_mode
|
565
|
+
attr_reader :parseInt
|
566
|
+
|
567
|
+
def initialize(pdf)
|
568
|
+
@options =
|
569
|
+
{
|
570
|
+
formsVersion: 11.008,
|
571
|
+
viewerVersion: 11.008,
|
572
|
+
viewerType: JavaScript::Viewers::ADOBE_READER,
|
573
|
+
viewerVariation: JavaScript::Viewers::ADOBE_READER,
|
574
|
+
platform: JavaScript::Platforms::WINDOWS,
|
575
|
+
console: STDOUT,
|
576
|
+
log_method_calls: false,
|
577
|
+
privileged_mode: false
|
578
|
+
}
|
579
|
+
|
580
|
+
@doc = JavaScript::Doc.new(self, pdf)
|
581
|
+
@context = V8::Context.new(with: @doc)
|
582
|
+
@privileged_mode = @options[:privileged_mode]
|
583
|
+
|
584
|
+
@parseInt = V8::Context.new['parseInt']
|
585
|
+
@hooks = {}
|
586
|
+
end
|
587
|
+
|
588
|
+
#
|
589
|
+
# Returns true if the engine is set to execute in privileged mode.
|
590
|
+
# Allows execution of security protected methods.
|
591
|
+
#
|
592
|
+
def privileged?
|
593
|
+
@privileged_mode
|
594
|
+
end
|
595
|
+
|
596
|
+
#
|
597
|
+
# Evaluates a JavaScript code in the current context.
|
598
|
+
#
|
599
|
+
def exec(script)
|
600
|
+
@context.eval(script)
|
601
|
+
end
|
602
|
+
|
603
|
+
#
|
604
|
+
# Set a hook on a JavaScript method.
|
605
|
+
#
|
606
|
+
def hook(name, &callback)
|
607
|
+
ns = name.split('.')
|
608
|
+
previous = @context
|
609
|
+
|
610
|
+
ns.each do |n|
|
611
|
+
raise JavaScript::EngineError, "#{name} does not exist" if previous.nil?
|
612
|
+
previous = previous[n]
|
613
|
+
end
|
614
|
+
|
615
|
+
case previous
|
616
|
+
when V8::Function, UnboundMethod, nil then
|
617
|
+
@context[name] = lambda do |*args|
|
618
|
+
callback[previous, *args]
|
619
|
+
end
|
620
|
+
|
621
|
+
@hooks[name] = [previous, callback]
|
622
|
+
else
|
623
|
+
raise JavaScript::EngineError, "#{name} is not a function"
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
#
|
628
|
+
# Removes an existing hook on a JavaScript method.
|
629
|
+
#
|
630
|
+
def unhook(name)
|
631
|
+
@context[name] = @hooks[name][0] if @hooks.has_key?(name)
|
632
|
+
end
|
633
|
+
|
634
|
+
#
|
635
|
+
# Returns an Hash of all defined members in specified object name.
|
636
|
+
#
|
637
|
+
def members(obj)
|
638
|
+
members = {}
|
639
|
+
list = @context.eval <<-JS
|
640
|
+
(function(base) {
|
641
|
+
var members = [];
|
642
|
+
for (var i in base) members.push([i, base[i]]);
|
643
|
+
return members;
|
644
|
+
})(#{obj})
|
645
|
+
JS
|
646
|
+
|
647
|
+
list.each do |var|
|
648
|
+
members[var[0]] = var[1]
|
649
|
+
end
|
650
|
+
|
651
|
+
members
|
652
|
+
end
|
653
|
+
|
654
|
+
#
|
655
|
+
# Returns all members in the global scope.
|
656
|
+
#
|
657
|
+
def scope
|
658
|
+
members('this')
|
659
|
+
end
|
660
|
+
|
661
|
+
#
|
662
|
+
# Binds the V8 remote debugging agent on the specified TCP _port_.
|
663
|
+
#
|
664
|
+
def enable_debugger(port = 5858)
|
665
|
+
V8::C::Debug.EnableAgent("Origami", port)
|
666
|
+
end
|
667
|
+
|
668
|
+
def debugger_break
|
669
|
+
exec 'debugger'
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
module String
|
675
|
+
#
|
676
|
+
# Evaluates the current String as JavaScript.
|
677
|
+
#
|
678
|
+
def eval_js
|
679
|
+
self.document.eval_js(self.value)
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
class Stream
|
684
|
+
#
|
685
|
+
# Evaluates the current Stream as JavaScript.
|
686
|
+
#
|
687
|
+
def eval_js
|
688
|
+
self.document.eval_js(self.data)
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
class PDF
|
693
|
+
#
|
694
|
+
# Executes a JavaScript script in the current document context.
|
695
|
+
#
|
696
|
+
def eval_js(code)
|
697
|
+
js_engine.exec(code)
|
698
|
+
end
|
699
|
+
|
700
|
+
#
|
701
|
+
# Returns the JavaScript engine (if JavaScript support is present).
|
702
|
+
#
|
703
|
+
def js_engine
|
704
|
+
@js_engine ||= PDF::JavaScript::Engine.new(self)
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
rescue LoadError
|
709
|
+
#
|
710
|
+
# V8 unavailable.
|
711
|
+
#
|
712
|
+
end
|
713
|
+
end
|