write_xlsx 0.70.0 → 0.71.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 +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