write_xlsx 0.79.0 → 0.80.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/Changes +6 -0
  3. data/README.md +1 -1
  4. data/lib/write_xlsx/chart.rb +38 -1
  5. data/lib/write_xlsx/chart/area.rb +4 -0
  6. data/lib/write_xlsx/chart/bar.rb +9 -0
  7. data/lib/write_xlsx/chart/column.rb +9 -0
  8. data/lib/write_xlsx/chart/line.rb +13 -0
  9. data/lib/write_xlsx/chart/pie.rb +10 -0
  10. data/lib/write_xlsx/chart/radar.rb +4 -0
  11. data/lib/write_xlsx/chart/scatter.rb +13 -0
  12. data/lib/write_xlsx/chart/series.rb +38 -26
  13. data/lib/write_xlsx/chart/stock.rb +13 -0
  14. data/lib/write_xlsx/utility.rb +1 -1
  15. data/lib/write_xlsx/version.rb +1 -1
  16. data/lib/write_xlsx/worksheet.rb +11 -4
  17. data/test/chart/test_write_d_lbls.rb +18 -1
  18. data/test/regression/test_chart_data_labels01.rb +47 -0
  19. data/test/regression/test_chart_data_labels02.rb +47 -0
  20. data/test/regression/test_chart_data_labels03.rb +47 -0
  21. data/test/regression/test_chart_data_labels04.rb +47 -0
  22. data/test/regression/test_chart_data_labels05.rb +50 -0
  23. data/test/regression/test_chart_data_labels06.rb +50 -0
  24. data/test/regression/test_chart_data_labels07.rb +40 -0
  25. data/test/regression/test_chart_data_labels08.rb +46 -0
  26. data/test/regression/test_chart_data_labels09.rb +47 -0
  27. data/test/regression/test_chart_data_labels10.rb +47 -0
  28. data/test/regression/test_chart_data_labels11.rb +37 -0
  29. data/test/regression/test_chart_data_labels12.rb +37 -0
  30. data/test/regression/test_chart_data_labels13.rb +37 -0
  31. data/test/regression/test_chart_data_labels14.rb +37 -0
  32. data/test/regression/test_chart_data_labels15.rb +37 -0
  33. data/test/regression/test_chart_data_labels16.rb +40 -0
  34. data/test/regression/test_chart_data_labels17.rb +63 -0
  35. data/test/regression/test_chart_data_labels18.rb +53 -0
  36. data/test/regression/test_chart_data_labels19.rb +53 -0
  37. data/test/regression/test_chart_data_labels20.rb +44 -0
  38. data/test/regression/test_chart_data_labels21.rb +48 -0
  39. data/test/regression/test_chart_data_labels22.rb +47 -0
  40. data/test/regression/test_chart_data_labels23.rb +50 -0
  41. data/test/regression/test_chart_format19.rb +1 -1
  42. data/test/regression/test_quote_name01.rb +48 -0
  43. data/test/regression/test_quote_name03.rb +41 -0
  44. data/test/regression/xlsx_files/chart_data_labels01.xlsx +0 -0
  45. data/test/regression/xlsx_files/chart_data_labels02.xlsx +0 -0
  46. data/test/regression/xlsx_files/chart_data_labels03.xlsx +0 -0
  47. data/test/regression/xlsx_files/chart_data_labels04.xlsx +0 -0
  48. data/test/regression/xlsx_files/chart_data_labels05.xlsx +0 -0
  49. data/test/regression/xlsx_files/chart_data_labels06.xlsx +0 -0
  50. data/test/regression/xlsx_files/chart_data_labels07.xlsx +0 -0
  51. data/test/regression/xlsx_files/chart_data_labels08.xlsx +0 -0
  52. data/test/regression/xlsx_files/chart_data_labels09.xlsx +0 -0
  53. data/test/regression/xlsx_files/chart_data_labels10.xlsx +0 -0
  54. data/test/regression/xlsx_files/chart_data_labels11.xlsx +0 -0
  55. data/test/regression/xlsx_files/chart_data_labels12.xlsx +0 -0
  56. data/test/regression/xlsx_files/chart_data_labels13.xlsx +0 -0
  57. data/test/regression/xlsx_files/chart_data_labels14.xlsx +0 -0
  58. data/test/regression/xlsx_files/chart_data_labels15.xlsx +0 -0
  59. data/test/regression/xlsx_files/chart_data_labels16.xlsx +0 -0
  60. data/test/regression/xlsx_files/chart_data_labels17.xlsx +0 -0
  61. data/test/regression/xlsx_files/chart_data_labels18.xlsx +0 -0
  62. data/test/regression/xlsx_files/chart_data_labels19.xlsx +0 -0
  63. data/test/regression/xlsx_files/chart_data_labels20.xlsx +0 -0
  64. data/test/regression/xlsx_files/chart_data_labels21.xlsx +0 -0
  65. data/test/regression/xlsx_files/chart_data_labels22.xlsx +0 -0
  66. data/test/regression/xlsx_files/chart_data_labels23.xlsx +0 -0
  67. data/test/regression/xlsx_files/chart_data_labels24.xlsx +0 -0
  68. data/test/regression/xlsx_files/quote_name01.xlsx +0 -0
  69. data/test/regression/xlsx_files/quote_name02.xlsx +0 -0
  70. data/test/regression/xlsx_files/quote_name03.xlsx +0 -0
  71. metadata +105 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e671b68b1de5d627fc5e2306b17a157dd669ac0b
