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.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +89 -0
  3. data/COPYING.LESSER +165 -0
  4. data/README.md +131 -0
  5. data/bin/config/pdfcop.conf.yml +236 -0
  6. data/bin/pdf2pdfa +87 -0
  7. data/bin/pdf2ruby +333 -0
  8. data/bin/pdfcop +476 -0
  9. data/bin/pdfdecompress +97 -0
  10. data/bin/pdfdecrypt +91 -0
  11. data/bin/pdfencrypt +113 -0
  12. data/bin/pdfexplode +223 -0
  13. data/bin/pdfextract +277 -0
  14. data/bin/pdfmetadata +143 -0
  15. data/bin/pdfsh +12 -0
  16. data/bin/shell/console.rb +128 -0
  17. data/bin/shell/hexdump.rb +59 -0
  18. data/bin/shell/irbrc +69 -0
  19. data/examples/README.md +34 -0
  20. data/examples/attachments/attachment.rb +38 -0
  21. data/examples/attachments/nested_document.rb +51 -0
  22. data/examples/encryption/encryption.rb +28 -0
  23. data/examples/events/events.rb +72 -0
  24. data/examples/flash/flash.rb +37 -0
  25. data/examples/flash/helloworld.swf +0 -0
  26. data/examples/forms/javascript.rb +54 -0
  27. data/examples/forms/xfa.rb +115 -0
  28. data/examples/javascript/hello_world.rb +22 -0
  29. data/examples/javascript/js_emulation.rb +54 -0
  30. data/examples/loop/goto.rb +32 -0
  31. data/examples/loop/named.rb +33 -0
  32. data/examples/signature/signature.rb +65 -0
  33. data/examples/uri/javascript.rb +56 -0
  34. data/examples/uri/open-uri.rb +21 -0
  35. data/examples/uri/submitform.rb +47 -0
  36. data/lib/origami/3d.rb +364 -0
  37. data/lib/origami/acroform.rb +321 -0
  38. data/lib/origami/actions.rb +318 -0
  39. data/lib/origami/annotations.rb +711 -0
  40. data/lib/origami/array.rb +242 -0
  41. data/lib/origami/boolean.rb +90 -0
  42. data/lib/origami/catalog.rb +418 -0
  43. data/lib/origami/collections.rb +144 -0
  44. data/lib/origami/compound.rb +161 -0
  45. data/lib/origami/destinations.rb +252 -0
  46. data/lib/origami/dictionary.rb +192 -0
  47. data/lib/origami/encryption.rb +1084 -0
  48. data/lib/origami/extensions/fdf.rb +347 -0
  49. data/lib/origami/extensions/ppklite.rb +422 -0
  50. data/lib/origami/filespec.rb +197 -0
  51. data/lib/origami/filters/ascii.rb +211 -0
  52. data/lib/origami/filters/ccitt/tables.rb +267 -0
  53. data/lib/origami/filters/ccitt.rb +357 -0
  54. data/lib/origami/filters/crypt.rb +38 -0
  55. data/lib/origami/filters/dct.rb +54 -0
  56. data/lib/origami/filters/flate.rb +69 -0
  57. data/lib/origami/filters/jbig2.rb +57 -0
  58. data/lib/origami/filters/jpx.rb +47 -0
  59. data/lib/origami/filters/lzw.rb +170 -0
  60. data/lib/origami/filters/predictors.rb +292 -0
  61. data/lib/origami/filters/runlength.rb +129 -0
  62. data/lib/origami/filters.rb +364 -0
  63. data/lib/origami/font.rb +196 -0
  64. data/lib/origami/functions.rb +79 -0
  65. data/lib/origami/graphics/colors.rb +230 -0
  66. data/lib/origami/graphics/instruction.rb +98 -0
  67. data/lib/origami/graphics/path.rb +182 -0
  68. data/lib/origami/graphics/patterns.rb +174 -0
  69. data/lib/origami/graphics/render.rb +62 -0
  70. data/lib/origami/graphics/state.rb +149 -0
  71. data/lib/origami/graphics/text.rb +225 -0
  72. data/lib/origami/graphics/xobject.rb +918 -0
  73. data/lib/origami/graphics.rb +38 -0
  74. data/lib/origami/header.rb +75 -0
  75. data/lib/origami/javascript.rb +713 -0
  76. data/lib/origami/linearization.rb +330 -0
  77. data/lib/origami/metadata.rb +172 -0
  78. data/lib/origami/name.rb +135 -0
  79. data/lib/origami/null.rb +65 -0
  80. data/lib/origami/numeric.rb +181 -0
  81. data/lib/origami/obfuscation.rb +245 -0
  82. data/lib/origami/object.rb +760 -0
  83. data/lib/origami/optionalcontent.rb +183 -0
  84. data/lib/origami/outline.rb +54 -0
  85. data/lib/origami/outputintents.rb +85 -0
  86. data/lib/origami/page.rb +722 -0
  87. data/lib/origami/parser.rb +269 -0
  88. data/lib/origami/parsers/fdf.rb +56 -0
  89. data/lib/origami/parsers/pdf/lazy.rb +176 -0
  90. data/lib/origami/parsers/pdf/linear.rb +122 -0
  91. data/lib/origami/parsers/pdf.rb +118 -0
  92. data/lib/origami/parsers/ppklite.rb +57 -0
  93. data/lib/origami/pdf.rb +1108 -0
  94. data/lib/origami/reference.rb +134 -0
  95. data/lib/origami/signature.rb +702 -0
  96. data/lib/origami/stream.rb +705 -0
  97. data/lib/origami/string.rb +444 -0
  98. data/lib/origami/template/patterns.rb +56 -0
  99. data/lib/origami/template/widgets.rb +151 -0
  100. data/lib/origami/trailer.rb +190 -0
  101. data/lib/origami/tree.rb +62 -0
  102. data/lib/origami/version.rb +23 -0
  103. data/lib/origami/webcapture.rb +100 -0
  104. data/lib/origami/xfa/config.rb +453 -0
  105. data/lib/origami/xfa/connectionset.rb +146 -0
  106. data/lib/origami/xfa/datasets.rb +49 -0
  107. data/lib/origami/xfa/localeset.rb +42 -0
  108. data/lib/origami/xfa/package.rb +59 -0
  109. data/lib/origami/xfa/pdf.rb +73 -0
  110. data/lib/origami/xfa/signature.rb +42 -0
  111. data/lib/origami/xfa/sourceset.rb +43 -0
  112. data/lib/origami/xfa/stylesheet.rb +44 -0
  113. data/lib/origami/xfa/template.rb +1691 -0
  114. data/lib/origami/xfa/xdc.rb +42 -0
  115. data/lib/origami/xfa/xfa.rb +146 -0
  116. data/lib/origami/xfa/xfdf.rb +43 -0
  117. data/lib/origami/xfa/xmpmeta.rb +43 -0
  118. data/lib/origami/xfa.rb +62 -0
  119. data/lib/origami/xreftable.rb +557 -0
  120. data/lib/origami.rb +47 -0
  121. data/test/dataset/calc.pdf +85 -0
  122. data/test/dataset/crypto.pdf +36 -0
  123. data/test/dataset/empty.pdf +49 -0
  124. data/test/test_actions.rb +27 -0
  125. data/test/test_annotations.rb +68 -0
  126. data/test/test_forms.rb +30 -0
  127. data/test/test_native_types.rb +83 -0
  128. data/test/test_object_tree.rb +33 -0
  129. data/test/test_pages.rb +60 -0
  130. data/test/test_pdf.rb +20 -0
  131. data/test/test_pdf_attachment.rb +34 -0
  132. data/test/test_pdf_create.rb +24 -0
  133. data/test/test_pdf_encrypt.rb +102 -0
  134. data/test/test_pdf_parse.rb +134 -0
  135. data/test/test_pdf_parse_lazy.rb +69 -0
  136. data/test/test_pdf_sign.rb +97 -0
  137. data/test/test_streams.rb +184 -0
  138. data/test/test_xrefs.rb +67 -0
  139. 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