xlsxrb 0.1.14 → 0.1.15
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 +47 -0
- data/README.md +15 -14
- data/lib/xlsxrb/elements/workbook.rb +36 -0
- data/lib/xlsxrb/elements/worksheet.rb +102 -7
- data/lib/xlsxrb/ooxml/reader/listeners/feature_listeners.rb +7 -1
- data/lib/xlsxrb/ooxml/reader.rb +2 -3
- data/lib/xlsxrb/ooxml/workbook_parser.rb +21 -1
- data/lib/xlsxrb/ooxml/workbook_writer.rb +17 -9
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +71 -19
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +1 -1
- data/lib/xlsxrb/ooxml/xml_builder.rb +14 -2
- data/lib/xlsxrb/ooxml/zip_reader.rb +35 -3
- data/lib/xlsxrb/stream_sheet.rb +234 -2
- data/lib/xlsxrb/stream_writer.rb +162 -17
- data/lib/xlsxrb/utils.rb +53 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +1 -10
- data/lib/xlsxrb/worksheet_builder.rb +512 -9
- data/lib/xlsxrb.rb +500 -9
- data/sig/generated/xlsxrb/elements/workbook.rbs +28 -0
- data/sig/generated/xlsxrb/elements/worksheet.rbs +53 -2
- data/sig/generated/xlsxrb/ooxml/workbook_writer.rbs +1 -1
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +10 -2
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +3 -1
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +3 -0
- data/sig/generated/xlsxrb/stream_sheet.rbs +111 -1
- data/sig/generated/xlsxrb/stream_writer.rbs +100 -16
- data/sig/generated/xlsxrb/utils.rbs +32 -0
- data/sig/generated/xlsxrb/worksheet_builder.rbs +249 -7
- data/sig/generated/xlsxrb.rbs +39 -2
- metadata +1 -1
|
@@ -47,7 +47,7 @@ module Xlsxrb
|
|
|
47
47
|
# split_pane: { x_split:, y_split:, top_left_cell: }
|
|
48
48
|
# selection: { active_cell:, sqref:, pane: }
|
|
49
49
|
# 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)
|
|
50
|
+
def start(columns: [], sheet_properties: nil, freeze_pane: nil, split_pane: nil, selection: nil, sheet_view: nil, sheet_format: nil)
|
|
51
51
|
return if @started
|
|
52
52
|
|
|
53
53
|
@started = true
|
|
@@ -75,6 +75,7 @@ module Xlsxrb
|
|
|
75
75
|
|
|
76
76
|
write_sheet_properties(sheet_properties) if sheet_properties && !sheet_properties.empty?
|
|
77
77
|
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?)
|
|
78
|
+
write_sheet_format_pr(sheet_format) if sheet_format && !sheet_format.empty?
|
|
78
79
|
write_columns(columns) unless columns.empty?
|
|
79
80
|
|
|
80
81
|
@builder.open_tag("sheetData")
|
|
@@ -88,7 +89,7 @@ module Xlsxrb
|
|
|
88
89
|
row_num_str = row_num.to_s
|
|
89
90
|
buf = @row_buffer ||= String.new(capacity: 65_536)
|
|
90
91
|
buf << '<row r="' << row_num_str << '"'
|
|
91
|
-
buf << ' s="' << attrs[:style_index].to_s << '" customFormat="1"' if attrs[:style_index]
|
|
92
|
+
buf << ' s="' << attrs[:style_index].to_s << '" customFormat="1"' if attrs[:style_index].is_a?(Integer)
|
|
92
93
|
if attrs[:height]
|
|
93
94
|
buf << ' ht="' << attrs[:height].to_s << '" customHeight="1"'
|
|
94
95
|
elsif attrs[:custom_height]
|
|
@@ -128,6 +129,7 @@ module Xlsxrb
|
|
|
128
129
|
|
|
129
130
|
col_style = @column_styles && col_idx ? @column_styles[col_idx] : nil
|
|
130
131
|
style_id ||= attrs[:style_index] || col_style
|
|
132
|
+
style_id = nil unless style_id.is_a?(Integer)
|
|
131
133
|
|
|
132
134
|
# Fast path for common unstyled cells
|
|
133
135
|
if !formula && !style_id && !cell_type_val
|
|
@@ -425,8 +427,7 @@ module Xlsxrb
|
|
|
425
427
|
end
|
|
426
428
|
|
|
427
429
|
is_styles_collection = styles && (styles.is_a?(Array) || styles.is_a?(Hash))
|
|
428
|
-
single_style_id =
|
|
429
|
-
single_style_id = style_map[styles] if styles && style_map && !is_styles_collection
|
|
430
|
+
single_style_id = resolve_style_id(styles, style_map) unless is_styles_collection
|
|
430
431
|
|
|
431
432
|
buf << '<row r="' << row_num_str << '"'
|
|
432
433
|
if attrs
|
|
@@ -444,7 +445,7 @@ module Xlsxrb
|
|
|
444
445
|
end
|
|
445
446
|
|
|
446
447
|
row_style = attrs ? attrs[:style_index] : nil
|
|
447
|
-
row_style_id =
|
|
448
|
+
row_style_id = resolve_style_id(row_style, style_map)
|
|
448
449
|
|
|
449
450
|
col_index = 0
|
|
450
451
|
while col_index < max_len
|
|
@@ -456,20 +457,12 @@ module Xlsxrb
|
|
|
456
457
|
else
|
|
457
458
|
styles[col_index]
|
|
458
459
|
end
|
|
459
|
-
style_id =
|
|
460
|
-
style_map[style_name] || style_map[style_name.to_s] || style_name
|
|
461
|
-
else
|
|
462
|
-
style_name
|
|
463
|
-
end
|
|
460
|
+
style_id = resolve_style_id(style_name, style_map)
|
|
464
461
|
end
|
|
465
462
|
|
|
466
463
|
if style_id.nil?
|
|
467
464
|
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
|
|
465
|
+
col_style_id = resolve_style_id(col_style, style_map)
|
|
473
466
|
style_id = row_style_id || col_style_id
|
|
474
467
|
end
|
|
475
468
|
|
|
@@ -638,7 +631,7 @@ module Xlsxrb
|
|
|
638
631
|
end
|
|
639
632
|
end
|
|
640
633
|
f_attrs << ' ca="1"' if formula_ca
|
|
641
|
-
buf << "
|
|
634
|
+
buf << "><f#{f_attrs}>" << escape_xml(formula_expr) << "</f>"
|
|
642
635
|
if xml_val
|
|
643
636
|
buf << "<v>" << xml_val.to_s << "</v></c>"
|
|
644
637
|
else
|
|
@@ -662,12 +655,12 @@ module Xlsxrb
|
|
|
662
655
|
|
|
663
656
|
# Write the worksheet footer. Call once after all rows.
|
|
664
657
|
# Options for post-sheetData elements (in OOXML order):
|
|
665
|
-
def finish(drawing_rid: nil, sheet_protection: nil, auto_filter: nil,
|
|
658
|
+
def finish(drawing_rid: nil, sheet_protection: nil, protected_ranges: nil, auto_filter: nil,
|
|
666
659
|
filter_columns: nil, sort_state: nil, merge_cells: nil,
|
|
667
660
|
conditional_formats: nil, data_validations: nil,
|
|
668
661
|
hyperlinks: nil, print_options: nil, page_margins: nil,
|
|
669
662
|
page_setup: nil, header_footer: nil, row_breaks: nil,
|
|
670
|
-
col_breaks: nil, tables: nil, table_start_rid: nil,
|
|
663
|
+
col_breaks: nil, cell_watches: nil, tables: nil, table_start_rid: nil,
|
|
671
664
|
legacy_drawing_rid: nil, sparkline_groups: nil)
|
|
672
665
|
return if @finished
|
|
673
666
|
|
|
@@ -681,6 +674,7 @@ module Xlsxrb
|
|
|
681
674
|
|
|
682
675
|
# Elements must appear in OOXML specification order after sheetData
|
|
683
676
|
write_sheet_protection(sheet_protection) if sheet_protection
|
|
677
|
+
write_protected_ranges(protected_ranges) if protected_ranges && !protected_ranges.empty?
|
|
684
678
|
write_auto_filter(auto_filter, filter_columns, sort_state) if auto_filter
|
|
685
679
|
write_merge_cells(merge_cells) if merge_cells && !merge_cells.empty?
|
|
686
680
|
write_conditional_formatting(conditional_formats) if conditional_formats && !conditional_formats.empty?
|
|
@@ -692,6 +686,7 @@ module Xlsxrb
|
|
|
692
686
|
write_header_footer(header_footer) if header_footer && !header_footer.empty?
|
|
693
687
|
write_row_breaks(row_breaks) if row_breaks && !row_breaks.empty?
|
|
694
688
|
write_col_breaks(col_breaks) if col_breaks && !col_breaks.empty?
|
|
689
|
+
write_cell_watches(cell_watches) if cell_watches && !cell_watches.empty?
|
|
695
690
|
@builder.empty_tag("drawing", { "r:id": drawing_rid }) if drawing_rid
|
|
696
691
|
@builder.empty_tag("legacyDrawing", { "r:id": legacy_drawing_rid }) if legacy_drawing_rid
|
|
697
692
|
write_table_parts(tables, table_start_rid) if tables && !tables.empty?
|
|
@@ -701,6 +696,17 @@ module Xlsxrb
|
|
|
701
696
|
|
|
702
697
|
private
|
|
703
698
|
|
|
699
|
+
def resolve_style_id(style, style_map)
|
|
700
|
+
return nil if style.nil?
|
|
701
|
+
|
|
702
|
+
if style_map
|
|
703
|
+
mapped = style_map[style] || style_map[style.to_s]
|
|
704
|
+
return mapped if mapped
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
style.is_a?(Integer) ? style : nil
|
|
708
|
+
end
|
|
709
|
+
|
|
704
710
|
# --- Pre-sheetData elements ---
|
|
705
711
|
|
|
706
712
|
def write_sheet_properties(props)
|
|
@@ -770,6 +776,22 @@ module Xlsxrb
|
|
|
770
776
|
@builder.close_tag("sheetViews")
|
|
771
777
|
end
|
|
772
778
|
|
|
779
|
+
def write_sheet_format_pr(fmt)
|
|
780
|
+
return if fmt.nil? || fmt.empty?
|
|
781
|
+
|
|
782
|
+
attrs = {}
|
|
783
|
+
attrs[:defaultRowHeight] = fmt[:default_row_height].to_s if fmt[:default_row_height]
|
|
784
|
+
attrs[:defaultColWidth] = fmt[:default_col_width].to_s if fmt[:default_col_width]
|
|
785
|
+
attrs[:baseColWidth] = fmt[:base_col_width].to_s if fmt[:base_col_width]
|
|
786
|
+
attrs[:outlineLevelRow] = fmt[:outline_level_row].to_s if fmt[:outline_level_row]
|
|
787
|
+
attrs[:outlineLevelCol] = fmt[:outline_level_col].to_s if fmt[:outline_level_col]
|
|
788
|
+
attrs[:customHeight] = "1" if fmt[:custom_height]
|
|
789
|
+
attrs[:zeroHeight] = "1" if fmt[:zero_height]
|
|
790
|
+
attrs[:thickTop] = "1" if fmt[:thick_top]
|
|
791
|
+
attrs[:thickBottom] = "1" if fmt[:thick_bottom]
|
|
792
|
+
@builder.empty_tag("sheetFormatPr", attrs) unless attrs.empty?
|
|
793
|
+
end
|
|
794
|
+
|
|
773
795
|
# --- Post-sheetData elements (in OOXML order) ---
|
|
774
796
|
|
|
775
797
|
def write_sheet_protection(opts)
|
|
@@ -798,6 +820,28 @@ module Xlsxrb
|
|
|
798
820
|
@builder.empty_tag("sheetProtection", attrs)
|
|
799
821
|
end
|
|
800
822
|
|
|
823
|
+
def write_protected_ranges(ranges)
|
|
824
|
+
@builder.open_tag("protectedRanges")
|
|
825
|
+
ranges.each do |pr|
|
|
826
|
+
attrs = { sqref: pr[:sqref].to_s, name: pr[:name].to_s }
|
|
827
|
+
attrs[:password] = pr[:password].to_s if pr[:password]
|
|
828
|
+
attrs[:algorithmName] = pr[:algorithm_name].to_s if pr[:algorithm_name]
|
|
829
|
+
attrs[:hashValue] = pr[:hash_value].to_s if pr[:hash_value]
|
|
830
|
+
attrs[:saltValue] = pr[:salt_value].to_s if pr[:salt_value]
|
|
831
|
+
attrs[:spinCount] = pr[:spin_count].to_s if pr[:spin_count]
|
|
832
|
+
attrs[:securityDescriptor] = pr[:security_descriptor].to_s if pr[:security_descriptor]
|
|
833
|
+
sds = pr[:security_descriptors] || []
|
|
834
|
+
if sds.empty?
|
|
835
|
+
@builder.empty_tag("protectedRange", attrs)
|
|
836
|
+
else
|
|
837
|
+
@builder.open_tag("protectedRange", attrs)
|
|
838
|
+
sds.each { |sd| @builder.tag("securityDescriptor") { @builder.text(sd.to_s) } }
|
|
839
|
+
@builder.close_tag("protectedRange")
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
@builder.close_tag("protectedRanges")
|
|
843
|
+
end
|
|
844
|
+
|
|
801
845
|
def write_auto_filter(range, filter_columns, sort_state)
|
|
802
846
|
if (filter_columns && !filter_columns.empty?) || sort_state
|
|
803
847
|
@builder.open_tag("autoFilter", { ref: range })
|
|
@@ -1133,6 +1177,14 @@ module Xlsxrb
|
|
|
1133
1177
|
@builder.close_tag("colBreaks")
|
|
1134
1178
|
end
|
|
1135
1179
|
|
|
1180
|
+
def write_cell_watches(watches)
|
|
1181
|
+
@builder.open_tag("cellWatches")
|
|
1182
|
+
watches.each do |w|
|
|
1183
|
+
@builder.empty_tag("cellWatch", { r: w.to_s })
|
|
1184
|
+
end
|
|
1185
|
+
@builder.close_tag("cellWatches")
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1136
1188
|
def write_table_parts(tables, start_rid)
|
|
1137
1189
|
@builder.open_tag("tableParts", { count: tables.size.to_s })
|
|
1138
1190
|
tables.each_with_index do |_tbl, idx|
|
|
@@ -1243,7 +1295,7 @@ module Xlsxrb
|
|
|
1243
1295
|
col_idx = cell[:column_index]
|
|
1244
1296
|
col_style = @column_styles && col_idx ? @column_styles[col_idx] : nil
|
|
1245
1297
|
style_id = cell[:style_index] || col_style
|
|
1246
|
-
attrs[:s] = style_id.to_s if style_id
|
|
1298
|
+
attrs[:s] = style_id.to_s if style_id.is_a?(Integer)
|
|
1247
1299
|
|
|
1248
1300
|
if value.nil? && formula.nil?
|
|
1249
1301
|
@builder.empty_tag("c", attrs)
|
|
@@ -715,7 +715,7 @@ module Xlsxrb
|
|
|
715
715
|
"<vt:i4>#{prop[:value]}</vt:i4>"
|
|
716
716
|
when :float, :r8
|
|
717
717
|
"<vt:r8>#{prop[:value]}</vt:r8>"
|
|
718
|
-
when :bool
|
|
718
|
+
when :bool, :boolean
|
|
719
719
|
"<vt:bool>#{prop[:value] ? "true" : "false"}</vt:bool>"
|
|
720
720
|
when :date, :filetime
|
|
721
721
|
"<vt:filetime>#{prop[:value]}</vt:filetime>"
|
|
@@ -118,14 +118,26 @@ module Xlsxrb
|
|
|
118
118
|
str.match?(ESCAPE_RE) ? str.gsub(ESCAPE_RE, ESCAPE_MAP) : str
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
NUM_ENTITY_RE = /&#(?:(\d+)|x([0-9a-fA-F]+));/
|
|
122
|
+
private_constant :NUM_ENTITY_RE
|
|
123
|
+
|
|
124
|
+
# Unescapes predefined and numeric/hex XML entities in a string.
|
|
122
125
|
#
|
|
123
126
|
# @param value [Object]
|
|
124
127
|
# @return [String]
|
|
125
128
|
#: (untyped value) -> String
|
|
126
129
|
def self.unescape(value)
|
|
127
130
|
str = value.to_s
|
|
128
|
-
str.include?("&")
|
|
131
|
+
return str unless str.include?("&")
|
|
132
|
+
|
|
133
|
+
res = str.gsub(UNESCAPE_RE, UNESCAPE_MAP)
|
|
134
|
+
res.gsub(NUM_ENTITY_RE) do |match|
|
|
135
|
+
dec = Regexp.last_match(1)
|
|
136
|
+
codepoint = dec ? Integer(dec) : Integer(Regexp.last_match(2), 16)
|
|
137
|
+
codepoint.chr(Encoding::UTF_8)
|
|
138
|
+
rescue RangeError
|
|
139
|
+
match
|
|
140
|
+
end
|
|
129
141
|
end
|
|
130
142
|
|
|
131
143
|
private
|
|
@@ -397,7 +397,7 @@ module Xlsxrb
|
|
|
397
397
|
cd_catalog = parse_catalog_from_central_directory(io)
|
|
398
398
|
return cd_catalog if cd_catalog && !cd_catalog.empty?
|
|
399
399
|
|
|
400
|
-
io.seek(
|
|
400
|
+
io.seek(0, IO::SEEK_SET) if io.respond_to?(:seek)
|
|
401
401
|
parse_catalog_from_local_headers(io)
|
|
402
402
|
end
|
|
403
403
|
|
|
@@ -437,7 +437,10 @@ module Xlsxrb
|
|
|
437
437
|
|
|
438
438
|
_v_made, _v_need, _gp, method, _time, _date, crc, csize, usize, nlen, elen, clen, _dnum, _iattr, _eattr, offset = cd_header.unpack("vvvvvvVVVvvvvvVV")
|
|
439
439
|
entry_name = io.read(nlen).force_encoding("UTF-8")
|
|
440
|
-
io.read(elen
|
|
440
|
+
extra_data = elen.positive? ? io.read(elen) : nil
|
|
441
|
+
io.read(clen) if clen.positive?
|
|
442
|
+
|
|
443
|
+
usize, csize, offset = parse_zip64_extra(extra_data, usize, csize, offset) if extra_data && (usize == 0xFFFF_FFFF || csize == 0xFFFF_FFFF || offset == 0xFFFF_FFFF)
|
|
441
444
|
|
|
442
445
|
norm_name = normalize_entry_name(entry_name)
|
|
443
446
|
next if norm_name.end_with?("/")
|
|
@@ -496,7 +499,8 @@ module Xlsxrb
|
|
|
496
499
|
extra_len = header[24, 2].unpack1("v")
|
|
497
500
|
|
|
498
501
|
entry_name = io.read(name_len).force_encoding("UTF-8")
|
|
499
|
-
io.read(extra_len)
|
|
502
|
+
extra_data = extra_len.positive? ? io.read(extra_len) : nil
|
|
503
|
+
uncompressed_size, compressed_size, = parse_zip64_extra(extra_data, uncompressed_size, compressed_size, 0) if extra_data && (uncompressed_size == 0xFFFF_FFFF || compressed_size == 0xFFFF_FFFF)
|
|
500
504
|
data_offset = io.pos
|
|
501
505
|
|
|
502
506
|
norm_name = normalize_entry_name(entry_name)
|
|
@@ -645,6 +649,34 @@ module Xlsxrb
|
|
|
645
649
|
data.force_encoding(Encoding::UTF_8)
|
|
646
650
|
end
|
|
647
651
|
end
|
|
652
|
+
|
|
653
|
+
#: (String extra_data, Integer usize, Integer csize, Integer offset) -> [Integer, Integer, Integer]
|
|
654
|
+
def parse_zip64_extra(extra_data, usize, csize, offset)
|
|
655
|
+
pos = 0
|
|
656
|
+
len = extra_data.bytesize
|
|
657
|
+
while pos + 4 <= len
|
|
658
|
+
header_id, data_size = extra_data.byteslice(pos, 4).unpack("vv")
|
|
659
|
+
pos += 4
|
|
660
|
+
if header_id == 0x0001
|
|
661
|
+
field_data = extra_data.byteslice(pos, data_size)
|
|
662
|
+
if field_data
|
|
663
|
+
fpos = 0
|
|
664
|
+
if usize == 0xFFFF_FFFF && fpos + 8 <= field_data.bytesize
|
|
665
|
+
usize = field_data.byteslice(fpos, 8).unpack1("Q<")
|
|
666
|
+
fpos += 8
|
|
667
|
+
end
|
|
668
|
+
if csize == 0xFFFF_FFFF && fpos + 8 <= field_data.bytesize
|
|
669
|
+
csize = field_data.byteslice(fpos, 8).unpack1("Q<")
|
|
670
|
+
fpos += 8
|
|
671
|
+
end
|
|
672
|
+
offset = field_data.byteslice(fpos, 8).unpack1("Q<") if offset == 0xFFFF_FFFF && fpos + 8 <= field_data.bytesize
|
|
673
|
+
end
|
|
674
|
+
break
|
|
675
|
+
end
|
|
676
|
+
pos += data_size
|
|
677
|
+
end
|
|
678
|
+
[usize, csize, offset]
|
|
679
|
+
end
|
|
648
680
|
end
|
|
649
681
|
end
|
|
650
682
|
end
|
data/lib/xlsxrb/stream_sheet.rb
CHANGED
|
@@ -314,7 +314,41 @@ module Xlsxrb
|
|
|
314
314
|
# @api public
|
|
315
315
|
#: () -> Elements::Worksheet
|
|
316
316
|
def load
|
|
317
|
-
Xlsxrb.send(
|
|
317
|
+
Xlsxrb.send(
|
|
318
|
+
:build_worksheet,
|
|
319
|
+
@name,
|
|
320
|
+
raw_sheet_xml,
|
|
321
|
+
@shared_strings,
|
|
322
|
+
@styles,
|
|
323
|
+
state: @state,
|
|
324
|
+
zip_reader: @zip_reader,
|
|
325
|
+
entry_name: @entry_name,
|
|
326
|
+
hyperlinks: hyperlinks,
|
|
327
|
+
comments: comments,
|
|
328
|
+
images: images,
|
|
329
|
+
date1904: @date1904,
|
|
330
|
+
dimension: dimension,
|
|
331
|
+
trim_empty_rows: @trim_empty_rows,
|
|
332
|
+
pad_empty_rows: @pad_empty_rows,
|
|
333
|
+
pad_empty_cells: @pad_empty_cells,
|
|
334
|
+
merged_cells: merged_cells,
|
|
335
|
+
auto_filter: auto_filter,
|
|
336
|
+
tables: tables,
|
|
337
|
+
shapes: shapes,
|
|
338
|
+
freeze_pane: freeze_pane,
|
|
339
|
+
page_setup: page_setup,
|
|
340
|
+
page_margins: page_margins,
|
|
341
|
+
header_footer: header_footer,
|
|
342
|
+
print_options: print_options,
|
|
343
|
+
sheet_protection: sheet_protection,
|
|
344
|
+
sheet_view: sheet_view,
|
|
345
|
+
sheet_properties: sheet_properties,
|
|
346
|
+
sheet_format: sheet_format,
|
|
347
|
+
row_breaks: row_breaks,
|
|
348
|
+
col_breaks: col_breaks,
|
|
349
|
+
protected_ranges: protected_ranges,
|
|
350
|
+
cell_watches: cell_watches
|
|
351
|
+
)
|
|
318
352
|
end
|
|
319
353
|
alias to_worksheet load
|
|
320
354
|
|
|
@@ -511,6 +545,169 @@ module Xlsxrb
|
|
|
511
545
|
end
|
|
512
546
|
end
|
|
513
547
|
|
|
548
|
+
# Returns tables defined in this worksheet.
|
|
549
|
+
#
|
|
550
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
551
|
+
# @api public
|
|
552
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
553
|
+
def tables
|
|
554
|
+
@tables ||= Xlsxrb.send(:resolve_tables, zip_reader: @zip_reader, entry_name: @entry_name)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# Returns shapes defined in this worksheet.
|
|
558
|
+
#
|
|
559
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
560
|
+
# @api public
|
|
561
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
562
|
+
def shapes
|
|
563
|
+
@shapes ||= Xlsxrb.send(:resolve_shapes, zip_reader: @zip_reader, entry_name: @entry_name)
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# Returns freeze pane settings for this worksheet, or nil if none.
|
|
567
|
+
#
|
|
568
|
+
# @return [Hash[Symbol, untyped], nil]
|
|
569
|
+
# @api public
|
|
570
|
+
#: () -> Hash[Symbol, untyped]?
|
|
571
|
+
def freeze_pane
|
|
572
|
+
sheet_view_data[:pane]
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Returns sheet view properties for this worksheet.
|
|
576
|
+
#
|
|
577
|
+
# @return [Hash[Symbol, untyped]]
|
|
578
|
+
# @api public
|
|
579
|
+
#: () -> Hash[Symbol, untyped]
|
|
580
|
+
def sheet_view
|
|
581
|
+
sheet_view_data[:view] || {}
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Returns sheet protection settings as a hash, or nil if unprotected.
|
|
585
|
+
#
|
|
586
|
+
# @return [Hash[Symbol, untyped], nil]
|
|
587
|
+
# @api public
|
|
588
|
+
#: () -> Hash[Symbol, untyped]?
|
|
589
|
+
def sheet_protection
|
|
590
|
+
@sheet_protection ||= if raw_sheet_xml.include?("<sheetProtection")
|
|
591
|
+
listener = Ooxml::Reader::SheetProtectionListener.new
|
|
592
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
593
|
+
listener.protection
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# Returns sheet-level properties (tabColor, outlinePr) for this worksheet.
|
|
598
|
+
#
|
|
599
|
+
# @return [Hash[Symbol, untyped]]
|
|
600
|
+
# @api public
|
|
601
|
+
#: () -> Hash[Symbol, untyped]
|
|
602
|
+
def sheet_properties
|
|
603
|
+
@sheet_properties ||= if raw_sheet_xml.include?("<sheetPr")
|
|
604
|
+
listener = Ooxml::Reader::SheetPropertiesListener.new
|
|
605
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
606
|
+
listener.properties
|
|
607
|
+
else
|
|
608
|
+
{}
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Returns sheet format properties (defaultRowHeight, defaultColWidth, baseColWidth) for this worksheet.
|
|
613
|
+
#
|
|
614
|
+
# @return [Hash[Symbol, untyped]]
|
|
615
|
+
# @api public
|
|
616
|
+
#: () -> Hash[Symbol, untyped]
|
|
617
|
+
def sheet_format
|
|
618
|
+
@sheet_format ||= if raw_sheet_xml.include?("<sheetFormatPr")
|
|
619
|
+
listener = Ooxml::Reader::SheetFormatListener.new
|
|
620
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
621
|
+
listener.properties
|
|
622
|
+
else
|
|
623
|
+
{}
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# Returns page setup configurations for this worksheet.
|
|
628
|
+
#
|
|
629
|
+
# @return [Hash[Symbol, untyped]]
|
|
630
|
+
# @api public
|
|
631
|
+
#: () -> Hash[Symbol, untyped]
|
|
632
|
+
def page_setup
|
|
633
|
+
print_page_data[:page_setup] || {}
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Returns page margins for this worksheet, or nil if none.
|
|
637
|
+
#
|
|
638
|
+
# @return [Hash[Symbol, untyped], nil]
|
|
639
|
+
# @api public
|
|
640
|
+
#: () -> Hash[Symbol, untyped]?
|
|
641
|
+
def page_margins
|
|
642
|
+
print_page_data[:page_margins]
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
# Returns header/footer configurations for this worksheet.
|
|
646
|
+
#
|
|
647
|
+
# @return [Hash[Symbol, untyped]]
|
|
648
|
+
# @api public
|
|
649
|
+
#: () -> Hash[Symbol, untyped]
|
|
650
|
+
def header_footer
|
|
651
|
+
print_page_data[:header_footer] || {}
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# Returns print options for this worksheet.
|
|
655
|
+
#
|
|
656
|
+
# @return [Hash[Symbol, untyped]]
|
|
657
|
+
# @api public
|
|
658
|
+
#: () -> Hash[Symbol, untyped]
|
|
659
|
+
def print_options
|
|
660
|
+
print_page_data[:print_options] || {}
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# Returns explicit row page breaks for this worksheet.
|
|
664
|
+
#
|
|
665
|
+
# @return [Array<untyped>]
|
|
666
|
+
# @api public
|
|
667
|
+
#: () -> Array[untyped]
|
|
668
|
+
def row_breaks
|
|
669
|
+
print_page_data[:row_breaks] || []
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# Returns explicit column page breaks for this worksheet.
|
|
673
|
+
#
|
|
674
|
+
# @return [Array<untyped>]
|
|
675
|
+
# @api public
|
|
676
|
+
#: () -> Array[untyped]
|
|
677
|
+
def col_breaks
|
|
678
|
+
print_page_data[:col_breaks] || []
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Returns protected ranges defined for this worksheet.
|
|
682
|
+
#
|
|
683
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
684
|
+
# @api public
|
|
685
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
686
|
+
def protected_ranges
|
|
687
|
+
@protected_ranges ||= if raw_sheet_xml.include?("<protectedRanges")
|
|
688
|
+
listener = Ooxml::Reader::ProtectedRangesListener.new
|
|
689
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
690
|
+
listener.ranges
|
|
691
|
+
else
|
|
692
|
+
[]
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Returns cell watch cell references defined for this worksheet.
|
|
697
|
+
#
|
|
698
|
+
# @return [Array<String>]
|
|
699
|
+
# @api public
|
|
700
|
+
#: () -> Array[String]
|
|
701
|
+
def cell_watches
|
|
702
|
+
@cell_watches ||= if raw_sheet_xml.include?("<cellWatches")
|
|
703
|
+
listener = Ooxml::Reader::CellWatchesListener.new
|
|
704
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
705
|
+
listener.watches
|
|
706
|
+
else
|
|
707
|
+
[]
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
514
711
|
# Closes any underlying reader resources.
|
|
515
712
|
#: () -> void
|
|
516
713
|
def close
|
|
@@ -520,7 +717,42 @@ module Xlsxrb
|
|
|
520
717
|
|
|
521
718
|
private
|
|
522
719
|
|
|
523
|
-
|
|
720
|
+
#: () -> Hash[Symbol, untyped]
|
|
721
|
+
def sheet_view_data
|
|
722
|
+
@sheet_view_data ||= if raw_sheet_xml.include?("<sheetView")
|
|
723
|
+
listener = Ooxml::Reader::SheetViewListener.new
|
|
724
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
725
|
+
{ view: listener.view, pane: listener.pane, selection: listener.selection }
|
|
726
|
+
else
|
|
727
|
+
{ view: {}, pane: nil, selection: nil }
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
#: () -> Hash[Symbol, untyped]
|
|
732
|
+
def print_page_data
|
|
733
|
+
@print_page_data ||= if raw_sheet_xml.match?(/<(?:pageSetup|pageMargins|headerFooter|printOptions|rowBreaks|colBreaks)\b/)
|
|
734
|
+
listener = Ooxml::Reader::PrintPageListener.new
|
|
735
|
+
Ooxml::XmlParser.parse(raw_sheet_xml, listener)
|
|
736
|
+
{
|
|
737
|
+
page_margins: listener.page_margins,
|
|
738
|
+
page_setup: listener.page_setup,
|
|
739
|
+
header_footer: listener.header_footer,
|
|
740
|
+
print_options: listener.print_options,
|
|
741
|
+
row_breaks: listener.row_breaks,
|
|
742
|
+
col_breaks: listener.col_breaks
|
|
743
|
+
}
|
|
744
|
+
else
|
|
745
|
+
{
|
|
746
|
+
page_margins: nil,
|
|
747
|
+
page_setup: {},
|
|
748
|
+
header_footer: {},
|
|
749
|
+
print_options: {},
|
|
750
|
+
row_breaks: [],
|
|
751
|
+
col_breaks: []
|
|
752
|
+
}
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
|
|
524
756
|
#: () -> String
|
|
525
757
|
def raw_sheet_xml
|
|
526
758
|
@raw_sheet_xml ||= if @sheet_source.is_a?(String)
|