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
|
@@ -17,7 +17,7 @@ module Xlsxrb
|
|
|
17
17
|
class Worksheet
|
|
18
18
|
[Enumerable, CoordinateAccess].each { |m| include m }
|
|
19
19
|
|
|
20
|
-
attr_reader :name, :rows, :columns, :charts, :conditional_formatting, :data_validations, :unmapped_data, :errors, :state, :hyperlinks, :comments, :images, :styles, :date1904, :print_area, :print_titles, :sparkline_groups
|
|
20
|
+
attr_reader :name, :rows, :columns, :charts, :conditional_formatting, :data_validations, :unmapped_data, :errors, :state, :hyperlinks, :comments, :images, :styles, :date1904, :print_area, :print_titles, :sparkline_groups, :merged_cells, :auto_filter, :tables, :shapes, :freeze_pane, :page_setup, :page_margins, :header_footer, :print_options, :sheet_protection, :sheet_view, :sheet_properties, :sheet_format, :row_breaks, :col_breaks, :protected_ranges, :cell_watches, :selection
|
|
21
21
|
attr_writer :dimension
|
|
22
22
|
alias conditional_formats conditional_formatting
|
|
23
23
|
|
|
@@ -42,8 +42,26 @@ module Xlsxrb
|
|
|
42
42
|
# @param print_area [String, nil] Sheet print area range (e.g. "A1:H50").
|
|
43
43
|
# @param print_titles [Hash, nil] Repeating print title rows/cols (e.g. { rows: "1:2", cols: "A:B" }).
|
|
44
44
|
# @param sparkline_groups [Array<Hash>] Sparkline groups in the sheet.
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
# @param merged_cells [Array<String>] Merged cell ranges (e.g. ["A1:B2"]).
|
|
46
|
+
# @param auto_filter [String, nil] AutoFilter range (e.g. "A1:E10").
|
|
47
|
+
# @param tables [Array<Hash>] Table definitions in the sheet.
|
|
48
|
+
# @param shapes [Array<Hash>] Shape definitions in the sheet.
|
|
49
|
+
# @param freeze_pane [Hash, nil] Freeze pane settings (e.g. { row: 1, col: 0 }).
|
|
50
|
+
# @param page_setup [Hash] Page setup properties (e.g. orientation, paper size).
|
|
51
|
+
# @param page_margins [Hash, nil] Page margins settings.
|
|
52
|
+
# @param header_footer [Hash] Header and footer configurations.
|
|
53
|
+
# @param print_options [Hash] Print options (e.g. gridlines, headings).
|
|
54
|
+
# @param sheet_protection [Hash, nil] Sheet protection settings.
|
|
55
|
+
# @param sheet_view [Hash] Sheet view configurations (e.g. zoom, gridlines).
|
|
56
|
+
# @param sheet_properties [Hash] Sheet properties (e.g. tabColor, outlinePr).
|
|
57
|
+
# @param sheet_format [Hash] Sheet format properties (e.g. defaultRowHeight, defaultColWidth).
|
|
58
|
+
# @param row_breaks [Array<Integer | Hash>] Explicit page row breaks.
|
|
59
|
+
# @param col_breaks [Array<Integer | Hash>] Explicit page column breaks.
|
|
60
|
+
# @param protected_ranges [Array<Hash>] Protected range definitions.
|
|
61
|
+
# @param cell_watches [Array<String>] Watched cell references.
|
|
62
|
+
# @param selection [Hash, Array<Hash>, nil] Active cell and selection settings.
|
|
63
|
+
#: (name: String?, ?rows: Array[Elements::Row], ?columns: Array[Elements::Column], ?charts: Array[Hash[Symbol, untyped]], ?conditional_formatting: Array[Hash[Symbol, untyped]]?, ?data_validations: Array[Hash[Symbol, untyped]], ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?, ?conditional_formats: Array[Hash[Symbol, untyped]]?, ?state: Symbol, ?hyperlinks: Hash[String, untyped], ?comments: Array[Hash[Symbol, untyped]], ?images: Array[Elements::Image], ?styles: (Elements::Styles | Hash[untyped, untyped])?, ?date1904: bool, ?dimension: String?, ?trim_empty_rows: bool, ?pad_empty_rows: bool, ?pad_empty_cells: bool, ?print_area: String?, ?print_titles: Hash[Symbol, untyped]?, ?sparkline_groups: Array[Hash[Symbol, untyped]], ?merged_cells: Array[String], ?auto_filter: String?, ?tables: Array[Hash[Symbol, untyped]], ?shapes: Array[Hash[Symbol, untyped]], ?freeze_pane: Hash[Symbol, untyped]?, ?page_setup: Hash[Symbol, untyped], ?page_margins: Hash[Symbol, untyped]?, ?header_footer: Hash[Symbol, untyped], ?print_options: Hash[Symbol, untyped], ?sheet_protection: Hash[Symbol, untyped]?, ?sheet_view: Hash[Symbol, untyped], ?sheet_properties: Hash[Symbol, untyped], ?sheet_format: Hash[Symbol, untyped], ?row_breaks: Array[untyped], ?col_breaks: Array[untyped], ?protected_ranges: Array[Hash[Symbol, untyped]], ?cell_watches: Array[String], ?selection: (Hash[Symbol, untyped] | Array[Hash[Symbol, untyped]])?) -> void
|
|
64
|
+
def initialize(name:, rows: [], columns: [], charts: [], conditional_formatting: nil, data_validations: [], unmapped_data: {}, errors: nil, conditional_formats: nil, state: :visible, hyperlinks: {}, comments: [], images: [], styles: nil, date1904: false, dimension: nil, trim_empty_rows: false, pad_empty_rows: false, pad_empty_cells: false, print_area: nil, print_titles: nil, sparkline_groups: [], merged_cells: [], auto_filter: nil, tables: [], shapes: [], freeze_pane: nil, page_setup: {}, page_margins: nil, header_footer: {}, print_options: {}, sheet_protection: nil, sheet_view: {}, sheet_properties: {}, sheet_format: {}, row_breaks: [], col_breaks: [], protected_ranges: [], cell_watches: [], selection: nil)
|
|
47
65
|
@name = name
|
|
48
66
|
@rows = (rows || []).freeze
|
|
49
67
|
@columns = (columns || []).freeze
|
|
@@ -67,6 +85,24 @@ module Xlsxrb
|
|
|
67
85
|
@trim_empty_rows = trim_empty_rows ? true : false
|
|
68
86
|
@pad_empty_rows = pad_empty_rows ? true : false
|
|
69
87
|
@pad_empty_cells = pad_empty_cells ? true : false
|
|
88
|
+
@merged_cells = (merged_cells || []).freeze
|
|
89
|
+
@auto_filter = auto_filter
|
|
90
|
+
@tables = (tables || []).freeze
|
|
91
|
+
@shapes = (shapes || []).freeze
|
|
92
|
+
@freeze_pane = freeze_pane&.freeze
|
|
93
|
+
@page_setup = (page_setup || {}).freeze
|
|
94
|
+
@page_margins = page_margins&.freeze
|
|
95
|
+
@header_footer = (header_footer || {}).freeze
|
|
96
|
+
@print_options = (print_options || {}).freeze
|
|
97
|
+
@sheet_protection = sheet_protection&.freeze
|
|
98
|
+
@sheet_view = (sheet_view || {}).freeze
|
|
99
|
+
@sheet_properties = (sheet_properties || {}).freeze
|
|
100
|
+
@sheet_format = (sheet_format || unmapped_data[:sheet_format] || unmapped_data.dig(:facade, :sheet_format) || {}).freeze
|
|
101
|
+
@row_breaks = (row_breaks || []).freeze
|
|
102
|
+
@col_breaks = (col_breaks || []).freeze
|
|
103
|
+
@protected_ranges = (protected_ranges || unmapped_data[:protected_ranges] || unmapped_data.dig(:facade, :protected_ranges) || []).freeze
|
|
104
|
+
@cell_watches = (cell_watches || unmapped_data[:cell_watches] || unmapped_data.dig(:facade, :cell_watches) || []).freeze
|
|
105
|
+
@selection = selection&.freeze
|
|
70
106
|
end
|
|
71
107
|
|
|
72
108
|
# Returns whether trailing empty rows are omitted during iteration.
|
|
@@ -328,6 +364,15 @@ module Xlsxrb
|
|
|
328
364
|
@state == :visible
|
|
329
365
|
end
|
|
330
366
|
|
|
367
|
+
# Returns whether the worksheet is very hidden (:very_hidden).
|
|
368
|
+
#
|
|
369
|
+
# @return [Boolean]
|
|
370
|
+
# @api public
|
|
371
|
+
#: () -> bool
|
|
372
|
+
def very_hidden?
|
|
373
|
+
@state == :very_hidden
|
|
374
|
+
end
|
|
375
|
+
|
|
331
376
|
# Returns hyperlink metadata for the given cell reference, or nil.
|
|
332
377
|
#
|
|
333
378
|
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
@@ -488,6 +533,24 @@ module Xlsxrb
|
|
|
488
533
|
new_print_titles = changes.key?(:print_titles) ? changes[:print_titles] : print_titles
|
|
489
534
|
new_sparklines = changes.key?(:sparkline_groups) ? changes[:sparkline_groups] : sparkline_groups
|
|
490
535
|
new_trim = changes.key?(:trim_empty_rows) ? changes[:trim_empty_rows] : @trim_empty_rows
|
|
536
|
+
new_merged = changes.key?(:merged_cells) ? changes[:merged_cells] : merged_cells
|
|
537
|
+
new_auto_filter = changes.key?(:auto_filter) ? changes[:auto_filter] : auto_filter
|
|
538
|
+
new_tables = changes.key?(:tables) ? changes[:tables] : tables
|
|
539
|
+
new_shapes = changes.key?(:shapes) ? changes[:shapes] : shapes
|
|
540
|
+
new_freeze_pane = changes.key?(:freeze_pane) ? changes[:freeze_pane] : freeze_pane
|
|
541
|
+
new_page_setup = changes.key?(:page_setup) ? changes[:page_setup] : page_setup
|
|
542
|
+
new_page_margins = changes.key?(:page_margins) ? changes[:page_margins] : page_margins
|
|
543
|
+
new_header_footer = changes.key?(:header_footer) ? changes[:header_footer] : header_footer
|
|
544
|
+
new_print_options = changes.key?(:print_options) ? changes[:print_options] : print_options
|
|
545
|
+
new_sheet_protection = changes.key?(:sheet_protection) ? changes[:sheet_protection] : sheet_protection
|
|
546
|
+
new_sheet_view = changes.key?(:sheet_view) ? changes[:sheet_view] : sheet_view
|
|
547
|
+
new_sheet_properties = changes.key?(:sheet_properties) ? changes[:sheet_properties] : sheet_properties
|
|
548
|
+
new_sheet_format = changes.key?(:sheet_format) ? changes[:sheet_format] : sheet_format
|
|
549
|
+
new_row_breaks = changes.key?(:row_breaks) ? changes[:row_breaks] : row_breaks
|
|
550
|
+
new_col_breaks = changes.key?(:col_breaks) ? changes[:col_breaks] : col_breaks
|
|
551
|
+
new_protected_ranges = changes.key?(:protected_ranges) ? changes[:protected_ranges] : protected_ranges
|
|
552
|
+
new_cell_watches = changes.key?(:cell_watches) ? changes[:cell_watches] : cell_watches
|
|
553
|
+
new_selection = changes.key?(:selection) ? changes[:selection] : selection
|
|
491
554
|
|
|
492
555
|
self.class.new(
|
|
493
556
|
name: new_name,
|
|
@@ -507,7 +570,25 @@ module Xlsxrb
|
|
|
507
570
|
print_area: new_print_area,
|
|
508
571
|
print_titles: new_print_titles,
|
|
509
572
|
sparkline_groups: new_sparklines,
|
|
510
|
-
trim_empty_rows: new_trim
|
|
573
|
+
trim_empty_rows: new_trim,
|
|
574
|
+
merged_cells: new_merged,
|
|
575
|
+
auto_filter: new_auto_filter,
|
|
576
|
+
tables: new_tables,
|
|
577
|
+
shapes: new_shapes,
|
|
578
|
+
freeze_pane: new_freeze_pane,
|
|
579
|
+
page_setup: new_page_setup,
|
|
580
|
+
page_margins: new_page_margins,
|
|
581
|
+
header_footer: new_header_footer,
|
|
582
|
+
print_options: new_print_options,
|
|
583
|
+
sheet_protection: new_sheet_protection,
|
|
584
|
+
sheet_view: new_sheet_view,
|
|
585
|
+
sheet_properties: new_sheet_properties,
|
|
586
|
+
sheet_format: new_sheet_format,
|
|
587
|
+
row_breaks: new_row_breaks,
|
|
588
|
+
col_breaks: new_col_breaks,
|
|
589
|
+
protected_ranges: new_protected_ranges,
|
|
590
|
+
cell_watches: new_cell_watches,
|
|
591
|
+
selection: new_selection
|
|
511
592
|
)
|
|
512
593
|
end
|
|
513
594
|
|
|
@@ -533,7 +614,23 @@ module Xlsxrb
|
|
|
533
614
|
print_area: print_area,
|
|
534
615
|
print_titles: print_titles,
|
|
535
616
|
sparkline_groups: sparkline_groups,
|
|
536
|
-
trim_empty_rows: @trim_empty_rows
|
|
617
|
+
trim_empty_rows: @trim_empty_rows,
|
|
618
|
+
merged_cells: merged_cells,
|
|
619
|
+
auto_filter: auto_filter,
|
|
620
|
+
tables: tables,
|
|
621
|
+
shapes: shapes,
|
|
622
|
+
freeze_pane: freeze_pane,
|
|
623
|
+
page_setup: page_setup,
|
|
624
|
+
page_margins: page_margins,
|
|
625
|
+
header_footer: header_footer,
|
|
626
|
+
print_options: print_options,
|
|
627
|
+
sheet_protection: sheet_protection,
|
|
628
|
+
sheet_view: sheet_view,
|
|
629
|
+
sheet_properties: sheet_properties,
|
|
630
|
+
sheet_format: sheet_format,
|
|
631
|
+
row_breaks: row_breaks,
|
|
632
|
+
col_breaks: col_breaks,
|
|
633
|
+
selection: selection
|
|
537
634
|
}
|
|
538
635
|
end
|
|
539
636
|
|
|
@@ -544,13 +641,26 @@ module Xlsxrb
|
|
|
544
641
|
|
|
545
642
|
name == other.name && rows == other.rows && columns == other.columns && charts == other.charts &&
|
|
546
643
|
conditional_formatting == other.conditional_formatting && data_validations == other.data_validations &&
|
|
547
|
-
state == other.state && hyperlinks == other.hyperlinks && comments == other.comments
|
|
644
|
+
state == other.state && hyperlinks == other.hyperlinks && comments == other.comments &&
|
|
645
|
+
merged_cells == other.merged_cells && auto_filter == other.auto_filter &&
|
|
646
|
+
tables == other.tables && shapes == other.shapes && freeze_pane == other.freeze_pane &&
|
|
647
|
+
page_setup == other.page_setup && page_margins == other.page_margins &&
|
|
648
|
+
header_footer == other.header_footer && print_options == other.print_options &&
|
|
649
|
+
sheet_protection == other.sheet_protection && sheet_view == other.sheet_view &&
|
|
650
|
+
sheet_properties == other.sheet_properties && sheet_format == other.sheet_format &&
|
|
651
|
+
row_breaks == other.row_breaks && col_breaks == other.col_breaks &&
|
|
652
|
+
selection == other.selection
|
|
548
653
|
end
|
|
549
654
|
alias eql? ==
|
|
550
655
|
|
|
551
656
|
#: () -> Integer
|
|
552
657
|
def hash
|
|
553
|
-
[
|
|
658
|
+
[
|
|
659
|
+
self.class, name, rows, columns, charts, conditional_formatting, data_validations,
|
|
660
|
+
state, hyperlinks, comments, merged_cells, auto_filter, tables, shapes, freeze_pane,
|
|
661
|
+
page_setup, page_margins, header_footer, print_options, sheet_protection, sheet_view,
|
|
662
|
+
sheet_properties, sheet_format, row_breaks, col_breaks, selection
|
|
663
|
+
].hash
|
|
554
664
|
end
|
|
555
665
|
|
|
556
666
|
# Returns self when load is called on an already in-memory Worksheet.
|
data/lib/xlsxrb/ooxml/cfb.rb
CHANGED
|
@@ -100,9 +100,16 @@ module Xlsxrb
|
|
|
100
100
|
raise Xlsxrb::Error, "CFB file is too small" if @data.bytesize < 512
|
|
101
101
|
|
|
102
102
|
_, major_ver = @data[0x18, 4].unpack("v2")
|
|
103
|
+
raise Xlsxrb::Error, "Unsupported CFB major version: #{major_ver}" unless [3, 4].include?(major_ver)
|
|
104
|
+
|
|
103
105
|
@major_version = major_ver
|
|
104
106
|
@sector_shift = @data[0x1E, 2].unpack1("v")
|
|
105
107
|
@mini_sector_shift = @data[0x20, 2].unpack1("v")
|
|
108
|
+
|
|
109
|
+
expected_shift = @major_version == 4 ? 12 : 9
|
|
110
|
+
raise Xlsxrb::Error, "Invalid CFB sector shift in header: #{@sector_shift}" unless @sector_shift == expected_shift
|
|
111
|
+
raise Xlsxrb::Error, "Invalid CFB mini sector shift in header: #{@mini_sector_shift}" unless @mini_sector_shift == 6
|
|
112
|
+
|
|
106
113
|
@sector_size = 1 << @sector_shift
|
|
107
114
|
@mini_sector_size = 1 << @mini_sector_shift
|
|
108
115
|
|
|
@@ -123,6 +130,9 @@ module Xlsxrb
|
|
|
123
130
|
end
|
|
124
131
|
|
|
125
132
|
def read_sector(sector_id)
|
|
133
|
+
max_sectors = (@data.bytesize - 512) / @sector_size
|
|
134
|
+
return "".b if sector_id.negative? || sector_id >= max_sectors
|
|
135
|
+
|
|
126
136
|
offset = sector_offset(sector_id)
|
|
127
137
|
@data[offset, @sector_size] || "".b
|
|
128
138
|
end
|
|
@@ -24,12 +24,15 @@ module Xlsxrb
|
|
|
24
24
|
@runs = []
|
|
25
25
|
@current_font = {}
|
|
26
26
|
@has_runs = false
|
|
27
|
+
@inside_rph = false
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def start_element(_uri, local_name, qname, attributes)
|
|
30
31
|
name = element_name(local_name, qname)
|
|
31
32
|
|
|
32
33
|
case name
|
|
34
|
+
when "rPh"
|
|
35
|
+
@inside_rph = true
|
|
33
36
|
when "si"
|
|
34
37
|
@inside_si = true
|
|
35
38
|
@text_buffer = +""
|
|
@@ -74,7 +77,7 @@ module Xlsxrb
|
|
|
74
77
|
when "scheme"
|
|
75
78
|
@current_font[:scheme] = attributes["val"] if @inside_rpr
|
|
76
79
|
when "t"
|
|
77
|
-
@inside_t = @inside_si
|
|
80
|
+
@inside_t = @inside_si && !@inside_rph
|
|
78
81
|
@text_buffer = +"" if @inside_r
|
|
79
82
|
end
|
|
80
83
|
end
|
|
@@ -87,12 +90,16 @@ module Xlsxrb
|
|
|
87
90
|
name = element_name(local_name, qname)
|
|
88
91
|
|
|
89
92
|
case name
|
|
93
|
+
when "rPh"
|
|
94
|
+
@inside_rph = false
|
|
90
95
|
when "t"
|
|
91
96
|
@inside_t = false
|
|
92
97
|
when "rPr"
|
|
93
98
|
@inside_rpr = false
|
|
94
99
|
when "r"
|
|
95
|
-
|
|
100
|
+
decoded_text = XmlBuilder.unescape(@text_buffer)
|
|
101
|
+
decoded_text = decoded_text.gsub("\r\n", "\n").tr("\r", "\n") if decoded_text.include?("\r")
|
|
102
|
+
run = { text: decoded_text }
|
|
96
103
|
run[:font] = @current_font.dup unless @current_font.empty?
|
|
97
104
|
@runs << run
|
|
98
105
|
@inside_r = false
|
|
@@ -100,7 +107,9 @@ module Xlsxrb
|
|
|
100
107
|
@strings << if @has_runs
|
|
101
108
|
Xlsxrb::Elements::RichText.new(runs: @runs)
|
|
102
109
|
else
|
|
103
|
-
@text_buffer
|
|
110
|
+
decoded_text = XmlBuilder.unescape(@text_buffer)
|
|
111
|
+
decoded_text = decoded_text.gsub("\r\n", "\n").tr("\r", "\n") if decoded_text.include?("\r")
|
|
112
|
+
decoded_text
|
|
104
113
|
end
|
|
105
114
|
@inside_si = false
|
|
106
115
|
@text_buffer = +""
|
|
@@ -145,18 +154,23 @@ module Xlsxrb
|
|
|
145
154
|
@is_has_runs = false
|
|
146
155
|
@is_current_font = {}
|
|
147
156
|
@is_run_text = +""
|
|
157
|
+
@inside_rph = false
|
|
158
|
+
@has_inline_text = false
|
|
148
159
|
end
|
|
149
160
|
|
|
150
161
|
def start_element(_uri, local_name, qname, attributes)
|
|
151
162
|
name = element_name(local_name, qname)
|
|
152
163
|
|
|
153
164
|
case name
|
|
165
|
+
when "rPh"
|
|
166
|
+
@inside_rph = true
|
|
154
167
|
when "row"
|
|
155
168
|
parse_row_attributes(attributes)
|
|
156
169
|
when "c"
|
|
157
170
|
@current_cell_ref = attributes["r"]
|
|
158
171
|
@current_cell_type = attributes["t"]
|
|
159
172
|
@current_cell_ph = %w[1 true].include?(attributes["ph"])
|
|
173
|
+
@has_inline_text = false
|
|
160
174
|
@value_buffer = +""
|
|
161
175
|
@inline_text_buffer = +""
|
|
162
176
|
@formula_buffer = +""
|
|
@@ -188,6 +202,7 @@ module Xlsxrb
|
|
|
188
202
|
@formula_r1 = attributes["r1"] if attributes["r1"]
|
|
189
203
|
@formula_r2 = attributes["r2"] if attributes["r2"]
|
|
190
204
|
when "is"
|
|
205
|
+
@has_inline_text = true
|
|
191
206
|
@inside_is = true if @current_cell_type == "inlineStr"
|
|
192
207
|
when "r"
|
|
193
208
|
if @inside_is
|
|
@@ -231,11 +246,15 @@ module Xlsxrb
|
|
|
231
246
|
when "scheme"
|
|
232
247
|
@is_current_font[:scheme] = attributes["val"] if @inside_is_rpr
|
|
233
248
|
when "t"
|
|
234
|
-
|
|
235
|
-
@
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
249
|
+
unless @inside_rph
|
|
250
|
+
if @inside_is_r
|
|
251
|
+
@is_run_text = +""
|
|
252
|
+
@inside_inline_text = true
|
|
253
|
+
@has_inline_text = true
|
|
254
|
+
elsif @inside_is || (@current_cell_type == "inlineStr" && !@current_cell_ref.nil?)
|
|
255
|
+
@inside_inline_text = true
|
|
256
|
+
@has_inline_text = true
|
|
257
|
+
end
|
|
239
258
|
end
|
|
240
259
|
end
|
|
241
260
|
end
|
|
@@ -256,6 +275,8 @@ module Xlsxrb
|
|
|
256
275
|
name = element_name(local_name, qname)
|
|
257
276
|
|
|
258
277
|
case name
|
|
278
|
+
when "rPh"
|
|
279
|
+
@inside_rph = false
|
|
259
280
|
when "v"
|
|
260
281
|
@inside_value = false
|
|
261
282
|
when "f"
|
|
@@ -309,7 +330,14 @@ module Xlsxrb
|
|
|
309
330
|
return if @current_cell_ref.nil?
|
|
310
331
|
|
|
311
332
|
unless @formula_buffer.empty? && @formula_type.nil?
|
|
312
|
-
cached = @value_buffer.empty?
|
|
333
|
+
cached = if @value_buffer.empty?
|
|
334
|
+
nil
|
|
335
|
+
elsif @current_cell_type == "b"
|
|
336
|
+
v = @value_buffer.strip
|
|
337
|
+
v == "1" || v.casecmp?("true")
|
|
338
|
+
else
|
|
339
|
+
@value_buffer.dup
|
|
340
|
+
end
|
|
313
341
|
f_type = { "shared" => :shared, "array" => :array, "dataTable" => :data_table }[@formula_type]
|
|
314
342
|
@cells[@current_cell_ref] = Xlsxrb::Elements::Formula.new(
|
|
315
343
|
expression: @formula_buffer.dup,
|
|
@@ -328,39 +356,29 @@ module Xlsxrb
|
|
|
328
356
|
return
|
|
329
357
|
end
|
|
330
358
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
Xlsxrb::Elements::RichText.new(runs: @is_runs.map(&:dup))
|
|
335
|
-
else
|
|
336
|
-
@inline_text_buffer.dup
|
|
337
|
-
end
|
|
338
|
-
when "s"
|
|
339
|
-
index = @value_buffer.to_i
|
|
340
|
-
@cells[@current_cell_ref] = @shared_strings[index] || ""
|
|
341
|
-
when "e"
|
|
342
|
-
code = @value_buffer.dup
|
|
343
|
-
@cells[@current_cell_ref] = if Xlsxrb::Elements::VALID_ERROR_CODES.include?(code)
|
|
344
|
-
Xlsxrb::Elements::CellError.new(code:)
|
|
345
|
-
else
|
|
346
|
-
code
|
|
347
|
-
end
|
|
348
|
-
when "b"
|
|
349
|
-
@cells[@current_cell_ref] = @value_buffer.strip == "1"
|
|
350
|
-
when nil, "", "n"
|
|
351
|
-
return if @value_buffer.empty?
|
|
352
|
-
|
|
353
|
-
raw = @value_buffer.dup
|
|
354
|
-
@cells[@current_cell_ref] = numeric_value(raw)
|
|
359
|
+
if @current_cell_type == "inlineStr" && @is_has_runs
|
|
360
|
+
@cells[@current_cell_ref] = Xlsxrb::Elements::RichText.new(runs: @is_runs.map(&:dup))
|
|
361
|
+
return
|
|
355
362
|
end
|
|
363
|
+
|
|
364
|
+
v = if @current_cell_type == "inlineStr"
|
|
365
|
+
@has_inline_text ? @inline_text_buffer : nil
|
|
366
|
+
else
|
|
367
|
+
@value_buffer.empty? ? nil : @value_buffer
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
val = Xlsxrb::Ooxml::WorksheetParser.resolve_cell_value(@current_cell_type, v, nil, @shared_strings)
|
|
371
|
+
return if val.nil? && (@current_cell_type.nil? || @current_cell_type == "" || @current_cell_type == "n" || @current_cell_type == "s" || @current_cell_type == "inlineStr")
|
|
372
|
+
|
|
373
|
+
@cells[@current_cell_ref] = if @current_cell_type == "e" && val && Xlsxrb::Elements::VALID_ERROR_CODES.include?(val)
|
|
374
|
+
Xlsxrb::Elements::CellError.new(code: val)
|
|
375
|
+
else
|
|
376
|
+
val
|
|
377
|
+
end
|
|
356
378
|
end
|
|
357
379
|
|
|
358
380
|
def numeric_value(raw)
|
|
359
|
-
|
|
360
|
-
raw.to_f
|
|
361
|
-
else
|
|
362
|
-
raw.to_i
|
|
363
|
-
end
|
|
381
|
+
Xlsxrb::Ooxml::WorksheetParser.numeric_cell_value(raw)
|
|
364
382
|
end
|
|
365
383
|
|
|
366
384
|
def element_name(local_name, qname)
|
|
@@ -204,7 +204,7 @@ module Xlsxrb
|
|
|
204
204
|
def start_element(_uri, local_name, qname, _attributes)
|
|
205
205
|
name = element_name(local_name, qname)
|
|
206
206
|
case name
|
|
207
|
-
when "Application", "AppVersion"
|
|
207
|
+
when "Application", "AppVersion", "Company", "Manager"
|
|
208
208
|
@current_field = name
|
|
209
209
|
@text_buffer = +""
|
|
210
210
|
when "HeadingPairs"
|
|
@@ -232,6 +232,12 @@ module Xlsxrb
|
|
|
232
232
|
when "AppVersion"
|
|
233
233
|
@properties[:app_version] = @text_buffer.dup
|
|
234
234
|
@current_field = nil
|
|
235
|
+
when "Company"
|
|
236
|
+
@properties[:company] = @text_buffer.dup
|
|
237
|
+
@current_field = nil
|
|
238
|
+
when "Manager"
|
|
239
|
+
@properties[:manager] = @text_buffer.dup
|
|
240
|
+
@current_field = nil
|
|
235
241
|
when "lpstr"
|
|
236
242
|
if @inside_titles_of_parts
|
|
237
243
|
@titles_of_parts << @text_buffer.dup
|
|
@@ -956,11 +962,14 @@ module Xlsxrb
|
|
|
956
962
|
when "formula2"
|
|
957
963
|
@inside_formula2 = true
|
|
958
964
|
@text_buffer = +""
|
|
965
|
+
when "sqref"
|
|
966
|
+
@inside_sqref = true
|
|
967
|
+
@text_buffer = +""
|
|
959
968
|
end
|
|
960
969
|
end
|
|
961
970
|
|
|
962
971
|
def characters(text)
|
|
963
|
-
@text_buffer << text if @inside_formula1 || @inside_formula2
|
|
972
|
+
@text_buffer << text if @inside_formula1 || @inside_formula2 || @inside_sqref
|
|
964
973
|
end
|
|
965
974
|
|
|
966
975
|
def end_element(_uri, local_name, qname)
|
|
@@ -972,6 +981,9 @@ module Xlsxrb
|
|
|
972
981
|
when "formula2"
|
|
973
982
|
@current_dv[:formula2] = @text_buffer.dup if @current_dv
|
|
974
983
|
@inside_formula2 = false
|
|
984
|
+
when "sqref"
|
|
985
|
+
@current_dv[:sqref] = @text_buffer.strip if @current_dv && (@current_dv[:sqref].nil? || @current_dv[:sqref].empty?)
|
|
986
|
+
@inside_sqref = false
|
|
975
987
|
when "dataValidation"
|
|
976
988
|
@validations << @current_dv if @current_dv
|
|
977
989
|
@current_dv = nil
|
|
@@ -135,19 +135,19 @@ module Xlsxrb
|
|
|
135
135
|
when "font"
|
|
136
136
|
@current_font = {} if @inside_fonts || @inside_dxfs
|
|
137
137
|
when "b"
|
|
138
|
-
@current_font[:bold] =
|
|
138
|
+
@current_font[:bold] = truthy_val?(attributes) if @current_font
|
|
139
139
|
when "i"
|
|
140
|
-
@current_font[:italic] =
|
|
140
|
+
@current_font[:italic] = truthy_val?(attributes) if @current_font
|
|
141
141
|
when "strike"
|
|
142
|
-
@current_font[:strike] =
|
|
142
|
+
@current_font[:strike] = truthy_val?(attributes) if @current_font
|
|
143
143
|
when "shadow"
|
|
144
|
-
@current_font[:shadow] =
|
|
144
|
+
@current_font[:shadow] = truthy_val?(attributes) if @current_font
|
|
145
145
|
when "outline"
|
|
146
|
-
@current_font[:outline] =
|
|
146
|
+
@current_font[:outline] = truthy_val?(attributes) if @current_font
|
|
147
147
|
when "condense"
|
|
148
|
-
@current_font[:condense] =
|
|
148
|
+
@current_font[:condense] = truthy_val?(attributes) if @current_font
|
|
149
149
|
when "extend"
|
|
150
|
-
@current_font[:extend] =
|
|
150
|
+
@current_font[:extend] = truthy_val?(attributes) if @current_font
|
|
151
151
|
when "u"
|
|
152
152
|
if @current_font
|
|
153
153
|
val = attributes["val"]
|
|
@@ -303,6 +303,11 @@ module Xlsxrb
|
|
|
303
303
|
|
|
304
304
|
private
|
|
305
305
|
|
|
306
|
+
def truthy_val?(attributes)
|
|
307
|
+
val = attributes["val"]
|
|
308
|
+
val.nil? || (val != "0" && val.downcase != "false")
|
|
309
|
+
end
|
|
310
|
+
|
|
306
311
|
def parse_color(attributes)
|
|
307
312
|
if @inside_mru_colors
|
|
308
313
|
c = {}
|