write_xlsx 0.70.0 → 0.71.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.rdoc +7 -1
  4. data/lib/write_xlsx/chart.rb +133 -1713
  5. data/lib/write_xlsx/chart/axis.rb +6 -8
  6. data/lib/write_xlsx/chart/caption.rb +19 -0
  7. data/lib/write_xlsx/chart/scatter.rb +15 -15
  8. data/lib/write_xlsx/chart/series.rb +307 -0
  9. data/lib/write_xlsx/chart/stock.rb +5 -5
  10. data/lib/write_xlsx/version.rb +1 -1
  11. data/lib/write_xlsx/workbook.rb +1 -1
  12. data/lib/write_xlsx/worksheet.rb +2 -2
  13. data/test/chart/test_add_series.rb +113 -115
  14. data/test/chart/test_write_d_lbls.rb +19 -18
  15. data/test/chart/test_write_number_format.rb +4 -4
  16. data/test/helper.rb +13 -0
  17. data/test/perl_output/comments2.xlsx +0 -0
  18. data/test/perl_output/demo.xlsx +0 -0
  19. data/test/regression/images/issue32.jpg +0 -0
  20. data/test/regression/images/issue32.png +0 -0
  21. data/test/regression/images/logo.png +0 -0
  22. data/test/regression/images/mylogo.png +0 -0
  23. data/test/regression/test_chart_area04.rb +44 -0
  24. data/test/regression/test_chart_bar24.rb +1 -2
  25. data/test/regression/test_chart_column04.rb +1 -1
  26. data/test/regression/test_chart_line02.rb +1 -1
  27. data/test/regression/test_chart_scatter07.rb +1 -1
  28. data/test/regression/test_chart_stock02.rb +1 -1
  29. data/test/regression/test_image10.rb +24 -0
  30. data/test/regression/test_image11.rb +24 -0
  31. data/test/regression/test_image12.rb +27 -0
  32. data/test/regression/test_image13.rb +27 -0
  33. data/test/regression/test_image14.rb +29 -0
  34. data/test/regression/test_image15.rb +29 -0
  35. data/test/regression/test_image16.rb +24 -0
  36. data/test/regression/test_image17.rb +27 -0
  37. data/test/regression/test_image18.rb +27 -0
  38. data/test/regression/xlsx_files/image10.xlsx +0 -0
  39. data/test/regression/xlsx_files/image11.xlsx +0 -0
  40. data/test/regression/xlsx_files/image12.xlsx +0 -0
  41. data/test/regression/xlsx_files/image13.xlsx +0 -0
  42. data/test/regression/xlsx_files/image14.xlsx +0 -0
  43. data/test/regression/xlsx_files/image15.xlsx +0 -0
  44. data/test/regression/xlsx_files/image16.xlsx +0 -0
  45. data/test/regression/xlsx_files/image17.xlsx +0 -0
  46. data/test/regression/xlsx_files/image18.xlsx +0 -0
  47. data/test/republic.png +0 -0
  48. data/test/test_example_match.rb +2 -4
  49. data/test/workbook/test_workbook_01.rb +1 -1
  50. data/test/workbook/test_workbook_02.rb +1 -1
  51. data/test/workbook/test_workbook_03.rb +1 -1
  52. data/write_xlsx.gemspec +1 -1
  53. metadata +53 -30
  54. data/html/en/doc_en.html +0 -7765
  55. data/html/index.html +0 -16
  56. data/html/style.css +0 -433
@@ -1,28 +1,27 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'write_xlsx/package/xml_writer_simple'
3
3
  require 'write_xlsx/utility'
4
+ require 'write_xlsx/chart/caption'
4
5
 
5
6
  module Writexlsx
6
7
  class Chart
7
- class Axis
8
+ class Axis < Caption
8
9
  include Writexlsx::Utility
9
10
 
10
- attr_accessor :defaults, :name, :formula, :data_id, :reverse
11
+ attr_accessor :defaults, :reverse
11
12
  attr_accessor :min, :max
12
13
  attr_accessor :minor_unit, :major_unit, :minor_unit_type, :major_unit_type
