write_xlsx 1.11.1 → 1.12.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cabafeb058f3742956ec06251cab5ecf67d9b76cd5db3ec659e2e4184d2c1483
4
- data.tar.gz: 95a68e0390bff44e03dc8577b31162c1d1c2cb80d77f3943947e1114327afb01
3
+ metadata.gz: b8f704b9674f7d6de467d512cea780114dccc51ed88d99ad4d57727fdafd32b4
4
+ data.tar.gz: 54df4aec8346b1935bb3b0ba283c11b04f415b3b644308ab8f4cce2f0595d14d
5
5
  SHA512:
6
- metadata.gz: 70a98e3ffc2be69574805f72a63678b2023cc3aef9a1c1a97c5f8a13de80de29c641c573d8c8c9bd7d33da088827a15fb6feb3b4c7591b50ffb26cb321591b49
7
- data.tar.gz: 1d387aa195a7a35f0b664e4ff155ca71de765ad6ae886df66debc9063d1fea8c4c905ab10a7aef735d69813b79faa1d6e7ded96e70ce23a75df56e5f172b1648
6
+ metadata.gz: ecbd4957e8b5ee4d9c77e46da68ddfa0f877dd94166551a20c1b76a782209db761a43a86bae340657181ec8993bdc277ee67b66c4c889bc5ad418e6fe2b953c5
7
+ data.tar.gz: c2a9f0b209de183bbb87ddceb0897dca8708b372d7bb8a5fb7953595440fdaceac60fe4500dafe2ed74585208c6f17b03c554e20c4b5d05af2ec019854b0a259
data/.rubocop.yml CHANGED
@@ -82,6 +82,9 @@ Naming/HeredocDelimiterNaming:
82
82
  Naming/MethodName:
83
83
  Enabled: false
84
84
 
85
+ Naming/MethodParameterName:
86
+ Enabled: false
87
+
85
88
  Naming/VariableNumber:
86
89
  Enabled: false
87
90
 
data/Changes CHANGED
@@ -1,5 +1,14 @@
1
1
  Change history of write_xlsx rubygem.
2
2
 
3
+ 2023-12-26 v1.11.2
4
+ Fix issue where header images in chartsheets weren't displayed.
5
+ Add support for custom table total functions.
6
+ Add chart option to display N/A as empty cells.
7
+ Add support for leader lines to all chart types.
8
+ Fix issue where column formulas were overwritten by table data
9
+ add Worksheet#very_hidden method.
10
+ Add add trendline equation formatting for chart.
11
+
3
12
  2023-08-09 v1.11.1
4
13
  Fixed issue #110. Redefining costant Struct::ColInfo
5
14
 
data/README.md CHANGED
@@ -85,7 +85,7 @@ the first worksheet in an Excel XML spreadsheet called ruby.xlsx:
85
85
  Original Perl module was written by John McNamara(jmcnamara@cpan.org).
86
86
 
87
87
  Converted to ruby by Hideo NAKAMURA(nakamrua.hideo@gmail.com)
88
- Copyright (c) 2012-2023 Hideo NAKAMURA.
88
+ Copyright (c) 2012-2024 Hideo NAKAMURA.
89
89
 
90
90
  See LICENSE.txt for further details.
91
91
 
@@ -218,7 +218,7 @@ shapes_list.each_line do |line|
218
218
  line = line.chomp
219
219
  next unless line =~ /^\w/ # Skip blank lines and comments.
220
220
 
221
- sheet, name = line.split(/\t/)
221
+ sheet, name = line.split("\t")
222
222
  if last_sheet != sheet
223
223
  worksheet = workbook.add_worksheet(sheet)
224
224
  row = 2
@@ -123,8 +123,7 @@ module Writexlsx
123
123
  # Write the <c:legend> element.
124
124
  #
125
125
  def write_legend
126
- position = @legend.position
127
- allowed = %w[right left top bottom]
126
+ allowed = %w[right left top bottom]
128
127
  delete_series = @legend.delete_series || []
129
128
 
130
129
  if @legend.position =~ /^overlay_/
