write_xlsx 0.65.1 → 0.69.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +7 -0
  3. data/lib/write_xlsx/chart.rb +20 -2
  4. data/lib/write_xlsx/drawing.rb +4 -7
  5. data/lib/write_xlsx/package/comments.rb +13 -10
  6. data/lib/write_xlsx/package/conditional_format.rb +1 -1
  7. data/lib/write_xlsx/package/table.rb +4 -18
  8. data/lib/write_xlsx/package/vml.rb +2 -2
  9. data/lib/write_xlsx/sheets.rb +10 -4
  10. data/lib/write_xlsx/utility.rb +7 -10
  11. data/lib/write_xlsx/version.rb +1 -1
  12. data/lib/write_xlsx/workbook.rb +73 -88
  13. data/lib/write_xlsx/worksheet.rb +39 -85
  14. data/lib/write_xlsx/worksheet/hyperlink.rb +2 -1
  15. data/test/drawing/test_write_c_chart.rb +1 -1
  16. data/test/package/table/test_table13.rb +69 -0
  17. data/test/regression/test_button08.rb +28 -0
  18. data/test/regression/test_chart_axis25.rb +45 -0
  19. data/test/regression/test_chart_axis26.rb +45 -0
  20. data/test/regression/test_chart_axis27.rb +45 -0
  21. data/test/regression/test_chart_axis28.rb +45 -0
  22. data/test/regression/test_chart_axis29.rb +44 -0
  23. data/test/regression/test_chart_scatter08.rb +57 -0
  24. data/test/regression/test_comment11.rb +33 -0
  25. data/test/regression/test_selection01.rb +23 -0
  26. data/test/regression/test_selection02.rb +33 -0
  27. data/test/regression/test_shape01.rb +25 -0
  28. data/test/regression/test_shape02.rb +42 -0
  29. data/test/regression/test_shape03.rb +45 -0
  30. data/test/regression/test_shape04.rb +43 -0
  31. data/test/regression/test_table15.rb +37 -0
  32. data/test/regression/test_utf8_01.rb +23 -0
  33. data/test/regression/test_utf8_03.rb +23 -0
  34. data/test/regression/test_utf8_04.rb +23 -0
  35. data/test/regression/test_utf8_05.rb +26 -0
  36. data/test/regression/test_utf8_06.rb +28 -0
  37. data/test/regression/test_utf8_07.rb +27 -0
  38. data/test/regression/test_utf8_08.rb +38 -0
  39. data/test/regression/test_utf8_09.rb +24 -0
  40. data/test/regression/test_utf8_10.rb +42 -0
  41. data/test/regression/xlsx_files/button08.xlsx +0 -0
  42. data/test/regression/xlsx_files/button09.xlsx +0 -0
  43. data/test/regression/xlsx_files/button10.xlsx +0 -0
  44. data/test/regression/xlsx_files/button11.xlsx +0 -0
  45. data/test/regression/xlsx_files/button12.xlsx +0 -0
  46. data/test/regression/xlsx_files/chart_axis25.xlsx +0 -0
  47. data/test/regression/xlsx_files/chart_axis26.xlsx +0 -0
  48. data/test/regression/xlsx_files/chart_axis27.xlsx +0 -0
  49. data/test/regression/xlsx_files/chart_axis28.xlsx +0 -0
  50. data/test/regression/xlsx_files/chart_axis29.xlsx +0 -0
  51. data/test/regression/xlsx_files/chart_scatter08.xlsx +0 -0
  52. data/test/regression/xlsx_files/comment11.xlsx +0 -0
  53. data/test/regression/xlsx_files/selection01.xlsx +0 -0
  54. data/test/regression/xlsx_files/selection02.xlsx +0 -0
  55. data/test/regression/xlsx_files/shape01.xlsx +0 -0
  56. data/test/regression/xlsx_files/shape02.xlsx +0 -0
  57. data/test/regression/xlsx_files/shape03.xlsx +0 -0
  58. data/test/regression/xlsx_files/shape04.xlsx +0 -0
  59. data/test/regression/xlsx_files/table15.xlsx +0 -0
  60. data/test/regression/xlsx_files/utf8_01.xlsx +0 -0
  61. data/test/regression/xlsx_files/utf8_03.xlsx +0 -0
  62. data/test/regression/xlsx_files/utf8_04.xlsx +0 -0
  63. data/test/regression/xlsx_files/utf8_05.xlsx +0 -0
  64. data/test/regression/xlsx_files/utf8_06.xlsx +0 -0
  65. data/test/regression/xlsx_files/utf8_07.xlsx +0 -0
  66. data/test/regression/xlsx_files/utf8_08.xlsx +0 -0
  67. data/test/regression/xlsx_files/utf8_09.xlsx +0 -0
  68. data/test/regression/xlsx_files/utf8_10.xlsx +0 -0
  69. data/test/workbook/test_define_name.rb +16 -0
  70. data/test/worksheet/test_cond_format_18.rb +1 -1
  71. data/test/worksheet/test_convert_date_time_04.rb +19 -0
  72. data/test/worksheet/test_write_row_element.rb +14 -14
  73. metadata +113 -3
