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.
@@ -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