xlsxrb 0.1.5 → 0.1.6
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/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +9 -0
- data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +2332 -0
- data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +4 -0
- data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +9 -0
- data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +8 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +42 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +5 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +47 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +30 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +9 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +7 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +19 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +31 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +18 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +12 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +11 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +13 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +95 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +31 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +58 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +24 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +17 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +23 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +22 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +30 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +20 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +15 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +26 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +21 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +131 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +14 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +33 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +21 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +63 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +55 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +35 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +7 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +10 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +22 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +29 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +17 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +13 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +10 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +8 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +6 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +30 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +10 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +10 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +5 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +15 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +3 -0
- data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +40 -0
- data/CHANGELOG.md +18 -0
- data/README.md +39 -32
- data/Rakefile +2 -1
- data/Steepfile +0 -2
- data/benchmark.rb +376 -0
- data/docs/ARCHITECTURE.md +12 -5
- data/docs/wasm/ruby.wasm +0 -0
- data/lib/xlsxrb/elements/cell.rb +39 -7
- data/lib/xlsxrb/elements/row.rb +17 -4
- data/lib/xlsxrb/elements/types.rb +4 -0
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +104 -40
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +144 -23
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +160 -145
- data/lib/xlsxrb/ooxml/xml_builder.rb +16 -10
- data/lib/xlsxrb/ooxml/zip_reader.rb +104 -18
- data/lib/xlsxrb/ooxml/zip_writer.rb +63 -25
- data/lib/xlsxrb/style_builder.rb +158 -30
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb.rb +443 -236
- data/rbs_collection.lock.yaml +28 -0
- data/sig/generated/xlsxrb/elements/cell.rbs +30 -0
- data/sig/generated/xlsxrb/elements/column.rbs +30 -0
- data/sig/generated/xlsxrb/elements/row.rbs +32 -0
- data/sig/generated/xlsxrb/elements/types.rbs +74 -0
- data/sig/generated/xlsxrb/elements/workbook.rbs +25 -0
- data/sig/generated/xlsxrb/elements/worksheet.rbs +27 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +1412 -0
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +21 -0
- data/sig/generated/xlsxrb/ooxml/styles_parser.rbs +44 -0
- data/sig/generated/xlsxrb/ooxml/utils.rbs +40 -0
- data/sig/generated/xlsxrb/ooxml/workbook_parser.rbs +46 -0
- data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +66 -0
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +67 -0
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +92 -0
- data/sig/generated/xlsxrb/ooxml/writer.rbs +880 -0
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +45 -0
- data/sig/generated/xlsxrb/ooxml/xml_parser.rbs +30 -0
- data/sig/generated/xlsxrb/ooxml/zip_generator.rbs +38 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +45 -0
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +45 -0
- data/sig/generated/xlsxrb/style_builder.rbs +212 -50
- data/sig/generated/xlsxrb.rbs +1276 -0
- data/sig/rexml.rbs +4 -0
- metadata +76 -1
|
@@ -9,13 +9,20 @@ module Xlsxrb
|
|
|
9
9
|
# SAX-based parser for xl/sharedStrings.xml.
|
|
10
10
|
# Returns an Array of strings (index = SST index).
|
|
11
11
|
class SharedStringsParser
|
|
12
|
+
XML_ENTITIES = { "&" => "&", "<" => "<", ">" => ">", """ => '"', "'" => "'" }.freeze
|
|
13
|
+
|
|
12
14
|
# Parses all shared strings and returns an Array of strings.
|
|
13
|
-
def self.parse(xml_string,
|
|
15
|
+
def self.parse(xml_string, _part_name: "xl/sharedStrings.xml")
|
|
14
16
|
return [] if xml_string.nil? || xml_string.empty?
|
|
15
17
|
|
|
18
|
+
if xml_string.include?("<!DOCTYPE") || xml_string.include?("<!ENTITY")
|
|
19
|
+
listener = Class.new { include REXML::SAX2Listener }.new
|
|
20
|
+
XmlParser.parse(xml_string, listener)
|
|
21
|
+
end
|
|
22
|
+
|
|
16
23
|
strings = []
|
|
17
|
-
|
|
18
|
-
strings <<
|
|
24
|
+
fast_scan(xml_string) do |str|
|
|
25
|
+
strings << str
|
|
19
26
|
end
|
|
20
27
|
strings
|
|
21
28
|
end
|
|
@@ -25,53 +32,110 @@ module Xlsxrb
|
|
|
25
32
|
return enum_for(:each_event, xml_string, part_name: part_name) unless block
|
|
26
33
|
return if xml_string.nil? || xml_string.empty?
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
index = 0
|
|
36
|
+
fast_scan(xml_string) do |str|
|
|
37
|
+
block.call(Event.new(
|
|
38
|
+
type: :sst_item,
|
|
39
|
+
args: [str],
|
|
40
|
+
source: { part: part_name, index: index }
|
|
41
|
+
))
|
|
42
|
+
index += 1
|
|
43
|
+
end
|
|
30
44
|
end
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def initialize(part_name, &block)
|
|
37
|
-
@part_name = part_name
|
|
38
|
-
@block = block
|
|
39
|
-
@in_si = false
|
|
40
|
-
@in_t = false
|
|
41
|
-
@current_text = +""
|
|
42
|
-
@index = 0
|
|
43
|
-
end
|
|
46
|
+
def self.fast_scan(xml_src)
|
|
47
|
+
xml = xml_src.b
|
|
48
|
+
sst_start = xml.index("<sst")
|
|
49
|
+
return unless sst_start
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
sst_open_end = xml.index(">", sst_start)
|
|
52
|
+
return unless sst_open_end
|
|
53
|
+
return if xml.getbyte(sst_open_end - 1) == 47 # self-closing <sst/>
|
|
54
|
+
|
|
55
|
+
sst_end = xml.index("</sst>", sst_open_end)
|
|
56
|
+
sst_end ||= xml.bytesize
|
|
57
|
+
|
|
58
|
+
pos = sst_open_end + 1
|
|
59
|
+
while pos < sst_end
|
|
60
|
+
si_start = xml.index("<si", pos)
|
|
61
|
+
break unless si_start && si_start < sst_end
|
|
62
|
+
|
|
63
|
+
si_open_end = xml.index(">", si_start + 3)
|
|
64
|
+
break unless si_open_end
|
|
65
|
+
|
|
66
|
+
# Self-closing <si/>
|
|
67
|
+
if xml.getbyte(si_open_end - 1) == 47
|
|
68
|
+
yield ""
|
|
69
|
+
pos = si_open_end + 1
|
|
70
|
+
next
|
|
52
71
|
end
|
|
53
|
-
end
|
|
54
72
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
si_end = xml.index("</si>", si_open_end + 1)
|
|
74
|
+
break unless si_end
|
|
75
|
+
|
|
76
|
+
# Fast path: check for simple single <t>...</t> inside <si>
|
|
77
|
+
first_t = xml.index("<t", si_open_end + 1)
|
|
78
|
+
if first_t && first_t < si_end
|
|
79
|
+
t_open_end = xml.index(">", first_t + 2)
|
|
80
|
+
if t_open_end && t_open_end < si_end
|
|
81
|
+
if xml.getbyte(t_open_end - 1) == 47 # <t/>
|
|
82
|
+
str = ""
|
|
83
|
+
else
|
|
84
|
+
t_end = xml.index("</t>", t_open_end + 1)
|
|
85
|
+
if t_end && t_end <= si_end
|
|
86
|
+
next_t = xml.index("<t", t_end + 4)
|
|
87
|
+
if next_t && next_t < si_end
|
|
88
|
+
# Multiple <t> tags (rich text runs)
|
|
89
|
+
str = extract_multi_t(xml, si_open_end + 1, si_end)
|
|
90
|
+
else
|
|
91
|
+
raw_str = xml.byteslice(t_open_end + 1, t_end - t_open_end - 1).force_encoding("UTF-8")
|
|
92
|
+
str = raw_str.include?("&") ? raw_str.gsub(/&(?:amp|lt|gt|quot|apos);/, XML_ENTITIES) : raw_str
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
str = ""
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
str = ""
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
str = ""
|
|
68
103
|
end
|
|
104
|
+
|
|
105
|
+
yield str.freeze
|
|
106
|
+
pos = si_end + 5
|
|
69
107
|
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private_class_method :fast_scan
|
|
111
|
+
|
|
112
|
+
def self.extract_multi_t(xml, from, to)
|
|
113
|
+
buf = +""
|
|
114
|
+
pos = from
|
|
115
|
+
while pos < to
|
|
116
|
+
t_start = xml.index("<t", pos)
|
|
117
|
+
break unless t_start && t_start < to
|
|
70
118
|
|
|
71
|
-
|
|
72
|
-
|
|
119
|
+
t_open_end = xml.index(">", t_start + 2)
|
|
120
|
+
break unless t_open_end
|
|
121
|
+
|
|
122
|
+
if xml.getbyte(t_open_end - 1) == 47
|
|
123
|
+
pos = t_open_end + 1
|
|
124
|
+
next
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
t_end = xml.index("</t>", t_open_end + 1)
|
|
128
|
+
break unless t_end && t_end <= to
|
|
129
|
+
|
|
130
|
+
raw_chunk = xml.byteslice(t_open_end + 1, t_end - t_open_end - 1).force_encoding("UTF-8")
|
|
131
|
+
raw_chunk = raw_chunk.gsub(/&(?:amp|lt|gt|quot|apos);/, XML_ENTITIES) if raw_chunk.include?("&")
|
|
132
|
+
buf << raw_chunk
|
|
133
|
+
pos = t_end + 4
|
|
73
134
|
end
|
|
135
|
+
buf
|
|
74
136
|
end
|
|
137
|
+
|
|
138
|
+
private_class_method :extract_multi_t
|
|
75
139
|
end
|
|
76
140
|
end
|
|
77
141
|
end
|
|
@@ -393,8 +393,7 @@ module Xlsxrb
|
|
|
393
393
|
if raw.include?(".")
|
|
394
394
|
raw.to_f
|
|
395
395
|
else
|
|
396
|
-
|
|
397
|
-
int_val.to_s == raw ? int_val : raw.to_f
|
|
396
|
+
raw.to_i
|
|
398
397
|
end
|
|
399
398
|
end
|
|
400
399
|
end
|
|
@@ -482,19 +481,53 @@ module Xlsxrb
|
|
|
482
481
|
next
|
|
483
482
|
end
|
|
484
483
|
|
|
485
|
-
row_tag = xml.byteslice(row_start, tag_end - row_start)
|
|
486
484
|
row_index = 0
|
|
487
|
-
|
|
488
|
-
|
|
485
|
+
has_custom_attrs = false
|
|
486
|
+
ri = row_start + 4
|
|
487
|
+
while ri < tag_end
|
|
488
|
+
rb = xml.getbyte(ri)
|
|
489
|
+
if rb == 114 && xml.getbyte(ri + 1) == 61 && xml.getbyte(ri + 2) == 34 # r="
|
|
490
|
+
ri += 3
|
|
491
|
+
while ri < tag_end
|
|
492
|
+
cb = xml.getbyte(ri)
|
|
493
|
+
break unless cb.between?(48, 57)
|
|
494
|
+
|
|
495
|
+
row_index = (row_index * 10) + (cb - 48)
|
|
496
|
+
ri += 1
|
|
497
|
+
|
|
498
|
+
end
|
|
499
|
+
row_index -= 1
|
|
500
|
+
elsif [104, 99, 111].include?(rb) # 'h', 'c', 'o' for ht, customHeight, hidden, outlineLevel
|
|
501
|
+
has_custom_attrs = true
|
|
502
|
+
ri += 1
|
|
503
|
+
else
|
|
504
|
+
ri += 1
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
attrs = if has_custom_attrs
|
|
509
|
+
row_tag = xml.byteslice(row_start, tag_end - row_start)
|
|
510
|
+
extract_row_attrs(row_tag)
|
|
511
|
+
else
|
|
512
|
+
EMPTY_HASH
|
|
513
|
+
end
|
|
489
514
|
|
|
490
|
-
attrs = extract_row_attrs(row_tag)
|
|
491
515
|
row_end = xml.index("</row>", tag_end + 1)
|
|
492
516
|
break unless row_end
|
|
493
517
|
|
|
494
518
|
row_source = { part: part_name, row: row_index }
|
|
495
519
|
cells = fast_parse_cells_direct(xml, tag_end + 1, row_end, shared_strings, row_source)
|
|
496
520
|
|
|
497
|
-
|
|
521
|
+
row_obj = Elements::Row.new(
|
|
522
|
+
index: row_index,
|
|
523
|
+
cells: cells,
|
|
524
|
+
height: attrs[:height],
|
|
525
|
+
hidden: attrs[:hidden] || false,
|
|
526
|
+
custom_height: attrs[:custom_height] || false,
|
|
527
|
+
outline_level: attrs[:outline_level],
|
|
528
|
+
errors: Elements::EMPTY_ERRORS
|
|
529
|
+
)
|
|
530
|
+
block.call(row_obj)
|
|
498
531
|
|
|
499
532
|
pos = row_end + 6
|
|
500
533
|
end
|
|
@@ -519,18 +552,90 @@ module Xlsxrb
|
|
|
519
552
|
c_tag_end = xml.index(">", c_start + 2)
|
|
520
553
|
break unless c_tag_end
|
|
521
554
|
|
|
522
|
-
#
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
555
|
+
# Zero-allocation attribute scan directly within the tag
|
|
556
|
+
type = nil
|
|
557
|
+
style_index = nil
|
|
558
|
+
col_idx = nil
|
|
559
|
+
row_idx = nil
|
|
560
|
+
|
|
561
|
+
ai = c_start + 2
|
|
562
|
+
while ai < c_tag_end
|
|
563
|
+
b = xml.getbyte(ai)
|
|
564
|
+
if [32, 9, 10, 13].include?(b)
|
|
565
|
+
ai += 1
|
|
566
|
+
next
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
if b == 114 && xml.getbyte(ai + 1) == 61 && xml.getbyte(ai + 2) == 34 # r="
|
|
570
|
+
ai += 3
|
|
571
|
+
col_idx = 0
|
|
572
|
+
while ai < c_tag_end
|
|
573
|
+
cb = xml.getbyte(ai)
|
|
574
|
+
if cb.between?(65, 90)
|
|
575
|
+
col_idx = (col_idx * 26) + (cb - 64)
|
|
576
|
+
ai += 1
|
|
577
|
+
elsif cb.between?(97, 122)
|
|
578
|
+
col_idx = (col_idx * 26) + (cb - 96)
|
|
579
|
+
ai += 1
|
|
580
|
+
else
|
|
581
|
+
break
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
col_idx -= 1
|
|
585
|
+
row_idx = 0
|
|
586
|
+
while ai < c_tag_end
|
|
587
|
+
cb = xml.getbyte(ai)
|
|
588
|
+
break unless cb.between?(48, 57)
|
|
589
|
+
|
|
590
|
+
row_idx = (row_idx * 10) + (cb - 48)
|
|
591
|
+
ai += 1
|
|
592
|
+
|
|
593
|
+
end
|
|
594
|
+
row_idx -= 1
|
|
595
|
+
ai += 1 if xml.getbyte(ai) == 34
|
|
596
|
+
elsif b == 116 && xml.getbyte(ai + 1) == 61 && xml.getbyte(ai + 2) == 34 # t="
|
|
597
|
+
ai += 3
|
|
598
|
+
type_b = xml.getbyte(ai)
|
|
599
|
+
if type_b == 115 && xml.getbyte(ai + 1) == 34 # "s"
|
|
600
|
+
type = "s"
|
|
601
|
+
ai += 2
|
|
602
|
+
elsif type_b == 98 && xml.getbyte(ai + 1) == 34 # "b"
|
|
603
|
+
type = "b"
|
|
604
|
+
ai += 2
|
|
605
|
+
elsif type_b == 101 && xml.getbyte(ai + 1) == 34 # "e"
|
|
606
|
+
type = "e"
|
|
607
|
+
ai += 2
|
|
608
|
+
else
|
|
609
|
+
t_end = xml.index('"', ai)
|
|
610
|
+
type = xml.byteslice(ai, t_end - ai) if t_end
|
|
611
|
+
ai = t_end ? t_end + 1 : c_tag_end
|
|
612
|
+
end
|
|
613
|
+
elsif b == 115 && xml.getbyte(ai + 1) == 61 && xml.getbyte(ai + 2) == 34 # s="
|
|
614
|
+
ai += 3
|
|
615
|
+
style_index = 0
|
|
616
|
+
while ai < c_tag_end
|
|
617
|
+
cb = xml.getbyte(ai)
|
|
618
|
+
break unless cb.between?(48, 57)
|
|
619
|
+
|
|
620
|
+
style_index = (style_index * 10) + (cb - 48)
|
|
621
|
+
ai += 1
|
|
622
|
+
|
|
623
|
+
end
|
|
624
|
+
ai += 1 if xml.getbyte(ai) == 34
|
|
625
|
+
else
|
|
626
|
+
ai += 1
|
|
627
|
+
end
|
|
628
|
+
end
|
|
528
629
|
|
|
529
630
|
# Self-closing <c ... />
|
|
530
631
|
if xml.getbyte(c_tag_end - 1) == 47
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
632
|
+
cells << Elements::Cell.new(
|
|
633
|
+
row_index: row_idx || row_source[:row],
|
|
634
|
+
column_index: col_idx || cells.size,
|
|
635
|
+
value: nil,
|
|
636
|
+
style_index: style_index,
|
|
637
|
+
errors: Elements::EMPTY_ERRORS
|
|
638
|
+
)
|
|
534
639
|
pos = c_tag_end + 1
|
|
535
640
|
next
|
|
536
641
|
end
|
|
@@ -554,8 +659,21 @@ module Xlsxrb
|
|
|
554
659
|
v_val_start = tag_pos + 3
|
|
555
660
|
v_end = xml.index("</v>", v_val_start)
|
|
556
661
|
if v_end
|
|
557
|
-
|
|
558
|
-
|
|
662
|
+
if type == "s"
|
|
663
|
+
# Zero-allocation integer parse for SST index
|
|
664
|
+
s_idx = 0
|
|
665
|
+
v_i = v_val_start
|
|
666
|
+
while v_i < v_end
|
|
667
|
+
s_idx = (s_idx * 10) + (xml.getbyte(v_i) - 48)
|
|
668
|
+
v_i += 1
|
|
669
|
+
end
|
|
670
|
+
value = shared_strings[s_idx] || ""
|
|
671
|
+
elsif type == "b"
|
|
672
|
+
value = xml.getbyte(v_val_start) == 49 # '1'
|
|
673
|
+
else
|
|
674
|
+
raw_value = xml.byteslice(v_val_start, v_end - v_val_start)
|
|
675
|
+
value = resolve_fast_value(raw_value, type, shared_strings)
|
|
676
|
+
end
|
|
559
677
|
cpos = v_end + 4
|
|
560
678
|
else
|
|
561
679
|
cpos = tag_pos + 3
|
|
@@ -603,11 +721,14 @@ module Xlsxrb
|
|
|
603
721
|
end
|
|
604
722
|
|
|
605
723
|
val_to_use = inline_str || value
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
724
|
+
cells << Elements::Cell.new(
|
|
725
|
+
row_index: row_idx || row_source[:row],
|
|
726
|
+
column_index: col_idx || cells.size,
|
|
727
|
+
value: val_to_use,
|
|
728
|
+
formula: formula,
|
|
729
|
+
style_index: style_index,
|
|
730
|
+
errors: Elements::EMPTY_ERRORS
|
|
731
|
+
)
|
|
611
732
|
|
|
612
733
|
pos = c_end + 4
|
|
613
734
|
end
|