@@ -790,7 +790,7 @@ def set_selection(*args)
790
790
  if row_first == row_last && col_first == col_last
791
791
  sqref = active_cell
792
792
  else
793
- sqref = xl_range(row_first, col_first, row_last, col_last)
793
+ sqref = xl_range(row_first, row_last, col_first, col_last)
794
794
  end
795
795
  end
796
796
 
@@ -5463,10 +5463,6 @@ def set_comments_author(author)
5463
5463
  self.comments_author = author
5464
5464
  end
5465
5465
 
5466
- def comments_count # :nodoc:
5467
- @comments.size
5468
- end
5469
-
5470
5466
  def has_vml? # :nodoc:
5471
5467
  @has_vml
5472
5468
  end
@@ -5475,13 +5471,17 @@ def has_comments? # :nodoc:
5475
5471
  !@comments.empty?
5476
5472
  end
5477
5473
 
5474
+ def has_shapes?
5475
+ @has_shapes
5476
+ end
5477
+
5478
5478
  def is_chartsheet? # :nodoc:
5479
5479
  !!@is_chartsheet
5480
5480
  end
5481
5481
 
5482
- def set_external_vml_links(comment_id) # :nodoc:
5482
+ def set_external_vml_links(vml_drawing_id) # :nodoc:
5483
5483
  @external_vml_links <<
