write_xlsx 1.12.3 → 1.15.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -0
  3. data/Changes +23 -0
  4. data/README.md +1 -1
  5. data/lib/write_xlsx/chart/area.rb +2 -2
  6. data/lib/write_xlsx/chart/axis.rb +55 -32
  7. data/lib/write_xlsx/chart/axis_writer.rb +528 -0
  8. data/lib/write_xlsx/chart/bar.rb +2 -2
  9. data/lib/write_xlsx/chart/caption.rb +16 -9
  10. data/lib/write_xlsx/chart/chart_area.rb +121 -0
  11. data/lib/write_xlsx/chart/column.rb +2 -2
  12. data/lib/write_xlsx/chart/d_pt_point_writer.rb +14 -0
  13. data/lib/write_xlsx/chart/doughnut.rb +0 -3
  14. data/lib/write_xlsx/chart/formatting_writer.rb +652 -0
  15. data/lib/write_xlsx/chart/initialization.rb +100 -0
  16. data/lib/write_xlsx/chart/line.rb +4 -3
  17. data/lib/write_xlsx/chart/pie.rb +6 -2
  18. data/lib/write_xlsx/chart/radar.rb +2 -2
  19. data/lib/write_xlsx/chart/scatter.rb +4 -3
  20. data/lib/write_xlsx/chart/series.rb +35 -15
  21. data/lib/write_xlsx/chart/series_data.rb +132 -0
  22. data/lib/write_xlsx/chart/series_writer.rb +318 -0
  23. data/lib/write_xlsx/chart/settings.rb +226 -0
  24. data/lib/write_xlsx/chart/stock.rb +2 -2
  25. data/lib/write_xlsx/chart/table.rb +50 -0
  26. data/lib/write_xlsx/chart/xml_writer.rb +305 -0
  27. data/lib/write_xlsx/chart.rb +286 -2477
  28. data/lib/write_xlsx/chartsheet.rb +35 -83
  29. data/lib/write_xlsx/constants.rb +11 -0
  30. data/lib/write_xlsx/drawing.rb +5 -3
  31. data/lib/write_xlsx/format/alignment_state.rb +39 -0
  32. data/lib/write_xlsx/format/alignment_style.rb +92 -0
  33. data/lib/write_xlsx/format/border_state.rb +47 -0
  34. data/lib/write_xlsx/format/border_style.rb +116 -0
  35. data/lib/write_xlsx/format/fill_state.rb +26 -0
  36. data/lib/write_xlsx/format/fill_style.rb +52 -0
  37. data/lib/write_xlsx/format/font_state.rb +74 -0
  38. data/lib/write_xlsx/format/font_style.rb +172 -0
  39. data/lib/write_xlsx/format/format_state.rb +65 -0
  40. data/lib/write_xlsx/format/number_format_state.rb +20 -0
  41. data/lib/write_xlsx/format/number_format_style.rb +28 -0
  42. data/lib/write_xlsx/format/protection_state.rb +20 -0
  43. data/lib/write_xlsx/format/protection_style.rb +28 -0
  44. data/lib/write_xlsx/format.rb +1093 -426
  45. data/lib/write_xlsx/formats.rb +0 -2
  46. data/lib/write_xlsx/image_property.rb +4 -1
  47. data/lib/write_xlsx/inserted_chart.rb +1 -1
  48. data/lib/write_xlsx/object_positioning.rb +203 -0
  49. data/lib/write_xlsx/package/app.rb +3 -3
  50. data/lib/write_xlsx/package/button.rb +6 -2
  51. data/lib/write_xlsx/package/comments.rb +11 -3
  52. data/lib/write_xlsx/package/conditional_format.rb +7 -3
  53. data/lib/write_xlsx/package/content_types.rb +2 -2
  54. data/lib/write_xlsx/package/core.rb +2 -2
  55. data/lib/write_xlsx/package/custom.rb +3 -2
  56. data/lib/write_xlsx/package/metadata.rb +2 -2
  57. data/lib/write_xlsx/package/packager.rb +0 -3
  58. data/lib/write_xlsx/package/relationships.rb +2 -2
  59. data/lib/write_xlsx/package/rich_value.rb +4 -2
  60. data/lib/write_xlsx/package/rich_value_rel.rb +2 -2
  61. data/lib/write_xlsx/package/rich_value_structure.rb +2 -2
  62. data/lib/write_xlsx/package/rich_value_types.rb +3 -3
  63. data/lib/write_xlsx/package/shared_strings.rb +2 -2
  64. data/lib/write_xlsx/package/styles.rb +13 -9
  65. data/lib/write_xlsx/package/table.rb +8 -2
  66. data/lib/write_xlsx/package/theme.rb +0 -3
  67. data/lib/write_xlsx/package/vml.rb +2 -2
  68. data/lib/write_xlsx/page_setup.rb +192 -0
  69. data/lib/write_xlsx/shape.rb +97 -100
  70. data/lib/write_xlsx/sheets.rb +9 -4
  71. data/lib/write_xlsx/sparkline.rb +2 -2
  72. data/lib/write_xlsx/utility/cell_reference.rb +124 -0
  73. data/lib/write_xlsx/utility/chart_formatting.rb +262 -0
  74. data/lib/write_xlsx/utility/common.rb +44 -0
  75. data/lib/write_xlsx/utility/date_time.rb +113 -0
  76. data/lib/write_xlsx/utility/dimensions.rb +40 -0
  77. data/lib/write_xlsx/utility/drawing.rb +136 -0
  78. data/lib/write_xlsx/utility/rich_text.rb +184 -0
  79. data/lib/write_xlsx/utility/sheetname_quoting.rb +73 -0
  80. data/lib/write_xlsx/utility/string_width.rb +45 -0
  81. data/lib/write_xlsx/utility/url.rb +27 -0
  82. data/lib/write_xlsx/utility/xml_primitives.rb +32 -0
  83. data/lib/write_xlsx/version.rb +1 -1
  84. data/lib/write_xlsx/workbook/chart_data.rb +188 -0
  85. data/lib/write_xlsx/workbook/format_preparation.rb +199 -0
  86. data/lib/write_xlsx/workbook/initialization.rb +223 -0
  87. data/lib/write_xlsx/workbook/package_preparation.rb +231 -0
  88. data/lib/write_xlsx/workbook/workbook_writer.rb +164 -0
  89. data/lib/write_xlsx/workbook.rb +143 -981
  90. data/lib/write_xlsx/worksheet/asset_manager.rb +60 -0
  91. data/lib/write_xlsx/worksheet/autofilter.rb +390 -0
  92. data/lib/write_xlsx/worksheet/cell_data.rb +13 -6
  93. data/lib/write_xlsx/worksheet/cell_data_manager.rb +47 -0
  94. data/lib/write_xlsx/worksheet/cell_data_store.rb +61 -0
  95. data/lib/write_xlsx/worksheet/columns.rb +204 -0
  96. data/lib/write_xlsx/worksheet/comments_support.rb +61 -0
  97. data/lib/write_xlsx/worksheet/conditional_formats.rb +30 -0
  98. data/lib/write_xlsx/worksheet/data_validation.rb +9 -1
  99. data/lib/write_xlsx/worksheet/data_writing.rb +1017 -0
  100. data/lib/write_xlsx/worksheet/drawing_methods.rb +308 -0
  101. data/lib/write_xlsx/worksheet/drawing_preparation.rb +290 -0
  102. data/lib/write_xlsx/worksheet/drawing_relations.rb +76 -0
  103. data/lib/write_xlsx/worksheet/drawing_xml_writer.rb +50 -0
  104. data/lib/write_xlsx/worksheet/formatting.rb +418 -0
  105. data/lib/write_xlsx/worksheet/hyperlink.rb +9 -1
  106. data/lib/write_xlsx/worksheet/initialization.rb +146 -0
  107. data/lib/write_xlsx/worksheet/panes.rb +64 -0
  108. data/lib/write_xlsx/worksheet/print_options.rb +72 -0
  109. data/lib/write_xlsx/worksheet/protection.rb +65 -0
  110. data/lib/write_xlsx/worksheet/rich_text_helpers.rb +78 -0
  111. data/lib/write_xlsx/worksheet/row_col_sizing.rb +69 -0
  112. data/lib/write_xlsx/worksheet/rows.rb +84 -0
  113. data/lib/write_xlsx/worksheet/selection.rb +41 -0
  114. data/lib/write_xlsx/worksheet/xml_writer.rb +1246 -0
  115. data/lib/write_xlsx/worksheet.rb +376 -4530
  116. metadata +66 -4
  117. data/lib/write_xlsx/utility.rb +0 -986
  118. data/lib/write_xlsx/worksheet/page_setup.rb +0 -192
