xlsxrb 0.1.13 → 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 +57 -0
- data/README.md +2 -0
- data/lib/xlsxrb/colors.rb +118 -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 +85 -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 +7 -1
- 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 +65 -14
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +319 -79
- 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 +14 -0
- data/lib/xlsxrb/ooxml/zip_reader.rb +109 -25
- data/lib/xlsxrb/stream_row.rb +243 -16
- data/lib/xlsxrb/stream_sheet.rb +414 -15
- data/lib/xlsxrb/stream_writer.rb +213 -17
- 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 +73 -3
- data/lib/xlsxrb/worksheet_builder.rb +227 -16
- data/lib/xlsxrb.rb +578 -79
- data/sig/generated/xlsxrb/colors.rbs +40 -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 +51 -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 -0
- 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 +28 -6
- data/sig/generated/xlsxrb/ooxml/worksheet_writer.rbs +3 -1
- data/sig/generated/xlsxrb/ooxml/writer.rbs +2 -0
- data/sig/generated/xlsxrb/ooxml/zip_reader.rbs +10 -0
- data/sig/generated/xlsxrb/stream_row.rbs +100 -6
- data/sig/generated/xlsxrb/stream_sheet.rbs +247 -13
- 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 +186 -27
- metadata +11 -1
data/lib/xlsxrb/stream_sheet.rb
CHANGED
|
@@ -33,6 +33,16 @@ module Xlsxrb
|
|
|
33
33
|
#: String
|
|
34
34
|
attr_reader :name
|
|
35
35
|
|
|
36
|
+
# @return [Symbol] The sheet visibility state (:visible, :hidden, or :very_hidden).
|
|
37
|
+
# @api public
|
|
38
|
+
#: Symbol
|
|
39
|
+
attr_reader :state
|
|
40
|
+
|
|
41
|
+
# @return [Hash, nil] The parsed styles definition hash.
|
|
42
|
+
# @api public
|
|
43
|
+
#: Hash[untyped, untyped]?
|
|
44
|
+
attr_reader :styles
|
|
45
|
+
|
|
36
46
|
# Initializes a streaming worksheet context.
|
|
37
47
|
#
|
|
38
48
|
# @param name [String] The sheet name.
|
|
@@ -41,15 +51,136 @@ module Xlsxrb
|
|
|
41
51
|
# @param styles [Hash, nil] Optional parsed styles hash.
|
|
42
52
|
# @param zip_reader [Ooxml::ZipReader, nil] Optional ZipReader context.
|
|
43
53
|
# @param entry_name [String, nil] Archive entry name for this sheet.
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
# @param date1904 [Boolean] Whether the 1904 date system is active.
|
|
55
|
+
# @param dimension [String, nil] Sheet dimension reference string (e.g. "A1:Z50000").
|
|
56
|
+
# @param trim_empty_rows [Boolean] Whether to omit trailing empty rows during iteration.
|
|
57
|
+
# @param pad_empty_rows [Boolean] Whether to yield empty rows for skipped row numbers.
|
|
58
|
+
# @param pad_empty_cells [Boolean] Whether to pad missing cells within rows.
|
|
59
|
+
# @param images [Array<Elements::Image>, nil] Optional preloaded images array.
|
|
60
|
+
#: (String name, untyped sheet_source, Array[String] shared_strings, ?Hash[untyped, untyped]? styles, ?zip_reader: Ooxml::ZipReader?, ?entry_name: String?, ?state: Symbol, ?date1904: bool, ?dimension: String?, ?trim_empty_rows: bool, ?pad_empty_rows: bool, ?pad_empty_cells: bool, ?images: Array[Elements::Image]?) -> void
|
|
61
|
+
def initialize(name, sheet_source, shared_strings, styles = nil, zip_reader: nil, entry_name: nil, state: :visible, date1904: false, dimension: nil, trim_empty_rows: false, pad_empty_rows: false, pad_empty_cells: false, images: nil)
|
|
46
62
|
@name = name
|
|
47
63
|
@sheet_source = sheet_source
|
|
48
64
|
@shared_strings = shared_strings
|
|
49
65
|
@styles = styles
|
|
50
66
|
@zip_reader = zip_reader
|
|
51
67
|
@entry_name = entry_name
|
|
68
|
+
@state = state ? state.to_sym : :visible
|
|
69
|
+
@date1904 = date1904 ? true : false
|
|
70
|
+
@dimension = dimension
|
|
71
|
+
@dimension_checked = !dimension.nil?
|
|
52
72
|
@sheet_xml = sheet_source if sheet_source.is_a?(String)
|
|
73
|
+
@trim_empty_rows = trim_empty_rows ? true : false
|
|
74
|
+
@pad_empty_rows = pad_empty_rows ? true : false
|
|
75
|
+
@pad_empty_cells = pad_empty_cells ? true : false
|
|
76
|
+
@images = images
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns whether trailing empty rows are omitted during iteration.
|
|
80
|
+
#
|
|
81
|
+
# @return [Boolean]
|
|
82
|
+
# @api public
|
|
83
|
+
#: () -> bool
|
|
84
|
+
def trim_empty_rows?
|
|
85
|
+
@trim_empty_rows ? true : false
|
|
86
|
+
end
|
|
87
|
+
alias trim_empty_rows trim_empty_rows?
|
|
88
|
+
|
|
89
|
+
# Returns whether skipped rows are padded during iteration.
|
|
90
|
+
#
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
# @api public
|
|
93
|
+
#: () -> bool
|
|
94
|
+
def pad_empty_rows?
|
|
95
|
+
@pad_empty_rows ? true : false
|
|
96
|
+
end
|
|
97
|
+
alias pad_empty_rows pad_empty_rows?
|
|
98
|
+
|
|
99
|
+
# Returns whether missing cells within rows are padded during iteration.
|
|
100
|
+
#
|
|
101
|
+
# @return [Boolean]
|
|
102
|
+
# @api public
|
|
103
|
+
#: () -> bool
|
|
104
|
+
def pad_empty_cells?
|
|
105
|
+
@pad_empty_cells ? true : false
|
|
106
|
+
end
|
|
107
|
+
alias pad_empty_cells pad_empty_cells?
|
|
108
|
+
|
|
109
|
+
# Returns the dimension reference string (e.g. "A1:Z50000"), or nil if not present.
|
|
110
|
+
#
|
|
111
|
+
# @return [String, nil]
|
|
112
|
+
# @api public
|
|
113
|
+
#: () -> String?
|
|
114
|
+
def dimension
|
|
115
|
+
return @dimension if @dimension_checked
|
|
116
|
+
|
|
117
|
+
@dimension_checked = true
|
|
118
|
+
@dimension = extract_dimension
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns the 1-based index of the first row defined in the sheet dimension, or nil.
|
|
122
|
+
#
|
|
123
|
+
# @return [Integer, nil]
|
|
124
|
+
# @api public
|
|
125
|
+
#: () -> Integer?
|
|
126
|
+
def first_row
|
|
127
|
+
dimension_bounds&.[](0)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns the 1-based index of the first column defined in the sheet dimension, or nil.
|
|
131
|
+
#
|
|
132
|
+
# @return [Integer, nil]
|
|
133
|
+
# @api public
|
|
134
|
+
#: () -> Integer?
|
|
135
|
+
def first_column
|
|
136
|
+
dimension_bounds&.[](1)
|
|
137
|
+
end
|
|
138
|
+
alias first_col first_column
|
|
139
|
+
|
|
140
|
+
# Returns the 1-based index of the last row defined in the sheet dimension, or nil.
|
|
141
|
+
#
|
|
142
|
+
# @return [Integer, nil]
|
|
143
|
+
# @api public
|
|
144
|
+
#: () -> Integer?
|
|
145
|
+
def last_row
|
|
146
|
+
dimension_bounds&.[](2)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns the 1-based index of the last column defined in the sheet dimension, or nil.
|
|
150
|
+
#
|
|
151
|
+
# @return [Integer, nil]
|
|
152
|
+
# @api public
|
|
153
|
+
#: () -> Integer?
|
|
154
|
+
def last_column
|
|
155
|
+
dimension_bounds&.[](3)
|
|
156
|
+
end
|
|
157
|
+
alias last_col last_column
|
|
158
|
+
|
|
159
|
+
# Returns whether the sheet uses the 1904 date system.
|
|
160
|
+
#
|
|
161
|
+
# @return [Boolean]
|
|
162
|
+
# @api public
|
|
163
|
+
#: () -> bool
|
|
164
|
+
def date1904?
|
|
165
|
+
@date1904
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns whether the sheet is hidden (:hidden or :very_hidden).
|
|
169
|
+
#
|
|
170
|
+
# @return [Boolean]
|
|
171
|
+
# @api public
|
|
172
|
+
#: () -> bool
|
|
173
|
+
def hidden?
|
|
174
|
+
@state == :hidden || @state == :very_hidden
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Returns whether the sheet is visible.
|
|
178
|
+
#
|
|
179
|
+
# @return [Boolean]
|
|
180
|
+
# @api public
|
|
181
|
+
#: () -> bool
|
|
182
|
+
def visible?
|
|
183
|
+
@state == :visible
|
|
53
184
|
end
|
|
54
185
|
|
|
55
186
|
# Iterates over rows in this streaming worksheet with O(1) memory.
|
|
@@ -57,16 +188,41 @@ module Xlsxrb
|
|
|
57
188
|
# @overload each_row(&block)
|
|
58
189
|
# @yield [row]
|
|
59
190
|
# @yieldparam row [StreamRow, Elements::Row] The current row.
|
|
191
|
+
# @overload each_row(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &block)
|
|
192
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows (defaults to sheet setting).
|
|
193
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers (defaults to sheet setting).
|
|
194
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows (defaults to sheet setting).
|
|
195
|
+
# @yield [row]
|
|
196
|
+
# @yieldparam row [StreamRow, Elements::Row] The current row.
|
|
60
197
|
# @return [void]
|
|
61
198
|
#
|
|
62
|
-
# @overload each_row
|
|
199
|
+
# @overload each_row(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil)
|
|
200
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows (defaults to sheet setting).
|
|
201
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers (defaults to sheet setting).
|
|
202
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows (defaults to sheet setting).
|
|
63
203
|
# @return [Enumerator<StreamRow | Elements::Row, void>]
|
|
64
204
|
#
|
|
65
205
|
# @api public
|
|
66
|
-
#: () { (StreamRow | Elements::Row) -> void } -> void
|
|
67
|
-
#: () -> Enumerator[StreamRow | Elements::Row, void]
|
|
68
|
-
def each_row(&)
|
|
69
|
-
return enum_for(:each_row) unless block_given?
|
|
206
|
+
#: (?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) { (StreamRow | Elements::Row) -> void } -> void
|
|
207
|
+
#: (?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) -> Enumerator[StreamRow | Elements::Row, void]
|
|
208
|
+
def each_row(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &)
|
|
209
|
+
return enum_for(:each_row, trim_empty_rows: trim_empty_rows, pad_empty_rows: pad_empty_rows, pad_empty_cells: pad_empty_cells) unless block_given?
|
|
210
|
+
|
|
211
|
+
trim = if trim_empty_rows.nil?
|
|
212
|
+
@trim_empty_rows
|
|
213
|
+
else
|
|
214
|
+
(trim_empty_rows ? true : false)
|
|
215
|
+
end
|
|
216
|
+
pad_rows = if pad_empty_rows.nil?
|
|
217
|
+
@pad_empty_rows
|
|
218
|
+
else
|
|
219
|
+
(pad_empty_rows ? true : false)
|
|
220
|
+
end
|
|
221
|
+
pad_cells = if pad_empty_cells.nil?
|
|
222
|
+
@pad_empty_cells
|
|
223
|
+
else
|
|
224
|
+
(pad_empty_cells ? true : false)
|
|
225
|
+
end
|
|
70
226
|
|
|
71
227
|
source = if @sheet_source
|
|
72
228
|
@sheet_source
|
|
@@ -74,7 +230,7 @@ module Xlsxrb
|
|
|
74
230
|
->(&blk) { @zip_reader.each_entry_chunk(@entry_name, &blk) }
|
|
75
231
|
end
|
|
76
232
|
|
|
77
|
-
Ooxml::WorksheetParser.each_row(source, shared_strings: @shared_strings, &)
|
|
233
|
+
Ooxml::WorksheetParser.each_row(source, shared_strings: @shared_strings, styles: @styles, date1904: @date1904, trim_empty_rows: trim, pad_empty_rows: pad_rows, pad_empty_cells: pad_cells, &)
|
|
78
234
|
end
|
|
79
235
|
|
|
80
236
|
# Iterates over all cells across all rows continuously with O(1) memory.
|
|
@@ -98,21 +254,56 @@ module Xlsxrb
|
|
|
98
254
|
end
|
|
99
255
|
end
|
|
100
256
|
|
|
257
|
+
# Iterates over row values directly as Arrays without wrapping cells in Elements::Cell objects.
|
|
258
|
+
#
|
|
259
|
+
# @overload each_row_values(type_cast: false, trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &block)
|
|
260
|
+
# @param type_cast [Boolean] Whether to coerce date/time serial numbers into Date/Time instances.
|
|
261
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows (defaults to sheet setting).
|
|
262
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers (defaults to sheet setting).
|
|
263
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows (defaults to sheet setting).
|
|
264
|
+
# @yield [values]
|
|
265
|
+
# @yieldparam values [Array<Object>] Row values array.
|
|
266
|
+
# @return [void]
|
|
267
|
+
#
|
|
268
|
+
# @overload each_row_values(type_cast: false, trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil)
|
|
269
|
+
# @param type_cast [Boolean] Whether to coerce date/time serial numbers into Date/Time instances.
|
|
270
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows (defaults to sheet setting).
|
|
271
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers (defaults to sheet setting).
|
|
272
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows (defaults to sheet setting).
|
|
273
|
+
# @return [Enumerator<Array<Object>, void>]
|
|
274
|
+
#
|
|
275
|
+
# @api public
|
|
276
|
+
#: (?type_cast: bool, ?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) { (Array[untyped]) -> void } -> void
|
|
277
|
+
#: (?type_cast: bool, ?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) -> Enumerator[Array[untyped], void]
|
|
278
|
+
def each_row_values(type_cast: false, trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &block)
|
|
279
|
+
return enum_for(:each_row_values, type_cast: type_cast, trim_empty_rows: trim_empty_rows, pad_empty_rows: pad_empty_rows, pad_empty_cells: pad_empty_cells) unless block
|
|
280
|
+
|
|
281
|
+
each_row(trim_empty_rows: trim_empty_rows, pad_empty_rows: pad_empty_rows, pad_empty_cells: pad_empty_cells) do |row|
|
|
282
|
+
block.call(row.values(type_cast: type_cast))
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
101
286
|
# Default Enumerable iteration delegates to {#each_row}.
|
|
102
287
|
#
|
|
103
|
-
# @overload each(&block)
|
|
288
|
+
# @overload each(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &block)
|
|
289
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows.
|
|
290
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers.
|
|
291
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows.
|
|
104
292
|
# @yield [row]
|
|
105
293
|
# @yieldparam row [StreamRow, Elements::Row]
|
|
106
294
|
# @return [void]
|
|
107
295
|
#
|
|
108
|
-
# @overload each
|
|
296
|
+
# @overload each(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil)
|
|
297
|
+
# @param trim_empty_rows [Boolean, nil] Whether to omit trailing empty rows.
|
|
298
|
+
# @param pad_empty_rows [Boolean, nil] Whether to yield empty rows for skipped row numbers.
|
|
299
|
+
# @param pad_empty_cells [Boolean, nil] Whether to pad missing cells within rows.
|
|
109
300
|
# @return [Enumerator<StreamRow | Elements::Row, void>]
|
|
110
301
|
#
|
|
111
302
|
# @api public
|
|
112
|
-
#: () { (StreamRow | Elements::Row) -> void } -> void
|
|
113
|
-
#: () -> Enumerator[StreamRow | Elements::Row, void]
|
|
114
|
-
def each(&)
|
|
115
|
-
each_row(&)
|
|
303
|
+
#: (?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) { (StreamRow | Elements::Row) -> void } -> void
|
|
304
|
+
#: (?trim_empty_rows: bool?, ?pad_empty_rows: bool?, ?pad_empty_cells: bool?) -> Enumerator[StreamRow | Elements::Row, void]
|
|
305
|
+
def each(trim_empty_rows: nil, pad_empty_rows: nil, pad_empty_cells: nil, &)
|
|
306
|
+
each_row(trim_empty_rows: trim_empty_rows, pad_empty_rows: pad_empty_rows, pad_empty_cells: pad_empty_cells, &)
|
|
116
307
|
end
|
|
117
308
|
|
|
118
309
|
# Loads this sheet completely into an in-memory {Elements::Worksheet},
|
|
@@ -123,10 +314,151 @@ module Xlsxrb
|
|
|
123
314
|
# @api public
|
|
124
315
|
#: () -> Elements::Worksheet
|
|
125
316
|
def load
|
|
126
|
-
Xlsxrb.send(:build_worksheet, @name, raw_sheet_xml, @shared_strings, @styles)
|
|
317
|
+
Xlsxrb.send(:build_worksheet, @name, raw_sheet_xml, @shared_strings, @styles, state: @state, zip_reader: @zip_reader, entry_name: @entry_name, hyperlinks: hyperlinks, comments: comments, images: images, date1904: @date1904, dimension: dimension, trim_empty_rows: @trim_empty_rows, pad_empty_rows: @pad_empty_rows, pad_empty_cells: @pad_empty_cells)
|
|
127
318
|
end
|
|
128
319
|
alias to_worksheet load
|
|
129
320
|
|
|
321
|
+
# Returns hyperlinks configured for this worksheet as a Hash of cell references to hyperlink hashes.
|
|
322
|
+
#
|
|
323
|
+
# @return [Hash<String, Hash[Symbol, untyped]>]
|
|
324
|
+
# @api public
|
|
325
|
+
#: () -> Hash[String, Hash[Symbol, untyped]]
|
|
326
|
+
def hyperlinks
|
|
327
|
+
@hyperlinks ||= Xlsxrb.send(:resolve_hyperlinks, raw_sheet_xml, zip_reader: @zip_reader, entry_name: @entry_name)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Returns hyperlink metadata for a specific cell reference, or nil.
|
|
331
|
+
#
|
|
332
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
333
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
334
|
+
# @return [Hash, nil]
|
|
335
|
+
# @api public
|
|
336
|
+
#: (String | Symbol | Integer ref_or_row, ?Integer? col) -> Hash[Symbol, untyped]?
|
|
337
|
+
def hyperlink(ref_or_row, col = nil)
|
|
338
|
+
ref = if col
|
|
339
|
+
"#{Elements::Cell.column_letter(col)}#{ref_or_row.to_i + 1}"
|
|
340
|
+
else
|
|
341
|
+
ref_or_row.to_s.upcase
|
|
342
|
+
end
|
|
343
|
+
hyperlinks[ref]
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Returns comments configured for this worksheet as an Array of comment hashes.
|
|
347
|
+
#
|
|
348
|
+
# @return [Array<Hash[Symbol, untyped]>]
|
|
349
|
+
# @api public
|
|
350
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
351
|
+
def comments
|
|
352
|
+
@comments ||= Xlsxrb.send(:resolve_comments, zip_reader: @zip_reader, entry_name: @entry_name)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Returns comment metadata for a specific cell reference, or nil.
|
|
356
|
+
#
|
|
357
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
358
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
359
|
+
# @return [Hash, nil]
|
|
360
|
+
# @api public
|
|
361
|
+
#: (String | Symbol | Integer ref_or_row, ?Integer? col) -> Hash[Symbol, untyped]?
|
|
362
|
+
def comment(ref_or_row, col = nil)
|
|
363
|
+
ref = if col
|
|
364
|
+
"#{Elements::Cell.column_letter(col)}#{ref_or_row.to_i + 1}"
|
|
365
|
+
else
|
|
366
|
+
ref_or_row.to_s.upcase
|
|
367
|
+
end
|
|
368
|
+
comments_by_ref[ref]
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Returns comments indexed by cell reference.
|
|
372
|
+
#
|
|
373
|
+
# @return [Hash<String, Hash[Symbol, untyped]>]
|
|
374
|
+
# @api public
|
|
375
|
+
#: () -> Hash[String, Hash[Symbol, untyped]]
|
|
376
|
+
def comments_by_ref
|
|
377
|
+
@comments_by_ref ||= comments.each_with_object({}) do |c, acc|
|
|
378
|
+
r = c[:ref] || c[:cell]
|
|
379
|
+
acc[r.to_s.upcase] = c if r
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Returns all embedded images in this worksheet.
|
|
384
|
+
#
|
|
385
|
+
# @return [Array<Elements::Image>]
|
|
386
|
+
# @api public
|
|
387
|
+
#: () -> Array[Elements::Image]
|
|
388
|
+
def images
|
|
389
|
+
@images ||= Xlsxrb.send(:resolve_images, zip_reader: @zip_reader, entry_name: @entry_name)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Returns all images anchored at a specific cell reference or 0-based coordinate.
|
|
393
|
+
#
|
|
394
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
395
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
396
|
+
# @return [Array<Elements::Image>]
|
|
397
|
+
# @api public
|
|
398
|
+
#: (String | Symbol | Integer ref_or_row, ?Integer? col) -> Array[Elements::Image]
|
|
399
|
+
def images_at(ref_or_row, col = nil)
|
|
400
|
+
ref = if col
|
|
401
|
+
"#{Elements::Cell.column_letter(col)}#{ref_or_row.to_i + 1}"
|
|
402
|
+
else
|
|
403
|
+
ref_or_row.to_s.upcase
|
|
404
|
+
end
|
|
405
|
+
images.select { |img| img.cell_ref == ref }
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Loads and returns images for this worksheet, optionally yielding self for block syntax.
|
|
409
|
+
#
|
|
410
|
+
# @yield [sheet]
|
|
411
|
+
# @yieldparam sheet [StreamSheet]
|
|
412
|
+
# @return [StreamSheet, void]
|
|
413
|
+
# @api public
|
|
414
|
+
#: () { (StreamSheet) -> void } -> void
|
|
415
|
+
#: () -> StreamSheet
|
|
416
|
+
def with_images
|
|
417
|
+
images
|
|
418
|
+
if block_given?
|
|
419
|
+
yield self
|
|
420
|
+
nil
|
|
421
|
+
else
|
|
422
|
+
self
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Access a cell by reference or 0-based coordinates.
|
|
427
|
+
#
|
|
428
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
429
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
430
|
+
# @return [Elements::Cell, nil]
|
|
431
|
+
# @api public
|
|
432
|
+
#: (String | Symbol | Integer ref_or_row, ?Integer? col) -> Elements::Cell?
|
|
433
|
+
def cell(ref_or_row, col = nil)
|
|
434
|
+
target_row, target_col = if col
|
|
435
|
+
[ref_or_row.to_i, col.to_i]
|
|
436
|
+
else
|
|
437
|
+
parsed = Elements::Cell.parse_ref(ref_or_row.to_s)
|
|
438
|
+
return nil unless parsed
|
|
439
|
+
|
|
440
|
+
parsed
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
each_row do |row|
|
|
444
|
+
next if row.index < target_row
|
|
445
|
+
return row.cell_at(target_col) if row.index == target_row
|
|
446
|
+
break if row.index > target_row
|
|
447
|
+
end
|
|
448
|
+
nil
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# Returns the formatted string representation of a cell's value.
|
|
452
|
+
#
|
|
453
|
+
# @param ref_or_row [String, Symbol, Integer] Cell reference (e.g. "A1") or 0-based row index.
|
|
454
|
+
# @param col [Integer, nil] Optional 0-based column index.
|
|
455
|
+
# @return [String, nil]
|
|
456
|
+
# @api public
|
|
457
|
+
#: (String | Symbol | Integer ref_or_row, ?Integer? col) -> String?
|
|
458
|
+
def formatted_value(ref_or_row, col = nil)
|
|
459
|
+
cell(ref_or_row, col)&.formatted_value
|
|
460
|
+
end
|
|
461
|
+
|
|
130
462
|
# Returns merged cell ranges (e.g. ["A1:B2"]) for this worksheet.
|
|
131
463
|
#
|
|
132
464
|
# @return [Array<String>]
|
|
@@ -203,5 +535,72 @@ module Xlsxrb
|
|
|
203
535
|
""
|
|
204
536
|
end
|
|
205
537
|
end
|
|
538
|
+
|
|
539
|
+
# Returns 1-based bounds [first_row, first_col, last_row, last_col] from dimension ref.
|
|
540
|
+
#: () -> Array[Integer]?
|
|
541
|
+
def dimension_bounds
|
|
542
|
+
return @dimension_bounds if defined?(@dimension_bounds)
|
|
543
|
+
|
|
544
|
+
ref = dimension
|
|
545
|
+
return @dimension_bounds = nil unless ref
|
|
546
|
+
|
|
547
|
+
parts = ref.split(":", 2)
|
|
548
|
+
first_coords = Elements::Cell.parse_ref(parts[0])
|
|
549
|
+
return @dimension_bounds = nil unless first_coords
|
|
550
|
+
|
|
551
|
+
last_coords = parts[1] ? Elements::Cell.parse_ref(parts[1]) : first_coords
|
|
552
|
+
return @dimension_bounds = nil unless last_coords
|
|
553
|
+
|
|
554
|
+
@dimension_bounds = [
|
|
555
|
+
first_coords[0] + 1,
|
|
556
|
+
first_coords[1] + 1,
|
|
557
|
+
last_coords[0] + 1,
|
|
558
|
+
last_coords[1] + 1
|
|
559
|
+
].freeze
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Extracts dimension reference from XML source lazily without parsing the entire file.
|
|
563
|
+
#: () -> String?
|
|
564
|
+
def extract_dimension
|
|
565
|
+
if @sheet_source.is_a?(String)
|
|
566
|
+
@sheet_source.slice(/<(?:[a-zA-Z0-9_]+:)?dimension\b[^>]*\bref=["']([^"']+)["']/, 1)
|
|
567
|
+
elsif @zip_reader && @entry_name
|
|
568
|
+
stream = @zip_reader.open_entry_io(@entry_name)
|
|
569
|
+
buf = +""
|
|
570
|
+
dim = nil
|
|
571
|
+
begin
|
|
572
|
+
while (chunk = stream.read(65_536))
|
|
573
|
+
buf << chunk
|
|
574
|
+
if (m = buf.match(/<(?:[a-zA-Z0-9_]+:)?dimension\b[^>]*\bref=["']([^"']+)["']/))
|
|
575
|
+
dim = m[1]
|
|
576
|
+
break
|
|
577
|
+
end
|
|
578
|
+
break if buf.include?("<sheetData") || buf.include?(":sheetData")
|
|
579
|
+
end
|
|
580
|
+
ensure
|
|
581
|
+
stream.close
|
|
582
|
+
end
|
|
583
|
+
dim
|
|
584
|
+
elsif @sheet_source.respond_to?(:call)
|
|
585
|
+
buf = +""
|
|
586
|
+
dim = nil
|
|
587
|
+
@sheet_source.call do |chunk|
|
|
588
|
+
buf << chunk
|
|
589
|
+
if (m = buf.match(/<(?:[a-zA-Z0-9_]+:)?dimension\b[^>]*\bref=["']([^"']+)["']/))
|
|
590
|
+
dim = m[1]
|
|
591
|
+
break
|
|
592
|
+
end
|
|
593
|
+
break if buf.include?("<sheetData") || buf.include?(":sheetData")
|
|
594
|
+
end
|
|
595
|
+
dim
|
|
596
|
+
elsif @sheet_source.respond_to?(:seek) && @sheet_source.respond_to?(:pos)
|
|
597
|
+
pos = @sheet_source.pos
|
|
598
|
+
buf = @sheet_source.read(65_536) || ""
|
|
599
|
+
@sheet_source.seek(pos, IO::SEEK_SET)
|
|
600
|
+
buf.slice(/<(?:[a-zA-Z0-9_]+:)?dimension\b[^>]*\bref=["']([^"']+)["']/, 1)
|
|
601
|
+
else
|
|
602
|
+
raw_sheet_xml.slice(/<(?:[a-zA-Z0-9_]+:)?dimension\b[^>]*\bref=["']([^"']+)["']/, 1)
|
|
603
|
+
end
|
|
604
|
+
end
|
|
206
605
|
end
|
|
207
606
|
end
|