origami 1.0.4 → 1.1.1

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.
@@ -41,19 +41,30 @@ module Origami
41
41
  DEFAULT_LINEWIDTH = 1.0
42
42
 
43
43
  attr_reader :instructions
44
+ attr_accessor :canvas
44
45
 
45
46
  def initialize(rawdata = "", dictionary = {})
46
47
 
47
48
  @instructions = nil
48
- @gs = Graphics::State.new
49
+ @canvas = Graphics::DummyCanvas.new
49
50
 
50
51
  super(rawdata, dictionary)
51
52
  end
52
53
 
54
+ def render(engine)
55
+ load! if @instructions.nil?
56
+
57
+ @instructions.each do |instruction|
58
+ instruction.render(engine)
59
+ end
60
+
61
+ nil
62
+ end
63
+
53
64
  def pre_build #:nodoc:
54
65
  load! if @instructions.nil?
55
- if @gs.text_state.is_in_text_object?
56
- @instructions << PDF::Instruction.new('ET').update_state(@gs)
66
+ if @canvas.gs.text_state.is_in_text_object?
67
+ @instructions << PDF::Instruction.new('ET').render(@canvas)
57
68
  end
58
69
 
59
70
  @data = @instructions.join
@@ -99,16 +110,16 @@ module Origami
99
110
  set_line_join(line_join)
100
111
  set_dash_pattern(dash_pattern)
101
112
 
102
- if @gs.text_state.is_in_text_object?
103
- @instructions << PDF::Instruction.new('ET').update_state(@gs)
113
+ if @canvas.gs.text_state.is_in_text_object?
114
+ @instructions << PDF::Instruction.new('ET').render(@canvas)
104
115
  end
105
116
 
106
117
  unless coords.size < 1
107
118
  x,y = coords.slice!(0)
108
- @instructions << PDF::Instruction.new('m',x,y).update_state(@gs)
119
+ @instructions << PDF::Instruction.new('m',x,y).render(@canvas)
109
120
 
110
121
  coords.each do |px,py|
111
- @instructions << PDF::Instruction.new('l',px,py).update_state(@gs)
122
+ @instructions << PDF::Instruction.new('l',px,py).render(@canvas)
112
123
  end
113
124
 
114
125
  @instructions << (i =
@@ -121,7 +132,7 @@ module Origami
121
132
  end
122
133
  )
123
134
 
124
- i.update_state(@gs)
135
+ i.render(@canvas)
125
136
  end
126
137
 
127
138
  self
@@ -152,11 +163,11 @@ module Origami
152
163
  set_line_join(line_join)
153
164
  set_dash_pattern(dash_pattern)
154
165
 
155
- if @gs.text_state.is_in_text_object?
156
- @instructions << PDF::Instruction.new('ET').update_state(@gs)
166
+ if @canvas.gs.text_state.is_in_text_object?
167
+ @instructions << PDF::Instruction.new('ET').render(@canvas)
157
168
  end
158
169
 
159
- @instructions << PDF::Instruction.new('re', x,y,width,height).update_state(@gs)
170
+ @instructions << PDF::Instruction.new('re', x,y,width,height).render(@canvas)
160
171
 
161
172
  @instructions << (i =
162
173
  if stroke and not fill
@@ -168,7 +179,7 @@ module Origami
168
179
  end
169
180
  )
170
181
 
171
- i.update_state(@gs)
182
+ i.render(@canvas)
172
183
 
173
184
  self
174
185
  end
@@ -194,10 +205,10 @@ module Origami
194
205
  rise = attr[:rise]
195
206
  rendering = attr[:rendering]
196
207
 
197
- @instructions << PDF::Instruction.new('ET').update_state(@gs) if (x or y) and @gs.text_state.is_in_text_object?
208
+ @instructions << PDF::Instruction.new('ET').render(@canvas) if (x or y) and @canvas.gs.text_state.is_in_text_object?
198
209
 
199
- unless @gs.text_state.is_in_text_object?
200
- @instructions << PDF::Instruction.new('BT').update_state(@gs)
210
+ unless @canvas.gs.text_state.is_in_text_object?
211
+ @instructions << PDF::Instruction.new('BT').render(@canvas)
201
212
  end
202
213
 
203
214
  set_text_font(font, size)
@@ -219,15 +230,15 @@ module Origami
219
230
 
220
231
  def paint_shading(shade)
221
232
  load! if @instructions.nil?
222
- @instructions << PDF::Instruction.new('sh', shade).update_state(@gs)
233
+ @instructions << PDF::Instruction.new('sh', shade).render(@canvas)
223
234
 
224
235
  self
225
236
  end
226
237
 
227
238
  def set_text_font(fontname, size)
228
239
  load! if @instructions.nil?
