origami 1.0.4 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,4 +30,5 @@ require 'origami/graphics/xobject'
30
30
  require 'origami/graphics/patterns'
31
31
  require 'origami/graphics/text'
32
32
  require 'origami/graphics/state'
33
+ require 'origami/graphics/render'
33
34
 
@@ -24,7 +24,7 @@
24
24
  =end
25
25
 
26
26
  begin
27
- require 'color'
27
+ require 'color'
28
28
  rescue LoadError
29
29
  end
30
30
 
@@ -118,73 +118,73 @@ module Origami
118
118
 
119
119
  class PDF::Instruction
120
120
 
121
- insn 'CS', Name do |gs, cs| gs.stroking_colorspace = cs end
122
- insn 'cs', Name do |gs, cs| gs.nonstroking_colorspace = cs end
123
- insn 'SC', '*' do |gs, *c| gs.stroking_color = c end
124
- insn 'sc', '*' do |gs, *c| gs.nonstroking_color = c end
121
+ insn 'CS', Name do |canvas, cs| canvas.gs.stroking_colorspace = cs end
122
+ insn 'cs', Name do |canvas, cs| canvas.gs.nonstroking_colorspace = cs end
123
+ insn 'SC', '*' do |canvas, *c| canvas.gs.stroking_color = c end
124
+ insn 'sc', '*' do |canvas, *c| canvas.gs.nonstroking_color = c end
125
125
 
126
- insn 'G', Real do |gs, c|
126
+ insn 'G', Real do |canvas, c|
127
127
  unless (0..1).include? c
128
128
  raise Graphics::InvalidColorError,
129
129
  "Not a valid color for DeviceGray: #{c}"
130
130
  end
131
131
 
132
- gs.stroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
133
- gs.stroking_color = [ c ]
132
+ canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
133
+ canvas.gs.stroking_color = [ c ]
134
134
  end
135
135
 
136
- insn 'g', Real do |gs, c|
136
+ insn 'g', Real do |canvas, c|
137
137
  unless (0..1).include? c
138
138
  raise Graphics::InvalidColorError,
139
139
  "Not a valid color for DeviceGray: #{c}"
140
140
  end
141
141
 
142
- gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
143
- gs.nonstroking_color = [ c ]
142
+ canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
143
+ canvas.gs.nonstroking_color = [ c ]
144
144
  end
145
145
 
146
- insn 'RG', Real, Real, Real do |gs, r,g,b|
146
+ insn 'RG', Real, Real, Real do |canvas, r,g,b|
147
147
  c = [ r, g, b ]
148
148
  unless c.all? {|b| (0..1).include? b}
149
149
  raise Graphics::InvalidColorError,
150
150
  "Not a valid color for DeviceRGB: #{c.inspect}"
151
151
  end
152
152
 
153
- gs.stroking_colorspace = Graphics::Color::Space::DEVICE_RGB
154
- gs.stroking_color = c
153
+ canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_RGB
154
+ canvas.gs.stroking_color = c
155
155
  end
156
156
 
157
- insn 'rg', Real, Real, Real do |gs, r,g,b|
157
+ insn 'rg', Real, Real, Real do |canvas, r,g,b|
158
158
  c = [ r, g, b ]
159
159
  unless c.all? {|b| (0..1).include? b}
160
160
  raise Graphics::InvalidColorError,
161
161
  "Not a valid color for DeviceRGB: #{c.inspect}"
162
162
  end
163
163
 
164
- gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_RGB
165
- gs.nonstroking_color = c
164
+ canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_RGB
165
+ canvas.gs.nonstroking_color = c
166
166
  end
167
167
 
168
- insn 'K', Real, Real, Real, Real do |gs, c,m,y,k|
168
+ insn 'K', Real, Real, Real, Real do |canvas, c,m,y,k|
169
169
  c = [ c, m, y, k ]
170
170
  unless c.all? {|b| (0..1).include? b}
171
171
  raise Graphics::InvalidColorError,
172
172
  "Not a valid color for DeviceCMYK: #{c.inspect}"
173
173
  end
174
174
 
175
- gs.stroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
176
- gs.stroking_color = c
175
+ canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
176
+ canvas.gs.stroking_color = c
177
177
  end
178
178
 
179
- insn 'k', Real, Real, Real, Real do |gs, c,m,y,k|
179
+ insn 'k', Real, Real, Real, Real do |canvas, c,m,y,k|
180
180
  c = [ c, m, y, k ]