4
- data.tar.gz: 9a33233b791e917331d2714bfa1212789ff7d515
3
+ metadata.gz: 9f7b091a25a820d796b03d56e1e599383281513d
4
+ data.tar.gz: 6253c7a5c2dc797c7924593d5e1747f721758899
5
5
  SHA512:
6
- metadata.gz: 2b218047dedb38eb475c41a3dda88d3eefca2d185ea34af3270f6d5ca9ebb884f1e81d3b8f893b16cbb84930fda41f28b2a62c926d2a58b9754c2c0e296204ce
7
- data.tar.gz: d9ea75c34d0510b98d1a9a985fabbe3bcd62a73b8f31eba6b9d334313d1f94e29ffe6392dda2bc12cc84e1d4a778ace0e8ddff6afe530a5c5eb3661aeaa7ef07
6
+ metadata.gz: d4b0e1065e775a4638f962cbb5aa7345a1859f0c4fda350af0119a20c02b1455a7208480d5098283ba83f9ab64e616eed61684a84af0822a7de4a9fe4436be17
7
+ data.tar.gz: 57547d50d45c5961e8827049a0132ff50d392b927f0add7ea5f4bf57974398538d87d4d8bb3ce3d3ed88c36b5bdd81d7379a887cb2382e834d6b3d417429088c
data/Changes CHANGED
@@ -1,4 +1,10 @@
1
1
  Change history of write_xlsx rubygem.