229
- if fontname != @gs.text_state.font or size != @gs.text_state.font_size
230
- @instructions << PDF::Instruction.new('Tf', fontname, size).update_state(@gs)
240
+ if fontname != @canvas.gs.text_state.font or size != @canvas.gs.text_state.font_size
241
+ @instructions << PDF::Instruction.new('Tf', fontname, size).render(@canvas)
231
242
  end
232
243
 
233
244
  self
@@ -235,15 +246,15 @@ module Origami
235
246
 
236
247
  def set_text_pos(tx,ty)
237
248
  load! if @instructions.nil?
238
- @instructions << PDF::Instruction.new('Td', tx, ty).update_state(@gs)
249
+ @instructions << PDF::Instruction.new('Td', tx, ty).render(@canvas)
239
250
 
240
251
  self
241
252
  end
242
253
 
243
254
  def set_text_leading(leading)
244
255
  load! if @instructions.nil?
245
- if leading != @gs.text_state.leading
246
- @instructions << PDF::Instruction.new('TL', leading).update_state(@gs)
256
+ if leading != @canvas.gs.text_state.leading
257
+ @instructions << PDF::Instruction.new('TL', leading).render(@canvas)
247
258
  end
248
259
 
249
260
  self
@@ -251,8 +262,8 @@ module Origami
251
262
 
252
263
  def set_text_rendering(rendering)
253
264
  load! if @instructions.nil?
254
- if rendering != @gs.text_state.rendering_mode
255
- @instructions << PDF::Instruction.new('Tr', rendering).update_state(@gs)
265
+ if rendering != @canvas.gs.text_state.rendering_mode
266
+ @instructions << PDF::Instruction.new('Tr', rendering).render(@canvas)
256
267
  end
257
268
 
258
269
  self
@@ -260,8 +271,8 @@ module Origami
260
271
 
261
272
  def set_text_rise(rise)
262
273
  load! if @instructions.nil?
263
- if rise != @gs.text_state.text_rise
264
- @instructions << PDF::Instruction.new('Ts', rise).update_state(@gs)
274
+ if rise != @canvas.gs.text_state.text_rise
275
+ @instructions << PDF::Instruction.new('Ts', rise).render(@canvas)
265
276
  end
266
277
 
267
278
  self
@@ -269,8 +280,8 @@ module Origami
269
280
 
270
281
  def set_text_scale(scaling)
271
282
  load! if @instructions.nil?
272
- if scale != @gs.text_state.scaling
273
- @instructions << PDF::Instruction.new('Tz', scaling).update_state(@gs)
283
+ if scale != @canvas.gs.text_state.scaling
284
+ @instructions << PDF::Instruction.new('Tz', scaling).render(@canvas)
274
285
  end
275
286
 
276
287
  self
@@ -278,8 +289,8 @@ module Origami
278
289
 
279
290
  def set_text_word_spacing(word_spacing)
280
291
  load! if @instructions.nil?
281
- if word_spacing != @gs.text_state.word_spacing
282
- @instructions << PDF::Instruction.new('Tw', word_spacing).update_state(@gs)
292
+ if word_spacing != @canvas.gs.text_state.word_spacing
293
+ @instructions << PDF::Instruction.new('Tw', word_spacing).render(@canvas)
283
294
  end
284
295
 
285
296
  self
@@ -287,8 +298,8 @@ module Origami
287
298
 
288
299
  def set_text_char_spacing(char_spacing)
289
300
  load! if @instructions.nil?
290
- if char_spacing != @gs.text_state.char_spacing
291
- @instructions << PDF::Instruction.new('Tc', char_spacing).update_state(@gs)
301
+ if char_spacing != @canvas.gs.text_state.char_spacing
302
+ @instructions << PDF::Instruction.new('Tc', char_spacing).render(@canvas)
292
303
  end
293
304
 
294
305
  self
@@ -302,25 +313,25 @@ module Origami
302
313
  r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
303
314
  g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
304
315
  b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
305
- PDF::Instruction.new('rg', r, g, b) if @gs.nonstroking_color != [r,g,b]
316
+ PDF::Instruction.new('rg', r, g, b) if @canvas.gs.nonstroking_color != [r,g,b]
306
317
 
307
318
  elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
308
319
  c = (color.respond_to?(:c) ? color.c : color[0]).to_f
309
320
  m = (color.respond_to?(:m) ? color.m : color[1]).to_f
310
321
  y = (color.respond_to?(:y) ? color.y : color[2]).to_f
311
322
  k = (color.respond_to?(:k) ? color.k : color[3]).to_f
312
- PDF::Instruction.new('k', c, m, y, k) if @gs.nonstroking_color != [c,m,y,k]
323
+ PDF::Instruction.new('k', c, m, y, k) if @canvas.gs.nonstroking_color != [c,m,y,k]
313
324
 
314
325
  elsif color.respond_to?:g or (0.0..1.0) === color