181
181
  unless c.all? {|b| (0..1).include? b}
182
182
  raise Graphics::InvalidColorError,
183
183
  "Not a valid color for DeviceCMYK: #{c.inspect}"
184
184
  end
185
185
 
186
- gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
187
- gs.nonstroking_color = c
186
+ canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
187
+ canvas.gs.nonstroking_color = c
188
188
  end
189
189
  end
190
190
 
@@ -31,7 +31,7 @@ module Origami
31
31
  attr_accessor :operands
32
32
 
33
33
  @@regexp = Regexp.new('([^ \\t\\r\\n\\0\\[\\]<>()%\\/]+)')
34
- @insns = Hash.new(:operands => [], :proc => lambda{}, :callback => lambda{})
34
+ @insns = Hash.new(:operands => [], :render => lambda{})
35
35
 
36
36
  def initialize(operator, *operands)
37
37
  @operator = operator
@@ -47,17 +47,8 @@ module Origami
47
47
  end
48
48
  end
49
49
 
50
- def update_state(gs)
51
- self.class.get_proc(@operator)[gs, *operands] if self.class.has_proc?(@operator)
52
- self
53
- end
54
-
55
- def render
56
- end
57
-
58
- def exec(gs)
59
- update_state(gs)
60
- self.class.get_callback(@operator)[gs] if self.class.has_callback?(@operator)
50
+ def render(canvas)
51
+ self.class.get_render_proc(@operator)[canvas, *@operands]
61
52
 
62
53
  self
63
54
  end
@@ -67,31 +58,18 @@ module Origami
67
58
  end
68
59
 
69
60
  class << self
70
- def insn(operator, *operands, &p)
61
+ def insn(operator, *operands, &render_proc)
71
62
  @insns[operator] = {}
72
63
  @insns[operator][:operands] = operands
73
- @insns[operator][:proc] = p if block_given?
64
+ @insns[operator][:render] = render_proc || lambda{}
74
65
  end
75
66
 
76
67
  def has_op?(operator)
77
68
  @insns.has_key? operator
78
69
  end
79
70
 
80
- def has_proc?(operator)
81
- self.has_op?(operator) and @insns[operator].has_key?(:proc)
82
- end
83
-
84
- def has_callback?(operator)
85
- self.has_op?(operator) and @insns[operator].has_key?(:callback)
86
- end
87
-
88
- def set_callback(operator, &b)
89
- raise RuntimeError, "Operator `#{operator}' does not exist" unless @insns.has_key?(operator)
90
- @insns[operator][:callback] = b
91
- end
92
-
93
- def get_proc(operator)
94
- @insns[operator][:proc]
71
+ def get_render_proc(operator)
72
+ @insns[operator][:render]
95
73
  end
96
74
 
97
75
  def get_operands(operator)
@@ -105,49 +105,83 @@ module Origami
105
105
  end
106
106
 
107
107
  class PDF::Instruction
108
- insn 'm', Real, Real do |gs, x,y|
109
- gs.current_path << (subpath = Graphics::Path.new)
108
+ insn 'm', Real, Real do |canvas, x,y|
109
+ canvas.gs.current_path << (subpath = Graphics::Path.new)
110
110
  subpath.current_point = [x,y]
111
111
  end
112
112
 
113
- insn 'l', Real, Real do |gs, x,y|
114
- if gs.current_path.empty?
113
+ insn 'l', Real, Real do |canvas, x,y|
114
+ if canvas.gs.current_path.empty?
115
115
  raise InvalidPathError, "No current point is defined"
116
116
  end
117
117
 
118
- subpath = gs.current_path.last
118
+ subpath = canvas.gs.current_path.last
119
119
 
120
120
  from = subpath.current_point
121
121
  to = [x,y]
122
122
  subpath.add_segment(Graphics::Path::Line.new(from, to))
123
123
  end
124
124
 
125
- insn 'h' do |gs|
126
- unless gs.current_path.empty?
127
- subpath = gs.current_path.last
125
+ insn 'h' do |canvas|
126
+ unless canvas.gs.current_path.empty?
127
+ subpath = canvas.gs.current_path.last
128
128
  subpath.close! unless subpath.is_closed?
129
129
  end
130
130
  end
131
131
 
