xlsxrb 0.1.12 → 0.1.14
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 +116 -42
- data/README.md +28 -22
- data/lib/xlsxrb/colors.rb +118 -0
- data/lib/xlsxrb/dsl_helpers.rb +59 -0
- data/lib/xlsxrb/elements/cell.rb +205 -16
- data/lib/xlsxrb/elements/column.rb +6 -4
- data/lib/xlsxrb/elements/coordinate_access.rb +32 -0
- data/lib/xlsxrb/elements/image.rb +128 -0
- data/lib/xlsxrb/elements/row.rb +206 -9
- data/lib/xlsxrb/elements/styles.rb +157 -0
- data/lib/xlsxrb/elements/types.rb +95 -1
- data/lib/xlsxrb/elements/workbook.rb +96 -4
- data/lib/xlsxrb/elements/worksheet.rb +424 -29
- data/lib/xlsxrb/elements.rb +2 -0
- data/lib/xlsxrb/number_formatter.rb +429 -0
- data/lib/xlsxrb/ooxml/cfb.rb +1 -1
- data/lib/xlsxrb/ooxml/reader/listeners/core_listeners.rb +7 -5
- data/lib/xlsxrb/ooxml/reader/listeners/drawing_listeners.rb +11 -4
- data/lib/xlsxrb/ooxml/reader.rb +12 -14
- data/lib/xlsxrb/ooxml/shared_strings_parser.rb +10 -6
- data/lib/xlsxrb/ooxml/styles_parser.rb +39 -9
- data/lib/xlsxrb/ooxml/utils.rb +34 -18
- data/lib/xlsxrb/ooxml/workbook_parser.rb +53 -15
- data/lib/xlsxrb/ooxml/workbook_writer.rb +70 -21
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +466 -63
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +206 -61
- data/lib/xlsxrb/ooxml/writer/features_xml.rb +4 -2
- data/lib/xlsxrb/ooxml/writer.rb +16 -1
- data/lib/xlsxrb/ooxml/xml_builder.rb +20 -0
- data/lib/xlsxrb/ooxml/zip_reader.rb +452 -82
- data/lib/xlsxrb/ooxml/zip_writer.rb +32 -5
- data/lib/xlsxrb/stream_row.rb +243 -16
- data/lib/xlsxrb/stream_sheet.rb +504 -17
- data/lib/xlsxrb/stream_writer.rb +246 -83
- data/lib/xlsxrb/style_builder.rb +308 -22
- data/lib/xlsxrb/utils.rb +158 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +74 -4
- data/lib/xlsxrb/worksheet_builder.rb +230 -47
- data/lib/xlsxrb.rb +1005 -229
- data/sig/generated/xlsxrb/colors.rbs +40 -0
- data/sig/generated/xlsxrb/dsl_helpers.rbs +32 -0
- data/sig/generated/xlsxrb/elements/cell.rbs +113 -8
- data/sig/generated/xlsxrb/elements/column.rbs +3 -2
- data/sig/generated/xlsxrb/elements/coordinate_access.rbs +18 -0
- data/sig/generated/xlsxrb/elements/image.rbs +82 -0
- data/sig/generated/xlsxrb/elements/row.rbs +83 -4
- data/sig/generated/xlsxrb/elements/styles.rbs +79 -0
- data/sig/generated/xlsxrb/elements/types.rbs +43 -0
- data/sig/generated/xlsxrb/elements/workbook.rbs +58 -2
- data/sig/generated/xlsxrb/elements/worksheet.rbs +231 -18
- data/sig/generated/xlsxrb/number_formatter.rbs +132 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +6 -0
- data/sig/generated/xlsxrb/ooxml/shared_strings_parser.rbs +2 -2
- data/sig/generated/xlsxrb/ooxml/styles_parser.rbs +5 -2
- data/sig/generated/xlsxrb/ooxml/utils.rbs +17 -8
- data/sig/generated/xlsxrb/ooxml/workbook_parser.rbs +9 -3
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +31 -8
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +3 -1
- data/sig/generated/xlsxrb/ooxml/writer.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/xml_builder.rbs +11 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +106 -8
- data/sig/generated/xlsxrb/ooxml/zip_writer.rbs +5 -1
- data/sig/generated/xlsxrb/stream_row.rbs +100 -6
- data/sig/generated/xlsxrb/stream_sheet.rbs +288 -14
- data/sig/generated/xlsxrb/stream_writer.rbs +120 -14
- data/sig/generated/xlsxrb/style_builder.rbs +55 -18
- data/sig/generated/xlsxrb/utils.rbs +119 -0
- data/sig/generated/xlsxrb/workbook_builder.rbs +26 -0
- data/sig/generated/xlsxrb/worksheet_builder.rbs +107 -11
- data/sig/generated/xlsxrb.rbs +202 -36
- metadata +16 -146
- data/.devcontainer/Dockerfile +0 -65
- data/.devcontainer/devcontainer.json +0 -17
- data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
- data/.gem_rbs_collection/concurrent-ruby/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/concurrent-ruby/1.1/array.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/atomic_reference.rbs +0 -16
- data/.gem_rbs_collection/concurrent-ruby/1.1/executor.rbs +0 -96
- data/.gem_rbs_collection/concurrent-ruby/1.1/hash.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/map.rbs +0 -68
- data/.gem_rbs_collection/concurrent-ruby/1.1/promises.rbs +0 -249
- data/.gem_rbs_collection/concurrent-ruby/1.1/set.rbs +0 -4
- data/.gem_rbs_collection/concurrent-ruby/1.1/timer_task.rbs +0 -47
- data/.gem_rbs_collection/concurrent-ruby/1.1/utility/processor_counter.rbs +0 -5
- data/.gem_rbs_collection/csv/3.3/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/csv/3.3/csv.rbs +0 -3871
- data/.gem_rbs_collection/csv/3.3/manifest.yaml +0 -3
- data/.gem_rbs_collection/lint_roller/1.1/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/lint_roller/1.1/lint_roller.rbs +0 -48
- data/.gem_rbs_collection/listen/3.9/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/listen/3.9/listen.rbs +0 -25
- data/.gem_rbs_collection/listen/3.9/listener.rbs +0 -24
- data/.gem_rbs_collection/logger/1.7/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/logger/1.7/formatter.rbs +0 -45
- data/.gem_rbs_collection/logger/1.7/log_device.rbs +0 -100
- data/.gem_rbs_collection/logger/1.7/logger.rbs +0 -796
- data/.gem_rbs_collection/logger/1.7/manifest.yaml +0 -2
- data/.gem_rbs_collection/logger/1.7/period.rbs +0 -17
- data/.gem_rbs_collection/logger/1.7/severity.rbs +0 -34
- data/.gem_rbs_collection/nokogiri/1.11/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/nokogiri/1.11/nokogiri.rbs +0 -2332
- data/.gem_rbs_collection/nokogiri/1.11/patch.rbs +0 -4
- data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
- data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
- data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -194
- data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
- data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
- data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
- data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
- data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
- data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
- data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
- data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -208
- data/.gem_rbs_collection/rubocop-ast/1.46/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop-ast/1.46/rubocop-ast.rbs +0 -903
- data/.gem_rbs_collection/rubyzip/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/manifest.yaml +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/central_directory.rbs +0 -42
- data/.gem_rbs_collection/rubyzip/3.2/zip/compressor.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/constants.rbs +0 -47
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/aes_encryption.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/decrypted_io.rbs +0 -9
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/encryption.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/null_encryption.rbs +0 -19
- data/.gem_rbs_collection/rubyzip/3.2/zip/crypto/traditional_encryption.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/decompressor.rbs +0 -18
- data/.gem_rbs_collection/rubyzip/3.2/zip/deflater.rbs +0 -12
- data/.gem_rbs_collection/rubyzip/3.2/zip/dirtyable.rbs +0 -11
- data/.gem_rbs_collection/rubyzip/3.2/zip/dos_time.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry.rbs +0 -95
- data/.gem_rbs_collection/rubyzip/3.2/zip/entry_set.rbs +0 -31
- data/.gem_rbs_collection/rubyzip/3.2/zip/errors.rbs +0 -58
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/aes.rbs +0 -24
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/generic.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/ntfs.rbs +0 -23
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/old_unix.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/universal_time.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unix.rbs +0 -20
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/unknown.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field/zip64.rbs +0 -26
- data/.gem_rbs_collection/rubyzip/3.2/zip/extra_field.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/file.rbs +0 -131
- data/.gem_rbs_collection/rubyzip/3.2/zip/file_split.rbs +0 -14
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/dir.rbs +0 -33
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/directory_iterator.rbs +0 -21
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file.rbs +0 -63
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/file_stat.rbs +0 -55
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem/zip_file_name_mapper.rbs +0 -35
- data/.gem_rbs_collection/rubyzip/3.2/zip/filesystem.rbs +0 -7
- data/.gem_rbs_collection/rubyzip/3.2/zip/inflater.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/input_stream.rbs +0 -22
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_input_stream.rbs +0 -29
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras/abstract_output_stream.rbs +0 -17
- data/.gem_rbs_collection/rubyzip/3.2/zip/ioextras.rbs +0 -13
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_decompressor.rbs +0 -8
- data/.gem_rbs_collection/rubyzip/3.2/zip/null_input_stream.rbs +0 -6
- data/.gem_rbs_collection/rubyzip/3.2/zip/output_stream.rbs +0 -30
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_compressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/pass_thru_decompressor.rbs +0 -10
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_directory.rbs +0 -5
- data/.gem_rbs_collection/rubyzip/3.2/zip/streamable_stream.rbs +0 -15
- data/.gem_rbs_collection/rubyzip/3.2/zip/version.rbs +0 -3
- data/.gem_rbs_collection/rubyzip/3.2/zip.rbs +0 -40
- data/.mutant.yml +0 -8
- data/Rakefile +0 -633
- data/Steepfile +0 -18
- data/benchmark.rb +0 -406
- data/docs/ARCHITECTURE.md +0 -514
- data/docs/DEVELOPMENT.md +0 -137
- data/docs/MUTATION_TESTING.md +0 -52
- data/docs/PEER_LIBRARIES.md +0 -121
- data/docs/QUALITY_ASSURANCE.md +0 -26
- data/docs/SPEC_SOURCES.md +0 -48
- data/docs/assets/benchmark_results.svg +0 -144
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/coi-serviceworker.js +0 -82
- data/docs/office_thread.js +0 -77
- data/docs/preview.html +0 -719
- data/docs/visual/VisualGallery.md +0 -4683
- data/docs/wasm/ruby.wasm +0 -0
- data/docs/wasm/wasm_doc_helper.css +0 -351
- data/docs/wasm/wasm_doc_helper.js +0 -439
- data/docs/zeta.js +0 -1107
- data/rbs_collection.lock.yaml +0 -252
- data/rbs_collection.yaml +0 -19
- data/vendor/sdk_runner/Program.cs +0 -93
- data/vendor/sdk_runner/sdk_runner.csproj +0 -13
data/lib/xlsxrb/elements/row.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Xlsxrb
|
|
|
14
14
|
#
|
|
15
15
|
# @api public
|
|
16
16
|
# rubocop:disable Style/DataInheritance -- Required as class syntax for mutant subject matcher
|
|
17
|
-
class Row < Data.define(:index, :cells, :height, :hidden, :custom_height, :outline_level, :unmapped_data, :errors)
|
|
17
|
+
class Row < Data.define(:index, :cells, :height, :hidden, :custom_height, :collapsed, :outline_level, :unmapped_data, :errors)
|
|
18
18
|
# rubocop:enable Style/DataInheritance
|
|
19
19
|
[Enumerable].each { |m| include m }
|
|
20
20
|
|
|
@@ -23,17 +23,18 @@ module Xlsxrb
|
|
|
23
23
|
# @param height [Float, Integer, nil] Row height in points.
|
|
24
24
|
# @param hidden [Boolean] Whether the row is hidden.
|
|
25
25
|
# @param custom_height [Boolean] Whether custom height is set.
|
|
26
|
+
# @param collapsed [Boolean] Whether the row is collapsed.
|
|
26
27
|
# @param outline_level [Integer, nil] Grouping/outline level.
|
|
27
28
|
# @param unmapped_data [Hash] Additional metadata.
|
|
28
29
|
# @param errors [Array<String>, nil] Validation errors.
|
|
29
|
-
#: (index: Integer, ?cells: Array[Elements::Cell], ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil, ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?) -> void
|
|
30
|
-
def initialize(index:, cells: EMPTY_CELLS, height: nil, hidden: false, custom_height: false,
|
|
31
|
-
unmapped_data: EMPTY_HASH, errors: nil)
|
|
30
|
+
#: (index: Integer, ?cells: Array[Elements::Cell], ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?collapsed: bool, ?outline_level: Integer | nil, ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?) -> void
|
|
31
|
+
def initialize(index:, cells: EMPTY_CELLS, height: nil, hidden: false, custom_height: false, collapsed: false,
|
|
32
|
+
outline_level: nil, unmapped_data: EMPTY_HASH, errors: nil)
|
|
32
33
|
computed_errors = errors || self.class.validate(index, cells)
|
|
33
34
|
computed_errors = computed_errors.freeze unless computed_errors.frozen?
|
|
34
35
|
cells = cells.freeze unless cells.frozen?
|
|
35
36
|
super(index: index, cells: cells, height: height, hidden: hidden,
|
|
36
|
-
custom_height: custom_height, outline_level: outline_level,
|
|
37
|
+
custom_height: custom_height, collapsed: collapsed, outline_level: outline_level,
|
|
37
38
|
unmapped_data: unmapped_data, errors: computed_errors)
|
|
38
39
|
end
|
|
39
40
|
|
|
@@ -53,18 +54,144 @@ module Xlsxrb
|
|
|
53
54
|
when Symbol
|
|
54
55
|
case col_index
|
|
55
56
|
when :cells then cells
|
|
57
|
+
when :values then values
|
|
58
|
+
when :raw_values then raw_values
|
|
59
|
+
when :formatted_values then formatted_values
|
|
60
|
+
when :formulas then formulas
|
|
56
61
|
when :index then index
|
|
57
62
|
when :height then height
|
|
58
63
|
when :hidden then hidden
|
|
59
64
|
when :custom_height then custom_height
|
|
65
|
+
when :collapsed then collapsed
|
|
60
66
|
when :outline_level then outline_level
|
|
61
|
-
when :
|
|
67
|
+
when :style_index then style_index
|
|
68
|
+
when :hidden? then hidden?
|
|
69
|
+
when :collapsed? then collapsed?
|
|
70
|
+
when :custom_height? then custom_height?
|
|
71
|
+
when :attributes then attributes
|
|
72
|
+
when :cells_hash then cells_hash
|
|
73
|
+
when :attrs
|
|
74
|
+
h = { height: height, hidden: hidden, custom_height: custom_height, collapsed: collapsed, outline_level: outline_level }
|
|
75
|
+
h[:style_index] = style_index if style_index
|
|
76
|
+
h
|
|
62
77
|
end
|
|
63
78
|
else
|
|
64
79
|
cells[col_index]
|
|
65
80
|
end
|
|
66
81
|
end
|
|
67
82
|
|
|
83
|
+
# Returns a new Row with missing cells padded up to the maximum column index.
|
|
84
|
+
#
|
|
85
|
+
# @return [Elements::Row]
|
|
86
|
+
# @api public
|
|
87
|
+
#: () -> Elements::Row
|
|
88
|
+
def pad_empty_cells
|
|
89
|
+
return self if cells.empty?
|
|
90
|
+
|
|
91
|
+
max_col = cells.map(&:column_index).max || 0
|
|
92
|
+
return self if cells.size == max_col + 1
|
|
93
|
+
|
|
94
|
+
is_d1904 = cells.first ? cells.first.date1904? : false
|
|
95
|
+
cell_map = {}
|
|
96
|
+
cells.each { |c| cell_map[c.column_index] = c }
|
|
97
|
+
padded = Array.new(max_col + 1)
|
|
98
|
+
(0..max_col).each do |c_idx|
|
|
99
|
+
padded[c_idx] = cell_map[c_idx] || Cell.fast_create(index, c_idx, nil, nil, nil, nil, nil, is_d1904)
|
|
100
|
+
end
|
|
101
|
+
Row.new(
|
|
102
|
+
index: index,
|
|
103
|
+
cells: padded,
|
|
104
|
+
height: height,
|
|
105
|
+
hidden: hidden,
|
|
106
|
+
custom_height: custom_height,
|
|
107
|
+
collapsed: collapsed,
|
|
108
|
+
outline_level: outline_level,
|
|
109
|
+
unmapped_data: unmapped_data,
|
|
110
|
+
errors: errors
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Returns a coordinate-keyed Hash of cell values (e.g. {"A1" => "value", "B1" => 123}).
|
|
115
|
+
#
|
|
116
|
+
# @param type_cast [Boolean] Whether to coerce date/time serial numbers into Date/Time instances.
|
|
117
|
+
# @return [Hash{String => Object}]
|
|
118
|
+
# @api public
|
|
119
|
+
#: (?type_cast: bool) -> Hash[String, untyped]
|
|
120
|
+
def cells_hash(type_cast: false)
|
|
121
|
+
result = {}
|
|
122
|
+
cells.each do |c|
|
|
123
|
+
val = c.value
|
|
124
|
+
if type_cast && val.is_a?(Numeric) && c.format_code
|
|
125
|
+
fmt_type = NumberFormatter.format_type(c.format_code)
|
|
126
|
+
begin
|
|
127
|
+
case fmt_type
|
|
128
|
+
when :date
|
|
129
|
+
val = Ooxml::Utils.serial_to_date(val, date1904: c.date1904?)
|
|
130
|
+
when :datetime, :time
|
|
131
|
+
val = Ooxml::Utils.serial_to_datetime(val, date1904: c.date1904?)
|
|
132
|
+
end
|
|
133
|
+
rescue StandardError
|
|
134
|
+
# Keep val numeric on failure
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
result[c.ref] = val
|
|
138
|
+
end
|
|
139
|
+
result
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Returns whether the row is hidden.
|
|
143
|
+
#
|
|
144
|
+
# @return [Boolean]
|
|
145
|
+
# @api public
|
|
146
|
+
#: () -> bool
|
|
147
|
+
def hidden?
|
|
148
|
+
hidden == true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Returns whether the row is collapsed.
|
|
152
|
+
#
|
|
153
|
+
# @return [Boolean]
|
|
154
|
+
# @api public
|
|
155
|
+
#: () -> bool
|
|
156
|
+
def collapsed?
|
|
157
|
+
collapsed == true
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Returns whether custom height is set.
|
|
161
|
+
#
|
|
162
|
+
# @return [Boolean]
|
|
163
|
+
# @api public
|
|
164
|
+
#: () -> bool
|
|
165
|
+
def custom_height?
|
|
166
|
+
custom_height == true
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns a frozen Hash of row attributes.
|
|
170
|
+
#
|
|
171
|
+
# @return [Hash{Symbol => Object}]
|
|
172
|
+
# @api public
|
|
173
|
+
#: () -> Hash[Symbol, untyped]
|
|
174
|
+
def attributes
|
|
175
|
+
h = {
|
|
176
|
+
index: index,
|
|
177
|
+
height: height,
|
|
178
|
+
hidden: hidden,
|
|
179
|
+
custom_height: custom_height,
|
|
180
|
+
collapsed: collapsed,
|
|
181
|
+
outline_level: outline_level
|
|
182
|
+
}
|
|
183
|
+
h[:style_index] = style_index if style_index
|
|
184
|
+
h.freeze
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns style_index if present in unmapped_data.
|
|
188
|
+
#
|
|
189
|
+
# @return [Integer, nil]
|
|
190
|
+
#: () -> Integer?
|
|
191
|
+
def style_index
|
|
192
|
+
unmapped_data[:style_index]
|
|
193
|
+
end
|
|
194
|
+
|
|
68
195
|
# Iterate over cells in this row.
|
|
69
196
|
#
|
|
70
197
|
# @example
|
|
@@ -130,6 +257,17 @@ module Xlsxrb
|
|
|
130
257
|
errors.empty?
|
|
131
258
|
end
|
|
132
259
|
|
|
260
|
+
# Returns whether the row contains no cell values (or all cell values are nil or empty strings).
|
|
261
|
+
#
|
|
262
|
+
# @return [Boolean]
|
|
263
|
+
# @api public
|
|
264
|
+
#: () -> bool
|
|
265
|
+
def empty?
|
|
266
|
+
return true if cells.empty?
|
|
267
|
+
|
|
268
|
+
cells.all? { |c| c.value.nil? || (c.value.is_a?(String) && c.value.empty?) }
|
|
269
|
+
end
|
|
270
|
+
|
|
133
271
|
# Returns the cell at the given 0-based column index, or nil.
|
|
134
272
|
#
|
|
135
273
|
# @param column_index [Integer] 0-based column index.
|
|
@@ -142,15 +280,74 @@ module Xlsxrb
|
|
|
142
280
|
|
|
143
281
|
# Returns cell values as an Array (sparse columns get nil).
|
|
144
282
|
#
|
|
283
|
+
# @param type_cast [Boolean] Whether to coerce date/time serial numbers into Date/Time instances.
|
|
145
284
|
# @return [Array<Object>]
|
|
146
285
|
# @api public
|
|
147
|
-
#: () -> Array[untyped]
|
|
148
|
-
def values
|
|
286
|
+
#: (?type_cast: bool) -> Array[untyped]
|
|
287
|
+
def values(type_cast: false)
|
|
288
|
+
return [] if cells.empty?
|
|
289
|
+
|
|
290
|
+
max_col = cells.max_by(&:column_index)&.column_index || 0
|
|
291
|
+
result = Array.new(max_col + 1)
|
|
292
|
+
cells.each do |c|
|
|
293
|
+
val = c.value
|
|
294
|
+
if type_cast && val.is_a?(Numeric) && c.format_code
|
|
295
|
+
fmt_type = NumberFormatter.format_type(c.format_code)
|
|
296
|
+
begin
|
|
297
|
+
case fmt_type
|
|
298
|
+
when :date
|
|
299
|
+
val = Ooxml::Utils.serial_to_date(val, date1904: c.date1904?)
|
|
300
|
+
when :datetime, :time
|
|
301
|
+
val = Ooxml::Utils.serial_to_datetime(val, date1904: c.date1904?)
|
|
302
|
+
end
|
|
303
|
+
rescue StandardError
|
|
304
|
+
# Keep val numeric on failure
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
result[c.column_index] = val
|
|
308
|
+
end
|
|
309
|
+
result
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Returns unparsed raw cell values as an Array (sparse columns get nil).
|
|
313
|
+
#
|
|
314
|
+
# @return [Array<String, nil>]
|
|
315
|
+
# @api public
|
|
316
|
+
#: () -> Array[String?]
|
|
317
|
+
def raw_values
|
|
318
|
+
return [] if cells.empty?
|
|
319
|
+
|
|
320
|
+
max_col = cells.max_by(&:column_index).column_index
|
|
321
|
+
result = Array.new(max_col + 1)
|
|
322
|
+
cells.each { |c| result[c.column_index] = c.raw_value }
|
|
323
|
+
result
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Returns formatted string representations of cell values as an Array (sparse columns get nil).
|
|
327
|
+
#
|
|
328
|
+
# @return [Array<String, nil>]
|
|
329
|
+
# @api public
|
|
330
|
+
#: () -> Array[String?]
|
|
331
|
+
def formatted_values
|
|
332
|
+
return [] if cells.empty?
|
|
333
|
+
|
|
334
|
+
max_col = cells.max_by(&:column_index).column_index
|
|
335
|
+
result = Array.new(max_col + 1)
|
|
336
|
+
cells.each { |c| result[c.column_index] = c.formatted_value }
|
|
337
|
+
result
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Returns formula expressions without leading '=' as an Array (sparse columns get nil).
|
|
341
|
+
#
|
|
342
|
+
# @return [Array<String, nil>]
|
|
343
|
+
# @api public
|
|
344
|
+
#: () -> Array[String?]
|
|
345
|
+
def formulas
|
|
149
346
|
return [] if cells.empty?
|
|
150
347
|
|
|
151
348
|
max_col = cells.max_by(&:column_index).column_index
|
|
152
349
|
result = Array.new(max_col + 1)
|
|
153
|
-
cells.each { |c| result[c.column_index] = c.
|
|
350
|
+
cells.each { |c| result[c.column_index] = c.formula_expression }
|
|
154
351
|
result
|
|
155
352
|
end
|
|
156
353
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
require_relative "../number_formatter"
|
|
6
|
+
require_relative "../ooxml/utils"
|
|
7
|
+
|
|
8
|
+
module Xlsxrb
|
|
9
|
+
module Elements
|
|
10
|
+
# Represents the workbook stylesheet definitions (fonts, fills, borders, cellXfs, and numFmts).
|
|
11
|
+
# Inherits from Hash for backward compatibility with existing styles hash structures,
|
|
12
|
+
# while providing precomputed O(1) number format classifications.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class Styles < Hash #[untyped, untyped]
|
|
16
|
+
# Initializes the styles container, optionally wrapping an existing styles hash.
|
|
17
|
+
#
|
|
18
|
+
# @param initial_hash [Hash, nil] Initial styles dictionary.
|
|
19
|
+
#: (?Hash[untyped, untyped]? initial_hash) -> void
|
|
20
|
+
def initialize(initial_hash = nil)
|
|
21
|
+
super()
|
|
22
|
+
merge!(initial_hash) if initial_hash.is_a?(Hash)
|
|
23
|
+
@format_codes = nil
|
|
24
|
+
@format_types = nil
|
|
25
|
+
@date_flags = nil
|
|
26
|
+
@datetime_flags = nil
|
|
27
|
+
@time_flags = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the format code string for a given style index.
|
|
31
|
+
#
|
|
32
|
+
# @param style_index [Integer, nil]
|
|
33
|
+
# @return [String, nil]
|
|
34
|
+
# @api public
|
|
35
|
+
#: (Integer? style_index) -> String?
|
|
36
|
+
def number_format(style_index)
|
|
37
|
+
return nil unless style_index.is_a?(Integer) && style_index >= 0
|
|
38
|
+
|
|
39
|
+
precompute! unless @format_codes
|
|
40
|
+
@format_codes[style_index]
|
|
41
|
+
end
|
|
42
|
+
alias format_code number_format
|
|
43
|
+
alias format_code_for number_format
|
|
44
|
+
|
|
45
|
+
# Returns the classified format category (:date, :datetime, :time, :number, :text, :general) for a style index.
|
|
46
|
+
#
|
|
47
|
+
# @param style_index [Integer, nil]
|
|
48
|
+
# @return [Symbol, nil]
|
|
49
|
+
# @api public
|
|
50
|
+
#: (Integer? style_index) -> Symbol?
|
|
51
|
+
def format_type(style_index)
|
|
52
|
+
return nil unless style_index.is_a?(Integer) && style_index >= 0
|
|
53
|
+
|
|
54
|
+
precompute! unless @format_types
|
|
55
|
+
@format_types[style_index]
|
|
56
|
+
end
|
|
57
|
+
alias classification_for format_type
|
|
58
|
+
|
|
59
|
+
# Returns whether the style index represents a date, datetime, or time pattern.
|
|
60
|
+
#
|
|
61
|
+
# @param style_index [Integer, nil]
|
|
62
|
+
# @return [Boolean]
|
|
63
|
+
# @api public
|
|
64
|
+
#: (Integer? style_index) -> bool
|
|
65
|
+
def date_format?(style_index)
|
|
66
|
+
return false unless style_index.is_a?(Integer) && style_index >= 0
|
|
67
|
+
|
|
68
|
+
precompute! unless @date_flags
|
|
69
|
+
@date_flags[style_index] || false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns whether the style index represents a datetime pattern.
|
|
73
|
+
#
|
|
74
|
+
# @param style_index [Integer, nil]
|
|
75
|
+
# @return [Boolean]
|
|
76
|
+
# @api public
|
|
77
|
+
#: (Integer? style_index) -> bool
|
|
78
|
+
def datetime_format?(style_index)
|
|
79
|
+
return false unless style_index.is_a?(Integer) && style_index >= 0
|
|
80
|
+
|
|
81
|
+
precompute! unless @datetime_flags
|
|
82
|
+
@datetime_flags[style_index] || false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns whether the style index represents a time pattern.
|
|
86
|
+
#
|
|
87
|
+
# @param style_index [Integer, nil]
|
|
88
|
+
# @return [Boolean]
|
|
89
|
+
# @api public
|
|
90
|
+
#: (Integer? style_index) -> bool
|
|
91
|
+
def time_format?(style_index)
|
|
92
|
+
return false unless style_index.is_a?(Integer) && style_index >= 0
|
|
93
|
+
|
|
94
|
+
precompute! unless @time_flags
|
|
95
|
+
@time_flags[style_index] || false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Returns whether the style index represents a date-only pattern.
|
|
99
|
+
#
|
|
100
|
+
# @param style_index [Integer, nil]
|
|
101
|
+
# @return [Boolean]
|
|
102
|
+
# @api public
|
|
103
|
+
#: (Integer? style_index) -> bool
|
|
104
|
+
def date_only_format?(style_index)
|
|
105
|
+
format_type(style_index) == :date
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Precomputes format code and type classification lookups for all cellXfs.
|
|
109
|
+
#
|
|
110
|
+
# @return [self]
|
|
111
|
+
# @api public
|
|
112
|
+
#: () -> self
|
|
113
|
+
def precompute!
|
|
114
|
+
cell_xfs = self[:cell_xfs]
|
|
115
|
+
size = cell_xfs.is_a?(Array) ? cell_xfs.size : 0
|
|
116
|
+
|
|
117
|
+
codes = Array.new(size)
|
|
118
|
+
types = Array.new(size)
|
|
119
|
+
d_flags = Array.new(size, false)
|
|
120
|
+
dt_flags = Array.new(size, false)
|
|
121
|
+
t_flags = Array.new(size, false)
|
|
122
|
+
|
|
123
|
+
if size.positive?
|
|
124
|
+
cell_style_xfs = self[:cell_style_xfs]
|
|
125
|
+
num_fmts = self[:num_fmts] || {}
|
|
126
|
+
|
|
127
|
+
size.times do |i|
|
|
128
|
+
xf = cell_xfs[i]
|
|
129
|
+
next unless xf.is_a?(Hash)
|
|
130
|
+
|
|
131
|
+
num_fmt_id = xf[:num_fmt_id]
|
|
132
|
+
if (num_fmt_id.nil? || num_fmt_id.zero?) && xf[:xf_id] && cell_style_xfs.is_a?(Array)
|
|
133
|
+
parent_xf = cell_style_xfs[xf[:xf_id]]
|
|
134
|
+
num_fmt_id = parent_xf[:num_fmt_id] if parent_xf.is_a?(Hash)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
code = num_fmt_id ? (num_fmts[num_fmt_id] || Ooxml::Utils::BUILTIN_NUM_FMT_CODES[num_fmt_id]) : nil
|
|
138
|
+
|
|
139
|
+
codes[i] = code
|
|
140
|
+
type = NumberFormatter.format_type(code)
|
|
141
|
+
types[i] = type
|
|
142
|
+
d_flags[i] = %i[date datetime time].include?(type)
|
|
143
|
+
dt_flags[i] = (type == :datetime)
|
|
144
|
+
t_flags[i] = (type == :time)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@format_codes = codes
|
|
149
|
+
@format_types = types
|
|
150
|
+
@date_flags = d_flags
|
|
151
|
+
@datetime_flags = dt_flags
|
|
152
|
+
@time_flags = t_flags
|
|
153
|
+
self
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -53,6 +53,43 @@ module Xlsxrb
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# Represents a formatted text run within a rich text string.
|
|
57
|
+
#
|
|
58
|
+
# @example
|
|
59
|
+
# run = Elements::RichTextRun.new(text: "Hello", font: { bold: true })
|
|
60
|
+
#
|
|
61
|
+
# @api public
|
|
62
|
+
RichTextRun = Data.define(:text, :font) do
|
|
63
|
+
# @param text [String] Plain text for this run.
|
|
64
|
+
# @param font [Hash, nil] Optional font styling properties.
|
|
65
|
+
#: (?text: String, ?font: Hash[Symbol, untyped]?) -> void
|
|
66
|
+
def initialize(text: "", font: nil)
|
|
67
|
+
super
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Supports hash-like key access for backwards compatibility.
|
|
71
|
+
#
|
|
72
|
+
# @param key [Symbol]
|
|
73
|
+
# @return [Object, nil]
|
|
74
|
+
# @api public
|
|
75
|
+
#: (Symbol key) -> untyped
|
|
76
|
+
def [](key)
|
|
77
|
+
case key
|
|
78
|
+
when :text then text
|
|
79
|
+
when :font then font
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the plain text of this run.
|
|
84
|
+
#
|
|
85
|
+
# @return [String]
|
|
86
|
+
# @api public
|
|
87
|
+
#: () -> String
|
|
88
|
+
def to_s
|
|
89
|
+
text
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
56
93
|
# Represents a rich text string with multiple formatting runs.
|
|
57
94
|
#
|
|
58
95
|
# @example
|
|
@@ -66,7 +103,64 @@ module Xlsxrb
|
|
|
66
103
|
# @api public
|
|
67
104
|
#: () -> String
|
|
68
105
|
def to_s
|
|
69
|
-
runs.map { |r| r[:text] }.join
|
|
106
|
+
runs.map { |r| r[:text] || r.to_s }.join
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Represents a worksheet hyperlink with an optional target URL, display text, tooltip (ScreenTip), and internal location.
|
|
111
|
+
#
|
|
112
|
+
# @example Create a hyperlink
|
|
113
|
+
# link = Elements::Hyperlink.new(url: "https://example.com", tooltip: "Visit website")
|
|
114
|
+
#
|
|
115
|
+
# @api public
|
|
116
|
+
# rubocop:disable Lint/DataDefineOverride -- :display represents the hyperlink display text attribute in OOXML
|
|
117
|
+
Hyperlink = Data.define(:url, :display, :tooltip, :location) do
|
|
118
|
+
# rubocop:enable Lint/DataDefineOverride
|
|
119
|
+
# @param url [String, nil] Target external URL or URI.
|
|
120
|
+
# @param display [String, nil] Optional friendly display label.
|
|
121
|
+
# @param tooltip [String, nil] Optional hover ScreenTip text.
|
|
122
|
+
# @param location [String, nil] Optional internal sheet/cell reference.
|
|
123
|
+
#: (?url: String?, ?display: String?, ?tooltip: String?, ?location: String?) -> void
|
|
124
|
+
def initialize(url: nil, display: nil, tooltip: nil, location: nil)
|
|
125
|
+
super
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Supports hash-like key access for backwards compatibility.
|
|
129
|
+
#
|
|
130
|
+
# @param key [Symbol]
|
|
131
|
+
# @return [Object, nil]
|
|
132
|
+
# @api public
|
|
133
|
+
#: (Symbol key) -> untyped
|
|
134
|
+
def [](key)
|
|
135
|
+
case key
|
|
136
|
+
when :url then url
|
|
137
|
+
when :display then display
|
|
138
|
+
when :tooltip then tooltip
|
|
139
|
+
when :location then location
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Supports Hash conversion.
|
|
144
|
+
#
|
|
145
|
+
# @return [Hash{Symbol => String}]
|
|
146
|
+
# @api public
|
|
147
|
+
#: () -> Hash[Symbol, String]
|
|
148
|
+
def to_h
|
|
149
|
+
h = {}
|
|
150
|
+
h[:url] = url if url
|
|
151
|
+
h[:display] = display if display
|
|
152
|
+
h[:tooltip] = tooltip if tooltip
|
|
153
|
+
h[:location] = location if location
|
|
154
|
+
h
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns the target URL or location string.
|
|
158
|
+
#
|
|
159
|
+
# @return [String, nil]
|
|
160
|
+
# @api public
|
|
161
|
+
#: () -> String?
|
|
162
|
+
def to_s
|
|
163
|
+
url || location
|
|
70
164
|
end
|
|
71
165
|
end
|
|
72
166
|
end
|
|
@@ -13,7 +13,7 @@ module Xlsxrb
|
|
|
13
13
|
#
|
|
14
14
|
# @api public
|
|
15
15
|
# rubocop:disable Style/DataInheritance -- Required as class syntax for mutant subject matcher
|
|
16
|
-
class Workbook < Data.define(:sheets, :shared_strings, :styles, :unmapped_data, :errors)
|
|
16
|
+
class Workbook < Data.define(:sheets, :shared_strings, :styles, :unmapped_data, :errors, :defined_names)
|
|
17
17
|
# rubocop:enable Style/DataInheritance
|
|
18
18
|
[Enumerable].each { |m| include m }
|
|
19
19
|
|
|
@@ -22,11 +22,19 @@ module Xlsxrb
|
|
|
22
22
|
# @param styles [Hash] Styles definition.
|
|
23
23
|
# @param unmapped_data [Hash] Additional metadata for round-tripping.
|
|
24
24
|
# @param errors [Array<String>, nil] Validation errors.
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
# @param defined_names [Array<Hash>, nil] Defined names list.
|
|
26
|
+
# @param date1904 [Boolean] Whether the workbook uses the 1904 date system.
|
|
27
|
+
#: (?sheets: Array[Elements::Worksheet | StreamSheet], ?shared_strings: Array[String], ?styles: Hash[untyped, untyped], ?unmapped_data: Hash[untyped, untyped], ?errors: Array[String]?, ?defined_names: Array[Hash[Symbol, untyped]]?, ?date1904: bool) -> void
|
|
28
|
+
def initialize(sheets: [], shared_strings: [], styles: {}, unmapped_data: {}, errors: nil, defined_names: nil, date1904: false)
|
|
29
|
+
dns = defined_names || unmapped_data[:defined_names] || unmapped_data.dig(:facade, :defined_names) || []
|
|
27
30
|
computed_errors = errors || self.class.validate(sheets)
|
|
31
|
+
unmapped = unmapped_data
|
|
32
|
+
if date1904 && !unmapped.dig(:workbook_properties, :date1904) && !unmapped.dig(:facade, :workbook_properties, :date1904)
|
|
33
|
+
unmapped = unmapped.dup
|
|
34
|
+
unmapped[:workbook_properties] = (unmapped[:workbook_properties] || {}).merge(date1904: true)
|
|
35
|
+
end
|
|
28
36
|
super(sheets: sheets.freeze, shared_strings: shared_strings.freeze, styles: styles,
|
|
29
|
-
unmapped_data:
|
|
37
|
+
unmapped_data: unmapped, errors: computed_errors.freeze, defined_names: dns.freeze)
|
|
30
38
|
end
|
|
31
39
|
|
|
32
40
|
# Iterate over worksheets.
|
|
@@ -54,6 +62,41 @@ module Xlsxrb
|
|
|
54
62
|
errors.empty?
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
# Returns whether the workbook uses the 1904 date system.
|
|
66
|
+
#
|
|
67
|
+
# @return [Boolean]
|
|
68
|
+
# @api public
|
|
69
|
+
#: () -> bool
|
|
70
|
+
def date1904?
|
|
71
|
+
props = unmapped_data[:workbook_properties] || unmapped_data.dig(:facade, :workbook_properties)
|
|
72
|
+
props&.[](:date1904) ? true : false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the defined name matching name (and optionally sheet), or nil.
|
|
76
|
+
#
|
|
77
|
+
# @example
|
|
78
|
+
# wb.defined_name("SalesTotal")
|
|
79
|
+
# wb.defined_name("Print_Area", sheet: "Sheet1")
|
|
80
|
+
#
|
|
81
|
+
# @param name [String] The defined name.
|
|
82
|
+
# @param sheet [String, Integer, nil] Optional sheet name or 0-based index.
|
|
83
|
+
# @return [Hash[Symbol, untyped], nil]
|
|
84
|
+
# @api public
|
|
85
|
+
#: (String name, ?sheet: (String | Integer)?) -> Hash[Symbol, untyped]?
|
|
86
|
+
def defined_name(name, sheet: nil)
|
|
87
|
+
sheet_idx = case sheet
|
|
88
|
+
when Integer then sheet
|
|
89
|
+
when String then sheets.find_index { |s| s.name == sheet }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if sheet_idx
|
|
93
|
+
defined_names.find { |dn| dn[:name] == name && dn[:local_sheet_id] == sheet_idx }
|
|
94
|
+
else
|
|
95
|
+
defined_names.find { |dn| dn[:name] == name && dn[:local_sheet_id].nil? } ||
|
|
96
|
+
defined_names.find { |dn| dn[:name] == name }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
57
100
|
# Returns the worksheet at the given 0-based index or by name.
|
|
58
101
|
#
|
|
59
102
|
# @example
|
|
@@ -74,6 +117,44 @@ module Xlsxrb
|
|
|
74
117
|
end
|
|
75
118
|
alias [] sheet
|
|
76
119
|
|
|
120
|
+
# Returns the formatted string representation of a cell's value in the specified sheet.
|
|
121
|
+
#
|
|
122
|
+
# @param sheet_identifier [Integer, String, untyped] 0-based index or sheet name.
|
|
123
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
124
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
125
|
+
# @return [String, nil]
|
|
126
|
+
# @api public
|
|
127
|
+
#: (Integer | String | untyped sheet_identifier, String | Symbol | Integer ref_or_row, ?Integer? col) -> String?
|
|
128
|
+
def formatted_value(sheet_identifier, ref_or_row, col = nil)
|
|
129
|
+
target_sheet = sheet(sheet_identifier)
|
|
130
|
+
target_sheet&.formatted_value(ref_or_row, col)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Returns all embedded images across all worksheets.
|
|
134
|
+
#
|
|
135
|
+
# @return [Array<Elements::Image>]
|
|
136
|
+
# @api public
|
|
137
|
+
#: () -> Array[Elements::Image]
|
|
138
|
+
def images
|
|
139
|
+
sheets.flat_map(&:images)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Returns all images anchored at a specific cell in the given worksheet.
|
|
143
|
+
#
|
|
144
|
+
# @param sheet_identifier [Integer, String, untyped] Sheet index or sheet name.
|
|
145
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
146
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
147
|
+
# @return [Array<Elements::Image>]
|
|
148
|
+
# @api public
|
|
149
|
+
#: (Integer | String | untyped sheet_identifier, String | Symbol | Integer ref_or_row, ?Integer? col) -> Array[Elements::Image]
|
|
150
|
+
def images_at(sheet_identifier, ref_or_row, col = nil)
|
|
151
|
+
ident = sheet_identifier.is_a?(Symbol) ? sheet_identifier.to_s : sheet_identifier
|
|
152
|
+
target_sheet = sheet(ident)
|
|
153
|
+
return [] unless target_sheet
|
|
154
|
+
|
|
155
|
+
target_sheet.images_at(ref_or_row, col)
|
|
156
|
+
end
|
|
157
|
+
|
|
77
158
|
# Loads all sheets into memory, returning an Elements::Workbook where every
|
|
78
159
|
# worksheet is a fully-parsed Elements::Worksheet supporting coordinate random access.
|
|
79
160
|
#
|
|
@@ -86,10 +167,21 @@ module Xlsxrb
|
|
|
86
167
|
#: () -> Elements::Workbook
|
|
87
168
|
def load
|
|
88
169
|
loaded_sheets = sheets.map { |s| s.respond_to?(:load) ? s.load : s }
|
|
170
|
+
close
|
|
89
171
|
with(sheets: loaded_sheets)
|
|
90
172
|
end
|
|
91
173
|
alias to_workbook load
|
|
92
174
|
|
|
175
|
+
# Closes any streaming resources associated with worksheets.
|
|
176
|
+
#
|
|
177
|
+
# @return [void]
|
|
178
|
+
# @api public
|
|
179
|
+
#: () -> void
|
|
180
|
+
def close
|
|
181
|
+
sheets.each { |s| s.close if s.respond_to?(:close) }
|
|
182
|
+
nil
|
|
183
|
+
end
|
|
184
|
+
|
|
93
185
|
# Returns a new Workbook with the specified sheet updated.
|
|
94
186
|
# Yields the matched worksheet to the block, which must return a new Worksheet.
|
|
95
187
|
#
|