2
+ 2014-11-30 v0.80.0
3
+ Chart Data Label enhancements. Added number formatting, font handling
4
+ (Excel::Writer::XLSX issue #106), separator (Same issue #107) and legend key.
5
+ Added chart specific handling of data label positions since not all
6
+ positions are available for all chart types. Excel::Writer::XLSX Issue #110.
7
+
2
8
  2014-11-29 v0.79.0
3
9
  Added option to add images to headers and footers.
4
10
  Added option to not scale heaader/footer with page.
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  gem to create a new file in the Excel 2007+ XLSX format, and you can use the
6
6
  same interface as writeexcel gem. write_xlsx is converted from Perl's module
7
- [Excel::Writer::XLSX-0.79](https://github.com/jmcnamara/excel-writer-xlsx) .
7
+ [Excel::Writer::XLSX-0.80](https://github.com/jmcnamara/excel-writer-xlsx) .
8
8
 
9
9
  ## Description
10
10
 
@@ -141,6 +141,7 @@ class Chart
141
141
  attr_reader :embedded, :formula_ids, :formula_data # :nodoc:
142
142
  attr_reader :x_scale, :y_scale, :x_offset, :y_offset # :nodoc:
143
143
  attr_reader :width, :height # :nodoc:
144
+ attr_reader :label_positions, :label_position_default
144
145
 
145
146
  #
146
147
  # Factory method for returning chart objects based on their class type.
@@ -1392,6 +1393,20 @@ def write_cat_number_format(axis)
1392
1393
  axis.write_cat_number_format(@writer, @cat_has_num_fmt)
1393
1394
  end
1394
1395
 
1396
+ #
1397
+ # Write the <c:numberFormat> element for data labels.
1398
+ #
1399
+ def write_data_label_number_format(format_code)
1400
+ source_linked = 0
1401
+
1402
+ attributes = [
1403
+ ['formatCode', format_code],
1404
+ ['sourceLinked', source_linked]
1405
+ ]
1406
+
1407
+ @writer.empty_tag('c:numFmt', attributes)
1408
+ end
1409
+
1395
1410
  #
1396
1411
  # Write the <c:majorTickMark> element.
1397
1412
  #
@@ -2232,8 +2247,14 @@ def write_d_lbls(labels) # :nodoc:
2232
2247
  return unless labels
2233
2248
 
2234
2249
  @writer.tag_elements('c:dLbls') do
2250
+ # Write the c:numFmt element.
2251
+ write_data_label_number_format(labels[:num_format]) if labels[:num_format]
2252
+ # Write the data label font elements.
2253
+ write_axis_font(labels[:font]) if labels[:font]
2235
2254
  # Write the c:dLblPos element.
2236
- write_d_lbl_pos(labels[:position]) if labels[:position]
2255
+ write_d_lbl_pos(labels[:position]) if ptrue?(labels[:position])
2256
+ # Write the c:showLegendKey element.
2257
+ write_show_legend_key if labels[:legend_key]
2237
2258
  # Write the c:showVal element.
2238
2259
  write_show_val if labels[:value]
2239
2260
  # Write the c:showCatName element.
@@ -2242,11 +2263,20 @@ def write_d_lbls(labels) # :nodoc:
2242
2263
  write_show_ser_name if labels[:series_name]
2243
2264
  # Write the c:showPercent element.
2244
2265
  write_show_percent if labels[:percentage]
2266
+ # Write the c:separator element.
2267
+ write_separator(labels[:separator]) if labels[:separator]
2245
2268
  # Write the c:showLeaderLines element.
2246
2269
  write_show_leader_lines if labels[:leader_lines]
2247
2270
  end
2248
2271
  end
2249
2272
 
2273
+ #
2274
+ # Write the <c:showLegendKey> element.
2275
+ #
2276
+ def write_show_legend_key
2277
+ @writer.empty_tag('c:showLegendKey', [ ['val', 1] ])
2278
+ end
2279
+
2250
2280
  #
2251
2281
  # Write the <c:showVal> element.
2252
2282
  #
@@ -2275,6 +2305,13 @@ def write_show_percent
2275
2305
  @writer.empty_tag('c:showPercent', [ ['val', 1] ])
2276
2306
  end
2277
2307
 
2308
+ #
2309
+ # Write the <c:separator> element.
2310
+ #
2311
+ def write_separator(data)
2312
+ @writer.data_element('c:separator', data)
2313
+ end
2314
+
2278
2315
  #
2279
2316
  # Write the <c:showLeaderLines> element.
2280
2317
  #
@@ -32,6 +32,10 @@ def initialize(subtype)
32
32
  end
33
33
 
34
34
  set_y_axis
35
+
36
+ # Set the available data label positions for this chart type.
37
+ @label_position_default = 'center'
38
+ @label_positions = { 'center' => 'ctr' }
35
39
  end
36
40
 
37
41
  #
@@ -31,6 +31,15 @@ def initialize(subtype)
31
31
  axis_defaults_set
32
32
  set_x_axis
33
33
  set_y_axis
34
+
35
+ # Set the available data label positions for this chart type.
36
+ @label_position_default = 'outside_end'
37
+ @label_positions = {
38
+ 'center' => 'ctr',
39
+ 'inside_base' => 'inBase',
40
+ 'inside_end' => 'inEnd',
41
+ 'outside_end' => 'outEnd'
42
+ }
34
43
  end
35
44
 
36
45
  #
@@ -39,6 +39,15 @@ def initialize(subtype)
39
39
  end
40
40
 
41
41
  set_y_axis
42
+
43
+ # Set the available data label positions for this chart type.
44
+ @label_position_default = 'outside_end'
45
+ @label_positions = {
46
+ 'center' => 'ctr',
47
+ 'inside_base' => 'inBase',
48
+ 'inside_end' => 'inEnd',
49
+ 'outside_end' => 'outEnd'
50
+ }
42
51
  end
43
52
 
44
53
  #
@@ -24,6 +24,19 @@ def initialize(subtype)
24
24
  super(subtype)
25
25
  @default_marker = Marker.new(:type => 'none')
26
26
  @smooth_allowed = 1
27
+
28
+ # Set the available data label positions for this chart type.
29
+ @label_position_default = 'right'
30
+ @label_positions = {
31
+ 'center' => 'ctr',
32
+ 'right' => 'r',
33
+ 'left' => 'l',
34
+ 'above' => 't',
35
+ 'below' => 'b',
36
+ # For backward compatibility.
37
+ 'top' => 't',
38
+ 'bottom' => 'b'
39
+ }
27
40
  end
28
41
 
29
42
  #
@@ -30,6 +30,16 @@ def initialize(subtype)
30
30
  super(subtype)
31
31
  @vary_data_color = 1
32
32
  @rotation = 0
33
+
34
+ # Set the available data label positions for this chart type.
35
+ @label_position_default = 'best_fit'
36
+ @label_positions = {
37
+ 'center' => 'ctr',
38
+ 'inside_base' => 'inBase',
39
+ 'inside_end' => 'inEnd',
40
+ 'outside_end' => 'outEnd',
41
+ 'best_fit' => 'bestFit'
42
+ }
33
43
  end
34
44
 
35
45
  #
@@ -41,6 +41,10 @@ def initialize(subtype)
41
41
 
42
42
  # Hardcode major_tick_mark for now untill there is an accessor.
43
43
  @y_axis.major_tick_mark = 'cross'
44
+
45
+ # Set the available data label positions for this chart type.
46
+ @label_position_default = 'center'
47
+ @label_positions = { 'center' => 'ctr' }
44
48
  end
45
49
 
46
50
  #
@@ -43,6 +43,19 @@ def initialize(subtype)
43
43
  @horiz_val_axis = 0
44
44
  @val_axis_position = 'b'
45
45
  @smooth_allowed = 1
46
+
47
+ # Set the available data label positions for this chart type.
48
+ @label_position_default = 'right'
49
+ @label_positions = {
50
+ 'center' => 'ctr',
51
+ 'right' => 'r',
52
+ 'left' => 'l',
53
+ 'above' => 't',
54
+ 'below' => 'b',
55
+ # For backward compatibility.
56
+ 'top' => 't',
57
+ 'bottom' => 'b'
58
+ }
46
59
  end
47
60
 
48
61
  #
@@ -168,12 +168,13 @@ class Series
168
168
  attr_accessor :line, :marker
169
169
 
170
170
  def initialize(chart, params = {})
171
+ @chart = chart
171
172
  @values = aref_to_formula(params[:values])
172
173
  @categories = aref_to_formula(params[:categories])
173
174
  @name, @name_formula =
174
175
  chart.process_names(params[:name], params[:name_formula])
175
176
 
176
- set_data_ids(chart, params)
177
+ set_data_ids(params)
177
178
 
178
179
  @line = line_properties(params[:border] || params[:line])
179
180
  @fill = fill_properties(params[:fill])
@@ -182,6 +183,9 @@ def initialize(chart, params = {})
182
183
  @trendline = Trendline.new(params[:trendline]) if params[:trendline]
183
184
  @error_bars = errorbars(params[:x_error_bars], params[:y_error_bars])
184
185
  @points = params[:points].collect { |p| p ? Point.new(p) : p } if params[:points]
186
+
187
+ @label_positions = chart.label_positions
188
+ @label_position_default = chart.label_position_default
185
189
  @labels = labels_properties(params[:data_labels])
186
190
 
187
191
  [:smooth, :invert_if_negative, :x2_axis, :y2_axis].
@@ -214,10 +218,10 @@ def aref_to_formula(data) # :nodoc:
214
218
  xl_range_formula(*data)
215
219
  end
216
220
 
217
- def set_data_ids(chart, params)
218
- @cat_data_id = chart.data_id(@categories, params[:categories_data])
219
- @val_data_id = chart.data_id(@values, params[:values_data])
220
- @name_id = chart.data_id(@name_formula, params[:name_data])
221
+ def set_data_ids(params)
222
+ @cat_data_id = @chart.data_id(@categories, params[:categories_data])
223
+ @val_data_id = @chart.data_id(@values, params[:values_data])
224
+ @name_id = @chart.data_id(@name_formula, params[:name_data])
221
225
  end
222
226
 
223
227
  def errorbars(x, y)
@@ -233,31 +237,39 @@ def errorbars(x, y)
233
237
  def labels_properties(labels) # :nodoc:
234
238
  return nil unless labels
235
239
 
240
+ # Map user defined label positions to Excel positions.
236
241
  position = labels[:position]
237
- if position.nil? || position.empty?
238
- labels.delete(:position)
239
- else
240
- # Map user defined label positions to Excel positions.
241
- labels[:position] = value_or_raise(positions, position, 'label position')
242
+ if ptrue?(position)
243
+ if @label_positions[position]
244
+ if position == @label_position_default
245
+ labels[:position] = nil
246
+ else
247
+ labels[:position] = @label_positions[position]
248
+ end
249
+ else
250
+ raise "Unsupported label position '#{position}' for this chart type"
251
+ end
242
252
  end
243
253
 
244
- labels
245
- end
246
-
247
- def positions
248
- {
249
- :center => 'ctr',
250
- :right => 'r',
251
- :left => 'l',
252
- :top => 't',
253
- :above => 't',
254
- :bottom => 'b',
255
- :below => 'b',
256
- :inside_base => 'inBase',
257
- :inside_end => 'inEnd',
258
- :outside_end => 'outEnd',
259
- :best_fit => 'bestFit'
254
+ # Map the user defined label separator to the Excel separator.
255
+ separators = {
256
+ "," => ", ",
257
+ ";" => "; ",
258
+ "." => ". ",
259
+ "\n" => "\n",
260
+ " " => " "
260
261
  }
262
+ separator = labels[:separator]
263
+ unless separator.nil? || separator.empty?
264
+ raise "unsuppoted label separator #{separator}" unless separators[separator]
265
+ labels[:separator] = separators[separator]
266
+ end
267
+
268
+ if labels[:font]
269
+ labels[:font] = @chart.convert_font_args(labels[:font])
270
+ end
271
+
272
+ labels
261
273
  end
262
274
  end
263
275
  end
@@ -35,6 +35,19 @@ def initialize(subtype)
35
35
  @x2_axis.defaults[:num_format] = 'dd/mm/yyyy'
36
36
  set_x_axis
37
37
  set_x2_axis
38
+
39
+ # Set the available data label positions for this chart type.
40
+ @label_position_default = 'right'
41
+ @label_positions = {
42
+ 'center' => 'ctr',
43
+ 'right' => 'r',
44
+ 'left' => 'l',
45
+ 'above' => 't',
46
+ 'below' => 'b',
47
+ # For backward compatibility.
48
+ 'top' => 't',
49
+ 'bottom' => 'b',
50
+ }
38
51
  end
39
52
 
40
53
  #
@@ -551,7 +551,7 @@ def dash_types
551
551
  end
552
552
 
553
553
  def value_or_raise(hash, key, msg)
554
- raise "Unknown #{msg} '#{key}'" unless hash[key.to_sym]
554
+ raise "Unknown #{msg} '#{key}'" if hash[key.to_sym].nil?
555
555
  hash[key.to_sym]
556
556
  end
557
557
 
@@ -1,5 +1,5 @@
1
1
  require 'write_xlsx/workbook'
2
2
 
3
3
  class WriteXLSX < Writexlsx::Workbook
4
- VERSION = "0.79.0"
4
+ VERSION = "0.80.0"
5
5
  end
@@ -1489,7 +1489,7 @@ def repeat_rows(row_min, row_max = nil)
1489
1489
  area = "$#{row_min}:$#{row_max}"
1490
1490
 
1491
1491
  # Build up the print titles "Sheet1!$1:$2"
1492
- sheetname = quote_sheetname(name)
1492
+ sheetname = quote_sheetname(@name)
1493
1493
  @page_setup.repeat_rows = "#{sheetname}!#{area}"
1494
1494
  end
1495
1495
 
@@ -7515,7 +7515,7 @@ def convert_name_area(row_num_1, col_num_1, row_num_2, col_num_2) #:nodoc:
7515
7515
  end
7516
7516
 
7517
7517
  # Build up the print area range "Sheet1!$A$1:$C$13".
7518
- "#{quote_sheetname(name)}!#{area}"
7518
+ "#{quote_sheetname(@name)}!#{area}"
7519
7519
  end
7520
7520
 
7521
7521
  #
@@ -7524,8 +7524,15 @@ def convert_name_area(row_num_1, col_num_1, row_num_2, col_num_2) #:nodoc:
7524
7524
  # TODO. We need to handle more special cases.
7525
7525
  #
7526
7526
  def quote_sheetname(sheetname) #:nodoc:
7527
- return sheetname if sheetname =~ /^Sheet\d+$/
7528
- return "'#{sheetname}'"
7527
+ # Use Excel's conventions and quote the sheet name if it comtains any
7528
+ # non-word character or if it isn't already quoted.
7529
+ name = sheetname.dup
7530
+ if name =~ /\W/ && !(name =~ /^'/)
7531
+ # Double quote and single quoted strings.
7532
+ name = name.gsub(/'/, "''")
7533
+ name = "'#{name}'"
7534
+ end
7535
+ name
7529
7536
  end
7530
7537
 
7531
7538
  def fit_page? #:nodoc:
@@ -4,7 +4,24 @@
4
4
 
5
5
  class TestWriteDLbls < Test::Unit::TestCase
6
6
  def setup
7
- @chart = Writexlsx::Chart.new('Pie')
7
+ @chart = Writexlsx::Chart.new('Line')
8
+ @chart.instance_variable_set(
9
+ :@label_positions,
10
+ {
11
+ 'center' => 'ctr',
12
+ 'right' => 'r',
13
+ 'left' => 'l',
14
+ 'top' => 't',
15
+ 'above' => 't',
16
+ 'bottom' => 'b',
17
+ 'below' => 'b',
18
+ 'inside_base' => 'inBase',
19
+ 'inside_end' => 'inEnd',
20
+ 'outside_end' => 'outEnd',
21
+ 'best_fit' => 'bestFit'
22
+ }
23
+ )
24
+ @chart.instance_variable_set(:@label_position_default, '')
8
25
  @series = Writexlsx::Chart::Series.new(@chart)
9
26
  end
10
27
 
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ class TestRegressionChartDataLabels01 < Test::Unit::TestCase
5
+ def setup
6
+ setup_dir_var
7
+ end
8
+
9
+ def teardown
10
+ File.delete(@xlsx) if File.exist?(@xlsx)
11
+ end
12
+
13
+ def test_chart_data_labels01
14
+ @xlsx = 'chart_data_labels01.xlsx'
15
+ workbook = WriteXLSX.new(@xlsx)
16
+ worksheet = workbook.add_worksheet
17
+ chart = workbook.add_chart(:type => 'column', :embedded => 1)
18
+
19
+ # For testing, copy the randomly generated axis ids in the target xlsx file.
20
+ chart.instance_variable_set(:@axis_ids, [45848832, 47718784])
21
+
22
+ data = [
23
+ [1, 2, 3, 4, 5],
24
+ [2, 4, 6, 8, 10],
25
+ [3, 6, 9, 12, 15]
26
+ ]
27
+
28
+ worksheet.write('A1', data)
29
+
30
+ chart.add_series(
31
+ :values => '=Sheet1!$A$1:$A$5',
32
+ :data_labels => { :value => 1, :position => 'outside_end' }
33
+ )
34
+
35
+ chart.add_series(
36
+ :values => '=Sheet1!$B$1:$B$5',
37
+ :data_labels => { :value => 1, :position => 'inside_base' }
38
+ )
39
+
40
+ chart.add_series( :values => '=Sheet1!$C$1:$C$5' )
41
+
42
+ worksheet.insert_chart('E9', chart)
43
+
44
+ workbook.close
45
+ compare_xlsx_for_regression(File.join(@regression_output, @xlsx), @xlsx)
46
+ end
47
+ end