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