132
- insn 're', Real, Real, Real, Real do |gs, x,y,width,height|
132
+ insn 're', Real, Real, Real, Real do |canvas, x,y,width,height|
133
133
  tx = x + width
134
134
  ty = y + height
135
- gs.current_path << (subpath = Graphics::Path.new)
135
+ canvas.gs.current_path << (subpath = Graphics::Path.new)
136
136
  subpath.segments << Graphics::Path::Line.new([x,y], [tx,y])
137
137
  subpath.segments << Graphics::Path::Line.new([tx,y], [tx, ty])
138
138
  subpath.segments << Graphics::Path::Line.new([tx, ty], [x, ty])
139
139
  subpath.close!
140
140
  end
141
141
 
142
- insn 'S'
143
- insn 's' do |gs| gs.current_path.last.close! end
144
- insn 'f'
145
- insn 'F'
146
- insn 'f*'
147
- insn 'B'
148
- insn 'B*'
149
- insn 'b' do |gs| gs.current_path.last.close! end
150
- insn 'b*' do |gs| gs.current_path.last.close! end
142
+ insn 'S' do |canvas|
143
+ canvas.stroke_path
144
+ end
145
+
146
+ insn 's' do |canvas|
147
+ canvas.gs.current_path.last.close!
148
+ canvas.stroke_path
149
+ end
150
+
151
+ insn 'f' do |canvas|
152
+ canvas.fill_path
153
+ end
154
+
155
+ insn 'F' do |canvas|
156
+ canvas.fill_path
157
+ end
158
+
159
+ insn 'f*' do |canvas|
160
+ canvas.fill_path
161
+ end
162
+
163
+ insn 'B' do |canvas|
164
+ canvas.fill_path
165
+ canvas.stroke_path
166
+ end
167
+
168
+ insn 'B*' do |canvas|
169
+ canvas.fill_path
170
+ canvas.stroke_path
171
+ end
172
+
173
+ insn 'b' do |canvas|
174
+ canvas.gs.current_path.last.close!
175
+ canvas.fill_path
176
+ canvas.stroke_path
177
+ end
178
+
179
+ insn 'b*' do |canvas|
180
+ canvas.gs.current_path.last.close!
181
+ canvas.fill_path
182
+ canvas.stroke_path
183
+ end
184
+
151
185
  insn 'n'
152
186
  end
153
187
 
@@ -0,0 +1,69 @@
1
+ =begin
2
+
3
+ = File
4
+ graphics/render.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
+ module Graphics
29
+
30
+ module Canvas
31
+ attr_reader :gs
32
+
33
+ def initialize
34
+ @gs = Graphics::State.new
35
+ end
36
+
37
+ def clear
38
+ @gs.reset
39
+ end
40
+
41
+ def write_text(s); end
42
+ def stroke_path; end
43
+ def fill_path; end
44
+ end
45
+
46
+ class DummyCanvas
47
+ include Canvas
48
+ end
49
+
50
+ class TextCanvas
51
+ include Canvas
52
+
53
+ def initialize(output = STDOUT, columns = 80, lines = 25)
54
+ super()
55
+
56
+ @output = output
57
+ @columns, @lines = columns, lines
58
+ end
59
+
60
+ def write_text(s)
61
+ @output.print(s)
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
@@ -147,18 +147,18 @@ module Origami
147
147
  end #module Graphics
148
148
 
149
149
  class PDF::Instruction
150
- insn 'q' do |gs| gs.save; gs.reset end
151
- insn 'Q' do |gs| gs.restore end
152
- insn 'w', Real do |gs, lw| gs.line_width = lw end
153
- insn 'J', Real do |gs, lc| gs.line_cap = lc end
154
- insn 'j', Real do |gs, lj| gs.line_join = lj end
155
- insn 'M', Real do |gs, ml| gs.miter_limit = ml end
150
+ insn 'q' do |canvas| canvas.gs.save; canvas.gs.reset end
151
+ insn 'Q' do |canvas| canvas.gs.restore end
152
+ insn 'w', Real do |canvas, lw| canvas.gs.line_width = lw end
153
+ insn 'J', Real do |canvas, lc| canvas.gs.line_cap = lc end
154
+ insn 'j', Real do |canvas, lj| canvas.gs.line_join = lj end
155
+ insn 'M', Real do |canvas, ml| canvas.gs.miter_limit = ml end
156
156
 