315
326
  g = color.respond_to?(:g) ? color.g : color
316
- PDF::Instruction.new('g', g) if @gs.nonstroking_color != [ g ]
327
+ PDF::Instruction.new('g', g) if @canvas.gs.nonstroking_color != [ g ]
317
328
 
318
329
  else
319
330
  raise TypeError, "Invalid color : #{color}"
320
331
  end
321
332
  )
322
333
 
323
- i.update_state(@gs) if i
334
+ i.render(@canvas) if i
324
335
  self
325
336
  end
326
337
 
@@ -332,32 +343,32 @@ module Origami
332
343
  r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
333
344
  g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
334
345
  b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
335
- PDF::Instruction.new('RG', r, g, b) if @gs.stroking_color != [r,g,b]
346
+ PDF::Instruction.new('RG', r, g, b) if @canvas.gs.stroking_color != [r,g,b]
336
347
 
337
348
  elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
338
349
  c = (color.respond_to?(:c) ? color.c : color[0]).to_f
339
350
  m = (color.respond_to?(:m) ? color.m : color[1]).to_f
340
351
  y = (color.respond_to?(:y) ? color.y : color[2]).to_f
341
352
  k = (color.respond_to?(:k) ? color.k : color[3]).to_f
342
- PDF::Instruction.new('K', c, m, y, k) if @gs.stroking_color != [c,m,y,k]
353
+ PDF::Instruction.new('K', c, m, y, k) if @canvas.gs.stroking_color != [c,m,y,k]
343
354
 
344
355
  elsif color.respond_to?:g or (0.0..1.0) === color
345
356
  g = color.respond_to?(:g) ? color.g : color
346
- PDF::Instruction.new('G', g) if @gs.stroking_color != [ g ]
357
+ PDF::Instruction.new('G', g) if @canvas.gs.stroking_color != [ g ]
347
358
 
348
359
  else
349
360
  raise TypeError, "Invalid color : #{color}"
350
361
  end
351
362
  )
352
363
 
353
- i.update_state(@gs) if i
364
+ i.render(@canvas) if i
354
365
  self
355
366
  end
356
367
 
357
368
  def set_dash_pattern(pattern)
358
369
  load! if @instructions.nil?
359
- unless @gs.dash_pattern.eql? pattern
360
- @instructions << PDF::Instruction.new('d', pattern.array, pattern.phase).update_state(@gs)
370
+ unless @canvas.gs.dash_pattern.eql? pattern
371
+ @instructions << PDF::Instruction.new('d', pattern.array, pattern.phase).render(@canvas)
361
372
  end
362
373
 
363
374
  self
@@ -365,8 +376,8 @@ module Origami
365
376
 
366
377
  def set_line_width(width)
367
378
  load! if @instructions.nil?
368
- if @gs.line_width != width
369
- @instructions << PDF::Instruction.new('w', width).update_state(@gs)
379
+ if @canvas.gs.line_width != width
380
+ @instructions << PDF::Instruction.new('w', width).render(@canvas)
370
381
  end
371
382
 
372
383
  self
@@ -374,8 +385,8 @@ module Origami
374
385
 
375
386
  def set_line_cap(cap)
376
387
  load! if @instructions.nil?
377
- if @gs.line_cap != cap
378
- @instructions << PDF::Instruction.new('J', cap).update_state(@gs)
388
+ if @canvas.gs.line_cap != cap
389
+ @instructions << PDF::Instruction.new('J', cap).render(@canvas)
379
390
  end
380
391
 
381
392
  self
@@ -383,8 +394,8 @@ module Origami
383
394
 
384
395
  def set_line_join(join)
385
396
  load! if @instructions.nil?
386
- if @gs.line_join != join
387
- @instructions << PDF::Instruction.new('j', join).update_state(@gs)
397
+ if @canvas.gs.line_join != join
398
+ @instructions << PDF::Instruction.new('j', join).render(@canvas)
388
399
  end
389
400
 
390
401
  self
@@ -397,7 +408,11 @@ module Origami
397
408
 
398
409
  code = StringScanner.new self.data
399
410
  @instructions = []
400
- @instructions << PDF::Instruction.parse(code) until code.eos?
411
+
412
+ until code.eos?
413
+ insn = PDF::Instruction.parse(code)
414
+ @instructions << insn if insn
415
+ end
401
416
 
402
417
  self
403
418
  end
@@ -406,9 +421,9 @@ module Origami
406
421
 
407
422
  lines = text.split("\n").map!{|line| line.to_s}
408
423
 
409
- @instructions << PDF::Instruction.new('Tj', lines.slice!(0)).update_state(@gs)
424
+ @instructions << PDF::Instruction.new('Tj', lines.slice!(0)).render(@canvas)
410
425
  lines.each do |line|
