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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +16 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1168 -0
  6. data/.travis.yml +17 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/CONTRIBUTORS.md +6 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +142 -0
  12. data/Rakefile +7 -43
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/prawn-graph.rb +16 -0
  16. data/lib/prawn/graph/calculations.rb +1 -0
  17. data/lib/prawn/graph/calculations/layout_calculator.rb +108 -0
  18. data/lib/prawn/graph/chart_components.rb +2 -0
  19. data/lib/prawn/graph/chart_components/canvas.rb +138 -0
  20. data/lib/prawn/graph/chart_components/series_renderer.rb +173 -0
  21. data/lib/prawn/graph/charts.rb +4 -0
  22. data/lib/prawn/graph/charts/bar.rb +18 -0
  23. data/lib/prawn/graph/charts/base.rb +69 -0
  24. data/lib/prawn/graph/charts/legacy.rb +4 -0
  25. data/lib/prawn/graph/charts/legacy/bar.rb +28 -0
  26. data/lib/prawn/graph/charts/legacy/base.rb +193 -0
  27. data/lib/prawn/graph/charts/legacy/grid.rb +51 -0
  28. data/lib/prawn/graph/charts/legacy/line.rb +39 -0
  29. data/lib/prawn/graph/charts/line.rb +18 -0
  30. data/lib/prawn/graph/extension.rb +59 -0
  31. data/lib/prawn/graph/series.rb +79 -0
  32. data/lib/prawn/graph/theme.rb +41 -0
  33. data/lib/prawn/graph/version.rb +5 -0
  34. data/prawn-graph.gemspec +42 -0
  35. metadata +156 -80
  36. data/README.markdown +0 -64
  37. data/examples/example_helper.rb +0 -10
  38. data/examples/graph/advanced_bar_chart.rb +0 -22
  39. data/examples/graph/bar_chart.pdf +0 -185
  40. data/examples/graph/bar_chart.rb +0 -18
  41. data/examples/graph/line_chart.pdf +0 -219
  42. data/examples/graph/line_chart.rb +0 -18
  43. data/examples/graph/themed_bar_chart.rb +0 -18
  44. data/examples/graph/themed_line_chart.rb +0 -18
  45. data/lib/prawn/graph.rb +0 -94
  46. data/lib/prawn/graph/bar.rb +0 -64
  47. data/lib/prawn/graph/base.rb +0 -231
  48. data/lib/prawn/graph/chart.rb +0 -4
  49. data/lib/prawn/graph/errors.rb +0 -7
  50. data/lib/prawn/graph/grid.rb +0 -50
  51. data/lib/prawn/graph/line.rb +0 -74
  52. data/lib/prawn/graph/themes.rb +0 -116
  53. data/lib/prawn/graph/themes/37signals.yml +0 -14
  54. data/lib/prawn/graph/themes/keynote.yml +0 -14
  55. data/lib/prawn/graph/themes/monochome.yml +0 -8
  56. 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