@@ -207,6 +206,19 @@ module Writexlsx
207
206
  def write_first_slice_ang
208
207
  @writer.empty_tag('c:firstSliceAng', [['val', @rotation]])
209
208
  end
209
+
210
+ #
211
+ # Write the <c:showLeaderLines> element. This is for Pie/Doughnut charts.
212
+ # Other chart types only supported leader lines after Excel 2015 via an
213
+ # extension element.
214
+ #
215
+ def write_show_leader_lines
216
+ val = 1
217
+
218
+ attributes = [['val', val]]
219
+
220
+ @writer.empty_tag('c:showLeaderLines', attributes)
221
+ end
210
222
  end
211
223
  end
212
224
  end
@@ -59,10 +59,13 @@ module Writexlsx
59
59
  class Trendline < Chartline
60
60
  attr_reader :name, :forward, :backward, :order, :period
61
61
  attr_reader :intercept, :display_equation, :display_r_squared
62
+ attr_reader :label
62
63
 
63
64
  def initialize(params)
64
65
  super(params)
65
66
 
67
+ @label = trendline_label_properties(params[:label])
68
+
66
69
  @name = params[:name]
67
70
  @forward = params[:forward]
68
71
  @backward = params[:backward]
@@ -76,6 +79,51 @@ module Writexlsx
76
79
 
77
80
  private
78
81
 
82
+ #
83
+ # Convert user defined trendline label properties to the structure required
84
+ # internally.
85
+ #
86
+ def trendline_label_properties(_label)
87
+ return unless _label || _label.is_a?(Hash)
88
+
89
+ # Copy the user supplied properties.
90
+ label = {}
91
+
92
+ # Set the font properties for the label.
93
+ label[:font] = convert_font_args(_label[:font]) if ptrue?(_label[:font])
94
+
95
+ # Set the line properties for the label.
96
+ line = line_properties(_label[:line])
97
+
98
+ # Allow 'border' as a synonym for 'line'.
99
+ line = line_properties(_label[:border]) if ptrue?(_label[:border])
100
+
101
+ # Set the fill properties for the label.
102
+ fill = fill_properties(_label[:fill])
103
+
104
+ # Set the pattern properties for the label.
105
+ pattern = pattern_properties(_label[:pattern])
106
+
107
+ # Set the gradient fill properties for the label.
108
+ gradient = gradient_properties(_label[:gradient])
109
+
110
+ # Pattern fill overrides solid fill.
111
+ fill = nil if ptrue?(pattern)
112
+
113
+ # Gradient fill overrides solid and pattern fills.
114
+ if ptrue?(gradient)
115
+ pattern = nil
116
+ fill = nil
117
+ end
118
+
119
+ label[:line] = line
120
+ label[:fill] = fill
121
+ label[:pattern] = pattern
122
+ label[:gradient] = gradient
123
+
124
+ label
125
+ end
126
+
79
127
  def types
