prawn-graph 0.0.4 → 0.9.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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +16 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1168 -0
- data/.travis.yml +17 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTORS.md +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +142 -0
- data/Rakefile +7 -43
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/prawn-graph.rb +16 -0
- data/lib/prawn/graph/calculations.rb +1 -0
- data/lib/prawn/graph/calculations/layout_calculator.rb +108 -0
- data/lib/prawn/graph/chart_components.rb +2 -0
- data/lib/prawn/graph/chart_components/canvas.rb +138 -0
- data/lib/prawn/graph/chart_components/series_renderer.rb +173 -0
- data/lib/prawn/graph/charts.rb +4 -0
- data/lib/prawn/graph/charts/bar.rb +18 -0
- data/lib/prawn/graph/charts/base.rb +69 -0
- data/lib/prawn/graph/charts/legacy.rb +4 -0
- data/lib/prawn/graph/charts/legacy/bar.rb +28 -0
- data/lib/prawn/graph/charts/legacy/base.rb +193 -0
- data/lib/prawn/graph/charts/legacy/grid.rb +51 -0
- data/lib/prawn/graph/charts/legacy/line.rb +39 -0
- data/lib/prawn/graph/charts/line.rb +18 -0
- data/lib/prawn/graph/extension.rb +59 -0
- data/lib/prawn/graph/series.rb +79 -0
- data/lib/prawn/graph/theme.rb +41 -0
- data/lib/prawn/graph/version.rb +5 -0
- data/prawn-graph.gemspec +42 -0
- metadata +156 -80
- data/README.markdown +0 -64
- data/examples/example_helper.rb +0 -10
- data/examples/graph/advanced_bar_chart.rb +0 -22
- data/examples/graph/bar_chart.pdf +0 -185
- data/examples/graph/bar_chart.rb +0 -18
- data/examples/graph/line_chart.pdf +0 -219
- data/examples/graph/line_chart.rb +0 -18
- data/examples/graph/themed_bar_chart.rb +0 -18
- data/examples/graph/themed_line_chart.rb +0 -18
- data/lib/prawn/graph.rb +0 -94
- data/lib/prawn/graph/bar.rb +0 -64
- data/lib/prawn/graph/base.rb +0 -231
- data/lib/prawn/graph/chart.rb +0 -4
- data/lib/prawn/graph/errors.rb +0 -7
- data/lib/prawn/graph/grid.rb +0 -50
- data/lib/prawn/graph/line.rb +0 -74
- data/lib/prawn/graph/themes.rb +0 -116
- data/lib/prawn/graph/themes/37signals.yml +0 -14
- data/lib/prawn/graph/themes/keynote.yml +0 -14
- data/lib/prawn/graph/themes/monochome.yml +0 -8
- data/lib/prawn/graph/themes/odeo.yml +0 -14
@@ -0,0 +1,193 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
|
3
|
+
module Prawn
|
4
|
+
module Graph
|
5
|
+
module Charts
|
6
|
+
module Legacy
|
7
|
+
|
8
|
+
class Base
|
9
|
+
|
10
|
+
attr_accessor :grid, :headings, :values, :highest_value, :document, :colour
|
11
|
+
|
12
|
+
def initialize(data, document, options = {})
|
13
|
+
opts = { :theme => Prawn::Graph::Theme::Default, :width => 500, :height => 200, :spacing => 20, :at => [0,0] }.merge(options)
|
14
|
+
(@headings, @values, @highest_value) = process_the data
|
15
|
+
(grid_x_start, grid_y_start, grid_width, grid_height) = parse_sizing_from opts
|
16
|
+
@colour = false
|
17
|
+
@document = document
|
18
|
+
@theme = Prawn::Graph::Theme::Default
|
19
|
+
@grid = Prawn::Graph::Charts::Legacy::Grid.new(grid_x_start, grid_y_start, grid_width, grid_height, opts[:spacing], document, Prawn::Graph::Theme::Default)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Draws the graph on the document which we have a reference to.
|
23
|
+
#
|
24
|
+
def draw
|
25
|
+
draw_bounding_box
|
26
|
+
@grid.draw
|
27
|
+
label_axes
|
28
|
+
if @title
|
29
|
+
draw_title
|
30
|
+
end
|
31
|
+
plot_values
|
32
|
+
if @x_label
|
33
|
+
draw_x_axis_label
|
34
|
+
end
|
35
|
+
if @y_label
|
36
|
+
draw_y_axis_label
|
37
|
+
end
|
38
|
+
reset
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def draw_bounding_box
|
44
|
+
@document.fill_color @theme.background
|
45
|
+
@document.fill_and_stroke_rectangle [(@point.first - 10), (@point.last + ( @total_height + 40 ))], @document.bounds.width, (@total_height + 40)
|
46
|
+
@document.fill_color '000000'
|
47
|
+
end
|
48
|
+
|
49
|
+
def label_axes
|
50
|
+
@document.fill_color @theme.title
|
51
|
+
base_x = @grid.start_x + 1
|
52
|
+
base_y = @grid.start_y + 1
|
53
|
+
|
54
|
+
# Put the values up the Y Axis
|
55
|
+
#
|
56
|
+
@document.draw_text @highest_value, :at => [base_x - 15, base_y + @grid.height], :size => 5
|
57
|
+
@document.draw_text '0', :at => [base_x - 15, base_y ], :size => 5
|
58
|
+
|
59
|
+
# Put the column headings along the X Axis
|
60
|
+
#
|
61
|
+
point_spacing = calculate_plot_spacing
|
62
|
+
last_position = base_x + (point_spacing / 2)
|
63
|
+
@headings.each do |heading|
|
64
|
+
@document.draw_text heading, :at => [last_position, base_y - 15 ], :size => 5
|
65
|
+
last_position += point_spacing
|
66
|
+
end
|
67
|
+
@document.fill_color @theme.background
|
68
|
+
end
|
69
|
+
|
70
|
+
def draw_title
|
71
|
+
@document.fill_color @theme.title
|
72
|
+
x_coord = calculate_x_axis_center_point(@title, 10)
|
73
|
+
y_coord = @grid.start_y + @grid.height + 10
|
74
|
+
@document.draw_text @title, :at => [x_coord, y_coord] ,:size => 10
|
75
|
+
@document.fill_color @theme.background
|
76
|
+
end
|
77
|
+
|
78
|
+
def draw_x_axis_label
|
79
|
+
@document.fill_color @theme.axes
|
80
|
+
x_coord = calculate_x_axis_center_point(@x_label, 8)
|
81
|
+
y_coord = @grid.start_y - 30
|
82
|
+
@document.draw_text @x_label, :at => [x_coord, y_coord] ,:size => 8
|
83
|
+
@document.fill_color @theme.background
|
84
|
+
end
|
85
|
+
|
86
|
+
def draw_y_axis_label
|
87
|
+
@document.fill_color @theme.axes
|
88
|
+
y_coord = calculate_y_axis_center_point(@y_label, 8)
|
89
|
+
x_coord = @grid.start_x - 30
|
90
|
+
@document.draw_text @y_label, :at => [x_coord, y_coord] ,:size => 8, :rotate => 90
|
91
|
+
@document.fill_color @theme.background
|
92
|
+
end
|
93
|
+
|
94
|
+
# All subclasses of Prawn::Chart::Base must implement thier own plot_values
|
95
|
+
# method, which does the actual real heavy lifting of drawing the graph.
|
96
|
+
#
|
97
|
+
def plot_values
|
98
|
+
raise RuntimeError.new('subclasses of Prawn::Chart::Base must implement their own plot_values method')
|
99
|
+
end
|
100
|
+
|
101
|
+
def reset
|
102
|
+
@document.line_width 1
|
103
|
+
@document.stroke_color '000000'
|
104
|
+
@document.fill_color '000000'
|
105
|
+
@document.move_to @grid.point
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Utility methods for dealing with working out where things should be
|
110
|
+
# the calculations and such done here are all very rough, but are
|
111
|
+
# sufficient for now to plot just what we need.
|
112
|
+
#
|
113
|
+
|
114
|
+
|
115
|
+
def parse_sizing_from(o)
|
116
|
+
x_offset = 15
|
117
|
+
y_offset = 0
|
118
|
+
move_y_up = 0
|
119
|
+
grid_width = o[:width]
|
120
|
+
grid_height = o[:height]
|
121
|
+
|
122
|
+
@total_width = o[:width]
|
123
|
+
@total_height = o[:height]
|
124
|
+
@point = o[:at].dup
|
125
|
+
|
126
|
+
# Make room for the title if we're choosing to Render it.
|
127
|
+
#
|
128
|
+
if o[:title]
|
129
|
+
@title = o[:title]
|
130
|
+
y_offset += 10
|
131
|
+
end
|
132
|
+
|
133
|
+
# Make room for X Axis labels if we're using them.
|
134
|
+
#
|
135
|
+
if o[:label_x]
|
136
|
+
y_offset += 30
|
137
|
+
move_y_up += 30
|
138
|
+
@x_label = o[:label_x]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Make room for Y Axis labels if we're using them.
|
142
|
+
#
|
143
|
+
if o[:label_y]
|
144
|
+
@y_label = o[:label_y]
|
145
|
+
x_offset += 15
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# Return the values calculated here.
|
150
|
+
#
|
151
|
+
[ (o[:at][0] + x_offset), (o[:at][1] + move_y_up + 20), (grid_width - (x_offset - 20)), (grid_height - y_offset) ]
|
152
|
+
end
|
153
|
+
|
154
|
+
def process_the(data_array)
|
155
|
+
col = []
|
156
|
+
val = []
|
157
|
+
data_array.each { |i| val << i[1]; col << i[0] }
|
158
|
+
[ col, val ,val.sort.last ]
|
159
|
+
end
|
160
|
+
|
161
|
+
def calculate_x_axis_center_point(text, text_size, graph_start_x = @grid.start_x, graph_width = @grid.width)
|
162
|
+
((graph_start_x + (graph_width / 2)) - ((text.length * text_size) / 4))
|
163
|
+
end
|
164
|
+
alias calculate_x_axis_centre_point calculate_x_axis_center_point
|
165
|
+
|
166
|
+
def calculate_y_axis_center_point(text, text_size, graph_start_y = @grid.start_y, graph_height = @grid.height)
|
167
|
+
((graph_start_y + (graph_height / 2)) - ((text.length * text_size) / 4))
|
168
|
+
end
|
169
|
+
alias calculate_y_axis_centre_point calculate_y_axis_center_point
|
170
|
+
|
171
|
+
def calculate_plot_spacing
|
172
|
+
(@grid.width / @values.count)
|
173
|
+
end
|
174
|
+
|
175
|
+
def calculate_bar_width
|
176
|
+
calculate_plot_spacing / 2
|
177
|
+
end
|
178
|
+
|
179
|
+
def calculate_point_height_from(column_value)
|
180
|
+
cv = BigDecimal("#{column_value}")
|
181
|
+
hv = BigDecimal("#{@highest_value}")
|
182
|
+
gh = BigDecimal("#{@grid.height}")
|
183
|
+
percentage = (cv / (hv / 100))
|
184
|
+
((gh / 100) * percentage).to_i
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
module Charts
|
4
|
+
module Legacy
|
5
|
+
|
6
|
+
class Grid
|
7
|
+
|
8
|
+
attr_accessor :width, :height, :point, :spacing, :document
|
9
|
+
|
10
|
+
def initialize(grid_x_start, grid_y_start, grid_width, grid_height, spacing, document, theme = Prawn::Graph::Theme::Default)
|
11
|
+
@point = [grid_x_start, grid_y_start]
|
12
|
+
@width = grid_width
|
13
|
+
@height = grid_height
|
14
|
+
@spacing = spacing
|
15
|
+
@document = document
|
16
|
+
@theme = Prawn::Graph::Theme::Default
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_x; @point.first; end
|
20
|
+
def start_y; @point.last; end
|
21
|
+
|
22
|
+
# Draws the Grid on the specified Prawn::Document
|
23
|
+
#
|
24
|
+
def draw
|
25
|
+
@document.stroke_color @theme.markers
|
26
|
+
if @theme.stroke_grid_lines
|
27
|
+
(@height / @spacing).times do |x|
|
28
|
+
offset = @spacing * (x + 1)
|
29
|
+
@document.move_to [@point.first, (@point.last + offset)]
|
30
|
+
@document.line_width(0.5)
|
31
|
+
@document.stroke_line_to([(@point.first + @width), (@point.last + offset)])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@document.move_to @point
|
35
|
+
@document.line_width(2)
|
36
|
+
@document.stroke_line_to([@point.first, @point.last + @height])
|
37
|
+
@document.move_to @point
|
38
|
+
@document.line_width(2)
|
39
|
+
@document.stroke_line_to([(@point.first + @width), @point.last])
|
40
|
+
@document.move_to @point.first, (@point.last + height)
|
41
|
+
@document.stroke_color '000000'
|
42
|
+
@document.line_width(1)
|
43
|
+
@document.move_to @point
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
module Charts
|
4
|
+
module Legacy
|
5
|
+
class Line < Prawn::Graph::Charts::Legacy::Base
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def plot_values
|
10
|
+
base_x = @grid.start_x + 1
|
11
|
+
base_y = @grid.start_y + 1
|
12
|
+
p = [ [base_x, base_y] ]
|
13
|
+
bar_width = calculate_bar_width
|
14
|
+
@document.line_width bar_width
|
15
|
+
last_position = base_x + bar_width
|
16
|
+
point_spacing = calculate_plot_spacing
|
17
|
+
@values.each do |value|
|
18
|
+
@document.move_to [base_x + last_position, base_y]
|
19
|
+
bar_height = calculate_point_height_from value
|
20
|
+
point = [base_x + last_position, base_y + bar_height]
|
21
|
+
p << point
|
22
|
+
@document.fill_color @theme.series.last
|
23
|
+
@document.fill_circle_at point, :radius => 1
|
24
|
+
last_position += point_spacing
|
25
|
+
end
|
26
|
+
@document.line_width 2
|
27
|
+
@document.stroke_color @theme.series.last
|
28
|
+
p.each_with_index do |point,i|
|
29
|
+
next if point == p.last
|
30
|
+
@document.move_to point
|
31
|
+
@document.stroke_line_to p[i+1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
module Charts
|
4
|
+
class Line < Base
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def chart_object
|
9
|
+
Prawn::Graph::Charts::Legacy::Line.new(@series.collect(&:to_a), @prawn, @options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def series_type
|
13
|
+
:line
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
module Extension
|
4
|
+
|
5
|
+
# @deprecated bar_graph and bar_chart are deprecated and will be removed in a future version. Use the new graph / chart methods instead.
|
6
|
+
# Draws a bar graph into the PDF using the legacy graph stuff. Please avoid.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# bar_graph [ ["A", 1], ["B", 2], ["C", 3] ], at: [10,10]
|
11
|
+
#
|
12
|
+
def bar_graph(data, options = {}, &block)
|
13
|
+
deprecate :bar_graph
|
14
|
+
draw_graph(Prawn::Graph::Charts::Bar, data, options, &block)
|
15
|
+
end
|
16
|
+
alias bar_chart bar_graph
|
17
|
+
|
18
|
+
# @deprecated line_graph and line_chart are deprecated and will be removed in a future version. Use the new graph / chart methods instead.
|
19
|
+
# Draws a line graph into the PDF using the legacy graph stuff. Please avoid.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# line_graph [ ["A", 1], ["B", 2], ["C", 3] ], at: [10,10]
|
24
|
+
#
|
25
|
+
def line_graph(data, options = {}, &block)
|
26
|
+
deprecate :line_graph
|
27
|
+
draw_graph(Prawn::Graph::Charts::Line, data, options, &block)
|
28
|
+
end
|
29
|
+
alias line_chart line_graph
|
30
|
+
|
31
|
+
|
32
|
+
# Plots one or more Prawn::Graph::Series on a chart. Expects an array-like object of
|
33
|
+
# Prawn::Graph::Series objects and some options for positioning the sizing the
|
34
|
+
# rendered graph
|
35
|
+
#
|
36
|
+
# @param series [Array] of Prawn::Graph::Series objects
|
37
|
+
# @param options [Hash] of options, which can be: `:width`, `:height` , `:at` , or `:title`
|
38
|
+
#
|
39
|
+
def graph(series, options = {}, &block)
|
40
|
+
canvas = Prawn::Graph::ChartComponents::Canvas.new(series, self, options, &block)
|
41
|
+
canvas.draw
|
42
|
+
{warnings: [], width: self.bounds.width, height: self.bounds.height}
|
43
|
+
end
|
44
|
+
alias chart graph
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def draw_graph(klass, data, options, &block)
|
49
|
+
graph = klass.new(data, self, options, &block)
|
50
|
+
graph.draw
|
51
|
+
{warnings: [], width: self.bounds.width, height: self.bounds.height}
|
52
|
+
end
|
53
|
+
|
54
|
+
def deprecate(method)
|
55
|
+
warn "[DEPRECATION] #{method} is deprecated and will be removed in future versions of prawn-graph. Use chart or graph instead."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
|
4
|
+
# A Prawn::Graph::Series represents a series of data which are to be plotted
|
5
|
+
# on a chart.
|
6
|
+
#
|
7
|
+
class Series
|
8
|
+
attr_accessor :values, :options
|
9
|
+
|
10
|
+
DEFAULT_OPTIONS = {
|
11
|
+
title: nil,
|
12
|
+
type: :bar,
|
13
|
+
mark_average: false,
|
14
|
+
mark_minimum: false,
|
15
|
+
mark_maximum: false,
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(values = [], options = {})
|
19
|
+
@values = values
|
20
|
+
@options = OpenStruct.new(DEFAULT_OPTIONS.merge(options))
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [String] The value of +options.title+.
|
24
|
+
#
|
25
|
+
def title
|
26
|
+
options.title
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Symbol] The value of +options.type+.
|
30
|
+
#
|
31
|
+
def type
|
32
|
+
options.type
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param value [Object] a value to be added to the series. Must be of the same kind as other +values+.
|
36
|
+
# @return [Array] The modified +values+ object.
|
37
|
+
#
|
38
|
+
def <<(value)
|
39
|
+
@values << value
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Numeric] The smallest value stored in the +values+ of this Series.
|
43
|
+
#
|
44
|
+
def min
|
45
|
+
@values.min || 0
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Numeric] The largest value stored in the +values+ of this Series.
|
49
|
+
#
|
50
|
+
def max
|
51
|
+
@values.max || 0
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Numeric] The average value stored in the +values+ of this Series.
|
55
|
+
#
|
56
|
+
def avg
|
57
|
+
if size > 0
|
58
|
+
@values.inject(:+) / size
|
59
|
+
else
|
60
|
+
0
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Numeric] The size of the +values+ stored in this Series.
|
65
|
+
#
|
66
|
+
def size
|
67
|
+
@values.size
|
68
|
+
end
|
69
|
+
|
70
|
+
# @deprecated Provided to allow for tempory backwards compatibilty with legacy graph drawing. Do not use.
|
71
|
+
# @return [Array] Series represented as an array in the format [ title, val1, val2... ]
|
72
|
+
#
|
73
|
+
def to_a
|
74
|
+
warn "[DEPRECATION] Series#to_a is deprecated and will be removed in a future version of prawn-graph."
|
75
|
+
[options.title, @values].compact.flatten
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Graph
|
3
|
+
module Theme
|
4
|
+
Default = OpenStruct.new({
|
5
|
+
series: [
|
6
|
+
'EBEDEF',
|
7
|
+
'D6DBDF',
|
8
|
+
'85929E',
|
9
|
+
'34495E',
|
10
|
+
'1B2631',
|
11
|
+
'EBEDEF',
|
12
|
+
'D6DBDF',
|
13
|
+
'85929E',
|
14
|
+
'34495E',
|
15
|
+
'1B2631',
|
16
|
+
'EBEDEF',
|
17
|
+
'D6DBDF',
|
18
|
+
'85929E',
|
19
|
+
'34495E',
|
20
|
+
'1B2631',
|
21
|
+
],
|
22
|
+
title: '17202A',
|
23
|
+
background: 'FFFFFF',
|
24
|
+
grid: 'F2F4F4',
|
25
|
+
axes: '17202A',
|
26
|
+
markers: '34495E',
|
27
|
+
stroke_grid_lines: true,
|
28
|
+
default: '333333',
|
29
|
+
average: '34495E',
|
30
|
+
max: '17202A',
|
31
|
+
min: '17202A',
|
32
|
+
font_sizes: OpenStruct.new({
|
33
|
+
default: 8,
|
34
|
+
main_title: 10,
|
35
|
+
axis_labels: 5,
|
36
|
+
series_key: 8,
|
37
|
+
}),
|
38
|
+
})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|