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,60 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Worksheet
6
+ class AssetManager
7
+ attr_reader :charts, :images, :tables, :sparklines, :shapes
8
+ attr_accessor :background_image
9
+ attr_reader :header_images, :footer_images
10
+
11
+ def initialize
12
+ @charts = []
13
+ @images = []
14
+ @tables = []
15
+ @sparklines = []
16
+ @shapes = []
17
+
18
+ @header_images = []
19
+ @footer_images = []
20
+ @background_image = nil
21
+ end
22
+
23
+ def add_chart(chart)
24
+ @charts << chart
25
+ end
26
+
27
+ def add_image(image)
28
+ @images << image
29
+ end
30
+
31
+ def add_table(table)
32
+ @tables << table
33
+ end
34
+
35
+ def add_sparkline(sparkline)
36
+ @sparklines << sparkline
37
+ end
38
+
39
+ def add_shape(shape)
40
+ @shapes << shape
41
+ end
42
+
43
+ def reset_header_images
44
+ @header_images
45
+ end
46
+
47
+ def reset_footer_images
48
+ @footer_images
49
+ end
50
+
51
+ def add_header_image(image)
52
+ @header_images << image
53
+ end
54
+
55
+ def add_footer_image(image)
56
+ @footer_images << image
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,390 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'write_xlsx/constants'
4
+
5
+ module Writexlsx
6
+ class Worksheet
7
+ # Autofilter-related operations extracted from Worksheet to slim the main class.
8
+ module Autofilter
9
+ include Constants
10
+
11
+ #
12
+ # :call-seq:
13
+ # autofilter(first_row, first_col, last_row, last_col)
14
+ #
15
+ # Set the autofilter area in the worksheet.
16
+ #
17
+ def autofilter(row1, col1 = nil, row2 = nil, col2 = nil)
18
+ if (row_col_array = row_col_notation(row1))
19
+ _row1, _col1, _row2, _col2 = row_col_array
20
+ else
21
+ _row1 = row1
22
+ _col1 = col1
23
+ _row2 = row2
24
+ _col2 = col2
25
+ end
26
+ return if [_row1, _col1, _row2, _col2].include?(nil)
27
+
28
+ # Reverse max and min values if necessary.
29
+ _row1, _row2 = _row2, _row1 if _row2 < _row1
30
+ _col1, _col2 = _col2, _col1 if _col2 < _col1
31
+
32
+ @autofilter_area = convert_name_area(_row1, _col1, _row2, _col2)
33
+ @autofilter_ref = xl_range(_row1, _row2, _col1, _col2)
34
+ @filter_range = [_col1, _col2]
35
+
36
+ # Store the filter cell positions for use in the autofit calculation.
37
+ (_col1.._col2).each do |col|
38
+ @filter_cells["#{_row1}:#{col}"] = 1
39
+ end
40
+ end
41
+
42
+ #
43
+ # Set the column filter criteria.
44
+ #
45
+ # The filter_column method can be used to filter columns in a autofilter
46
+ # range based on simple conditions.
47
+ #
48
+ def filter_column(col, expression)
49
+ raise "Must call autofilter before filter_column" unless @autofilter_area
50
+
51
+ col = prepare_filter_column(col)
52
+
53
+ tokens = extract_filter_tokens(expression)
54
+
55
+ raise "Incorrect number of tokens in expression '#{expression}'" unless [3, 7].include?(tokens.size)
56
+
57
+ tokens = parse_filter_expression(expression, tokens)
58
+
59
+ # Excel handles single or double custom filters as default filters. We need
60
+ # to check for them and handle them accordingly.
61
+ if tokens.size == 2 && tokens[0] == 2
62
+ # Single equality.
63
+ filter_column_list(col, tokens[1])
64
+ elsif tokens.size == 5 && tokens[0] == 2 && tokens[2] == 1 && tokens[3] == 2
65
+ # Double equality with "or" operator.
66
+ filter_column_list(col, tokens[1], tokens[4])
67
+ else
68
+ # Non default custom filter.
69
+ @filter_cols[col] = Array.new(tokens)
70
+ @filter_type[col] = 0
71
+ end
72
+
73
+ @filter_on = 1
74
+ end
75
+
76
+ #
77
+ # Set the column filter criteria in Excel 2007 list style.
78
+ #
79
+ def filter_column_list(col, *tokens)
80
+ tokens.flatten!
81
+ raise "Incorrect number of arguments to filter_column_list" if tokens.empty?
82
+ raise "Must call autofilter before filter_column_list" unless @autofilter_area
83
+
84
+ col = prepare_filter_column(col)
85
+
86
+ @filter_cols[col] = tokens
87
+ @filter_type[col] = 1 # Default style.
88
+ @filter_on = 1
89
+ end
90
+
91
+ def prepare_filter_column(col) # :nodoc:
92
+ # Check for a column reference in A1 notation and substitute.
93
+ if col.to_s =~ /^\D/
94
+ col_letter = col
95
+
96
+ # Convert col ref to a cell ref and then to a col number.
97
+ _dummy, col = substitute_cellref("#{col}1")
98
+ raise "Invalid column '#{col_letter}'" if col >= COL_MAX
99
+ end
100
+
101
+ col_first, col_last = @filter_range
102
+
103
+ # Reject column if it is outside filter range.
104
+ raise "Column '#{col}' outside autofilter column range (#{col_first} .. #{col_last})" if col < col_first || col > col_last
105
+
106
+ col
107
+ end
108
+
109
+ #
110
+ # Write the <autoFilter> element.
111
+ #
112
+ def write_auto_filter # :nodoc:
113
+ return unless autofilter_ref?
114
+
115
+ attributes = [
116
+ ['ref', @autofilter_ref]
117
+ ]
118
+
119
+ if filter_on?
120
+ # Autofilter defined active filters.
121
+ @writer.tag_elements('autoFilter', attributes) do
122
+ write_autofilters
123
+ end
124
+ else
125
+ # Autofilter defined without active filters.
126
+ @writer.empty_tag('autoFilter', attributes)
127
+ end
128
+ end
129
+
130
+ #
131
+ # Function to iterate through the columns that form part of an autofilter
132
+ # range and write the appropriate filters.
133
+ #
134
+ def write_autofilters # :nodoc:
135
+ col1, col2 = @filter_range
136
+
137
+ (col1..col2).each do |col|
138
+ # Skip if column doesn't have an active filter.
139
+ next unless @filter_cols[col]
140
+
141
+ # Retrieve the filter tokens and write the autofilter records.
142
+ tokens = @filter_cols[col]
143
+ type = @filter_type[col]
144
+
145
+ # Filters are relative to first column in the autofilter.
146
+ write_filter_column(col - col1, type, *tokens)
147
+ end
148
+ end
149
+
150
+ #
151
+ # Write the <filterColumn> element.
152
+ #
153
+ def write_filter_column(col_id, type, *filters) # :nodoc:
154
+ @writer.tag_elements('filterColumn', [['colId', col_id]]) do
155
+ if type == 1
156
+ # Type == 1 is the new XLSX style filter.
157
+ write_filters(*filters)
158
+ else
159
+ # Type == 0 is the classic "custom" filter.
160
+ write_custom_filters(*filters)
161
+ end
162
+ end
163
+ end
164
+
165
+ #
166
+ # Write the <filters> element.
167
+ #
168
+ def write_filters(*filters) # :nodoc:
169
+ non_blanks = filters.reject { |filter| filter.to_s =~ /^blanks$/i }
170
+ attributes = []
171
+
172
+ attributes = [['blank', 1]] if filters != non_blanks
173
+
174
+ if filters.size == 1 && non_blanks.empty?
175
+ # Special case for blank cells only.
176
+ @writer.empty_tag('filters', attributes)
177
+ else
178
+ # General case.
179
+ @writer.tag_elements('filters', attributes) do
180
+ non_blanks.sort.each { |filter| write_filter(filter) }
181
+ end
182
+ end
183
+ end
184
+
185
+ #
186
+ # Write the <filter> element.
187
+ #
188
+ def write_filter(val) # :nodoc:
189
+ @writer.empty_tag('filter', [['val', val]])
190
+ end
191
+
192
+ #
193
+ # Write the <customFilters> element.
194
+ #
195
+ def write_custom_filters(*tokens) # :nodoc:
196
+ if tokens.size == 2
197
+ # One filter expression only.
198
+ @writer.tag_elements('customFilters') { write_custom_filter(*tokens) }
199
+ else
200
+ # Two filter expressions.
201
+
202
+ # Check if the "join" operand is "and" or "or".
203
+ attributes = if tokens[2] == 0
204
+ [['and', 1]]
205
+ else
206
+ [['and', 0]]
207
+ end
208
+
209
+ # Write the two custom filters.
210
+ @writer.tag_elements('customFilters', attributes) do
211
+ write_custom_filter(tokens[0], tokens[1])
212
+ write_custom_filter(tokens[3], tokens[4])
213
+ end
214
+ end
215
+ end
216
+
217
+ #
218
+ # Write the <customFilter> element.
219
+ #
220
+ def write_custom_filter(operator, val) # :nodoc:
221
+ operators = {
222
+ 1 => 'lessThan',
223
+ 2 => 'equal',
224
+ 3 => 'lessThanOrEqual',
225
+ 4 => 'greaterThan',
226
+ 5 => 'notEqual',
227
+ 6 => 'greaterThanOrEqual',
228
+ 7 => 'startsWith',
229
+ 8 => 'notStartsWith',
230
+ 9 => 'endsWith',
231
+ 10 => 'notEndsWith',
232
+ 11 => 'contains',
233
+ 12 => 'notContains',
234
+ 13 => 'between',
235
+ 14 => 'notBetween'
236
+ }
237
+
238
+ attributes = [
239
+ ['operator', operators[operator]],
240
+ ['val', val]
241
+ ]
242
+
243
+ @writer.empty_tag('customFilter', attributes)
244
+ end
245
+
246
+ private
247
+
248
+ #
249
+ # Extract the tokens from the filter expression. The tokens are mainly non-
250
+ # whitespace groups. The only tricky part is to extract string tokens that
251
+ # contain whitespace and/or quoted double quotes (Excel's escaped quotes).
252
+ #
253
+ def extract_filter_tokens(expression = nil) # :nodoc:
254
+ return [] unless expression
255
+
256
+ tokens = []
257
+ str = expression
258
+ while str =~ /"(?:[^"]|"")*"|\S+/
259
+ tokens << ::Regexp.last_match(0)
260
+ str = $LAST_MATCH_INFO.post_match
261
+ end
262
+
263
+ # Remove leading and trailing quotes and unescape other quotes
264
+ tokens.map! do |token|
265
+ token.sub!(/^"/, '')
266
+ token.sub!(/"$/, '')
267
+ token.gsub!('""', '"')
268
+
269
+ # if token is number, convert to numeric.
270
+ if token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
271
+ token.to_f == token.to_i ? token.to_i : token.to_f
272
+ else
273
+ token
274
+ end
275
+ end
276
+
277
+ tokens
278
+ end
279
+
280
+ #
281
+ # Converts the tokens of a possibly conditional expression into 1 or 2
282
+ # sub expressions for further parsing.
283
+ #
284
+ def parse_filter_expression(expression, tokens) # :nodoc:
285
+ # The number of tokens will be either 3 (for 1 expression)
286
+ # or 7 (for 2 expressions).
287
+ #
288
+ if tokens.size == 7
289
+ conditional = tokens[3]
290
+ if conditional =~ /^(and|&&)$/
291
+ conditional = 0
292
+ elsif conditional =~ /^(or|\|\|)$/
293
+ conditional = 1
294
+ else
295
+ raise "Token '#{conditional}' is not a valid conditional " \
296
+ "in filter expression '#{expression}'"
297
+ end
298
+ expression_1 = parse_filter_tokens(expression, tokens[0..2])
299
+ expression_2 = parse_filter_tokens(expression, tokens[4..6])
300
+ [expression_1, conditional, expression_2].flatten
301
+ else
302
+ parse_filter_tokens(expression, tokens)
303
+ end
304
+ end
305
+
306
+ #
307
+ # Parse the 3 tokens of a filter expression and return the operator and token.
308
+ #
309
+ def parse_filter_tokens(expression, tokens) # :nodoc:
310
+ operators = {
311
+ '==' => 2,
312
+ '=' => 2,
313
+ '=~' => 2,
314
+ 'eq' => 2,
315
+
316
+ '!=' => 5,
317
+ '!~' => 5,
318
+ 'ne' => 5,
319
+ '<>' => 5,
320
+
321
+ '<' => 1,
322
+ '<=' => 3,
323
+ '>' => 4,
324
+ '>=' => 6
325
+ }
326
+
327
+ operator = operators[tokens[1]]
328
+ token = tokens[2]
329
+
330
+ # Special handling of "Top" filter expressions.
331
+ if tokens[0] =~ /^top|bottom$/i
332
+ value = tokens[1]
333
+ if value.to_s =~ /\D/ || value.to_i < 1 || value.to_i > 500
334
+ raise "The value '#{value}' in expression '#{expression}' " \
335
+ "must be in the range 1 to 500"
336
+ end
337
+ token.downcase!
338
+ if token != 'items' && token != '%'
339
+ raise "The type '#{token}' in expression '#{expression}' " \
340
+ "must be either 'items' or '%'"
341
+ end
342
+
343
+ operator = if tokens[0] =~ /^top$/i
344
+ 30
345
+ else
346
+ 32
347
+ end
348
+
349
+ operator += 1 if tokens[2] == '%'
350
+
351
+ token = value
352
+ end
353
+
354
+ if !operator && tokens[0]
355
+ raise "Token '#{tokens[1]}' is not a valid operator " \
356
+ "in filter expression '#{expression}'"
357
+ end
358
+
359
+ # Special handling for Blanks/NonBlanks.
360
+ if token.to_s =~ /^blanks|nonblanks$/i
361
+ # Only allow Equals or NotEqual in this context.
362
+ if operator != 2 && operator != 5
363
+ raise "The operator '#{tokens[1]}' in expression '#{expression}' " \
364
+ "is not valid in relation to Blanks/NonBlanks'"
365
+ end
366
+
367
+ token.downcase!
368
+
369
+ # The operator should always be 2 (=) to flag a "simple" equality in
370
+ # the binary record. Therefore we convert <> to =.
371
+ if token == 'blanks'
372
+ token = ' ' if operator == 5
373
+ elsif operator == 5
374
+ operator = 2
375
+ token = 'blanks'
376
+ else
377
+ operator = 5
378
+ token = ' '
379
+ end
380
+ end
381
+
382
+ # if the string token contains an Excel match character then change the
383
+ # operator type to indicate a non "simple" equality.
384
+ operator = 22 if operator == 2 && token.to_s =~ /[*?]/
385
+
386
+ [operator, token]
387
+ end
388
+ end
389
+ end
390
+ end
@@ -1,10 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'write_xlsx/utility/common'
5
+ require 'write_xlsx/utility/cell_reference'
6
+
4
7
  module Writexlsx
5
8
  class Worksheet
6
9
  class CellData # :nodoc:
7
- include Writexlsx::Utility
10
+ include Writexlsx::Utility::Common
11
+ include Writexlsx::Utility::CellReference
8
12
 
9
13
  attr_reader :xf
10
14
 
@@ -105,20 +109,23 @@ module Writexlsx
105
109
  truefalse = { 'TRUE' => 1, 'FALSE' => 0 }
106
110
  error_code = ['#DIV/0!', '#N/A', '#NAME?', '#NULL!', '#NUM!', '#REF!', '#VALUE!']
107
111
 
112
+ value = @result
108
113
  attributes = cell_attributes(worksheet, row, row_name, col)
109
- if @result && !(@result.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
110
- if truefalse[@result]
114
+
115
+ if value && !(value.to_s =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/)
116
+ if truefalse[value]
111
117
  attributes << %w[t b]
112
- @result = truefalse[@result]
113
- elsif error_code.include?(@result)
118
+ value = truefalse[value]
119
+ elsif error_code.include?(value)
114
120
  attributes << %w[t e]
115
121
  else
116
122
  attributes << %w[t str]
117
123
  end
118
124
  end
125
+
119
126
  worksheet.writer.tag_elements('c', attributes) do
120
127
  worksheet.write_cell_formula(token)
121
- worksheet.write_cell_value(result || 0)
128
+ worksheet.write_cell_value(value || 0)
122
129
  end
123
130
  end
124
131
  end
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Worksheet
6
+ module CellDataManager
7
+ #
8
+ # Store a CellData object in the worksheet cell table.
9
+ #
10
+ def store_data_to_table(cell_data, row, col) # :nodoc:
11
+ @cell_data_store.store(cell_data, row, col)
12
+ end
13
+
14
+ #
15
+ # Track row/col min/max used for range calculation.
16
+ #
17
+ def store_row_col_max_min_values(row, col)
18
+ store_row_max_min_values(row)
19
+ store_col_max_min_values(col)
20
+ end
21
+
22
+ #
23
+ # Add a string to the shared string table, if it isn't already there, and
24
+ # return the string index.
25
+ #
26
+ def shared_string_index(str) # :nodoc:
27
+ @workbook.shared_string_index(str)
28
+ end
29
+
30
+ #
31
+ # Returns a range of data from the worksheet cell table to be used in
32
+ # chart cached data. Strings are returned as SST ids and decoded in the
33
+ # workbook. Return nils for data that doesn't exist since Excel can chart
34
+ # series with data missing.
35
+ #
36
+ def get_range_data(row_start, col_start, row_end, col_end)
37
+ @cell_data_store.get_range_data(row_start, col_start, row_end, col_end)
38
+ end
39
+
40
+ private
41
+
42
+ def cell_data_store # :nodoc:
43
+ @cell_data_store
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+ module Writexlsx
5
+ class Worksheet
6
+ class CellDataStore # :nodoc:
7
+ def initialize
8
+ @table = []
9
+ end
10
+
11
+ def [](row)
12
+ @table[row]
13
+ end
14
+
15
+ def row(row)
16
+ @table[row]
17
+ end
18
+
19
+ def row?(row)
20
+ !@table[row].nil?
21
+ end
22
+
23
+ def store(cell_data, row, col)
24
+ @table[row] ||= []
25
+ @table[row][col] = cell_data
26
+ end
27
+
28
+ def fetch(row, col)
29
+ return nil unless @table[row]
30
+
31
+ @table[row][col]
32
+ end
33
+
34
+ def get_range_data(row_start, col_start, row_end, col_end)
35
+ data = []
36
+
37
+ (row_start..row_end).each do |row_num|
38
+ unless row?(row_num)
39
+ data << nil
40
+ next
41
+ end
42
+
43
+ (col_start..col_end).each do |col_num|
44
+ cell = fetch(row_num, col_num)
45
+ data << (cell ? cell.data : nil)
46
+ end
47
+ end
48
+
49
+ data
50
+ end
51
+
52
+ def each_row
53
+ return enum_for(__method__) unless block_given?
54
+
55
+ @table.each_with_index do |row_data, row_num|
56
+ yield row_num, row_data if row_data
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end