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:
parent
4f6a849a59
commit
e14aee3e51
@ -41,6 +41,7 @@ ExtendScript
|
||||
IndexedDB
|
||||
JavaScriptCore
|
||||
LocalStorage
|
||||
NestJS
|
||||
NPM
|
||||
Nuxt.js
|
||||
Redis
|
||||
|
7
Makefile
7
Makefile
@ -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
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
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
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
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
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, |