13
14
  attr_accessor :log_base, :crossing, :position, :label_position, :visible
14
- attr_accessor :num_format, :num_format_linked, :num_font, :name_font
15
+ attr_accessor :num_format, :num_format_linked, :num_font
15
16
  attr_accessor :major_gridlines, :minor_gridlines, :major_tick_mark
16
17
 
17
18
  #
18
19
  # Convert user defined axis values into axis instance.
19
20
  #
20
- def merge_with_hash(chart, params) # :nodoc:
21
- @chart = chart
21
+ def merge_with_hash(params) # :nodoc:
22
+ super
22
23
  args = (defaults || {}).merge(params)
23
24
 
24
- @name, @formula = @chart.process_names(args[:name], args[:name_formula])
25
- @data_id = @chart.get_data_id(@formula, args[:data])
26
25
  @reverse = args[:reverse]
27
26
  @min = args[:min]
28
27
  @max = args[:max]
@@ -53,7 +52,6 @@ def merge_with_hash(chart, params) # :nodoc:
53
52
 
54
53
  # Set the font properties if present.
55
54
  @num_font = @chart.convert_font_args(args[:num_font])
56
- @name_font = @chart.convert_font_args(args[:name_font])
57
55
  end
58
56
 
59
57
  #
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Writexlsx
4
+ class Chart
5
+ class Caption
6
+ attr_accessor :name, :formula, :data_id, :name_font
7
+
8
+ def initialize(chart)
9
+ @chart = chart
10
+ end
11
+
12
+ def merge_with_hash(params) # :nodoc:
13
+ @name, @formula = @chart.process_names(params[:name], params[:name_formula])
14
+ @data_id = @chart.get_data_id(@formula, params[:data])
15
+ @name_font = @chart.convert_font_args(params[:name_font])
16
+ end
17
+ end
18
+ end
19
+ end
@@ -110,24 +110,24 @@ def write_ser(series)
110
110
  # Write the c:spPr element.
111
111
  write_sp_pr(series)
112
112
  # Write the c:marker element.
113
- write_marker(series[:_marker])
113
+ write_marker(series.marker)
114
114
  # Write the c:dPt element.
115
- write_d_pt(series[:_points])
115
+ write_d_pt(series.points)
116
116
  # Write the c:dLbls element.
117
- write_d_lbls(series[:_labels])
117
+ write_d_lbls(series.labels)
118
118
  # Write the c:trendline element.
119
- write_trendline(series[:_trendline])
119
+ write_trendline(series.trendline)
120
120
  # Write the c:errBars element.
121
- write_error_bars(series[:_error_bars])
121
+ write_error_bars(series.error_bars)
122
122
  # Write the c:xVal element.
123
123
  write_x_val(series)
124
124
  # Write the c:yVal element.
125
125
  write_y_val(series)
126
126
  # Write the c:smooth element.
127
- if @subtype =~ /smooth/ && !series[:_smooth]
127
+ if @subtype =~ /smooth/ && !series.smooth
128
128
  write_c_smooth(1)
129
129
  else
130
- write_c_smooth(series[:_smooth])
130
+ write_c_smooth(series.smooth)
131
131
  end
132
132
  end
133
133
  end
@@ -188,8 +188,8 @@ def write_plot_area
188
188
  # Write the <c:xVal> element.
189
189
  #
190
190
  def write_x_val(series)
191
- formula = series[:_categories]
192
- data_id = series[:_cat_data_id]
191
+ formula = series.categories
192
+ data_id = series.cat_data_id
193
193
  data = @formula_data[data_id]
194
194
 
195
195
  @writer.tag_elements('c:xVal') do
@@ -211,8 +211,8 @@ def write_x_val(series)
211
211
  # Write the <c:yVal> element.
212
212
  #
213
213
  def write_y_val(series)
214
- formula = series[:_values]
215
- data_id = series[:_val_data_id]
214
+ formula = series.values
215
+ data_id = series.val_data_id
216
216
  data = @formula_data[data_id]
217
217
 
218
218
  @writer.tag_elements('c:yVal') do
@@ -244,8 +244,8 @@ def modify_series_formatting
244
244
  # Go through each series and define default values.
