xlsxrb 0.1.9 → 0.1.10
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 +21 -0
- data/README.md +71 -253
- data/Rakefile +2 -1
- data/docs/PEER_LIBRARIES.md +121 -0
- data/docs/assets/benchmark_results.svg +144 -0
- data/docs/assets/lsp_autocompletion.png +0 -0
- data/docs/assets/playground_preview.png +0 -0
- data/docs/visual/VisualGallery.md +183 -0
- data/docs/wasm/ruby.wasm +0 -0
- data/lib/xlsxrb/chart_builder.rb +333 -0
- data/lib/xlsxrb/elements/workbook.rb +2 -2
- data/lib/xlsxrb/ooxml/reader/listeners.rb +6450 -0
- data/lib/xlsxrb/ooxml/reader.rb +2 -6438
- data/lib/xlsxrb/ooxml/workbook_writer.rb +10 -10
- data/lib/xlsxrb/ooxml/worksheet_parser.rb +161 -116
- data/lib/xlsxrb/ooxml/worksheet_writer.rb +5 -1
- data/lib/xlsxrb/stream_row.rb +5 -3
- data/lib/xlsxrb/stream_sheet.rb +125 -0
- data/lib/xlsxrb/stream_writer.rb +1646 -0
- data/lib/xlsxrb/version.rb +1 -1
- data/lib/xlsxrb/workbook_builder.rb +309 -0
- data/lib/xlsxrb/worksheet_builder.rb +691 -0
- data/lib/xlsxrb.rb +238 -2968
- data/sig/generated/xlsxrb/chart_builder.rbs +269 -0
- data/sig/generated/xlsxrb/ooxml/reader/listeners.rbs +1074 -0
- data/sig/generated/xlsxrb/ooxml/reader.rbs +0 -1067
- data/sig/generated/xlsxrb/ooxml/worksheet_parser.rbs +3 -3
- data/sig/generated/xlsxrb/stream_row.rbs +3 -2
- data/sig/generated/xlsxrb/stream_sheet.rbs +100 -0
- data/sig/generated/xlsxrb/stream_writer.rbs +954 -0
- data/sig/generated/xlsxrb/workbook_builder.rbs +146 -0
- data/sig/generated/xlsxrb/worksheet_builder.rbs +349 -0
- data/sig/generated/xlsxrb.rbs +103 -1563
- metadata +17 -1
|
@@ -0,0 +1,1646 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
require "tempfile"
|
|
6
|
+
require_relative "style_builder"
|
|
7
|
+
require_relative "chart_builder"
|
|
8
|
+
require_relative "ooxml/writer"
|
|
9
|
+
require_relative "ooxml/worksheet_writer"
|
|
10
|
+
require_relative "ooxml/workbook_writer"
|
|
11
|
+
|
|
12
|
+
module Xlsxrb
|
|
13
|
+
# High-performance streaming writer that outputs XLSX files with O(1) constant memory.
|
|
14
|
+
#
|
|
15
|
+
# @example Streaming write using StreamWriter
|
|
16
|
+
# Xlsxrb.write("output.xlsx") do |writer|
|
|
17
|
+
# writer.sheet("Sales") do |s|
|
|
18
|
+
# s.row(["Item", "Price"])
|
|
19
|
+
# s.row(["Coffee", 3.50])
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
class StreamWriter
|
|
25
|
+
# @return [String, nil] Name of the currently active worksheet.
|
|
26
|
+
#: String?
|
|
27
|
+
attr_reader :current_sheet
|
|
28
|
+
|
|
29
|
+
# Initializes a streaming writer context.
|
|
30
|
+
#
|
|
31
|
+
# @param target [String, IO, StringIO] Destination file path or writable IO stream.
|
|
32
|
+
# @param strict_excel_mode [Boolean] Whether to enforce Microsoft Excel specification limits.
|
|
33
|
+
#: (untyped target, ?strict_excel_mode: bool) -> void
|
|
34
|
+
def initialize(target, strict_excel_mode: true)
|
|
35
|
+
@target = target
|
|
36
|
+
@strict_excel_mode = strict_excel_mode
|
|
37
|
+
@sst = []
|
|
38
|
+
@sst_index = {}
|
|
39
|
+
@sheets = []
|
|
40
|
+
@current_sheet = nil
|
|
41
|
+
@current_row_index = 0
|
|
42
|
+
@tempfiles = []
|
|
43
|
+
@current_tempfile = nil
|
|
44
|
+
@current_row_writer = nil
|
|
45
|
+
@current_columns = []
|
|
46
|
+
@current_charts = []
|
|
47
|
+
@current_hyperlinks = []
|
|
48
|
+
@current_auto_filter = nil
|
|
49
|
+
@current_filter_columns = {}
|
|
50
|
+
@current_sort_state = nil
|
|
51
|
+
@current_data_validations = []
|
|
52
|
+
@current_conditional_formats = []
|
|
53
|
+
@current_tables = []
|
|
54
|
+
@current_comments = []
|
|
55
|
+
@current_merge_cells = []
|
|
56
|
+
@current_freeze_pane = nil
|
|
57
|
+
@current_split_pane = nil
|
|
58
|
+
@current_selection = nil
|
|
59
|
+
@current_page_margins = nil
|
|
60
|
+
@current_page_setup = {}
|
|
61
|
+
@current_header_footer = {}
|
|
62
|
+
@current_print_options = {}
|
|
63
|
+
@current_sheet_protection = nil
|
|
64
|
+
@current_images = []
|
|
65
|
+
@current_shapes = []
|
|
66
|
+
@current_sheet_properties = {}
|
|
67
|
+
@current_sheet_view = {}
|
|
68
|
+
@current_row_breaks = []
|
|
69
|
+
@current_col_breaks = []
|
|
70
|
+
@styles = {} # { style_name => StyleBuilder }
|
|
71
|
+
@style_writer = Ooxml::Writer.new
|
|
72
|
+
@style_name_to_id = {}
|
|
73
|
+
# Workbook-level settings
|
|
74
|
+
@defined_names = []
|
|
75
|
+
@core_properties = {}
|
|
76
|
+
@app_properties = {}
|
|
77
|
+
@custom_properties = []
|
|
78
|
+
@workbook_protection = nil
|
|
79
|
+
@workbook_properties = { update_links: "never" }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Sets a workbook property.
|
|
83
|
+
#
|
|
84
|
+
# @note **SECURITY WARNING:** If you set `:update_links` to anything other than `"never"`,
|
|
85
|
+
# you may expose end-users to malicious external reference vulnerabilities (e.g., CSV/DDE Injection)
|
|
86
|
+
# when they open the generated Excel file. Ensure you fully trust the exported data.
|
|
87
|
+
#
|
|
88
|
+
# @param name [Symbol] Property name (e.g. :update_links).
|
|
89
|
+
# @param value [String, Integer, Boolean] Property value.
|
|
90
|
+
# @return [void]
|
|
91
|
+
#: (Symbol name, String | Integer | bool value) -> void
|
|
92
|
+
def workbook_property(name, value)
|
|
93
|
+
@workbook_properties[name] = value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Defines or configures a named cell style.
|
|
97
|
+
#
|
|
98
|
+
# @param name [String, Symbol] The name of the style.
|
|
99
|
+
# @param opts [Hash] Style options (e.g. bold: true, fill_color: "FF0000").
|
|
100
|
+
# @yield [style_builder]
|
|
101
|
+
# @yieldparam style_builder [Xlsxrb::StyleBuilder]
|
|
102
|
+
# @return [StyleBuilder]
|
|
103
|
+
#: (String | Symbol name, **untyped opts) ?{ (StyleBuilder) -> void } -> StyleBuilder
|
|
104
|
+
def style(name, **opts)
|
|
105
|
+
style_name = name.to_s
|
|
106
|
+
style_builder = StyleBuilder.new(style_name)
|
|
107
|
+
style_builder.apply_options!(**opts) unless opts.empty?
|
|
108
|
+
yield style_builder if block_given?
|
|
109
|
+
@styles[style_name] = style_builder
|
|
110
|
+
|
|
111
|
+
# Register immediately with low-level style writer
|
|
112
|
+
@style_name_to_id[style_name] = style_builder.register_with(@style_writer)
|
|
113
|
+
|
|
114
|
+
style_builder
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Proxy object yielded by the `sheet` method to prevent writing to inactive sheets in streaming mode.
|
|
118
|
+
#
|
|
119
|
+
# @api public
|
|
120
|
+
class WorksheetProxy
|
|
121
|
+
# @param writer [StreamWriter]
|
|
122
|
+
# @param sheet_name [String]
|
|
123
|
+
def initialize(writer, sheet_name)
|
|
124
|
+
@writer = writer
|
|
125
|
+
@sheet_name = sheet_name
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Defines or configures a named cell style on the active sheet.
|
|
129
|
+
#
|
|
130
|
+
# @example
|
|
131
|
+
# s.style(:header, bold: true, fill_color: "4F81BD", font_color: "FFFFFF")
|
|
132
|
+
#
|
|
133
|
+
# @param name [String, Symbol] The name of the style.
|
|
134
|
+
# @param opts [Hash] Style options (e.g. bold: true, fill_color: "FF0000").
|
|
135
|
+
# @yield [style_builder]
|
|
136
|
+
# @yieldparam style_builder [Xlsxrb::StyleBuilder]
|
|
137
|
+
# @return [Xlsxrb::StyleBuilder]
|
|
138
|
+
# @api public
|
|
139
|
+
#: (String | Symbol name, **untyped opts) ?{ (Xlsxrb::StyleBuilder) -> void } -> Xlsxrb::StyleBuilder
|
|
140
|
+
def style(...)
|
|
141
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
142
|
+
|
|
143
|
+
@writer.style(...)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Merges a range of cells into a single cell.
|
|
147
|
+
#
|
|
148
|
+
# @example Merge with cell reference string
|
|
149
|
+
# s.merge("A1:C1")
|
|
150
|
+
#
|
|
151
|
+
# @example Merge with coordinates
|
|
152
|
+
# s.merge(row: 0, col_start: 0, col_end: 2)
|
|
153
|
+
#
|
|
154
|
+
# @param range [String, nil] The cell range (e.g. "A1:B2").
|
|
155
|
+
# @param row [Integer, nil] 0-based row index.
|
|
156
|
+
# @param col_start [Integer, String, nil] 0-based start column index or letter.
|
|
157
|
+
# @param col_end [Integer, String, nil] 0-based end column index or letter.
|
|
158
|
+
# @param row_start [Integer, nil] 0-based start row index.
|
|
159
|
+
# @param row_end [Integer, nil] 0-based end row index.
|
|
160
|
+
# @return [void]
|
|
161
|
+
# @api public
|
|
162
|
+
#: (?String? range, ?row: Integer | nil, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer | nil, ?row_end: Integer | nil) -> void
|
|
163
|
+
def merge(...)
|
|
164
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
165
|
+
|
|
166
|
+
@writer.merge(...)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Adds a drawing shape to the sheet.
|
|
170
|
+
#
|
|
171
|
+
# @example
|
|
172
|
+
# s.shape(preset: "ellipse", text: "Circle", from_col: 1, from_row: 1, to_col: 4, to_row: 5)
|
|
173
|
+
#
|
|
174
|
+
# @param preset [String] Preset shape type (e.g. "rect", "ellipse").
|
|
175
|
+
# @param text [String, nil] Shape label text.
|
|
176
|
+
# @param from_col [Integer] Starting column index (0-based).
|
|
177
|
+
# @param from_row [Integer] Starting row index (0-based).
|
|
178
|
+
# @param to_col [Integer] Ending column index (0-based).
|
|
179
|
+
# @param to_row [Integer] Ending row index (0-based).
|
|
180
|
+
# @param opts [Hash] Additional shape formatting options.
|
|
181
|
+
# @return [void]
|
|
182
|
+
# @api public
|
|
183
|
+
#: (?preset: String, ?text: String?, ?from_col: Integer, ?from_row: Integer, ?to_col: Integer, ?to_row: Integer, **untyped opts) -> void
|
|
184
|
+
def shape(...)
|
|
185
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
186
|
+
|
|
187
|
+
@writer.shape(...)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# simplecov:disable
|
|
191
|
+
# Edge case / untested delegation block
|
|
192
|
+
#: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped
|
|
193
|
+
def internal_sheet_setup(...)
|
|
194
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
195
|
+
|
|
196
|
+
@writer.internal_sheet_setup(...)
|
|
197
|
+
end
|
|
198
|
+
# simplecov:enable
|
|
199
|
+
|
|
200
|
+
# Appends a row of cells to the active sheet.
|
|
201
|
+
#
|
|
202
|
+
# @example Write an array of values
|
|
203
|
+
# s.row(["Name", "Age", "City"])
|
|
204
|
+
#
|
|
205
|
+
# @example Write with explicit column keys and styles
|
|
206
|
+
# s.row({ A: "Header", C: 100 }, styles: { A: :bold })
|
|
207
|
+
#
|
|
208
|
+
# @param values [Array, Hash] The cell values (e.g. `[1, 2, 3]` or `{ A: 1, C: 3 }`).
|
|
209
|
+
# @param styles [String, Symbol, Array, Hash, nil] Style names or hashes to apply.
|
|
210
|
+
# @param height [Float, Integer, nil] The row height in points (0 - 409).
|
|
211
|
+
# @param hidden [Boolean] Whether the row is hidden.
|
|
212
|
+
# @param custom_height [Boolean] Whether to flag as custom height.
|
|
213
|
+
# @param outline_level [Integer, nil] Grouping/outline level (0 - 7).
|
|
214
|
+
# @return [void]
|
|
215
|
+
# @api public
|
|
216
|
+
#: (Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
|
|
217
|
+
def row(...)
|
|
218
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
219
|
+
|
|
220
|
+
@writer.row(...)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Configures column width and properties.
|
|
224
|
+
#
|
|
225
|
+
# @example Set column A width
|
|
226
|
+
# s.column(0, width: 25.0)
|
|
227
|
+
#
|
|
228
|
+
# @param col_index [Integer, String, Symbol] 0-based column index or letter (e.g. 0 or "A" or :A).
|
|
229
|
+
# @param width [Float, Integer, nil] Column width in characters.
|
|
230
|
+
# @param hidden [Boolean] Whether the column is hidden.
|
|
231
|
+
# @param best_fit [Boolean] Whether the column automatically fits content.
|
|
232
|
+
# @param custom_width [Boolean] Whether to flag as custom width.
|
|
233
|
+
# @param outline_level [Integer, nil] Grouping/outline level (0 - 7).
|
|
234
|
+
# @param collapsed [Boolean] Whether the outline group is collapsed.
|
|
235
|
+
# @return [void]
|
|
236
|
+
# @api public
|
|
237
|
+
#: (Integer | String | Symbol | Range[Integer | String] | Array[Integer | String] col_index, ?width: Float | Integer | nil, ?hidden: bool, ?best_fit: bool, ?custom_width: bool, ?outline_level: Integer | nil, ?collapsed: bool) -> void
|
|
238
|
+
def column(...)
|
|
239
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
240
|
+
|
|
241
|
+
@writer.column(...)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Adds a chart to the active sheet.
|
|
245
|
+
#
|
|
246
|
+
# @example
|
|
247
|
+
# s.chart(:bar) do |chart_builder|
|
|
248
|
+
# chart_builder.title("Quarterly Sales")
|
|
249
|
+
# chart_builder.series(values: "Sheet1!$B$2:$B$5", categories: "Sheet1!$A$2:$A$5", name: "Revenue")
|
|
250
|
+
# end
|
|
251
|
+
#
|
|
252
|
+
# @param type [Symbol, String, nil] The chart type (:bar, :col, :line, :pie, :scatter, :area, :doughnut, :radar).
|
|
253
|
+
# @param opts [Hash] Additional chart options.
|
|
254
|
+
# @yield [chart_builder]
|
|
255
|
+
# @yieldparam chart_builder [Xlsxrb::ChartBuilder]
|
|
256
|
+
# @return [void]
|
|
257
|
+
# @api public
|
|
258
|
+
#: (?Symbol | String? type, **untyped opts) ?{ (Xlsxrb::ChartBuilder) -> void } -> void
|
|
259
|
+
def chart(...)
|
|
260
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
261
|
+
|
|
262
|
+
@writer.chart(...)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Adds a hyperlink to a cell.
|
|
266
|
+
#
|
|
267
|
+
# @example Positional URL
|
|
268
|
+
# s.hyperlink("A1", "https://example.com", display: "Example")
|
|
269
|
+
#
|
|
270
|
+
# @example Keyword location
|
|
271
|
+
# s.hyperlink("A1", location: "https://example.com", tooltip: "Go to Example")
|
|
272
|
+
#
|
|
273
|
+
# @param cell [String] The cell reference (e.g. "A1").
|
|
274
|
+
# @param url [String, nil] The target URL or URI.
|
|
275
|
+
# @param display [String, nil] Display text for the link.
|
|
276
|
+
# @param tooltip [String, nil] Tooltip text when hovering.
|
|
277
|
+
# @param location [String, nil] Destination location / URL (keyword alternative).
|
|
278
|
+
# @return [void]
|
|
279
|
+
# @api public
|
|
280
|
+
#: (String cell, ?String? url, ?display: String?, ?tooltip: String?, ?location: String?) -> void
|
|
281
|
+
def hyperlink(...)
|
|
282
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
283
|
+
|
|
284
|
+
@writer.hyperlink(...)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Sets the auto-filter range on the active sheet.
|
|
288
|
+
#
|
|
289
|
+
# @example
|
|
290
|
+
# s.auto_filter("A1:D100")
|
|
291
|
+
#
|
|
292
|
+
# @param ref [String] The cell range (e.g. "A1:D10").
|
|
293
|
+
# @return [void]
|
|
294
|
+
# @api public
|
|
295
|
+
#: (String ref) -> void
|
|
296
|
+
def auto_filter(...)
|
|
297
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
298
|
+
|
|
299
|
+
@writer.auto_filter(...)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Sets filter criteria for a column in the auto-filter.
|
|
303
|
+
#
|
|
304
|
+
# @example Simple values filter
|
|
305
|
+
# s.filter_column(0, ["Active", "Pending"])
|
|
306
|
+
#
|
|
307
|
+
# @param col_id [Integer] 0-based column index relative to auto-filter range.
|
|
308
|
+
# @param filter_values [Array<String>, Hash] Values or filter specification hash.
|
|
309
|
+
# @return [void]
|
|
310
|
+
# @api public
|
|
311
|
+
#: (Integer col_id, Array[String] | Hash[Symbol, untyped] filter_values) -> void
|
|
312
|
+
def filter_column(...)
|
|
313
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
314
|
+
|
|
315
|
+
@writer.filter_column(...)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Configures sort state on a range.
|
|
319
|
+
#
|
|
320
|
+
# @example
|
|
321
|
+
# s.sort_state("A1:A10", [{ ref: "A1:A10", descending: true }])
|
|
322
|
+
#
|
|
323
|
+
# @param ref [String] The range to sort.
|
|
324
|
+
# @param sort_conditions [Array<Hash>, Hash] Sort conditions array or options hash.
|
|
325
|
+
# @param opts [Hash] Additional sort options.
|
|
326
|
+
# @return [void]
|
|
327
|
+
# @api public
|
|
328
|
+
#: (String ref, Array[Hash[Symbol, untyped]] | Hash[Symbol, untyped] sort_conditions, **untyped opts) -> void
|
|
329
|
+
def sort_state(...)
|
|
330
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
331
|
+
|
|
332
|
+
@writer.sort_state(...)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Adds data validation rules to a range.
|
|
336
|
+
#
|
|
337
|
+
# @example Dropdown list validation
|
|
338
|
+
# s.validate_data("B2:B100", type: "list", formula1: '"High,Medium,Low"')
|
|
339
|
+
#
|
|
340
|
+
# @param range [String] The cell range (e.g. "B2:B10").
|
|
341
|
+
# @param type [String, Symbol] Validation type ("list", "whole", "decimal", "date", "time", "textLength", "custom").
|
|
342
|
+
# @param opts [Hash] Validation options.
|
|
343
|
+
# @return [void]
|
|
344
|
+
# @api public
|
|
345
|
+
#: (String range, ?type: String | Symbol, **untyped opts) -> void
|
|
346
|
+
def validate_data(...)
|
|
347
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
348
|
+
|
|
349
|
+
@writer.validate_data(...)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Adds conditional formatting to a range.
|
|
353
|
+
#
|
|
354
|
+
# @example Highlight values greater than 100
|
|
355
|
+
# s.conditional_format("A1:A10", type: "cellIs", operator: "greaterThan", formula: 100, style: :highlight)
|
|
356
|
+
#
|
|
357
|
+
# @param range [String] The cell range (e.g. "A1:A10").
|
|
358
|
+
# @param type [String, Symbol] Rule type ("cellIs", "colorScale", "dataBar", "expression").
|
|
359
|
+
# @param opts [Hash] Rule options.
|
|
360
|
+
# @return [void]
|
|
361
|
+
# @api public
|
|
362
|
+
#: (String range, ?type: String | Symbol, **untyped opts) -> void
|
|
363
|
+
def conditional_format(...)
|
|
364
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
365
|
+
|
|
366
|
+
@writer.conditional_format(...)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Adds a formatted Excel Table to the sheet.
|
|
370
|
+
#
|
|
371
|
+
# @example
|
|
372
|
+
# s.table("A1:C10", columns: ["ID", "Name", "Total"], name: "SalesTable", style: "TableStyleMedium9")
|
|
373
|
+
#
|
|
374
|
+
# @param ref [String] The cell range for the table (e.g. "A1:D10").
|
|
375
|
+
# @param columns [Array<String>, Array<Hash>] Column names or definitions.
|
|
376
|
+
# @param name [String, nil] Table name.
|
|
377
|
+
# @param display_name [String, nil] Display name.
|
|
378
|
+
# @param style [String, nil] Table style name.
|
|
379
|
+
# @param opts [Hash] Additional options.
|
|
380
|
+
# @return [void]
|
|
381
|
+
# @api public
|
|
382
|
+
#: (String ref, columns: untyped, ?name: String?, ?display_name: String?, ?style: String?, **untyped opts) -> void
|
|
383
|
+
def table(...)
|
|
384
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
385
|
+
|
|
386
|
+
@writer.table(...)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# simplecov:disable
|
|
390
|
+
# Edge case / untested delegation block
|
|
391
|
+
#: () -> void
|
|
392
|
+
def cleanup!(...)
|
|
393
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
394
|
+
|
|
395
|
+
@writer.cleanup!(...)
|
|
396
|
+
end
|
|
397
|
+
# simplecov:enable
|
|
398
|
+
|
|
399
|
+
# Adds a comment to a cell.
|
|
400
|
+
#
|
|
401
|
+
# @example
|
|
402
|
+
# s.comment("A1", "Reviewed and approved", author: "Auditor")
|
|
403
|
+
#
|
|
404
|
+
# @param cell [String, Integer] The cell reference (e.g. "A1").
|
|
405
|
+
# @param text [String] The comment text.
|
|
406
|
+
# @param author [String] The author name.
|
|
407
|
+
# @return [void]
|
|
408
|
+
# @api public
|
|
409
|
+
#: (String | Integer cell, String text, ?author: String) -> void
|
|
410
|
+
def comment(...)
|
|
411
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
412
|
+
|
|
413
|
+
@writer.comment(...)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Adds a Pivot Table to the sheet.
|
|
417
|
+
#
|
|
418
|
+
# @param source_ref [String] Source data range reference (e.g. "Sheet1!A1:D100").
|
|
419
|
+
# @param row_fields [Array<String>] Field names for rows.
|
|
420
|
+
# @param data_fields [Array<String>] Field names for data values.
|
|
421
|
+
# @param col_fields [Array<String>] Field names for columns.
|
|
422
|
+
# @param dest_ref [String] Target top-left cell reference (default: "E1").
|
|
423
|
+
# @param name [String, nil] Pivot table name.
|
|
424
|
+
# @param field_names [Array<String>, nil] Override field names.
|
|
425
|
+
# @param items [Array, nil] Items configuration.
|
|
426
|
+
# @param opts [Hash] Additional options.
|
|
427
|
+
# @return [void]
|
|
428
|
+
# @api public
|
|
429
|
+
#: (String source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: String, ?name: String?, ?field_names: untyped, ?items: untyped, **untyped opts) -> void
|
|
430
|
+
def pivot_table(...)
|
|
431
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
432
|
+
|
|
433
|
+
@writer.pivot_table(...)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Adds sparklines to the sheet.
|
|
437
|
+
#
|
|
438
|
+
# @example
|
|
439
|
+
# s.sparkline_group(sparklines: [{ data_ref: "A1:E1", location_ref: "F1" }], type: "line")
|
|
440
|
+
#
|
|
441
|
+
# @param sparklines [Array<Hash>] Array of { data_ref:, location_ref: } hashes.
|
|
442
|
+
# @param type [String, Symbol, nil] "line" (default), "column", or "stacked".
|
|
443
|
+
# @param opts [Hash] Additional sparkline options.
|
|
444
|
+
# @return [void]
|
|
445
|
+
# @api public
|
|
446
|
+
#: (sparklines: Array[Hash[Symbol, untyped]], ?type: (String | Symbol)?, **untyped opts) -> void
|
|
447
|
+
def sparkline_group(...)
|
|
448
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
449
|
+
|
|
450
|
+
@writer.sparkline_group(...)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Sets workbook-level properties.
|
|
454
|
+
#
|
|
455
|
+
# @param opts [Hash] Workbook property options.
|
|
456
|
+
# @return [void]
|
|
457
|
+
# @api public
|
|
458
|
+
#: (**untyped opts) -> void
|
|
459
|
+
def workbook_property(...)
|
|
460
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
461
|
+
|
|
462
|
+
@writer.workbook_property(...)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Sets sheet properties (e.g. tab color, page setup flags).
|
|
466
|
+
#
|
|
467
|
+
# @example
|
|
468
|
+
# s.sheet_properties(:tab_color, "FF0000")
|
|
469
|
+
#
|
|
470
|
+
# @param name [Symbol, String] Property name.
|
|
471
|
+
# @param value [Object] Property value.
|
|
472
|
+
# @return [void]
|
|
473
|
+
# @api public
|
|
474
|
+
#: (Symbol | String name, untyped value) -> void
|
|
475
|
+
def sheet_properties(...)
|
|
476
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
477
|
+
|
|
478
|
+
@writer.sheet_properties(...)
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Adds a defined named range or formula.
|
|
482
|
+
#
|
|
483
|
+
# @example
|
|
484
|
+
# s.defined_name("TaxRate", "0.10")
|
|
485
|
+
#
|
|
486
|
+
# @param name [String] The name.
|
|
487
|
+
# @param formula [String] The formula or range expression.
|
|
488
|
+
# @param sheet_id [Integer, nil] Optional sheet scope.
|
|
489
|
+
# @param hidden [Boolean] Whether the name is hidden.
|
|
490
|
+
# @return [void]
|
|
491
|
+
# @api public
|
|
492
|
+
#: (String name, String formula, ?sheet_id: Integer | nil, ?hidden: bool) -> void
|
|
493
|
+
def defined_name(...)
|
|
494
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
495
|
+
|
|
496
|
+
@writer.defined_name(...)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Freezes rows and/or columns for scrolling.
|
|
500
|
+
#
|
|
501
|
+
# @example Freeze top row
|
|
502
|
+
# s.freeze_pane(row: 1)
|
|
503
|
+
#
|
|
504
|
+
# @example Freeze first column and top 2 rows
|
|
505
|
+
# s.freeze_pane(row: 2, col: 1)
|
|
506
|
+
#
|
|
507
|
+
# @param row [Integer, nil] Number of rows to freeze.
|
|
508
|
+
# @param col [Integer, nil] Number of columns to freeze.
|
|
509
|
+
# @return [void]
|
|
510
|
+
# @api public
|
|
511
|
+
#: (?row: Integer | nil, ?col: Integer | nil) -> void
|
|
512
|
+
def freeze_pane(...)
|
|
513
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
514
|
+
|
|
515
|
+
@writer.freeze_pane(...)
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# simplecov:disable
|
|
519
|
+
# Edge case / untested delegation block
|
|
520
|
+
# Sets the print area range for the sheet.
|
|
521
|
+
#
|
|
522
|
+
# @param ref [String] Range reference.
|
|
523
|
+
# @return [void]
|
|
524
|
+
# @api public
|
|
525
|
+
#: (String ref) -> void
|
|
526
|
+
def print_area(...)
|
|
527
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
528
|
+
|
|
529
|
+
@writer.print_area(...)
|
|
530
|
+
end
|
|
531
|
+
# simplecov:enable
|
|
532
|
+
|
|
533
|
+
# simplecov:disable
|
|
534
|
+
# Edge case / untested delegation block
|
|
535
|
+
# Configures repeating title rows and columns for printing.
|
|
536
|
+
#
|
|
537
|
+
# @param rows [String, nil] Row range to repeat (e.g. "1:2").
|
|
538
|
+
# @param cols [String, nil] Column range to repeat (e.g. "A:B").
|
|
539
|
+
# @return [void]
|
|
540
|
+
# @api public
|
|
541
|
+
#: (?rows: String?, ?cols: String?) -> void
|
|
542
|
+
def print_titles(...)
|
|
543
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
544
|
+
|
|
545
|
+
@writer.print_titles(...)
|
|
546
|
+
end
|
|
547
|
+
# simplecov:enable
|
|
548
|
+
|
|
549
|
+
# Splits sheet view into panes.
|
|
550
|
+
#
|
|
551
|
+
# @param x_split [Numeric, nil] Horizontal split position.
|
|
552
|
+
# @param y_split [Numeric, nil] Vertical split position.
|
|
553
|
+
# @param top_left_cell [String, nil] Top-left visible cell in bottom-right pane.
|
|
554
|
+
# @param active_pane [String, nil] Active pane identifier.
|
|
555
|
+
# @param state [String, nil] Split state.
|
|
556
|
+
# @return [void]
|
|
557
|
+
# @api public
|
|
558
|
+
#: (?x_split: Numeric | nil, ?y_split: Numeric | nil, ?top_left_cell: String?, ?active_pane: String?, ?state: String?) -> void
|
|
559
|
+
def split_pane(...)
|
|
560
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
561
|
+
|
|
562
|
+
@writer.split_pane(...)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# simplecov:disable
|
|
566
|
+
# Edge case / untested delegation block
|
|
567
|
+
# Protects the workbook structure.
|
|
568
|
+
#
|
|
569
|
+
# @param opts [Hash] Protection options.
|
|
570
|
+
# @return [void]
|
|
571
|
+
# @api public
|
|
572
|
+
#: (**untyped opts) -> void
|
|
573
|
+
def protect_workbook(...)
|
|
574
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
575
|
+
|
|
576
|
+
@writer.protect_workbook(...)
|
|
577
|
+
end
|
|
578
|
+
# simplecov:enable
|
|
579
|
+
|
|
580
|
+
# simplecov:disable
|
|
581
|
+
# Edge case / untested delegation block
|
|
582
|
+
# Sets core metadata property.
|
|
583
|
+
#
|
|
584
|
+
# @param name [String, Symbol] Property name.
|
|
585
|
+
# @param value [Object] Property value.
|
|
586
|
+
# @return [void]
|
|
587
|
+
# @api public
|
|
588
|
+
#: (String | Symbol name, untyped value) -> void
|
|
589
|
+
def core_property(...)
|
|
590
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
591
|
+
|
|
592
|
+
@writer.core_property(...)
|
|
593
|
+
end
|
|
594
|
+
# simplecov:enable
|
|
595
|
+
|
|
596
|
+
# Sets the active/selected cell on the sheet.
|
|
597
|
+
#
|
|
598
|
+
# @param active_cell [String] Cell reference (e.g. "A1").
|
|
599
|
+
# @param sqref [String, nil] Selection range.
|
|
600
|
+
# @param pane [String, Symbol, nil] Pane identifier.
|
|
601
|
+
# @return [void]
|
|
602
|
+
# @api public
|
|
603
|
+
#: (String active_cell, ?sqref: String?, ?pane: (String | Symbol)?) -> void
|
|
604
|
+
def select_cell(...)
|
|
605
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
606
|
+
|
|
607
|
+
@writer.select_cell(...)
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# Configures page margins for printing.
|
|
611
|
+
#
|
|
612
|
+
# @param left [Float, nil] Left margin in inches.
|
|
613
|
+
# @param right [Float, nil] Right margin in inches.
|
|
614
|
+
# @param top [Float, nil] Top margin in inches.
|
|
615
|
+
# @param bottom [Float, nil] Bottom margin in inches.
|
|
616
|
+
# @param header [Float, nil] Header margin in inches.
|
|
617
|
+
# @param footer [Float, nil] Footer margin in inches.
|
|
618
|
+
# @return [void]
|
|
619
|
+
# @api public
|
|
620
|
+
#: (?left: Float | nil, ?right: Float | nil, ?top: Float | nil, ?bottom: Float | nil, ?header: Float | nil, ?footer: Float | nil) -> void
|
|
621
|
+
def page_margins(...)
|
|
622
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
623
|
+
|
|
624
|
+
@writer.page_margins(...)
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# Configures page orientation, paper size, and print setup.
|
|
628
|
+
#
|
|
629
|
+
# @param orientation [String, Symbol, nil] "portrait" or "landscape" (or :portrait, :landscape).
|
|
630
|
+
# @param paper_size [Integer, nil] Paper size index (e.g. 9 for A4, 1 for Letter).
|
|
631
|
+
# @param opts [Hash] Additional options (scale, fit_to_width, fit_to_height).
|
|
632
|
+
# @return [void]
|
|
633
|
+
# @api public
|
|
634
|
+
#: (?orientation: (String | Symbol)?, ?paper_size: Integer | nil, **untyped opts) -> void
|
|
635
|
+
def page_setup(...)
|
|
636
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
637
|
+
|
|
638
|
+
@writer.page_setup(...)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# Configures header and footer text for printing.
|
|
642
|
+
#
|
|
643
|
+
# @param opts [Hash] Header and footer specifications.
|
|
644
|
+
# @return [void]
|
|
645
|
+
# @api public
|
|
646
|
+
#: (**untyped opts) -> void
|
|
647
|
+
def header_footer(...)
|
|
648
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
649
|
+
|
|
650
|
+
@writer.header_footer(...)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# Configures print options (e.g. gridlines, headings).
|
|
654
|
+
#
|
|
655
|
+
# @param name [Symbol, String] Print option name.
|
|
656
|
+
# @param value [Object] Print option value.
|
|
657
|
+
# @return [void]
|
|
658
|
+
# @api public
|
|
659
|
+
#: (Symbol | String name, untyped value) -> void
|
|
660
|
+
def print_options(...)
|
|
661
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
662
|
+
|
|
663
|
+
@writer.print_options(...)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# simplecov:disable
|
|
667
|
+
# Edge case / untested delegation block
|
|
668
|
+
# Sets document metadata properties (core, app, custom).
|
|
669
|
+
#
|
|
670
|
+
# @param core [Hash, nil] Core properties (title, creator, subject, etc.).
|
|
671
|
+
# @param app [Hash, nil] App properties (company, manager).
|
|
672
|
+
# @param custom [Hash, nil] Custom properties.
|
|
673
|
+
# @return [void]
|
|
674
|
+
# @api public
|
|
675
|
+
#: (?core: Hash[untyped, untyped]?, ?app: Hash[untyped, untyped]?, ?custom: Hash[untyped, untyped]?) -> void
|
|
676
|
+
def properties(...)
|
|
677
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
678
|
+
|
|
679
|
+
@writer.properties(...)
|
|
680
|
+
end
|
|
681
|
+
# simplecov:enable
|
|
682
|
+
|
|
683
|
+
# simplecov:disable
|
|
684
|
+
# Edge case / untested delegation block
|
|
685
|
+
# Sets app metadata property.
|
|
686
|
+
#
|
|
687
|
+
# @param name [String, Symbol] Property name.
|
|
688
|
+
# @param value [Object] Property value.
|
|
689
|
+
# @return [void]
|
|
690
|
+
# @api public
|
|
691
|
+
#: (String | Symbol name, untyped value) -> void
|
|
692
|
+
def app_property(...)
|
|
693
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
694
|
+
|
|
695
|
+
@writer.app_property(...)
|
|
696
|
+
end
|
|
697
|
+
# simplecov:enable
|
|
698
|
+
|
|
699
|
+
# Protects the worksheet against modifications.
|
|
700
|
+
#
|
|
701
|
+
# @param opts [Hash] Protection options.
|
|
702
|
+
# @return [void]
|
|
703
|
+
# @api public
|
|
704
|
+
#: (**untyped opts) -> void
|
|
705
|
+
def protect_sheet(...)
|
|
706
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
707
|
+
|
|
708
|
+
@writer.protect_sheet(...)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# simplecov:disable
|
|
712
|
+
# Edge case / untested delegation block
|
|
713
|
+
# Sets custom metadata property.
|
|
714
|
+
#
|
|
715
|
+
# @param name [String, Symbol] Property name.
|
|
716
|
+
# @param value [Object] Property value.
|
|
717
|
+
# @return [void]
|
|
718
|
+
# @api public
|
|
719
|
+
#: (String | Symbol name, untyped value) -> void
|
|
720
|
+
def custom_property(...)
|
|
721
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
722
|
+
|
|
723
|
+
@writer.custom_property(...)
|
|
724
|
+
end
|
|
725
|
+
# simplecov:enable
|
|
726
|
+
|
|
727
|
+
# Inserts an image into the sheet.
|
|
728
|
+
#
|
|
729
|
+
# @param file_data [String] Binary image data or file content.
|
|
730
|
+
# @param ext [String] Image extension ("png", "jpeg", etc.).
|
|
731
|
+
# @param from_col [Integer] Starting column index (0-based).
|
|
732
|
+
# @param from_row [Integer] Starting row index (0-based).
|
|
733
|
+
# @param to_col [Integer] Ending column index (0-based).
|
|
734
|
+
# @param to_row [Integer] Ending row index (0-based).
|
|
735
|
+
# @param opts [Hash] Additional anchor and sizing options.
|
|
736
|
+
# @return [void]
|
|
737
|
+
# @api public
|
|
738
|
+
#: (String file_data, ?ext: String, ?from_col: Integer, ?from_row: Integer, ?to_col: Integer, ?to_row: Integer, **untyped opts) -> void
|
|
739
|
+
def image(...)
|
|
740
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
741
|
+
|
|
742
|
+
@writer.image(...)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
# Configures sheet view settings (zoom scale, grid lines visibility).
|
|
746
|
+
#
|
|
747
|
+
# @param name [Symbol, String] View setting name.
|
|
748
|
+
# @param value [Object] View setting value.
|
|
749
|
+
# @return [void]
|
|
750
|
+
# @api public
|
|
751
|
+
#: (Symbol | String name, untyped value) -> void
|
|
752
|
+
def sheet_view(...)
|
|
753
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
754
|
+
|
|
755
|
+
@writer.sheet_view(...)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
# simplecov:disable
|
|
759
|
+
# Edge case / untested delegation block
|
|
760
|
+
# Adds a horizontal page break after the given row index.
|
|
761
|
+
#
|
|
762
|
+
# @param row_index [Integer] 0-based row index.
|
|
763
|
+
# @return [void]
|
|
764
|
+
# @api public
|
|
765
|
+
#: (Integer row_index) -> void
|
|
766
|
+
def page_break_row(...)
|
|
767
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
768
|
+
|
|
769
|
+
@writer.page_break_row(...)
|
|
770
|
+
end
|
|
771
|
+
# simplecov:enable
|
|
772
|
+
|
|
773
|
+
# simplecov:disable
|
|
774
|
+
# Edge case / untested delegation block
|
|
775
|
+
# Adds a vertical page break after the given column index.
|
|
776
|
+
#
|
|
777
|
+
# @param col_index [Integer] 0-based column index.
|
|
778
|
+
# @return [void]
|
|
779
|
+
# @api public
|
|
780
|
+
#: (Integer col_index) -> void
|
|
781
|
+
def page_break_col(...)
|
|
782
|
+
raise Error, "Sheet '#{@sheet_name}' is no longer active. In streaming mode, you cannot write to a previous sheet." if @writer.current_sheet != @sheet_name
|
|
783
|
+
|
|
784
|
+
@writer.page_break_col(...)
|
|
785
|
+
end
|
|
786
|
+
# simplecov:enable
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
# Adds a new sheet to the workbook and starts streaming rows into it.
|
|
790
|
+
#
|
|
791
|
+
# @param name [String, nil] Sheet name (max 31 characters).
|
|
792
|
+
# @param opts [Hash] Sheet-level configuration.
|
|
793
|
+
# @yield [sheet_proxy]
|
|
794
|
+
# @yieldparam sheet_proxy [WorksheetProxy] The streaming sheet proxy.
|
|
795
|
+
# @return [String] The sheet name.
|
|
796
|
+
# @api public
|
|
797
|
+
#: (?String? name, **untyped opts) ?{ (WorksheetProxy) -> void } -> untyped
|
|
798
|
+
def sheet(name = nil, **opts)
|
|
799
|
+
name ||= "Sheet#{@sheets.size + 1}"
|
|
800
|
+
raise ArgumentError, "Sheet name '#{name}' must be <= 31 characters (Excel limitation)" if @strict_excel_mode && name.length > 31
|
|
801
|
+
raise ArgumentError, "Sheet name '#{name}' contains invalid characters (ECMA-376 OOXML specification)" if name.match?(%r{[\[\]*?/\\]})
|
|
802
|
+
raise ArgumentError, "Sheet name '#{name}' is already used. Excel requires unique sheet names." if @strict_excel_mode && @sheets.map { |s| s.respond_to?(:name) ? s.name.downcase : s.to_s.downcase }.include?(name.downcase)
|
|
803
|
+
|
|
804
|
+
internal_sheet_setup(name)
|
|
805
|
+
opts.each { |k, v| sheet_properties(k, v) }
|
|
806
|
+
|
|
807
|
+
yield WorksheetProxy.new(self, @current_sheet) if block_given?
|
|
808
|
+
@current_sheet
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
# Internal: Start or switch to a named sheet (internal helper).
|
|
812
|
+
#: (?String? name) ?{ (WorksheetProxy) -> void } -> (WorksheetProxy | nil)
|
|
813
|
+
def internal_sheet_setup(name = nil)
|
|
814
|
+
flush_current_sheet
|
|
815
|
+
name ||= "Sheet#{@sheets.size + 1}"
|
|
816
|
+
@current_sheet = name
|
|
817
|
+
@current_row_index = 0
|
|
818
|
+
@current_tempfile = Tempfile.new(["xlsxrb_rows", ".xml"])
|
|
819
|
+
@current_tempfile.binmode
|
|
820
|
+
@current_row_writer = Ooxml::WorksheetWriter.new(@current_tempfile)
|
|
821
|
+
@current_row_writer.instance_variable_set(:@started, true)
|
|
822
|
+
|
|
823
|
+
@current_columns = []
|
|
824
|
+
@current_charts = []
|
|
825
|
+
@current_hyperlinks = []
|
|
826
|
+
@current_auto_filter = nil
|
|
827
|
+
@current_filter_columns = {}
|
|
828
|
+
@current_sort_state = nil
|
|
829
|
+
@current_data_validations = []
|
|
830
|
+
@current_conditional_formats = []
|
|
831
|
+
@current_tables = []
|
|
832
|
+
@current_pivot_tables = []
|
|
833
|
+
@current_sparkline_groups = []
|
|
834
|
+
@current_comments = []
|
|
835
|
+
@current_merge_cells = []
|
|
836
|
+
@current_freeze_pane = nil
|
|
837
|
+
@current_split_pane = nil
|
|
838
|
+
@current_selection = nil
|
|
839
|
+
@current_page_margins = nil
|
|
840
|
+
@current_page_setup = {}
|
|
841
|
+
@current_header_footer = {}
|
|
842
|
+
@current_print_options = {}
|
|
843
|
+
@current_sheet_protection = nil
|
|
844
|
+
@current_images = []
|
|
845
|
+
@current_shapes = []
|
|
846
|
+
@current_sheet_properties = {}
|
|
847
|
+
@current_sheet_view = {}
|
|
848
|
+
@current_row_breaks = []
|
|
849
|
+
@current_col_breaks = []
|
|
850
|
+
@current_cells = {}
|
|
851
|
+
|
|
852
|
+
return unless block_given?
|
|
853
|
+
|
|
854
|
+
# simplecov:disable
|
|
855
|
+
# Edge case / untested delegation block
|
|
856
|
+
yield self
|
|
857
|
+
flush_current_sheet
|
|
858
|
+
# simplecov:enable
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
# Appends a row of values to the active sheet.
|
|
862
|
+
#
|
|
863
|
+
# @param values [Array<Object>, Hash] The cell values.
|
|
864
|
+
# @param styles [String, Symbol, Array, Hash, nil] Style names or inline style definitions.
|
|
865
|
+
# @param height [Float, Integer, nil] The row height in points (0 - 409).
|
|
866
|
+
# @param hidden [Boolean] Whether the row is hidden.
|
|
867
|
+
# @param custom_height [Boolean] Whether custom row height is set.
|
|
868
|
+
# @param outline_level [Integer, nil] Grouping/outline level (0 - 7).
|
|
869
|
+
# @return [void]
|
|
870
|
+
# @api public
|
|
871
|
+
#: (Array[untyped] | Hash[untyped, untyped] values, ?styles: untyped, ?height: Float | Integer | nil, ?hidden: bool, ?custom_height: bool, ?outline_level: Integer | nil) -> void
|
|
872
|
+
def row(values, styles: nil, height: nil, hidden: false, custom_height: false, outline_level: nil)
|
|
873
|
+
sheet if @current_sheet.nil?
|
|
874
|
+
|
|
875
|
+
row_index = @current_row_index
|
|
876
|
+
# See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
|
|
877
|
+
if @strict_excel_mode
|
|
878
|
+
raise ArgumentError, "Row index #{row_index} exceeds Excel limit of 1,048,576 rows" if row_index >= 1_048_576
|
|
879
|
+
raise ArgumentError, "Row height #{height} must be between 0 and 409 points (Excel limitation)" if height && (height.negative? || height > 409)
|
|
880
|
+
end
|
|
881
|
+
@current_row_index += 1
|
|
882
|
+
|
|
883
|
+
if values.is_a?(Hash)
|
|
884
|
+
max_col = values.keys.map { |k| Elements::Cell.column_index(k) }.max || -1
|
|
885
|
+
cells_array = Array.new(max_col + 1)
|
|
886
|
+
values.each do |k, v|
|
|
887
|
+
idx = Elements::Cell.column_index(k)
|
|
888
|
+
cells_array[idx] = v
|
|
889
|
+
end
|
|
890
|
+
values = cells_array
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
if styles.is_a?(Hash)
|
|
894
|
+
expanded_styles = {}
|
|
895
|
+
styles.each do |k, v|
|
|
896
|
+
if k.is_a?(Range) || k.is_a?(Array)
|
|
897
|
+
k.each { |idx| expanded_styles[Elements::Cell.column_index(idx)] = v }
|
|
898
|
+
else
|
|
899
|
+
expanded_styles[Elements::Cell.column_index(k)] = v
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
max_col_style = expanded_styles.keys.max || -1
|
|
903
|
+
styles_array = Array.new(max_col_style + 1)
|
|
904
|
+
expanded_styles.each do |idx, v|
|
|
905
|
+
styles_array[idx] = v
|
|
906
|
+
end
|
|
907
|
+
styles = styles_array
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
# Auto-detect Date / Time for built-in styles
|
|
911
|
+
values.each_with_index do |val, idx|
|
|
912
|
+
cell_style = styles.is_a?(Array) ? styles[idx] : styles
|
|
913
|
+
|
|
914
|
+
if val.is_a?(Date) && cell_style.nil?
|
|
915
|
+
style("__xlsxrb_date", number_format: "yyyy-mm-dd") unless @styles.key?("__xlsxrb_date")
|
|
916
|
+
styles = [] if styles.nil?
|
|
917
|
+
styles = Array.new(values.size, styles) unless styles.is_a?(Array)
|
|
918
|
+
styles[idx] = "__xlsxrb_date"
|
|
919
|
+
elsif val.is_a?(Time) && cell_style.nil?
|
|
920
|
+
style("__xlsxrb_time", number_format: "yyyy-mm-dd hh:mm:ss") unless @styles.key?("__xlsxrb_time")
|
|
921
|
+
styles = [] if styles.nil?
|
|
922
|
+
styles = Array.new(values.size, styles) unless styles.is_a?(Array)
|
|
923
|
+
styles[idx] = "__xlsxrb_time"
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
has_charts = @current_charts && !@current_charts.empty?
|
|
928
|
+
row_num = row_index + 1 if has_charts
|
|
929
|
+
|
|
930
|
+
max_len = values.size
|
|
931
|
+
max_len = [max_len, styles.size].max if styles.is_a?(Array)
|
|
932
|
+
# See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
|
|
933
|
+
raise ArgumentError, "Row contains #{max_len} columns, exceeding Excel limit of 16_384 columns" if @strict_excel_mode && max_len > 16_384
|
|
934
|
+
|
|
935
|
+
if has_charts || @strict_excel_mode
|
|
936
|
+
@current_cells ||= {} if has_charts
|
|
937
|
+
max_len.times do |col_idx|
|
|
938
|
+
val = col_idx < values.size ? values[col_idx] : nil
|
|
939
|
+
next if val.nil?
|
|
940
|
+
|
|
941
|
+
raise ArgumentError, "Invalid cell value type or value: #{val.class} for value #{val.inspect}" unless val.nil? || val.is_a?(String) || (val.is_a?(Numeric) && !(val.is_a?(Float) && (val.infinite? || val.nan?))) || val.is_a?(TrueClass) || val.is_a?(FalseClass) || val.is_a?(Date) || val.is_a?(Time) || val.is_a?(Elements::Formula) || (val.is_a?(Hash) && val.key?(:formula)) || val.is_a?(Elements::RichText) || val.is_a?(Elements::CellError)
|
|
942
|
+
|
|
943
|
+
# See: https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
|
|
944
|
+
raise ArgumentError, "Cell text length #{val.length} exceeds Excel limit of 32,767 characters" if @strict_excel_mode && val.is_a?(String) && val.length > 32_767
|
|
945
|
+
|
|
946
|
+
if has_charts
|
|
947
|
+
addr = "#{Elements::Cell.column_letter(col_idx)}#{row_num}"
|
|
948
|
+
@current_cells[addr] = val
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
attrs = nil
|
|
954
|
+
if height || hidden || outline_level
|
|
955
|
+
attrs = {}
|
|
956
|
+
attrs[:height] = height if height
|
|
957
|
+
attrs[:hidden] = true if hidden
|
|
958
|
+
attrs[:custom_height] = custom_height || !height.nil?
|
|
959
|
+
attrs[:outline_level] = outline_level if outline_level
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
@current_row_writer.write_row_values(row_index, values, styles: styles, style_map: @style_name_to_id, sst: @sst, sst_index: @sst_index, attrs: attrs)
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
# Sets column formatting and properties for one or multiple columns.
|
|
966
|
+
#
|
|
967
|
+
# @param index [Integer, String, Range, Array] Column index (0-based) or letter ("A".."D").
|
|
968
|
+
# @param width [Float, Integer, nil] Column width in character units (0 - 255).
|
|
969
|
+
# @param hidden [Boolean] Whether the column is hidden.
|
|
970
|
+
# @param custom_width [Boolean] Whether custom width is set.
|
|
971
|
+
# @param outline_level [Integer, nil] Grouping/outline level.
|
|
972
|
+
# @return [void]
|
|
973
|
+
# @api public
|
|
974
|
+
#: (Integer | String | Range[Integer | String] | Array[Integer | String] index, ?width: Float | Integer | nil, ?hidden: bool, ?custom_width: bool, ?outline_level: Integer | nil) -> void
|
|
975
|
+
def column(index, width: nil, hidden: false, custom_width: false, outline_level: nil)
|
|
976
|
+
raise ArgumentError, "Column width #{width} must be between 0 and 255 characters (Excel limitation)" if @strict_excel_mode && width && (width.negative? || width > 255)
|
|
977
|
+
|
|
978
|
+
indices = case index
|
|
979
|
+
when Range, Array
|
|
980
|
+
index.map { |i| Elements::Cell.column_index(i) }
|
|
981
|
+
else
|
|
982
|
+
[Elements::Cell.column_index(index)]
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
sheet if @current_sheet.nil?
|
|
986
|
+
|
|
987
|
+
indices.each do |idx|
|
|
988
|
+
@current_columns << { index: idx, width: width, hidden: hidden, custom_width: custom_width || !width.nil?, outline_level: outline_level }
|
|
989
|
+
end
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
# Adds a chart to the current worksheet.
|
|
993
|
+
#
|
|
994
|
+
# @param options [Hash] Chart options.
|
|
995
|
+
# @yield [builder]
|
|
996
|
+
# @yieldparam builder [Xlsxrb::ChartBuilder]
|
|
997
|
+
# @return [void]
|
|
998
|
+
# @api public
|
|
999
|
+
#: (**untyped options) ?{ (ChartBuilder) -> void } -> void
|
|
1000
|
+
def chart(**options)
|
|
1001
|
+
sheet if @current_sheet.nil?
|
|
1002
|
+
|
|
1003
|
+
if block_given?
|
|
1004
|
+
builder = ChartBuilder.new
|
|
1005
|
+
yield builder
|
|
1006
|
+
options = builder.options.merge(options)
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
@current_charts << options
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
# Adds a hyperlink on a cell.
|
|
1013
|
+
#
|
|
1014
|
+
# @param cell [String, Integer] Cell coordinate (e.g. "A1").
|
|
1015
|
+
# @param url [String, nil] Target URL.
|
|
1016
|
+
# @param display [String, nil] Display text.
|
|
1017
|
+
# @param tooltip [String, nil] Tooltip text.
|
|
1018
|
+
# @param location [String, nil] Internal location.
|
|
1019
|
+
# @return [void]
|
|
1020
|
+
# @api public
|
|
1021
|
+
#: (String | Integer cell, ?String? url, ?display: String?, ?tooltip: String?, ?location: String?) -> void
|
|
1022
|
+
def hyperlink(cell, url = nil, display: nil, tooltip: nil, location: nil)
|
|
1023
|
+
sheet if @current_sheet.nil?
|
|
1024
|
+
link = { cell: cell }
|
|
1025
|
+
link[:url] = url if url
|
|
1026
|
+
link[:display] = display if display
|
|
1027
|
+
link[:tooltip] = tooltip if tooltip
|
|
1028
|
+
link[:location] = location if location
|
|
1029
|
+
@current_hyperlinks << link
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
# Sets the auto-filter range on the active sheet.
|
|
1033
|
+
#
|
|
1034
|
+
# @param range [String] Cell range (e.g. "A1:E100").
|
|
1035
|
+
# @return [void]
|
|
1036
|
+
# @api public
|
|
1037
|
+
#: (String range) -> void
|
|
1038
|
+
def auto_filter(range)
|
|
1039
|
+
sheet if @current_sheet.nil?
|
|
1040
|
+
@current_auto_filter = range
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
# Sets filter criteria on a column in the auto-filter.
|
|
1044
|
+
#
|
|
1045
|
+
# @param col_id [Integer] 0-based column index.
|
|
1046
|
+
# @param filter [Hash] Filter criteria.
|
|
1047
|
+
# @return [void]
|
|
1048
|
+
# @api public
|
|
1049
|
+
#: (untyped col_id, untyped filter) -> untyped
|
|
1050
|
+
def filter_column(col_id, filter)
|
|
1051
|
+
sheet if @current_sheet.nil?
|
|
1052
|
+
@current_filter_columns[col_id] = filter
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
# Configures column sort state on the active sheet.
|
|
1056
|
+
#
|
|
1057
|
+
# @param ref [String] The sorted range.
|
|
1058
|
+
# @param sort_conditions [Array<Hash>] Sort conditions.
|
|
1059
|
+
# @param opts [Hash] Additional options.
|
|
1060
|
+
# @return [void]
|
|
1061
|
+
# @api public
|
|
1062
|
+
#: (untyped ref, untyped sort_conditions, **untyped opts) -> untyped
|
|
1063
|
+
def sort_state(ref, sort_conditions, **opts)
|
|
1064
|
+
sheet if @current_sheet.nil?
|
|
1065
|
+
@current_sort_state = { ref: ref, sort_conditions: sort_conditions }.merge(opts)
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
# Adds a data validation rule.
|
|
1069
|
+
#
|
|
1070
|
+
# @param sqref [String] Cell range.
|
|
1071
|
+
# @param opts [Hash] Validation options.
|
|
1072
|
+
# @return [void]
|
|
1073
|
+
# @api public
|
|
1074
|
+
#: (untyped sqref, **untyped opts) -> void
|
|
1075
|
+
def validate_data(sqref, **opts)
|
|
1076
|
+
sheet if @current_sheet.nil?
|
|
1077
|
+
@current_data_validations << opts.merge(sqref: sqref)
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1080
|
+
# Adds a conditional formatting rule.
|
|
1081
|
+
#
|
|
1082
|
+
# @param sqref [String] Cell range.
|
|
1083
|
+
# @param opts [Hash] Rule options.
|
|
1084
|
+
# @return [void]
|
|
1085
|
+
# @api public
|
|
1086
|
+
#: (untyped sqref, **untyped opts) -> void
|
|
1087
|
+
def conditional_format(sqref, **opts)
|
|
1088
|
+
sheet if @current_sheet.nil?
|
|
1089
|
+
@current_conditional_formats << opts.merge(sqref: sqref)
|
|
1090
|
+
end
|
|
1091
|
+
|
|
1092
|
+
# Adds an Excel Table (ListObject).
|
|
1093
|
+
#
|
|
1094
|
+
# @param ref [String] Table range.
|
|
1095
|
+
# @param columns [Array<String>] Column names.
|
|
1096
|
+
# @param name [String, nil] Table name.
|
|
1097
|
+
# @param display_name [String, nil] Display name.
|
|
1098
|
+
# @param style [String, nil] Table style.
|
|
1099
|
+
# @param opts [Hash] Additional options.
|
|
1100
|
+
# @return [void]
|
|
1101
|
+
# @api public
|
|
1102
|
+
#: (untyped ref, columns: untyped, ?name: untyped, ?display_name: untyped, ?style: untyped, **untyped opts) -> void
|
|
1103
|
+
def table(ref, columns:, name: nil, display_name: nil, style: nil, **opts)
|
|
1104
|
+
sheet if @current_sheet.nil?
|
|
1105
|
+
tbl = { ref: ref, columns: columns }
|
|
1106
|
+
tbl[:name] = name if name
|
|
1107
|
+
tbl[:display_name] = display_name if display_name
|
|
1108
|
+
tbl[:style] = style if style
|
|
1109
|
+
tbl.merge!(opts)
|
|
1110
|
+
@current_tables << tbl
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
# Adds a Pivot Table.
|
|
1114
|
+
#
|
|
1115
|
+
# @param source_ref [String] Source data range.
|
|
1116
|
+
# @param row_fields [Array<Integer>] 0-based field indices for rows.
|
|
1117
|
+
# @param data_fields [Array<Hash>] Data fields.
|
|
1118
|
+
# @param col_fields [Array<Integer>] 0-based field indices for columns.
|
|
1119
|
+
# @param dest_ref [String] Top-left destination cell (default: "E1").
|
|
1120
|
+
# @param name [String, nil] Pivot table name.
|
|
1121
|
+
# @param field_names [Array<String>, nil] Override field names.
|
|
1122
|
+
# @param items [Array, nil] Items configuration.
|
|
1123
|
+
# @param opts [Hash] Additional options.
|
|
1124
|
+
# @return [void]
|
|
1125
|
+
# @api public
|
|
1126
|
+
#: (untyped source_ref, row_fields: untyped, data_fields: untyped, ?col_fields: untyped, ?dest_ref: untyped, ?name: untyped, ?field_names: untyped, ?items: untyped, **untyped opts) -> void
|
|
1127
|
+
def pivot_table(source_ref, row_fields:, data_fields:, col_fields: [], dest_ref: "E1", name: nil, field_names: nil, items: nil)
|
|
1128
|
+
sheet if @current_sheet.nil?
|
|
1129
|
+
@current_pivot_tables ||= []
|
|
1130
|
+
@current_pivot_tables << {
|
|
1131
|
+
source_ref: source_ref, row_fields: row_fields,
|
|
1132
|
+
data_fields: data_fields, col_fields: col_fields,
|
|
1133
|
+
dest_ref: dest_ref, name: name,
|
|
1134
|
+
field_names: field_names, items: items
|
|
1135
|
+
}
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
# Adds a comment to a cell.
|
|
1139
|
+
#
|
|
1140
|
+
# @param cell [String, Integer] Cell coordinate (e.g. "A1").
|
|
1141
|
+
# @param text [String] Comment text.
|
|
1142
|
+
# @param author [String] Author name.
|
|
1143
|
+
# @return [void]
|
|
1144
|
+
# @api public
|
|
1145
|
+
#: (String | Integer cell, String text, ?author: ::String) -> void
|
|
1146
|
+
def comment(cell, text, author: "Author")
|
|
1147
|
+
sheet if @current_sheet.nil?
|
|
1148
|
+
@current_comments << { cell: cell, text: text, author: author }
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
# Adds a sparkline group.
|
|
1152
|
+
#
|
|
1153
|
+
# @param sparklines [Array<Hash>] Sparkline definitions.
|
|
1154
|
+
# @param type [String, nil] "line", "column", or "stacked".
|
|
1155
|
+
# @param opts [Hash] Additional options.
|
|
1156
|
+
# @return [void]
|
|
1157
|
+
# @api public
|
|
1158
|
+
#: (sparklines: untyped, ?type: untyped, **untyped opts) -> void
|
|
1159
|
+
def sparkline_group(sparklines:, type: nil, **opts)
|
|
1160
|
+
sheet if @current_sheet.nil?
|
|
1161
|
+
group = { sparklines: sparklines }
|
|
1162
|
+
group[:type] = type if type
|
|
1163
|
+
group.merge!(opts)
|
|
1164
|
+
@current_sparkline_groups << group
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
# Merges a range of cells into a single cell.
|
|
1168
|
+
#
|
|
1169
|
+
# @param range [String, Hash, nil] Cell range (e.g. "A1:B2") or hash of coordinates.
|
|
1170
|
+
# @param row [Integer, nil] Single row index.
|
|
1171
|
+
# @param col_start [Integer, String, nil] Starting column.
|
|
1172
|
+
# @param col_end [Integer, String, nil] Ending column.
|
|
1173
|
+
# @param row_start [Integer, nil] Starting row index.
|
|
1174
|
+
# @param row_end [Integer, nil] Ending row index.
|
|
1175
|
+
# @return [void]
|
|
1176
|
+
# @api public
|
|
1177
|
+
#: (?(String | Hash[Symbol, Integer | String])? range, ?row: Integer?, ?col_start: (Integer | String)?, ?col_end: (Integer | String)?, ?row_start: Integer?, ?row_end: Integer?) -> void
|
|
1178
|
+
def merge(range = nil, row: nil, col_start: nil, col_end: nil, row_start: nil, row_end: nil)
|
|
1179
|
+
sheet if @current_sheet.nil?
|
|
1180
|
+
if range.is_a?(Hash)
|
|
1181
|
+
row = range[:row]
|
|
1182
|
+
row_start = range[:row_start]
|
|
1183
|
+
row_end = range[:row_end]
|
|
1184
|
+
col_start = range[:col_start]
|
|
1185
|
+
col_end = range[:col_end]
|
|
1186
|
+
range = nil
|
|
1187
|
+
end
|
|
1188
|
+
|
|
1189
|
+
if range
|
|
1190
|
+
raise ArgumentError, "Invalid merge range format: '#{range}'. Expected format like 'A1:B2'." if @strict_excel_mode && !range.match?(/^[A-Za-z]{1,3}\d+(:[A-Za-z]{1,3}\d+)?$/)
|
|
1191
|
+
|
|
1192
|
+
@current_merge_cells << range
|
|
1193
|
+
else
|
|
1194
|
+
r_start = row || row_start || 0
|
|
1195
|
+
r_end = row || row_end || 0
|
|
1196
|
+
c_start = Elements::Cell.column_index(col_start || 0)
|
|
1197
|
+
c_end = Elements::Cell.column_index(col_end || 0)
|
|
1198
|
+
start_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_start)}#{r_start + 1}"
|
|
1199
|
+
end_ref = "#{Xlsxrb::Elements::Cell.column_letter(c_end)}#{r_end + 1}"
|
|
1200
|
+
@current_merge_cells << "#{start_ref}:#{end_ref}"
|
|
1201
|
+
end
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
# Freezes window panes at the given row and column.
|
|
1205
|
+
#
|
|
1206
|
+
# @param row [Integer] Number of rows to freeze.
|
|
1207
|
+
# @param col [Integer, String] Number of columns to freeze.
|
|
1208
|
+
# @return [void]
|
|
1209
|
+
# @api public
|
|
1210
|
+
#: (?row: Integer, ?col: (Integer | String)) -> void
|
|
1211
|
+
def freeze_pane(row: 0, col: 0)
|
|
1212
|
+
col = Elements::Cell.column_index(col)
|
|
1213
|
+
sheet if @current_sheet.nil?
|
|
1214
|
+
@current_freeze_pane = { row: row, col: col }
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
# Splits window panes without freezing.
|
|
1218
|
+
#
|
|
1219
|
+
# @param x_split [Integer] Horizontal split in points.
|
|
1220
|
+
# @param y_split [Integer] Vertical split in points.
|
|
1221
|
+
# @param top_left_cell [String, nil] Top-left cell reference in bottom-right pane.
|
|
1222
|
+
# @return [void]
|
|
1223
|
+
# @api public
|
|
1224
|
+
#: (?x_split: ::Integer, ?y_split: ::Integer, ?top_left_cell: String?) -> void
|
|
1225
|
+
def split_pane(x_split: 0, y_split: 0, top_left_cell: nil)
|
|
1226
|
+
sheet if @current_sheet.nil?
|
|
1227
|
+
@current_split_pane = { x_split: x_split, y_split: y_split, top_left_cell: top_left_cell }
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
# Sets the active cell selection on the current sheet.
|
|
1231
|
+
#
|
|
1232
|
+
# @param active_cell [String] Active cell reference (e.g. "A1").
|
|
1233
|
+
# @param sqref [String, nil] Selection range.
|
|
1234
|
+
# @param pane [String, Symbol, nil] Target pane.
|
|
1235
|
+
# @return [void]
|
|
1236
|
+
# @api public
|
|
1237
|
+
#: (String active_cell, ?sqref: String?, ?pane: (String | Symbol)?) -> void
|
|
1238
|
+
def select_cell(active_cell, sqref: nil, pane: nil)
|
|
1239
|
+
sheet if @current_sheet.nil?
|
|
1240
|
+
@current_selection = { active_cell: active_cell, sqref: sqref || active_cell }
|
|
1241
|
+
@current_selection[:pane] = pane if pane
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
# Sets page margins in inches for printing.
|
|
1245
|
+
#
|
|
1246
|
+
# @param left [Float, nil] Left margin.
|
|
1247
|
+
# @param right [Float, nil] Right margin.
|
|
1248
|
+
# @param top [Float, nil] Top margin.
|
|
1249
|
+
# @param bottom [Float, nil] Bottom margin.
|
|
1250
|
+
# @param header [Float, nil] Header margin.
|
|
1251
|
+
# @param footer [Float, nil] Footer margin.
|
|
1252
|
+
# @return [void]
|
|
1253
|
+
# @api public
|
|
1254
|
+
#: (?left: Float?, ?right: Float?, ?top: Float?, ?bottom: Float?, ?header: Float?, ?footer: Float?) -> void
|
|
1255
|
+
def page_margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
|
|
1256
|
+
sheet if @current_sheet.nil?
|
|
1257
|
+
@current_page_margins = { left: left, right: right, top: top, bottom: bottom, header: header, footer: footer }.compact
|
|
1258
|
+
end
|
|
1259
|
+
|
|
1260
|
+
# Sets page setup configuration.
|
|
1261
|
+
#
|
|
1262
|
+
# @param opts [Hash] Page setup options (e.g. orientation: :landscape).
|
|
1263
|
+
# @return [void]
|
|
1264
|
+
# @api public
|
|
1265
|
+
#: (**untyped opts) -> void
|
|
1266
|
+
def page_setup(**opts)
|
|
1267
|
+
sheet if @current_sheet.nil?
|
|
1268
|
+
@current_page_setup.merge!(opts)
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
# Configures headers and footers for printing.
|
|
1272
|
+
#
|
|
1273
|
+
# @param opts [Hash] Header/footer options.
|
|
1274
|
+
# @return [void]
|
|
1275
|
+
# @api public
|
|
1276
|
+
#: (**untyped opts) -> void
|
|
1277
|
+
def header_footer(**opts)
|
|
1278
|
+
sheet if @current_sheet.nil?
|
|
1279
|
+
@current_header_footer.merge!(opts)
|
|
1280
|
+
end
|
|
1281
|
+
|
|
1282
|
+
# Sets a print option (e.g. grid_lines: true).
|
|
1283
|
+
#
|
|
1284
|
+
# @param name [Symbol] Option name.
|
|
1285
|
+
# @param value [Object] Option value.
|
|
1286
|
+
# @return [void]
|
|
1287
|
+
# @api public
|
|
1288
|
+
#: (Symbol name, untyped value) -> void
|
|
1289
|
+
def print_options(name, value)
|
|
1290
|
+
sheet if @current_sheet.nil?
|
|
1291
|
+
@current_print_options[name] = value
|
|
1292
|
+
end
|
|
1293
|
+
|
|
1294
|
+
# Sets sheet-level protection with optional password hashing.
|
|
1295
|
+
#
|
|
1296
|
+
# @param opts [Hash] Protection options.
|
|
1297
|
+
# @return [void]
|
|
1298
|
+
# @api public
|
|
1299
|
+
#: (**untyped opts) -> void
|
|
1300
|
+
def protect_sheet(**opts)
|
|
1301
|
+
sheet if @current_sheet.nil?
|
|
1302
|
+
normalized = opts.dup
|
|
1303
|
+
plain_password = normalized[:password]
|
|
1304
|
+
needs_hash = plain_password.is_a?(String) && !plain_password.empty? &&
|
|
1305
|
+
normalized[:algorithm_name].nil? && normalized[:hash_value].nil? &&
|
|
1306
|
+
normalized[:salt_value].nil? && normalized[:spin_count].nil? &&
|
|
1307
|
+
!plain_password.match?(/\A[0-9A-Fa-f]{4}\z/)
|
|
1308
|
+
if needs_hash
|
|
1309
|
+
normalized.delete(:password)
|
|
1310
|
+
normalized.merge!(Xlsxrb::Ooxml::Utils.hash_password(plain_password))
|
|
1311
|
+
end
|
|
1312
|
+
@current_sheet_protection = normalized
|
|
1313
|
+
end
|
|
1314
|
+
|
|
1315
|
+
# Inserts an embedded image into the active sheet.
|
|
1316
|
+
#
|
|
1317
|
+
# @param file_data [String] Binary image data.
|
|
1318
|
+
# @param ext [String] File extension ("png", "jpeg", etc.).
|
|
1319
|
+
# @param from_col [Integer] Top-left starting column.
|
|
1320
|
+
# @param from_row [Integer] Top-left starting row.
|
|
1321
|
+
# @param to_col [Integer] Bottom-right ending column.
|
|
1322
|
+
# @param to_row [Integer] Bottom-right ending row.
|
|
1323
|
+
# @param opts [Hash] Additional options.
|
|
1324
|
+
# @return [void]
|
|
1325
|
+
# @api public
|
|
1326
|
+
#: (String file_data, ?ext: ::String, ?from_col: ::Integer, ?from_row: ::Integer, ?to_col: ::Integer, ?to_row: ::Integer, **untyped opts) -> void
|
|
1327
|
+
def image(file_data, ext: "png", from_col: 0, from_row: 0, to_col: 5, to_row: 10, **opts)
|
|
1328
|
+
sheet if @current_sheet.nil?
|
|
1329
|
+
img = { file_data: file_data, ext: ext, from_col: from_col, from_row: from_row, to_col: to_col, to_row: to_row }
|
|
1330
|
+
img.merge!(opts)
|
|
1331
|
+
@current_images << img
|
|
1332
|
+
end
|
|
1333
|
+
|
|
1334
|
+
# Inserts a drawing shape.
|
|
1335
|
+
#
|
|
1336
|
+
# @param preset [String] Preset shape name.
|
|
1337
|
+
# @param text [String, nil] Label text.
|
|
1338
|
+
# @param from_col [Integer] Top-left column.
|
|
1339
|
+
# @param from_row [Integer] Top-left row.
|
|
1340
|
+
# @param to_col [Integer] Bottom-right column.
|
|
1341
|
+
# @param to_row [Integer] Bottom-right row.
|
|
1342
|
+
# @param opts [Hash] Additional options.
|
|
1343
|
+
# @return [void]
|
|
1344
|
+
# @api public
|
|
1345
|
+
#: (**untyped opts) -> void
|
|
1346
|
+
def shape(preset: "rect", text: nil, from_col: 0, from_row: 0, to_col: 5, to_row: 5, **opts)
|
|
1347
|
+
sheet if @current_sheet.nil?
|
|
1348
|
+
shape = { preset: preset, text: text, from_col: from_col, from_row: from_row, to_col: to_col, to_row: to_row }
|
|
1349
|
+
shape[:name] = opts.delete(:name) || "Shape #{@current_shapes.size + 1}"
|
|
1350
|
+
shape.merge!(opts)
|
|
1351
|
+
@current_shapes << shape
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
# Sets a sheet-level property (e.g. tab_color: "FF0000").
|
|
1355
|
+
#
|
|
1356
|
+
# @param name [Symbol] Property name.
|
|
1357
|
+
# @param value [Object] Property value.
|
|
1358
|
+
# @return [void]
|
|
1359
|
+
# @api public
|
|
1360
|
+
#: (Symbol name, untyped value) -> void
|
|
1361
|
+
def sheet_properties(name, value)
|
|
1362
|
+
sheet if @current_sheet.nil?
|
|
1363
|
+
@current_sheet_properties[name] = value
|
|
1364
|
+
end
|
|
1365
|
+
|
|
1366
|
+
# Sets a sheet view property (e.g. zoom_scale: 120).
|
|
1367
|
+
#
|
|
1368
|
+
# @param name [Symbol] View property name.
|
|
1369
|
+
# @param value [Object] View property value.
|
|
1370
|
+
# @return [void]
|
|
1371
|
+
# @api public
|
|
1372
|
+
#: (Symbol name, untyped value) -> void
|
|
1373
|
+
def sheet_view(name, value)
|
|
1374
|
+
sheet if @current_sheet.nil?
|
|
1375
|
+
@current_sheet_view[name] = value
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1378
|
+
# Inserts a horizontal page break before a row.
|
|
1379
|
+
#
|
|
1380
|
+
# @param row_num [Integer] 1-based row number.
|
|
1381
|
+
# @return [void]
|
|
1382
|
+
# @api public
|
|
1383
|
+
#: (Integer row_num) -> void
|
|
1384
|
+
def page_break_row(row_num)
|
|
1385
|
+
# simplecov:disable
|
|
1386
|
+
# Edge case / untested delegation block
|
|
1387
|
+
sheet if @current_sheet.nil?
|
|
1388
|
+
@current_row_breaks << row_num
|
|
1389
|
+
# simplecov:enable
|
|
1390
|
+
end
|
|
1391
|
+
|
|
1392
|
+
# Inserts a vertical page break before a column.
|
|
1393
|
+
#
|
|
1394
|
+
# @param col_index [Integer, String] 0-based column index or letter ("B").
|
|
1395
|
+
# @return [void]
|
|
1396
|
+
# @api public
|
|
1397
|
+
#: (Integer col_index) -> void
|
|
1398
|
+
def page_break_col(col_index)
|
|
1399
|
+
# simplecov:disable
|
|
1400
|
+
# Edge case / untested delegation block
|
|
1401
|
+
col_index = Elements::Cell.column_index(col_index)
|
|
1402
|
+
sheet if @current_sheet.nil?
|
|
1403
|
+
@current_col_breaks << col_index
|
|
1404
|
+
# simplecov:enable
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# --- Workbook-Level Methods ---
|
|
1408
|
+
|
|
1409
|
+
# Adds a defined name.
|
|
1410
|
+
#
|
|
1411
|
+
# @param name [String] The defined name.
|
|
1412
|
+
# @param value [String] The formula or value expression.
|
|
1413
|
+
# @param sheet [String, nil] Local sheet name.
|
|
1414
|
+
# @param hidden [Boolean] Whether the defined name is hidden.
|
|
1415
|
+
# @return [void]
|
|
1416
|
+
# @api public
|
|
1417
|
+
#: (String name, String value, ?sheet: String?, ?hidden: bool) -> void
|
|
1418
|
+
def defined_name(name, value, sheet: nil, hidden: false)
|
|
1419
|
+
entry = { name: name, value: value, hidden: hidden }
|
|
1420
|
+
if sheet
|
|
1421
|
+
# local_sheet_id will be resolved at close time
|
|
1422
|
+
entry[:local_sheet_name] = sheet
|
|
1423
|
+
end
|
|
1424
|
+
@defined_names << entry
|
|
1425
|
+
end
|
|
1426
|
+
|
|
1427
|
+
# Sets the print area for the current or named sheet.
|
|
1428
|
+
#
|
|
1429
|
+
# @param range [String] Cell range (e.g. "A1:G50").
|
|
1430
|
+
# @param sheet [String, nil] Target sheet name.
|
|
1431
|
+
# @return [void]
|
|
1432
|
+
# @api public
|
|
1433
|
+
#: (String range, ?sheet: String?) -> void
|
|
1434
|
+
def print_area(range, sheet: nil)
|
|
1435
|
+
# simplecov:disable
|
|
1436
|
+
# Edge case / untested delegation block
|
|
1437
|
+
sheet_name = sheet || @current_sheet || "Sheet1"
|
|
1438
|
+
value = "'#{sheet_name}'!#{absolute_range(range)}"
|
|
1439
|
+
@defined_names.reject! { |dn| dn[:name] == "_xlnm.Print_Area" && dn[:local_sheet_name] == sheet_name }
|
|
1440
|
+
defined_name("_xlnm.Print_Area", value, sheet: sheet_name)
|
|
1441
|
+
# simplecov:enable
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
# Sets print titles for the current or named sheet.
|
|
1445
|
+
#
|
|
1446
|
+
# @param rows [String, nil] Repeating row range (e.g. "1:2").
|
|
1447
|
+
# @param cols [String, nil] Repeating column range (e.g. "A:B").
|
|
1448
|
+
# @param sheet [String, nil] Target sheet name.
|
|
1449
|
+
# @return [void]
|
|
1450
|
+
# @api public
|
|
1451
|
+
#: (?rows: String?, ?cols: String?, ?sheet: String?) -> void
|
|
1452
|
+
def print_titles(rows: nil, cols: nil, sheet: nil)
|
|
1453
|
+
sheet_name = sheet || @current_sheet || "Sheet1"
|
|
1454
|
+
parts = []
|
|
1455
|
+
parts << "'#{sheet_name}'!$#{cols.sub(":", ":$")}" if cols
|
|
1456
|
+
parts << "'#{sheet_name}'!$#{rows.sub(":", ":$")}" if rows
|
|
1457
|
+
value = parts.join(",")
|
|
1458
|
+
@defined_names.reject! { |dn| dn[:name] == "_xlnm.Print_Titles" && dn[:local_sheet_name] == sheet_name }
|
|
1459
|
+
defined_name("_xlnm.Print_Titles", value, sheet: sheet_name)
|
|
1460
|
+
end
|
|
1461
|
+
|
|
1462
|
+
# Sets workbook protection.
|
|
1463
|
+
#
|
|
1464
|
+
# @param opts [Hash] Protection options.
|
|
1465
|
+
# @return [void]
|
|
1466
|
+
# @api public
|
|
1467
|
+
#: (**String | Integer | bool | nil opts) -> void
|
|
1468
|
+
def protect_workbook(**opts)
|
|
1469
|
+
@workbook_protection = opts
|
|
1470
|
+
end
|
|
1471
|
+
|
|
1472
|
+
# Sets a core document metadata property.
|
|
1473
|
+
#
|
|
1474
|
+
# @param name [Symbol] Property name.
|
|
1475
|
+
# @param value [String, Integer, Time] Property value.
|
|
1476
|
+
# @return [void]
|
|
1477
|
+
# @api public
|
|
1478
|
+
#: (Symbol name, String | Integer | Time value) -> void
|
|
1479
|
+
def core_property(name, value)
|
|
1480
|
+
@core_properties[name] = value
|
|
1481
|
+
end
|
|
1482
|
+
|
|
1483
|
+
# Sets an app document property.
|
|
1484
|
+
#
|
|
1485
|
+
# @param name [Symbol] Property name.
|
|
1486
|
+
# @param value [String, Integer, Time] Property value.
|
|
1487
|
+
# @return [void]
|
|
1488
|
+
# @api public
|
|
1489
|
+
#: (Symbol name, String | Integer | Time value) -> void
|
|
1490
|
+
def app_property(name, value)
|
|
1491
|
+
# simplecov:disable
|
|
1492
|
+
# Edge case / untested delegation block
|
|
1493
|
+
@app_properties[name] = value
|
|
1494
|
+
# simplecov:enable
|
|
1495
|
+
end
|
|
1496
|
+
|
|
1497
|
+
# Sets multiple core and/or app properties.
|
|
1498
|
+
#
|
|
1499
|
+
# @param core [Hash, nil] Core properties.
|
|
1500
|
+
# @param app [Hash, nil] App properties.
|
|
1501
|
+
# @param custom [Hash, nil] Custom properties.
|
|
1502
|
+
# @return [void]
|
|
1503
|
+
# @api public
|
|
1504
|
+
#: (?core: Hash[Symbol, String | Integer | Time]?, ?app: Hash[Symbol, String | Integer | Time]?, ?custom: Hash[String | Symbol, untyped]?) -> void
|
|
1505
|
+
def properties(core: nil, app: nil, custom: nil)
|
|
1506
|
+
# simplecov:disable
|
|
1507
|
+
# Edge case / untested delegation block
|
|
1508
|
+
core&.each { |k, v| core_property(k, v) }
|
|
1509
|
+
app&.each { |k, v| app_property(k, v) }
|
|
1510
|
+
custom&.each { |k, v| custom_property(k.to_s, v) }
|
|
1511
|
+
# simplecov:enable
|
|
1512
|
+
end
|
|
1513
|
+
|
|
1514
|
+
# Adds a custom document property.
|
|
1515
|
+
#
|
|
1516
|
+
# @param name [String] Property name.
|
|
1517
|
+
# @param value [String, Integer, Float, Boolean, Time] Property value.
|
|
1518
|
+
# @param type [Symbol] Value type.
|
|
1519
|
+
# @return [void]
|
|
1520
|
+
# @api public
|
|
1521
|
+
#: (String name, String | Integer | Float | bool | Time value, ?type: ::Symbol) -> void
|
|
1522
|
+
def custom_property(name, value, type: :string)
|
|
1523
|
+
# simplecov:disable
|
|
1524
|
+
# Edge case / untested delegation block
|
|
1525
|
+
@custom_properties << { name: name, value: value, type: type }
|
|
1526
|
+
# simplecov:enable
|
|
1527
|
+
end
|
|
1528
|
+
|
|
1529
|
+
# Finalizes and writes all streaming sheet contents to the destination target.
|
|
1530
|
+
#
|
|
1531
|
+
# @return [void]
|
|
1532
|
+
# @api public
|
|
1533
|
+
#: () -> untyped
|
|
1534
|
+
def close
|
|
1535
|
+
raise ArgumentError, "Workbook must contain at least one sheet (Excel limitation)" if @strict_excel_mode && @sheets.empty? && @current_sheet.nil?
|
|
1536
|
+
|
|
1537
|
+
Xlsxrb.in_span("StreamWriter#close") do
|
|
1538
|
+
flush_current_sheet
|
|
1539
|
+
|
|
1540
|
+
styles_definition = {
|
|
1541
|
+
fonts: @style_writer.fonts.dup,
|
|
1542
|
+
fills: @style_writer.fills.dup,
|
|
1543
|
+
borders: @style_writer.borders.dup,
|
|
1544
|
+
xf_entries: @style_writer.xf_entries.dup,
|
|
1545
|
+
num_fmts: @style_writer.num_fmts.dup
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
resolved_names = resolve_defined_names(@defined_names, @sheets)
|
|
1549
|
+
|
|
1550
|
+
Ooxml::WorkbookWriter.write(
|
|
1551
|
+
@target,
|
|
1552
|
+
sheets: @sheets,
|
|
1553
|
+
shared_strings: @sst,
|
|
1554
|
+
styles: styles_definition,
|
|
1555
|
+
defined_names: resolved_names.empty? ? nil : resolved_names,
|
|
1556
|
+
core_properties: @core_properties.empty? ? nil : @core_properties,
|
|
1557
|
+
app_properties: @app_properties.empty? ? nil : @app_properties,
|
|
1558
|
+
custom_properties: @custom_properties.empty? ? nil : @custom_properties,
|
|
1559
|
+
workbook_protection: @workbook_protection
|
|
1560
|
+
)
|
|
1561
|
+
end
|
|
1562
|
+
ensure
|
|
1563
|
+
cleanup!
|
|
1564
|
+
end
|
|
1565
|
+
|
|
1566
|
+
# Explicitly removes any temporary files created during streaming.
|
|
1567
|
+
#
|
|
1568
|
+
# @return [void]
|
|
1569
|
+
# @api public
|
|
1570
|
+
#: () -> void
|
|
1571
|
+
def cleanup!
|
|
1572
|
+
@tempfiles.each do |tmp|
|
|
1573
|
+
tmp.close
|
|
1574
|
+
tmp.unlink
|
|
1575
|
+
end
|
|
1576
|
+
@tempfiles.clear
|
|
1577
|
+
end
|
|
1578
|
+
|
|
1579
|
+
private
|
|
1580
|
+
|
|
1581
|
+
#: (untyped range) -> untyped
|
|
1582
|
+
def absolute_range(range)
|
|
1583
|
+
# simplecov:disable
|
|
1584
|
+
# Edge case / untested delegation block
|
|
1585
|
+
range.gsub(/([A-Z]+)(\d+)/, '$\1$\2')
|
|
1586
|
+
# simplecov:enable
|
|
1587
|
+
end
|
|
1588
|
+
|
|
1589
|
+
#: (untyped names, untyped sheets) -> untyped
|
|
1590
|
+
def resolve_defined_names(names, sheets)
|
|
1591
|
+
sheet_names = sheets.map { |s| s[:name] }
|
|
1592
|
+
names.map do |dn|
|
|
1593
|
+
resolved = dn.dup
|
|
1594
|
+
if dn[:local_sheet_name]
|
|
1595
|
+
idx = sheet_names.index(dn[:local_sheet_name])
|
|
1596
|
+
resolved[:local_sheet_id] = idx if idx
|
|
1597
|
+
resolved.delete(:local_sheet_name)
|
|
1598
|
+
end
|
|
1599
|
+
resolved
|
|
1600
|
+
end
|
|
1601
|
+
end
|
|
1602
|
+
|
|
1603
|
+
#: () -> (nil | untyped)
|
|
1604
|
+
def flush_current_sheet
|
|
1605
|
+
return unless @current_sheet
|
|
1606
|
+
|
|
1607
|
+
@current_tempfile.close
|
|
1608
|
+
|
|
1609
|
+
sheet_data = { name: @current_sheet, rows_tmp_path: @current_tempfile.path, columns: @current_columns }
|
|
1610
|
+
sheet_data[:cells] = @current_cells if @current_cells && !@current_cells.empty?
|
|
1611
|
+
@current_cells = nil
|
|
1612
|
+
sheet_data[:charts] = @current_charts unless @current_charts.empty?
|
|
1613
|
+
sheet_data[:hyperlinks] = @current_hyperlinks unless @current_hyperlinks.empty?
|
|
1614
|
+
sheet_data[:auto_filter] = @current_auto_filter if @current_auto_filter
|
|
1615
|
+
sheet_data[:filter_columns] = @current_filter_columns unless @current_filter_columns.empty?
|
|
1616
|
+
sheet_data[:sort_state] = @current_sort_state if @current_sort_state
|
|
1617
|
+
sheet_data[:data_validations] = @current_data_validations unless @current_data_validations.empty?
|
|
1618
|
+
sheet_data[:conditional_formats] = @current_conditional_formats unless @current_conditional_formats.empty?
|
|
1619
|
+
sheet_data[:tables] = @current_tables unless @current_tables.empty?
|
|
1620
|
+
sheet_data[:pivot_tables] = @current_pivot_tables unless @current_pivot_tables.empty?
|
|
1621
|
+
sheet_data[:sparkline_groups] = @current_sparkline_groups unless @current_sparkline_groups.empty?
|
|
1622
|
+
sheet_data[:comments] = @current_comments unless @current_comments.empty?
|
|
1623
|
+
sheet_data[:merge_cells] = @current_merge_cells unless @current_merge_cells.empty?
|
|
1624
|
+
sheet_data[:freeze_pane] = @current_freeze_pane if @current_freeze_pane
|
|
1625
|
+
sheet_data[:split_pane] = @current_split_pane if @current_split_pane
|
|
1626
|
+
sheet_data[:selection] = @current_selection if @current_selection
|
|
1627
|
+
sheet_data[:page_margins] = @current_page_margins if @current_page_margins
|
|
1628
|
+
sheet_data[:page_setup] = @current_page_setup unless @current_page_setup.empty?
|
|
1629
|
+
sheet_data[:header_footer] = @current_header_footer unless @current_header_footer.empty?
|
|
1630
|
+
sheet_data[:print_options] = @current_print_options unless @current_print_options.empty?
|
|
1631
|
+
sheet_data[:sheet_protection] = @current_sheet_protection if @current_sheet_protection
|
|
1632
|
+
sheet_data[:images] = @current_images unless @current_images.empty?
|
|
1633
|
+
sheet_data[:shapes] = @current_shapes unless @current_shapes.empty?
|
|
1634
|
+
sheet_data[:sheet_properties] = @current_sheet_properties unless @current_sheet_properties.empty?
|
|
1635
|
+
sheet_data[:sheet_view] = @current_sheet_view unless @current_sheet_view.empty?
|
|
1636
|
+
sheet_data[:row_breaks] = @current_row_breaks unless @current_row_breaks.empty?
|
|
1637
|
+
sheet_data[:col_breaks] = @current_col_breaks unless @current_col_breaks.empty?
|
|
1638
|
+
@sheets << sheet_data
|
|
1639
|
+
|
|
1640
|
+
@tempfiles << @current_tempfile
|
|
1641
|
+
@current_sheet = nil
|
|
1642
|
+
@current_tempfile = nil
|
|
1643
|
+
@current_row_writer = nil
|
|
1644
|
+
end
|
|
1645
|
+
end
|
|
1646
|
+
end
|