xlsxrb 0.1.14 → 0.1.16
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/CHANGELOG.md +108 -0
- data/README.md +37 -17
- data/benchmark/micro_ips.rb +70 -0
- data/lib/xlsxrb/elements/styles.rb +427 -15
- data/lib/xlsxrb/elements/workbook.rb +67 -4
- data/lib/xlsxrb/elements/worksheet.rb +117 -7
- data/lib/xlsxrb/ooxml/cfb.rb +10 -0
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +56 -38
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +14 -2
- data/lib/xlsxrb/ooxml/reader/listeners/style_listeners.rb +12 -7
- data/lib/xlsxrb/ooxml/reader.rb +167 -139
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +83 -28
- data/lib/xlsxrb/ooxml/styles_parser.rb +57 -14
- data/lib/xlsxrb/ooxml/utils.rb +59 -2
- data/lib/xlsxrb/ooxml/workbook_parser.rb +63 -4
- data/lib/xlsxrb/ooxml/workbook_writer.rb +232 -59
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +192 -123
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +307 -125
- data/lib/xlsxrb/ooxml/writer/drawing_xml.rb +16 -12
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +1 -1
- data/lib/xlsxrb/ooxml/writer/styles_xml.rb +64 -15
- data/lib/xlsxrb/ooxml/writer.rb +31 -28
- data/lib/xlsxrb/ooxml/xml_builder.rb +22 -2
- data/lib/xlsxrb/ooxml/xml_parser.rb +45 -0
- data/lib/xlsxrb/ooxml/zip_reader.rb +83 -32
- data/lib/xlsxrb/stream_row.rb +4 -3
- data/lib/xlsxrb/stream_sheet.rb +259 -7
- data/lib/xlsxrb/stream_writer.rb +197 -31
- data/lib/xlsxrb/utils.rb +68 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +110 -19
- data/lib/xlsxrb/worksheet_builder.rb +526 -15
- data/lib/xlsxrb.rb +889 -90
- data/sig/generated/xlsxrb/elements/styles.rbs +271 -5
- data/sig/generated/xlsxrb/elements/workbook.rbs +54 -3
- data/sig/generated/xlsxrb/elements/worksheet.rbs +63 -2
- data/sig/generated/xlsxrb/ooxml/reader/listeners/style_listeners.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +12 -0
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +3 -1
- data/sig/generated/xlsxrb/ooxml/styles_parser.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/utils.rbs +22 -0
- data/sig/generated/xlsxrb/ooxml/workbook_parser.rbs +7 -0
- data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +13 -1
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +18 -2
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +13 -3
- data/sig/generated/xlsxrb/ooxml/writer/styles_xml.rbs +5 -2
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +6 -1
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +9 -6
- data/sig/generated/xlsxrb/stream_row.rbs +3 -2
- data/sig/generated/xlsxrb/stream_sheet.rbs +132 -6
- data/sig/generated/xlsxrb/stream_writer.rbs +118 -27
- data/sig/generated/xlsxrb/utils.rbs +45 -0
- data/sig/generated/xlsxrb/worksheet_builder.rbs +252 -9
- data/sig/generated/xlsxrb.rbs +84 -10
- metadata +2 -1
|
@@ -24,11 +24,14 @@ module Xlsxrb
|
|
|
24
24
|
result.freeze
|
|
25
25
|
end.freeze
|
|
26
26
|
|
|
27
|
-
INTEGER_STRINGS = (0..65_535).map
|
|
27
|
+
INTEGER_STRINGS = (0..65_535).map { |n| -n.to_s }.freeze
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
attr_accessor :escape_formulas
|
|
30
|
+
|
|
31
|
+
def initialize(io, date1904: false, escape_formulas: false)
|
|
30
32
|
@io = io
|
|
31
33
|
@date1904 = date1904 ? true : false
|
|
34
|
+
@escape_formulas = escape_formulas ? true : false
|
|
32
35
|
@builder = XmlBuilder.new(@io)
|
|
33
36
|
@row_buffer = String.new(capacity: 65_536)
|
|
34
37
|
@started = false
|
|
@@ -47,7 +50,7 @@ module Xlsxrb
|
|
|
47
50
|
# split_pane: { x_split:, y_split:, top_left_cell: }
|
|
48
51
|
# selection: { active_cell:, sqref:, pane: }
|
|
49
52
|
# sheet_view: Hash of sheet view properties
|
|
50
|
-
def start(columns: [], sheet_properties: nil, freeze_pane: nil, split_pane: nil, selection: nil, sheet_view: nil)
|
|
53
|
+
def start(columns: [], sheet_properties: nil, freeze_pane: nil, split_pane: nil, selection: nil, sheet_view: nil, sheet_format: nil)
|
|
51
54
|
return if @started
|
|
52
55
|
|
|
53
56
|
@started = true
|
|
@@ -59,12 +62,12 @@ module Xlsxrb
|
|
|
59
62
|
columns.each do |col|
|
|
60
63
|
if col.is_a?(Elements::Column)
|
|
61
64
|
s = col.style_index
|
|
62
|
-
min = col.index
|
|
63
|
-
max = col.index
|
|
65
|
+
min = col.unmapped_data[:min] ? col.unmapped_data[:min] - 1 : col.index
|
|
66
|
+
max = col.unmapped_data[:max] ? col.unmapped_data[:max] - 1 : col.index
|
|
64
67
|
else
|
|
65
68
|
s = col[:style_index] || col[:style]
|
|
66
|
-
min = col[:
|
|
67
|
-
max = col[:
|
|
69
|
+
min = col[:min] ? col[:min] - 1 : (col[:index] || 0)
|
|
70
|
+
max = col[:max] ? col[:max] - 1 : (col[:index] || min)
|
|
68
71
|
end
|
|
69
72
|
next unless s
|
|
70
73
|
|
|
@@ -75,6 +78,7 @@ module Xlsxrb
|
|
|
75
78
|
|
|
76
79
|
write_sheet_properties(sheet_properties) if sheet_properties && !sheet_properties.empty?
|
|
77
80
|
write_sheet_views(freeze_pane: freeze_pane, split_pane: split_pane, selection: selection, sheet_view: sheet_view) if freeze_pane || split_pane || selection || (sheet_view && !sheet_view.empty?)
|
|
81
|
+
write_sheet_format_pr(sheet_format) if sheet_format && !sheet_format.empty?
|
|
78
82
|
write_columns(columns) unless columns.empty?
|
|
79
83
|
|
|
80
84
|
@builder.open_tag("sheetData")
|
|
@@ -88,7 +92,7 @@ module Xlsxrb
|
|
|
88
92
|
row_num_str = row_num.to_s
|
|
89
93
|
buf = @row_buffer ||= String.new(capacity: 65_536)
|
|
90
94
|
buf << '<row r="' << row_num_str << '"'
|
|
91
|
-
buf << ' s="' << attrs[:style_index].to_s << '" customFormat="1"' if attrs[:style_index]
|
|
95
|
+
buf << ' s="' << attrs[:style_index].to_s << '" customFormat="1"' if attrs[:style_index].is_a?(Integer)
|
|
92
96
|
if attrs[:height]
|
|
93
97
|
buf << ' ht="' << attrs[:height].to_s << '" customHeight="1"'
|
|
94
98
|
elsif attrs[:custom_height]
|
|
@@ -102,7 +106,7 @@ module Xlsxrb
|
|
|
102
106
|
cells.each do |cell|
|
|
103
107
|
if cell.is_a?(Elements::Cell)
|
|
104
108
|
value = cell.value
|
|
105
|
-
|
|
109
|
+
cell_style_id = cell.style_index
|
|
106
110
|
col_ref = cell.ref || "#{column_letter(cell.column_index)}#{row_num_str}"
|
|
107
111
|
col_idx = cell.column_index
|
|
108
112
|
formula = cell.formula
|
|
@@ -110,7 +114,7 @@ module Xlsxrb
|
|
|
110
114
|
cell_type_val = nil
|
|
111
115
|
elsif cell.is_a?(Hash)
|
|
112
116
|
value = cell[:value]
|
|
113
|
-
|
|
117
|
+
cell_style_id = cell[:style_index]
|
|
114
118
|
col_idx = cell[:column_index]
|
|
115
119
|
col_ref = cell[:ref] || (col_idx ? "#{column_letter(col_idx)}#{row_num_str}" : nil)
|
|
116
120
|
formula = cell[:formula]
|
|
@@ -118,7 +122,7 @@ module Xlsxrb
|
|
|
118
122
|
cell_type_val = cell[:type]
|
|
119
123
|
else
|
|
120
124
|
value = cell
|
|
121
|
-
|
|
125
|
+
cell_style_id = nil
|
|
122
126
|
col_idx = nil
|
|
123
127
|
col_ref = nil
|
|
124
128
|
formula = nil
|
|
@@ -127,7 +131,8 @@ module Xlsxrb
|
|
|
127
131
|
end
|
|
128
132
|
|
|
129
133
|
col_style = @column_styles && col_idx ? @column_styles[col_idx] : nil
|
|
130
|
-
style_id
|
|
134
|
+
style_id = cell_style_id || attrs[:style_index] || col_style
|
|
135
|
+
style_id = nil unless style_id.is_a?(Integer)
|
|
131
136
|
|
|
132
137
|
# Fast path for common unstyled cells
|
|
133
138
|
if !formula && !style_id && !cell_type_val
|
|
@@ -136,7 +141,7 @@ module Xlsxrb
|
|
|
136
141
|
buf << '<c r="' << col_ref << '"><v>' << value.to_s << "</v></c>"
|
|
137
142
|
next
|
|
138
143
|
when String
|
|
139
|
-
if
|
|
144
|
+
if sst_index && (idx = sst_index[value])
|
|
140
145
|
buf << '<c r="' << col_ref << '" t="s"><v>' << idx.to_s << "</v></c>"
|
|
141
146
|
next
|
|
142
147
|
end
|
|
@@ -157,17 +162,14 @@ module Xlsxrb
|
|
|
157
162
|
end
|
|
158
163
|
|
|
159
164
|
if value.nil? && formula.nil?
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
if cell_style_id.is_a?(Integer)
|
|
166
|
+
buf << '<c r="' << col_ref << '" s="' << cell_style_id.to_s << '"/>'
|
|
167
|
+
else
|
|
168
|
+
buf << '<c r="' << col_ref << '"/>'
|
|
169
|
+
end
|
|
163
170
|
next
|
|
164
171
|
end
|
|
165
172
|
|
|
166
|
-
if value.is_a?(String) && value.start_with?("=") && !formula
|
|
167
|
-
formula = value
|
|
168
|
-
value = nil
|
|
169
|
-
end
|
|
170
|
-
|
|
171
173
|
xml_val = value
|
|
172
174
|
type = cell_type_val
|
|
173
175
|
formula_type = nil
|
|
@@ -181,22 +183,40 @@ module Xlsxrb
|
|
|
181
183
|
formula_type = formula.type
|
|
182
184
|
formula_ref = formula.ref
|
|
183
185
|
formula_si = formula.shared_index
|
|
184
|
-
xml_val = formula.cached_value.nil? ?
|
|
186
|
+
xml_val = formula.cached_value.nil? ? value : formula.cached_value
|
|
185
187
|
elsif formula.is_a?(Hash)
|
|
186
188
|
formula_expr = formula[:formula] || formula[:expression]
|
|
187
189
|
formula_ca = formula[:calculate_always]
|
|
188
190
|
formula_type = formula[:type]
|
|
189
191
|
formula_ref = formula[:ref]
|
|
190
192
|
formula_si = formula[:si] || formula[:shared_index]
|
|
191
|
-
xml_val =
|
|
193
|
+
xml_val = if formula.key?(:value)
|
|
194
|
+
formula[:value]
|
|
195
|
+
elsif formula.key?(:cached_value)
|
|
196
|
+
formula[:cached_value]
|
|
197
|
+
else
|
|
198
|
+
value
|
|
199
|
+
end
|
|
192
200
|
else
|
|
193
201
|
formula_expr = formula
|
|
194
|
-
xml_val = value
|
|
202
|
+
xml_val = value
|
|
195
203
|
end
|
|
196
|
-
formula_expr = formula_expr[1..] if formula_expr.start_with?("=")
|
|
204
|
+
formula_expr = formula_expr[1..] if formula_expr.is_a?(String) && formula_expr.start_with?("=")
|
|
197
205
|
end
|
|
198
206
|
|
|
199
|
-
if
|
|
207
|
+
if formula_expr
|
|
208
|
+
case xml_val
|
|
209
|
+
when true
|
|
210
|
+
type = "b"
|
|
211
|
+
xml_val = "1"
|
|
212
|
+
when false
|
|
213
|
+
type = "b"
|
|
214
|
+
xml_val = "0"
|
|
215
|
+
when Xlsxrb::Elements::CellError
|
|
216
|
+
type = "e"
|
|
217
|
+
xml_val = xml_val.code
|
|
218
|
+
end
|
|
219
|
+
elsif !type
|
|
200
220
|
case value
|
|
201
221
|
when String
|
|
202
222
|
if sst_index && (idx = sst_index[value])
|
|
@@ -288,13 +308,18 @@ module Xlsxrb
|
|
|
288
308
|
end
|
|
289
309
|
f_attrs << ' ca="1"' if formula_ca
|
|
290
310
|
buf << "><f#{f_attrs}>" << escape_xml(formula_expr) << "</f>"
|
|
291
|
-
if xml_val
|
|
292
|
-
buf << "<v>" << xml_val.to_s << "</v></c>"
|
|
293
|
-
else
|
|
311
|
+
if xml_val.nil?
|
|
294
312
|
buf << "<v>0</v></c>"
|
|
313
|
+
else
|
|
314
|
+
v_str = xml_val.to_s
|
|
315
|
+
v_str = v_str.dup.force_encoding(Encoding::UTF_8) if v_str.encoding == Encoding::BINARY
|
|
316
|
+
v_str = escape_xml(v_str) if type == "str" || xml_val.is_a?(String)
|
|
317
|
+
buf << "<v>" << v_str << "</v></c>"
|
|
295
318
|
end
|
|
296
319
|
else
|
|
297
|
-
|
|
320
|
+
v_str = xml_val.to_s
|
|
321
|
+
v_str = v_str.dup.force_encoding(Encoding::UTF_8) if v_str.encoding == Encoding::BINARY
|
|
322
|
+
buf << "><v>" << v_str << "</v></c>"
|
|
298
323
|
end
|
|
299
324
|
end
|
|
300
325
|
|
|
@@ -316,7 +341,7 @@ module Xlsxrb
|
|
|
316
341
|
buf = @row_buffer ||= String.new(capacity: 65_536)
|
|
317
342
|
|
|
318
343
|
if styles.nil? && attrs.nil? && @column_styles.nil?
|
|
319
|
-
buf <<
|
|
344
|
+
buf << '<row r="' << row_num_str << '">'
|
|
320
345
|
col_index = 0
|
|
321
346
|
max_len = values.length
|
|
322
347
|
while col_index < max_len
|
|
@@ -329,27 +354,26 @@ module Xlsxrb
|
|
|
329
354
|
case value
|
|
330
355
|
when Integer
|
|
331
356
|
val_str = value >= 0 && value < 65_536 ? INTEGER_STRINGS[value] : value.to_s
|
|
332
|
-
buf <<
|
|
357
|
+
buf << '<c r="' << col_ref << row_num_str << '"><v>' << val_str << "</v></c>"
|
|
333
358
|
when Float
|
|
334
|
-
buf <<
|
|
359
|
+
buf << '<c r="' << col_ref << row_num_str << '"><v>' << value.to_s << "</v></c>"
|
|
335
360
|
when String
|
|
336
|
-
|
|
337
|
-
raise ArgumentError, "Cell text length #{value.length} exceeds Excel limit of 32,767 characters" if @strict_excel_mode && value.length > 32_767
|
|
361
|
+
raise ArgumentError, "Cell text length #{value.length} exceeds Excel limit of 32,767 characters" if @strict_excel_mode && value.length > 32_767
|
|
338
362
|
|
|
363
|
+
if sst_index
|
|
339
364
|
idx = (sst_index[value] ||= begin
|
|
340
365
|
sst << value
|
|
341
366
|
sst.size - 1
|
|
342
367
|
end)
|
|
343
368
|
idx_str = idx < 65_536 ? INTEGER_STRINGS[idx] : idx.to_s
|
|
344
|
-
buf <<
|
|
345
|
-
|
|
369
|
+
buf << '<c r="' << col_ref << row_num_str << '" t="s"><v>' << idx_str << "</v></c>"
|
|
370
|
+
else
|
|
371
|
+
buf << '<c r="' << col_ref << row_num_str << '" t="inlineStr"><is><t>' << escape_xml(value) << "</t></is></c>"
|
|
346
372
|
end
|
|
347
|
-
formula_expr = value[1..]
|
|
348
|
-
buf << "<c r=\"#{col_ref}#{row_num_str}\"><f>#{escape_xml(formula_expr)}</f><v>0</v></c>"
|
|
349
373
|
when true
|
|
350
|
-
buf <<
|
|
374
|
+
buf << '<c r="' << col_ref << row_num_str << '" t="b"><v>1</v></c>'
|
|
351
375
|
when false
|
|
352
|
-
buf <<
|
|
376
|
+
buf << '<c r="' << col_ref << row_num_str << '" t="b"><v>0</v></c>'
|
|
353
377
|
when Date
|
|
354
378
|
date_style_id = style_map ? style_map["__xlsxrb_date"] : nil
|
|
355
379
|
buf << if date_style_id
|
|
@@ -380,8 +404,23 @@ module Xlsxrb
|
|
|
380
404
|
end
|
|
381
405
|
end
|
|
382
406
|
f_attrs << ' ca="1"' if value.calculate_always
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
c_type = ""
|
|
408
|
+
case value.cached_value
|
|
409
|
+
when true
|
|
410
|
+
c_type = ' t="b"'
|
|
411
|
+
v_val = "1"
|
|
412
|
+
when false
|
|
413
|
+
c_type = ' t="b"'
|
|
414
|
+
v_val = "0"
|
|
415
|
+
when Xlsxrb::Elements::CellError
|
|
416
|
+
c_type = ' t="e"'
|
|
417
|
+
v_val = value.cached_value.code
|
|
418
|
+
when nil
|
|
419
|
+
v_val = 0
|
|
420
|
+
else
|
|
421
|
+
v_val = value.cached_value.is_a?(String) ? escape_xml(value.cached_value) : value.cached_value
|
|
422
|
+
end
|
|
423
|
+
buf << "<c r=\"#{col_ref}#{row_num_str}\"#{c_type}><f#{f_attrs}>#{escape_xml(formula_expr)}</f><v>#{v_val}</v></c>"
|
|
385
424
|
when Hash
|
|
386
425
|
if value.key?(:formula)
|
|
387
426
|
formula_expr = value[:formula] || value[:expression]
|
|
@@ -399,8 +438,27 @@ module Xlsxrb
|
|
|
399
438
|
end
|
|
400
439
|
end
|
|
401
440
|
f_attrs << ' ca="1"' if value[:calculate_always]
|
|
402
|
-
xml_val = value
|
|
403
|
-
|
|
441
|
+
xml_val = if value.key?(:value) && !value[:value].nil?
|
|
442
|
+
value[:value]
|
|
443
|
+
elsif value.key?(:cached_value) && !value[:cached_value].nil?
|
|
444
|
+
value[:cached_value]
|
|
445
|
+
else
|
|
446
|
+
0
|
|
447
|
+
end
|
|
448
|
+
c_type = ""
|
|
449
|
+
case xml_val
|
|
450
|
+
when true
|
|
451
|
+
c_type = ' t="b"'
|
|
452
|
+
xml_val = "1"
|
|
453
|
+
when false
|
|
454
|
+
c_type = ' t="b"'
|
|
455
|
+
xml_val = "0"
|
|
456
|
+
when Xlsxrb::Elements::CellError
|
|
457
|
+
c_type = ' t="e"'
|
|
458
|
+
xml_val = xml_val.code
|
|
459
|
+
end
|
|
460
|
+
xml_val = escape_xml(xml_val) if xml_val.is_a?(String)
|
|
461
|
+
buf << "<c r=\"#{col_ref}#{row_num_str}\"#{c_type}><f#{f_attrs}>#{escape_xml(formula_expr)}</f><v>#{xml_val}</v></c>"
|
|
404
462
|
end
|
|
405
463
|
when Xlsxrb::Elements::CellError
|
|
406
464
|
buf << "<c r=\"#{col_ref}#{row_num_str}\" t=\"e\"><v>#{value.code}</v></c>"
|
|
@@ -425,12 +483,19 @@ module Xlsxrb
|
|
|
425
483
|
end
|
|
426
484
|
|
|
427
485
|
is_styles_collection = styles && (styles.is_a?(Array) || styles.is_a?(Hash))
|
|
428
|
-
single_style_id =
|
|
429
|
-
|
|
486
|
+
single_style_id = resolve_style_id(styles, style_map) unless is_styles_collection
|
|
487
|
+
|
|
488
|
+
row_style = attrs ? attrs[:style_index] : nil
|
|
489
|
+
row_style_id = resolve_style_id(row_style, style_map)
|
|
430
490
|
|
|
431
491
|
buf << '<row r="' << row_num_str << '"'
|
|
492
|
+
buf << ' s="' << row_style_id.to_s << '" customFormat="1"' if row_style_id.is_a?(Integer)
|
|
432
493
|
if attrs
|
|
433
|
-
|
|
494
|
+
if attrs[:height]
|
|
495
|
+
buf << ' ht="' << attrs[:height].to_s << '" customHeight="1"'
|
|
496
|
+
elsif attrs[:custom_height]
|
|
497
|
+
buf << ' customHeight="1"'
|
|
498
|
+
end
|
|
434
499
|
buf << ' hidden="1"' if attrs[:hidden]
|
|
435
500
|
buf << ' collapsed="1"' if attrs[:collapsed]
|
|
436
501
|
buf << ' outlineLevel="' << attrs[:outline_level].to_s << '"' if attrs[:outline_level]
|
|
@@ -443,40 +508,31 @@ module Xlsxrb
|
|
|
443
508
|
max_len = [max_len, styles_len].max
|
|
444
509
|
end
|
|
445
510
|
|
|
446
|
-
row_style = attrs ? attrs[:style_index] : nil
|
|
447
|
-
row_style_id = row_style && style_map ? (style_map[row_style] || style_map[row_style.to_s] || row_style) : row_style
|
|
448
|
-
|
|
449
511
|
col_index = 0
|
|
450
512
|
while col_index < max_len
|
|
451
513
|
value = col_index < values.length ? values[col_index] : nil
|
|
452
|
-
|
|
514
|
+
cell_style_id = single_style_id
|
|
453
515
|
if is_styles_collection
|
|
454
516
|
style_name = if styles.is_a?(Array)
|
|
455
517
|
col_index < styles.length ? styles[col_index] : nil
|
|
456
518
|
else
|
|
457
519
|
styles[col_index]
|
|
458
520
|
end
|
|
459
|
-
|
|
460
|
-
style_map[style_name] || style_map[style_name.to_s] || style_name
|
|
461
|
-
else
|
|
462
|
-
style_name
|
|
463
|
-
end
|
|
521
|
+
cell_style_id = resolve_style_id(style_name, style_map)
|
|
464
522
|
end
|
|
465
523
|
|
|
466
|
-
if
|
|
524
|
+
if cell_style_id.nil?
|
|
467
525
|
col_style = @column_styles ? @column_styles[col_index] : nil
|
|
468
|
-
col_style_id =
|
|
469
|
-
style_map[col_style] || style_map[col_style.to_s] || col_style
|
|
470
|
-
else
|
|
471
|
-
col_style
|
|
472
|
-
end
|
|
526
|
+
col_style_id = resolve_style_id(col_style, style_map)
|
|
473
527
|
style_id = row_style_id || col_style_id
|
|
528
|
+
else
|
|
529
|
+
style_id = cell_style_id
|
|
474
530
|
end
|
|
475
531
|
|
|
476
532
|
col_ref = COLUMN_LETTERS[col_index] || column_letter(col_index)
|
|
477
533
|
|
|
478
534
|
if value.nil?
|
|
479
|
-
buf << '<c r="' << col_ref << row_num_str << '" s="' <<
|
|
535
|
+
buf << '<c r="' << col_ref << row_num_str << '" s="' << cell_style_id.to_s << '"/>' if cell_style_id
|
|
480
536
|
col_index += 1
|
|
481
537
|
next
|
|
482
538
|
end
|
|
@@ -494,7 +550,7 @@ module Xlsxrb
|
|
|
494
550
|
col_index += 1
|
|
495
551
|
next
|
|
496
552
|
when String
|
|
497
|
-
|
|
553
|
+
if sst_index
|
|
498
554
|
idx = sst_index[value]
|
|
499
555
|
unless idx
|
|
500
556
|
sst << value
|
|
@@ -503,9 +559,11 @@ module Xlsxrb
|
|
|
503
559
|
end
|
|
504
560
|
idx_str = INTEGER_STRINGS[idx] || idx.to_s
|
|
505
561
|
buf << '<c r="' << col_ref << row_num_str << '" t="s"><v>' << idx_str << "</v></c>"
|
|
506
|
-
|
|
507
|
-
|
|
562
|
+
else
|
|
563
|
+
buf << '<c r="' << col_ref << row_num_str << '" t="inlineStr"><is><t>' << escape_xml(value) << "</t></is></c>"
|
|
508
564
|
end
|
|
565
|
+
col_index += 1
|
|
566
|
+
next
|
|
509
567
|
when true
|
|
510
568
|
buf << '<c r="' << col_ref << row_num_str << '" t="b"><v>1</v></c>'
|
|
511
569
|
col_index += 1
|
|
@@ -551,6 +609,9 @@ module Xlsxrb
|
|
|
551
609
|
when false
|
|
552
610
|
type = "b"
|
|
553
611
|
xml_val = "0"
|
|
612
|
+
when Xlsxrb::Elements::CellError
|
|
613
|
+
type = "e"
|
|
614
|
+
xml_val = value.cached_value.code
|
|
554
615
|
end
|
|
555
616
|
when Hash
|
|
556
617
|
if value.key?(:formula)
|
|
@@ -559,7 +620,11 @@ module Xlsxrb
|
|
|
559
620
|
formula_type = value[:type]
|
|
560
621
|
formula_ref = value[:ref]
|
|
561
622
|
formula_si = value[:si] || value[:shared_index]
|
|
562
|
-
xml_val = value
|
|
623
|
+
xml_val = if value.key?(:value)
|
|
624
|
+
value[:value]
|
|
625
|
+
elsif value.key?(:cached_value)
|
|
626
|
+
value[:cached_value]
|
|
627
|
+
end
|
|
563
628
|
case xml_val
|
|
564
629
|
when String
|
|
565
630
|
type = "str"
|
|
@@ -569,26 +634,12 @@ module Xlsxrb
|
|
|
569
634
|
when false
|
|
570
635
|
type = "b"
|
|
571
636
|
xml_val = "0"
|
|
637
|
+
when Xlsxrb::Elements::CellError
|
|
638
|
+
type = "e"
|
|
639
|
+
xml_val = xml_val.code
|
|
572
640
|
end
|
|
573
641
|
end
|
|
574
|
-
when String
|
|
575
|
-
if value.start_with?("=") && value.length > 1
|
|
576
|
-
formula_expr = value
|
|
577
|
-
xml_val = nil
|
|
578
|
-
elsif sst_index
|
|
579
|
-
idx = sst_index[value]
|
|
580
|
-
unless idx
|
|
581
|
-
sst << value
|
|
582
|
-
idx = sst.size - 1
|
|
583
|
-
sst_index[value] = idx
|
|
584
|
-
end
|
|
585
|
-
xml_val = idx
|
|
586
|
-
type = "s"
|
|
587
|
-
else
|
|
588
|
-
xml_val = value
|
|
589
|
-
type = "inlineStr"
|
|
590
|
-
end
|
|
591
|
-
when Xlsxrb::Elements::RichText
|
|
642
|
+
when String, Xlsxrb::Elements::RichText
|
|
592
643
|
if sst_index
|
|
593
644
|
idx = sst_index[value]
|
|
594
645
|
unless idx
|
|
@@ -638,11 +689,13 @@ module Xlsxrb
|
|
|
638
689
|
end
|
|
639
690
|
end
|
|
640
691
|
f_attrs << ' ca="1"' if formula_ca
|
|
641
|
-
buf << "
|
|
642
|
-
if xml_val
|
|
643
|
-
buf << "<v>" << xml_val.to_s << "</v></c>"
|
|
644
|
-
else
|
|
692
|
+
buf << "><f#{f_attrs}>" << escape_xml(formula_expr) << "</f>"
|
|
693
|
+
if xml_val.nil?
|
|
645
694
|
buf << "<v>0</v></c>"
|
|
695
|
+
else
|
|
696
|
+
v_str = xml_val.to_s
|
|
697
|
+
v_str = escape_xml(v_str) if type == "str" || xml_val.is_a?(String)
|
|
698
|
+
buf << "<v>" << v_str << "</v></c>"
|
|
646
699
|
end
|
|
647
700
|
elsif type == "inlineStr"
|
|
648
701
|
buf << "><is><t>" << escape_xml(xml_val.to_s) << "</t></is></c>"
|
|
@@ -662,12 +715,12 @@ module Xlsxrb
|
|
|
662
715
|
|
|
663
716
|
# Write the worksheet footer. Call once after all rows.
|
|
664
717
|
# Options for post-sheetData elements (in OOXML order):
|
|
665
|
-
def finish(drawing_rid: nil, sheet_protection: nil, auto_filter: nil,
|
|
718
|
+
def finish(drawing_rid: nil, sheet_protection: nil, protected_ranges: nil, auto_filter: nil,
|
|
666
719
|
filter_columns: nil, sort_state: nil, merge_cells: nil,
|
|
667
720
|
conditional_formats: nil, data_validations: nil,
|
|
668
721
|
hyperlinks: nil, print_options: nil, page_margins: nil,
|
|
669
722
|
page_setup: nil, header_footer: nil, row_breaks: nil,
|
|
670
|
-
col_breaks: nil, tables: nil, table_start_rid: nil,
|
|
723
|
+
col_breaks: nil, cell_watches: nil, tables: nil, table_start_rid: nil,
|
|
671
724
|
legacy_drawing_rid: nil, sparkline_groups: nil)
|
|
672
725
|
return if @finished
|
|
673
726
|
|
|
@@ -681,6 +734,7 @@ module Xlsxrb
|
|
|
681
734
|
|
|
682
735
|
# Elements must appear in OOXML specification order after sheetData
|
|
683
736
|
write_sheet_protection(sheet_protection) if sheet_protection
|
|
737
|
+
write_protected_ranges(protected_ranges) if protected_ranges && !protected_ranges.empty?
|
|
684
738
|
write_auto_filter(auto_filter, filter_columns, sort_state) if auto_filter
|
|
685
739
|
write_merge_cells(merge_cells) if merge_cells && !merge_cells.empty?
|
|
686
740
|
write_conditional_formatting(conditional_formats) if conditional_formats && !conditional_formats.empty?
|
|
@@ -692,6 +746,7 @@ module Xlsxrb
|
|
|
692
746
|
write_header_footer(header_footer) if header_footer && !header_footer.empty?
|
|
693
747
|
write_row_breaks(row_breaks) if row_breaks && !row_breaks.empty?
|
|
694
748
|
write_col_breaks(col_breaks) if col_breaks && !col_breaks.empty?
|
|
749
|
+
write_cell_watches(cell_watches) if cell_watches && !cell_watches.empty?
|
|
695
750
|
@builder.empty_tag("drawing", { "r:id": drawing_rid }) if drawing_rid
|
|
696
751
|
@builder.empty_tag("legacyDrawing", { "r:id": legacy_drawing_rid }) if legacy_drawing_rid
|
|
697
752
|
write_table_parts(tables, table_start_rid) if tables && !tables.empty?
|
|
@@ -701,6 +756,17 @@ module Xlsxrb
|
|
|
701
756
|
|
|
702
757
|
private
|
|
703
758
|
|
|
759
|
+
def resolve_style_id(style, style_map)
|
|
760
|
+
return nil if style.nil?
|
|
761
|
+
|
|
762
|
+
if style_map
|
|
763
|
+
mapped = style_map[style] || style_map[style.to_s]
|
|
764
|
+
return mapped if mapped
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
style.is_a?(Integer) ? style : nil
|
|
768
|
+
end
|
|
769
|
+
|
|
704
770
|
# --- Pre-sheetData elements ---
|
|
705
771
|
|
|
706
772
|
def write_sheet_properties(props)
|
|
@@ -727,12 +793,26 @@ module Xlsxrb
|
|
|
727
793
|
|
|
728
794
|
def write_sheet_views(freeze_pane: nil, split_pane: nil, selection: nil, sheet_view: nil)
|
|
729
795
|
@builder.open_tag("sheetViews")
|
|
730
|
-
sv_attrs = {
|
|
796
|
+
sv_attrs = { workbookViewId: "0" }
|
|
731
797
|
if sheet_view
|
|
732
|
-
sv_attrs[:
|
|
733
|
-
sv_attrs[:
|
|
734
|
-
sv_attrs[:
|
|
798
|
+
sv_attrs[:windowProtection] = sheet_view[:window_protection] ? "1" : "0" unless sheet_view[:window_protection].nil?
|
|
799
|
+
sv_attrs[:showFormulas] = sheet_view[:show_formulas] ? "1" : "0" unless sheet_view[:show_formulas].nil?
|
|
800
|
+
sv_attrs[:showGridLines] = sheet_view[:show_grid_lines] ? "1" : "0" unless sheet_view[:show_grid_lines].nil?
|
|
801
|
+
sv_attrs[:showRowColHeaders] = sheet_view[:show_row_col_headers] ? "1" : "0" unless sheet_view[:show_row_col_headers].nil?
|
|
802
|
+
sv_attrs[:showZeros] = sheet_view[:show_zeros] ? "1" : "0" unless sheet_view[:show_zeros].nil?
|
|
803
|
+
sv_attrs[:rightToLeft] = sheet_view[:right_to_left] ? "1" : "0" unless sheet_view[:right_to_left].nil?
|
|
804
|
+
sv_attrs[:tabSelected] = "1" if sheet_view[:tab_selected]
|
|
805
|
+
sv_attrs[:showRuler] = sheet_view[:show_ruler] ? "1" : "0" unless sheet_view[:show_ruler].nil?
|
|
806
|
+
sv_attrs[:showOutlineSymbols] = sheet_view[:show_outline_symbols] ? "1" : "0" unless sheet_view[:show_outline_symbols].nil?
|
|
807
|
+
sv_attrs[:defaultGridColor] = sheet_view[:default_grid_color] ? "1" : "0" unless sheet_view[:default_grid_color].nil?
|
|
808
|
+
sv_attrs[:showWhiteSpace] = sheet_view[:show_white_space] ? "1" : "0" unless sheet_view[:show_white_space].nil?
|
|
809
|
+
sv_attrs[:view] = sheet_view[:view].to_s if sheet_view[:view]
|
|
810
|
+
sv_attrs[:topLeftCell] = sheet_view[:top_left_cell].to_s if sheet_view[:top_left_cell]
|
|
811
|
+
sv_attrs[:colorId] = sheet_view[:color_id].to_s if sheet_view[:color_id]
|
|
735
812
|
sv_attrs[:zoomScale] = sheet_view[:zoom_scale].to_s if sheet_view[:zoom_scale]
|
|
813
|
+
sv_attrs[:zoomScaleNormal] = sheet_view[:zoom_scale_normal].to_s if sheet_view[:zoom_scale_normal]
|
|
814
|
+
sv_attrs[:zoomScaleSheetLayoutView] = sheet_view[:zoom_scale_sheet_layout_view].to_s if sheet_view[:zoom_scale_sheet_layout_view]
|
|
815
|
+
sv_attrs[:zoomScalePageLayoutView] = sheet_view[:zoom_scale_page_layout_view].to_s if sheet_view[:zoom_scale_page_layout_view]
|
|
736
816
|
end
|
|
737
817
|
@builder.open_tag("sheetView", sv_attrs)
|
|
738
818
|
if freeze_pane
|
|
@@ -741,35 +821,59 @@ module Xlsxrb
|
|
|
741
821
|
pane_attrs[:ySplit] = freeze_pane[:row].to_s if freeze_pane[:row]&.positive?
|
|
742
822
|
top_left_col = column_letter(freeze_pane[:col] || 0)
|
|
743
823
|
top_left_row = (freeze_pane[:row] || 0) + 1
|
|
744
|
-
pane_attrs[:topLeftCell] = "#{top_left_col}#{top_left_row}"
|
|
824
|
+
pane_attrs[:topLeftCell] = freeze_pane[:top_left_cell] || "#{top_left_col}#{top_left_row}"
|
|
745
825
|
pane_attrs[:state] = "frozen"
|
|
746
826
|
# Determine active pane
|
|
747
|
-
pane_attrs[:activePane] = if (freeze_pane[:col] || 0).positive? && (freeze_pane[:row] || 0).positive?
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
827
|
+
pane_attrs[:activePane] = freeze_pane[:active_pane] || if (freeze_pane[:col] || 0).positive? && (freeze_pane[:row] || 0).positive?
|
|
828
|
+
"bottomRight"
|
|
829
|
+
elsif (freeze_pane[:col] || 0).positive?
|
|
830
|
+
"topRight"
|
|
831
|
+
else
|
|
832
|
+
"bottomLeft"
|
|
833
|
+
end
|
|
754
834
|
@builder.empty_tag("pane", pane_attrs)
|
|
755
835
|
elsif split_pane
|
|
756
836
|
pane_attrs = {}
|
|
757
837
|
pane_attrs[:xSplit] = split_pane[:x_split].to_s if split_pane[:x_split]&.positive?
|
|
758
838
|
pane_attrs[:ySplit] = split_pane[:y_split].to_s if split_pane[:y_split]&.positive?
|
|
759
839
|
pane_attrs[:topLeftCell] = split_pane[:top_left_cell] if split_pane[:top_left_cell]
|
|
840
|
+
pane_attrs[:activePane] = split_pane[:active_pane] if split_pane[:active_pane]
|
|
841
|
+
pane_attrs[:state] = split_pane[:state].to_s if split_pane[:state]
|
|
760
842
|
@builder.empty_tag("pane", pane_attrs)
|
|
761
843
|
end
|
|
762
|
-
if
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
844
|
+
active_selection = selection || (sheet_view[:selection] if sheet_view)
|
|
845
|
+
if active_selection
|
|
846
|
+
selections = active_selection.is_a?(Array) ? active_selection : [active_selection]
|
|
847
|
+
selections.each do |sel|
|
|
848
|
+
sel_attrs = {}
|
|
849
|
+
sel_attrs[:pane] = sel[:pane].to_s if sel[:pane]
|
|
850
|
+
sel_attrs[:activeCell] = sel[:active_cell].to_s if sel[:active_cell]
|
|
851
|
+
sel_attrs[:activeCellId] = sel[:active_cell_id].to_s if sel[:active_cell_id]
|
|
852
|
+
sqref = sel[:sqref] || sel[:active_cell]
|
|
853
|
+
sel_attrs[:sqref] = sqref.to_s if sqref
|
|
854
|
+
@builder.empty_tag("selection", sel_attrs) unless sel_attrs.empty?
|
|
855
|
+
end
|
|
768
856
|
end
|
|
769
857
|
@builder.close_tag("sheetView")
|
|
770
858
|
@builder.close_tag("sheetViews")
|
|
771
859
|
end
|
|
772
860
|
|
|
861
|
+
def write_sheet_format_pr(fmt)
|
|
862
|
+
return if fmt.nil? || fmt.empty?
|
|
863
|
+
|
|
864
|
+
attrs = {}
|
|
865
|
+
attrs[:defaultRowHeight] = fmt[:default_row_height].to_s if fmt[:default_row_height]
|
|
866
|
+
attrs[:defaultColWidth] = fmt[:default_col_width].to_s if fmt[:default_col_width]
|
|
867
|
+
attrs[:baseColWidth] = fmt[:base_col_width].to_s if fmt[:base_col_width]
|
|
868
|
+
attrs[:outlineLevelRow] = fmt[:outline_level_row].to_s if fmt[:outline_level_row]
|
|
869
|
+
attrs[:outlineLevelCol] = fmt[:outline_level_col].to_s if fmt[:outline_level_col]
|
|
870
|
+
attrs[:customHeight] = "1" if fmt[:custom_height]
|
|
871
|
+
attrs[:zeroHeight] = "1" if fmt[:zero_height]
|
|
872
|
+
attrs[:thickTop] = "1" if fmt[:thick_top]
|
|
873
|
+
attrs[:thickBottom] = "1" if fmt[:thick_bottom]
|
|
874
|
+
@builder.empty_tag("sheetFormatPr", attrs) unless attrs.empty?
|
|
875
|
+
end
|
|
876
|
+
|
|
773
877
|
# --- Post-sheetData elements (in OOXML order) ---
|
|
774
878
|
|
|
775
879
|
def write_sheet_protection(opts)
|
|
@@ -798,6 +902,28 @@ module Xlsxrb
|
|
|
798
902
|
@builder.empty_tag("sheetProtection", attrs)
|
|
799
903
|
end
|
|
800
904
|
|
|
905
|
+
def write_protected_ranges(ranges)
|
|
906
|
+
@builder.open_tag("protectedRanges")
|
|
907
|
+
ranges.each do |pr|
|
|
908
|
+
attrs = { sqref: pr[:sqref].to_s, name: pr[:name].to_s }
|
|
909
|
+
attrs[:password] = pr[:password].to_s if pr[:password]
|
|
910
|
+
attrs[:algorithmName] = pr[:algorithm_name].to_s if pr[:algorithm_name]
|
|
911
|
+
attrs[:hashValue] = pr[:hash_value].to_s if pr[:hash_value]
|
|
912
|
+
attrs[:saltValue] = pr[:salt_value].to_s if pr[:salt_value]
|
|
913
|
+
attrs[:spinCount] = pr[:spin_count].to_s if pr[:spin_count]
|
|
914
|
+
attrs[:securityDescriptor] = pr[:security_descriptor].to_s if pr[:security_descriptor]
|
|
915
|
+
sds = pr[:security_descriptors] || []
|
|
916
|
+
if sds.empty?
|
|
917
|
+
@builder.empty_tag("protectedRange", attrs)
|
|
918
|
+
else
|
|
919
|
+
@builder.open_tag("protectedRange", attrs)
|
|
920
|
+
sds.each { |sd| @builder.tag("securityDescriptor") { @builder.text(sd.to_s) } }
|
|
921
|
+
@builder.close_tag("protectedRange")
|
|
922
|
+
end
|
|
923
|
+
end
|
|
924
|
+
@builder.close_tag("protectedRanges")
|
|
925
|
+
end
|
|
926
|
+
|
|
801
927
|
def write_auto_filter(range, filter_columns, sort_state)
|
|
802
928
|
if (filter_columns && !filter_columns.empty?) || sort_state
|
|
803
929
|
@builder.open_tag("autoFilter", { ref: range })
|
|
@@ -1026,6 +1152,9 @@ module Xlsxrb
|
|
|
1026
1152
|
end
|
|
1027
1153
|
|
|
1028
1154
|
def write_data_validations(validations)
|
|
1155
|
+
validations = validations.reject { |dv| dv[:sqref].nil? || dv[:sqref].to_s.empty? }
|
|
1156
|
+
return if validations.empty?
|
|
1157
|
+
|
|
1029
1158
|
@builder.open_tag("dataValidations", { count: validations.size.to_s })
|
|
1030
1159
|
validations.each do |dv|
|
|
1031
1160
|
if dv[:in].is_a?(Array)
|
|
@@ -1045,6 +1174,8 @@ module Xlsxrb
|
|
|
1045
1174
|
dv_attrs[:error] = dv[:error] if dv[:error]
|
|
1046
1175
|
dv_attrs[:promptTitle] = dv[:prompt_title] if dv[:prompt_title]
|
|
1047
1176
|
dv_attrs[:prompt] = dv[:prompt] if dv[:prompt]
|
|
1177
|
+
dv_attrs[:showDropDown] = "1" if dv[:show_drop_down]
|
|
1178
|
+
dv_attrs[:imeMode] = dv[:ime_mode].to_s if dv[:ime_mode]
|
|
1048
1179
|
|
|
1049
1180
|
has_formulas = dv[:formula1] || dv[:formula2]
|
|
1050
1181
|
if has_formulas
|
|
@@ -1101,7 +1232,7 @@ module Xlsxrb
|
|
|
1101
1232
|
attrs[:scale] = opts[:scale].to_s if opts[:scale]
|
|
1102
1233
|
attrs[:fitToWidth] = opts[:fit_to_width].to_s if opts[:fit_to_width]
|
|
1103
1234
|
attrs[:fitToHeight] = opts[:fit_to_height].to_s if opts[:fit_to_height]
|
|
1104
|
-
attrs[:firstPageNumber] = opts[:first_page_number].to_s if opts[:first_page_number]
|
|
1235
|
+
attrs[:firstPageNumber] = opts[:first_page_number].to_s if opts[:first_page_number] && opts[:first_page_number].to_i >= 0
|
|
1105
1236
|
attrs[:pageOrder] = opts[:page_order].to_s if opts[:page_order]
|
|
1106
1237
|
attrs[:blackAndWhite] = "1" if opts[:black_and_white]
|
|
1107
1238
|
attrs[:draft] = "1" if opts[:draft]
|
|
@@ -1120,7 +1251,14 @@ module Xlsxrb
|
|
|
1120
1251
|
def write_row_breaks(breaks)
|
|
1121
1252
|
@builder.open_tag("rowBreaks", { count: breaks.size.to_s, manualBreakCount: breaks.size.to_s })
|
|
1122
1253
|
breaks.each do |brk|
|
|
1123
|
-
|
|
1254
|
+
attrs = if brk.is_a?(Hash)
|
|
1255
|
+
a = { id: (brk[:id] || brk[:row] || 0).to_s, max: (brk[:max] || 16_383).to_s, man: brk.fetch(:man, true) ? "1" : "0" }
|
|
1256
|
+
a[:min] = brk[:min].to_s if brk[:min]
|
|
1257
|
+
a
|
|
1258
|
+
else
|
|
1259
|
+
{ id: brk.to_s, max: "16383", man: "1" }
|
|
1260
|
+
end
|
|
1261
|
+
@builder.empty_tag("brk", attrs)
|
|
1124
1262
|
end
|
|
1125
1263
|
@builder.close_tag("rowBreaks")
|
|
1126
1264
|
end
|
|
@@ -1128,11 +1266,26 @@ module Xlsxrb
|
|
|
1128
1266
|
def write_col_breaks(breaks)
|
|
1129
1267
|
@builder.open_tag("colBreaks", { count: breaks.size.to_s, manualBreakCount: breaks.size.to_s })
|
|
1130
1268
|
breaks.each do |brk|
|
|
1131
|
-
|
|
1269
|
+
attrs = if brk.is_a?(Hash)
|
|
1270
|
+
a = { id: (brk[:id] || brk[:col] || 0).to_s, max: (brk[:max] || 1_048_575).to_s, man: brk.fetch(:man, true) ? "1" : "0" }
|
|
1271
|
+
a[:min] = brk[:min].to_s if brk[:min]
|
|
1272
|
+
a
|
|
1273
|
+
else
|
|
1274
|
+
{ id: brk.to_s, max: "1048575", man: "1" }
|
|
1275
|
+
end
|
|
1276
|
+
@builder.empty_tag("brk", attrs)
|
|
1132
1277
|
end
|
|
1133
1278
|
@builder.close_tag("colBreaks")
|
|
1134
1279
|
end
|
|
1135
1280
|
|
|
1281
|
+
def write_cell_watches(watches)
|
|
1282
|
+
@builder.open_tag("cellWatches")
|
|
1283
|
+
watches.each do |w|
|
|
1284
|
+
@builder.empty_tag("cellWatch", { r: w.to_s })
|
|
1285
|
+
end
|
|
1286
|
+
@builder.close_tag("cellWatches")
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1136
1289
|
def write_table_parts(tables, start_rid)
|
|
1137
1290
|
@builder.open_tag("tableParts", { count: tables.size.to_s })
|
|
1138
1291
|
tables.each_with_index do |_tbl, idx|
|
|
@@ -1216,16 +1369,32 @@ module Xlsxrb
|
|
|
1216
1369
|
def write_columns(columns)
|
|
1217
1370
|
@builder.open_tag("cols")
|
|
1218
1371
|
columns.each do |col|
|
|
1372
|
+
if col.is_a?(Elements::Column)
|
|
1373
|
+
min = col.unmapped_data[:min] || (col.index + 1)
|
|
1374
|
+
max = col.unmapped_data[:max] || (col.index + 1)
|
|
1375
|
+
width = col.width
|
|
1376
|
+
hidden = col.hidden
|
|
1377
|
+
custom_width = col.custom_width || col.width
|
|
1378
|
+
outline_level = col.outline_level
|
|
1379
|
+
style_id = col.style_index
|
|
1380
|
+
else
|
|
1381
|
+
min = col[:min] || ((col[:index] || 0) + 1)
|
|
1382
|
+
max = col[:max] || ((col[:index] || 0) + 1)
|
|
1383
|
+
width = col[:width]
|
|
1384
|
+
hidden = col[:hidden]
|
|
1385
|
+
custom_width = col[:custom_width] || col[:width]
|
|
1386
|
+
outline_level = col[:outline_level]
|
|
1387
|
+
style_id = col[:style_index] || col[:style]
|
|
1388
|
+
end
|
|
1219
1389
|
attrs = {
|
|
1220
|
-
min:
|
|
1221
|
-
max:
|
|
1390
|
+
min: min.to_s,
|
|
1391
|
+
max: max.to_s
|
|
1222
1392
|
}
|
|
1223
|
-
attrs[:width] =
|
|
1224
|
-
attrs[:hidden] = "1" if
|
|
1225
|
-
attrs[:customWidth] = "1" if
|
|
1226
|
-
attrs[:outlineLevel] =
|
|
1227
|
-
|
|
1228
|
-
attrs[:style] = style_id.to_s if style_id
|
|
1393
|
+
attrs[:width] = width.to_s if width
|
|
1394
|
+
attrs[:hidden] = "1" if hidden
|
|
1395
|
+
attrs[:customWidth] = "1" if custom_width
|
|
1396
|
+
attrs[:outlineLevel] = outline_level.to_s if outline_level
|
|
1397
|
+
attrs[:style] = style_id.to_s if style_id.is_a?(Integer)
|
|
1229
1398
|
@builder.empty_tag("col", attrs)
|
|
1230
1399
|
end
|
|
1231
1400
|
@builder.close_tag("cols")
|
|
@@ -1237,15 +1406,27 @@ module Xlsxrb
|
|
|
1237
1406
|
|
|
1238
1407
|
value = cell[:value]
|
|
1239
1408
|
formula = cell[:formula]
|
|
1240
|
-
# For formula cells with cached values,
|
|
1241
|
-
type =
|
|
1409
|
+
# For formula cells with cached values, infer type for booleans and strings
|
|
1410
|
+
type = cell[:type] || if formula
|
|
1411
|
+
case value
|
|
1412
|
+
when true, false then "b"
|
|
1413
|
+
end
|
|
1414
|
+
else
|
|
1415
|
+
cell_type(value)
|
|
1416
|
+
end
|
|
1242
1417
|
attrs[:t] = type if type
|
|
1243
1418
|
col_idx = cell[:column_index]
|
|
1244
1419
|
col_style = @column_styles && col_idx ? @column_styles[col_idx] : nil
|
|
1245
|
-
|
|
1246
|
-
|
|
1420
|
+
cell_style_id = cell[:style_index]
|
|
1421
|
+
style_id = cell_style_id || col_style
|
|
1422
|
+
attrs[:s] = style_id.to_s if style_id.is_a?(Integer)
|
|
1247
1423
|
|
|
1248
1424
|
if value.nil? && formula.nil?
|
|
1425
|
+
if cell_style_id.is_a?(Integer)
|
|
1426
|
+
attrs[:s] = cell_style_id.to_s
|
|
1427
|
+
else
|
|
1428
|
+
attrs.delete(:s)
|
|
1429
|
+
end
|
|
1249
1430
|
@builder.empty_tag("c", attrs)
|
|
1250
1431
|
return
|
|
1251
1432
|
end
|
|
@@ -1317,6 +1498,7 @@ module Xlsxrb
|
|
|
1317
1498
|
|
|
1318
1499
|
def escape_xml(value)
|
|
1319
1500
|
str = value.to_s
|
|
1501
|
+
str = str.dup.force_encoding(Encoding::UTF_8) if str.encoding == Encoding::BINARY
|
|
1320
1502
|
str = str.gsub(INVALID_XML_CHARS_RE, "") if str.match?(INVALID_XML_CHARS_RE)
|
|
1321
1503
|
str.match?(ESCAPE_RE) ? str.gsub(ESCAPE_RE, ESCAPE_MAP) : str
|
|
1322
1504
|
end
|