245
245
  @series.each do |series|
246
246
  # Set a line type unless there is already a user defined type.
247
- if series[:_line][:_defined] == 0
248
- series[:_line] = { :width => 2.25, :none => 1, :_defined => 1 }
247
+ if series.line[:_defined] == 0
248
+ series.line = { :width => 2.25, :none => 1, :_defined => 1 }
249
249
  end
250
250
  end
251
251
  end
@@ -255,8 +255,8 @@ def modify_series_formatting
255
255
  # Go through each series and define default values.
256
256
  @series.each do |series|
257
257
  # Set a marker type unless there is already a user defined type.
258
- if !series[:_marker] || series[:_marker][:_defined] == 0
259
- series[:_marker] = { :type => 'none', :_defined => 1 }
258
+ if !series.marker || series.marker[:_defined] == 0
259
+ series.marker = { :type => 'none', :_defined => 1 }
260
260
  end
261
261
  end
262
262
  end
@@ -0,0 +1,307 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class Series
4
+ include Writexlsx::Utility
5
+
6
+ attr_reader :values, :categories, :name, :name_formula, :name_id
7
+ attr_reader :cat_data_id, :val_data_id, :fill
8
+ attr_reader :trendline, :smooth, :labels, :invert_if_neg
9
+ attr_reader :x2_axis, :y2_axis, :error_bars, :points
10
+ attr_accessor :line, :marker
11
+
12
+ def initialize(chart, params = {})
13
+ @values = aref_to_formula(params[:values])
14
+ @categories = aref_to_formula(params[:categories])
15
+ @name, @name_formula =
16
+ chart.process_names(params[:name], params[:name_formula])
17
+ @cat_data_id = chart.get_data_id(@categories, params[:categories_data])
18
+ @val_data_id = chart.get_data_id(@values, params[:values_data])
19
+ @name_id = chart.get_data_id(@name_formula, params[:name_data])
20
+ if params[:border]
21
+ @line = line_properties(params[:border])
22
+ else
23
+ @line = line_properties(params[:line])
24
+ end
25
+ @fill = fill_properties(params[:fill])
26
+ @marker = marker_properties(params[:marker])
27
+ @trendline = trendline_properties(params[:trendline])
28
+ @smooth = params[:smooth]
29
+ @error_bars = {
30
+ :_x_error_bars => error_bars_properties(params[:x_error_bars]),
31
+ :_y_error_bars => error_bars_properties(params[:y_error_bars])
32
+ }
33
+ @points = points_properties(params[:points])
34
+ @labels = labels_properties(params[:data_labels])
35
+ @invert_if_neg = params[:invert_if_negative]
36
+ @x2_axis = params[:x2_axis]
37
+ @y2_axis = params[:y2_axis]
38
+ end
39
+
40
+ def ==(other)
41
+ methods = %w[categories values name name_formula name_id
42
+ cat_data_id val_data_id
43
+ line fill marker trendline
44
+ smooth labels invert_if_neg x2_axis y2_axis error_bars points ]
45
+ methods.each do |method|
46
+ return false unless self.instance_variable_get("@#{method}") == other.instance_variable_get("@#{method}")
47
+ end
48
+ true
49
+ end
50
+
51
+ private
52
+
53
+ #
54
+ # Convert and aref of row col values to a range formula.
55
+ #
56
+ def aref_to_formula(data) # :nodoc:
57
+ # If it isn't an array ref it is probably a formula already.
58
+ return data unless data.kind_of?(Array)
59
+ xl_range_formula(*data)
60
+ end
61
+
62
+ #
63
+ # Convert user defined line properties to the structure required internally.
64
+ #
65
+ def line_properties(line) # :nodoc:
66
+ return { :_defined => 0 } unless line
67
+
68
+ dash_types = {
69
+ :solid => 'solid',
70
+ :round_dot => 'sysDot',
71
+ :square_dot => 'sysDash',
72
+ :dash => 'dash',
73
+ :dash_dot => 'dashDot',
74
+ :long_dash => 'lgDash',
75
+ :long_dash_dot => 'lgDashDot',
76
+ :long_dash_dot_dot => 'lgDashDotDot',
77
+ :dot => 'dot',
78
+ :system_dash_dot => 'sysDashDot',
79
+ :system_dash_dot_dot => 'sysDashDotDot'
80
+ }
81
+
82
+ # Check the dash type.
83
+ dash_type = line[:dash_type]
84
+
85
+ if dash_type
86
+ line[:dash_type] = value_or_raise(dash_types, dash_type, 'dash type')
87
+ end
88
+
89
+ line[:_defined] = 1
90
+
91
+ line
92
+ end
93
+
94
+ #
95
+ # Convert user defined fill properties to the structure required internally.
96
+ #
97
+ def fill_properties(fill) # :nodoc:
98
+ return { :_defined => 0 } unless fill
99
+
100
+ fill[:_defined] = 1
101
+
102
+ fill
103
+ end
104
+
105
+ #
106
+ # Convert user defined marker properties to the structure required internally.
107
+ #
108
+ def marker_properties(marker) # :nodoc:
109
+ return unless marker
110
+
111
+ types = {
112
+ :automatic => 'automatic',
113
+ :none => 'none',
114
+ :square => 'square',
115
+ :diamond => 'diamond',
116
+ :triangle => 'triangle',
117
+ :x => 'x',
118
+ :star => 'start',
119
+ :dot => 'dot',
120
+ :short_dash => 'dot',
121
+ :dash => 'dash',
122
+ :long_dash => 'dash',
123
+ :circle => 'circle',
124
+ :plus => 'plus',
125
+ :picture => 'picture'
126
+ }
127
+
128
+ # Check for valid types.
129
+ marker_type = marker[:type]
130
+
131
+ if marker_type
132
+ marker[:automatic] = 1 if marker_type == 'automatic'
133
+ marker[:type] = value_or_raise(types, marker_type, 'maker type')
134
+ end
135
+
136
+ # Set the line properties for the marker..
137
+ line = line_properties(marker[:line])
138
+
139
+ # Allow 'border' as a synonym for 'line'.
140
+ line = line_properties(marker[:border]) if marker[:border]
141
+
142
+ # Set the fill properties for the marker.
143
+ fill = fill_properties(marker[:fill])
144
+
145
+ marker[:_line] = line
146
+ marker[:_fill] = fill
147
+
148
+ marker
149
+ end
150
+
151
+ #
152
+ # Convert user defined trendline properties to the structure required internally.
153
+ #
154
+ def trendline_properties(trendline) # :nodoc:
155
+ return unless trendline
156
+
157
+ types = {
158
+ :exponential => 'exp',
159
+ :linear => 'linear',
160
+ :log => 'log',
161
+ :moving_average => 'movingAvg',
162
+ :polynomial => 'poly',
163
+ :power => 'power'
164
+ }
165
+
166
+ # Check the trendline type.
167
+ trend_type = trendline[:type]
168
+
169
+ trendline[:type] = value_or_raise(types, trend_type, 'trendline type')
170
+
171
+ # Set the line properties for the trendline..
172
+ line = line_properties(trendline[:line])
173
+
174
+ # Allow 'border' as a synonym for 'line'.
175
+ line = line_properties(trendline[:border]) if trendline[:border]
176
+
177
+ # Set the fill properties for the trendline.
178
+ fill = fill_properties(trendline[:fill])
179
+
180
+ trendline[:_line] = line
181
+ trendline[:_fill] = fill
182
+
183
+ return trendline
184
+ end
185
+
186
+ #
187
+ # Convert user defined error bars properties to structure required
188
+ # internally.
189
+ #
190
+ def error_bars_properties(params = {})
191
+ return if !ptrue?(params) || params.empty?
192
+
193
+ # Default values.
194
+ error_bars = {
195
+ :_type => 'fixedVal',
196
+ :_value => 1,
197
+ :_endcap => 1,
198
+ :_direction => 'both'
199
+ }
200
+
201
+ types = {
202
+ :fixed => 'fixedVal',
203
+ :percentage => 'percentage',
204
+ :standard_deviation => 'stdDev',
205
+ :standard_error => 'stdErr'
206
+ }
207
+
208
+ # Check the error bars type.
209
+ error_type = params[:type].to_sym
210
+
211
+ if types.key?(error_type)
212
+ error_bars[:_type] = types[error_type]
213
+ else
214
+ raise "Unknown error bars type '#{error_type}'\n"
215
+ end
216
+
217
+ # Set the value for error types that require it.
218
+ if params.key?(:value)
219
+ error_bars[:_value] = params[:value]
220
+ end
221
+
222
+ # Set the end-cap style.
223
+ if params.key?(:end_style)
224
+ error_bars[:_endcap] = params[:end_style]
225
+ end
226
+
227
+ # Set the error bar direction.
228
+ if params.key?(:direction)
229
+ if params[:direction] == 'minus'
230
+ error_bars[:_direction] = 'minus'
231
+ elsif params[:direction] == 'plus'
232
+ error_bars[:_direction] = 'plus'
233
+ else
234
+ # Default to 'both'
235
+ end
236
+ end
237
+
238
+ # Set the line properties for the error bars.
239
+ error_bars[:_line] = line_properties(params[:line])
240
+
241
+ error_bars
242
+ end
243
+
244
+ #
245
+ # Convert user defined points properties to structure required internally.
246
+ #
247
+ def points_properties(user_points = nil)
248
+ return unless user_points
249
+
250
+ points = []
251
+ user_points.each do |user_point|
252
+ if user_point
253
+ # Set the lline properties for the point.
254
+ line = line_properties(user_point[:line])
255
+
256
+ # Allow 'border' as a synonym for 'line'.
257
+ if user_point[:border]
258
+ line = line_properties(user_point[:border])
259
+ end
260
+
261
+ # Set the fill properties for the chartarea.
262
+ fill = fill_properties(user_point[:fill])
263
+
264
+ point = {}
265
+ point[:_line] = line
266
+ point[:_fill] = fill
267
+ end
268
+ points << point
269
+ end
270
+ points
271
+ end
272
+
273
+ #
274
+ # Convert user defined labels properties to the structure required internally.
275
+ #
276
+ def labels_properties(labels) # :nodoc:
277
+ return nil unless labels
278
+
279
+ position = labels[:position]
280
+ if position.nil? || position.empty?
281
+ labels.delete(:position)
282
+ else
283
+ # Map user defined label positions to Excel positions.
284
+ positions = {
285
+ :center => 'ctr',
286
+ :right => 'r',
287
+ :left => 'l',
288
+ :top => 't',
289
+ :above => 't',
290
+ :bottom => 'b',
291
+ :below => 'b',
292
+ :inside_end => 'inEnd',
293
+ :outside_end => 'outEnd',
294
+ :best_fit => 'bestFit'
295
+ }
296
+
297
+ labels[:position] = value_or_raise(positions, position, 'label position')
298
+ end
299
+
300
+ labels
301
+ end
302
+
303
+ def value_or_raise(hash, key, msg)
304
+ raise "Unknown #{msg} '#{key}'" unless hash[key.to_sym]
305
+ hash[key.to_sym]
306
+ end
307
+ end
@@ -95,19 +95,19 @@ def modify_series_formatting
95
95
  array = []
96
96
  @series.each do |series|
97
97
  if index % 4 != 3
98
- if series[:_line][:_defined].nil? || series[:_line][:_defined] == 0
99
- series[:_line] = {
98
+ if series.line[:_defined].nil? || series.line[:_defined] == 0
99
+ series.line = {
100
100
  :width => 2.25,
101
101
  :none => 1,
102
102
  :_defined => 1
103
103
  }
104
104
  end
105
105
 
106
- if series[:_marker].nil? || series[:_marker] == 0
106
+ if series.marker.nil? || series.marker == 0
107
107
  if index % 4 == 2
108
- series[:_marker] = { :type => 'dot', :size => 3 }
108
+ series.marker = { :type => 'dot', :size => 3 }
109
109
  else
110
- series[:_marker] = { :type => 'none' }
110
+ series.marker = { :type => 'none' }
111
111
  end
112
112
  end
113
113
  end