version bump 0.18.3

- XLSX / XLSB dynamic array formulae
- use Uint8Array when available in write (fixes #2539 h/t @RScherzer)
- mini build cleanup to satiate webpack (fixes #2526 #2530)
This commit is contained in:
SheetJS 2022-03-03 03:35:39 -05:00
parent 4f6a849a59
commit e14aee3e51
57 changed files with 3624 additions and 2249 deletions

@ -41,6 +41,7 @@ ExtendScript
IndexedDB
JavaScriptCore
LocalStorage
NestJS
NPM
Nuxt.js
Redis

@ -18,6 +18,7 @@ ESMJSDEPS=$(shell cat misc/mjs.lst)
ULIB=$(shell echo $(LIB) | tr a-z A-Z)
DEPS=$(sort $(wildcard bits/*.js))
TSBITS=$(patsubst modules/%,bits/%,$(wildcard modules/[0-9][0-9]_*.js))
MTSBITS=$(patsubst modules/%,misc/%,$(wildcard modules/[0-9][0-9]_*.js))
TARGET=$(LIB).js
FLOWTARGET=$(LIB).flow.js
FLOWAUX=$(patsubst %.js,%.flow.js,$(AUXTARGETS))
@ -52,6 +53,9 @@ bits/18_cfb.js: node_modules/cfb/xlscfb.flow.js
$(TSBITS): bits/%: modules/%
cp $^ $@
$(MTSBITS): misc/%: modules/%
cp $^ $@
.PHONY: clean
clean: ## Remove targets and build artifacts
@ -82,6 +86,7 @@ dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
uglifyjs $(REQS) dist/$(TARGET) $(UGLIFYOPTS) -o dist/$(LIB).core.min.js --source-map dist/$(LIB).core.min.map --preamble "$$(head -n 1 bits/00_header.js)"
misc/strip_sourcemap.sh dist/$(LIB).core.min.js
@# full
#cat <(head -n 1 bits/00_header.js) $(DISTHDR) $(REQS) $(ADDONS) dist/$(TARGET) $(AUXTARGETS) > dist/$(LIB).full.js
uglifyjs $(DISTHDR) $(REQS) $(ADDONS) dist/$(TARGET) $(AUXTARGETS) $(UGLIFYOPTS) -o dist/$(LIB).full.min.js --source-map dist/$(LIB).full.min.map --preamble "$$(head -n 1 bits/00_header.js)"
misc/strip_sourcemap.sh dist/$(LIB).full.min.js
@# mini
@ -101,7 +106,7 @@ dist-deps: ## Copy dependencies for distribution
aux: $(AUXTARGETS)
BYTEFILEC=dist/xlsx.{full,core,mini}.min.js
BYTEFILER=dist/xlsx.extendscript.js
BYTEFILER=dist/xlsx.extendscript.js xlsx.mjs
.PHONY: bytes
bytes: ## Display minified and gzipped file sizes
@for i in $(BYTEFILEC); do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done

572
README.md

@ -24,12 +24,8 @@ port calculations to web apps; automate common spreadsheet tasks, and much more!
![circo graph of format support](formats.png)
<details><summary><b>Diagram Legend</b> (click to show)</summary>
![graph legend](legend.png)
</details>
## Table of Contents
<details>
@ -46,14 +42,17 @@ port calculations to web apps; automate common spreadsheet tasks, and much more!
* [Parsing Workbooks](#parsing-workbooks)
* [Processing JSON and JS Data](#processing-json-and-js-data)
* [Processing HTML Tables](#processing-html-tables)
- [Working with the Workbook](#working-with-the-workbook)
* [Parsing and Writing Examples](#parsing-and-writing-examples)
- [Processing Data](#processing-data)
* [Modifying Workbook Structure](#modifying-workbook-structure)
* [Modifying Cell Values](#modifying-cell-values)
* [Modifying Other Worksheet / Workbook / Cell Properties](#modifying-other-worksheet--workbook--cell-properties)
- [Packaging and Releasing Data](#packaging-and-releasing-data)
* [Writing Workbooks](#writing-workbooks)
* [Writing Examples](#writing-examples)
* [Streaming Write](#streaming-write)
* [Generating JSON and JS Data](#generating-json-and-js-data)
* [Generating HTML Tables](#generating-html-tables)
* [Generating Single-Worksheet Snapshots](#generating-single-worksheet-snapshots)
- [Interface](#interface)
* [Parsing functions](#parsing-functions)
* [Writing functions](#writing-functions)
@ -150,10 +149,11 @@ For example, `unpkg` makes the latest version available at:
The complete single-file version is generated at `dist/xlsx.full.min.js`
`dist/xlsx.core.min.js` omits codepage library (no support for XLS encodings)
A slimmer build is generated at `dist/xlsx.mini.min.js`. Compared to full build:
- codepage library skipped (no support for XLS encodings)
- XLSX compression option not currently available
- no support for XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003
- no support for XLSB / XLS / Lotus 1-2-3 / SpreadsheetML 2003 / Numbers
- node stream utils removed
</details>
@ -317,6 +317,18 @@ and approaches for steps 1 and 5.
Utility functions help with step 3.
["Acquiring and Extracting Data"](#acquiring-and-extracting-data) describes
solutions for common data import scenarios.
["Packaging and Releasing Data"](#packaging-and-releasing-data) describes
solutions for common data export scenarios.
["Processing Data"](#packaging-and-releasing-data) describes solutions for
common workbook processing and manipulation scenarios.
["Utility Functions"](#utility-functions) details utility functions for
translating JSON Arrays and other common JS structures into worksheet objects.
### The Zen of SheetJS
_Data processing should fit in any workflow_
@ -325,15 +337,6 @@ The library does not impose a separate lifecycle. It fits nicely in websites
and apps built using any framework. The plain JS data objects play nice with
Web Workers and future APIs.
["Acquiring and Extracting Data"](#acquiring-and-extracting-data) describes
solutions for common data import scenarios.
["Writing Workbooks"](#writing-workbooks) describes solutions for common data
export scenarios involving actual spreadsheet files.
["Utility Functions"](#utility-functions) details utility functions for
translating JSON Arrays and other common JS structures into worksheet objects.
_JavaScript is a powerful language for data processing_
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
@ -576,6 +579,12 @@ The [`demos` directory](demos/) includes sample projects for:
Other examples are included in the [showcase](demos/showcase/).
<https://sheetjs.com/demos/modify.html> shows a complete example of reading,
modifying, and writing files.
<https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs> is the command-line
tool included with node installations, reading spreadsheet files and exporting
the contents in various formats.
## Acquiring and Extracting Data
### Parsing Workbooks
@ -1018,12 +1027,13 @@ const workbook = XLSX.read(data);
</details>
More detailed examples are covered in the [included demos](demos/)
### Processing JSON and JS Data
JSON and JS data tend to represent single worksheets. This section will use a
few utility functions to generate workbooks:
few utility functions to generate workbooks.
_Create a new Worksheet_
_Create a new Workbook_
```js
var workbook = XLSX.utils.book_new();
@ -1031,16 +1041,9 @@ var workbook = XLSX.utils.book_new();
The `book_new` utility function creates an empty workbook with no worksheets.
_Append a Worksheet to a Workbook_
```js
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
```
The `book_append_sheet` utility function appends a worksheet to the workbook.
The third argument specifies the desired worksheet name. Multiple worksheets can
be added to a workbook by calling the function multiple times.
Spreadsheet software generally require at least one worksheet and enforce the
requirement in the user interface. This library enforces the requirement at
write time, throwing errors if an empty workbook is passed to write functions.
**API**
@ -1053,14 +1056,14 @@ var worksheet = XLSX.utils.aoa_to_sheet(aoa, opts);
The `aoa_to_sheet` utility function walks an "array of arrays" in row-major
order, generating a worksheet object. The following snippet generates a sheet
with cell `A1` set to the string `A1`, cell `B1` set to `B2`, etc:
with cell `A1` set to the string `A1`, cell `B1` set to `B1`, etc:
```js
var worksheet = XLSX.utils.aoa_to_sheet([
["A1", "B1", "C1"],
["A2", "B2", "C2"],
["A3", "B3", "C3"]
])
]);
```
["Array of Arrays Input"](#array-of-arrays-input) describes the function and the
@ -1361,41 +1364,57 @@ const workbook = XLSX.utils.table_to_book(doc);
</details>
## Working with the Workbook
## Processing Data
The full object format is described later in this README.
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
representation of the core concepts of a workbook. The utility functions work
with the object representation and are intended to handle common use cases.
<details>
<summary><b>Reading a specific cell </b> (click to show)</summary>
### Modifying Workbook Structure
This example extracts the value stored in cell A1 from the first worksheet:
**API**
_Append a Worksheet to a Workbook_
```js
var first_sheet_name = workbook.SheetNames[0];
var address_of_cell = 'A1';
/* Get worksheet */
var worksheet = workbook.Sheets[first_sheet_name];
/* Find desired cell */
var desired_cell = worksheet[address_of_cell];
/* Get the value */
var desired_value = (desired_cell ? desired_cell.v : undefined);
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
```
</details>
The `book_append_sheet` utility function appends a worksheet to the workbook.
The third argument specifies the desired worksheet name. Multiple worksheets can
be added to a workbook by calling the function multiple times.
_List the Worksheet names in tab order_
```js
var wsnames = workbook.SheetNames;
```
The `SheetNames` property of the workbook object is a list of the worksheet
names in "tab order". API functions will look at this array.
_Replace a Worksheet in place_
```js
workbook.Sheets[sheet_name] = new_worksheet;
```
The `Sheets` property of the workbook object is an object whose keys are names
and whose values are worksheet objects. By reassigning to a property of the
`Sheets` object, the worksheet object can be changed without disrupting the
rest of the worksheet structure.
**Examples**
<details>
<summary><b>Adding a new worksheet to a workbook</b> (click to show)</summary>
<summary><b>Add a new worksheet to a workbook</b> (click to show)</summary>
This example uses [`XLSX.utils.aoa_to_sheet`](#array-of-arrays-input) to make a
sheet and `XLSX.utils.book_append_sheet` to append the sheet to the workbook:
This example uses [`XLSX.utils.aoa_to_sheet`](#array-of-arrays-input).
```js
var ws_name = "SheetJS";
/* make worksheet */
/* Create worksheet */
var ws_data = [
[ "S", "h", "e", "e", "t", "J", "S" ],
[ 1 , 2 , 3 , 4 , 5 ]
@ -1408,41 +1427,60 @@ XLSX.utils.book_append_sheet(wb, ws, ws_name);
</details>
<details>
<summary><b>Creating a new workbook from scratch</b> (click to show)</summary>
### Modifying Cell Values
The workbook object contains a `SheetNames` array of names and a `Sheets` object
mapping sheet names to sheet objects. The `XLSX.utils.book_new` utility function
creates a new workbook object:
**API**
_Modify a single cell value in a worksheet_
```js
/* create a new blank workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.sheet_add_aoa(worksheet, [[new_value]], { origin: address });
```
The new workbook is blank and contains no worksheets. The write functions will
error if the workbook is empty.
_Modify multiple cell values in a worksheet_
```js
XLSX.utils.sheet_add_aoa(worksheet, aoa, opts);
```
The `sheet_add_aoa` utility function modifies cell values in a worksheet. The
first argument is the worksheet object. The second argument is an array of
arrays of values. The `origin` key of the third argument controls where cells
will be written. The following snippet sets `B3=1` and `E5="abc"`:
```js
XLSX.utils.sheet_add_aoa(worksheet, [
[1], // <-- Write 1 to cell B3
, // <-- Do nothing in row 4
[/*B5*/, /*C5*/, /*D5*/, "abc"] // <-- Write "abc" to cell E5
], { origin: "B3" });
```
["Array of Arrays Input"](#array-of-arrays-input) describes the function and the
optional `opts` argument in more detail.
**Examples**
<details>
<summary><b>Appending rows to a worksheet</b> (click to show)</summary>
The special origin value `-1` instructs `sheet_add_aoa` to start in column A of
the row after the last row in the range, appending the data:
```js
XLSX.utils.sheet_add_aoa(worksheet, [
["first row after data", 1],
["second row after data", 2]
], { origin: -1 });
```
</details>
### Parsing and Writing Examples
### Modifying Other Worksheet / Workbook / Cell Properties
- <https://sheetjs.com/demos/modify.html> read + modify + write files
- <https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs> node
The node version installs a command line tool `xlsx` which can read spreadsheet
files and output the contents in various formats. The source is available at
`xlsx.njs` in the bin directory.
Some helper functions in `XLSX.utils` generate different views of the sheets:
- `XLSX.utils.sheet_to_csv` generates CSV
- `XLSX.utils.sheet_to_txt` generates UTF16 Formatted Text
- `XLSX.utils.sheet_to_html` generates HTML
- `XLSX.utils.sheet_to_json` generates an array of objects
- `XLSX.utils.sheet_to_formulae` generates a list of formulae
The ["Common Spreadsheet Format"](#common-spreadsheet-format) section describes
the object structures in greater detail.
## Packaging and Releasing Data
@ -1963,6 +2001,45 @@ The [`vuejs` demo](demos/vue) includes more React examples.
</details>
### Generating Single-Worksheet Snapshots
The `sheet_to_*` functions accept a worksheet object.
**API**
_Generate a CSV from a single worksheet_
```js
var csv = XLSX.utils.sheet_to_csv(worksheet, opts);
```
This snapshot is designed to replicate the "CSV UTF8 (`.csv`)" output type.
["Delimiter-Separated Output"](#delimiter-separated-output) describes the
function and the optional `opts` argument in more detail.
_Generate "Text" from a single worksheet_
```js
var txt = XLSX.utils.sheet_to_txt(worksheet, opts);
```
This snapshot is designed to replicate the "UTF16 Text (`.txt`)" output type.
["Delimiter-Separated Output"](#delimiter-separated-output) describes the
function and the optional `opts` argument in more detail.
_Generate a list of formulae from a single worksheet_
```js
var fmla = XLSX.utils.sheet_to_formulae(worksheet);
```
This snapshot generates an array of entries representing the embedded formulae.
Array formulae are rendered in the form `range=formula` while plain cells are
rendered in the form `cell=formula or value`. String literals are prefixed with
an apostrophe `'`, consistent with Excel's formula bar display.
["Formulae Output"](#formulae-output) describes the function in more detail.
## Interface
`XLSX` is the exposed variable in the browser and the exported node variable
@ -2065,6 +2142,7 @@ Cell objects are plain JS objects with keys and values following the convention:
| `t` | type: `b` Boolean, `e` Error, `n` Number, `d` Date, `s` Text, `z` Stub |
| `f` | cell formula encoded as an A1-style string (if applicable) |
| `F` | range of enclosing array if formula is array formula (if applicable) |
| `D` | if true, array formula is dynamic (if applicable) |
| `r` | rich text encoding (if applicable) |
| `h` | HTML rendering of the rich text (if applicable) |
| `c` | comments associated with the cell |
@ -2416,79 +2494,7 @@ Even though some formats store formulae with a leading equal sign, CSF formulae
do not start with `=`.
<details>
<summary><b>Representation of A1=1, A2=2, A3=A1+A2</b> (click to show)</summary>
```js
{
"!ref": "A1:A3",
A1: { t:'n', v:1 },
A2: { t:'n', v:2 },
A3: { t:'n', v:3, f:'A1+A2' }
}
```
</details>
Shared formulae are decompressed and each cell has the formula corresponding to
its cell. Writers generally do not attempt to generate shared formulae.
Cells with formula entries but no value will be serialized in a way that Excel
and other spreadsheet tools will recognize. This library will not automatically
compute formula results! For example, to compute `BESSELJ` in a worksheet:
<details>
<summary><b>Formula without known value</b> (click to show)</summary>
```js
{
"!ref": "A1:A3",
A1: { t:'n', v:3.14159 },
A2: { t:'n', v:2 },
A3: { t:'n', f:'BESSELJ(A1,A2)' }
}
```
</details>
**Array Formulae**
Array formulae are stored in the top-left cell of the array block. All cells
of an array formula have a `F` field corresponding to the range. A single-cell
formula can be distinguished from a plain formula by the presence of `F` field.
<details>
<summary><b>Array Formula examples</b> (click to show)</summary>
For example, setting the cell `C1` to the array formula `{=SUM(A1:A3*B1:B3)}`:
```js
worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" };
```
For a multi-cell array formula, every cell has the same array range but only the
first cell specifies the formula. Consider `D1:D3=A1:A3*B1:B3`:
```js
worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" };
worksheet['D2'] = { t:'n', F:"D1:D3" };
worksheet['D3'] = { t:'n', F:"D1:D3" };
```
</details>
Utilities and writers are expected to check for the presence of a `F` field and
ignore any possible formula element `f` in cells other than the starting cell.
They are not expected to perform validation of the formulae!
<details>
<summary><b>Formula Output Utility Function</b> (click to show)</summary>
The `sheet_to_formulae` method generates one line per formula or array formula.
Array formulae are rendered in the form `range=formula` while plain cells are
rendered in the form `cell=formula or value`. Note that string literals are
prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
</details>
<details>
<summary><b>Formulae File Format Details</b> (click to show)</summary>
<summary><b>Formulae File Format Support</b> (click to show)</summary>
| Storage Representation | Formats | Read | Write |
|:-----------------------|:-------------------------|:-----:|:-----:|
@ -2502,6 +2508,272 @@ Since Excel prohibits named cells from colliding with names of A1 or RC style
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
formulae and Lotus Parsed formulae have to be explicitly unwound. OpenFormula
formulae can be converted with regular expressions.
Shared formulae are decompressed and each cell has the formula corresponding to
its cell. Writers generally do not attempt to generate shared formulae.
</details>
**Single-Cell Formulae**
For simple formulae, the `f` key of the desired cell can be set to the actual
formula text. This worksheet represents `A1=1`, `A2=2`, and `A3=A1+A2`:
```js
var worksheet = {
"!ref": "A1:A3",
A1: { t:'n', v:1 },
A2: { t:'n', v:2 },
A3: { t:'n', v:3, f:'A1+A2' }
};
```
Utilities like `aoa_to_sheet` will accept cell objects in lieu of values:
```js
var worksheet = XLSX.utils.aoa_to_sheet([
[ 1 ], // A1
[ 2 ], // A2
[ {t: "n", v: 3, f: "A1+A2"} ] // A3
]);
```
Cells with formula entries but no value will be serialized in a way that Excel
and other spreadsheet tools will recognize. This library will not automatically
compute formula results! For example, the following worksheet will include the
`BESSELJ` function but the result will not be available in JavaScript:
```js
var worksheet = XLSX.utils.aoa_to_sheet([
[ 3.14159, 2 ], // Row "1"
[ { t:'n', f:'BESSELJ(A1,B1)' } ] // Row "2" will be calculated on file open
}
```
If the actual results are needed in JS, [SheetJS Pro](https://sheetjs.com/pro)
offers a formula calculator component for evaluating expressions, updating
values and dependent cells, and refreshing entire workbooks.
**Array Formulae**
_Assign an array formula_
```js
XLSX.utils.sheet_set_array_formula(worksheet, range, formula);
```
Array formulae are stored in the top-left cell of the array block. All cells
of an array formula have a `F` field corresponding to the range. A single-cell
formula can be distinguished from a plain formula by the presence of `F` field.
For example, setting the cell `C1` to the array formula `{=SUM(A1:A3*B1:B3)}`:
```js
// API function
XLSX.utils.sheet_set_array_formula(worksheet, "C1", "SUM(A1:A3*B1:B3)");
// ... OR raw operations
worksheet['C1'] = { t:'n', f: "SUM(A1:A3*B1:B3)", F:"C1:C1" };
```
For a multi-cell array formula, every cell has the same array range but only the
first cell specifies the formula. Consider `D1:D3=A1:A3*B1:B3`:
```js
// API function
XLSX.utils.sheet_set_array_formula(worksheet, "D1:D3", "A1:A3*B1:B3");
// ... OR raw operations
worksheet['D1'] = { t:'n', F:"D1:D3", f:"A1:A3*B1:B3" };
worksheet['D2'] = { t:'n', F:"D1:D3" };
worksheet['D3'] = { t:'n', F:"D1:D3" };
```
Utilities and writers are expected to check for the presence of a `F` field and
ignore any possible formula element `f` in cells other than the starting cell.
They are not expected to perform validation of the formulae!
**Dynamic Array Formulae**
_Assign a dynamic array formula_
```js
XLSX.utils.sheet_set_array_formula(worksheet, range, formula, true);
```
Released in 2020, Dynamic Array Formulae are supported in the XLSX/XLSM and XLSB
file formats. They are represented like normal array formulae but have special
cell metadata indicating that the formula should be allowed to adjust the range.
An array formula can be marked as dynamic by setting the cell's `D` property to
true. The `F` range is expected but can be the set to the current cell:
```js
// API function
XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1);
// ... OR raw operations
worksheet['C1'] = { t: "s", f: "_xlfn.UNIQUE(A1:A3)", F:"C1", D: 1 }; // dynamic
```
**Localization with Function Names**
SheetJS operates at the file level. Excel stores formula expressions using the
English (United States) function names. For non-English users, Excel uses a
localized set of function names.
For example, when the computer language and region is set to French (France),
Excel interprets `=SOMME(A1:C3)` as if `SOMME` is the `SUM` function. However,
in the actual file, Excel stores `SUM(A1:C3)`.
**Prefixed "Future Functions"**
Functions introduced in newer versions of Excel are prefixed with `_xlfn.` when
stored in files. When writing formula expressions using these functions, the
prefix is required for maximal compatibility:
```js
// Broadest compatibility
XLSX.utils.sheet_set_array_formula(worksheet, "C1", "_xlfn.UNIQUE(A1:A3)", 1);
// Can cause errors in spreadsheet software
XLSX.utils.sheet_set_array_formula(worksheet, "C1", "UNIQUE(A1:A3)", 1);
```
When reading a file, the `xlfn` option preserves the prefixes.
<details>
<summary><b> Functions requiring `_xlfn.` prefix</b> (click to show)</summary>
This list is growing with each Excel release.
```
ACOT
ACOTH
AGGREGATE
ARABIC
BASE
BETA.DIST
BETA.INV
BINOM.DIST
BINOM.DIST.RANGE
BINOM.INV
BITAND
BITLSHIFT
BITOR
BITRSHIFT
BITXOR
BYCOL
BYROW
CEILING.MATH
CEILING.PRECISE
CHISQ.DIST
CHISQ.DIST.RT
CHISQ.INV
CHISQ.INV.RT
CHISQ.TEST
COMBINA
CONFIDENCE.NORM
CONFIDENCE.T
COT
COTH
COVARIANCE.P
COVARIANCE.S
CSC
CSCH
DAYS
DECIMAL
ERF.PRECISE
ERFC.PRECISE
EXPON.DIST
F.DIST
F.DIST.RT
F.INV
F.INV.RT
F.TEST
FIELDVALUE
FILTERXML
FLOOR.MATH
FLOOR.PRECISE
FORMULATEXT
GAMMA
GAMMA.DIST
GAMMA.INV
GAMMALN.PRECISE
GAUSS
HYPGEOM.DIST
IFNA
IMCOSH
IMCOT
IMCSC
IMCSCH
IMSEC
IMSECH
IMSINH
IMTAN
ISFORMULA
ISOMITTED
ISOWEEKNUM
LAMBDA
LET
LOGNORM.DIST
LOGNORM.INV
MAKEARRAY
MAP
MODE.MULT
MODE.SNGL
MUNIT
NEGBINOM.DIST
NORM.DIST
NORM.INV
NORM.S.DIST
NORM.S.INV
NUMBERVALUE
PDURATION
PERCENTILE.EXC
PERCENTILE.INC
PERCENTRANK.EXC
PERCENTRANK.INC
PERMUTATIONA
PHI
POISSON.DIST
QUARTILE.EXC
QUARTILE.INC
QUERYSTRING
RANDARRAY
RANK.AVG
RANK.EQ
REDUCE
RRI
SCAN
SEC
SECH
SEQUENCE
SHEET
SHEETS
SKEW.P
SORTBY
STDEV.P
STDEV.S
T.DIST
T.DIST.2T
T.DIST.RT
T.INV
T.INV.2T
T.TEST
UNICHAR
UNICODE
UNIQUE
VAR.P
VAR.S
WEBSERVICE
WEIBULL.DIST
XLOOKUP
XOR
Z.TEST
```
</details>
#### Row and Column Properties

@ -129,6 +129,7 @@ function wb_fmt() {
seen = true;
opts.cellFormula = true;
opts.cellNF = true;
opts.xlfn = true;
if(program.output) sheetname = program.output;
}
function isfmt(m/*:string*/)/*:boolean*/ {

@ -1 +1 @@
XLSX.version = '0.18.2';
XLSX.version = '0.18.3';

@ -13,7 +13,7 @@ function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
if(typeof Deno !== 'undefined') {
/* in this spot, it's safe to assume typed arrays and TextEncoder/TextDecoder exist */
if(enc) switch(enc) {
if(enc && typeof payload == "string") switch(enc) {
case "utf8": payload = new TextEncoder(enc).encode(payload); break;
case "binary": payload = s2ab(payload); break;
/* TODO: binary equivalent */

@ -1,3 +1,6 @@
function parse_Int32LE(data) {
return data.read_shift(4, 'i');
}
function write_UInt32LE(x/*:number*/, o) {
if (!o) o = new_buf(4);
o.write_shift(4, x);

@ -61,8 +61,8 @@ var ct2type/*{[string]:string}*/ = ({
"application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml": "links",
/* Metadata */
"application/vnd.ms-excel.sheetMetadata": "TODO",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "TODO",
"application/vnd.ms-excel.sheetMetadata": "metadata",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "metadata",
/* PivotCache */
"application/vnd.ms-excel.pivotCacheDefinition": "TODO",
@ -179,6 +179,10 @@ var CT_LIST = (function(){
xlsx: "application/vnd.ms-excel.macrosheet+xml",
xlsb: "application/vnd.ms-excel.macrosheet"
},
metadata: { /* Metadata (Stock/Geography and Dynamic Array) */
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml",
xlsb: "application/vnd.ms-excel.sheetMetadata"
},
styles: { /* Styles */
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
xlsb: "application/vnd.ms-excel.styles"
@ -198,7 +202,7 @@ function new_ct()/*:any*/ {
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
rels:[], strs:[], comments:[], links:[],
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
calcchains:[], vba: [], drawings: [],
calcchains:[], vba: [], drawings: [], metadata: [],
TODO:[], xmlns: "" }/*:any*/);
}
@ -297,6 +301,7 @@ function write_ct(ct, opts)/*:string*/ {
f3('vba');
f3('comments');
f3('drawings');
f2('metadata');
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
return o.join("");
}

151
bits/51_xlmeta.js Normal file

@ -0,0 +1,151 @@
RELS.XLMETA = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata";
function parse_xlmeta_xml(data, name, opts) {
var out = { Types: [] };
if (!data)
return out;
var pass = false;
data.replace(tagregex, function(x, idx) {
var y = parsexmltag(x);
switch (strip_ns(y[0])) {
case "<?xml":
break;
case "<metadata":
case "</metadata>":
break;
case "<metadataTypes":
case "</metadataTypes>":
break;
case "<metadataType":
out.Types.push({ name: y.name });
break;
case "<futureMetadata":
break;
case "</futureMetadata>":
break;
case "<bk>":
break;
case "</bk>":
break;
case "<rc":
break;
case "</rc>":
break;
case "<cellMetadata":
case "</cellMetadata>":
break;
case "<valueMetadata":
break;
case "</valueMetadata>":
break;
case "<extLst":
case "<extLst>":
case "</extLst>":
case "<extLst/>":
break;
case "<ext":
pass = true;
break;
case "</ext>":
pass = false;
break;
default:
if (!pass && opts.WTF)
throw new Error("unrecognized " + y[0] + " in metadata");
}
return x;
});
return out;
}
function write_xlmeta_xml() {
var o = [XML_HEADER];
o.push('<metadata xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xlrd="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" xmlns:xda="http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray">\n <metadataTypes count="1">\n <metadataType name="XLDAPR" minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1" cellMeta="1"/>\n </metadataTypes>\n <futureMetadata name="XLDAPR" count="1">\n <bk>\n <extLst>\n <ext uri="{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}">\n <xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>\n </ext>\n </extLst>\n </bk>\n </futureMetadata>\n <cellMetadata count="1">\n <bk>\n <rc t="1" v="0"/>\n </bk>\n </cellMetadata>\n</metadata>');
return o.join("");
}
function parse_BrtMdtinfo(data, length) {
return {
flags: data.read_shift(4),
version: data.read_shift(4),
name: parse_XLWideString(data, length - 8)
};
}
function write_BrtMdtinfo(data) {
var o = new_buf(12 + 2 * data.name.length);
o.write_shift(4, data.flags);
o.write_shift(4, data.version);
write_XLWideString(data.name, o);
return o.slice(0, o.l);
}
function write_BrtMdb(mdb) {
var o = new_buf(4 + 8 * mdb.length);
o.write_shift(4, mdb.length);
for (var i = 0; i < mdb.length; ++i) {
o.write_shift(4, mdb[i][0]);
o.write_shift(4, mdb[i][1]);
}
return o;
}
function write_BrtBeginEsfmd(cnt, name) {
var o = new_buf(8 + 2 * name.length);
o.write_shift(4, cnt);
write_XLWideString(name, o);
return o.slice(0, o.l);
}
function write_BrtBeginEsmdb(cnt, cm) {
var o = new_buf(8);
o.write_shift(4, cnt);
o.write_shift(4, cm ? 1 : 0);
return o;
}
function parse_xlmeta_bin(data, name, _opts) {
var out = { Types: [] };
var opts = _opts || {};
var state = [];
var pass = false;
recordhopper(data, function(val, R_n, RT) {
switch (RT) {
case 335:
out.Types.push({ name: val.name });
break;
case 51:
break;
case 35:
state.push(R_n);
pass = true;
break;
case 36:
state.pop();
pass = false;
break;
default:
if ((R_n || "").indexOf("Begin") > 0) {
} else if ((R_n || "").indexOf("End") > 0) {
} else if (!pass || opts.WTF && state[state.length - 1] != "BrtFRTBegin")
throw new Error("Unexpected record " + RT + " " + R_n);
}
});
return out;
}
function write_xlmeta_bin() {
var ba = buf_array();
write_record(ba, "BrtBeginMetadata");
write_record(ba, "BrtBeginEsmdtinfo", write_UInt32LE(1));
write_record(ba, "BrtMdtinfo", write_BrtMdtinfo({
name: "XLDAPR",
version: 12e4,
flags: 3496657072
}));
write_record(ba, "BrtEndEsmdtinfo");
write_record(ba, "BrtBeginEsfmd", write_BrtBeginEsfmd(1, "XLDAPR"));
write_record(ba, "BrtBeginFmd");
write_record(ba, "BrtFRTBegin", write_UInt32LE(514));
write_record(ba, "BrtBeginDynamicArrayPr", write_UInt32LE(0));
write_record(ba, "BrtEndDynamicArrayPr", writeuint16(1));
write_record(ba, "BrtFRTEnd");
write_record(ba, "BrtEndFmd");
write_record(ba, "BrtEndEsfmd");
write_record(ba, "BrtBeginEsmdb", write_BrtBeginEsmdb(1, true));
write_record(ba, "BrtMdb", write_BrtMdb([[1, 0]]));
write_record(ba, "BrtEndEsmdb");
write_record(ba, "BrtEndMetadata");
return ba.end();
}

@ -870,7 +870,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
var name = lbl ? lbl.Name : "SH33TJSNAME" + String(nameidx);
/* [MS-XLSB] 2.5.97.10 Ftab -- last verified 20220204 */
if(name && name.slice(0,6) == "_xlfn.") name = name.slice(6);
if(name && name.slice(0,6) == "_xlfn." && !opts.xlfn) name = name.slice(6);
stack.push(name);
break;

@ -300,6 +300,7 @@ function write_ws_xml_cell(cell/*:Cell*/, ref, ws, opts/*::, idx, wb*/)/*:string
}
if(cell.l) ws['!links'].push([ref, cell.l]);
if(cell.c) ws['!comments'].push([ref, cell.c]);
if(cell.D) o.cm = 1;
return writextag('c', v, o);
}
@ -476,6 +477,10 @@ return function parse_ws_xml_data(sdata/*:string*/, s, opts, guess/*:Range*/, th
}
safe_format(p, fmtid, fillid, opts, themes, styles);
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) { p.t = 'd'; p.v = numdate(p.v); }
if(tag.cm && opts.xlmeta) {
var cm = (opts.xlmeta.Types||[])[+tag.cm-1];
if(cm && cm.name == 'XLDAPR') p.D = true;
}
if(dense) {
var _r = decode_cell(tag.r);
if(!s[_r.r]) s[_r.r] = [];

@ -538,6 +538,8 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
XLSBRecordEnum[0x0010] = { n:"BrtShortReal", f:parse_BrtShortReal };
var cm, vm;
recordhopper(data, function ws_parse(val, R_n, RT) {
if(end) return;
switch(RT) {
@ -595,6 +597,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
}
if(!af && val.length > 3) p.f = val[3];
}
if(refguess.s.r > row.r) refguess.s.r = row.r;
if(refguess.s.c > C) refguess.s.c = C;
if(refguess.e.r < row.r) refguess.e.r = row.r;
@ -602,12 +605,17 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
if(opts.cellDates && cf && p.t == 'n' && SSF.is_date(SSF._table[cf.numFmtId])) {
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
}
if(cm) {
if(cm.name == 'XLDAPR') p.D = true;
cm = void 0;
}
if(vm) vm = void 0;
break;
case 0x0001: /* 'BrtCellBlank' */
case 0x000C: /* 'BrtShortBlank' */
if(!opts.sheetStubs || pass) break;
p = ({t:'z',v:undefined}/*:any*/);
p = ({t:'z',v:void 0}/*:any*/);
C = val[0].c == -1 ? C + 1 : val[0].c;
if(opts.dense) { if(!s[R]) s[R] = []; s[R][C] = p; }
else s[encode_col(C) + rr] = p;
@ -615,11 +623,20 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
if(refguess.s.c > C) refguess.s.c = C;
if(refguess.e.r < row.r) refguess.e.r = row.r;
if(refguess.e.c < C) refguess.e.c = C;
if(cm) {
if(cm.name == 'XLDAPR') p.D = true;
cm = void 0;
}
if(vm) vm = void 0;
break;
case 0x00B0: /* 'BrtMergeCell' */
merges.push(val); break;
case 0x0031: { /* 'BrtCellMeta' */
cm = ((opts.xlmeta||{}).Types||[])[val-1];
} break;
case 0x01EE: /* 'BrtHLink' */
var rel = rels['!id'][val.relId];
if(rel) {
@ -707,7 +724,6 @@ function parse_ws_bin(data, _opts, idx, rels, wb/*:WBWBProps*/, themes, styles)/
case 0x041A: /* 'BrtCFVO14' */
case 0x0289: /* 'BrtCellIgnoreEC' */
case 0x0451: /* 'BrtCellIgnoreEC14' */
case 0x0031: /* 'BrtCellMeta' */
case 0x024D: /* 'BrtCellSmartTagProperty' */
case 0x025F: /* 'BrtCellWatch' */
case 0x0234: /* 'BrtColor' */

@ -52,6 +52,11 @@ function parse_xlink(data, rel, name/*:string*/, opts) {
return parse_xlink_xml((data/*:any*/), rel, name, opts);
}
function parse_xlmeta(data, name/*:string*/, opts) {
if(name.slice(-4)===".bin") return parse_xlmeta_bin((data/*:any*/), name, opts);
return parse_xlmeta_xml((data/*:any*/), name, opts);
}
function write_wb(wb, name/*:string*/, opts) {
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
}
@ -81,3 +86,7 @@ function write_cc(data, name:string, opts) {
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
}
*/
function write_xlmeta(name/*:string*/) {
return (name.slice(-4)===".bin" ? write_xlmeta_bin : write_xlmeta_xml)();
}

@ -48,7 +48,7 @@ var XLSBRecordEnum = {
/*::[*/0x002E/*::]*/: { n:"BrtBorder", f:parse_BrtBorder },
/*::[*/0x002F/*::]*/: { n:"BrtXF", f:parse_BrtXF },
/*::[*/0x0030/*::]*/: { n:"BrtStyle" },
/*::[*/0x0031/*::]*/: { n:"BrtCellMeta" },
/*::[*/0x0031/*::]*/: { n:"BrtCellMeta", f:parse_Int32LE },
/*::[*/0x0032/*::]*/: { n:"BrtValueMeta" },
/*::[*/0x0033/*::]*/: { n:"BrtMdb" },
/*::[*/0x0034/*::]*/: { n:"BrtBeginFmd" },
@ -274,7 +274,7 @@ var XLSBRecordEnum = {
/*::[*/0x014C/*::]*/: { n:"BrtBeginMetadata" },
/*::[*/0x014D/*::]*/: { n:"BrtEndMetadata" },
/*::[*/0x014E/*::]*/: { n:"BrtBeginEsmdtinfo" },
/*::[*/0x014F/*::]*/: { n:"BrtMdtinfo" },
/*::[*/0x014F/*::]*/: { n:"BrtMdtinfo", f:parse_BrtMdtinfo },
/*::[*/0x0150/*::]*/: { n:"BrtEndEsmdtinfo" },
/*::[*/0x0151/*::]*/: { n:"BrtBeginEsmdb" },
/*::[*/0x0152/*::]*/: { n:"BrtEndEsmdb" },
@ -795,7 +795,7 @@ var XLSBRecordEnum = {
/*::[*/0x0835/*::]*/: { n:"BrtEndTimelineStyleElements" },
/*::[*/0x0836/*::]*/: { n:"BrtDxf15" },
/*::[*/0x0837/*::]*/: { n:"BrtBeginDxfs15" },
/*::[*/0x0838/*::]*/: { n:"brtEndDxfs15" },
/*::[*/0x0838/*::]*/: { n:"BrtEndDxfs15" },
/*::[*/0x0839/*::]*/: { n:"BrtSlicerCacheHideItemsWithNoData" },
/*::[*/0x083A/*::]*/: { n:"BrtBeginItemUniqueNames" },
/*::[*/0x083B/*::]*/: { n:"BrtEndItemUniqueNames" },
@ -835,9 +835,27 @@ var XLSBRecordEnum = {
/*::[*/0x085D/*::]*/: { n:"BrtModelTimeGroupingCalcCol" },
/*::[*/0x0C00/*::]*/: { n:"BrtUid" },
/*::[*/0x0C01/*::]*/: { n:"BrtRevisionPtr" },
/*::[*/0x13e7/*::]*/: { n:"BrtBeginCalcFeatures" },
/*::[*/0x13e8/*::]*/: { n:"BrtEndCalcFeatures" },
/*::[*/0x13e9/*::]*/: { n:"BrtCalcFeature" },
/*::[*/0x1000/*::]*/: { n:"BrtBeginDynamicArrayPr" },
/*::[*/0x1001/*::]*/: { n:"BrtEndDynamicArrayPr" },
/*::[*/0x138A/*::]*/: { n:"BrtBeginRichValueBlock" },
/*::[*/0x138B/*::]*/: { n:"BrtEndRichValueBlock" },
/*::[*/0x13D9/*::]*/: { n:"BrtBeginRichFilters" },
/*::[*/0x13DA/*::]*/: { n:"BrtEndRichFilters" },
/*::[*/0x13DB/*::]*/: { n:"BrtRichFilter" },
/*::[*/0x13DC/*::]*/: { n:"BrtBeginRichFilterColumn" },
/*::[*/0x13DD/*::]*/: { n:"BrtEndRichFilterColumn" },
/*::[*/0x13DE/*::]*/: { n:"BrtBeginCustomRichFilters" },
/*::[*/0x13DF/*::]*/: { n:"BrtEndCustomRichFilters" },
/*::[*/0x13E0/*::]*/: { n:"BrtCustomRichFilter" },
/*::[*/0x13E1/*::]*/: { n:"BrtTop10RichFilter" },
/*::[*/0x13E2/*::]*/: { n:"BrtDynamicRichFilter" },
/*::[*/0x13E4/*::]*/: { n:"BrtBeginRichSortCondition" },
/*::[*/0x13E5/*::]*/: { n:"BrtEndRichSortCondition" },
/*::[*/0x13E6/*::]*/: { n:"BrtRichFilterDateGroupItem" },
/*::[*/0x13E7/*::]*/: { n:"BrtBeginCalcFeatures" },
/*::[*/0x13E8/*::]*/: { n:"BrtEndCalcFeatures" },
/*::[*/0x13E9/*::]*/: { n:"BrtCalcFeature" },
/*::[*/0x13EB/*::]*/: { n:"BrtExternalLinksPr" },
/*::[*/0xFFFF/*::]*/: { n:"" }
};

@ -161,9 +161,16 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
var wbrelsi = dir.workbooks[0].lastIndexOf("/");
var wbrelsfile = (dir.workbooks[0].slice(0, wbrelsi+1) + "_rels/" + dir.workbooks[0].slice(wbrelsi+1) + ".rels").replace(/^\//,"");
if(!safegetzipfile(zip, wbrelsfile)) wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
var wbrels = parse_rels(getzipstr(zip, wbrelsfile, true), wbrelsfile);
var wbrels = parse_rels(getzipstr(zip, wbrelsfile, true), wbrelsfile.replace(/_rels.*/, "s5s"));
if((dir.metadata || []).length >= 1) {
/* TODO: MDX and other types of metadata */
opts.xlmeta = parse_xlmeta(getzipdata(zip, strip_front_slash(dir.metadata[0])),dir.metadata[0],opts);
}
if(wbrels) wbrels = safe_parse_wbrels(wbrels, wb.Sheets);
/* Numbers iOS hack */
var nmode = (getzipdata(zip,"xl/worksheets/sheet.xml",true))?1:0;
wsloop: for(i = 0; i != props.Worksheets; ++i) {

@ -124,6 +124,11 @@ function write_zip_xlsxb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA);
}
f = "xl/metadata." + wbext;
zip_add_file(zip, f, write_xlmeta(f));
ct.metadata.push(f);
add_rels(opts.wbrels, -1, "metadata." + wbext, RELS.XLMETA);
zip_add_file(zip, "[Content_Types].xml", write_ct(ct, opts));
zip_add_file(zip, '_rels/.rels', write_rels(opts.rels));
zip_add_file(zip, 'xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
@ -132,7 +137,7 @@ function write_zip_xlsxb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
return zip;
}
/* this version does not reference XLSB write functions */
function write_zip_xlsx(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
_shapeid = 1024;
if(wb && !wb.SSF) {
@ -254,6 +259,11 @@ function write_zip_xlsx(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA);
}
f = "xl/metadata." + wbext;
zip_add_file(zip, f, write_xlmeta_xml());
ct.metadata.push(f);
add_rels(opts.wbrels, -1, "metadata." + wbext, RELS.XLMETA);
zip_add_file(zip, "[Content_Types].xml", write_ct(ct, opts));
zip_add_file(zip, '_rels/.rels', write_rels(opts.rels));
zip_add_file(zip, 'xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));

@ -45,14 +45,15 @@ function write_zip_typeXLSX(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
}
function write_zip_denouement(z/*:any*/, o/*:?WriteOpts*/)/*:any*/ {
var oopts = {};
var ftype = has_buf ? "nodebuffer" : (typeof Uint8Array !== "undefined" ? "array" : "string");
if(o.compression) oopts.compression = 'DEFLATE';
if(o.password) oopts.type = has_buf ? "nodebuffer" : "string";
if(o.password) oopts.type = ftype;
else switch(o.type) {
case "base64": oopts.type = "base64"; break;
case "binary": oopts.type = "string"; break;
case "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
case "buffer":
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
case "file": oopts.type = ftype; break;
default: throw new Error("Unrecognized type " + o.type);
}
var out = z.FullPaths ? CFB.write(z, {fileType:"zip", type: /*::(*/{"nodebuffer": "buffer", "string": "binary"}/*:: :any)*/[oopts.type] || oopts.type, compression: !!o.compression}) : z.generate(oopts);

@ -98,7 +98,7 @@ utils.cell_add_comment = function(cell/*:Cell*/, text/*:string*/, author/*:?stri
};
/* set array formula and flush related cells */
utils.sheet_set_array_formula = function(ws/*:Worksheet*/, range, formula/*:string*/) {
utils.sheet_set_array_formula = function(ws/*:Worksheet*/, range, formula/*:string*/, dynamic/*:boolean*/) {
var rng = typeof range != "string" ? range : safe_decode_range(range);
var rngstr = typeof range == "string" ? range : encode_range(range);
for(var R = rng.s.r; R <= rng.e.r; ++R) for(var C = rng.s.c; C <= rng.e.c; ++C) {
@ -106,7 +106,10 @@ utils.sheet_set_array_formula = function(ws/*:Worksheet*/, range, formula/*:stri
cell.t = 'n';
cell.F = rngstr;
delete cell.v;
if(R == rng.s.r && C == rng.s.c) cell.f = formula;
if(R == rng.s.r && C == rng.s.c) {
cell.f = formula;
if(dynamic) cell.D = true;
}
}
return ws;
};

@ -169,8 +169,8 @@ curl -X GET http://localhost:7262/?f=sheetjs.xlsb
[NestJS](https://nestjs.com/) is a Node.js framework for server-side web applications.
This demo uses SheetJS to injest a spreadsheet via a POST API endpoint. The file
arrive to the endpoint as body `form-data`, accessible using the `file` key.
This demo uses SheetJS to parse a spreadsheet via a POST API endpoint. The file
arrives to the endpoint as body `form-data`, accessible using the `file` key.
After parsing the file, CSV contents of the first worksheet will be returned.
[Body parsing uses `multer`](https://docs.nestjs.com/techniques/file-upload).

30
dist/xlsx.core.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.core.min.map generated vendored

File diff suppressed because one or more lines are too long

322
dist/xlsx.extendscript.js generated vendored

@ -160,7 +160,7 @@ var DO_NOT_EXPORT_CODEPAGE = true;
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
function make_xlsx_lib(XLSX){
XLSX.version = '0.18.2';
XLSX.version = '0.18.3';
var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true, window */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -288,7 +288,7 @@ var Base64 = function() {
}
};
}();
var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && !!process.versions.node);
var has_buf = (typeof Buffer !== 'undefined' && typeof undefined !== 'undefined' && typeof ({}) !== 'undefined' && !!({}).node);
var Buffer_from = function(){};
@ -1640,7 +1640,7 @@ function parse_extra_field(blob) {
return o;
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function get_fs() { return fs || (fs = undefined); }
function parse(file, options) {
if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
if((file[0] | 0x20) == 0x6d && (file[1]|0x20) == 0x69) return parse_mad(file, options);
@ -3256,7 +3256,7 @@ return exports;
if(typeof require !== 'undefined' && typeof module !== 'undefined' && typeof DO_NOT_EXPORT_CFB === 'undefined') { module.exports = CFB; }
var _fs;
if(typeof require !== 'undefined') try { _fs = require('fs'); } catch(e) {}
if(typeof require !== 'undefined') try { _fs = undefined; } catch(e) {}
/* normalize data for blob ctor */
function blobify(data) {
@ -3270,7 +3270,7 @@ function write_dl(fname, payload, enc) {
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname,