playfair 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/playfair.rb +5 -0
- data/lib/playfair/chart.rb +30 -0
- data/lib/playfair/google_chart_renderer.rb +38 -0
- data/lib/playfair/pdf_renderer.rb +195 -0
- data/lib/playfair/renderer.rb +36 -0
- data/lib/playfair/version.rb +3 -0
- metadata +72 -0
data/lib/playfair.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Playfair
|
2
|
+
class Chart < Array
|
3
|
+
|
4
|
+
attr_accessor :title
|
5
|
+
attr_accessor :renderer
|
6
|
+
|
7
|
+
def initialize(&block)
|
8
|
+
@renderer = GoogleChartRenderer
|
9
|
+
instance_eval &block
|
10
|
+
end
|
11
|
+
|
12
|
+
def value(value,label="")
|
13
|
+
push Value.new(value,label)
|
14
|
+
end
|
15
|
+
|
16
|
+
def values
|
17
|
+
collect { |value| value.value }
|
18
|
+
end
|
19
|
+
|
20
|
+
def labels
|
21
|
+
collect { |value| value.label }
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(type, options={})
|
25
|
+
renderer.new(self, options).render(type)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Value = Struct.new(:value, :label)
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Playfair
|
4
|
+
class GoogleChartRenderer < Renderer
|
5
|
+
|
6
|
+
API_URL = "http://chart.apis.google.com/chart"
|
7
|
+
|
8
|
+
render :bar_chart do
|
9
|
+
chart_url "cht=bvs&chd=t:#{values.join(',')}&chxt=x,y&chxl=0:|#{labels.join('|')}"
|
10
|
+
end
|
11
|
+
|
12
|
+
render :line_chart do
|
13
|
+
chart_url "cht=lc&chd=t:#{values.join(',')}&chxt=x,y&chxl=0:|#{labels.join('|')}"
|
14
|
+
end
|
15
|
+
|
16
|
+
render :pie_chart do
|
17
|
+
chart_url "cht=p&chd=t:#{values.join(',')}&chl=#{labels.join('|')}"
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def chart_url(chart_params)
|
23
|
+
rendered_chart = "#{API_URL}?#{chart_params}"
|
24
|
+
rendered_chart << "&chs=#{width}x#{height}"
|
25
|
+
rendered_chart << "&chtt=#{title}" if title
|
26
|
+
rendered_chart
|
27
|
+
end
|
28
|
+
|
29
|
+
def labels
|
30
|
+
source.collect { |v| CGI.escape(v.label.to_s) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
CGI.escape(super) if super
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'prawn'
|
2
|
+
|
3
|
+
module Playfair
|
4
|
+
class PDFRenderer < Renderer
|
5
|
+
|
6
|
+
attr_reader :margin, :gutter, :font_size, :label_width, :text_color, :stroke_color, :output_filename
|
7
|
+
|
8
|
+
COLORS = ["E213A9","F1A637","B55EF0","42D13E", "F50000","DDE93D","7BD2F9","F78A64"]
|
9
|
+
|
10
|
+
def initialize(chart, options={})
|
11
|
+
@margin = options[:margin] || 15
|
12
|
+
@gutter = options[:gutter] || 15
|
13
|
+
@font_size = options[:font_size] || 7
|
14
|
+
@label_width = options[:label_width] || 20
|
15
|
+
@text_color = options[:text_color] || "333333"
|
16
|
+
@stroke_color = options[:stroke_color] || "333333"
|
17
|
+
@output_filename = options[:to] || "chart.pdf"
|
18
|
+
super(chart, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
render :bar_chart do
|
22
|
+
draw_chart do |pdf|
|
23
|
+
x_position = starting_x_position
|
24
|
+
|
25
|
+
values.each_with_index do |value, i|
|
26
|
+
# Draw bar
|
27
|
+
pdf.fill_color color(i)
|
28
|
+
bar_height = height_for_value(value, values.max)
|
29
|
+
pdf.fill_and_stroke_rounded_rectangle [x_position, bar_height + bottom_margin],
|
30
|
+
bar_width, bar_height, 2
|
31
|
+
|
32
|
+
# Draw label
|
33
|
+
pdf.fill_color text_color
|
34
|
+
text_with_shadow pdf, labels[i],
|
35
|
+
:align => :center,
|
36
|
+
:at => [x_position, margin + font_size],
|
37
|
+
:width => bar_width,
|
38
|
+
:overflow => :shrink_to_fit
|
39
|
+
|
40
|
+
x_position += bar_width + gutter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
render :line_chart do
|
46
|
+
draw_chart do |pdf|
|
47
|
+
x_position = starting_x_position
|
48
|
+
values.each_with_index do |value, i|
|
49
|
+
# Draw line
|
50
|
+
if values[i + 1]
|
51
|
+
pdf.stroke_line x_position,
|
52
|
+
height_for_value(value, values.max) + bottom_margin,
|
53
|
+
x_position + point_interval,
|
54
|
+
height_for_value(values[i + 1], values.max) + bottom_margin
|
55
|
+
x_position += point_interval
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Draw points
|
60
|
+
x_position = starting_x_position
|
61
|
+
values.each do |value|
|
62
|
+
pdf.fill_circle_at [x_position, height_for_value(value, values.max) + bottom_margin], :radius => 3
|
63
|
+
x_position += point_interval
|
64
|
+
end
|
65
|
+
|
66
|
+
# Draw labels
|
67
|
+
x_position = starting_x_position
|
68
|
+
values.each_with_index do |value, i|
|
69
|
+
pdf.fill_color text_color
|
70
|
+
text_with_shadow pdf, labels[i],
|
71
|
+
:align => :center,
|
72
|
+
:at => [x_position - point_interval/2, margin + font_size],
|
73
|
+
:width => point_interval,
|
74
|
+
:overflow => :shrink_to_fit
|
75
|
+
x_position += point_interval
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def chart_width
|
83
|
+
right_margin = margin
|
84
|
+
|
85
|
+
width - left_margin - right_margin
|
86
|
+
end
|
87
|
+
|
88
|
+
def chart_height
|
89
|
+
height - top_margin - bottom_margin
|
90
|
+
end
|
91
|
+
|
92
|
+
def top_margin
|
93
|
+
top = margin
|
94
|
+
top += font_size + margin if title
|
95
|
+
top
|
96
|
+
end
|
97
|
+
|
98
|
+
def left_margin
|
99
|
+
margin + label_width + 3
|
100
|
+
end
|
101
|
+
|
102
|
+
def bottom_margin
|
103
|
+
margin + font_size + 3
|
104
|
+
end
|
105
|
+
|
106
|
+
def starting_x_position
|
107
|
+
left_margin + gutter + ((chart_width - gutter)%values.size)/2
|
108
|
+
end
|
109
|
+
|
110
|
+
def bar_width
|
111
|
+
((chart_width - gutter)/values.size) - gutter
|
112
|
+
end
|
113
|
+
|
114
|
+
def point_interval
|
115
|
+
(chart_width - gutter*2)/(values.size - 1)
|
116
|
+
end
|
117
|
+
|
118
|
+
def draw_chart
|
119
|
+
pdf = Prawn::Document.new(:page_size => [width, height], :margin => 0)
|
120
|
+
pdf.line_width 1.2
|
121
|
+
pdf.stroke_color stroke_color
|
122
|
+
|
123
|
+
draw_title(pdf) if title
|
124
|
+
draw_y_axis(pdf)
|
125
|
+
yield(pdf)
|
126
|
+
draw_x_axis(pdf)
|
127
|
+
|
128
|
+
pdf.render_file output_filename
|
129
|
+
end
|
130
|
+
|
131
|
+
def draw_title(pdf)
|
132
|
+
pdf.font_size font_size * 1.6
|
133
|
+
text_with_shadow pdf,
|
134
|
+
title,
|
135
|
+
:at => [left_margin, height - margin],
|
136
|
+
:width => chart_width,
|
137
|
+
:align => :center,
|
138
|
+
:style => :bold,
|
139
|
+
:single_line => true
|
140
|
+
pdf.font_size font_size
|
141
|
+
end
|
142
|
+
|
143
|
+
def draw_x_axis(pdf)
|
144
|
+
pdf.stroke_line [left_margin, bottom_margin], [left_margin + chart_width, bottom_margin]
|
145
|
+
end
|
146
|
+
|
147
|
+
def draw_y_axis(pdf)
|
148
|
+
draw_y_axis_label pdf, "0",
|
149
|
+
[margin,bottom_margin+font_size/2.5]
|
150
|
+
|
151
|
+
max_line_height = chart_height + bottom_margin
|
152
|
+
draw_y_axis_label pdf, values.max.to_s,
|
153
|
+
[margin, max_line_height + font_size/2.5]
|
154
|
+
|
155
|
+
halfway_line_height = chart_height/2 + bottom_margin
|
156
|
+
halfway_point = values.max / 2
|
157
|
+
draw_y_axis_label pdf, halfway_point.to_s,
|
158
|
+
[margin, halfway_line_height + font_size/2.5]
|
159
|
+
|
160
|
+
pdf.transparent(0.3) do
|
161
|
+
pdf.dash 2, :space => 1
|
162
|
+
pdf.stroke_line [left_margin, max_line_height], [left_margin + chart_width, max_line_height]
|
163
|
+
pdf.stroke_line [left_margin, halfway_line_height], [left_margin + chart_width, halfway_line_height]
|
164
|
+
pdf.undash
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def height_for_value(value, max_value)
|
169
|
+
ratio = chart_height/max_value.to_f
|
170
|
+
(value * ratio).to_i
|
171
|
+
end
|
172
|
+
|
173
|
+
def draw_y_axis_label(pdf, label, point)
|
174
|
+
text_with_shadow pdf,
|
175
|
+
label,
|
176
|
+
:at => point,
|
177
|
+
:width => label_width,
|
178
|
+
:height => font_size,
|
179
|
+
:align => :right,
|
180
|
+
:overflow => :shrink_to_fit
|
181
|
+
end
|
182
|
+
|
183
|
+
def text_with_shadow(pdf, text, options={})
|
184
|
+
at = options[:at]
|
185
|
+
pdf.transparent(0.3) do
|
186
|
+
pdf.text_box text, options.merge(:at => [at.first, at.last + pdf.font_size/22.0])
|
187
|
+
end
|
188
|
+
pdf.text_box text, options
|
189
|
+
end
|
190
|
+
|
191
|
+
def color(index)
|
192
|
+
COLORS[index % COLORS.size]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Playfair
|
2
|
+
class Renderer
|
3
|
+
|
4
|
+
attr_reader :source, :width, :height
|
5
|
+
@@chart_types = {}
|
6
|
+
|
7
|
+
def initialize(chart, options={})
|
8
|
+
@source = chart
|
9
|
+
@width = options[:width] || 330
|
10
|
+
@height = options[:height] || 200
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.render(chart_type, &block)
|
14
|
+
@@chart_types[self] ||= {}
|
15
|
+
@@chart_types[self][chart_type] = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def render(chart_type)
|
19
|
+
instance_eval &@@chart_types[self.class][chart_type]
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def values
|
25
|
+
source.values
|
26
|
+
end
|
27
|
+
|
28
|
+
def labels
|
29
|
+
source.labels
|
30
|
+
end
|
31
|
+
|
32
|
+
def title
|
33
|
+
source.title
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: playfair
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Chris Dinn
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-24 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: chrisgdinn@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/playfair.rb
|
32
|
+
- lib/playfair/chart.rb
|
33
|
+
- lib/playfair/google_chart_renderer.rb
|
34
|
+
- lib/playfair/pdf_renderer.rb
|
35
|
+
- lib/playfair/renderer.rb
|
36
|
+
- lib/playfair/version.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/chrisdinn/playfair
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
hash: 3
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: "[none]"
|
67
|
+
rubygems_version: 1.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: A Ruby data-visualization library
|
71
|
+
test_files: []
|
72
|
+
|