write_xlsx 1.11.1 → 1.11.2

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: 9dae4b31e210af6cea2f7df87d92e29d48e8b36d7408cfc0c542b4aa0fe4ea22
4
+ data.tar.gz: 3c1be9afcd157d1c7a4aabf6f0688b049a80699133e6a3d31d17874bce453b5a
5
5
  SHA512:
6
- metadata.gz: 70a98e3ffc2be69574805f72a63678b2023cc3aef9a1c1a97c5f8a13de80de29c641c573d8c8c9bd7d33da088827a15fb6feb3b4c7591b50ffb26cb321591b49
7
- data.tar.gz: 1d387aa195a7a35f0b664e4ff155ca71de765ad6ae886df66debc9063d1fea8c4c905ab10a7aef735d69813b79faa1d6e7ded96e70ce23a75df56e5f172b1648
6
+ metadata.gz: 7975e43767efe81a4d32819a62ce805d88c51c879e7456f9ea653be43521501bf79612d61ea95ab065efcf5bf7c43fa7f4c2e657a0ab09e211310200237faceb
7
+ data.tar.gz: 03f711b7f01ba88e9e84676ce442737bf8c7cd14db67cd04bdd50aa30eddaad850133d476e11b7a0c0ed25f485740810d6724e0449f7343cc10345bc67ba9c5c
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
 
@@ -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
@@ -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
@@ -142,13 +142,12 @@ module Writexlsx
142
142
  def write_app_file
143
143
  app = Package::App.new(@workbook)
144
144
 
145
+ # Add the Worksheet parts.
146
+ app.add_worksheet_part_names
145
147
  # Add the Worksheet heading pairs.
146
148
  app.add_worksheet_heading_pairs
147
149
  # Add the Chartsheet heading pairs.
148
150
  app.add_chartsheet_heading_pairs
149
-
150
- # Add the Worksheet parts.
151
- app.add_worksheet_part_names
152
151
  # Add the Chartsheet parts.
153
152
  app.add_chartsheet_part_names
154
153
  # Add the Named Range heading pairs.
@@ -12,13 +12,14 @@ module Writexlsx
12
12
  class ColumnData
13
13
  attr_reader :id
14
14
  attr_accessor :name, :format, :formula, :name_format
15
- attr_accessor :total_string, :total_function
15
+ attr_accessor :total_string, :total_function, :custom_total
16
16
 
17
17
  def initialize(id, param = {})
18
18
  @id = id
19
19
  @name = "Column#{id}"
20
20
  @total_string = ''
21
21
  @total_function = ''
22
+ @custom_total = ''
22
23
  @formula = ''
23
24
  @format = nil
24
25
  @name_format = nil
@@ -51,6 +52,7 @@ module Writexlsx
51
52
 
52
53
  add_the_table_columns
53
54
  write_the_cell_data_if_supplied
55
+ write_any_columns_formulas_after_the_user_supplied_table_data
54
56
  store_filter_cell_positions
55
57
  end
56
58
 
@@ -78,7 +80,7 @@ module Writexlsx
78
80
  # Set up the default column data.
79
81
  col_data = Package::Table::ColumnData.new(col_id + 1, @param[:columns])
80
82
 
81
- overrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
83
+ overwrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
82
84
 
83
85
  # Store the column data.
84
86
  @columns << col_data
@@ -89,7 +91,7 @@ module Writexlsx
89
91
  end # Table columns.
90
92
  end
91
93
 
92
- def overrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
94
+ def overwrite_the_defaults_with_any_use_defined_values(col_id, col_data, col_num)
93
95
  # Check if there are user defined values for this column.
94
96
  if @param[:columns] && (user_data = @param[:columns][col_id])
95
97
  # Map user defined values to internal values.
@@ -156,6 +158,23 @@ module Writexlsx
156
158
  end
157
159
  end
158
160
 
161
+ def write_any_columns_formulas_after_the_user_supplied_table_data
162
+ col_id = 0
163
+
164
+ (@col1..@col2).each do |col|
165
+ column_data = @columns[col_id]
166
+ if ptrue?(column_data) && ptrue?(column_data.formula)
167
+ formula_format = @col_formats[col_id]
168
+ formula = column_data.formula
169
+
170
+ (@first_data_row..@last_data_row).each do |row|
171
+ @worksheet.write_formula(row, col, formula, formula_format)
172
+ end
173
+ end
174
+ col_id += 1
175
+ end
176
+ end
177
+
159
178
  def store_filter_cell_positions
160
179
  if ptrue?(@param[:autofilter])
161
180
  (@col1..@col2).each do |col|
@@ -234,25 +253,45 @@ module Writexlsx
234
253
  ]
235
254
  end
236
255
 
237
- def handle_the_column_formula(col_data, col_num, formula, format)
256
+ def handle_the_column_formula(col_data, _col_num, formula, _format)
238
257
  return unless formula
239
258
 
240
- col_data.formula = formula.sub(/^=/, '').gsub(/@/, '[#This Row],')
259
+ formula = formula.sub(/^=/, '').gsub(/@/, '[#This Row],')
241
260
 