157
- insn 'd', Array, Integer do |gs, array, phase|
158
- gs.dash_pattern = Graphics::DashPattern.new array, phase
157
+ insn 'd', Array, Integer do |canvas, array, phase|
158
+ canvas.gs.dash_pattern = Graphics::DashPattern.new array, phase
159
159
  end
160
160
 
161
- insn 'ri', Name do |gs, ri| gs.rendering_intent = ri end
161
+ insn 'ri', Name do |canvas, ri| canvas.gs.rendering_intent = ri end
162
162
  end
163
163
  end
164
164
 
@@ -119,106 +119,124 @@ module Origami
119
119
 
120
120
  class PDF::Instruction
121
121
  # Text instructions definitions
122
- insn 'Tc', Real do |gs, cS| gs.text_state.char_spacing = cS end
123
- insn 'Tw', Real do |gs, wS| gs.text_state.word_spacing = wS end
124
- insn 'Tz', Real do |gs, s| gs.text_state.scaling = s end
125
- insn 'TL', Real do |gs, l| gs.text_state.leading = l end
122
+ insn 'Tc', Real do |canvas, cS| canvas.gs.text_state.char_spacing = cS end
123
+ insn 'Tw', Real do |canvas, wS| canvas.gs.text_state.word_spacing = wS end
124
+ insn 'Tz', Real do |canvas, s| canvas.gs.text_state.scaling = s end
125
+ insn 'TL', Real do |canvas, l| canvas.gs.text_state.leading = l end
126
126
 
127
- insn 'Tf', Name, Real do |gs, font, size|
128
- gs.text_state.font = font
129
- gs.text_state.font_size = size
127
+ insn 'Tf', Name, Real do |canvas, font, size|
128
+ canvas.gs.text_state.font = font
129
+ canvas.gs.text_state.font_size = size
130
130
  end
131
131
 
132
- insn 'Tr', Integer do |gs, r| gs.text_state.rendering_mode = r end
133
- insn 'Ts', Real do |gs, s| gs.text_state.text_rise = s end
134
- insn 'BT' do |gs| gs.text_state.begin_text_object end
135
- insn 'ET' do |gs| gs.text_state.end_text_object end
132
+ insn 'Tr', Integer do |canvas, r| canvas.gs.text_state.rendering_mode = r end
133
+ insn 'Ts', Real do |canvas, s| canvas.gs.text_state.text_rise = s end
134
+ insn 'BT' do |canvas| canvas.gs.text_state.begin_text_object end
135
+ insn 'ET' do |canvas| canvas.gs.text_state.end_text_object end
136
136
 
137
- insn 'Td', Real, Real do |gs, tx, ty|
138
- unless gs.text_state.is_in_text_object?
137
+ insn 'Td', Real, Real do |canvas, tx, ty|
138
+ unless canvas.gs.text_state.is_in_text_object?
139
139
  raise TextStateError,
140
140
  "Must be in a text object to use operator : Td"
141
141
  end
142
142
 
143
- gs.text_state.text_matrix =
144
- gs.text_state.text_line_matrix =
145
- Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * gs.text_state.text_line_matrix
143
+ canvas.gs.text_state.text_matrix =
144
+ canvas.gs.text_state.text_line_matrix =
145
+ Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
146
146
  end
147
147
 
148
- insn 'TD', Real, Real do |gs, tx, ty|
149
- unless gs.text_state.is_in_text_object?
148
+ insn 'TD', Real, Real do |canvas, tx, ty|
149
+ unless canvas.gs.text_state.is_in_text_object?
150
150
  raise TextStateError,
151
151
  "Must be in a text object to use operator : TD"
152
152
  end
153
153
 
154
- gs.text_state.leading = -ty
154
+ canvas.gs.text_state.leading = -ty
155
155
 
156
- gs.text_state.text_matrix =
157
- gs.text_state.text_line_matrix =
158
- Matrix.rows([[1,0,0],[0,1,0],[tx,ty,1]]) * gs.text_state.text_line_matrix
156
+ canvas.gs.text_state.text_matrix =
157
+ canvas.gs.text_state.text_line_matrix =
158
+ Matrix.rows([[1,0,0],[0,1,0],[tx,ty,1]]) * canvas.gs.text_state.text_line_matrix
159
159
  end
160
160
 