5484
- ['/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"]
5484
+ ['/vmlDrawing', "../drawings/vmlDrawing#{vml_drawing_id}.vml"]
5485
5485
  end
5486
5486
 
5487
5487
  def set_external_comment_links(comment_id) # :nodoc:
@@ -5662,7 +5662,7 @@ def comments_visible? # :nodoc:
5662
5662
  !!@comments_visible
5663
5663
  end
5664
5664
 
5665
- def comments_array # :nodoc:
5665
+ def sorted_comments # :nodoc:
5666
5666
  @comments.sorted_comments
5667
5667
  end
5668
5668
 
@@ -5734,28 +5734,21 @@ def drawing_links
5734
5734
  # Turn the HoH that stores the comments into an array for easier handling
5735
5735
  # and set the external links for comments and buttons.
5736
5736
  #
5737
- def prepare_vml_objects(vml_data_id, vml_shape_id, comment_id)
5738
- @external_vml_links <<
5739
- [ '/vmlDrawing', "../drawings/vmlDrawing#{comment_id}.vml"]
5740
-
5741
- if has_comments?
5742
- @comments_array = @comments.sorted_comments
5743
- @external_comment_links <<
5744
- [ '/comments', "../comments#{comment_id}.xml" ]
5745
- end
5746
-
5747
- count = @comments.size
5748
- start_data_id = vml_data_id
5737
+ def prepare_vml_objects(vml_data_id, vml_shape_id, vml_drawing_id, comment_id)
5738
+ set_external_vml_links(vml_drawing_id)
5739
+ set_external_comment_links(comment_id) if has_comments?
5749
5740
 
5750
5741
  # The VML o:idmap data id contains a comma separated range when there is
5751
5742
  # more than one 1024 block of comments, like this: data="1,2".
5752
- (1 .. (count / 1024)).each do |i|
5753
- vml_data_id = "#{vml_data_id},#{start_data_id + i}"
5743
+ (1 .. num_comments_block).each do |i|
5744
+ vml_data_id = "#{vml_data_id},#{vml_data_id + i}"
5754
5745
  end
5755
5746
  @vml_data_id = vml_data_id
5756
5747
  @vml_shape_id = vml_shape_id
5748
+ end
5757
5749
 
5758
- count
5750
+ def num_comments_block
5751
+ @comments.size / 1024
5759
5752
  end
5760
5753
 
5761
5754
  def tables_count
@@ -6244,6 +6237,7 @@ def prepare_shape(index, drawing_id)
6244
6237
  @drawing = Drawing.new
6245
6238
  @drawing.embedded = 1
6246
6239
  @external_drawing_links << ['/drawing', "../drawings/drawing#{drawing_id}.xml"]
6240
+ @has_shapes = true
6247
6241
  end
6248
6242
 
6249
6243
  # Validate the he shape against various rules.
@@ -6593,14 +6587,10 @@ def write_rows #:nodoc:
6593
6587
 
6594
6588
  # Write the cells if the row contains data.
6595
6589
  if @cell_data_table[row_num]
6596
- if !@set_rows[row_num]
6597
- write_row_element(row_num, span)
6598
- else
6599
- write_row_element(row_num, span, *(@set_rows[row_num]))
6590
+ args = @set_rows[row_num] || []
6591
+ write_row_element(row_num, span, *args) do
6592
+ write_cell_column_dimension(row_num)
6600
6593
  end
6601
-
6602
- write_cell_column_dimension(row_num)
6603
- @writer.end_tag('row')
6604
6594
  elsif @comments[row_num]
6605
6595
  write_empty_row(row_num, span, *(@set_rows[row_num]))
6606
6596
  else
@@ -6610,40 +6600,6 @@ def write_rows #:nodoc:
6610
6600
  end
6611
6601
  end
6612
6602
 
6613
- #
6614
- # Write out the worksheet data as a single row with cells. This method is
6615
- # used when memory optimisation is on. A single row is written and the data
6616
- # table is reset. That way only one row of data is kept in memory at any one
6617
- # time. We don't write span data in the optimised case since it is optional.
6618
- #
6619
- def write_single_row(current_row = 0) #:nodoc:
6620
- row_num = @previous_row
6621
-
6622
- # Set the new previous row as the current row.
6623
- @previous_row = current_row
6624
-
6625
- # Skip row if it doesn't contain row formatting, cell data or a comment.
6626
- return not_contain_formatting_or_data?(row_num)
6627
-
6628
- # Write the cells if the row contains data.
6629
- if @cell_data_table[row_num]
6630
- if !@set_rows[row_num]
6631
- write_row(row_num)
6632
- else
6633
- write_row(row_num, nil, @set_rows[row_num])
6634
- end
6635
-
6636
- write_cell_column_dimension(row_num)
6637
- @writer.end_tag('row')
6638
- else
6639
- # Row attributes or comments only.
6640
- write_empty_row(row_num, nil, @set_rows[row_num])
6641
- end
6642
-
6643
- # Reset table.
6644
- @cell_data_table = {}
6645
- end
6646
-
6647
6603
  def not_contain_formatting_or_data?(row_num) # :nodoc:
6648
6604
  !@set_rows[row_num] && !@cell_data_table[row_num] && !@comments.has_comment_in_row?(row_num)
6649
6605
  end
@@ -6657,12 +6613,24 @@ def write_cell_column_dimension(row_num) # :nodoc:
6657
6613
  #
6658
6614
  # Write the <row> element.
6659
6615
  #
6660
- def write_row_element(r, spans = nil, height = nil, format = nil, hidden = false, level = 0, collapsed = false, empty_row = false) #:nodoc:
6616
+ def write_row_element(*args) # :nodoc:
6617
+ @writer.tag_elements('row', row_attributes(args)) do
6618
+ yield
6619
+ end
6620
+ end
6621
+
6622
+ #
6623
+ # Write and empty <row> element, i.e., attributes only, no cell data.
6624
+ #
6625
+ def write_empty_row(*args) #:nodoc:
6626
+ @writer.empty_tag('row', row_attributes(args))
6627
+ end
6628
+
6629
+ def row_attributes(args)
6630
+ r, spans, height, format, hidden, level, collapsed, empty_row = args
6661
6631
  height ||= @default_row_height
6662
6632
  hidden ||= 0
6663
6633
  level ||= 0
6664
- collapsed ||= 0
6665
- empty_row ||= 0
6666
6634
  xf_index = format ? format.get_xf_index : 0
6667
6635
 
6668
6636
  attributes = ['r', r + 1]
@@ -6679,20 +6647,7 @@ def write_row_element(r, spans = nil, height = nil, format = nil, hidden = false
6679
6647
  if @excel_version == 2010
6680
6648
  attributes << 'x14ac:dyDescent' << '0.25'
6681
6649
  end
6682
- if ptrue?(empty_row)
6683
- @writer.empty_tag('row', attributes)
6684
- else
6685
- @writer.start_tag('row', attributes)
6686
- end
6687
- end
6688
-
6689
- #
6690
- # Write and empty <row> element, i.e., attributes only, no cell data.
6691
- #
6692
- def write_empty_row(*args) #:nodoc:
6693
- new_args = args.dup
6694
- new_args[7] = 1
6695
- write_row_element(*new_args)
6650
+ attributes
6696
6651
  end
6697
6652
 
6698
6653
  #
@@ -7148,10 +7103,9 @@ def write_tab_color #:nodoc:
7148
7103
  # Write the <outlinePr> element.
7149
7104
  #
7150
7105
  def write_outline_pr
7151
- attributes = []
7152
-
7153
7106
  return unless outline_changed?
7154
7107
 
7108
+ attributes = []
7155
7109
  attributes << "applyStyles" << 1 if @outline_style != 0
7156
7110
  attributes << "summaryBelow" << 0 if @outline_below == 0
7157
7111
  attributes << "summaryRight" << 0 if @outline_right == 0
@@ -7228,7 +7182,7 @@ def write_table_parts
7228
7182
  # Write the <tablePart> element.
7229
7183
  #
7230
7184
  def write_table_part(id)
7231
- @writer.empty_tag('tablePart', ['r:id', "rId#{id}"])
7185
+ @writer.empty_tag('tablePart', r_id_attributes(id))
7232
7186
  end
7233
7187
 
7234
7188
  def increment_rel_id_and_write_r_id(tag)
@@ -7237,7 +7191,7 @@ def increment_rel_id_and_write_r_id(tag)
7237
7191
  end
7238
7192
 
7239
7193
  def write_r_id(tag, id)
7240
- @writer.empty_tag(tag, ['r:id', "rId#{id}"])
7194
+ @writer.empty_tag(tag, r_id_attributes(id))
7241
7195
  end
7242
7196
 
7243
7197
  #
@@ -90,7 +90,8 @@ def initialize(url, str = nil)
90
90
  def write_external_attributes(row, col, id)
91
91
  ref = xl_rowcol_to_cell(row, col)
92
92
 
93
- attributes = ['ref', ref, 'r:id', "rId#{id}"]
93
+ attributes = ['ref', ref]
94
+ attributes += r_id_attributes(id)
94
95
 
95
96
  attributes << 'location' << url_str if url_str
96
97
  attributes << 'display' << display if display
@@ -10,7 +10,7 @@ def setup
10
10
  def test_write_c_chart
11
11
  expected = '<c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId1"/>'
12
12
 
13
- @drawing.__send__(:write_c_chart, 'rId1')
13
+ @drawing.__send__(:write_c_chart, 1)
14
14
  result = @drawing.instance_variable_get(:@writer).string
15
15
 
16
16
  assert_equal(expected, result)
@@ -0,0 +1,69 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+ require 'write_xlsx'
4
+ require 'stringio'
5
+
6
+ class TestTable13 < Test::Unit::TestCase
7
+ def setup
8
+ workbook = WriteXLSX.new(StringIO.new)
9
+ @worksheet = workbook.add_worksheet
10
+ end
11
+
12
+ def test_table13_add_table_should_not_change_style_string
13
+ style = 'Table Style Light 17'
14
+ # Set the table properties.
15
+ @worksheet.add_table('D4:I15', :style => style)
16
+
17
+ # add_table should not change style string.
18
+ assert_equal('Table Style Light 17', style)
19
+ end
20
+
21
+ def test_table13_add_table_should_not_change_formula_string
22
+ formula = '=SUM(Table1[@[Column1]:[Column3]])'
23
+ @worksheet.add_table(
24
+ 'C2:F14',
25
+ {
26
+ :total_row => 1,
27
+ :columns => [
28
+ {:total_string => 'Total'},
29
+ {},
30
+ {},
31
+ {
32
+ :total_function => 'count',
33
+ :format => @format,
34
+ :formula => formula
35
+ }
36
+ ]
37
+ }
38
+ )
39
+
40
+ # add_table should not change style string.
41
+ assert_equal('=SUM(Table1[@[Column1]:[Column3]])', formula)
42
+ end
43
+
44
+ def test_table13_add_table_should_not_change_total_function_string
45
+ total_function = 'std Dev'
46
+ # Set the table properties.
47
+
48
+ @worksheet.add_table(
49
+ 'B2:K8',
50
+ {
51
+ :total_row => 1,
52
+ :columns => [
53
+ {:total_string => 'Total'},
54
+ {},
55
+ {:total_function => 'Average'},
56
+ {:total_function => 'COUNT'},
57
+ {:total_function => 'count_nums'},
58
+ {:total_function => 'max'},
59
+ {:total_function => 'min'},
60
+ {:total_function => 'sum'},
61
+ {:total_function => total_function},
62
+ {:total_function => 'var'}
63
+ ]
64
+ }
65
+ )
66
+ # add_table should not change total_function string.
67
+ assert_equal('std Dev', total_function)
68
+ end
69
+ end
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ class TestRegressionButton08 < 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_button08
14
+ @xlsx = 'button08.xlsx'
15
+ workbook = WriteXLSX.new(@xlsx)
16
+ worksheet1 = workbook.add_worksheet
17
+ worksheet2 = workbook.add_worksheet
18
+
19
+ worksheet1.insert_button('C2', {})
20
+
21
+ worksheet2.write_comment('A1', 'Foo')
22
+
23
+ worksheet2.comments_author = 'John'
24
+
25
+ workbook.close
26
+ compare_xlsx_for_regression(File.join(@regression_output, @xlsx), @xlsx)
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ class TestRegressionChartAxis25 < 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_axis25
14
+ @xlsx = 'chart_axis25.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, [47471232, 48509696])
21
+ data = [
22
+ [ 1, 2, 3, 4, 5 ],
23
+ [ 2, 4, 6, 8, 10 ],
24
+ [ 3, 6, 9, 12, 15 ]
25
+ ]
26
+
27
+ worksheet.write('A1', data)
28
+
29
+ chart.add_series(:values => '=Sheet1!$A$1:$A$5')
30
+ chart.add_series(:values => '=Sheet1!$B$1:$B$5')
31
+ chart.add_series(:values => '=Sheet1!$C$1:$C$5')
32
+
33
+ chart.set_x_axis(:num_format => '[$¥-411]#,##0.00')
34
+ chart.set_y_axis(:num_format => '0.00%')
35
+
36
+ worksheet.insert_chart('E9', chart)
37
+
38
+ workbook.close
39
+ compare_xlsx_for_regression(File.join(@regression_output, @xlsx),
40
+ @xlsx,
41
+ nil,
42
+ { 'xl/charts/chart1.xml' => ['<c:pageMargins'] }
43
+ )
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ class TestRegressionChartAxis26 < 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_axis26
14
+ @xlsx = 'chart_axis26.xlsx'
15
+ workbook = WriteXLSX.new(@xlsx)
16
+ worksheet = workbook.add_worksheet
17
+ chart = workbook.add_chart(:type => 'line', :embedded => 1)
18
+
19
+ # For testing, copy the randomly generated axis ids in the target xlsx file.
20
+ chart.instance_variable_set(:@axis_ids, [73048448, 73049984])
21
+
22
+ data = [
23
+ [ 1, 2, 3, 4, 5 ],
24
+ [ 2, 4, 6, 8, 10 ],
25
+ [ 3, 6, 9, 12, 15 ]
26
+ ]
27
+
28
+ chart.set_x_axis(:num_font => {:rotation => 45})
29
+
30
+ worksheet.write('A1', data)
31
+
32
+ chart.add_series(:values => '=Sheet1!$A$1:$A$5')
33
+ chart.add_series(:values => '=Sheet1!$B$1:$B$5')
34
+ chart.add_series(:values => '=Sheet1!$C$1:$C$5')
35
+
36
+ worksheet.insert_chart('E9', chart)
37
+
38
+ workbook.close
39
+ compare_xlsx_for_regression(File.join(@regression_output, @xlsx),
40
+ @xlsx,
41
+ nil,
42
+ { 'xl/charts/chart1.xml' => ['<a:defRPr'] }
43
+ )
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'helper'
3
+
4
+ class TestRegressionChartAxis27 < 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_axis27
14
+ @xlsx = 'chart_axis27.xlsx'
15
+ workbook = WriteXLSX.new(@xlsx)
16
+ worksheet = workbook.add_worksheet
17
+ chart = workbook.add_chart(:type => 'line', :embedded => 1)
18
+
19
+ # For testing, copy the randomly generated axis ids in the target xlsx file.
20
+ chart.instance_variable_set(:@axis_ids, [73048448, 73049984])
21
+
22
+ data = [
23
+ [ 1, 2, 3, 4, 5 ],
24
+ [ 2, 4, 6, 8, 10 ],
25
+ [ 3, 6, 9, 12, 15 ]
26
+ ]
27
+
28
+ chart.set_x_axis(:num_font => {:rotation => -35})
29
+
30
+ worksheet.write('A1', data)
31
+
32
+ chart.add_series(:values => '=Sheet1!$A$1:$A$5')
33
+ chart.add_series(:values => '=Sheet1!$B$1:$B$5')
34
+ chart.add_series(:values => '=Sheet1!$C$1:$C$5')
35
+
36
+ worksheet.insert_chart('E9', chart)
37
+
38
+ workbook.close
39
+ compare_xlsx_for_regression(File.join(@regression_output, @xlsx),
40
+ @xlsx,
41
+ nil,
42
+ { 'xl/charts/chart1.xml' => ['<a:defRPr'] }
43
+ )
44
+ end
45
+ end