playfair 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.
- 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
|
+
|