161
- insn 'Tm', Real, Real, Real, Real, Real, Real do |gs, a,b,c,d,e,f,g|
162
- unless gs.text_state.is_in_text_object?
161
+ insn 'Tm', Real, Real, Real, Real, Real, Real do |canvas, a,b,c,d,e,f,g|
162
+ unless canvas.gs.text_state.is_in_text_object?
163
163
  raise TextStateError,
164
164
  "Must be in a text object to use operator : Tm"
165
165
  end
166
166
 
167
- gs.text_state.text_matrix =
168
- gs.text_state.text_line_matrix =
167
+ canvas.gs.text_state.text_matrix =
168
+ canvas.gs.text_state.text_line_matrix =
169
169
  Matrix.rows([[a,b,0],[c,d,0],[e,f,1]])
170
170
  end
171
171
 
172
- insn 'T*' do |gs|
173
- unless gs.text_state.is_in_text_object?
172
+ insn 'T*' do |canvas|
173
+ unless canvas.gs.text_state.is_in_text_object?
174
174
  raise TextStateError,
175
175
  "Must be in a text object to use operator : T*"
176
176
  end
177
177
 
178
- tx, ty = 0, -gs.text_state.leading
178
+ tx, ty = 0, -canvas.gs.text_state.leading
179
179
 
180
- gs.text_state.text_matrix =
181
- gs.text_state.text_line_matrix =
182
- Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * gs.text_state.text_line_matrix
180
+ canvas.gs.text_state.text_matrix =
181
+ canvas.gs.text_state.text_line_matrix =
182
+ Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
183
183
  end
184
184
 
185
- insn 'Tj', String do |gs, s|
186
- unless gs.text_state.is_in_text_object?
185
+ insn 'Tj', String do |canvas, s|
186
+ unless canvas.gs.text_state.is_in_text_object?
187
187
  raise TextStateError,
188
188
  "Must be in a text object to use operator : Tj"
189
189
  end
190
+
191
+ canvas.write_text(s)
190
192
  end
191
193
 
192
- insn "'", String do |gs, s|
193
- unless gs.text_state.is_in_text_object?
194
+ insn "'", String do |canvas, s|
195
+ unless canvas.gs.text_state.is_in_text_object?
194
196
  raise TextStateError,
195
197
  "Must be in a text object to use operator : '"
196
198
  end
197
199
 
198
- tx, ty = 0, -gs.text_state.leading
200
+ tx, ty = 0, -canvas.gs.text_state.leading
201
+
202
+ canvas.gs.text_state.text_matrix =
203
+ canvas.gs.text_state.text_line_matrix =
204
+ Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
199
205
 
200
- gs.text_state.text_matrix =
201
- gs.text_state.text_line_matrix =
202
- Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * gs.text_state.text_line_matrix
206
+ canvas.write_text(s)
203
207
  end
204
208
 
205
- insn '"', Real, Real, String do |gs, w, c, s|
206
- unless gs.text_state.is_in_text_object?
209
+ insn '"', Real, Real, String do |canvas, w, c, s|
210
+ unless canvas.gs.text_state.is_in_text_object?
207
211
  raise TextStateError,
208
212
  "Must be in a text object to use operator : \""
209
213
  end
210
214
 
211
- gs.text_state.word_spacing = w
212
- gs.text_state.char_spacing = c
215
+ canvas.gs.text_state.word_spacing = w
216
+ canvas.gs.text_state.char_spacing = c
213
217
 
214
218
  tx, ty = 0, -gs.text_state.leading
215
219
 
216
- gs.text_state.text_matrix =
217
- gs.text_state.text_line_matrix =
218
- Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * gs.text_state.text_line_matrix
220
+ canvas.gs.text_state.text_matrix =
221
+ canvas.gs.text_state.text_line_matrix =
222
+ Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
223
+
224
+ canvas.write_text(s)
219
225
  end
220
226
 
221
- insn 'TJ', Array
227
+ insn 'TJ', Array do |canvas, arr|
228
+ arr.each do |g|
229
+ case g
230
+ when Fixnum,Float then
231
+ # XXX: handle this in text space ?
232
+ when ::String then
233
+ canvas.write_text(g)
234
+ else
235
+ raise InvalidPDFInstructionError,
236
+ "Invalid component type `#{g.class}` in TJ operand"
237
+ end
238
+ end
239
+ end
222
240
  end
223
241
 
224
242
  end