411
- @instructions << PDF::Instruction.new("'", line).update_state(@gs)
426
+ @instructions << PDF::Instruction.new("'", line).render(@canvas)
412
427
  end
413
428
 
414
429
  end
@@ -0,0 +1,135 @@
1
+ =begin
2
+
3
+ = File
4
+ javascript.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ module Origami
27
+
28
+ class PDF
29
+ begin
30
+ require 'johnson'
31
+
32
+ class JavaScriptEngine < Johnson::SpiderMonkey::Runtime
33
+
34
+ class DocumentObject
35
+ attr_reader :author, :creator, :creationDate, :keywords, :modDate, :producer, :subject, :title
36
+
37
+ def initialize(pdf)
38
+ @pdf = pdf
39
+
40
+ @author = @pdf.author || ''
41
+ @creator = @pdf.creator || ''
42
+ @keywords = @pdf.keywords || ''
43
+ @producer = @pdf.producer || ''
44
+ @subject = @pdf.subject || ''
45
+ end
46
+
47
+ def to_s
48
+ "[object Doc]"
49
+ end
50
+ end
51
+
52
+ class ApplicationObject
53
+ DEFAULT_VIEWER_VERSION = 9
54
+
55
+ attr_reader :viewerVersion
56
+ attr_reader :viewerVariation
57
+
58
+ def initialize
59
+ @viewerVersion = DEFAULT_VIEWER_VERSION
60
+ @viewerVariation = @viewerType = "Reader"
61
+ end
62
+
63
+ def to_s
64
+ "[object App]"
65
+ end
66
+ end
67
+
68
+ class ConsoleObject
69
+ def println(msg)
70
+ puts msg.to_s
71
+ end
72
+
73
+ def show; end
74
+ def clear; end
75
+ def hide; end
76
+
77
+ def to_s
78
+ "[object Console]"
79
+ end
80
+ end
81
+
82
+ class UtilObject
83
+ def to_s
84
+ "[object Util]"
85
+ end
86
+ end
87
+
88
+ def initialize(pdf, options = {})
89
+ super()
90
+
91
+ self['app'] = ApplicationObject.new
92
+ if options.has_key?(:viewerVersion)
93
+ self['app'].instance_variable_set :@viewerVersion, options[:viewerVersion]
94
+ end
95
+
96
+ self['console'] = ConsoleObject.new
97
+ self['util'] = UtilObject.new
98
+
99
+ # Johnson includes the Ruby namespace in the global scope.
100
+ # Unsets that for obvious security reasons.
101
+ self['Ruby'] = nil
102
+
103
+ # Hack the 'this' object to point to the DocumentObject
104
+ @doc_obj = self['doc'] = DocumentObject.new(pdf)
105
+ evaluate('this.doc.eval = function(script) {eval(script)}')
106
+ evaluate('this.doc = undefined')
107
+ end
108
+
109
+ def exec(script)
110
+ @doc_obj.eval(script)
111
+ end
112
+
113
+ end
114
+
115
+ rescue LoadError
116
+ end
117
+ end
118
+
119
+ if defined?(PDF::JavaScriptEngine)
120
+ module String
121
+ def eval_as_js(options = {})
122
+ runtime = options[:runtime] || PDF::JavaScriptEngine.new(self.pdf, options)
123
+ runtime.exec(self.value)
124
+ end
125
+ end
126
+
127
+ class Stream
128
+ def eval_as_js(options = {})
129
+ runtime = options[:runtime] || PDF::JavaScriptEngine.new(self.pdf, options)
130
+ runtime.exec(self.data)
131
+ end
132
+ end
133
+ end
134
+
135
+ end
@@ -50,6 +50,15 @@ module Origami
50
50
  get_doc_attr :Info
51
51
  end
52
52
 
53
+ def title; get_document_info_field(:Title) end
54
+ def author; get_document_info_field(:Author) end
55
+ def subject; get_document_info_field(:Subject) end
56
+ def keywords; get_document_info_field(:Keywords) end
57
+ def creator; get_document_info_field(:Creator) end
58
+ def producer; get_document_info_field(:Producer) end
59
+ def creation_date; get_document_info_field(:CreationDate) end
60
+ def mod_date; get_document_info_field(:ModDate) end
61
+
53
62
  #
54
63
  # Returns a Hash of the information found in the metadata stream
55
64
  #
@@ -81,6 +90,21 @@ module Origami
81
90
  end
82
91
  end
83
92
 
93
+ private
94
+
95
+ def get_document_info_field(field) #:nodoc:
96
+ if has_document_info?
97
+ doc_info = get_document_info
98
+
99
+ if doc_info.has_key?(field)
100
+ case obj = get_document_info[field].solve
101
+ when String then return obj.value
102
+ when Stream then return obj.data
103
+ end
104
+ end
105
+ end
106
+ end
107
+
84
108
  end
85
109
 
86
110
  #