technical_graph 0.3.2 → 0.4.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/DOCUMENTATION.md +14 -4
- data/DOCUMENTATION.textile +68 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/VERSION +1 -1
- data/lib/technical_graph/array.rb +25 -0
- data/lib/technical_graph/data_layer.rb +45 -7
- data/lib/technical_graph/data_layer_processor.rb +4 -1
- data/lib/technical_graph/data_layer_processor_noise_removal.rb +4 -23
- data/lib/technical_graph/data_layer_processor_simple_smoother.rb +9 -3
- data/lib/technical_graph/graph_axis.rb +69 -217
- data/lib/technical_graph/graph_color_library.rb +38 -22
- data/lib/technical_graph/graph_data_processor.rb +13 -1
- data/lib/technical_graph/graph_image_drawer.rb +97 -114
- data/lib/technical_graph/graph_image_drawer_module.rb +72 -0
- data/lib/technical_graph/graph_image_drawer_rasem.rb +185 -0
- data/lib/technical_graph/graph_image_drawer_rmagick.rb +237 -0
- data/lib/technical_graph.rb +23 -8
- data/test/helper.rb +4 -0
- data/test/test_technical_autocolor.rb +2 -2
- data/test/test_technical_axis_enlarge.rb +2 -3
- data/test/test_technical_graph.rb +4 -3
- data/test/test_technical_graph_axis.rb +2 -2
- data/test/test_technical_multilayer.rb +2 -2
- data/test/test_technical_rasem.rb +22 -0
- data/test/test_technical_readme.rb +39 -18
- data/test/test_technical_simple_graph.rb +2 -2
- data/test/test_technical_smoother.rb +2 -2
- data/test/test_technical_smoother_adv.rb +15 -5
- metadata +32 -14
@@ -1,8 +1,7 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
|
-
require 'RMagick'
|
5
3
|
require 'date'
|
4
|
+
require 'zlib'
|
6
5
|
|
7
6
|
# Universal class for creating graphs/charts.
|
8
7
|
|
@@ -16,6 +15,24 @@ require 'date'
|
|
16
15
|
|
17
16
|
class GraphImageDrawer
|
18
17
|
|
18
|
+
# Which type of drawing class use?
|
19
|
+
def drawing_class
|
20
|
+
if options[:drawer_class] == :rasem
|
21
|
+
require 'technical_graph/graph_image_drawer_rasem'
|
22
|
+
return GraphImageDrawerRasem
|
23
|
+
end
|
24
|
+
|
25
|
+
if options[:drawer_class] == :rmagick
|
26
|
+
require 'technical_graph/graph_image_drawer_rmagick'
|
27
|
+
return GraphImageDrawerRmagick
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Best output image format, used for testing
|
32
|
+
def best_output_format
|
33
|
+
@technical_graph.best_output_format
|
34
|
+
end
|
35
|
+
|
19
36
|
attr_reader :technical_graph
|
20
37
|
|
21
38
|
# Accessor for options Hash
|
@@ -38,6 +55,10 @@ class GraphImageDrawer
|
|
38
55
|
@technical_graph.axis
|
39
56
|
end
|
40
57
|
|
58
|
+
def logger
|
59
|
+
@technical_graph.logger
|
60
|
+
end
|
61
|
+
|
41
62
|
def truncate_string
|
42
63
|
options[:truncate_string]
|
43
64
|
end
|
@@ -54,18 +75,25 @@ class GraphImageDrawer
|
|
54
75
|
def initialize(technical_graph)
|
55
76
|
@technical_graph = technical_graph
|
56
77
|
|
78
|
+
t = Time.now
|
79
|
+
|
80
|
+
# drawer type
|
81
|
+
#options[:drawer_class] ||= :rmagick
|
82
|
+
options[:drawer_class] ||= :rasem
|
83
|
+
|
57
84
|
options[:width] ||= DEFAULT_WIDTH
|
58
85
|
options[:height] ||= DEFAULT_HEIGHT
|
59
86
|
|
87
|
+
options[:axis_value_and_param_labels] = true if options[:axis_value_and_param_labels].nil?
|
88
|
+
options[:axis_zero_labels] = true if options[:axis_zero_labels].nil?
|
89
|
+
|
60
90
|
# colors
|
61
91
|
options[:background_color] ||= 'white'
|
62
92
|
options[:background_hatch_color] ||= 'lightcyan2'
|
63
|
-
options[:axis_color] ||= '#
|
93
|
+
options[:axis_color] ||= '#000000'
|
64
94
|
|
65
95
|
# antialias
|
66
|
-
options[:
|
67
|
-
options[:axis_antialias] = false if options[:axis_antialias].nil?
|
68
|
-
options[:font_antialias] = false if options[:font_antialias].nil?
|
96
|
+
options[:antialias] = false if options[:antialias].nil?
|
69
97
|
|
70
98
|
# font sizes
|
71
99
|
options[:axis_font_size] ||= 10
|
@@ -82,6 +110,9 @@ class GraphImageDrawer
|
|
82
110
|
|
83
111
|
# array of all points drawn on graph, used for auto positioning of legend
|
84
112
|
@drawn_points = Array.new
|
113
|
+
|
114
|
+
logger.debug "initializing #{self.class}"
|
115
|
+
logger.debug " TIME COST #{Time.now - t}"
|
85
116
|
end
|
86
117
|
|
87
118
|
def width
|
@@ -100,8 +131,8 @@ class GraphImageDrawer
|
|
100
131
|
options[:height] = h.to_i if h.to_i > 0
|
101
132
|
end
|
102
133
|
|
103
|
-
def
|
104
|
-
options[:
|
134
|
+
def antialias
|
135
|
+
options[:antialias] == true
|
105
136
|
end
|
106
137
|
|
107
138
|
def draw_legend?
|
@@ -152,48 +183,17 @@ class GraphImageDrawer
|
|
152
183
|
# Create background image
|
153
184
|
def crate_blank_graph_image
|
154
185
|
pre_image_create_calculations
|
155
|
-
|
156
|
-
@
|
157
|
-
@image.new_image(
|
158
|
-
width,
|
159
|
-
height,
|
160
|
-
Magick::HatchFill.new(
|
161
|
-
options[:background_color],
|
162
|
-
options[:background_hatch_color]
|
163
|
-
)
|
164
|
-
)
|
165
|
-
|
166
|
-
return @image
|
186
|
+
# create drawing proxy
|
187
|
+
@drawer = drawing_class.new(self)
|
167
188
|
end
|
168
189
|
|
169
|
-
attr_reader :
|
190
|
+
attr_reader :drawer
|
170
191
|
|
171
|
-
# Render data layer
|
192
|
+
# Render data layer, calculate coords and execute proxy
|
172
193
|
def render_data_layer(l)
|
173
194
|
layer_data = l.processed_data
|
174
195
|
|
175
|
-
|
176
|
-
layer_text = Magick::Draw.new
|
177
|
-
|
178
|
-
# global layer antialias can be override using layer option
|
179
|
-
layer_antialias = l.antialias
|
180
|
-
layer_antialias = options[:layers_antialias] if layer_antialias.nil?
|
181
|
-
|
182
|
-
layer_line.stroke_antialias(layer_antialias)
|
183
|
-
layer_line.fill(l.color)
|
184
|
-
layer_line.fill_opacity(1)
|
185
|
-
layer_line.stroke(l.color)
|
186
|
-
layer_line.stroke_opacity(1.0)
|
187
|
-
layer_line.stroke_width(1.0)
|
188
|
-
layer_line.stroke_linecap('square')
|
189
|
-
layer_line.stroke_linejoin('miter')
|
190
|
-
|
191
|
-
layer_text.text_antialias(font_antialias)
|
192
|
-
layer_text.pointsize(options[:layers_font_size])
|
193
|
-
layer_text.font_family('helvetica')
|
194
|
-
layer_text.font_style(Magick::NormalStyle)
|
195
|
-
layer_text.text_align(Magick::LeftAlign)
|
196
|
-
layer_text.text_undercolor(options[:background_color])
|
196
|
+
t = Time.now
|
197
197
|
|
198
198
|
# calculate coords, draw text, and then lines and circles
|
199
199
|
coords = Array.new
|
@@ -216,44 +216,19 @@ class GraphImageDrawer
|
|
216
216
|
}
|
217
217
|
end
|
218
218
|
|
219
|
-
#
|
220
|
-
|
221
|
-
|
222
|
-
string_label = "#{truncate_string % c[:dy]}"
|
223
|
-
layer_text.text(
|
224
|
-
c[:ax] + 5, c[:ay],
|
225
|
-
string_label
|
226
|
-
)
|
227
|
-
end
|
228
|
-
layer_text.draw(@image)
|
229
|
-
end
|
219
|
+
logger.debug "rendering layer of size #{layer_data.size}, bitmap position calculation"
|
220
|
+
logger.debug " TIME COST #{Time.now - t}"
|
221
|
+
t = Time.now
|
230
222
|
|
231
|
-
#
|
232
|
-
|
233
|
-
|
234
|
-
layer_line.circle(
|
235
|
-
c[:ax], c[:ay],
|
236
|
-
c[:ax] + 3, c[:ay]
|
237
|
-
)
|
238
|
-
layer_line.circle(
|
239
|
-
c[:bx], c[:by],
|
240
|
-
c[:bx] + 3, c[:by]
|
241
|
-
)
|
242
|
-
|
243
|
-
# line
|
244
|
-
layer_line.line(
|
245
|
-
c[:ax], c[:ay],
|
246
|
-
c[:bx], c[:by]
|
247
|
-
)
|
248
|
-
|
249
|
-
# used for auto positioning of legend
|
250
|
-
if legend_auto_position
|
251
|
-
@drawn_points << { :x => c[:ax], :y => c[:ay] }
|
252
|
-
@drawn_points << { :x => c[:bx], :y => c[:by] }
|
253
|
-
end
|
254
|
-
end
|
255
|
-
layer_line.draw(@image)
|
223
|
+
# draw using proxy
|
224
|
+
drawer.render_data_layer(l, coords)
|
225
|
+
end
|
256
226
|
|
227
|
+
# Used for auto position for legend
|
228
|
+
def post_dot_drawn(bx, by)
|
229
|
+
if legend_auto_position
|
230
|
+
@drawn_points << { :x => bx, :y => by }
|
231
|
+
end
|
257
232
|
end
|
258
233
|
|
259
234
|
# height of 1 layer
|
@@ -262,7 +237,7 @@ class GraphImageDrawer
|
|
262
237
|
# Choose best location
|
263
238
|
def recalculate_legend_position
|
264
239
|
return unless legend_auto_position
|
265
|
-
|
240
|
+
logger.debug "Auto position calculation, drawn points #{@drawn_points.size}"
|
266
241
|
|
267
242
|
legend_height = layers.size * ONE_LAYER_LEGEND_HEIGHT
|
268
243
|
|
@@ -278,6 +253,8 @@ class GraphImageDrawer
|
|
278
253
|
{ :x => width - legend_margin - legend_width, :y => height - legend_margin - legend_height }, # bottom-right
|
279
254
|
]
|
280
255
|
|
256
|
+
t = Time.now
|
257
|
+
|
281
258
|
# calculate nearest distance of all drawn points
|
282
259
|
positions.each do |p|
|
283
260
|
p[:distance] = (width ** 2 + height ** 2) ** 0.5 # max distance, diagonal of graph
|
@@ -291,71 +268,77 @@ class GraphImageDrawer
|
|
291
268
|
end
|
292
269
|
end
|
293
270
|
|
294
|
-
|
271
|
+
logger.debug "auto legend best position distance calc."
|
272
|
+
logger.debug " TIME COST #{Time.now - t}"
|
273
|
+
t = Time.now
|
274
|
+
|
275
|
+
# chose position with highest distance
|
295
276
|
positions.sort! { |a, b| a[:distance] <=> b[:distance] }
|
296
277
|
best_position = positions.last
|
297
278
|
options[:legend_x] = best_position[:x]
|
298
279
|
options[:legend_y] = best_position[:y]
|
299
280
|
|
300
|
-
|
301
|
-
#
|
281
|
+
logger.debug "Best position x #{options[:legend_x]}, y #{options[:legend_y]}, distance #{best_position[:distance]}"
|
282
|
+
logger.debug " TIME COST #{Time.now - t}"
|
302
283
|
end
|
303
284
|
|
304
285
|
# Render legend on graph
|
305
286
|
def render_data_legend
|
306
287
|
return unless draw_legend?
|
307
|
-
|
308
288
|
recalculate_legend_position
|
309
289
|
|
310
|
-
legend_text = Magick::Draw.new
|
311
|
-
legend_text_antialias = options[:layers_font_size]
|
312
|
-
legend_text.stroke_antialias(legend_text_antialias)
|
313
|
-
legend_text.text_antialias(legend_text_antialias)
|
314
|
-
legend_text.pointsize(options[:axis_font_size])
|
315
|
-
legend_text.font_family('helvetica')
|
316
|
-
legend_text.font_style(Magick::NormalStyle)
|
317
|
-
legend_text.text_align(Magick::LeftAlign)
|
318
|
-
legend_text.text_undercolor(options[:background_color])
|
319
|
-
|
320
290
|
x = legend_x
|
321
291
|
y = legend_y
|
322
292
|
|
323
|
-
|
324
|
-
legend_text.fill(l.color)
|
325
|
-
|
326
|
-
string_label = l.label
|
327
|
-
legend_text.text(
|
328
|
-
x, y,
|
329
|
-
string_label
|
330
|
-
)
|
293
|
+
legend_data = Array.new
|
331
294
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
295
|
+
layers.each do |l|
|
296
|
+
h = Hash.new
|
297
|
+
h[:color] = l.color
|
298
|
+
h[:label] = l.label
|
299
|
+
h[:x] = x
|
300
|
+
h[:y] = y
|
337
301
|
|
302
|
+
legend_data << h
|
338
303
|
y += ONE_LAYER_LEGEND_HEIGHT
|
339
304
|
end
|
340
|
-
|
305
|
+
|
306
|
+
drawer.legend(legend_data)
|
341
307
|
end
|
342
308
|
|
343
309
|
# Save output to file
|
344
310
|
def save_to_file(file)
|
345
|
-
|
311
|
+
t = Time.now
|
312
|
+
|
313
|
+
drawer.save(file)
|
314
|
+
|
315
|
+
logger.debug "saving image"
|
316
|
+
logger.debug " TIME COST #{Time.now - t}"
|
346
317
|
end
|
347
318
|
|
348
319
|
# Export image
|
349
320
|
def to_format(format)
|
350
|
-
|
351
|
-
|
352
|
-
|
321
|
+
t = Time.now
|
322
|
+
|
323
|
+
blob = drawer.to_format(format)
|
324
|
+
|
325
|
+
logger.debug "exporting image as string"
|
326
|
+
logger.debug " TIME COST #{Time.now - t}"
|
327
|
+
|
328
|
+
return blob
|
353
329
|
end
|
354
330
|
|
355
331
|
# Return binary PNG
|
356
332
|
def to_png
|
357
|
-
|
333
|
+
drawer.to_png
|
334
|
+
end
|
335
|
+
|
336
|
+
def to_svg
|
337
|
+
drawer.to_svg
|
358
338
|
end
|
359
339
|
|
340
|
+
def to_svgz
|
341
|
+
drawer.to_svgz
|
342
|
+
end
|
360
343
|
|
361
344
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module GraphImageDrawerModule
|
4
|
+
|
5
|
+
def initialize(drawer)
|
6
|
+
@drawer = drawer
|
7
|
+
create_blank_image
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :drawer
|
11
|
+
|
12
|
+
def width
|
13
|
+
drawer.width
|
14
|
+
end
|
15
|
+
|
16
|
+
def height
|
17
|
+
drawer.height
|
18
|
+
end
|
19
|
+
|
20
|
+
def truncate_string
|
21
|
+
drawer.truncate_string
|
22
|
+
end
|
23
|
+
|
24
|
+
def options
|
25
|
+
drawer.options
|
26
|
+
end
|
27
|
+
|
28
|
+
def logger
|
29
|
+
drawer.logger
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Draw one or many axis
|
34
|
+
def x_axis(x_array, options = { :color => 'black', :width => 1 })
|
35
|
+
axis(x_array, [], options)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Draw one or many axis
|
39
|
+
def y_axis(y_array, options = { :color => 'black', :width => 1 })
|
40
|
+
axis([], y_array, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return binary PNG
|
44
|
+
def to_png
|
45
|
+
to_format('png')
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_svg
|
49
|
+
to_format('svg')
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_svgz
|
53
|
+
drawer.deflate_string( to_format('svg') )
|
54
|
+
end
|
55
|
+
|
56
|
+
def deflate_string(str, level = 9)
|
57
|
+
z = Zlib::Deflate.new(level)
|
58
|
+
dst = z.deflate(str, Zlib::FINISH)
|
59
|
+
z.close
|
60
|
+
dst
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_from_filename(file)
|
64
|
+
file.gsub(/^.*\./, '')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Used for creating temp files
|
68
|
+
def random_filename
|
69
|
+
(0...16).map{65.+(rand(25)).chr}.join
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require 'technical_graph/graph_image_drawer_module'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rasem'
|
6
|
+
require 'tmpdir'
|
7
|
+
|
8
|
+
class GraphImageDrawerRasem
|
9
|
+
include GraphImageDrawerModule
|
10
|
+
|
11
|
+
# Initialize blank image
|
12
|
+
def create_blank_image
|
13
|
+
@image = Rasem::SVGImage.new(drawer.width, drawer.height)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Draw both array axis
|
17
|
+
def axis(x_array, y_array, _options = { :color => 'black', :width => 1 }, render_labels = false, x_labels = [], y_labels = [])
|
18
|
+
# for single axis
|
19
|
+
x_array = [x_array] if not x_array.kind_of? Array
|
20
|
+
y_array = [y_array] if not y_array.kind_of? Array
|
21
|
+
|
22
|
+
_s = self
|
23
|
+
|
24
|
+
@image.group :stroke => _options[:color], :stroke_width => _options[:width] do
|
25
|
+
x_array.each_with_index do |x, i|
|
26
|
+
line(x, 0, x, _s.height, { })
|
27
|
+
|
28
|
+
# labels
|
29
|
+
label = x_labels[i]
|
30
|
+
if render_labels and not label.nil?
|
31
|
+
label = "#{_s.truncate_string % label}"
|
32
|
+
text(x + 15, _s.height - 15, label, { })
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
y_array.each_with_index do |y, i|
|
37
|
+
line(0, y, _s.width, y, { })
|
38
|
+
|
39
|
+
# labels
|
40
|
+
label = y_labels[i]
|
41
|
+
if render_labels and not label.nil?
|
42
|
+
label = "#{_s.truncate_string % label}"
|
43
|
+
text(15, y + 15, label, { })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Label for parameters and values
|
50
|
+
def axis_labels(parameter_label, value_label, _options = { :color => 'black', :width => 1, :size => 20 })
|
51
|
+
_s = self
|
52
|
+
@image.group :stroke => _options[:color], :stroke_width => _options[:width] do
|
53
|
+
text(
|
54
|
+
(_s.width / 2).to_i,
|
55
|
+
_s.height - 40,
|
56
|
+
parameter_label, { 'font-size' => "#{_options[:size]}px" }
|
57
|
+
)
|
58
|
+
|
59
|
+
text(
|
60
|
+
(_s.height / 2).to_i,
|
61
|
+
-40,
|
62
|
+
value_label, { :transform => 'rotate(90 0,0)', 'font-size' => "#{_options[:size]}px" }
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def render_data_layer(l, coords)
|
68
|
+
_s = self
|
69
|
+
_l = l
|
70
|
+
_coords = coords
|
71
|
+
|
72
|
+
# value labels
|
73
|
+
if l.value_labels
|
74
|
+
t = Time.now
|
75
|
+
|
76
|
+
@image.group :stroke => _s.options[:axis_color], :stroke_width => 1 do
|
77
|
+
_coords.each do |c|
|
78
|
+
string_label = "#{_s.truncate_string % c[:dy]}"
|
79
|
+
text(
|
80
|
+
c[:ax] + 5, c[:ay],
|
81
|
+
string_label
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
logger.debug "labels"
|
87
|
+
logger.debug " TIME COST #{Time.now - t}"
|
88
|
+
end
|
89
|
+
|
90
|
+
t = Time.now
|
91
|
+
|
92
|
+
# lines and dots
|
93
|
+
@image.group :stroke => l.color, :stroke_width => 1 do
|
94
|
+
_coords.each do |c|
|
95
|
+
# additional circle
|
96
|
+
circle(c[:ax], c[:ay], 2, { :fill => l.color })
|
97
|
+
circle(c[:bx], c[:by], 2, { :fill => l.color })
|
98
|
+
# line
|
99
|
+
line(
|
100
|
+
c[:ax], c[:ay],
|
101
|
+
c[:bx], c[:by],
|
102
|
+
{ }
|
103
|
+
)
|
104
|
+
|
105
|
+
_s.drawer.post_dot_drawn(c[:ax], c[:ay])
|
106
|
+
_s.drawer.post_dot_drawn(c[:bx], c[:by])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
logger.debug "dots and lines"
|
111
|
+
logger.debug " TIME COST #{Time.now - t}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def legend(legend_data)
|
115
|
+
_s = self
|
116
|
+
|
117
|
+
@image.group :stroke_width => 1, :stroke => '' do
|
118
|
+
legend_data.each do |l|
|
119
|
+
circle(l[:x], l[:y], 2, { :stroke => l[:color], :fill => l[:color] })
|
120
|
+
text(l[:x] + 5, l[:y], l[:label], { :stroke => l[:color], :fill => l[:color] })
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Needed before saving?
|
126
|
+
def close
|
127
|
+
@image.close if not closed?
|
128
|
+
@closed = true
|
129
|
+
end
|
130
|
+
|
131
|
+
def closed?
|
132
|
+
@closed
|
133
|
+
end
|
134
|
+
|
135
|
+
# Save to file, convert when needed
|
136
|
+
def save(file)
|
137
|
+
close
|
138
|
+
|
139
|
+
format = format_from_filename(file)
|
140
|
+
case format
|
141
|
+
when 'svg' then
|
142
|
+
string = to_svg
|
143
|
+
when 'svgz' then
|
144
|
+
string = to_svgz
|
145
|
+
else
|
146
|
+
# ugly hack, save to svg and then convert using image magick
|
147
|
+
tmp_file = file.gsub(/#{format}/, 'svg')
|
148
|
+
# save to svg
|
149
|
+
save(tmp_file)
|
150
|
+
# convert
|
151
|
+
`convert "#{tmp_file}" "#{file}"`
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
File.open(file, 'w') do |f|
|
156
|
+
f << string
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def to_format(format)
|
162
|
+
close
|
163
|
+
|
164
|
+
return @image.output if format == 'svg'
|
165
|
+
return to_svgz if format == 'svgz'
|
166
|
+
|
167
|
+
#raise 'Not implemented' if not format == 'svg'
|
168
|
+
return ugly_convert(format)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Ugly, save temporary file, convert, read, delete temp file
|
172
|
+
def ugly_convert(format)
|
173
|
+
# create temp file
|
174
|
+
tmp_file = File.join(Dir.tmpdir, "#{random_filename}.#{format}")
|
175
|
+
save(tmp_file)
|
176
|
+
# read content
|
177
|
+
contents = open(tmp_file, "rb") { |io| io.read }
|
178
|
+
# remove temp file
|
179
|
+
File.delete(tmp_file)
|
180
|
+
|
181
|
+
# return content
|
182
|
+
contents
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|