rsvgr 0.0.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.
- checksums.yaml +7 -0
- data/lib/rsvgr.rb +405 -0
- metadata +44 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cd4d756ecd876ced14a6866dd9594a1c93666f6e
|
4
|
+
data.tar.gz: d3e3f0d0e9da5624f44ebe704132cf6adfb2a17e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f508fc6214a96e9411ef2830e5e6bd79ae9476425be762854812b0edbf4f79f2e08928e99376f465d718483e9232b27e4d63919fb285f7e2993a7654e90e4098
|
7
|
+
data.tar.gz: 37c542277ac4c4f7184458b83820067259ae155c056f24c0dc8d7dfa63794beef0a406ded045f5473972d52f81af941730d0bda0da6fe009b39a5ba25897235e
|
data/lib/rsvgr.rb
ADDED
@@ -0,0 +1,405 @@
|
|
1
|
+
# I don't give a fuck about namespaces, xslt, etc.
|
2
|
+
# I don't use any xml library, because I just don't really need it here
|
3
|
+
# I don't give a fuck about xslt and other shit
|
4
|
+
# I JUST A PSYCHO REDING THIS AND DOING THE EASIEST WAY
|
5
|
+
|
6
|
+
# http://www.w3.org/TR/SVGTiny12/single-page.html
|
7
|
+
|
8
|
+
|
9
|
+
module RSVGR
|
10
|
+
|
11
|
+
DEFAULT_SIZE = 100.0
|
12
|
+
UNSUPPORTED = ->{ raise "not supported currently" }
|
13
|
+
|
14
|
+
class Node
|
15
|
+
def initialize args = {}
|
16
|
+
args[:x1] ||= 0
|
17
|
+
args[:y1] ||= 0
|
18
|
+
args[:x2] ||= DEFAULT_SIZE
|
19
|
+
args[:y2] ||= DEFAULT_SIZE
|
20
|
+
args.each{ |k,v| instance_variable_set "@#{k}",v }
|
21
|
+
end
|
22
|
+
def to_s
|
23
|
+
raise "you had to override me, while ascending from #{self.class.name} class"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class CanIHazChildren < Node
|
28
|
+
def initialize args = {}
|
29
|
+
@children = []
|
30
|
+
super
|
31
|
+
end
|
32
|
+
def << x
|
33
|
+
@children << x
|
34
|
+
self
|
35
|
+
end
|
36
|
+
def to_s_children additional_child = []
|
37
|
+
[*@children, *additional_child].map{ |child|
|
38
|
+
child.to_s.gsub(/.+/, ' \0')
|
39
|
+
}.join
|
40
|
+
end
|
41
|
+
def save_as what, where
|
42
|
+
File.open(where, "w"){ |f| f.puts what }
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Group < CanIHazChildren
|
48
|
+
def to_s additional_child = []
|
49
|
+
"<g transform=\"" \
|
50
|
+
"#{"translate(#{@x || 0},#{@y || 0}) " if @x || @y}" \
|
51
|
+
"#{"scale(#{@scale_x || @scale || 1},#{@scale_y || @scale || 1}) " if @scale_x || @scale_y || @scale}" \
|
52
|
+
"#{"rotate(#{@rotate})" if @rotate}\"" \
|
53
|
+
"#{" id=\"#{@id}\"" if @id}" \
|
54
|
+
"#{" fill=\"#{@fill_color}\"" if @fill_color}" \
|
55
|
+
"#{" stroke-width=\"#{@stroke_width}\"" if @stroke_width}" \
|
56
|
+
"#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
|
57
|
+
">\n" + to_s_children(additional_child) + "</g>\n"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Root < CanIHazChildren
|
62
|
+
def initialize args = {}
|
63
|
+
@fill_color = "silver"
|
64
|
+
@stroke_color = "black"
|
65
|
+
@stroke_width = DEFAULT_SIZE*0.01
|
66
|
+
@stroke_linecap = "round"
|
67
|
+
super
|
68
|
+
end
|
69
|
+
def to_s
|
70
|
+
"<?xml version=\"1.0\"?>\n" \
|
71
|
+
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.2\" baseProfile=\"tiny\"" \
|
72
|
+
" viewBox=\"#{[@x1, @y1, @x2, @y2].join " "}\"" \
|
73
|
+
" fill=\"#{@fill_color}\"" \
|
74
|
+
" stroke=\"#{@stroke_color}\"" \
|
75
|
+
" stroke-width=\"#{@stroke_width}\"" \
|
76
|
+
" stroke-linecap=\"#{@stroke_linecap}\"" \
|
77
|
+
" preserveAspectRatio=\"xMidYMid meet\"" \
|
78
|
+
">\n <title>Generated by RubySVGmakeR v0.1 (c) Nakilon</title>\n" +
|
79
|
+
to_s_children + "</svg>\n"
|
80
|
+
end
|
81
|
+
def to_html
|
82
|
+
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
|
83
|
+
" <head>\n" \
|
84
|
+
" <title>#{Time.now}</title>\n" \
|
85
|
+
" <style type=\"text/css\" media=\"screen\">\n" \
|
86
|
+
" body {\n" \
|
87
|
+
" background:lightgrey;\n" \
|
88
|
+
" margin:0;\n" \
|
89
|
+
" }\n" \
|
90
|
+
" svg {\n" \
|
91
|
+
" display:block;\n" \
|
92
|
+
" border:2px solid grey;\n" \
|
93
|
+
" position:absolute;\n" \
|
94
|
+
" top:3%;\n" \
|
95
|
+
" left:3%;\n" \
|
96
|
+
" width:94%;\n" \
|
97
|
+
" height:94%;\n" \
|
98
|
+
" background:white;\n" \
|
99
|
+
" }\n" \
|
100
|
+
" </style>\n" \
|
101
|
+
" </head>\n" \
|
102
|
+
" <body>\n" + to_s + " </body>\n</html>\n"
|
103
|
+
end
|
104
|
+
def cat
|
105
|
+
puts self.to_s
|
106
|
+
self
|
107
|
+
end
|
108
|
+
def save
|
109
|
+
save_as to_s, "temp.svg"
|
110
|
+
end
|
111
|
+
def save_html
|
112
|
+
UNSUPPORTED
|
113
|
+
save_as to_html, "temp.html"
|
114
|
+
end
|
115
|
+
def save_as what, where
|
116
|
+
UNSUPPORTED
|
117
|
+
File.open(where, "w"){ |f| f.puts what }
|
118
|
+
self
|
119
|
+
end
|
120
|
+
def open_with app
|
121
|
+
RUBY_PLATFORM["mingw"] ? `#{app} temp.svg` : `open -a #{app} temp.svg`
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
class Script < CanIHazChildren
|
127
|
+
def to_s
|
128
|
+
"<script" \
|
129
|
+
" type=\"text/ecmascript\"" \
|
130
|
+
"#{" id=\"#{@id}\"" if @id}" \
|
131
|
+
"><![CDATA[\n" + to_s_children + "]]></script>\n"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class Text < CanIHazChildren
|
136
|
+
def initialize args = {}
|
137
|
+
args[:x] ||= DEFAULT_SIZE/2
|
138
|
+
args[:y] ||= DEFAULT_SIZE/2
|
139
|
+
args[:size] ||= DEFAULT_SIZE*0.8
|
140
|
+
args[:anchor] ||= "middle"
|
141
|
+
args[:baseline] ||= "central"
|
142
|
+
args[:fill_color] ||= "black"
|
143
|
+
super
|
144
|
+
end
|
145
|
+
def to_s
|
146
|
+
"<text" \
|
147
|
+
"#{" id=\"#{@id}\"" if @id}" \
|
148
|
+
"#{" visibility=\"#{@visibility}\"" if @visibility}" \
|
149
|
+
" x=\"#{@x}\"" \
|
150
|
+
" y=\"#{@y}\"" \
|
151
|
+
" font-size=\"#{@size}\"" \
|
152
|
+
" text-anchor=\"#{@anchor}\"" \
|
153
|
+
" dominant-baseline=\"#{@baseline}\"" \
|
154
|
+
" fill=\"#{@fill_color}\"" \
|
155
|
+
"#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
|
156
|
+
"#{" style=\"#{@style}\"" if @style}" \
|
157
|
+
">\n" + to_s_children + "</text>\n"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
class Line < Node
|
162
|
+
def to_s
|
163
|
+
"<line " \
|
164
|
+
"x1=\"#{@x1}\" " \
|
165
|
+
"x2=\"#{@x2}\" " \
|
166
|
+
"y1=\"#{@y1}\" " \
|
167
|
+
"y2=\"#{@y2}\" " \
|
168
|
+
"#{"stroke=\"#{@stroke_color}\" " if @stroke_color}" \
|
169
|
+
"/>\n"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Path < Node
|
174
|
+
def initialize args = {}
|
175
|
+
args[:d] = "M " + args[:points].map{ |x, y| "#{x} #{y}" }.join(" L ")
|
176
|
+
args[:fill_color] ||= "none"
|
177
|
+
super
|
178
|
+
end
|
179
|
+
def to_s
|
180
|
+
"<path" \
|
181
|
+
" d=\"#{@d}\"" \
|
182
|
+
"#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
|
183
|
+
" fill=\"#{@fill_color}\"" \
|
184
|
+
"/>\n"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class Circle < Node
|
189
|
+
def initialize args = {}
|
190
|
+
args[:r] ||= DEFAULT_SIZE*0.5
|
191
|
+
super
|
192
|
+
end
|
193
|
+
def to_s
|
194
|
+
"<circle" \
|
195
|
+
"#{" cx=\"#{@x}\"" if @x}" \
|
196
|
+
"#{" cy=\"#{@y}\"" if @y}" \
|
197
|
+
" r=\"#{@r}\"" \
|
198
|
+
"#{" fill=\"#{@fill_color}\"" if @fill_color}" \
|
199
|
+
"#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
|
200
|
+
"#{" onmousemove=\"#{@onmousemove}\"" if @onmousemove}" \
|
201
|
+
"#{" onmouseout=\"#{@onmouseout}\"" if @onmouseout}" \
|
202
|
+
"/>\n"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class Rect < Node
|
207
|
+
def initialize args = {}
|
208
|
+
args[:width] ||= DEFAULT_SIZE
|
209
|
+
args[:height] ||= DEFAULT_SIZE
|
210
|
+
super
|
211
|
+
end
|
212
|
+
def to_s
|
213
|
+
"<rect" \
|
214
|
+
"#{" x=\"#{@x1}\"" if @x1}" \
|
215
|
+
"#{" y=\"#{@y1}\"" if @y1}" \
|
216
|
+
" width=\"#{@width}\"" \
|
217
|
+
" height=\"#{@height}\"" \
|
218
|
+
"#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
|
219
|
+
"#{" stroke-width=\"#{@stroke_width}\"" if @stroke_width}" \
|
220
|
+
"#{" fill=\"#{@fill_color}\"" if @fill_color}" \
|
221
|
+
"/>\n"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
module Samples
|
227
|
+
def self.face args = {}
|
228
|
+
args[:scale_x] = args[:scale_y] = args[:size]/DEFAULT_SIZE if args[:size]
|
229
|
+
args[:fill_color] = "yellow"
|
230
|
+
args[:x] = DEFAULT_SIZE/2
|
231
|
+
args[:y] = DEFAULT_SIZE/2
|
232
|
+
args[:stroke_width] = DEFAULT_SIZE/10
|
233
|
+
args[:stroke_color] = :black
|
234
|
+
Group.new(args) <<
|
235
|
+
Circle.new <<
|
236
|
+
Circle.new(x:-0.2*DEFAULT_SIZE, y:-0.2*DEFAULT_SIZE, r:0.1*DEFAULT_SIZE) <<
|
237
|
+
Circle.new(x:+0.2*DEFAULT_SIZE, y:-0.2*DEFAULT_SIZE, r:0.1*DEFAULT_SIZE) <<
|
238
|
+
Line.new(x1:-0.2*DEFAULT_SIZE, y1:+0.2*DEFAULT_SIZE, x2:+0.2*DEFAULT_SIZE, y2:+0.2*DEFAULT_SIZE)
|
239
|
+
end
|
240
|
+
class Grid < Group
|
241
|
+
def initialize args = {}
|
242
|
+
#args[:x2] ||= args[:xn] + args[:x1] ||= 0
|
243
|
+
#args[:y2] ||= args[:yn] + args[:y1] ||= 0
|
244
|
+
args[:fill_color] = "white"
|
245
|
+
super
|
246
|
+
if @xn*(@y2-@y1) > (@x2-@x1)*@yn
|
247
|
+
y = (@x2-@x1)*@yn/@xn
|
248
|
+
@y1 = (DEFAULT_SIZE-y)/2
|
249
|
+
@y2 = (DEFAULT_SIZE+y)/2
|
250
|
+
else
|
251
|
+
x = (@y2-@y1)*@xn/@yn
|
252
|
+
@x1 = (DEFAULT_SIZE-x)/2
|
253
|
+
@x2 = (DEFAULT_SIZE+x)/2
|
254
|
+
end
|
255
|
+
w = (@x2 - @x1)/@xn
|
256
|
+
h = (@y2 - @y1)/@yn
|
257
|
+
[*0...@yn].product([*0...@xn]) do |i,j|
|
258
|
+
self << cell = Group.new(
|
259
|
+
x: @x1+j*w,
|
260
|
+
y: @y1+i*h,
|
261
|
+
scale_x: w/DEFAULT_SIZE,
|
262
|
+
scale_y: h/DEFAULT_SIZE,
|
263
|
+
)
|
264
|
+
cell << Rect.new(stroke_color:"silver", stroke_width:DEFAULT_SIZE/10) << inside = Group.new(
|
265
|
+
x: DEFAULT_SIZE*0.1,
|
266
|
+
y: DEFAULT_SIZE*0.1,
|
267
|
+
scale_x: 0.8,
|
268
|
+
scale_y: 0.8,
|
269
|
+
)
|
270
|
+
yield inside,i,j if block_given?
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
class Plot < Group
|
275
|
+
def initialize args = {}
|
276
|
+
args[:stroke_width] = DEFAULT_SIZE * 0.002
|
277
|
+
args[:fill_color] = "white"
|
278
|
+
super
|
279
|
+
@lines = []
|
280
|
+
end
|
281
|
+
def add_line args = {}
|
282
|
+
args[:data_pairs] = Hash[args[:data_pairs].to_a]
|
283
|
+
@lines << args
|
284
|
+
self
|
285
|
+
end
|
286
|
+
def to_s
|
287
|
+
return @to_s_cached if @to_s_cached
|
288
|
+
ticks = lambda do |basics|
|
289
|
+
if basics.any?{ |i| ! i.is_a? Integer }
|
290
|
+
[
|
291
|
+
basics.uniq,
|
292
|
+
-> x { DEFAULT_SIZE * basics.index(x) / (basics.size-1) }
|
293
|
+
]
|
294
|
+
else
|
295
|
+
min, max = basics.minmax
|
296
|
+
[
|
297
|
+
[min, *(1...8).map{ |i| (max-min)*i/8 }, max],
|
298
|
+
-> x { DEFAULT_SIZE * (x - min) / (max - min) }
|
299
|
+
]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
ticks_x, f_x = ticks[ @ticks_x || @lines.flat_map{ |line| line[:data_pairs].keys } ]
|
303
|
+
ticks_y, f_y = ticks[ @lines.flat_map{ |line| line[:data_pairs].values } ]
|
304
|
+
top, left, scale = 0.05, 0.1
|
305
|
+
size = DEFAULT_SIZE * 0.03
|
306
|
+
self << field = Group.new(id:"TODOfield", x: DEFAULT_SIZE * left, y: DEFAULT_SIZE * top, scale: 0.7)
|
307
|
+
hsv2rgb = lambda do |h,s,v|
|
308
|
+
vmin = (100-s)*v/100
|
309
|
+
a = (v - vmin)*(h % 60)/60.0
|
310
|
+
vinc = vmin + a
|
311
|
+
vdec = v - a
|
312
|
+
case h/60
|
313
|
+
when 0 ; [v, vinc, vmin]
|
314
|
+
when 1 ; [vdec, v, vmin]
|
315
|
+
when 2 ; [vmin, v, vinc]
|
316
|
+
when 3 ; [vmin, vdec, v]
|
317
|
+
when 4 ; [vinc, vmin, v]
|
318
|
+
when 5 ; [v, vmin, vdec]
|
319
|
+
end.map{ |i| (2.55 * i).floor }
|
320
|
+
end
|
321
|
+
colors = (0...@lines.size).map{ |i| ?# + hsv2rgb[i*360/@lines.size, 100, 100].pack("C*").unpack("H*").first }
|
322
|
+
@lines.zip(colors).each_with_index do |(line, color), i|
|
323
|
+
#@@id = defined?(@@id) ? @@id + 1 : 0
|
324
|
+
field << (Text.new(
|
325
|
+
#id: @@id,
|
326
|
+
fill_color: "black",# color.tr("0123456789abcdef", "fedcba9876543210"),
|
327
|
+
stroke_color: "none",
|
328
|
+
size: DEFAULT_SIZE * 0.03,
|
329
|
+
x: DEFAULT_SIZE * 0.12,
|
330
|
+
y: DEFAULT_SIZE * (0.05 + i * 0.03),
|
331
|
+
anchor: "begin",
|
332
|
+
baseline: "text-before-edge",
|
333
|
+
) << line[:title])
|
334
|
+
field << Rect.new(
|
335
|
+
x1: DEFAULT_SIZE * 0.05,
|
336
|
+
y1: DEFAULT_SIZE * (0.05 + i * 0.03),
|
337
|
+
width: DEFAULT_SIZE * 0.05,
|
338
|
+
height: DEFAULT_SIZE * 0.03,
|
339
|
+
fill_color: color,
|
340
|
+
)
|
341
|
+
end
|
342
|
+
@lines.zip(colors).each_with_index do |(line, color), i|
|
343
|
+
field << Path.new(
|
344
|
+
stroke_color: color,
|
345
|
+
points: line[:data_pairs].map{ |k,v| [f_x[k], DEFAULT_SIZE-f_y[v]] }
|
346
|
+
)
|
347
|
+
end
|
348
|
+
field << Text.new(
|
349
|
+
id: "tooltip",
|
350
|
+
visibility: "hidden",
|
351
|
+
baseline: "before-edge",
|
352
|
+
size: DEFAULT_SIZE * 0.03,
|
353
|
+
stroke_color: "none",
|
354
|
+
)
|
355
|
+
field << (Script.new(id: "TODOscript") << "
|
356
|
+
var svg = document.getElementById(\"TODOscript\").ownerSVGElement;
|
357
|
+
svg.tooltip = svg.getElementById(\"tooltip\");
|
358
|
+
function show_tooltip(evt, text, x, y) {
|
359
|
+
var tooltip = evt.target.ownerSVGElement.tooltip;
|
360
|
+
tooltip.setAttributeNS(null, \"x\", x);
|
361
|
+
tooltip.setAttributeNS(null, \"y\", y);
|
362
|
+
tooltip.textContent = text;
|
363
|
+
tooltip.setAttributeNS(null, \"visibility\", \"visible\");
|
364
|
+
}
|
365
|
+
function hide_tooltip(evt) { evt.target.ownerSVGElement.tooltip.setAttributeNS(null, \"visibility\", \"hidden\"); }
|
366
|
+
")
|
367
|
+
@lines.zip(colors) do |line, color| line[:data_pairs].each do |k,v|
|
368
|
+
x = f_x[k]
|
369
|
+
y = DEFAULT_SIZE - f_y[v]
|
370
|
+
field << Circle.new(
|
371
|
+
fill_color: color,
|
372
|
+
stroke_color: color,
|
373
|
+
x: x,
|
374
|
+
y: y,
|
375
|
+
r: DEFAULT_SIZE * 0.005,
|
376
|
+
onmousemove: "show_tooltip(evt, '#{k}/#{v} - #{line[:title]}', #{x}, #{y - DEFAULT_SIZE*0.025})",
|
377
|
+
onmouseout: "hide_tooltip(evt)",
|
378
|
+
)
|
379
|
+
end end
|
380
|
+
field << Rect.new(fill_color:"none")
|
381
|
+
ticks_x.each_with_index{ |tick, i| _ = f_x[tick]
|
382
|
+
field << Line.new(y1: DEFAULT_SIZE * 1.025, x1:_, x2:_)
|
383
|
+
field << tick_group = Group.new(x: _, y: DEFAULT_SIZE * 1.025, rotate: 90)
|
384
|
+
tick_group << (Text.new(x: 0, y: 0, size: size,
|
385
|
+
#baseline: "text-before-edge",
|
386
|
+
anchor: "begin",
|
387
|
+
#style: "writing-mode: tb;",
|
388
|
+
) << tick.to_s)
|
389
|
+
}
|
390
|
+
ticks_y.each{ |tick| _ = f_y[tick]
|
391
|
+
field << Line.new(x2: -DEFAULT_SIZE * 0.025, y1:_, y2:_) <<
|
392
|
+
(Text.new(y: DEFAULT_SIZE - _, x: -DEFAULT_SIZE * left * 0.3, size: size, anchor: "end") << tick.to_s)
|
393
|
+
}
|
394
|
+
@to_s_cached = super
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
if __FILE__ == $0
|
403
|
+
puts `ruby -Cexamples chart.rb`
|
404
|
+
end
|
405
|
+
|
metadata
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rsvgr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Victor Maslov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: not ready for public usage -- uploaded for personal usage
|
14
|
+
email: nakilon@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/rsvgr.rb
|
20
|
+
homepage: http://rubygems.org/gems/rsvgr
|
21
|
+
licenses:
|
22
|
+
- MIT
|
23
|
+
metadata: {}
|
24
|
+
post_install_message:
|
25
|
+
rdoc_options: []
|
26
|
+
require_paths:
|
27
|
+
- lib
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.0
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
requirements: []
|
39
|
+
rubyforge_project:
|
40
|
+
rubygems_version: 2.0.14
|
41
|
+
signing_key:
|
42
|
+
specification_version: 4
|
43
|
+
summary: Ruby SVG Renderer.
|
44
|
+
test_files: []
|