fast_xlsx 0.2.0-aarch64-linux → 0.3.0-aarch64-linux
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 +16 -0
- data/README.md +39 -16
- data/bench/Gemfile.lock +2 -2
- data/lib/fast_xlsx/3.3/fast_xlsx.so +0 -0
- data/lib/fast_xlsx/3.4/fast_xlsx.so +0 -0
- data/lib/fast_xlsx/4.0/fast_xlsx.so +0 -0
- data/lib/fast_xlsx/version.rb +1 -1
- data/lib/fast_xlsx.rb +57 -10
- data/sig/fast_xlsx.rbs +16 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 11994ead3fe4a960a3a8d0da0b6550b84f835bbf1db7548ca3d9c15ec636a6ce
|
|
4
|
+
data.tar.gz: 32a563fd7df516a3347f3aedd93139cf64de65809ba759c99e19936cb68db731
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d5e61fd835ec50eeb81b4e5fce8bd46c472f22fc398324988ed624af9eca3b618948b0e7bf9569ae0038f5799ab17543f107e7b155eac0f51a5786ee420faf2
|
|
7
|
+
data.tar.gz: f0bec1819859e488a3abb455606eb18e513fa9dcf0ee05a095d4427ace8c96cdfcdb640f5b182f8d021a2b18cb3b58702e78839fe2d42553d286babbedb4e518
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2026-10-01
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- `to_xlsx` and `save` release Ruby's global lock while building and compressing the file, so other threads (e.g. in Puma or Sidekiq) keep running. An interrupt (Ctrl-C, `Timeout`, `Thread#raise`) takes effect once the save finishes.
|
|
8
|
+
- Saving is 20-30% faster: files are compressed with zlib-rs instead of C zlib (which also drops the `libz-sys` dependency). Adding rows is about 15% faster from building with link-time optimisation.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `write`, `write_comment`, `insert_image`, `insert_chart` and `freeze_panes` also take a cell reference such as `"B2"`. `write` is now native, which makes it about 15% faster.
|
|
13
|
+
- `Worksheet#zoom`, `#tab_color`, `#hide_gridlines`, `#activate`, `#hide`, and `#page_setup` (orientation, paper size, fit to pages, rows/columns repeated on every page, print area, printed gridlines).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- A `merge_range` that overlaps an earlier merge raises before writing anything; before, it also blanked the earlier merge's value.
|
|
18
|
+
|
|
3
19
|
## [0.2.0] - 2026-10-01
|
|
4
20
|
|
|
5
21
|
### Added
|
data/README.md
CHANGED
|
@@ -18,6 +18,7 @@ ws = wb.add_worksheet("Report") # later: wb.worksheet("Report")
|
|
|
18
18
|
ws << ["id", "name", "created_at"] # append a row
|
|
19
19
|
ws.concat(records.map { |r| [r.id, r.name, r.created_at] }) # append many rows in one call
|
|
20
20
|
ws.write(0, 5, 42) # write a single cell (row, col, value)
|
|
21
|
+
ws.write("F1", 42) # or by its Excel reference
|
|
21
22
|
|
|
22
23
|
wb.properties(title: "Q3 report", author: "Zac", keywords: "Confidential") # File > Info in Excel
|
|
23
24
|
wb.save("report.xlsx") # or wb.to_xlsx => binary String
|
|
@@ -91,6 +92,22 @@ ws.autofit # size other columns to the data written so fa
|
|
|
91
92
|
ws.autofilter("A1:D101") # filter buttons on A1:D101
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
### Sheet view and printing
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
ws.zoom(150) # 10..400 percent
|
|
99
|
+
ws.tab_color("#C00000")
|
|
100
|
+
ws.hide_gridlines
|
|
101
|
+
ws.activate # Excel opens on this sheet (un-hides it if hidden)
|
|
102
|
+
other.hide # the sheet Excel opens on can't be hidden: activate another one first
|
|
103
|
+
|
|
104
|
+
ws.page_setup(landscape: true, paper: :a4, # or :letter, :legal, :tabloid, :a3, :a5, Excel's paper number, 0 = printer default
|
|
105
|
+
fit_width: 1, # 1 page wide, as many pages tall as needed
|
|
106
|
+
repeat_rows: 0, # print the header row on every page (an index or a Range)
|
|
107
|
+
print_area: "A1:D100", # any cell range style
|
|
108
|
+
gridlines: true) # print the gridlines
|
|
109
|
+
```
|
|
110
|
+
|
|
94
111
|
### Cell ranges
|
|
95
112
|
|
|
96
113
|
`autofilter`, `merge_range`, `conditional_format`, `data_validation` and `add_table` take a range in any of these styles:
|
|
@@ -102,6 +119,8 @@ ws.autofilter(0..100, 0..3) # rows and columns, each an Integer or a Range
|
|
|
102
119
|
ws.merge_range(0, 0..3, "Q3 report", title) # row 1, columns A–D
|
|
103
120
|
```
|
|
104
121
|
|
|
122
|
+
Methods that take one cell (`write`, `write_comment`, `insert_image`, `insert_chart`, `freeze_panes`) take `(row, col)` or a reference like `"B2"`.
|
|
123
|
+
|
|
105
124
|
`autofit` only sees rows still in memory, so in `:constant` / `:low` memory mode it ignores rows already written to disk; set widths with `column_width` instead.
|
|
106
125
|
|
|
107
126
|
### Layout
|
|
@@ -254,17 +273,17 @@ Apple Silicon, Ruby 4.0.5. Each library uses its own idiomatic row-append API; x
|
|
|
254
273
|
|
|
255
274
|
| Library | Time | vs fastest | Ruby objects allocated |
|
|
256
275
|
|---|---:|---:|---:|
|
|
257
|
-
| **fast_xlsx** (`memory: :constant`) | **
|
|
258
|
-
| **fast_xlsx** (`memory: :low`) | **
|
|
259
|
-
| **fast_xlsx** | **
|
|
260
|
-
| [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 |
|
|
261
|
-
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) |
|
|
262
|
-
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 |
|
|
263
|
-
| [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 |
|
|
264
|
-
| [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 |
|
|
265
|
-
| [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 |
|
|
266
|
-
|
|
267
|
-
All outputs are
|
|
276
|
+
| **fast_xlsx** (`memory: :constant`) | **69 ms** | 1.0x | 7 |
|
|
277
|
+
| **fast_xlsx** (`memory: :low`) | **78 ms** | 1.1x | 7 |
|
|
278
|
+
| **fast_xlsx** | **80 ms** | 1.2x | 10 |
|
|
279
|
+
| [xlsxtream](https://github.com/felixbuenemann/xlsxtream) 3.1 | 171 ms | 2.5x | 561,728 |
|
|
280
|
+
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 (constant_memory) | 194 ms | 2.8x | 20,079 |
|
|
281
|
+
| [fast_excel](https://github.com/Paxa/fast_excel) 0.5 | 228 ms | 3.3x | 320,076 |
|
|
282
|
+
| [write_xlsx](https://github.com/cxn03651/write_xlsx) 1.15 | 583 ms | 8.4x | 1,483,899 |
|
|
283
|
+
| [caxlsx](https://github.com/caxlsx/caxlsx) 4.5 | 691 ms | 10.0x | 745,122 |
|
|
284
|
+
| [rubyXL](https://github.com/weshatheleopard/rubyXL) 3.4 | 2650 ms | 38.3x | 8,700,448 |
|
|
285
|
+
|
|
286
|
+
All outputs are 681–750 KB.
|
|
268
287
|
|
|
269
288
|
### Memory
|
|
270
289
|
|
|
@@ -272,14 +291,18 @@ All outputs are 702–750 KB.
|
|
|
272
291
|
|
|
273
292
|
| Library | Unique strings | Repeated strings |
|
|
274
293
|
|---|---:|---:|
|
|
275
|
-
| **fast_xlsx** (`memory: :constant`) | **+
|
|
276
|
-
| **fast_xlsx** (`memory: :low`) | +
|
|
277
|
-
| **fast_xlsx** | +
|
|
278
|
-
| fast_excel 0.5 (constant_memory) | +10 MB | +
|
|
279
|
-
| fast_excel 0.5 | +
|
|
294
|
+
| **fast_xlsx** (`memory: :constant`) | **+1 MB** | **+1 MB** |
|
|
295
|
+
| **fast_xlsx** (`memory: :low`) | +61 MB | **+1 MB** |
|
|
296
|
+
| **fast_xlsx** | +271 MB | +217 MB |
|
|
297
|
+
| fast_excel 0.5 (constant_memory) | +10 MB | +9 MB |
|
|
298
|
+
| fast_excel 0.5 | +182 MB | +151 MB |
|
|
280
299
|
|
|
281
300
|
The `:standard` mode uses more memory than fast_excel's: when saving, rust_xlsxwriter assembles each worksheet's XML in memory (so several worksheets can be built in parallel) instead of streaming it from a temp file. Use `memory: :constant` or `memory: :low` for large exports.
|
|
282
301
|
|
|
302
|
+
### Threads
|
|
303
|
+
|
|
304
|
+
Most of the time goes into saving (building the XML and compressing it). `to_xlsx` and `save` do that without holding Ruby's global lock, so in a threaded server (Puma, Sidekiq) other threads keep running while a large export is saved. An interrupt (Ctrl-C, `Timeout`, `Thread#raise`) takes effect once the save finishes.
|
|
305
|
+
|
|
283
306
|
### Reproduce
|
|
284
307
|
|
|
285
308
|
```bash
|
data/bench/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
fast_xlsx (0.
|
|
4
|
+
fast_xlsx (0.3.0)
|
|
5
5
|
rb_sys (~> 0.9.130)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -77,7 +77,7 @@ DEPENDENCIES
|
|
|
77
77
|
CHECKSUMS
|
|
78
78
|
caxlsx (4.5.0) sha256=e3d98d859f148df05d5462086b5079b523f29c1766b569535f1d68629ce743ff
|
|
79
79
|
fast_excel (0.5.0) sha256=59c418bdcf586a6030798d4e3a9f575742badaceaf7bb90f38eee5feb5bdc478
|
|
80
|
-
fast_xlsx (0.
|
|
80
|
+
fast_xlsx (0.3.0)
|
|
81
81
|
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
|
|
82
82
|
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
|
|
83
83
|
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/fast_xlsx/version.rb
CHANGED
data/lib/fast_xlsx.rb
CHANGED
|
@@ -102,10 +102,15 @@ module FastXlsx
|
|
|
102
102
|
|
|
103
103
|
# Cell writer for one sheet; create with Workbook#add_worksheet.
|
|
104
104
|
class Worksheet
|
|
105
|
-
|
|
105
|
+
# write(row, col, value, format = nil) or write("B2", value, format = nil)
|
|
106
|
+
# is native: the (row, col) form runs once per cell, so it skips a Ruby
|
|
107
|
+
# wrapper. Other forms come here.
|
|
108
|
+
def _write_ref(*args)
|
|
109
|
+
row, col, (value, format) = CellRange.cell(args, 1..2)
|
|
106
110
|
_write(row, col, value, format)
|
|
107
111
|
self
|
|
108
112
|
end
|
|
113
|
+
private :_write_ref
|
|
109
114
|
|
|
110
115
|
def append(values, format: nil)
|
|
111
116
|
_append(values, format)
|
|
@@ -158,14 +163,16 @@ module FastXlsx
|
|
|
158
163
|
end
|
|
159
164
|
|
|
160
165
|
# Adds a comment (Excel "note") to a cell.
|
|
161
|
-
def write_comment(
|
|
166
|
+
def write_comment(*args, author: nil)
|
|
167
|
+
row, col, (text, *) = CellRange.cell(args, 1..1)
|
|
162
168
|
_write_comment(row, col, text, author)
|
|
163
169
|
end
|
|
164
170
|
|
|
165
171
|
# Inserts a PNG, JPEG, GIF or BMP image with its top-left corner in the
|
|
166
172
|
# cell. source is a file path or an IO (anything responding to #read).
|
|
167
173
|
# Options: scale: or width:/height: (pixels), x_offset:, y_offset: (pixels), alt_text:.
|
|
168
|
-
def insert_image(
|
|
174
|
+
def insert_image(*args, **)
|
|
175
|
+
row, col, (source, *) = CellRange.cell(args, 1..1)
|
|
169
176
|
bytes = source.respond_to?(:read) ? source.read : File.binread(source)
|
|
170
177
|
_insert_image(row, col, bytes, { ** })
|
|
171
178
|
end
|
|
@@ -173,7 +180,8 @@ module FastXlsx
|
|
|
173
180
|
# Inserts a chart with its top-left corner in the cell. series is an Array
|
|
174
181
|
# of { values:, categories:, name: } with Excel ranges such as
|
|
175
182
|
# "Sheet1!$B$2:$B$13". Options: title:, x_axis:, y_axis:, width:, height:.
|
|
176
|
-
def insert_chart(
|
|
183
|
+
def insert_chart(*cell, type:, series:, **)
|
|
184
|
+
row, col, = CellRange.cell(cell)
|
|
177
185
|
_insert_chart(row, col, { type: type, series: series, ** })
|
|
178
186
|
end
|
|
179
187
|
|
|
@@ -197,6 +205,18 @@ module FastXlsx
|
|
|
197
205
|
margin ? margins(footer: margin) : self
|
|
198
206
|
end
|
|
199
207
|
|
|
208
|
+
# Printing: landscape:, paper: (:letter, :legal, :tabloid, :a3, :a4, :a5 or
|
|
209
|
+
# Excel's paper number), fit_width:/fit_height: (pages; 0 or left out =
|
|
210
|
+
# as many as needed), repeat_rows:/repeat_columns: (an index or Range,
|
|
211
|
+
# printed on every page), print_area: (any cell range), gridlines:.
|
|
212
|
+
def page_setup(repeat_rows: nil, repeat_columns: nil, print_area: nil, **options)
|
|
213
|
+
options[:repeat_rows] = CellRange.bounds(repeat_rows) if repeat_rows
|
|
214
|
+
options[:repeat_columns] = CellRange.bounds(repeat_columns) if repeat_columns
|
|
215
|
+
options[:print_area] = CellRange.split(print_area.is_a?(Array) ? print_area : [print_area]).first if print_area
|
|
216
|
+
_page_setup(options)
|
|
217
|
+
self
|
|
218
|
+
end
|
|
219
|
+
|
|
200
220
|
# Print margins in inches; margins not given keep their current value.
|
|
201
221
|
def margins(left: nil, right: nil, top: nil, bottom: nil, header: nil, footer: nil)
|
|
202
222
|
_margins(*[left, right, top, bottom, header, footer].map { |m| m || -1.0 })
|
|
@@ -214,6 +234,13 @@ module FastXlsx
|
|
|
214
234
|
_group_columns(*CellRange.bounds(columns), collapsed)
|
|
215
235
|
end
|
|
216
236
|
|
|
237
|
+
# Keeps the rows above and the columns left of the cell visible while
|
|
238
|
+
# scrolling: (1, 0) or "A2" freezes the first row.
|
|
239
|
+
def freeze_panes(*cell)
|
|
240
|
+
_freeze_panes(*CellRange.cell(cell).first(2))
|
|
241
|
+
self
|
|
242
|
+
end
|
|
243
|
+
|
|
217
244
|
# Locks the sheet against editing. Cells whose format has locked: false
|
|
218
245
|
# stay editable. allow: actions users may still take, any of :format_cells,
|
|
219
246
|
# :format_columns, :format_rows, :insert_columns, :insert_rows,
|
|
@@ -236,6 +263,7 @@ module FastXlsx
|
|
|
236
263
|
module CellRange
|
|
237
264
|
REF = /\A\$?([A-Za-z]{1,3})\$?([1-9]\d*)\z/ # ASCII only: /i also matches the Kelvin sign
|
|
238
265
|
FORMS = 'a range is (first_row, first_col, last_row, last_col), "A1:D10" or (rows, cols)'
|
|
266
|
+
CELL_FORMS = 'a cell is (row, col) or "B2"'
|
|
239
267
|
|
|
240
268
|
module_function
|
|
241
269
|
|
|
@@ -243,12 +271,7 @@ module FastXlsx
|
|
|
243
271
|
# Returns [[first_row, first_col, last_row, last_col], the args after it].
|
|
244
272
|
def split(args, following = 0..0)
|
|
245
273
|
range, rest = parse(args)
|
|
246
|
-
|
|
247
|
-
expected = following.minmax.uniq.join("..")
|
|
248
|
-
raise ArgumentError, "wrong number of arguments after the cell range (given #{rest.size}, " \
|
|
249
|
-
"expected #{expected}); #{FORMS}"
|
|
250
|
-
end
|
|
251
|
-
|
|
274
|
+
check_following(rest, following, "cell range", FORMS)
|
|
252
275
|
[range, rest]
|
|
253
276
|
end
|
|
254
277
|
|
|
@@ -263,6 +286,30 @@ module FastXlsx
|
|
|
263
286
|
end
|
|
264
287
|
end
|
|
265
288
|
|
|
289
|
+
# (row, col) or a single-cell reference ("B2") off the front of args, as
|
|
290
|
+
# [row, col, the args after it].
|
|
291
|
+
def cell(args, following = 0..0)
|
|
292
|
+
row, col, *rest = args.first.is_a?(String) ? [*single_cell(args.first), *args.drop(1)] : args
|
|
293
|
+
raise ArgumentError, "expected a cell, got #{args.inspect}; #{CELL_FORMS}" if col.nil?
|
|
294
|
+
|
|
295
|
+
check_following(rest, following, "cell", CELL_FORMS)
|
|
296
|
+
[row, col, rest]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def single_cell(ref)
|
|
300
|
+
first_row, first_col, last_row, last_col = excel(ref)
|
|
301
|
+
return [first_row, first_col] if first_row == last_row && first_col == last_col
|
|
302
|
+
|
|
303
|
+
raise ArgumentError, "expected a single cell like \"B2\", got #{ref.inspect}"
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def check_following(rest, following, what, forms)
|
|
307
|
+
return if following.cover?(rest.size)
|
|
308
|
+
|
|
309
|
+
raise ArgumentError, "wrong number of arguments after the #{what} " \
|
|
310
|
+
"(given #{rest.size}, expected #{following.minmax.uniq.join("..")}); #{forms}"
|
|
311
|
+
end
|
|
312
|
+
|
|
266
313
|
def rows_and_cols(rows, cols)
|
|
267
314
|
first_row, last_row = bounds(rows)
|
|
268
315
|
first_col, last_col = bounds(cols)
|
data/sig/fast_xlsx.rbs
CHANGED
|
@@ -57,6 +57,7 @@ module FastXlsx
|
|
|
57
57
|
|
|
58
58
|
class Worksheet
|
|
59
59
|
def write: (Integer row, Integer col, cell value, ?Format? format) -> self
|
|
60
|
+
| (String cell, cell value, ?Format? format) -> self
|
|
60
61
|
def append: (Array[cell] values, ?format: Format? | Array[Format?]) -> self
|
|
61
62
|
def <<: (Array[cell] row) -> self
|
|
62
63
|
def concat: (Array[Array[cell]] rows) -> self
|
|
@@ -72,11 +73,20 @@ module FastXlsx
|
|
|
72
73
|
| (String range) -> self
|
|
73
74
|
| (indexes rows, indexes cols) -> self
|
|
74
75
|
def freeze_panes: (Integer row, Integer col) -> self
|
|
76
|
+
| (String cell) -> self
|
|
75
77
|
def row_height: (Integer row, Numeric height) -> self
|
|
76
78
|
def page_breaks: (Array[Integer] rows) -> self
|
|
77
79
|
def vertical_page_breaks: (Array[Integer] cols) -> self
|
|
78
80
|
def page_header: (String text, ?margin: Numeric?) -> self
|
|
79
81
|
def page_footer: (String text, ?margin: Numeric?) -> self
|
|
82
|
+
def activate: () -> self
|
|
83
|
+
def hide: () -> self
|
|
84
|
+
def zoom: (Integer percent) -> self
|
|
85
|
+
def tab_color: (String | Integer color) -> self
|
|
86
|
+
def hide_gridlines: () -> self
|
|
87
|
+
def page_setup: (?landscape: bool, ?paper: :letter | :legal | :tabloid | :a3 | :a4 | :a5 | Integer,
|
|
88
|
+
?fit_width: Integer, ?fit_height: Integer, ?repeat_rows: indexes, ?repeat_columns: indexes,
|
|
89
|
+
?print_area: String | Array[cell_range], ?gridlines: bool) -> self
|
|
80
90
|
def margins: (?left: Numeric?, ?right: Numeric?, ?top: Numeric?, ?bottom: Numeric?,
|
|
81
91
|
?header: Numeric?, ?footer: Numeric?) -> self
|
|
82
92
|
type protect_action = :format_cells | :format_columns | :format_rows | :insert_columns | :insert_rows
|
|
@@ -98,13 +108,19 @@ module FastXlsx
|
|
|
98
108
|
?input_title: String, ?input_message: String,
|
|
99
109
|
?error_title: String, ?error_message: String) -> self
|
|
100
110
|
def write_comment: (Integer row, Integer col, String text, ?author: String?) -> self
|
|
111
|
+
| (String cell, String text, ?author: String?) -> self
|
|
101
112
|
def insert_image: (Integer row, Integer col, String | _Reader source, ?scale: Numeric, ?width: Numeric, ?height: Numeric,
|
|
102
113
|
?x_offset: Integer, ?y_offset: Integer, ?alt_text: String) -> self
|
|
114
|
+
| (String cell, String | _Reader source, ?scale: Numeric, ?width: Numeric, ?height: Numeric,
|
|
115
|
+
?x_offset: Integer, ?y_offset: Integer, ?alt_text: String) -> self
|
|
103
116
|
type chart_series = { values: String, ?categories: String, ?name: String }
|
|
104
117
|
|
|
105
118
|
def insert_chart: (Integer row, Integer col, type: Symbol, series: Array[chart_series],
|
|
106
119
|
?title: String, ?x_axis: String, ?y_axis: String,
|
|
107
120
|
?width: Integer, ?height: Integer) -> self
|
|
121
|
+
| (String cell, type: Symbol, series: Array[chart_series],
|
|
122
|
+
?title: String, ?x_axis: String, ?y_axis: String,
|
|
123
|
+
?width: Integer, ?height: Integer) -> self
|
|
108
124
|
type table_column = String | { header: String, ?total: Symbol, ?total_label: String, ?format: Format }
|
|
109
125
|
|
|
110
126
|
def add_table: (*cell_range range,
|