80
128
  {
81
129
  exponential: 'exp',
@@ -568,6 +568,13 @@ module Writexlsx
568
568
  @is_secondary
569
569
  end
570
570
 
571
+ #
572
+ # Set the option for displaying #N/A as an empty cell in a chart.
573
+ #
574
+ def show_na_as_empty_cell
575
+ @show_na_as_empty = true
576
+ end
577
+
571
578
  private
572
579
 
573
580
  def axis_setup
@@ -598,6 +605,7 @@ module Writexlsx
598
605
  @cross_between = 'between'
599
606
  @date_category = false
600
607
  @show_blanks = 'gap'
608
+ @show_na_as_empty = false
601
609
  @show_hidden_data = false
602
610
  @show_crosses = true
603
611
  end
@@ -782,6 +790,9 @@ module Writexlsx
782
790
 
783
791
  # Write the c:dispBlanksAs element.
784
792
  write_disp_blanks_as
793
+
794
+ # Write the c:extLst element.
795
+ write_ext_lst_display_na if @show_na_as_empty
785
796
  end
786
797
  end
787
798
 
@@ -950,12 +961,13 @@ module Writexlsx
950
961
  write_val(series)
951
962
  # Write the c:smooth element.
952
963
  write_c_smooth(series.smooth) if ptrue?(@smooth_allowed)
953
- write_ext_lst(series.inverted_color) if series.inverted_color
964
+ # Write the c:extLst element.
965
+ write_ext_lst_inverted_fill(series.inverted_color) if series.inverted_color
954
966
  end
955
967
  @series_index += 1
956
968
  end
957
969
 
958
- def write_ext_lst(color)
970
+ def write_ext_lst_inverted_fill(color)
959
971
  uri = '{6F2FDCE9-48DA-4B69-8628-5D25D57E5C99}'
960
972
  xmlns_c_14 =
961
973
  'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'
@@ -980,6 +992,31 @@ module Writexlsx
980
992
  end
981
993
  end
982
994
 
995
+ #
996
+ # Write the <c:extLst> element for the display N/A as empty cell option.
997
+ #
998
+ def write_ext_lst_display_na
999
+ uri = '{56B9EC1D-385E-4148-901F-78D8002777C0}'
1000
+ xmlns_c_16 = 'http://schemas.microsoft.com/office/drawing/2017/03/chart'
1001
+
1002
+ attributes1 = [
1003
+ ['uri', uri],
1004
+ ['xmlns:c16r3', xmlns_c_16]
1005
+ ]
1006
+
1007
+ attributes2 = [
1008
+ ['val', 1]
1009
+ ]
1010
+
1011
+ @writer.tag_elements('c:extLst') do
1012
+ @writer.tag_elements('c:ext', attributes1) do
1013
+ @writer.tag_elements('c16r3:dataDisplayOptions16') do
1014
+ @writer.empty_tag('c16r3:dispNaAsBlank', attributes2)
1015
+ end
1016
+ end
1017
+ end
1018
+ end
1019
+
983
1020
  def write_ser_base(series)
984
1021
  # Write the c:idx element.
985
1022
  write_idx(@series_index)
@@ -2122,7 +2159,7 @@ module Writexlsx
2122
2159
  # Write the c:dispEq element.
2123
2160
  write_disp_eq
2124
2161
  # Write the c:trendlineLbl element.
2125
- write_trendline_lbl
2162
+ write_trendline_lbl(trendline)
2126
2163
  end
2127
2164
  end
2128
2165
  end
@@ -2199,12 +2236,18 @@ module Writexlsx
2199
2236
  #
2200
2237
  # Write the <c:trendlineLbl> element.
2201
2238
  #
2202
- def write_trendline_lbl
2239
+ def write_trendline_lbl(trendline)
2203
2240
  @writer.tag_elements('c:trendlineLbl') do
2204
2241
  # Write the c:layout element.
2205
2242
  write_layout
2206
2243
  # Write the c:numFmt element.
2207
2244
  write_trendline_num_fmt
2245
+ # Write the c:spPr element for the label formatting.
2246
+ write_sp_pr(trendline.label)
2247
+ # Write the data label font elements.
2248
+ if trendline.label && ptrue?(trendline.label[:font])
2249
+ write_axis_font(trendline.label[:font])
2250
+ end
2208
2251
  end
2209
2252
  end
2210
2253
 
@@ -2544,11 +2587,25 @@ module Writexlsx
2544
2587
  @writer.data_element('c:separator', data)
2545
2588
  end
2546
2589
 
2547
- #
2548
- # Write the <c:showLeaderLines> element.
2549
- #
2590
+ # Write the <c:showLeaderLines> element. This is different for Pie/Doughnut
2591
+ # charts. Other chart types only supported leader lines after Excel 2015 via
2592
+ # an extension element.
2550
2593
  def write_show_leader_lines
2551
- @writer.empty_tag('c:showLeaderLines', [['val', 1]])
2594
+ uri = '{CE6537A1-D6FC-4f65-9D91-7224C49458BB}'
2595
+ xmlns_c_15 = 'http://schemas.microsoft.com/office/drawing/2012/chart'
2596
+
2597
+ attributes1 = [
2598
+ ['uri', uri],
2599
+ ['xmlns:c15', xmlns_c_15]
2600
+ ]
2601
+
2602
+ attributes2 = [['val', 1]]
2603
+
2604
+ @writer.tag_elements('c:extLst') do
2605
+ @writer.tag_elements('c:ext', attributes1) do
2606
+ @writer.empty_tag('c15:showLeaderLines', attributes2)
2607
+ end
2608
+ end
2552
2609
  end
2553
2610
 
2554
2611
  #
@@ -2862,8 +2919,6 @@ module Writexlsx
2862
2919
  # Write the <a:fillToRect> element.
2863
2920
  #
2864
2921
  def write_a_fill_to_rect(type)
2865
- attributes = []
2866
-
2867
2922
  attributes = if type == 'shape'
2868
2923
  [
2869
2924
  ['l', 50000],
@@ -2885,8 +2940,6 @@ module Writexlsx
2885
2940
  # Write the <a:tileRect> element.
2886
2941
  #
2887
2942
  def write_a_tile_rect(type)
2888
- attributes = []
2889
-
2890
2943
  attributes = if type == 'shape'
2891
2944
  []
2892
2945
  else
@@ -54,6 +54,8 @@ module Writexlsx
54
54
  write_header_footer
55
55
  # Write the drawing element.
56
56
  write_drawings
57
+ # Write the legaacyDrawingHF element.
58
+ write_legacy_drawing_hf
57
59
  # Close the worksheet tag.
58
60
  end
59
61
  end
@@ -145,6 +147,10 @@ module Writexlsx
145
147
  @chart.show_blanks_as(*args)
146
148
  end
147
149
 
150
+ def show_na_as_empty_cell
151
+ @chart.show_na_as_empty_cell(*args)
152
+ end
153
+
148
154
  def show_hidden_data(*args)
149
155
  @chart.show_hidden_data(*args)
150
156
  end
@@ -185,7 +191,10 @@ module Writexlsx
185
191
  end
186
192
 
187
193
  def external_links
188
- [@external_drawing_links]
194
+ [
195
+ @external_drawing_links,
196
+ @external_vml_links
197
+ ]
189
198
  end
190
199
 
191
200
  private
@@ -7,12 +7,16 @@ class ColName
7
7
  include Singleton
8
8
 
9
9
  def initialize
10
- @col_str_table = {}
10
+ @col_str_table = []
11
+ @row_str_table = []
11
12
  end
12
13
 
13
14
  def col_str(col)
14
- @col_str_table[col] = col_str_build(col) unless @col_str_table[col]
15
- @col_str_table[col]
15
+ @col_str_table[col] ||= col_str_build(col)
16
+ end
17
+
18
+ def row_str(row)
19
+ @row_str_table[row] ||= row.to_s
16
20
  end
17
21
 
18
22
  private
@@ -146,10 +146,10 @@ module Writexlsx
146
146
  return
147
147
  end
148
148
 
149
- # Indent is only allowed for horizontal left, right and distributed. If it
150
- # is defined for any other alignment or no alignment has been set then
151
- # default to left alignment.
152
- @text_h_align = 1 if @indent != 0 && ![1, 3, 7].include?(@text_h_align)
149
+ # Indent is only allowed for some alignment properties. If it is defined
150
+ # for any other alignment or no alignment has been set then default to
151
+ # left alignment.
152
+ @text_h_align = 1 if @indent != 0 && ![1, 3, 7].include?(@text_h_align) && ![1, 3, 5].include?(@text_v_align)
153
153
 
154
154
  # Check for properties that are mutually exclusive.
155
155
  @shrink = 0 if @text_wrap != 0
@@ -178,8 +178,8 @@ module Writexlsx
178
178
  align << %w[vertical justify] if @text_v_align == 4
179
179
  align << %w[vertical distributed] if @text_v_align == 5
180
180
 
181
- align << ['indent', @indent] if @indent != 0
182
181
  align << ['textRotation', @rotation] if @rotation != 0
182
+ align << ['indent', @indent] if @indent != 0
183
183
 
184
184
  align << ['wrapText', 1] if @text_wrap != 0
185
185
  align << ['shrinkToFit', 1] if @shrink != 0
@@ -324,7 +324,7 @@ module Writexlsx
324
324
  return colors[color_code.downcase.to_sym] if colors[color_code.downcase.to_sym]
325
325
 
326
326
  # or the default color if string is unrecognised,
327
- return 0x00 if color_code =~ /\D/
327
+ 0x00 if color_code =~ /\D/
328
328
  else
329
329
  # or an index < 8 mapped into the correct range,
330
330
  return color_code + 8 if color_code < 8
@@ -46,7 +46,9 @@ module Writexlsx
46
46
  add_heading_pair(
47
47
  [
48
48
  'Worksheets',
49
- @workbook.worksheets.reject { |s| s.is_chartsheet? }.count
49
+ @workbook.worksheets.reject do |s|
50
+ s.is_chartsheet? || s.very_hidden?
51
+ end.count
50
52
  ]
51
53
  )
52
54
  end
@@ -57,14 +59,16 @@ module Writexlsx
57
59
 
58
60
  def add_worksheet_part_names
59
61
  @workbook.worksheets
60
- .reject { |sheet| sheet.is_chartsheet? }
61
- .each { |sheet| add_part_name(sheet.name) }
62
+ .reject { |sheet| sheet.is_chartsheet? || sheet.very_hidden? }
63
+ .each do |sheet|
64
+ add_part_name(sheet.name)
65
+ end
62
66
  end
63
67
 
64
68
  def add_chartsheet_part_names
65
69
  @workbook.worksheets
66
- .select { |sheet| sheet.is_chartsheet? }
67
- .each { |sheet| add_part_name(sheet.name) }
70
+ .select { |sheet| sheet.is_chartsheet? }
71
+ .each { |sheet| add_part_name(sheet.name) }
68
72
  end
69
73
 
70
74
  def add_part_name(part_name)
@@ -411,7 +411,7 @@ module Writexlsx
411
411
  # Check for a cell reference in A1 notation and substitute row and column
412
412
  user_range = if args[0].to_s =~ (/^\D/) && (args[0] =~ /,/)
413
413
  # Check for a user defined multiple range like B3:K6,B8:K11.
414
- args[0].sub(/^=/, '').gsub(/\s*,\s*/, ' ').gsub(/\$/, '')
414
+ args[0].sub(/^=/, '').gsub(/\s*,\s*/, ' ').gsub("$", '')
415
415
  end
416
416
 
417
417
  if (row_col_array = row_col_notation(args.first))
@@ -769,7 +769,7 @@ module Writexlsx
769
769
  attr = super
770
770
  attr << ['percent', 1] if criteria == '%'
771
771
  attr << ['bottom', 1] if direction
772
- attr << ['rank', (value || 10)]
772
+ attr << ['rank', value || 10]
773
773
  attr
774
774
  end
775
775
  end
@@ -199,6 +199,28 @@ module Writexlsx
199
199
  )
200
200
  end
201
201
 
202
+ def add_richvalue
203
+ add_override(
204
+ '/xl/richData/rdRichValueTypes.xml',
205
+ 'application/vnd.ms-excel.rdrichvaluetypes+xml'
206
+ )
207
+
208
+ add_override(
209
+ '/xl/richData/rdrichvalue.xml',
210
+ 'application/vnd.ms-excel.rdrichvalue+xml'
211
+ )
212
+
213
+ add_override(
214
+ '/xl/richData/rdrichvaluestructure.xml',
215
+ 'application/vnd.ms-excel.rdrichvaluestructure+xml'
216
+ )
217
+
218
+ add_override(
219
+ '/xl/richData/richValueRel.xml',
220
+ 'application/vnd.ms-excel.richvaluerel+xml'
221
+ )
222
+ end
223
+
202
224
  private
203
225
 
204
226
  def change_the_workbook_xml_content_type_from_xlsx_to_xlsm