@@ -0,0 +1,1017 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ ###############################################################################
5
+ #
6
+ # DataWriting - A module for writing data to worksheet cells.
7
+ #
8
+ # Used in conjunction with WriteXLSX
9
+ #
10
+ # Copyright 2000-2011, John McNamara, jmcnamara@cpan.org
11
+ # Convert to ruby by Hideo NAKAMURA, nakamura.hideo@gmail.com
12
+ #
13
+ require 'write_xlsx/constants'
14
+ require 'write_xlsx/utility/common'
15
+
16
+ module Writexlsx
17
+ class Worksheet
18
+ module DataWriting
19
+ include Constants
20
+ include Writexlsx::Utility::Common
21
+
22
+ #
23
+ # :call-seq:
24
+ # write(row, column [ , token [ , format ] ])
25
+ #
26
+ # Excel makes a distinction between data types such as strings, numbers,
27
+ # blanks, formulas and hyperlinks. To simplify the process of writing
28
+ # data the {#write()}[#method-i-write] method acts as a general alias for several more
29
+ # specific methods:
30
+ #
31
+ def write(row, col, token = nil, format = nil, value1 = nil, value2 = nil)
32
+ # Check for a cell reference in A1 notation and substitute row and column
33
+ if (row_col_array = row_col_notation(row))
34
+ _row, _col = row_col_array
35
+ _token = col
36
+ _format = token
37
+ _value1 = format
38
+ _value2 = value1
39
+ else
40
+ _row = row
41
+ _col = col
42
+ _token = token
43
+ _format = format
44
+ _value1 = value1
45
+ _value2 = value2
46
+ end
47
+ _token ||= ''
48
+ _token = _token.to_s if token.instance_of?(Time) || token.instance_of?(Date)
49
+
50
+ if _format.respond_to?(:force_text_format?) && _format.force_text_format?
51
+ write_string(_row, _col, _token, _format) # Force text format
52
+ # Match an array ref.
53
+ elsif _token.respond_to?(:to_ary)
54
+ write_row(_row, _col, _token, _format, _value1, _value2)
55
+ elsif _token.respond_to?(:coerce) # Numeric
56
+ write_number(_row, _col, _token, _format)
57
+ elsif _token.respond_to?(:=~) # String
58
+ # Match integer with leading zero(s)
59
+ if @leading_zeros && _token =~ /^0\d*$/
60
+ write_string(_row, _col, _token, _format)
61
+ elsif _token =~ /\A([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?\Z/
62
+ write_number(_row, _col, _token, _format)
63
+ # Match formula
64
+ elsif _token =~ /^=/
65
+ write_formula(_row, _col, _token, _format, _value1)
66
+ # Match array formula
67
+ elsif _token =~ /^\{=.*\}$/
68
+ write_formula(_row, _col, _token, _format, _value1)
69
+ # Match blank
70
+ elsif _token == ''
71
+ # row_col_args.delete_at(2) # remove the empty string from the parameter list
72
+ write_blank(_row, _col, _format)
73
+ elsif @workbook.strings_to_urls
74
+ # https://, http://, ftp://, mailto:, internal:, external:
75
+ url_token_re = %r{\A(?:(?:https?|ftp)://|mailto:|(?:in|ex)ternal:)}
76
+
77
+ if _token =~ url_token_re
78
+ write_url(_row, _col, _token, _format, _value1, _value2)
79
+ else
80
+ write_string(_row, _col, _token, _format)
81
+ end
82
+ else
83
+ write_string(_row, _col, _token, _format)
84
+ end
85
+ else
86
+ write_string(_row, _col, _token, _format)
87
+ end
88
+ end
89
+
90
+ #
91
+ # :call-seq:
92
+ # write_row(row, col, array [ , format ])
93
+ #
94
+ # Write a row of data starting from (row, col). Call write_col() if any of
95
+ # the elements of the array are in turn array. This allows the writing
96
+ # of 1D or 2D arrays of data in one go.
97
+ #
98
+ def write_row(row, col, tokens = nil, *options)
99
+ # Check for a cell reference in A1 notation and substitute row and column
100
+ if (row_col_array = row_col_notation(row))
101
+ _row, _col = row_col_array
102
+ _tokens = col
103
+ _options = [tokens] + options
104
+ else
105
+ _row = row
106
+ _col = col
107
+ _tokens = tokens
108
+ _options = options
109
+ end
110
+ raise "Not an array ref in call to write_row()$!" unless _tokens.respond_to?(:to_ary)
111
+
112
+ _tokens.each do |_token|
113
+ # Check for nested arrays
114
+ if _token.respond_to?(:to_ary)
115
+ write_col(_row, _col, _token, *_options)
116
+ else
117
+ write(_row, _col, _token, *_options)
118
+ end
119
+ _col += 1
120
+ end
121
+ end
122
+
123
+ #
124
+ # :call-seq:
125
+ # write_col(row, col, array [ , format ])
126
+ #
127
+ # Write a column of data starting from (row, col). Call write_row() if any of
128
+ # the elements of the array are in turn array. This allows the writing
129
+ # of 1D or 2D arrays of data in one go.
130
+ #
131
+ def write_col(row, col, tokens = nil, *options)
132
+ if (row_col_array = row_col_notation(row))
133
+ _row, _col = row_col_array
134
+ _tokens = col
135
+ _options = [tokens] + options if options
136
+ else
137
+ _row = row
138
+ _col = col
139
+ _tokens = tokens
140
+ _options = options
141
+ end
142
+
143
+ _tokens.each do |_token|
144
+ # write() will deal with any nested arrays
145
+ write(_row, _col, _token, *_options)
146
+ _row += 1
147
+ end
148
+ end
149
+
150
+ #
151
+ # :call-seq:
152
+ # write_comment(row, column, string, options = {})
153
+ #
154
+ # Write a comment to the specified row and column (zero indexed).
155
+ #
156
+ def write_comment(row, col, string = nil, options = nil)
157
+ # Check for a cell reference in A1 notation and substitute row and column
158
+ if (row_col_array = row_col_notation(row))
159
+ _row, _col = row_col_array
160
+ _string = col
161
+ _options = string
162
+ else
163
+ _row = row
164
+ _col = col
165
+ _string = string
166
+ _options = options
167
+ end
168
+ raise WriteXLSXInsufficientArgumentError if [_row, _col, _string].include?(nil)
169
+
170
+ # Check that row and col are valid and store max and min values
171
+ check_dimensions(_row, _col)
172
+ store_row_col_max_min_values(_row, _col)
173
+
174
+ @has_vml = true
175
+
176
+ # Process the properties of the cell comment.
177
+ @comments.add(@workbook, self, _row, _col, _string, _options)
178
+ end
179
+
180
+ #
181
+ # :call-seq:
182
+ # write_number(row, column, number [ , format ])
183
+ #
184
+ # Write an integer or a float to the cell specified by row and column:
185
+ #
186
+ def write_number(row, col, number, format = nil)
187
+ # Check for a cell reference in A1 notation and substitute row and column
188
+ if (row_col_array = row_col_notation(row))
189
+ _row, _col = row_col_array
190
+ _number = col
191
+ _format = number
192
+ else
193
+ _row = row
194
+ _col = col
195
+ _number = number
196
+ _format = format
197
+ end
198
+ raise WriteXLSXInsufficientArgumentError if _row.nil? || _col.nil? || _number.nil?
199
+
200
+ # Check that row and col are valid and store max and min values
201
+ check_dimensions(_row, _col)
202
+ store_row_col_max_min_values(_row, _col)
203
+
204
+ store_data_to_table(NumberCellData.new(_number, _format), _row, _col)
205
+ end
206
+
207
+ #
208
+ # :call-seq:
209
+ # write_string(row, column, string [, format ])
210
+ #
211
+ # Write a string to the specified row and column (zero indexed).
212
+ # +format+ is optional.
213
+ #
214
+ def write_string(row, col, string = nil, format = nil)
215
+ # Check for a cell reference in A1 notation and substitute row and column
216
+ if (row_col_array = row_col_notation(row))
217
+ _row, _col = row_col_array
218
+ _string = col
219
+ _format = string
220
+ else
221
+ _row = row
222
+ _col = col
223
+ _string = string
224
+ _format = format
225
+ end
226
+ _string &&= _string.to_s
227
+ raise WriteXLSXInsufficientArgumentError if _row.nil? || _col.nil? || _string.nil?
228
+
229
+ # Check that row and col are valid and store max and min values
230
+ check_dimensions(_row, _col)
231
+ store_row_col_max_min_values(_row, _col)
232
+
233
+ index = shared_string_index(_string.length > STR_MAX ? _string[0, STR_MAX] : _string)
234
+
235
+ store_data_to_table(StringCellData.new(index, _format, _string), _row, _col)
236
+ end
237
+
238
+ #
239
+ # :call-seq:
240
+ # write_rich_string(row, column, (string | format, string)+, [,cell_format])
241
+ #
242
+ # The write_rich_string() method is used to write strings with multiple formats.
243
+ # The method receives string fragments prefixed by format objects. The final
244
+ # format object is used as the cell format.
245
+ #
246
+ def write_rich_string(row, col, *rich_strings)
247
+ # Check for a cell reference in A1 notation and substitute row and column
248
+ if (row_col_array = row_col_notation(row))
249
+ _row, _col = row_col_array
250
+ _rich_strings = [col] + rich_strings
251
+ else
252
+ _row = row
253
+ _col = col
254
+ _rich_strings = rich_strings
255
+ end
256
+ raise WriteXLSXInsufficientArgumentError if [_row, _col, _rich_strings[0]].include?(nil)
257
+
258
+ _xf = cell_format_of_rich_string(_rich_strings)
259
+
260
+ # Check that row and col are valid and store max and min values
261
+ check_dimensions(_row, _col)
262
+ store_row_col_max_min_values(_row, _col)
263
+
264
+ _fragments, _raw_string = rich_strings_fragments(_rich_strings)
265
+ # can't allow 2 formats in a row
266
+ return -4 unless _fragments
267
+
268
+ # Check that the string si < 32767 chars.
269
+ return 3 if _raw_string.size > @xls_strmax
270
+
271
+ index = shared_string_index(xml_str_of_rich_string(_fragments))
272
+
273
+ store_data_to_table(RichStringCellData.new(index, _xf, _raw_string), _row, _col)
274
+ end
275
+
276
+ #
277
+ # :call-seq:
278
+ # write_blank(row, col, format)
279
+ #
280
+ # Write a blank cell to the specified row and column (zero indexed).
281
+ # A blank cell is used to specify formatting without adding a string
282
+ # or a number.
283
+ #
284
+ def write_blank(row, col, format = nil)
285
+ # Check for a cell reference in A1 notation and substitute row and column
286
+ if (row_col_array = row_col_notation(row))
287
+ _row, _col = row_col_array
288
+ _format = col
289
+ else
290
+ _row = row
291
+ _col = col
292
+ _format = format
293
+ end
294
+ raise WriteXLSXInsufficientArgumentError if [_row, _col].include?(nil)
295
+
296
+ # Don't write a blank cell unless it has a format
297
+ return unless _format
298
+
299
+ # Check that row and col are valid and store max and min values
300
+ check_dimensions(_row, _col)
301
+ store_row_col_max_min_values(_row, _col)
302
+
303
+ store_data_to_table(BlankCellData.new(_format), _row, _col)
304
+ end
305
+
306
+ #
307
+ # Utility method to strip equal sign and array braces from a formula
308
+ # and also expand out future and dynamic array formulas.
309
+ #
310
+ def prepare_formula(given_formula, expand_future_functions = nil)
311
+ # Ignore empty/null formulas.
312
+ return given_formula unless ptrue?(given_formula)
313
+
314
+ # Remove array formula braces and the leading =.
315
+ formula = given_formula.sub(/^\{(.*)\}$/, '\1').sub(/^=/, '')
316
+
317
+ # # Don't expand formulas that the user has already expanded.
318
+ return formula if formula =~ /_xlfn\./
319
+
320
+ # Expand dynamic array formulas.
321
+ formula = expand_formula(formula, 'ANCHORARRAY\(')
322
+ formula = expand_formula(formula, 'BYCOL\(')
323
+ formula = expand_formula(formula, 'BYROW\(')
324
+ formula = expand_formula(formula, 'CHOOSECOLS\(')
325
+ formula = expand_formula(formula, 'CHOOSEROWS\(')
326
+ formula = expand_formula(formula, 'DROP\(')
327
+ formula = expand_formula(formula, 'EXPAND\(')
328
+ formula = expand_formula(formula, 'FILTER\(', '._xlws')
329
+ formula = expand_formula(formula, 'HSTACK\(')
330
+ formula = expand_formula(formula, 'LAMBDA\(')
331
+ formula = expand_formula(formula, 'MAKEARRAY\(')
332
+ formula = expand_formula(formula, 'MAP\(')
333
+ formula = expand_formula(formula, 'RANDARRAY\(')
334
+ formula = expand_formula(formula, 'REDUCE\(')
335
+ formula = expand_formula(formula, 'SCAN\(')
336
+ formula = expand_formula(formula, 'SEQUENCE\(')
337
+ formula = expand_formula(formula, 'SINGLE\(')
338
+ formula = expand_formula(formula, 'SORT\(', '._xlws')
339
+ formula = expand_formula(formula, 'SORTBY\(')
340
+ formula = expand_formula(formula, 'SWITCH\(')
341
+ formula = expand_formula(formula, 'TAKE\(')
342
+ formula = expand_formula(formula, 'TEXTSPLIT\(')
343
+ formula = expand_formula(formula, 'TOCOL\(')
344
+ formula = expand_formula(formula, 'TOROW\(')
345
+ formula = expand_formula(formula, 'UNIQUE\(')
346
+ formula = expand_formula(formula, 'VSTACK\(')
347
+ formula = expand_formula(formula, 'WRAPCOLS\(')
348
+ formula = expand_formula(formula, 'WRAPROWS\(')
349
+ formula = expand_formula(formula, 'XLOOKUP\(')
350
+
351
+ if !@use_future_functions && !ptrue?(expand_future_functions)
352
+ return formula
353
+ end
354
+
355
+ # Future functions.
356
+ formula = expand_formula(formula, 'ACOTH\(')
357
+ formula = expand_formula(formula, 'ACOT\(')
358
+ formula = expand_formula(formula, 'AGGREGATE\(')
359
+ formula = expand_formula(formula, 'ARABIC\(')
360
+ formula = expand_formula(formula, 'ARRAYTOTEXT\(')
361
+ formula = expand_formula(formula, 'BASE\(')
362
+ formula = expand_formula(formula, 'BETA.DIST\(')
363
+ formula = expand_formula(formula, 'BETA.INV\(')
364
+ formula = expand_formula(formula, 'BINOM.DIST.RANGE\(')
365
+ formula = expand_formula(formula, 'BINOM.DIST\(')
366
+ formula = expand_formula(formula, 'BINOM.INV\(')
367
+ formula = expand_formula(formula, 'BITAND\(')
368
+ formula = expand_formula(formula, 'BITLSHIFT\(')
369
+ formula = expand_formula(formula, 'BITOR\(')
370
+ formula = expand_formula(formula, 'BITRSHIFT\(')
371
+ formula = expand_formula(formula, 'BITXOR\(')
372
+ formula = expand_formula(formula, 'CEILING.MATH\(')
373
+ formula = expand_formula(formula, 'CEILING.PRECISE\(')
374
+ formula = expand_formula(formula, 'CHISQ.DIST.RT\(')
375
+ formula = expand_formula(formula, 'CHISQ.DIST\(')
376
+ formula = expand_formula(formula, 'CHISQ.INV.RT\(')
377
+ formula = expand_formula(formula, 'CHISQ.INV\(')
378
+ formula = expand_formula(formula, 'CHISQ.TEST\(')
379
+ formula = expand_formula(formula, 'COMBINA\(')
380
+ formula = expand_formula(formula, 'CONCAT\(')
381
+ formula = expand_formula(formula, 'CONFIDENCE.NORM\(')
382
+ formula = expand_formula(formula, 'CONFIDENCE.T\(')
383
+ formula = expand_formula(formula, 'COTH\(')
384
+ formula = expand_formula(formula, 'COT\(')
385
+ formula = expand_formula(formula, 'COVARIANCE.P\(')
386
+ formula = expand_formula(formula, 'COVARIANCE.S\(')
387
+ formula = expand_formula(formula, 'CSCH\(')
388
+ formula = expand_formula(formula, 'CSC\(')
389
+ formula = expand_formula(formula, 'DAYS\(')
390
+ formula = expand_formula(formula, 'DECIMAL\(')
391
+ formula = expand_formula(formula, 'ERF.PRECISE\(')
392
+ formula = expand_formula(formula, 'ERFC.PRECISE\(')
393
+ formula = expand_formula(formula, 'EXPON.DIST\(')
394
+ formula = expand_formula(formula, 'F.DIST.RT\(')
395
+ formula = expand_formula(formula, 'F.DIST\(')
396
+ formula = expand_formula(formula, 'F.INV.RT\(')
397
+ formula = expand_formula(formula, 'F.INV\(')
398
+ formula = expand_formula(formula, 'F.TEST\(')
399
+ formula = expand_formula(formula, 'FILTERXML\(')
400
+ formula = expand_formula(formula, 'FLOOR.MATH\(')
401
+ formula = expand_formula(formula, 'FLOOR.PRECISE\(')
402
+ formula = expand_formula(formula, 'FORECAST.ETS.CONFINT\(')
403
+ formula = expand_formula(formula, 'FORECAST.ETS.SEASONALITY\(')
404
+ formula = expand_formula(formula, 'FORECAST.ETS.STAT\(')
405
+ formula = expand_formula(formula, 'FORECAST.ETS\(')
406
+ formula = expand_formula(formula, 'FORECAST.LINEAR\(')
407
+ formula = expand_formula(formula, 'FORMULATEXT\(')
408
+ formula = expand_formula(formula, 'GAMMA.DIST\(')
409
+ formula = expand_formula(formula, 'GAMMA.INV\(')
410
+ formula = expand_formula(formula, 'GAMMALN.PRECISE\(')
411
+ formula = expand_formula(formula, 'GAMMA\(')
412
+ formula = expand_formula(formula, 'GAUSS\(')
413
+ formula = expand_formula(formula, 'HYPGEOM.DIST\(')
414
+ formula = expand_formula(formula, 'IFNA\(')
415
+ formula = expand_formula(formula, 'IFS\(')
416
+ formula = expand_formula(formula, 'IMAGE\(')
417
+ formula = expand_formula(formula, 'IMCOSH\(')
418
+ formula = expand_formula(formula, 'IMCOT\(')
419
+ formula = expand_formula(formula, 'IMCSCH\(')
420
+ formula = expand_formula(formula, 'IMCSC\(')
421
+ formula = expand_formula(formula, 'IMSECH\(')
422
+ formula = expand_formula(formula, 'IMSEC\(')
423
+ formula = expand_formula(formula, 'IMSINH\(')
424
+ formula = expand_formula(formula, 'IMTAN\(')
425
+ formula = expand_formula(formula, 'ISFORMULA\(')
426
+ formula = expand_formula(formula, 'ISOMITTED\(')
427
+ formula = expand_formula(formula, 'ISOWEEKNUM\(')
428
+ formula = expand_formula(formula, 'LET\(')
429
+ formula = expand_formula(formula, 'LOGNORM.DIST\(')
430
+ formula = expand_formula(formula, 'LOGNORM.INV\(')
431
+ formula = expand_formula(formula, 'MAXIFS\(')
432
+ formula = expand_formula(formula, 'MINIFS\(')
433
+ formula = expand_formula(formula, 'MODE.MULT\(')
434
+ formula = expand_formula(formula, 'MODE.SNGL\(')
435
+ formula = expand_formula(formula, 'MUNIT\(')
436
+ formula = expand_formula(formula, 'NEGBINOM.DIST\(')
437
+ formula = expand_formula(formula, 'NORM.DIST\(')
438
+ formula = expand_formula(formula, 'NORM.INV\(')
439
+ formula = expand_formula(formula, 'NORM.S.DIST\(')
440
+ formula = expand_formula(formula, 'NORM.S.INV\(')
441
+ formula = expand_formula(formula, 'NUMBERVALUE\(')
442
+ formula = expand_formula(formula, 'PDURATION\(')
443
+ formula = expand_formula(formula, 'PERCENTILE.EXC\(')
444
+ formula = expand_formula(formula, 'PERCENTILE.INC\(')
445
+ formula = expand_formula(formula, 'PERCENTRANK.EXC\(')
446
+ formula = expand_formula(formula, 'PERCENTRANK.INC\(')
447
+ formula = expand_formula(formula, 'PERMUTATIONA\(')
448
+ formula = expand_formula(formula, 'PHI\(')
449
+ formula = expand_formula(formula, 'POISSON.DIST\(')
450
+ formula = expand_formula(formula, 'QUARTILE.EXC\(')
451
+ formula = expand_formula(formula, 'QUARTILE.INC\(')
452
+ formula = expand_formula(formula, 'QUERYSTRING\(')
453
+ formula = expand_formula(formula, 'RANK.AVG\(')
454
+ formula = expand_formula(formula, 'RANK.EQ\(')
455
+ formula = expand_formula(formula, 'RRI\(')
456
+ formula = expand_formula(formula, 'SECH\(')
457
+ formula = expand_formula(formula, 'SEC\(')
458
+ formula = expand_formula(formula, 'SHEETS\(')
459
+ formula = expand_formula(formula, 'SHEET\(')
460
+ formula = expand_formula(formula, 'SKEW.P\(')
461
+ formula = expand_formula(formula, 'STDEV.P\(')
462
+ formula = expand_formula(formula, 'STDEV.S\(')
463
+ formula = expand_formula(formula, 'T.DIST.2T\(')
464
+ formula = expand_formula(formula, 'T.DIST.RT\(')
465
+ formula = expand_formula(formula, 'T.DIST\(')
466
+ formula = expand_formula(formula, 'T.INV.2T\(')
467
+ formula = expand_formula(formula, 'T.INV\(')
468
+ formula = expand_formula(formula, 'T.TEST\(')
469
+ formula = expand_formula(formula, 'TEXTAFTER\(')
470
+ formula = expand_formula(formula, 'TEXTBEFORE\(')
471
+ formula = expand_formula(formula, 'TEXTJOIN\(')
472
+ formula = expand_formula(formula, 'UNICHAR\(')
473
+ formula = expand_formula(formula, 'UNICODE\(')
474
+ formula = expand_formula(formula, 'VALUETOTEXT\(')
475
+ formula = expand_formula(formula, 'VAR.P\(')
476
+ formula = expand_formula(formula, 'VAR.S\(')
477
+ formula = expand_formula(formula, 'WEBSERVICE\(')
478
+ formula = expand_formula(formula, 'WEIBULL.DIST\(')
479
+ formula = expand_formula(formula, 'XMATCH\(')
480
+ formula = expand_formula(formula, 'XOR\(')
481
+ expand_formula(formula, 'Z.TEST\(')
482
+ end
483
+
484
+ #
485
+ # :call-seq:
486
+ # write_formula(row, column, formula [ , format [ , value ] ])
487
+ #
488
+ # Write a formula or function to the cell specified by +row+ and +column+:
489
+ #
490
+ def write_formula(row, col, formula = nil, format = nil, value = nil)
491
+ # Check for a cell reference in A1 notation and substitute row and column
492
+ if (row_col_array = row_col_notation(row))
493
+ _row, _col = row_col_array
494
+ _formula = col
495
+ _format = formula
496
+ _value = format
497
+ else
498
+ _row = row
499
+ _col = col
500
+ _formula = formula
501
+ _format = format
502
+ _value = value
503
+ end
504
+ raise WriteXLSXInsufficientArgumentError if [_row, _col, _formula].include?(nil)
505
+
506
+ # Check for dynamic array functions.
507
+ regex = /\bANCHORARRAY\(|\bBYCOL\(|\bBYROW\(|\bCHOOSECOLS\(|\bCHOOSEROWS\(|\bDROP\(|\bEXPAND\(|\bFILTER\(|\bHSTACK\(|\bLAMBDA\(|\bMAKEARRAY\(|\bMAP\(|\bRANDARRAY\(|\bREDUCE\(|\bSCAN\(|\bSEQUENCE\(|\bSINGLE\(|\bSORT\(|\bSORTBY\(|\bSWITCH\(|\bTAKE\(|\bTEXTSPLIT\(|\bTOCOL\(|\bTOROW\(|\bUNIQUE\(|\bVSTACK\(|\bWRAPCOLS\(|\bWRAPROWS\(|\bXLOOKUP\(/
508
+ if _formula =~ regex
509
+ return write_dynamic_array_formula(
510
+ _row, _col, _row, _col, _formula, _format, _value
511
+ )
512
+ end
513
+
514
+ # Hand off array formulas.
515
+ if _formula =~ /^\{=.*\}$/
516
+ write_array_formula(_row, _col, _row, _col, _formula, _format, _value)
517
+ else
518
+ check_dimensions(_row, _col)
519
+ store_row_col_max_min_values(_row, _col)
520
+ _formula = prepare_formula(_formula)
521
+
522
+ store_data_to_table(FormulaCellData.new(_formula, _format, _value), _row, _col)
523
+ end
524
+ end
525
+
526
+ #
527
+ # Internal method shared by the write_array_formula() and
528
+ # write_dynamic_array_formula() methods.
529
+ #
530
+ def write_array_formula_base(type, *args)
531
+ # Check for a cell reference in A1 notation and substitute row and column
532
+ # Convert single cell to range
533
+ if args.first.to_s =~ /^([A-Za-z]+[0-9]+)$/
534
+ range = "#{::Regexp.last_match(1)}:#{::Regexp.last_match(1)}"
535
+ params = [range] + args[1..-1]
536
+ else
537
+ params = args
538
+ end
539
+
540
+ if (row_col_array = row_col_notation(params.first))
541
+ row1, col1, row2, col2 = row_col_array
542
+ formula, xf, value = params[1..-1]
543
+ else
544
+ row1, col1, row2, col2, formula, xf, value = params
545
+ end
546
+ raise WriteXLSXInsufficientArgumentError if [row1, col1, row2, col2, formula].include?(nil)
547
+
548
+ # Swap last row/col with first row/col as necessary
549
+ row1, row2 = row2, row1 if row1 > row2
550
+ col1, col2 = col2, col1 if col1 > col2
551
+
552
+ # Check that row and col are valid and store max and min values
553
+ check_dimensions(row1, col1)
554
+ check_dimensions(row2, col2)
555
+ store_row_col_max_min_values(row1, col1)
556
+ store_row_col_max_min_values(row2, col2)
557
+
558
+ # Define array range
559
+ range = if row1 == row2 && col1 == col2
560
+ xl_rowcol_to_cell(row1, col1)
561
+ else
562
+ "#{xl_rowcol_to_cell(row1, col1)}:#{xl_rowcol_to_cell(row2, col2)}"
563
+ end
564
+
565
+ # Modify the formula string, as needed.
566
+ formula = prepare_formula(formula, 1)
567
+
568
+ store_data_to_table(
569
+ if type == 'a'
570
+ FormulaArrayCellData.new(formula, xf, range, value)
571
+ elsif type == 'd'
572
+ DynamicFormulaArrayCellData.new(formula, xf, range, value)
573
+ else
574
+ raise "invalid type in write_array_formula_base()."
575
+ end,
576
+ row1, col1
577
+ )
578
+
579
+ # Pad out the rest of the area with formatted zeroes.
580
+ (row1..row2).each do |row|
581
+ (col1..col2).each do |col|
582
+ next if row == row1 && col == col1
583
+
584
+ write_number(row, col, 0, xf)
585
+ end
586
+ end
587
+ end
588
+
589
+ #
590
+ # write_array_formula(row1, col1, row2, col2, formula, format)
591
+ #
592
+ # Write an array formula to the specified row and column (zero indexed).
593
+ #
594
+ def write_array_formula(row1, col1, row2 = nil, col2 = nil, formula = nil, format = nil, value = nil)
595
+ write_array_formula_base('a', row1, col1, row2, col2, formula, format, value)
596
+ end
597
+
598
+ #
599
+ # write_dynamic_array_formula(row1, col1, row2, col2, formula, format)
600
+ #
601
+ # Write a dynamic formula to the specified row and column (zero indexed).
602
+ #
603
+ def write_dynamic_array_formula(row1, col1, row2 = nil, col2 = nil, formula = nil, format = nil, value = nil)
604
+ write_array_formula_base('d', row1, col1, row2, col2, formula, format, value)
605
+ @has_dynamic_functions = true
606
+ end
607
+
608
+ #
609
+ # write_boolean(row, col, val, format)
610
+ #
611
+ # Write a boolean value to the specified row and column (zero indexed).
612
+ #
613
+ def write_boolean(row, col, val = nil, format = nil)
614
+ if (row_col_array = row_col_notation(row))
615
+ _row, _col = row_col_array
616
+ _val = col
617
+ _format = val
618
+ else
619
+ _row = row
620
+ _col = col
621
+ _val = val
622
+ _format = format
623
+ end
624
+ raise WriteXLSXInsufficientArgumentError if _row.nil? || _col.nil?
625
+
626
+ _val = _val ? 1 : 0 # Boolean value.
627
+ # xf : cell format.
628
+
629
+ # Check that row and col are valid and store max and min values
630
+ check_dimensions(_row, _col)
631
+ store_row_col_max_min_values(_row, _col)
632
+
633
+ store_data_to_table(BooleanCellData.new(_val, _format), _row, _col)
634
+ end
635
+
636
+ #
637
+ # :call-seq:
638
+ # update_format_with_params(row, col, format_params)
639
+ #
640
+ # Update formatting of the cell to the specified row and column (zero indexed).
641
+ #
642
+ def update_format_with_params(row, col, params = nil)
643
+ if (row_col_array = row_col_notation(row))
644
+ _row, _col = row_col_array
645
+ _params = col
646
+ else
647
+ _row = row
648
+ _col = col
649
+ _params = params
650
+ end
651
+ raise WriteXLSXInsufficientArgumentError if _row.nil? || _col.nil? || _params.nil?
652
+
653
+ # Check that row and col are valid and store max and min values
654
+ check_dimensions(_row, _col)
655
+ store_row_col_max_min_values(_row, _col)
656
+
657
+ format = nil
658
+ cell_data = nil
659
+ if @cell_data_store[_row].nil? || @cell_data_store[_row][_col].nil?
660
+ format = @workbook.add_format(_params)
661
+ write_blank(_row, _col, format)
662
+ else
663
+ if @cell_data_store[_row][_col].xf.nil?
664
+ format = @workbook.add_format(_params)
665
+ cell_data = @cell_data_store[_row][_col]
666
+ else
667
+ format = @workbook.add_format
668
+ cell_data = @cell_data_store[_row][_col]
669
+ format.copy(cell_data.xf)
670
+ format.set_format_properties(_params)
671
+ end
672
+ value = case cell_data
673
+ when FormulaCellData
674
+ "=#{cell_data.token}"
675
+ when FormulaArrayCellData
676
+ "{=#{cell_data.token}}"
677
+ when StringCellData
678
+ @workbook.shared_strings.string(cell_data.data[:sst_id])
679
+ else
680
+ cell_data.data
681
+ end
682
+ write(_row, _col, value, format)
683
+ end
684
+ end
685
+
686
+ #
687
+ # :call-seq:
688
+ # write_url(row, column, url [ , format, label, tip ])
689
+ #
690
+ # Write a hyperlink to a URL in the cell specified by +row+ and +column+.
691
+ # The hyperlink is comprised of two elements: the visible label and
692
+ # the invisible link. The visible label is the same as the link unless
693
+ # an alternative label is specified. The label parameter is optional.
694
+ # The label is written using the {#write()}[#method-i-write] method. Therefore it is
695
+ # possible to write strings, numbers or formulas as labels.
696
+ #
697
+ def write_url(row, col, url = nil, format = nil, str = nil, tip = nil, ignore_write_string = false)
698
+ # Check for a cell reference in A1 notation and substitute row and column
699
+ if (row_col_array = row_col_notation(row))
700
+ _row, _col = row_col_array
701
+ _url = col
702
+ _format = url
703
+ _str = format
704
+ _tip = str
705
+ _ignore_write_string = tip
706
+ else
707
+ _row = row
708
+ _col = col
709
+ _url = url
710
+ _format = format
711
+ _str = str
712
+ _tip = tip
713
+ _ignore_write_string = ignore_write_string
714
+ end
715
+
716
+ _format, _str = _str, _format if _str.respond_to?(:xf_index) || (_format && !_format.respond_to?(:xf_index))
717
+ raise WriteXLSXInsufficientArgumentError if [_row, _col, _url].include?(nil)
718
+
719
+ # Check that row and col are valid and store max and min values
720
+ check_dimensions(_row, _col)
721
+ store_row_col_max_min_values(_row, _col)
722
+
723
+ hyperlink = Hyperlink.factory(_url, _str, _tip, @max_url_length)
724
+ store_hyperlink(_row, _col, hyperlink)
725
+
726
+ raise "URL '#{url}' added but URL exceeds Excel's limit of 65,530 URLs per worksheet." if hyperlinks_count > 65_530
727
+
728
+ # Add the default URL format.
729
+ _format ||= @default_url_format
730
+
731
+ # Write the hyperlink string.
732
+ write_string(_row, _col, hyperlink.str, _format) unless _ignore_write_string
733
+ end
734
+
735
+ #
736
+ # :call-seq:
737
+ # write_date_time (row, col, date_string [ , format ])
738
+ #
739
+ # Write a datetime string in ISO8601 "yyyy-mm-ddThh:mm:ss.ss" format as a
740
+ # number representing an Excel date. format is optional.
741
+ #
742
+ def write_date_time(row, col, str, format = nil)
743
+ # Check for a cell reference in A1 notation and substitute row and column
744
+ if (row_col_array = row_col_notation(row))
745
+ _row, _col = row_col_array
746
+ _str = col
747
+ _format = str
748
+ else
749
+ _row = row
750
+ _col = col
751
+ _str = str
752
+ _format = format
753
+ end
754
+ raise WriteXLSXInsufficientArgumentError if [_row, _col, _str].include?(nil)
755
+
756
+ # Check that row and col are valid and store max and min values
757
+ check_dimensions(_row, _col)
758
+ store_row_col_max_min_values(_row, _col)
759
+
760
+ date_time = convert_date_time(_str)
761
+
762
+ if date_time
763
+ store_data_to_table(DateTimeCellData.new(date_time, _format), _row, _col)
764
+ else
765
+ # If the date isn't valid then write it as a string.
766
+ write_string(_row, _col, _str, _format)
767
+ end
768
+ end
769
+
770
+ #
771
+ # Causes the write() method to treat integers with a leading zero as a string.
772
+ # This ensures that any leading zeros such, as in zip codes, are maintained.
773
+ #
774
+ def keep_leading_zeros(flag = true)
775
+ @leading_zeros = !!flag
776
+ end
777
+
778
+ #
779
+ # merge_range(first_row, first_col, last_row, last_col, string, format)
780
+ #
781
+ # Merge a range of cells. The first cell should contain the data and the
782
+ # others should be blank. All cells should contain the same format.
783
+ #
784
+ def merge_range(*args)
785
+ if (row_col_array = row_col_notation(args.first))
786
+ row_first, col_first, row_last, col_last = row_col_array
787
+ string, format, *extra_args = args[1..-1]
788
+ else
789
+ row_first, col_first, row_last, col_last,
790
+ string, format, *extra_args = args
791
+ end
792
+
793
+ raise "Incorrect number of arguments" if [row_first, col_first, row_last, col_last, format].include?(nil)
794
+ raise "Fifth parameter must be a format object" unless format.respond_to?(:xf_index)
795
+ raise "Can't merge single cell" if row_first == row_last && col_first == col_last
796
+
797
+ # Swap last row/col with first row/col as necessary
798
+ row_first, row_last = row_last, row_first if row_first > row_last
799
+ col_first, col_last = col_last, col_first if col_first > col_last
800
+
801
+ # Check that the data range is valid and store the max and min values.
802
+ check_dimensions(row_first, col_first)
803
+ check_dimensions(row_last, col_last)
804
+ store_row_col_max_min_values(row_first, col_first)
805
+ store_row_col_max_min_values(row_last, col_last)
806
+
807
+ # Store the merge range.
808
+ @merge << [row_first, col_first, row_last, col_last]
809
+
810
+ # Write the first cell
811
+ write(row_first, col_first, string, format, *extra_args)
812
+
813
+ # Pad out the rest of the area with formatted blank cells.
814
+ write_formatted_blank_to_area(row_first, row_last, col_first, col_last, format)
815
+ end
816
+
817
+ #
818
+ # Same as merge_range() above except the type of
819
+ # {#write()}[#method-i-write] is specified.
820
+ #
821
+ def merge_range_type(type, *args)
822
+ case type
823
+ when 'array_formula', 'blank', 'rich_string'
824
+ if (row_col_array = row_col_notation(args.first))
825
+ row_first, col_first, row_last, col_last = row_col_array
826
+ *others = args[1..-1]
827
+ else
828
+ row_first, col_first, row_last, col_last, *others = args
829
+ end
830
+ format = others.pop
831
+ else
832
+ if (row_col_array = row_col_notation(args.first))
833
+ row_first, col_first, row_last, col_last = row_col_array
834
+ token, format, *others = args[1..-1]
835
+ else
836
+ row_first, col_first, row_last, col_last,
837
+ token, format, *others = args
838
+ end
839
+ end
840
+
841
+ raise "Format object missing or in an incorrect position" unless format.respond_to?(:xf_index)
842
+ raise "Can't merge single cell" if row_first == row_last && col_first == col_last
843
+
844
+ # Swap last row/col with first row/col as necessary
845
+ row_first, row_last = row_last, row_first if row_first > row_last
846
+ col_first, col_last = col_last, col_first if col_first > col_last
847
+
848
+ # Check that the data range is valid and store the max and min values.
849
+ check_dimensions(row_first, col_first)
850
+ check_dimensions(row_last, col_last)
851
+ store_row_col_max_min_values(row_first, col_first)
852
+ store_row_col_max_min_values(row_last, col_last)
853
+
854
+ # Store the merge range.
855
+ @merge << [row_first, col_first, row_last, col_last]
856
+
857
+ # Write the first cell
858
+ case type
859
+ when 'blank', 'rich_string', 'array_formula'
860
+ others << format
861
+ end
862
+
863
+ case type
864
+ when 'string'
865
+ write_string(row_first, col_first, token, format, *others)
866
+ when 'number'
867
+ write_number(row_first, col_first, token, format, *others)
868
+ when 'blank'
869
+ write_blank(row_first, col_first, *others)
870
+ when 'date_time'
871
+ write_date_time(row_first, col_first, token, format, *others)
872
+ when 'rich_string'
873
+ write_rich_string(row_first, col_first, *others)
874
+ when 'url'
875
+ write_url(row_first, col_first, token, format, *others)
876
+ when 'formula'
877
+ write_formula(row_first, col_first, token, format, *others)
878
+ when 'array_formula'
879
+ write_formula_array(row_first, col_first, *others)
880
+ else
881
+ raise "Unknown type '#{type}'"
882
+ end
883
+
884
+ # Pad out the rest of the area with formatted blank cells.
885
+ write_formatted_blank_to_area(row_first, row_last, col_first, col_last, format)
886
+ end
887
+
888
+ #
889
+ # :call-seq:
890
+ # repeat_formula(row, column, formula [ , format ])
891
+ #
892
+ # Deprecated. This is a writeexcel gem's method that is no longer
893
+ # required by WriteXLSX.
894
+ #
895
+ def repeat_formula(row, col, formula, format, *pairs)
896
+ # Check for a cell reference in A1 notation and substitute row and column.
897
+ if (row_col_array = row_col_notation(row))
898
+ _row, _col = row_col_array
899
+ _formula = col
900
+ _format = formula
901
+ _pairs = [format] + pairs
902
+ else
903
+ _row = row
904
+ _col = col
905
+ _formula = formula
906
+ _format = format
907
+ _pairs = pairs
908
+ end
909
+ raise WriteXLSXInsufficientArgumentError if [_row, _col].include?(nil)
910
+
911
+ raise "Odd number of elements in pattern/replacement list" unless _pairs.size.even?
912
+ raise "Not a valid formula" unless _formula.respond_to?(:to_ary)
913
+
914
+ tokens = _formula.join("\t").split("\t")
915
+ raise "No tokens in formula" if tokens.empty?
916
+
917
+ _value = nil
918
+ if _pairs[-2] == 'result'
919
+ _value = _pairs.pop
920
+ _pairs.pop
921
+ end
922
+ until _pairs.empty?
923
+ pattern = _pairs.shift
924
+ replace = _pairs.shift
925
+
926
+ tokens.each do |token|
927
+ break if token.sub!(pattern, replace)
928
+ end
929
+ end
930
+ _formula = tokens.join
931
+ write_formula(_row, _col, _formula, _format, _value)
932
+ end
933
+
934
+ #
935
+ # :call-seq:
936
+ # update_range_format_with_params(row_first, col_first, row_last, col_last, format_params)
937
+ #
938
+ # Update formatting of cells in range to the specified row and column (zero indexed).
939
+ #
940
+ # def update_range_format_with_params(row_first, col_first, row_last = nil, col_last = nil, params = nil)
941
+ # if (row_col_array = row_col_notation(row_first))
942
+ # _row_first, _col_first, _row_last, _col_last = row_col_array
943
+ # params = args[1..-1]
944
+ # else
945
+ # _row_first = row_first
946
+ # _col_first = col_first
947
+ # _row_last = row_last
948
+ # _col_last = col_last
949
+ # _params = params
950
+ # end
951
+
952
+ # raise WriteXLSXInsufficientArgumentError if [_row_first, _col_first, _row_last, _col_last, _params].include?(nil)
953
+
954
+ # # Swap last row/col with first row/col as necessary
955
+ # _row_first, _row_last = _row_last, _row_first if _row_first > _row_last
956
+ # _col_first, _col_last = _col_last, _col_first if _col_first > _col_last
957
+
958
+ # # Check that column number is valid and store the max value
959
+ # check_dimensions(_row_last, _col_last)
960
+ # store_row_col_max_min_values(_row_last, _col_last)
961
+
962
+ # (_row_first.._row_last).each do |row|
963
+ # (_col_first.._col_last).each do |col|
964
+ # update_format_with_params(row, col, _params)
965
+ # end
966
+ # end
967
+ # end
968
+ def update_range_format_with_params(row_first, col_first, row_last = nil, col_last = nil, params = nil)
969
+ if (row_col_array = row_col_notation(row_first))
970
+ _row_first, _col_first, _row_last, _col_last = row_col_array
971
+ _params = col_first
972
+ else
973
+ _row_first, _col_first = row_first, col_first
974
+ _row_last, _col_last = row_last, col_last
975
+ _params = params
976
+ end
977
+
978
+ raise WriteXLSXInsufficientArgumentError if [_row_first, _col_first, _row_last, _col_last, _params].include?(nil)
979
+
980
+ _row_first, _row_last = _row_last, _row_first if _row_first > _row_last
981
+ _col_first, _col_last = _col_last, _col_first if _col_first > _col_last
982
+
983
+ check_dimensions(_row_last, _col_last)
984
+ store_row_col_max_min_values(_row_last, _col_last)
985
+
986
+ (_row_first.._row_last).each do |row|
987
+ (_col_first.._col_last).each do |col|
988
+ update_format_with_params(row, col, _params)
989
+ end
990
+ end
991
+ end
992
+
993
+ private
994
+
995
+ def store_hyperlink(row, col, hyperlink)
996
+ @hyperlinks ||= {}
997
+ @hyperlinks[row] ||= {}
998
+ @hyperlinks[row][col] = hyperlink
999
+ end
1000
+
1001
+ def hyperlinks_count
1002
+ @hyperlinks.keys.inject(0) { |s, n| s += @hyperlinks[n].keys.size }
1003
+ end
1004
+
1005
+ # Pad out the rest of the area with formatted blank cells.
1006
+ def write_formatted_blank_to_area(row_first, row_last, col_first, col_last, format)
1007
+ (row_first..row_last).each do |row|
1008
+ (col_first..col_last).each do |col|
1009
+ next if row == row_first && col == col_first
1010
+
1011
+ write_blank(row, col, format)
1012
+ end
1013
+ end
1014
+ end
1015
+ end
1016
+ end
1017
+ end