242
- (@first_data_row..@last_data_row).each do |row|
243
- @worksheet.write_formula(row, col_num, col_data.formula, format)
244
- end
261
+ # Escape any future functions.
262
+ col_data.formula = @worksheet.prepare_formula(formula, 1)
263
+
264
+ # (@first_data_row..@last_data_row).each do |row|
265
+ # @worksheet.write_formula(row, col_num, col_data.formula, format)
266
+ # end
245
267
  end
246
268
 
247
269
  def handle_the_function_for_the_table_row(row2, col_data, col_num, user_data)
248
- function = user_data[:total_function].downcase.gsub(/[_\s]/, '')
270
+ formula = ''
271
+ function = user_data[:total_function]
272
+ function = 'countNums' if function == 'count_nums'
273
+ function = 'stdDev' if function == 'std_dev'
249
274
 
250
- function = 'countNums' if function == 'countnums'
251
- function = 'stdDev' if function == 'stddev'
275
+ subtotals = {
276
+ average: 101,
277
+ countNums: 102,
278
+ count: 103,
279
+ max: 104,
280
+ min: 105,
281
+ stdDev: 106,
282
+ sum: 109,
283
+ var: 110
284
+ }
252
285
 
253
- col_data.total_function = function
286
+ if subtotals[function.to_sym]
287
+ formula = table_function_to_formula(function, col_data.name)
288
+ else
289
+ formula = @worksheet.prepare_formula(function, 1)
290
+ col_data.custom_total = formula
291
+ function = 'custom'
292
+ end
254
293
 
255
- formula = table_function_to_formula(function, col_data.name)
294
+ col_data.total_function = function
256
295
  @worksheet.write_formula(row2, col_num, formula, user_data[:format], user_data[:total_value])
257
296
  end
258
297
 
@@ -260,10 +299,10 @@ module Writexlsx
260
299
  # Convert a table total function to a worksheet formula.
261
300
  #
262
301
  def table_function_to_formula(function, col_name)
263
- col_name = col_name.gsub(/'/, "''")
264
- .gsub(/#/, "'#")
265
- .gsub(/\[/, "'[")
266
- .gsub(/\]/, "']")
302
+ col_name = col_name.gsub("'", "''")
303
+ .gsub("#", "'#")
304
+ .gsub("[", "'[")
305
+ .gsub("]", "']")
267
306
 
268
307
  subtotals = {
269
308
  average: 101,
@@ -395,10 +434,16 @@ module Writexlsx
395
434
 
396
435
  attributes << [:dataDxfId, col_data.format] if col_data.format
397
436
 
398
- if ptrue?(col_data.formula)
437
+ if ptrue?(col_data.formula) || ptrue?(col_data.custom_total)
399
438
  @writer.tag_elements('tableColumn', attributes) do
400
- # Write the calculatedColumnFormula element.
401
- write_calculated_column_formula(col_data.formula)
439
+ if ptrue?(col_data.formula)
440
+ # Write the calculatedColumnFormula element.
441
+ write_calculated_column_formula(col_data.formula)
442
+ end
443
+ if ptrue?(col_data.custom_total)
444
+ # Write the totalsRowFormula element.
445
+ write_totals_row_formula(col_data.custom_total)
446
+ end
402
447
  end
403
448
  else
404
449
  @writer.empty_tag('tableColumn', attributes)
@@ -425,6 +470,15 @@ module Writexlsx
425
470
  def write_calculated_column_formula(formula)
426
471
  @writer.data_element('calculatedColumnFormula', formula)
427
472
  end
473
+
474
+ #
475
+ # _write_totals_row_formula()
476
+ #
477
+ # Write the <totalsRowFormula> element.
478
+ #
479
+ def write_totals_row_formula(formula)
480
+ @writer.data_element('totalsRowFormula', formula)
481
+ end
428
482
  end
429
483
  end
430
484
  end
@@ -29,33 +29,28 @@ module Writexlsx
29
29
  io_write(str)
30
30
  end
31
31
 
32
- def tag_elements(tag, attributes = [])
32
+ def tag_elements(tag, attributes = nil)
33
33
  start_tag(tag, attributes)
34
34
  yield
35
35
  end_tag(tag)
36
36
  end
37
37
 
38
- def tag_elements_str(tag, attributes = [])
38
+ def tag_elements_str(tag, attributes = nil)
39
39
  start_tag_str(tag, attributes) +
40
40
  yield +
41
41
  end_tag_str(tag)
42
42
  end
43
43
 
44
- def start_tag(tag, attr = [])
44
+ def start_tag(tag, attr = nil)
45
45
  io_write(start_tag_str(tag, attr))
46
46
  end
47
47
 
48
- def start_tag_str(tag, attr = [])
49
- if attr.empty?
50
- result = @tag_start_cache[tag]
51
- unless result
52
- result = "<#{tag}>"
53
- @tag_start_cache[tag] = result
54
- end
48
+ def start_tag_str(tag, attr = nil)
49
+ if attr.nil? || attr.empty?
50
+ @tag_start_cache[tag] ||= "<#{tag}>"
55
51
  else
56
- result = "<#{tag}#{key_vals(attr)}>"
52
+ "<#{tag}#{key_vals(attr)}>"
57
53
  end
58
- result
59
54
  end
60
55
 
61
56
  def end_tag(tag)
@@ -63,28 +58,15 @@ module Writexlsx
63
58
  end
64
59
 
65
60
  def end_tag_str(tag)
66
- result = @tag_end_cache[tag]
67
- unless result
68
- result = "</#{tag}>"
69
- @tag_end_cache[tag] = result
70
- end
71
- result
61
+ @tag_end_cache[tag] ||= "</#{tag}>"
72
62
  end
73
63
 
74
- def empty_tag(tag, attr = [])
64
+ def empty_tag(tag, attr = nil)
75
65
  str = "<#{tag}#{key_vals(attr)}/>"
76
66
  io_write(str)
77
67
  end
78
68
 
79
- def empty_tag_encoded(tag, attr = [])
80
- io_write(empty_tag_encoded_str(tag, attr))
81
- end
82
-
83
- def empty_tag_encoded_str(tag, attr = [])
84
- "<#{tag}#{key_vals(attr)}/>"
85
- end
86
-
87
- def data_element(tag, data, attr = [])
69
+ def data_element(tag, data, attr = nil)
88
70
  tag_elements(tag, attr) { io_write(escape_data(data)) }
89
71
  end
90
72
 
@@ -126,31 +108,37 @@ module Writexlsx
126
108
 
127
109
  private
128
110
 
129
- def key_val(key, val)
130
- %( #{key}="#{val}")
131
- end
132
-
133
111
  def key_vals(attribute)
134
- attribute
135
- .inject('') { |str, attr| str + key_val(attr.first, escape_attributes(attr.last)) }
112
+ if attribute
113
+ result = "".dup
114
+ attribute.each do |attr|
115
+ # Generate and concat %( #{key}="#{val}") values for attribute pair
116
+ result << " "
117
+ result << attr.first.to_s
118
+ result << '="'
119
+ result << escape_attributes(attr.last).to_s
120
+ result << '"'
121
+ end
122
+ result
123
+ end
136
124
  end
137
125
 
138
126
  def escape_attributes(str = '')
139
- return str unless str.to_s =~ /["&<>\n]/
127
+ return str unless str.respond_to?(:match) && str =~ /["&<>\n]/
140
128
 
141
129
  str
142
- .gsub(/&/, "&amp;")
143
- .gsub(/"/, "&quot;")
144
- .gsub(/</, "&lt;")
145
- .gsub(/>/, "&gt;")
146
- .gsub(/\n/, "&#xA;")
130
+ .gsub("&", "&amp;")
131
+ .gsub('"', "&quot;")
132
+ .gsub("<", "&lt;")
133
+ .gsub(">", "&gt;")
134
+ .gsub("\n", "&#xA;")
147
135
  end
148
136
 
149
137
  def escape_data(str = '')
150
- if str.to_s =~ /[&<>]/
151
- str.gsub(/&/, '&amp;')
152
- .gsub(/</, '&lt;')
153
- .gsub(/>/, '&gt;')
138
+ if str.respond_to?(:match) && str =~ /[&<>]/
139
+ str.gsub("&", '&amp;')
140
+ .gsub("<", '&lt;')
141
+ .gsub(">", '&gt;')
154
142
  else
155
143
  str
156
144
  end
@@ -252,9 +252,13 @@ module Writexlsx
252
252
  ['sheetId', sheet_id]
253
253
  ]
254
254
 
255
- attributes << %w[state hidden] if sheet.hidden?
255
+ if sheet.hidden?
256
+ attributes << %w[state hidden]
257
+ elsif sheet.very_hidden?
258
+ attributes << %w[state veryHidden]
259
+ end
256
260
  attributes << r_id_attributes(sheet_id)
257
- writer.empty_tag_encoded('sheet', attributes)
261
+ writer.empty_tag('sheet', attributes)
258
262
  end
259
263
  end
260
264
  end
@@ -43,13 +43,13 @@ module Writexlsx
43
43
  # Cleanup the input ranges.
44
44
  @ranges.collect! do |range|
45
45
  # Remove the absolute reference $ symbols.
46
- range = range.gsub(/\$/, '')
46
+ range = range.gsub("$", '')
47
47
  # Convert a simple range into a full Sheet1!A1:D1 range.
48
48
  range = "#{sheetname}!#{range}" unless range =~ /!/
49
49
  range
50
50
  end
51
51
  # Cleanup the input locations.
52
- @locations.collect! { |location| location.gsub(/\$/, '') }
52
+ @locations.collect! { |location| location.gsub("$", '') }
53
53
 
54
54
  # Map options.
55
55
  @high = param[:high_point]
@@ -31,10 +31,12 @@ module Writexlsx
31
31
  #
32
32
  # xl_rowcol_to_cell($row, col, row_absolute, col_absolute)
33
33
  #
34
- def xl_rowcol_to_cell(row, col, row_absolute = false, col_absolute = false)
35
- row += 1 # Change from 0-indexed to 1 indexed.
34
+ def xl_rowcol_to_cell(row_or_name, col, row_absolute = false, col_absolute = false)
35
+ if row_or_name.is_a?(Integer)
36
+ row_or_name += 1 # Change from 0-indexed to 1 indexed.
37
+ end
36
38
  col_str = xl_col_to_name(col, col_absolute)
37
- "#{col_str}#{absolute_char(row_absolute)}#{row}"
39
+ "#{col_str}#{absolute_char(row_absolute)}#{row_or_name}"
38
40
  end
39
41
 
40
42
  #
@@ -53,7 +55,7 @@ module Writexlsx
53
55
 
54
56
  # Convert base26 column string to number
55
57
  # All your Base are belong to us.
56
- chars = col.split(//)
58
+ chars = col.split("")
57
59
  expn = 0
58
60
  col = 0
59
61
 
@@ -112,7 +114,7 @@ module Writexlsx
112
114
  #
113
115
  def xl_string_pixel_width(string)
114
116
  length = 0
115
- string.to_s.split(//).each { |char| length += CHAR_WIDTHS[char] || 8 }
117
+ string.to_s.split("").each { |char| length += CHAR_WIDTHS[char] || 8 }
116
118
 
117
119
  length
118
120
  end
@@ -128,7 +130,7 @@ module Writexlsx
128
130
  name = sheetname.dup
129
131
  if name =~ /\W/ && !(name =~ /^'/)
130
132
  # Double quote and single quoted strings.
131
- name = name.gsub(/'/, "''")
133
+ name = name.gsub("'", "''")
132
134
  name = "'#{name}'"
133
135
  end
134
136
  name
@@ -159,7 +161,7 @@ module Writexlsx
159
161
  seconds = 0 # Time expressed as fraction of 24h hours in seconds
160
162
 
161
163
  # Split into date and time.
162
- date, time = date_time.split(/T/)
164
+ date, time = date_time.split("T")
163
165
 
164
166
  # We allow the time portion of the input DateTime to be optional.
165
167
  if time
@@ -207,7 +209,7 @@ module Writexlsx
207
209
  # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
208
210
  #
209
211
  epoch = date_1904? ? 1904 : 1900
210
- offset = date_1904? ? 4 : 0
212
+ offset = date_1904? ? 4 : 0
211
213
  norm = 300
212
214
  range = year - epoch
213
215
 
@@ -248,7 +250,7 @@ module Writexlsx
248
250
  def escape_url(url)
249
251
  unless url =~ /%[0-9a-fA-F]{2}/
250
252
  # Escape the URL escape symbol.
251
- url = url.gsub(/%/, "%25")
253
+ url = url.gsub("%", "%25")
252
254
 
253
255
  # Escape whitespae in URL.
254
256
  url = url.gsub(/[\s\x00]/, '%20')
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- WriteXLSX_VERSION = "1.11.1"
3
+ WriteXLSX_VERSION = "1.11.2"
@@ -715,7 +715,7 @@ module Writexlsx
715
715
 
716
716
  # Split the cell range into 2 cells or else use single cell for both.
717
717
  if cells =~ /:/
718
- cell_1, cell_2 = cells.split(/:/)
718
+ cell_1, cell_2 = cells.split(":")
719
719
  else
720
720
  cell_1 = cells
721
721
  cell_2 = cells
@@ -724,7 +724,7 @@ module Writexlsx
724
724
  # Remove leading/trailing apostrophes and convert escaped quotes to single.
725
725
  sheetname.sub!(/^'/, '')
726
726
  sheetname.sub!(/'$/, '')
727
- sheetname.gsub!(/''/, "'")
727
+ sheetname.gsub!("''", "'")
728
728
 
729
729
  row_start, col_start = xl_cell_to_rowcol(cell_1)
730
730
  row_end, col_end = xl_cell_to_rowcol(cell_2)
@@ -12,10 +12,10 @@ module Writexlsx
12
12
  # attributes for the <cell> element. This is the innermost loop so efficiency is
13
13
  # important where possible.
14
14
  #
15
- def cell_attributes(worksheet, row, col) # :nodoc:
15
+ def cell_attributes(worksheet, row, row_name, col) # :nodoc:
16
16
  xf_index = xf ? xf.get_xf_index : 0
17
17
  attributes = [
18
- ['r', xl_rowcol_to_cell(row, col)]
18
+ ['r', xl_rowcol_to_cell(row_name, col)]
19
19
  ]
20
20
 
21
21
  # Add the cell format index.
@@ -48,8 +48,8 @@ module Writexlsx
48
48
  @token
49
49
  end
50
50
 
51
- def write_cell(worksheet, row, col)
52
- worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, col)) do
51
+ def write_cell(worksheet, row, row_name, col)
52
+ worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, row_name, col)) do
53
53
  worksheet.write_cell_value(token)
54
54
  end
55
55
  end
@@ -69,8 +69,8 @@ module Writexlsx
69
69
  end
70
70
 
71
71
  TYPE_STR_ATTRS = %w[t s].freeze
72
- def write_cell(worksheet, row, col)
73
- attributes = cell_attributes(worksheet, row, col)
72
+ def write_cell(worksheet, row, row_name, col)
73
+ attributes = cell_attributes(worksheet, row, row_name, col)
74
74
  attributes << TYPE_STR_ATTRS
75
75
  worksheet.writer.tag_elements('c', attributes) do
76
76
  worksheet.write_cell_value(token)
@@ -101,11 +101,11 @@ module Writexlsx
101
101
  @result || 0
102
102
  end
103
103
 
104
- def write_cell(worksheet, row, col)
104
+ def write_cell(worksheet, row, row_name, col)
105
105
  truefalse = { 'TRUE' => 1, 'FALSE' => 0 }
106
106
  error_code = ['#DIV/0!', '#N/A', '#NAME?', '#NULL!', '#NUM!', '#REF!', '#VALUE!']
107
107
 
108
- attributes = cell_attributes(worksheet, row, col)
108
+ attributes = cell_attributes(worksheet, row, row_name, col)
109
109
  if @result && !(@result.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
110
110
  if truefalse[@result]
111
111
  attributes << %w[t b]
@@ -137,8 +137,8 @@ module Writexlsx
137
137
  @result || 0
138
138
  end
139
139
 
140
- def write_cell(worksheet, row, col)
141
- worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, col)) do
140
+ def write_cell(worksheet, row, row_name, col)
141
+ worksheet.writer.tag_elements('c', cell_attributes(worksheet, row, row_name, col)) do
142
142
  worksheet.write_cell_array_formula(token, range)
143
143
  worksheet.write_cell_value(result)
144
144
  end
@@ -159,9 +159,9 @@ module Writexlsx
159
159
  @result || 0
160
160
  end
161
161
 
162
- def write_cell(worksheet, row, col)
162
+ def write_cell(worksheet, row, row_name, col)
163
163
  # Add metadata linkage for dynamic array formulas.
164
- attributes = cell_attributes(worksheet, row, col)
164
+ attributes = cell_attributes(worksheet, row, row_name, col)
165
165
  attributes << %w[cm 1]
166
166
 
167
167
  worksheet.writer.tag_elements('c', attributes) do
@@ -183,8 +183,8 @@ module Writexlsx
183
183
  @token
184
184
  end
185
185
 
186
- def write_cell(worksheet, row, col)
187
- attributes = cell_attributes(worksheet, row, col)
186
+ def write_cell(worksheet, row, row_name, col)
187
+ attributes = cell_attributes(worksheet, row, row_name, col)
188
188
 
189
189
  attributes << %w[t b]
190
190
  worksheet.writer.tag_elements('c', attributes) do
@@ -202,8 +202,8 @@ module Writexlsx
202
202
  ''
203
203
  end
204
204
 
205
- def write_cell(worksheet, row, col)
206
- worksheet.writer.empty_tag('c', cell_attributes(worksheet, row, col))
205
+ def write_cell(worksheet, row, row_name, col)
206
+ worksheet.writer.empty_tag('c', cell_attributes(worksheet, row, row_name, col))
207
207
  end
208
208
  end
209
209
  end
@@ -28,7 +28,7 @@ module Writexlsx
28
28
  normalized_str = str.sub(/^mailto:/, '')
29
29
 
30
30
  # Split url into the link and optional anchor/location.
31
- url, @url_str = url.split(/#/, 2)
31
+ url, @url_str = url.split("#", 2)
32
32
 
33
33
  # Escape URL unless it looks already escaped.
34
34
  url = escape_url(url)
@@ -106,7 +106,7 @@ module Writexlsx
106
106
  str = str.sub(/^mailto:/, '')
107
107
 
108
108
  # Split url into the link and optional anchor/location.
109
- url, url_str = url.split(/#/, 2)
109
+ url, url_str = url.split("#", 2)
110
110
 
111
111
  # Escape URL unless it looks already escaped.
112
112
  url = escape_url(url)
@@ -228,15 +228,26 @@ module Writexlsx
228
228
  #
229
229
  # Hide this worksheet.
230
230
  #
231
- def hide
232
- @hidden = true
231
+ def hide(hidden = :hidden)
232
+ @hidden = hidden
233
233
  @selected = false
234
234
  @workbook.activesheet = 0 if @workbook.activesheet == @index
235
235
  @workbook.firstsheet = 0 if @workbook.firstsheet == @index
236
236
  end
237
237
 
238
+ #
239
+ # Hide this worksheet. This can only be unhidden from VBA.
240
+ #
241
+ def very_hidden
242
+ hide(:very_hidden)
243
+ end
244
+
238
245
  def hidden? # :nodoc:
239
- @hidden
246
+ @hidden == :hidden
247
+ end
248
+
249
+ def very_hidden? # :nodoc:
250
+ @hidden == :very_hidden
240
251
  end
241
252
 
242
253
  #
@@ -271,7 +282,7 @@ module Writexlsx
271
282
  if range.nil?
272
283
  raise "The range must be defined in unprotect_range())\n"
273
284
  else
274
- range = range.gsub(/\$/, "")
285
+ range = range.gsub("$", "")
275
286
  range = range.sub(/^=/, "")
276
287
  @num_protected_ranges += 1
277
288
  end
@@ -612,8 +623,17 @@ module Writexlsx
612
623
  #
613
624
  # This method is used to display the worksheet in "Page View/Layout" mode.
614
625
  #
615
- def set_page_view(flag = true)
616
- @page_view = !!flag
626
+ def set_page_view(flag = 1)
627
+ @page_view = flag
628
+ end
629
+
630
+ #
631
+ # set_pagebreak_view
632
+ #
633
+ # Set the page view mode.
634
+ #
635
+ def set_pagebreak_view
636
+ @page_view = 2
617
637
  end
618
638
 
619
639
  #
@@ -648,7 +668,7 @@ module Writexlsx
648
668
  raise 'Header string must be less than 255 characters' if string.length > 255
649
669
 
650
670
  # Replace the Excel placeholder &[Picture] with the internal &G.
651
- @page_setup.header = string.gsub(/&\[Picture\]/, '&G')
671
+ @page_setup.header = string.gsub("&[Picture]", '&G')
652
672
 
653
673
  @page_setup.header_footer_aligns = options[:align_with_margins] if options[:align_with_margins]
654
674
 
@@ -664,7 +684,7 @@ module Writexlsx
664
684
  end
665
685
 
666
686
  # placeholeder /&G/ の数
667
- placeholder_count = @page_setup.header.scan(/&G/).count
687
+ placeholder_count = @page_setup.header.scan("&G").count
668
688
 
669
689
  image_count = @header_images.count
670
690
 
@@ -685,7 +705,7 @@ module Writexlsx
685
705
  @page_setup.footer = string.dup
686
706
 
687
707
  # Replace the Excel placeholder &[Picture] with the internal &G.
688
- @page_setup.footer = string.gsub(/&\[Picture\]/, '&G')
708
+ @page_setup.footer = string.gsub("&[Picture]", '&G')
689
709
 
690
710
  @page_setup.header_footer_aligns = options[:align_with_margins] if options[:align_with_margins]
691
711
 
@@ -701,7 +721,7 @@ module Writexlsx
701
721
  end
702
722
 
703
723
  # placeholeder /&G/ の数
704
- placeholder_count = @page_setup.footer.scan(/&G/).count
724
+ placeholder_count = @page_setup.footer.scan("&G").count
705
725
 
706
726
  image_count = @footer_images.count
707
727
 
@@ -1320,7 +1340,7 @@ module Writexlsx
1320
1340
  # Utility method to strip equal sign and array braces from a formula
1321
1341
  # and also expand out future and dynamic array formulas.
1322
1342
  #
1323
- def prepare_formula(given_formula)
1343
+ def prepare_formula(given_formula, expand_future_functions = nil)
1324
1344
  # Ignore empty/null formulas.
1325
1345
  return given_formula unless ptrue?(given_formula)
1326
1346
 
@@ -1331,26 +1351,46 @@ module Writexlsx
1331
1351
  return formula if formula =~ /_xlfn\./
1332
1352
 
1333
1353
  # Expand dynamic array formulas.
1334
- formula = expand_formula(formula, 'LET\(')
1354
+ formula = expand_formula(formula, 'ANCHORARRAY\(')
1355
+ formula = expand_formula(formula, 'BYCOL\(')
1356
+ formula = expand_formula(formula, 'BYROW\(')
1357
+ formula = expand_formula(formula, 'CHOOSECOLS\(')
1358
+ formula = expand_formula(formula, 'CHOOSEROWS\(')
1359
+ formula = expand_formula(formula, 'DROP\(')
1360
+ formula = expand_formula(formula, 'EXPAND\(')
1361
+ formula = expand_formula(formula, 'FILTER\(', '._xlws')
1362
+ formula = expand_formula(formula, 'HSTACK\(')
1335
1363
  formula = expand_formula(formula, 'LAMBDA\(')
1364
+ formula = expand_formula(formula, 'MAKEARRAY\(')
1365
+ formula = expand_formula(formula, 'MAP\(')
1366
+ formula = expand_formula(formula, 'RANDARRAY\(')
1367
+ formula = expand_formula(formula, 'REDUCE\(')
1368
+ formula = expand_formula(formula, 'SCAN\(')
1369
+ formula = expand_formula(formula, 'SEQUENCE\(')
1336
1370
  formula = expand_formula(formula, 'SINGLE\(')
1371
+ formula = expand_formula(formula, 'SORT\(', '._xlws')
1337
1372
  formula = expand_formula(formula, 'SORTBY\(')
1373
+ formula = expand_formula(formula, 'SWITCH\(')
1374
+ formula = expand_formula(formula, 'TAKE\(')
1375
+ formula = expand_formula(formula, 'TEXTSPLIT\(')
1376
+ formula = expand_formula(formula, 'TOCOL\(')
1377
+ formula = expand_formula(formula, 'TOROW\(')
1338
1378
  formula = expand_formula(formula, 'UNIQUE\(')
1339
- formula = expand_formula(formula, 'XMATCH\(')
1379
+ formula = expand_formula(formula, 'VSTACK\(')
1380
+ formula = expand_formula(formula, 'WRAPCOLS\(')
1381
+ formula = expand_formula(formula, 'WRAPROWS\(')
1340
1382
  formula = expand_formula(formula, 'XLOOKUP\(')
1341
- formula = expand_formula(formula, 'SEQUENCE\(')
1342
- formula = expand_formula(formula, 'RANDARRAY\(')
1343
- formula = expand_formula(formula, 'SORT\(', '._xlws')
1344
- formula = expand_formula(formula, 'ANCHORARRAY\(')
1345
- formula = expand_formula(formula, 'FILTER\(', '._xlws')
1346
1383
 
1347
- return formula unless @use_future_functions
1384
+ if !@use_future_functions && !ptrue?(expand_future_functions)
1385
+ return formula
1386
+ end
1348
1387
 
1349
1388
  # Future functions.
1350
1389
  formula = expand_formula(formula, 'ACOTH\(')
1351
1390
  formula = expand_formula(formula, 'ACOT\(')
1352
1391
  formula = expand_formula(formula, 'AGGREGATE\(')
1353
1392
  formula = expand_formula(formula, 'ARABIC\(')
1393
+ formula = expand_formula(formula, 'ARRAYTOTEXT\(')
1354
1394
  formula = expand_formula(formula, 'BASE\(')
1355
1395
  formula = expand_formula(formula, 'BETA.DIST\(')
1356
1396
  formula = expand_formula(formula, 'BETA.INV\(')
@@ -1415,7 +1455,9 @@ module Writexlsx
1415
1455
  formula = expand_formula(formula, 'IMSINH\(')
1416
1456
  formula = expand_formula(formula, 'IMTAN\(')
1417
1457
  formula = expand_formula(formula, 'ISFORMULA\(')
1458
+ formula = expand_formula(formula, 'ISOMITTED\(')
1418
1459
  formula = expand_formula(formula, 'ISOWEEKNUM\(')
1460
+ formula = expand_formula(formula, 'LET\(')
1419
1461
  formula = expand_formula(formula, 'LOGNORM.DIST\(')
1420
1462
  formula = expand_formula(formula, 'LOGNORM.INV\(')
1421
1463
  formula = expand_formula(formula, 'MAXIFS\(')
@@ -1450,24 +1492,26 @@ module Writexlsx
1450
1492
  formula = expand_formula(formula, 'SKEW.P\(')
1451
1493
  formula = expand_formula(formula, 'STDEV.P\(')
1452
1494
  formula = expand_formula(formula, 'STDEV.S\(')
1453
- formula = expand_formula(formula, 'SWITCH\(')
1454
1495
  formula = expand_formula(formula, 'T.DIST.2T\(')
1455
1496
  formula = expand_formula(formula, 'T.DIST.RT\(')
1456
1497
  formula = expand_formula(formula, 'T.DIST\(')
1457
1498
  formula = expand_formula(formula, 'T.INV.2T\(')
1458
1499
  formula = expand_formula(formula, 'T.INV\(')
1459
1500
  formula = expand_formula(formula, 'T.TEST\(')
1501
+ formula = expand_formula(formula, 'TEXTAFTER\(')
1502
+ formula = expand_formula(formula, 'TEXTBEFORE\(')
1460
1503
  formula = expand_formula(formula, 'TEXTJOIN\(')
1461
1504
  formula = expand_formula(formula, 'UNICHAR\(')
1462
1505
  formula = expand_formula(formula, 'UNICODE\(')
1506
+ formula = expand_formula(formula, 'VALUETOTEXT\(')
1463
1507
  formula = expand_formula(formula, 'VAR.P\(')
1464
1508
  formula = expand_formula(formula, 'VAR.S\(')
1465
1509
  formula = expand_formula(formula, 'WEBSERVICE\(')
1466
1510
  formula = expand_formula(formula, 'WEIBULL.DIST\(')
1511
+ formula = expand_formula(formula, 'XMATCH\(')
1467
1512
  formula = expand_formula(formula, 'XOR\(')
1468
1513
  expand_formula(formula, 'Z.TEST\(')
1469
1514
  end
1470
- private :prepare_formula
1471
1515
 
1472
1516
  #
1473
1517
  # :call-seq:
@@ -1551,7 +1595,7 @@ module Writexlsx
1551
1595
  end
1552
1596
 
1553
1597
  # Modify the formula string, as needed.
1554
- formula = prepare_formula(formula)
1598
+ formula = prepare_formula(formula, 1)
1555
1599
 
1556
1600
  store_data_to_table(
1557
1601
  if type == 'a'
@@ -2926,7 +2970,7 @@ module Writexlsx
2926
2970
  tokens.map! do |token|
2927
2971
  token.sub!(/^"/, '')
2928
2972
  token.sub!(/"$/, '')
2929
- token.gsub!(/""/, '"')
2973
+ token.gsub!('""', '"')
2930
2974
 
2931
2975
  # if token is number, convert to numeric.
2932
2976
  if token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
@@ -3208,13 +3252,13 @@ module Writexlsx
3208
3252
  target = escape_url(url.sub(/^external:/, ''))
3209
3253
 
3210
3254
  # Additional escape not required in worksheet hyperlinks
3211
- target = target.gsub(/#/, '%23')
3255
+ target = target.gsub("#", '%23')
3212
3256
 
3213
3257
  # Prefix absolute paths (not relative) with file:///
3214
3258
  target = if target =~ /^\w:/ || target =~ /^\\\\/
3215
3259
  "file:///#{target}"
3216
3260
  else
3217
- target.gsub(/\\/, '/')
3261
+ target.gsub("\\", '/')
3218
3262
  end
3219
3263
  end
3220
3264
 
@@ -3437,7 +3481,7 @@ EOS
3437
3481
  def encode_password(password) # :nodoc:
3438
3482
  hash = 0
3439
3483
 
3440
- password.reverse.split(//).each do |char|
3484
+ password.reverse.split("").each do |char|
3441
3485
  hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff)
3442
3486
  hash ^= char.ord
3443
3487
  end
@@ -3559,16 +3603,27 @@ EOS
3559
3603
  attributes << ["showOutlineSymbols", 0] if @outline_on
3560
3604
 
3561
3605
  # Set the page view/layout mode if required.
3562
- # TODO. Add pageBreakPreview mode when requested.
3563
- attributes << %w[view pageLayout] if page_view?
3606
+ case @page_view
3607
+ when 1
3608
+ attributes << %w[view pageLayout]
3609
+ when 2
3610
+ attributes << %w[view pageBreakPreview]
3611
+ end
3564
3612
 
3565
3613
  # Set the first visible cell.
3566
3614
  attributes << ['topLeftCell', @top_left_cell] if ptrue?(@top_left_cell)
3567
3615
 
3568
3616
  # Set the zoom level.
3569
3617
  if @zoom != 100
3570
- attributes << ['zoomScale', @zoom] unless page_view?
3571
- attributes << ['zoomScaleNormal', @zoom] if zoom_scale_normal?
3618
+ attributes << ['zoomScale', @zoom]
3619
+
3620
+ if @page_view == 1
3621
+ attributes << ['zoomScalePageLayoutView', @zoom]
3622
+ elsif @page_view == 2
3623
+ attributes << ['zoomScaleSheetLayoutView', @zoom]
3624
+ elsif ptrue?(@zoom_scale_normal)
3625
+ attributes << ['zoomScaleNormal', @zoom]
3626
+ end
3572
3627
  end
3573
3628
 
3574
3629
  attributes << ['workbookViewId', 0]
@@ -3754,9 +3809,10 @@ EOS
3754
3809
 
3755
3810
  def write_cell_column_dimension(row_num) # :nodoc:
3756
3811
  row = @cell_data_table[row_num]
3812
+ row_name = (row_num + 1).to_s
3757
3813
  (@dim_colmin..@dim_colmax).each do |col_num|
3758
3814
  if (cell = row[col_num])
3759
- cell.write_cell(self, row_num, col_num)
3815
+ cell.write_cell(self, row_num, row_name, col_num)
3760
3816
  end
3761
3817
  end
3762
3818
  end
@@ -4755,10 +4811,6 @@ EOS
4755
4811
  ptrue?(@zoom_scale_normal)
4756
4812
  end
4757
4813
 
4758
- def page_view? # :nodoc:
4759
- !!@page_view
4760
- end
4761
-
4762
4814
  def right_to_left? # :nodoc:
4763
4815
  !!@right_to_left
4764
4816
  end
data/write_xlsx.gemspec CHANGED
@@ -20,9 +20,11 @@ Gem::Specification.new do |gem|
20
20
  end
21
21
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
22
22
  gem.require_paths = ['lib']
23
+ gem.add_runtime_dependency 'nkf'
23
24
  gem.add_runtime_dependency 'rubyzip', '>= 1.0.0'
24
25
  gem.add_development_dependency 'byebug'
25
26
  gem.add_development_dependency 'minitest'
27
+ gem.add_development_dependency 'mutex_m'
26
28
  gem.add_development_dependency 'rake'
27
29
  gem.add_development_dependency 'rubocop'
28
30
  gem.add_development_dependency 'rubocop-minitest'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: write_xlsx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 1.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hideo NAKAMURA
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-08 00:00:00.000000000 Z
11
+ date: 2023-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nkf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rubyzip
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,20 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mutex_m
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rake
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -276,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
304
  - !ruby/object:Gem::Version
277
305
  version: '0'
278
306
  requirements: []
279
- rubygems_version: 3.4.10
307
+ rubygems_version: 3.5.3
280
308
  signing_key:
281
309
  specification_version: 4
282
310
  summary: write_xlsx is a gem to create a new file in the Excel 2007+ XLSX format.