dyi 0.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.
- data/COPYING +674 -0
- data/README +28 -0
- data/examples/class_diagram.rb +151 -0
- data/examples/data/03311056.xlsx +0 -0
- data/examples/data/currency.xlsx +0 -0
- data/examples/data/money.csv +12 -0
- data/examples/line_and_bar.rb +26 -0
- data/examples/line_chart.rb +30 -0
- data/examples/logo.rb +68 -0
- data/examples/pie_chart.rb +19 -0
- data/examples/simple_shapes.rb +15 -0
- data/lib/dyi.rb +49 -0
- data/lib/dyi/chart.rb +34 -0
- data/lib/dyi/chart/array_reader.rb +136 -0
- data/lib/dyi/chart/base.rb +580 -0
- data/lib/dyi/chart/csv_reader.rb +93 -0
- data/lib/dyi/chart/excel_reader.rb +100 -0
- data/lib/dyi/chart/line_chart.rb +468 -0
- data/lib/dyi/chart/pie_chart.rb +141 -0
- data/lib/dyi/chart/table.rb +201 -0
- data/lib/dyi/color.rb +218 -0
- data/lib/dyi/coordinate.rb +224 -0
- data/lib/dyi/drawing.rb +32 -0
- data/lib/dyi/drawing/canvas.rb +100 -0
- data/lib/dyi/drawing/clipping.rb +61 -0
- data/lib/dyi/drawing/color_effect.rb +118 -0
- data/lib/dyi/drawing/filter.rb +74 -0
- data/lib/dyi/drawing/pen.rb +231 -0
- data/lib/dyi/drawing/pen_3d.rb +270 -0
- data/lib/dyi/font.rb +132 -0
- data/lib/dyi/formatter.rb +36 -0
- data/lib/dyi/formatter/base.rb +245 -0
- data/lib/dyi/formatter/emf_formatter.rb +253 -0
- data/lib/dyi/formatter/eps_formatter.rb +397 -0
- data/lib/dyi/formatter/svg_formatter.rb +260 -0
- data/lib/dyi/formatter/svg_reader.rb +113 -0
- data/lib/dyi/formatter/xaml_formatter.rb +317 -0
- data/lib/dyi/length.rb +399 -0
- data/lib/dyi/matrix.rb +122 -0
- data/lib/dyi/painting.rb +177 -0
- data/lib/dyi/shape.rb +1332 -0
- data/lib/dyi/svg_element.rb +149 -0
- data/lib/dyi/type.rb +104 -0
- data/lib/ironruby.rb +326 -0
- data/lib/util.rb +231 -0
- data/test/path_command_test.rb +217 -0
- data/test/test_length.rb +91 -0
- metadata +114 -0
@@ -0,0 +1,397 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2011 Sound-F Co., Ltd. All rights reserved.
|
4
|
+
#
|
5
|
+
# Author:: Mamoru Yuo
|
6
|
+
#
|
7
|
+
# This file is part of DYI.
|
8
|
+
#
|
9
|
+
# DYI is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# DYI is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with DYI. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
require 'nkf'
|
23
|
+
|
24
|
+
module DYI #:nodoc:
|
25
|
+
module Formatter #:nodoc:
|
26
|
+
|
27
|
+
class EpsFormatter < Base
|
28
|
+
|
29
|
+
def header_comment
|
30
|
+
<<-EOS
|
31
|
+
%!PS-Adobe-3.0 EPSF-3.0
|
32
|
+
%%CreationDate: #{Time.now.strftime('%a %b %d %H:%M:%S %Y')}
|
33
|
+
%%BoundingBox: 0 0 #{@canvas.real_width.to_f('pt').ceil} #{@canvas.real_height.to_f('pt').ceil}
|
34
|
+
%%EndComments
|
35
|
+
EOS
|
36
|
+
end
|
37
|
+
|
38
|
+
def prolog
|
39
|
+
<<-EOS
|
40
|
+
%%BeginProlog
|
41
|
+
/px { } bind def
|
42
|
+
/pt { #{Length.unit_ratio('pt')} mul } bind def
|
43
|
+
/cm { #{Length.unit_ratio('cm')} mul } bind def
|
44
|
+
/mm { #{Length.unit_ratio('mm')} mul } bind def
|
45
|
+
/in { #{Length.unit_ratio('in')} mul } bind def
|
46
|
+
/pc { #{Length.unit_ratio('pc')} mul } bind def
|
47
|
+
%%EndProlog
|
48
|
+
EOS
|
49
|
+
end
|
50
|
+
|
51
|
+
def footer_comment
|
52
|
+
<<-EOS
|
53
|
+
%%EOF
|
54
|
+
EOS
|
55
|
+
end
|
56
|
+
|
57
|
+
def base_transform
|
58
|
+
pt_ratio = 1.quo(Length.unit_ratio('pt'))
|
59
|
+
<<-EOS
|
60
|
+
[#{pt_ratio} 0 0 -#{pt_ratio} 0 #{@canvas.real_height * pt_ratio}] concat
|
61
|
+
EOS
|
62
|
+
end
|
63
|
+
|
64
|
+
def puts(io=$>)
|
65
|
+
StringFormat.set_default_formats(:color=>['%.3f %.3f %.3f', true], :length=>'0.### U', :coordinate=>'x y') {
|
66
|
+
if @canvas.root_node?
|
67
|
+
io << header_comment
|
68
|
+
io << prolog
|
69
|
+
io << base_transform
|
70
|
+
end
|
71
|
+
@canvas.write_as(self, io)
|
72
|
+
if @canvas.root_node?
|
73
|
+
io << footer_comment
|
74
|
+
end
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def write_canvas(canvas, io)
|
79
|
+
canvas.child_elements.each do |element|
|
80
|
+
element.write_as(self, io)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_rectangle(shape, io)
|
85
|
+
command_block(io) {
|
86
|
+
transform_path(io, shape)
|
87
|
+
clip_path(io, shape)
|
88
|
+
puts_line(io, 'newpath')
|
89
|
+
puts_line(io, shape.left, shape.top, 'moveto')
|
90
|
+
puts_line(io, shape.width, 0, 'rlineto')
|
91
|
+
puts_line(io, 0, shape.height, 'rlineto')
|
92
|
+
puts_line(io, -shape.width, 0, 'rlineto')
|
93
|
+
puts_line(io, 'closepath')
|
94
|
+
if block_given?
|
95
|
+
yield
|
96
|
+
else
|
97
|
+
fill_current_path(io, shape)
|
98
|
+
stroke_current_path(io, shape)
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def write_circle(shape, io)
|
104
|
+
command_block(io) {
|
105
|
+
transform_path(io, shape)
|
106
|
+
clip_path(io, shape)
|
107
|
+
puts_line(io, 'newpath')
|
108
|
+
puts_line(io, shape.center.x + shape.radius, shape.center.y, 'moveto')
|
109
|
+
puts_line(io, shape.center.x, shape.center.y, shape.radius, 0, 360, 'arc')
|
110
|
+
if block_given?
|
111
|
+
yield
|
112
|
+
else
|
113
|
+
fill_current_path(io, shape)
|
114
|
+
stroke_current_path(io, shape)
|
115
|
+
end
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def write_ellipse(shape, io)
|
120
|
+
ratio = shape.radius_x.to_f / shape.radius_y.to_f
|
121
|
+
command_block(io) {
|
122
|
+
transform_path(io, shape)
|
123
|
+
clip_path(io, shape)
|
124
|
+
puts_line(io, 1, 1.0 / ratio, 'scale')
|
125
|
+
puts_line(io, 'newpath')
|
126
|
+
puts_line(io, shape.center.x + shape.radius_x, shape.center.y * ratio, 'moveto')
|
127
|
+
puts_line(io, shape.center.x, shape.center.y * ratio, shape.radius_x, 0, 360, 'arc')
|
128
|
+
if block_given?
|
129
|
+
yield
|
130
|
+
else
|
131
|
+
fill_current_path(io, shape)
|
132
|
+
stroke_current_path(io, shape)
|
133
|
+
end
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_line(shape, io)
|
138
|
+
command_block(io) {
|
139
|
+
transform_path(io, shape)
|
140
|
+
clip_path(io, shape)
|
141
|
+
puts_line(io, 'newpath')
|
142
|
+
write_lines(io, [shape.start_point, shape.end_point])
|
143
|
+
if block_given?
|
144
|
+
yield
|
145
|
+
else
|
146
|
+
stroke_current_path(io, shape)
|
147
|
+
end
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
def write_polyline(shape, io)
|
152
|
+
command_block(io) {
|
153
|
+
transform_path(io, shape)
|
154
|
+
clip_path(io, shape)
|
155
|
+
puts_line(io, 'newpath')
|
156
|
+
write_lines(io, shape.points)
|
157
|
+
if block_given?
|
158
|
+
yield
|
159
|
+
else
|
160
|
+
fill_current_path(io, shape)
|
161
|
+
stroke_current_path(io, shape)
|
162
|
+
end
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
def write_polygon(shape, io)
|
167
|
+
command_block(io) {
|
168
|
+
transform_path(io, shape)
|
169
|
+
clip_path(io, shape)
|
170
|
+
puts_line(io, 'newpath')
|
171
|
+
write_lines(io, shape.points)
|
172
|
+
puts_line(io, 'closepath')
|
173
|
+
if block_given?
|
174
|
+
yield
|
175
|
+
else
|
176
|
+
fill_current_path(io, shape)
|
177
|
+
stroke_current_path(io, shape)
|
178
|
+
end
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
def write_path(shape, io)
|
183
|
+
command_block(io) {
|
184
|
+
transform_path(io, shape)
|
185
|
+
clip_path(io, shape)
|
186
|
+
puts_line(io, 'newpath')
|
187
|
+
shape.compatible_path_data.each do |cmd|
|
188
|
+
case cmd
|
189
|
+
when Shape::Path::MoveCommand
|
190
|
+
puts_line(io, cmd.point.x, cmd.point.y, cmd.relative? ? 'rmoveto' : 'moveto')
|
191
|
+
when Shape::Path::CloseCommand
|
192
|
+
puts_line(io, 'closepath')
|
193
|
+
when Shape::Path::LineCommand
|
194
|
+
puts_line(io, cmd.point.x, cmd.point.y, cmd.relative? ? 'rlineto' : 'lineto')
|
195
|
+
when Shape::Path::CurveCommand
|
196
|
+
puts_line(io,
|
197
|
+
cmd.control_point1.x, cmd.control_point1.y,
|
198
|
+
cmd.control_point2.x, cmd.control_point2.y,
|
199
|
+
cmd.point.x, cmd.point.y,
|
200
|
+
cmd.relative? ? 'rcurveto' : 'curveto')
|
201
|
+
else
|
202
|
+
raise TypeError, "unknown command: #{cmd.class}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
if block_given?
|
206
|
+
yield
|
207
|
+
else
|
208
|
+
fill_current_path(io, shape)
|
209
|
+
stroke_current_path(io, shape)
|
210
|
+
end
|
211
|
+
}
|
212
|
+
end
|
213
|
+
|
214
|
+
def write_text(shape, io)
|
215
|
+
command_block(io) {
|
216
|
+
puts_line(io, '/GothicBBB-Medium-RKSJ-H findfont', shape.font.draw_size, 'scalefont setfont')
|
217
|
+
text = NKF.nkf('-s -W', shape.formated_text).unpack('H*').first
|
218
|
+
case shape.attributes[:text_anchor]
|
219
|
+
when 'middle' then dx = "<#{text}> stringwidth pop -0.5 mul"
|
220
|
+
when 'end' then dx = "<#{text}> stringwidth pop -1 mul"
|
221
|
+
else dx = "0"
|
222
|
+
end
|
223
|
+
case shape.attributes[:alignment_baseline]
|
224
|
+
when 'top' then y = shape.point.y - shape.font_height * 0.85
|
225
|
+
when 'middle' then y = shape.point.y - shape.font_height * 0.35
|
226
|
+
when 'bottom' then y = shape.point.y + shape.font_height * 0.15
|
227
|
+
else y = shape.point.y
|
228
|
+
end
|
229
|
+
puts_line(io, "[ 1 0 0 -1 #{dx}", shape.point.y * 2, '] concat')
|
230
|
+
puts_line(io, shape.point.x, y, 'moveto')
|
231
|
+
puts_line(io, "<#{text}>", 'show')
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
def write_group(shape, io)
|
236
|
+
command_block(io) {
|
237
|
+
transform_path(io, shape)
|
238
|
+
clip_path(io, shape)
|
239
|
+
shape.child_elements.each do |element|
|
240
|
+
element.write_as(self, io)
|
241
|
+
end
|
242
|
+
} unless shape.child_elements.empty?
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def puts_line(io, *args) #:nodoc:
|
248
|
+
io << args.flatten.join(' ') << "\n"
|
249
|
+
end
|
250
|
+
|
251
|
+
def command_block(io) #:nodoc:
|
252
|
+
puts_line(io, 'gsave')
|
253
|
+
yield
|
254
|
+
puts_line(io, 'grestore')
|
255
|
+
end
|
256
|
+
|
257
|
+
def stroke_path(io, shape) #:nodoc:
|
258
|
+
cmds = []
|
259
|
+
if shape.respond_to?(:painting) && (attrs = shape.painting)
|
260
|
+
cmds.push([attrs.stroke, 'setrgbcolor']) if attrs.stroke
|
261
|
+
cmds.push([attrs.stroke_width, 'setlinewidth']) if attrs.stroke_width && attrs.stroke_width.to_f > 0
|
262
|
+
cmds.push(['[', attrs.stroke_dasharray, ']', attrs.stroke_dashoffset || 0, 'setdash']) if attrs.stroke_dasharray
|
263
|
+
cmds.push([linecap_to_num(attrs.stroke_linecap), 'setlinecap']) if linecap_to_num(attrs.stroke_linecap)
|
264
|
+
cmds.push([linejoin_to_num(attrs.stroke_linejoin), 'setlinejoin']) if linejoin_to_num(attrs.stroke_linejoin)
|
265
|
+
cmds.push([attrs.stroke_miterlimit, 'setmiterlimit']) if attrs.stroke_miterlimit
|
266
|
+
end
|
267
|
+
return if cmds.empty?
|
268
|
+
command_block(io) {
|
269
|
+
cmds.each do |cmd|
|
270
|
+
puts_line(io, *cmd)
|
271
|
+
end
|
272
|
+
yield
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
def stroke_current_path(io, shape) #:nodoc:
|
277
|
+
stroke_path(io, shape) {
|
278
|
+
puts_line(io, 'stroke')
|
279
|
+
} if shape.painting.stroke
|
280
|
+
end
|
281
|
+
|
282
|
+
def fill_path(io, shape) #:nodoc:
|
283
|
+
cmds = []
|
284
|
+
if shape.respond_to?(:painting) && (attrs = shape.painting)
|
285
|
+
if attrs.fill
|
286
|
+
if attrs.fill.respond_to?(:write_as)
|
287
|
+
cmds.push(linear_gradiant_commands(shape, attrs.fill))
|
288
|
+
else
|
289
|
+
cmds.push([attrs.fill, 'setrgbcolor'])
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
return if cmds.empty?
|
294
|
+
command_block(io) {
|
295
|
+
cmds.each do |cmd|
|
296
|
+
puts_line(io, *cmd)
|
297
|
+
end
|
298
|
+
yield
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
def fill_current_path(io, shape) #:nodoc:
|
303
|
+
fill_path(io, shape) {
|
304
|
+
puts_line(io, shape.painting.fill_rule == 'evenodd' ? 'eofill' : 'fill')
|
305
|
+
} if shape.painting.fill
|
306
|
+
end
|
307
|
+
|
308
|
+
def linecap_to_num(linecap) #:nodoc:
|
309
|
+
case linecap
|
310
|
+
when 'butt' then 0
|
311
|
+
when 'round' then 1
|
312
|
+
when 'square' then 2
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def linejoin_to_num(linejoin) #:nodoc:
|
317
|
+
case linejoin
|
318
|
+
when 'miter' then 0
|
319
|
+
when 'round' then 1
|
320
|
+
when 'bevel' then 2
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def write_lines(io, points) #:nodoc:
|
325
|
+
puts_line(io, points.first.x, points.first.y, 'moveto')
|
326
|
+
points[1..-1].each do |pt|
|
327
|
+
pt = Coordinate.new(pt)
|
328
|
+
puts_line(io, pt.x, pt.y, 'lineto')
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def linear_gradiant_commands(shape, lg)
|
333
|
+
x1 = shape.left * (1 - lg.start_point[0]) + shape.right * lg.start_point[0]
|
334
|
+
y1 = shape.top * (1 - lg.start_point[1]) + shape.bottom * lg.start_point[1]
|
335
|
+
x2 = shape.left * (1 - lg.stop_point[0]) + shape.right * lg.stop_point[0]
|
336
|
+
y2 = shape.top * (1 - lg.stop_point[1]) + shape.bottom * lg.stop_point[1]
|
337
|
+
|
338
|
+
last_stop = lg.child_elements.first
|
339
|
+
|
340
|
+
cmds = []
|
341
|
+
if last_stop
|
342
|
+
lg.child_elements[1..-1].each do |g_stop|
|
343
|
+
c1 = last_stop.color
|
344
|
+
c2 = g_stop.color
|
345
|
+
next unless c1 && c2
|
346
|
+
s_x1 = x1 * (1 - last_stop.offset) + x2 * last_stop.offset
|
347
|
+
s_y1 = y1 * (1 - last_stop.offset) + y2 * last_stop.offset
|
348
|
+
s_x2 = x1 * (1 - g_stop.offset) + x2 * g_stop.offset
|
349
|
+
s_y2 = y1 * (1 - g_stop.offset) + y2 * g_stop.offset
|
350
|
+
cmds.push(['<< /PatternType 2 /Shading << /ShadingType 2 /ColorSpace /DeviceRGB'])
|
351
|
+
cmds.push(['/Coords [', s_x1, s_y1, s_x2, s_y2, ']'])
|
352
|
+
cmds.push(['/Function << /FunctionType 2 /Domain [ 0 1 ]'])
|
353
|
+
cmds.push(['/C0 [', c1, ']'])
|
354
|
+
cmds.push(['/C1 [', c2, ']'])
|
355
|
+
cmds.push(['/N 1 >> >> >> matrix makepattern setpattern fill'])
|
356
|
+
current_stop = g_stop
|
357
|
+
end
|
358
|
+
end
|
359
|
+
cmds
|
360
|
+
end
|
361
|
+
|
362
|
+
def transform_path(io, shape) #:nodoc:
|
363
|
+
if shape.respond_to?(:transform) && !(tr = shape.transform).empty?
|
364
|
+
tr.each do |t|
|
365
|
+
case t[0]
|
366
|
+
when :translate
|
367
|
+
puts_line(io, t[1], t[2], 'translate')
|
368
|
+
when :scale
|
369
|
+
puts_line(io, t[1], t[2], 'scale')
|
370
|
+
when :rotate
|
371
|
+
puts_line(io, t[1], t[2], 'rotate')
|
372
|
+
when :skewX
|
373
|
+
tan = Math.tan(t[1] * Math::PI / 180)
|
374
|
+
puts_line(io, '[', 1, 0, tan, 1, 0, 0, ']', 'concat')
|
375
|
+
when :skewY
|
376
|
+
tan = Math.tan(t[1] * Math::PI / 180)
|
377
|
+
puts_line(io, '[', 1, tan, 0, 1, 0, 0, ']', 'concat')
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def clip_path(io, shape) #:nodoc:
|
384
|
+
if shape.respond_to?(:clipping) && shape.clipping
|
385
|
+
shape.clipping.each_shapes do |shape, rule|
|
386
|
+
s = shape.dup
|
387
|
+
s.painting.fill = nil
|
388
|
+
s.painting.stroke = nil
|
389
|
+
s.write_as(self, io) {
|
390
|
+
puts_line(io, rule == 'evenodd' ? 'eoclip' : 'clip')
|
391
|
+
}
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2011 Sound-F Co., Ltd. All rights reserved.
|
4
|
+
#
|
5
|
+
# Author:: Mamoru Yuo
|
6
|
+
#
|
7
|
+
# This file is part of DYI.
|
8
|
+
#
|
9
|
+
# DYI is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# DYI is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with DYI. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
require 'stringio'
|
23
|
+
|
24
|
+
module DYI #:nodoc:
|
25
|
+
module Formatter #:nodoc:
|
26
|
+
|
27
|
+
class SvgFormatter < XmlFormatter
|
28
|
+
|
29
|
+
def initialize(canvas, indent=0, level=0, version='1.1')
|
30
|
+
super(canvas, indent, level)
|
31
|
+
raise ArgumentError, "version `#{version}' is unknown version" unless ['1.0', '1.1'].include?(@version = version.to_s)
|
32
|
+
@defs = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def declaration
|
36
|
+
case @version
|
37
|
+
when '1.0' then %Q{<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">}
|
38
|
+
when '1.1' then %Q{<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def puts(io=$>)
|
43
|
+
StringFormat.set_default_formats(:coordinate => 'x,y') {
|
44
|
+
super
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def write_canvas(canvas, io)
|
49
|
+
@defs = {}
|
50
|
+
sio = StringIO.new
|
51
|
+
create_node(sio, 'svg',
|
52
|
+
:xmlns => "http://www.w3.org/2000/svg",
|
53
|
+
:version => @version,
|
54
|
+
:width => canvas.real_width,
|
55
|
+
:height => canvas.real_height,
|
56
|
+
:viewBox => canvas.view_box,
|
57
|
+
:preserveAspectRatio => canvas.preserve_aspect_ratio) {
|
58
|
+
@root_info = [sio.pos, @level]
|
59
|
+
canvas.child_elements.each do |element|
|
60
|
+
element.write_as(self, sio)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
if @defs.empty?
|
65
|
+
io << sio.string
|
66
|
+
else
|
67
|
+
sio.rewind
|
68
|
+
io << sio.read(@root_info[0])
|
69
|
+
|
70
|
+
_level = @level
|
71
|
+
@level = @root_info[1]
|
72
|
+
create_node(io, 'defs') {
|
73
|
+
@defs.each do |def_id, def_item|
|
74
|
+
def_item.write_as(self, io)
|
75
|
+
end
|
76
|
+
}
|
77
|
+
@level = _level
|
78
|
+
|
79
|
+
io << sio.read
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def write_rectangle(shape, io)
|
84
|
+
attrs = {:x=>shape.left, :y=>shape.top, :width=>shape.width, :height=>shape.height}
|
85
|
+
attrs.merge!(common_attributes(shape))
|
86
|
+
attrs[:rx] = shape.attributes[:rx] if shape.attributes[:rx]
|
87
|
+
attrs[:ry] = shape.attributes[:ry] if shape.attributes[:ry]
|
88
|
+
create_leaf_node(io, 'rect', attrs)
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_circle(shape, io)
|
92
|
+
attrs = {:cx=>shape.center.x, :cy=>shape.center.y, :r=>shape.radius}
|
93
|
+
attrs.merge!(common_attributes(shape))
|
94
|
+
create_leaf_node(io, 'circle', attrs)
|
95
|
+
end
|
96
|
+
|
97
|
+
def write_ellipse(shape, io)
|
98
|
+
attrs = {:cx=>shape.center.x, :cy=>shape.center.y, :rx=>shape.radius_x, :ry=>shape.radius_y}
|
99
|
+
attrs.merge!(common_attributes(shape))
|
100
|
+
create_leaf_node(io, 'ellipse', attrs)
|
101
|
+
end
|
102
|
+
|
103
|
+
def write_line(shape, io)
|
104
|
+
attrs = {:x1 => shape.start_point.x, :y1 => shape.start_point.y, :x2 => shape.end_point.x, :y2 => shape.end_point.y}
|
105
|
+
attrs.merge!(common_attributes(shape))
|
106
|
+
create_leaf_node(io, 'line', attrs)
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_polyline(shape, io)
|
110
|
+
attrs = {:points => shape.points.join(' ')}
|
111
|
+
attrs.merge!(common_attributes(shape))
|
112
|
+
create_leaf_node(io, 'polyline', attrs)
|
113
|
+
end
|
114
|
+
|
115
|
+
def write_polygon(shape, io)
|
116
|
+
attrs = {:points => shape.points.join(' ')}
|
117
|
+
attrs.merge!(common_attributes(shape))
|
118
|
+
create_leaf_node(io, 'polygon', attrs)
|
119
|
+
end
|
120
|
+
|
121
|
+
def write_path(shape, io)
|
122
|
+
# attrs = {:d => shape.concise_path_data}
|
123
|
+
attrs = {:d => shape.compatible_path_data.to_concise_syntax}
|
124
|
+
attrs.merge!(common_attributes(shape))
|
125
|
+
create_leaf_node(io, 'path', attrs)
|
126
|
+
end
|
127
|
+
|
128
|
+
def write_text(shape, io)
|
129
|
+
attrs = {:x => shape.point.x, :y => shape.point.y}
|
130
|
+
attrs.merge!(common_attributes(shape))
|
131
|
+
attrs[:"text-decoration"] = shape.attributes[:text_decoration] if shape.attributes[:text_decoration]
|
132
|
+
# attrs[:"alignment-baseline"] = shape.attributes[:alignment_baseline] if shape.attributes[:alignment_baseline]
|
133
|
+
case shape.attributes[:alignment_baseline]
|
134
|
+
when 'top' then attrs[:y] += shape.font_height * 0.85
|
135
|
+
when 'middle' then attrs[:y] += shape.font_height * 0.35
|
136
|
+
when 'bottom' then attrs[:y] -= shape.font_height * 0.15
|
137
|
+
end
|
138
|
+
attrs[:"text-anchor"] = shape.attributes[:text_anchor] if shape.attributes[:text_anchor]
|
139
|
+
attrs[:"writing-mode"] = shape.attributes[:writing_mode] if shape.attributes[:writing_mode]
|
140
|
+
attrs[:textLength] = shape.attributes[:textLength] if shape.attributes[:textLength]
|
141
|
+
attrs[:lengthAdjust] = shape.attributes[:lengthAdjust] if shape.attributes[:lengthAdjust]
|
142
|
+
text = shape.formated_text
|
143
|
+
if text =~ /(\r\n|\n|\r)/
|
144
|
+
create_node(io, 'text', attrs) {
|
145
|
+
create_leaf_node(io, 'tspan', $`.strip, :x => shape.point.x)
|
146
|
+
$'.each_line do |line|
|
147
|
+
create_leaf_node(io, 'tspan', line.strip, :x => shape.point.x, :dy => shape.dy)
|
148
|
+
end
|
149
|
+
}
|
150
|
+
else
|
151
|
+
create_leaf_node(io, 'text', text, attrs)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def write_group(shape, io)
|
156
|
+
create_node(io, 'g', common_attributes(shape)) {
|
157
|
+
shape.child_elements.each do |element|
|
158
|
+
element.write_as(self, io)
|
159
|
+
end
|
160
|
+
} unless shape.child_elements.empty?
|
161
|
+
end
|
162
|
+
|
163
|
+
def write_linear_gradient(shape, io)
|
164
|
+
attr = {
|
165
|
+
:id => @defs.find{|key, value| value==shape}[0],
|
166
|
+
:gradientUnit => 'objectBoundingBox',
|
167
|
+
:x1 => shape.start_point[0],
|
168
|
+
:y1 => shape.start_point[1],
|
169
|
+
:x2 => shape.stop_point[0],
|
170
|
+
:y2 => shape.stop_point[1]}
|
171
|
+
attr[:"spreadMethod"] = shape.spread_method if shape.spread_method
|
172
|
+
create_node(io, 'linearGradient', attr) {
|
173
|
+
shape.child_elements.each do |element|
|
174
|
+
element.write_as(self, io)
|
175
|
+
end
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
def write_gradient_stop(shape, io)
|
180
|
+
attrs = {:offset=>shape.offset}
|
181
|
+
attrs[:"stop-color"] = shape.color if shape.color
|
182
|
+
attrs[:"stop-opacity"] = shape.opacity if shape.opacity
|
183
|
+
create_leaf_node(io, 'stop', attrs)
|
184
|
+
end
|
185
|
+
|
186
|
+
def write_clipping(clipping, io)
|
187
|
+
attr = {:id => @defs.find{|key, value| value==clipping}[0]}
|
188
|
+
create_node(io, 'clipPath', attr) {
|
189
|
+
clipping.shapes.each_with_index do |shape, i|
|
190
|
+
shape.write_as(self, io)
|
191
|
+
end
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def name_to_attribute(name) #:nodoc:
|
198
|
+
name.to_s.gsub(/_/,'-').to_sym
|
199
|
+
end
|
200
|
+
|
201
|
+
def common_attributes(shape) #:nodoc:
|
202
|
+
attributes = {}
|
203
|
+
style = create_style(shape)
|
204
|
+
transform = create_transform(shape)
|
205
|
+
clip_path = create_clip_path(shape)
|
206
|
+
attributes[:style] = style if style
|
207
|
+
attributes[:transform] = transform if transform
|
208
|
+
attributes[:'clip-path'] = clip_path if clip_path
|
209
|
+
attributes
|
210
|
+
end
|
211
|
+
|
212
|
+
def create_style(shape) #:nodoc:
|
213
|
+
styles = {}
|
214
|
+
if shape.respond_to?(:font) && shape.font && !shape.font.empty?
|
215
|
+
styles.merge!(shape.font.attributes)
|
216
|
+
end
|
217
|
+
if shape.respond_to?(:painting) && shape.painting && !shape.painting.empty?
|
218
|
+
attributes = shape.painting.attributes
|
219
|
+
attributes[:stroke_dasharray] = attributes[:stroke_dasharray].join(',') if attributes.key?(:stroke_dasharray)
|
220
|
+
attributes[:fill] = 'none' unless attributes.key?(:fill)
|
221
|
+
styles.merge!(attributes)
|
222
|
+
end
|
223
|
+
styles.empty? ? nil : styles.map {|key, value| "#{attribute_name(key)}:#{attribute_string(value)}"}.join(';')
|
224
|
+
end
|
225
|
+
|
226
|
+
def attribute_string(value) #:nodoc:
|
227
|
+
value.respond_to?(:write_as) ? "url(##{add_defs(value)})" : value.to_s
|
228
|
+
end
|
229
|
+
|
230
|
+
def create_transform(shape) #:nodoc:
|
231
|
+
if shape.respond_to?(:transform) && !shape.transform.empty?
|
232
|
+
shape.transform.map{|item| "#{item[0]}(#{item[1...item.size].join(',')})"}.join(' ')
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def create_clip_path(shape) #:nodoc:
|
237
|
+
if shape.respond_to?(:clipping) && shape.clipping
|
238
|
+
"url(##{add_defs(shape.clipping)})"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def add_defs(value) #:nodoc:
|
243
|
+
@defs.each do |def_id, def_item|
|
244
|
+
return def_id if def_item == value
|
245
|
+
end
|
246
|
+
def_id = create_def_id(@defs.size)
|
247
|
+
@defs[def_id] = value
|
248
|
+
def_id
|
249
|
+
end
|
250
|
+
|
251
|
+
def create_def_id(index) #:nodoc:
|
252
|
+
'def%03d' % index
|
253
|
+
end
|
254
|
+
|
255
|
+
def attribute_name(key) #:nodoc:
|
256
|
+
key.to_s.gsub(/_/,'-')
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|