write_xlsx 0.65.1 → 0.69.0

Sign up to get free protection for your applications and to get access to all the features.
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