forked from sheetjs/sheetjs
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, 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 */
|
||||
@ -4213,43 +4213,6 @@ function encode_range_xls(r, opts) {
|
||||
}
|
||||
return encode_cell_xls(r.s, opts.biff) + ":" + encode_cell_xls(r.e, opts.biff);
|
||||
}
|
||||
var OFFCRYPTO = {};
|
||||
|
||||
var make_offcrypto = function(O, _crypto) {
|
||||
var crypto;
|
||||
if(typeof _crypto !== 'undefined') crypto = _crypto;
|
||||
else if(typeof require !== 'undefined') {
|
||||
try { crypto = undefined; }
|
||||
catch(e) { crypto = null; }
|
||||
}
|
||||
|
||||
O.rc4 = function(key, data) {
|
||||
var S = new Array(256);
|
||||
var c = 0, i = 0, j = 0, t = 0;
|
||||
for(i = 0; i != 256; ++i) S[i] = i;
|
||||
for(i = 0; i != 256; ++i) {
|
||||
j = (j + S[i] + (key[i%key.length]).charCodeAt(0))&255;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
}
|
||||
// $FlowIgnore
|
||||
i = j = 0; var out = new_raw_buf(data.length);
|
||||
for(c = 0; c != data.length; ++c) {
|
||||
i = (i + 1)&255;
|
||||
j = (j + S[i])%256;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
out[c] = (data[c] ^ S[(S[i]+S[j])&255]);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
O.md5 = function(hex) {
|
||||
if(!crypto) throw new Error("Unsupported crypto");
|
||||
return crypto.createHash('md5').update(hex).digest('hex');
|
||||
};
|
||||
};
|
||||
/*global crypto:true */
|
||||
make_offcrypto(OFFCRYPTO, typeof crypto !== "undefined" ? crypto : undefined);
|
||||
|
||||
function decode_row(rowstr) { return parseInt(unfix_row(rowstr),10) - 1; }
|
||||
function encode_row(row) { return "" + (row + 1); }
|
||||
function fix_row(cstr) { return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2"); }
|
||||
@ -4414,6 +4377,9 @@ function sheet_add_aoa(_ws, data, opts) {
|
||||
}
|
||||
function aoa_to_sheet(data, opts) { return sheet_add_aoa(null, data, opts); }
|
||||
|
||||
function parse_Int32LE(data) {
|
||||
return data.read_shift(4, 'i');
|
||||
}
|
||||
function write_UInt32LE(x, o) {
|
||||
if (!o) o = new_buf(4);
|
||||
o.write_shift(4, x);
|
||||
@ -5079,8 +5045,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",
|
||||
@ -5197,6 +5163,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"
|
||||
@ -5216,7 +5186,7 @@ function new_ct() {
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
|
||||
rels:[], strs:[], comments:[], links:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [], drawings: [],
|
||||
calcchains:[], vba: [], drawings: [], metadata: [],
|
||||
TODO:[], xmlns: "" });
|
||||
}
|
||||
|
||||
@ -5315,6 +5285,7 @@ function write_ct(ct, opts) {
|
||||
f3('vba');
|
||||
f3('comments');
|
||||
f3('drawings');
|
||||
f2('metadata');
|
||||
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
@ -5381,7 +5352,9 @@ var RELS_EXTERN = [RELS.HLINK, RELS.XPATH, RELS.XMISS];
|
||||
function add_rels(rels, rId, f, type, relobj, targetmode) {
|
||||
if(!relobj) relobj = {};
|
||||
if(!rels['!id']) rels['!id'] = {};
|
||||
if(rId < 0) for(rId = 1; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
if(!rels['!idx']) rels['!idx'] = 1;
|
||||
if(rId < 0) for(rId = rels['!idx']; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
rels['!idx'] = rId + 1;
|
||||
relobj.Id = 'rId' + rId;
|
||||
relobj.Type = type;
|
||||
relobj.Target = f;
|
||||
@ -11365,6 +11338,157 @@ function update_xfext(xf, xfext) {
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
/* 18.6 Calculation Chain */
|
||||
function parse_cc_xml(data) {
|
||||
var d = [];
|
||||
@ -12690,7 +12814,7 @@ ixti = f[1][1]; r = f[1][2];
|
||||
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;
|
||||
|
||||
@ -14588,6 +14712,7 @@ function write_ws_xml_cell(cell, ref, ws, opts) {
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -14764,6 +14889,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
}
|
||||
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] = [];
|
||||
@ -14846,7 +14975,7 @@ function write_ws_xml(idx, opts, wb, rels) {
|
||||
|
||||
o[o.length] = write_ws_xml_sheetviews(ws, opts, idx, wb);
|
||||
|
||||
/* TODO: store in WB, process styles */
|
||||
/* TODO: store in WB, undefined styles */
|
||||
if(opts.sheetFormat) o[o.length] = (writextag('sheetFormatPr', null, {
|
||||
defaultRowHeight:opts.sheetFormat.defaultRowHeight||'16',
|
||||
baseColWidth:opts.sheetFormat.baseColWidth||'10',
|
||||
@ -15477,6 +15606,8 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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) {
|
||||
@ -15534,6 +15665,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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;
|
||||
@ -15541,12 +15673,17 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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});
|
||||
p = ({t:'z',v:void 0});
|
||||
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;
|
||||
@ -15554,11 +15691,20 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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) {
|
||||
@ -15646,7 +15792,6 @@ function parse_ws_bin(data, _opts, idx, rels, wb, themes, styles) {
|
||||
case 0x041A: /* 'BrtCFVO14' */
|
||||
case 0x0289: /* 'BrtCellIgnoreEC' */
|
||||
case 0x0451: /* 'BrtCellIgnoreEC14' */
|
||||
case 0x0031: /* 'BrtCellMeta' */
|
||||
case 0x024D: /* 'BrtCellSmartTagProperty' */
|
||||
case 0x025F: /* 'BrtCellWatch' */
|
||||
case 0x0234: /* 'BrtColor' */
|
||||
@ -16851,6 +16996,11 @@ function parse_xlink(data, rel, name, opts) {
|
||||
return parse_xlink_xml((data), rel, name, opts);
|
||||
}
|
||||
|
||||
function parse_xlmeta(data, name, opts) {
|
||||
if(name.slice(-4)===".bin") return parse_xlmeta_bin((data), name, opts);
|
||||
return parse_xlmeta_xml((data), name, opts);
|
||||
}
|
||||
|
||||
function write_wb(wb, name, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
|
||||
}
|
||||
@ -16880,6 +17030,10 @@ function write_cc(data, name:string, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
|
||||
}
|
||||
*/
|
||||
|
||||
function write_xlmeta(name) {
|
||||
return (name.slice(-4)===".bin" ? write_xlmeta_bin : write_xlmeta_xml)();
|
||||
}
|
||||
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var attregex2=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
|
||||
function xlml_parsexmltag(tag, skip_root) {
|
||||
@ -18294,7 +18448,7 @@ function parse_workbook(blob, options) {
|
||||
var last_Rn = '';
|
||||
var file_depth = 0; /* TODO: make a real stack */
|
||||
var BIFF2Fmt = 0, BIFF2FmtTable = [];
|
||||
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
||||
var FilterDatabases = []; /* TODO: sort out supbooks and undefined elsewhere */
|
||||
var last_lbl;
|
||||
|
||||
/* explicit override for some broken writers */
|
||||
@ -19136,7 +19290,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" },
|
||||
@ -19362,7 +19516,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" },
|
||||
@ -19883,7 +20037,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" },
|
||||
@ -19923,9 +20077,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:"" }
|
||||
};
|
||||
|
||||
@ -20992,7 +21164,7 @@ function sheet_add_dom(ws, table, _opts) {
|
||||
}
|
||||
if(o.z === undefined && z != null) o.z = z;
|
||||
/* The first link is used. Links are assumed to be fully specified.
|
||||
* TODO: The right way to process relative links is to make a new <a> */
|
||||
* TODO: The right way to undefined relative links is to make a new <a> */
|
||||
var l = "", Aelts = elt.getElementsByTagName("A");
|
||||
if(Aelts && Aelts.length) for(var Aelti = 0; Aelti < Aelts.length; ++Aelti) if(Aelts[Aelti].hasAttribute("href")) {
|
||||
l = Aelts[Aelti].getAttribute("href"); if(l.charAt(0) != "#") break;
|
||||
@ -22778,9 +22950,16 @@ function parse_zip(zip, opts) {
|
||||
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) {
|
||||
@ -23013,6 +23192,11 @@ f = "docProps/app.xml";
|
||||
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));
|
||||
@ -23021,7 +23205,7 @@ f = "docProps/app.xml";
|
||||
return zip;
|
||||
}
|
||||
|
||||
|
||||
/* this version does not reference XLSB write functions */
|
||||
function write_zip_xlsx(wb, opts) {
|
||||
_shapeid = 1024;
|
||||
if(wb && !wb.SSF) {
|
||||
@ -23142,6 +23326,11 @@ f = "docProps/app.xml";
|
||||
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));
|
||||
@ -23195,6 +23384,7 @@ function read_plaintext_raw(data, o) {
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str);
|
||||
o.type = "binary";
|
||||
return read_plaintext(str, o);
|
||||
}
|
||||
|
||||
@ -23313,14 +23503,15 @@ function write_zip_typeXLSX(wb, opts) {
|
||||
}
|
||||
function write_zip_denouement(z, o) {
|
||||
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"}[oopts.type] || oopts.type, compression: !!o.compression}) : z.generate(oopts);
|
||||
@ -23838,7 +24029,7 @@ utils.cell_add_comment = function(cell, text, author) {
|
||||
};
|
||||
|
||||
/* set array formula and flush related cells */
|
||||
utils.sheet_set_array_formula = function(ws, range, formula) {
|
||||
utils.sheet_set_array_formula = function(ws, range, formula, dynamic) {
|
||||
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) {
|
||||
@ -23846,7 +24037,10 @@ utils.sheet_set_array_formula = function(ws, range, formula) {
|
||||
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;
|
||||
};
|
||||
|
30
dist/xlsx.full.min.js
generated
vendored
30
dist/xlsx.full.min.js
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.full.min.map
generated
vendored
2
dist/xlsx.full.min.map
generated
vendored
File diff suppressed because one or more lines are too long
16
dist/xlsx.mini.min.js
generated
vendored
16
dist/xlsx.mini.min.js
generated
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.mini.min.map
generated
vendored
2
dist/xlsx.mini.min.map
generated
vendored
File diff suppressed because one or more lines are too long
@ -24,9 +24,5 @@ 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>
|
||||
|
||||
|
@ -38,7 +38,6 @@ The complete single-file version is generated at `dist/xlsx.full.min.js`
|
||||
|
||||
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 / Numbers
|
||||
- node stream utils removed
|
||||
|
||||
|
@ -48,6 +48,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_
|
||||
@ -56,15 +68,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
|
||||
|
@ -40,3 +40,9 @@ 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.
|
||||
|
@ -440,3 +440,4 @@ const workbook = XLSX.read(data);
|
||||
</details>
|
||||
|
||||
More detailed examples are covered in the [included demos](demos/)
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
### 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();
|
||||
@ -11,16 +11,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**
|
||||
@ -33,14 +26,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
|
||||
|
@ -1,38 +1,54 @@
|
||||
## 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 ]
|
||||
@ -45,39 +61,58 @@ 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.
|
||||
|
||||
|
@ -255,3 +255,42 @@ 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.
|
||||
|
||||
|
@ -9,6 +9,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 |
|
||||
|
@ -6,79 +6,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 |
|
||||
|:-----------------------|:-------------------------|:-----:|:-----:|
|
||||
@ -92,5 +20,271 @@ 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>
|
||||
|
||||
|
1
mini.lst
1
mini.lst
@ -28,6 +28,7 @@ bits/42_sstxml.js
|
||||
bits/46_stycommon.js
|
||||
bits/47_styxml.js
|
||||
bits/49_theme.js
|
||||
misc/51_xlsxmeta.js
|
||||
bits/53_externlink.js
|
||||
bits/54_drawing.js
|
||||
bits/55_vml.js
|
||||
|
@ -14,7 +14,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 */
|
||||
|
63
misc/51_xlsxmeta.js
Normal file
63
misc/51_xlsxmeta.js
Normal file
@ -0,0 +1,63 @@
|
||||
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("");
|
||||
}
|
@ -24,10 +24,8 @@ port calculations to web apps; automate common spreadsheet tasks, and much more!
|
||||
|
||||
![circo graph of format support](formats.png)
|
||||
|
||||
|
||||
![graph legend](legend.png)
|
||||
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
||||
@ -42,14 +40,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)
|
||||
@ -140,10 +141,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
|
||||
|
||||
|
||||
@ -303,6 +305,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_
|
||||
@ -311,15 +325,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
|
||||
@ -559,6 +564,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
|
||||
@ -962,12 +973,13 @@ const workbook = XLSX.read(data);
|
||||
|
||||
|
||||
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();
|
||||
@ -975,16 +987,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**
|
||||
@ -997,14 +1002,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
|
||||
@ -1284,36 +1289,55 @@ const workbook = XLSX.utils.table_to_book(doc);
|
||||
```
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
### 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);
|
||||
```
|
||||
|
||||
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**
|
||||
|
||||
|
||||
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 ]
|
||||
@ -1325,38 +1349,57 @@ XLSX.utils.book_append_sheet(wb, ws, ws_name);
|
||||
```
|
||||
|
||||
|
||||
### 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**
|
||||
|
||||
|
||||
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 });
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
@ -1832,6 +1875,45 @@ const S5SComponent = {
|
||||
The [`vuejs` demo](demos/vue) includes more React examples.
|
||||
|
||||
|
||||
### 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
|
||||
@ -1934,6 +2016,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 |
|
||||
@ -2261,66 +2344,6 @@ Even though some formats store formulae with a leading equal sign, CSF formulae
|
||||
do not start with `=`.
|
||||
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:1 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', v:3, f:'A1+A2' }
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
|
||||
```js
|
||||
{
|
||||
"!ref": "A1:A3",
|
||||
A1: { t:'n', v:3.14159 },
|
||||
A2: { t:'n', v:2 },
|
||||
A3: { t:'n', f:'BESSELJ(A1,A2)' }
|
||||
}
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
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" };
|
||||
```
|
||||
|
||||
|
||||
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!
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
| Storage Representation | Formats | Read | Write |
|
||||
|:-----------------------|:-------------------------|:-----:|:-----:|
|
||||
| A1-style strings | XLSX | ✔ | ✔ |
|
||||
@ -2334,6 +2357,269 @@ 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.
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
#### Row and Column Properties
|
||||
|
||||
|
||||
|
@ -10,14 +10,17 @@
|
||||
* [Parsing Workbooks](README.md#parsing-workbooks)
|
||||
* [Processing JSON and JS Data](README.md#processing-json-and-js-data)
|
||||
* [Processing HTML Tables](README.md#processing-html-tables)
|
||||
- [Working with the Workbook](README.md#working-with-the-workbook)
|
||||
* [Parsing and Writing Examples](README.md#parsing-and-writing-examples)
|
||||
- [Processing Data](README.md#processing-data)
|
||||
* [Modifying Workbook Structure](README.md#modifying-workbook-structure)
|
||||
* [Modifying Cell Values](README.md#modifying-cell-values)
|
||||
* [Modifying Other Worksheet / Workbook / Cell Properties](README.md#modifying-other-worksheet--workbook--cell-properties)
|
||||
- [Packaging and Releasing Data](README.md#packaging-and-releasing-data)
|
||||
* [Writing Workbooks](README.md#writing-workbooks)
|
||||
* [Writing Examples](README.md#writing-examples)
|
||||
* [Streaming Write](README.md#streaming-write)
|
||||
* [Generating JSON and JS Data](README.md#generating-json-and-js-data)
|
||||
* [Generating HTML Tables](README.md#generating-html-tables)
|
||||
* [Generating Single-Worksheet Snapshots](README.md#generating-single-worksheet-snapshots)
|
||||
- [Interface](README.md#interface)
|
||||
* [Parsing functions](README.md#parsing-functions)
|
||||
* [Writing functions](README.md#writing-functions)
|
||||
|
@ -38,6 +38,7 @@ bits/47_styxml.js
|
||||
bits/48_stybin.js
|
||||
bits/49_theme.js
|
||||
bits/50_styxls.js
|
||||
bits/51_xlmeta.js
|
||||
bits/52_calcchain.js
|
||||
bits/53_externlink.js
|
||||
bits/54_drawing.js
|
||||
|
151
modules/51_xlmeta.js
Normal file
151
modules/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();
|
||||
}
|
88
modules/51_xlsbmeta.js
Normal file
88
modules/51_xlsbmeta.js
Normal file
@ -0,0 +1,88 @@
|
||||
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();
|
||||
}
|
120
modules/51_xlsbmeta.ts
Normal file
120
modules/51_xlsbmeta.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/// <reference path="src/types.ts"/>
|
||||
|
||||
/* [MS-XLSB] 2.4.698 BrtMdtinfo */
|
||||
interface BrtMdtinfo {
|
||||
flags: number;
|
||||
version: number;
|
||||
name: string;
|
||||
}
|
||||
function parse_BrtMdtinfo(data: ReadableData, length: number): BrtMdtinfo {
|
||||
return {
|
||||
flags: data.read_shift(4),
|
||||
version: data.read_shift(4),
|
||||
name: parse_XLWideString(data, length - 8)
|
||||
};
|
||||
}
|
||||
function write_BrtMdtinfo(data: BrtMdtinfo): RawData {
|
||||
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);
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.697 BrtMdb */
|
||||
type Mdir = [number, number]; // "t", "v" in XLSX parlance
|
||||
type BrtMdb = Mdir[];
|
||||
function write_BrtMdb(mdb: BrtMdb): RawData {
|
||||
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;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.72 BrtBeginEsfmd */
|
||||
function write_BrtBeginEsfmd(cnt: number, name: string): RawData {
|
||||
var o = new_buf(8 + 2 * name.length);
|
||||
o.write_shift(4, cnt);
|
||||
write_XLWideString(name, o);
|
||||
return o.slice(0, o.l);
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.4.73 BrtBeginEsmdb */
|
||||
function write_BrtBeginEsmdb(cnt: number, cm: boolean): RawData {
|
||||
var o = new_buf(8);
|
||||
o.write_shift(4, cnt);
|
||||
o.write_shift(4, cm ? 1 : 0);
|
||||
return o;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.1.7.34 Metadata */
|
||||
function parse_xlmeta_bin(data, name: string, _opts?: ParseXLMetaOptions): XLMeta {
|
||||
var out: XLMeta = { Types: [] };
|
||||
var opts = _opts || {};
|
||||
var state: string[] = [];
|
||||
var pass = false;
|
||||
|
||||
recordhopper(data, (val, R_n, RT) => {
|
||||
switch(RT) {
|
||||
// case 0x014C: /* 'BrtBeginMetadata' */
|
||||
// case 0x014D: /* 'BrtEndMetadata' */
|
||||
// case 0x014E: /* 'BrtBeginEsmdtinfo' */
|
||||
// case 0x0150: /* 'BrtEndEsmdtinfo' */
|
||||
// case 0x0151: /* 'BrtBeginEsmdb' */
|
||||
// case 0x0152: /* 'BrtEndEsmdb' */
|
||||
// case 0x0153: /* 'BrtBeginEsfmd' */
|
||||
// case 0x0154: /* 'BrtEndEsfmd' */
|
||||
// case 0x0034: /* 'BrtBeginFmd' */
|
||||
// case 0x0035: /* 'BrtEndFmd' */
|
||||
// case 0x1000: /* 'BrtBeginDynamicArrayPr' */
|
||||
// case 0x1001: /* 'BrtEndDynamicArrayPr' */
|
||||
// case 0x138A: /* 'BrtBeginRichValueBlock' */
|
||||
// case 0x138B: /* 'BrtEndRichValueBlock' */
|
||||
|
||||
case 0x014F: /* 'BrtMdtinfo' */
|
||||
out.Types.push({name: (val as BrtMdtinfo).name});
|
||||
break;
|
||||
case 0x0033: /* 'BrtMdb' */
|
||||
break;
|
||||
|
||||
case 0x0023: /* 'BrtFRTBegin' */
|
||||
state.push(R_n); pass = true; break;
|
||||
case 0x0024: /* 'BrtFRTEnd' */
|
||||
state.pop(); pass = false; break;
|
||||
default:
|
||||
if((R_n||"").indexOf("Begin") > 0){/* empty */}
|
||||
else if((R_n||"").indexOf("End") > 0){/* empty */}
|
||||
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: 120000,
|
||||
flags: 0xD06AC0B0
|
||||
}));
|
||||
write_record(ba, "BrtEndEsmdtinfo");
|
||||
/* [ESSTR] [ESMDX] */
|
||||
write_record(ba, "BrtBeginEsfmd", write_BrtBeginEsfmd(1, "XLDAPR"));
|
||||
write_record(ba, "BrtBeginFmd");
|
||||
write_record(ba, "BrtFRTBegin", write_UInt32LE(0x0202));
|
||||
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");
|
||||
/* *FRT */
|
||||
write_record(ba, "BrtEndMetadata");
|
||||
return ba.end();
|
||||
}
|
63
modules/51_xlsxmeta.js
Normal file
63
modules/51_xlsxmeta.js
Normal file
@ -0,0 +1,63 @@
|
||||
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("");
|
||||
}
|
85
modules/51_xlsxmeta.ts
Normal file
85
modules/51_xlsxmeta.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/// <reference path="src/types.ts"/>
|
||||
|
||||
RELS.XLMETA = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata";
|
||||
|
||||
/* 12.3.10 Metadata Part */
|
||||
function parse_xlmeta_xml(data: string, name: string, opts?: ParseXLMetaOptions): XLMeta {
|
||||
var out: XLMeta = { Types: [] };
|
||||
if(!data) return out;
|
||||
var pass = false;
|
||||
|
||||
data.replace(tagregex, (x: string, idx: number) => {
|
||||
var y: any = parsexmltag(x);
|
||||
switch(strip_ns(y[0])) {
|
||||
case '<?xml': break;
|
||||
|
||||
/* 18.9.8 */
|
||||
case '<metadata': case '</metadata>': break;
|
||||
|
||||
/* 18.9.11 */
|
||||
case '<metadataTypes': case '</metadataTypes>': break;
|
||||
|
||||
/* 18.9.10 */
|
||||
case '<metadataType':
|
||||
out.Types.push({ name: y.name });
|
||||
break;
|
||||
|
||||
/* 18.9.4 */
|
||||
case '<futureMetadata': break;
|
||||
case '</futureMetadata>': break;
|
||||
|
||||
/* 18.9.1 */
|
||||
case '<bk>': break;
|
||||
case '</bk>': break;
|
||||
|
||||
/* 18.9.15 */
|
||||
case '<rc': break;
|
||||
case '</rc>': break;
|
||||
|
||||
/* 18.9.3 */
|
||||
case '<cellMetadata':
|
||||
case '</cellMetadata>': break;
|
||||
|
||||
/* 18.9.17 */
|
||||
case '<valueMetadata': break;
|
||||
case '</valueMetadata>': break;
|
||||
|
||||
/* 18.2.10 extLst CT_ExtensionList ? */
|
||||
case '<extLst': case '<extLst>': case '</extLst>': case '<extLst/>': break;
|
||||
|
||||
/* 18.2.7 ext CT_Extension + */
|
||||
case '<ext': pass=true; break; //TODO: check with versions of excel
|
||||
case '</ext>': pass=false; break;
|
||||
|
||||
default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in metadata');
|
||||
}
|
||||
return x;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
/* TODO: coordinate with cell writing, pass flags */
|
||||
function write_xlmeta_xml(): string {
|
||||
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">
|
||||
<metadataTypes count="1">
|
||||
<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"/>
|
||||
</metadataTypes>
|
||||
<futureMetadata name="XLDAPR" count="1">
|
||||
<bk>
|
||||
<extLst>
|
||||
<ext uri="{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}">
|
||||
<xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>
|
||||
</ext>
|
||||
</extLst>
|
||||
</bk>
|
||||
</futureMetadata>
|
||||
<cellMetadata count="1">
|
||||
<bk>
|
||||
<rc t="1" v="0"/>
|
||||
</bk>
|
||||
</cellMetadata>
|
||||
</metadata>`);
|
||||
|
||||
return o.join("");
|
||||
}
|
@ -2,10 +2,13 @@ LIBFILES=$(wildcard src/*.ts)
|
||||
TSFILES=$(wildcard *.ts)
|
||||
ENTRIES=$(subst .ts,.js,$(TSFILES))
|
||||
|
||||
BAREJS=04_base64.js 59_vba.js 64_ftab.js
|
||||
BAREJS=04_base64.js 51_xlsxmeta.js 51_xlsbmeta.js 59_vba.js 64_ftab.js
|
||||
|
||||
.PHONY: all
|
||||
all: $(ENTRIES)
|
||||
all: $(ENTRIES) 51_xlmeta.js
|
||||
|
||||
51_xlmeta.js: 51_xlsxmeta.js 51_xlsbmeta.js
|
||||
cat $^ > $@
|
||||
|
||||
$(BAREJS): %.js: %.ts $(LIBFILES)
|
||||
npx esbuild $< --outfile=$@ --platform=browser --target=es5
|
||||
|
44
modules/src/types.ts
Normal file
44
modules/src/types.ts
Normal file
@ -0,0 +1,44 @@
|
||||
declare type RawData = Uint8Array | number[];
|
||||
declare function recordhopper(data: RawData, cb:(val: any, R_n: string, RT: number)=>void): void;
|
||||
declare interface ReadableData {
|
||||
l: number;
|
||||
read_shift(t: 4): number;
|
||||
read_shift(t: any): any;
|
||||
}
|
||||
declare type ParseFunc<T> = (data: ReadableData, length: number) => T;
|
||||
declare var parse_XLWideString: ParseFunc<string>;
|
||||
|
||||
declare interface WritableData {
|
||||
l: number;
|
||||
write_shift(t: 4, val: number): void;
|
||||
write_shift(t: number, val: string|number, f?: string): any;
|
||||
}
|
||||
declare type WritableRawData = WritableData & RawData;
|
||||
interface BufArray {
|
||||
end(): RawData;
|
||||
next(sz: number): WritableData;
|
||||
push(buf: RawData): void;
|
||||
}
|
||||
declare function buf_array(): BufArray;
|
||||
declare function write_record(ba: BufArray, type: string, payload?: RawData, length?: number): void;
|
||||
declare function new_buf(sz: number): RawData & WritableData & ReadableData;
|
||||
|
||||
declare var tagregex: RegExp;
|
||||
declare var XML_HEADER: string;
|
||||
declare var RELS: any;
|
||||
declare function parsexmltag(tag: string, skip_root?: boolean, skip_LC?: boolean): object;
|
||||
declare function strip_ns(x: string): string;
|
||||
declare function write_UInt32LE(x: number, o?: WritableData): RawData;
|
||||
declare function write_XLWideString(data: string, o?: WritableData): RawData;
|
||||
declare function writeuint16(x: number): RawData;
|
||||
|
||||
|
||||
interface ParseXLMetaOptions {
|
||||
WTF?: number|boolean;
|
||||
}
|
||||
interface XLMDT {
|
||||
name: string;
|
||||
}
|
||||
interface XLMeta {
|
||||
Types: XLMDT[];
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xlsx",
|
||||
"version": "0.18.2",
|
||||
"version": "0.18.3",
|
||||
"author": "sheetjs",
|
||||
"description": "SheetJS Spreadsheet data parser and writer",
|
||||
"keywords": [
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit c8fe8a48a8cfc3c480b02f0a6df75cc2aff1932d
|
||||
Subproject commit 8203afea252b2817ec0a67e18f3b7c9503d50b97
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
@ -833,7 +833,7 @@ export interface XLSX$Utils {
|
||||
cell_add_comment(cell: CellObject, text: string, author?: string): void;
|
||||
|
||||
/** Assign an Array Formula to a range */
|
||||
sheet_set_array_formula(ws: WorkSheet, range: Range|string, formula: string): WorkSheet;
|
||||
sheet_set_array_formula(ws: WorkSheet, range: Range|string, formula: string, dynamic?: boolean): WorkSheet;
|
||||
|
||||
/** Add an array of arrays of JS data to a worksheet */
|
||||
sheet_add_aoa<T>(ws: WorkSheet, data: T[][], opts?: SheetAOAOpts): WorkSheet;
|
||||
|
311
xlsx.flow.js
311
xlsx.flow.js
@ -4,7 +4,7 @@
|
||||
/*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;
|
||||
/*:: declare var cptable:any; */
|
||||
/*global cptable:true, window */
|
||||
@ -3189,7 +3189,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 */
|
||||
@ -4144,44 +4144,6 @@ function encode_range_xls(r, opts)/*:string*/ {
|
||||
}
|
||||
return encode_cell_xls(r.s, opts.biff) + ":" + encode_cell_xls(r.e, opts.biff);
|
||||
}
|
||||
var OFFCRYPTO = {};
|
||||
|
||||
var make_offcrypto = function(O, _crypto) {
|
||||
var crypto;
|
||||
if(typeof _crypto !== 'undefined') crypto = _crypto;
|
||||
else if(typeof require !== 'undefined') {
|
||||
try { crypto = require('crypto'); }
|
||||
catch(e) { crypto = null; }
|
||||
}
|
||||
|
||||
O.rc4 = function(key, data) {
|
||||
var S = new Array(256);
|
||||
var c = 0, i = 0, j = 0, t = 0;
|
||||
for(i = 0; i != 256; ++i) S[i] = i;
|
||||
for(i = 0; i != 256; ++i) {
|
||||
j = (j + S[i] + (key[i%key.length]).charCodeAt(0))&255;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
}
|
||||
// $FlowIgnore
|
||||
i = j = 0; var out = new_raw_buf(data.length);
|
||||
for(c = 0; c != data.length; ++c) {
|
||||
i = (i + 1)&255;
|
||||
j = (j + S[i])%256;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
out[c] = (data[c] ^ S[(S[i]+S[j])&255]);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
O.md5 = function(hex) {
|
||||
if(!crypto) throw new Error("Unsupported crypto");
|
||||
return crypto.createHash('md5').update(hex).digest('hex');
|
||||
};
|
||||
};
|
||||
/*:: declare var crypto:any; */
|
||||
/*global crypto:true */
|
||||
make_offcrypto(OFFCRYPTO, typeof crypto !== "undefined" ? crypto : undefined);
|
||||
|
||||
function decode_row(rowstr/*:string*/)/*:number*/ { return parseInt(unfix_row(rowstr),10) - 1; }
|
||||
function encode_row(row/*:number*/)/*:string*/ { return "" + (row + 1); }
|
||||
function fix_row(cstr/*:string*/)/*:string*/ { return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2"); }
|
||||
@ -4351,6 +4313,9 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
|
||||
}
|
||||
function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ { return sheet_add_aoa(null, data, opts); }
|
||||
|
||||
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);
|
||||
@ -5016,8 +4981,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",
|
||||
@ -5134,6 +5099,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"
|
||||
@ -5153,7 +5122,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*/);
|
||||
}
|
||||
|
||||
@ -5252,6 +5221,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("");
|
||||
}
|
||||
@ -5318,7 +5288,9 @@ var RELS_EXTERN = [RELS.HLINK, RELS.XPATH, RELS.XMISS];
|
||||
function add_rels(rels, rId/*:number*/, f, type, relobj, targetmode/*:?string*/)/*:number*/ {
|
||||
if(!relobj) relobj = {};
|
||||
if(!rels['!id']) rels['!id'] = {};
|
||||
if(rId < 0) for(rId = 1; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
if(!rels['!idx']) rels['!idx'] = 1;
|
||||
if(rId < 0) for(rId = rels['!idx']; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
rels['!idx'] = rId + 1;
|
||||
relobj.Id = 'rId' + rId;
|
||||
relobj.Type = type;
|
||||
relobj.Target = f;
|
||||
@ -11310,6 +11282,157 @@ function update_xfext(xf, xfext) {
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
/* 18.6 Calculation Chain */
|
||||
function parse_cc_xml(data/*::, name, opts*/)/*:Array<any>*/ {
|
||||
var d = [];
|
||||
@ -12636,7 +12759,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;
|
||||
|
||||
@ -14534,6 +14657,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);
|
||||
}
|
||||
|
||||
@ -14710,6 +14834,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] = [];
|
||||
@ -15424,6 +15552,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) {
|
||||
@ -15481,6 +15611,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;
|
||||
@ -15488,12 +15619,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;
|
||||
@ -15501,11 +15637,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) {
|
||||
@ -15593,7 +15738,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' */
|
||||
@ -16800,6 +16944,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);
|
||||
}
|
||||
@ -16829,6 +16978,10 @@ 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)();
|
||||
}
|
||||
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var attregex2=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
|
||||
function xlml_parsexmltag(tag/*:string*/, skip_root/*:?boolean*/) {
|
||||
@ -19097,7 +19250,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" },
|
||||
@ -19323,7 +19476,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" },
|
||||
@ -19844,7 +19997,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" },
|
||||
@ -19884,9 +20037,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:"" }
|
||||
};
|
||||
|
||||
@ -22740,9 +22911,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) {
|
||||
@ -22978,6 +23156,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));
|
||||
@ -22986,7 +23169,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) {
|
||||
@ -23108,6 +23291,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));
|
||||
@ -23161,6 +23349,7 @@ function read_plaintext_raw(data/*:RawData*/, o/*:ParseOpts*/)/*:Workbook*/ {
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str);
|
||||
o.type = "binary";
|
||||
return read_plaintext(str, o);
|
||||
}
|
||||
|
||||
@ -23280,14 +23469,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);
|
||||
@ -23811,7 +24001,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) {
|
||||
@ -23819,7 +24009,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;
|
||||
};
|
||||
|
310
xlsx.js
generated
310
xlsx.js
generated
@ -4,7 +4,7 @@
|
||||
/*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') {
|
||||
@ -3114,7 +3114,7 @@ function write_dl(fname, payload, enc) {
|
||||
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 */
|
||||
@ -4057,43 +4057,6 @@ function encode_range_xls(r, opts) {
|
||||
}
|
||||
return encode_cell_xls(r.s, opts.biff) + ":" + encode_cell_xls(r.e, opts.biff);
|
||||
}
|
||||
var OFFCRYPTO = {};
|
||||
|
||||
var make_offcrypto = function(O, _crypto) {
|
||||
var crypto;
|
||||
if(typeof _crypto !== 'undefined') crypto = _crypto;
|
||||
else if(typeof require !== 'undefined') {
|
||||
try { crypto = require('crypto'); }
|
||||
catch(e) { crypto = null; }
|
||||
}
|
||||
|
||||
O.rc4 = function(key, data) {
|
||||
var S = new Array(256);
|
||||
var c = 0, i = 0, j = 0, t = 0;
|
||||
for(i = 0; i != 256; ++i) S[i] = i;
|
||||
for(i = 0; i != 256; ++i) {
|
||||
j = (j + S[i] + (key[i%key.length]).charCodeAt(0))&255;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
}
|
||||
// $FlowIgnore
|
||||
i = j = 0; var out = new_raw_buf(data.length);
|
||||
for(c = 0; c != data.length; ++c) {
|
||||
i = (i + 1)&255;
|
||||
j = (j + S[i])%256;
|
||||
t = S[i]; S[i] = S[j]; S[j] = t;
|
||||
out[c] = (data[c] ^ S[(S[i]+S[j])&255]);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
O.md5 = function(hex) {
|
||||
if(!crypto) throw new Error("Unsupported crypto");
|
||||
return crypto.createHash('md5').update(hex).digest('hex');
|
||||
};
|
||||
};
|
||||
/*global crypto:true */
|
||||
make_offcrypto(OFFCRYPTO, typeof crypto !== "undefined" ? crypto : undefined);
|
||||
|
||||
function decode_row(rowstr) { return parseInt(unfix_row(rowstr),10) - 1; }
|
||||
function encode_row(row) { return "" + (row + 1); }
|
||||
function fix_row(cstr) { return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2"); }
|
||||
@ -4258,6 +4221,9 @@ function sheet_add_aoa(_ws, data, opts) {
|
||||
}
|
||||
function aoa_to_sheet(data, opts) { return sheet_add_aoa(null, data, opts); }
|
||||
|
||||
function parse_Int32LE(data) {
|
||||
return data.read_shift(4, 'i');
|
||||
}
|
||||
function write_UInt32LE(x, o) {
|
||||
if (!o) o = new_buf(4);
|
||||
o.write_shift(4, x);
|
||||
@ -4923,8 +4889,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",
|
||||
@ -5041,6 +5007,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"
|
||||
@ -5060,7 +5030,7 @@ function new_ct() {
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
|
||||
rels:[], strs:[], comments:[], links:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [], drawings: [],
|
||||
calcchains:[], vba: [], drawings: [], metadata: [],
|
||||
TODO:[], xmlns: "" });
|
||||
}
|
||||
|
||||
@ -5159,6 +5129,7 @@ function write_ct(ct, opts) {
|
||||
f3('vba');
|
||||
f3('comments');
|
||||
f3('drawings');
|
||||
f2('metadata');
|
||||
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
@ -5225,7 +5196,9 @@ var RELS_EXTERN = [RELS.HLINK, RELS.XPATH, RELS.XMISS];
|
||||
function add_rels(rels, rId, f, type, relobj, targetmode) {
|
||||
if(!relobj) relobj = {};
|
||||
if(!rels['!id']) rels['!id'] = {};
|
||||
if(rId < 0) for(rId = 1; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
if(!rels['!idx']) rels['!idx'] = 1;
|
||||
if(rId < 0) for(rId = rels['!idx']; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
rels['!idx'] = rId + 1;
|
||||
relobj.Id = 'rId' + rId;
|
||||
relobj.Type = type;
|
||||
relobj.Target = f;
|
||||
@ -11209,6 +11182,157 @@ function update_xfext(xf, xfext) {
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
/* 18.6 Calculation Chain */
|
||||
function parse_cc_xml(data) {
|
||||
var d = [];
|
||||
@ -12534,7 +12658,7 @@ ixti = f[1][1]; r = f[1][2];
|
||||
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;
|
||||
|
||||
@ -14432,6 +14556,7 @@ function write_ws_xml_cell(cell, ref, ws, opts) {
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -14608,6 +14733,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
}
|
||||
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] = [];
|
||||
@ -15321,6 +15450,8 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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) {
|
||||
@ -15378,6 +15509,7 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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;
|
||||
@ -15385,12 +15517,17 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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});
|
||||
p = ({t:'z',v:void 0});
|
||||
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;
|
||||
@ -15398,11 +15535,20 @@ function parse_ws_bin(data, _opts, idx, rels, wb, 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) {
|
||||
@ -15490,7 +15636,6 @@ function parse_ws_bin(data, _opts, idx, rels, wb, themes, styles) {
|
||||
case 0x041A: /* 'BrtCFVO14' */
|
||||
case 0x0289: /* 'BrtCellIgnoreEC' */
|
||||
case 0x0451: /* 'BrtCellIgnoreEC14' */
|
||||
case 0x0031: /* 'BrtCellMeta' */
|
||||
case 0x024D: /* 'BrtCellSmartTagProperty' */
|
||||
case 0x025F: /* 'BrtCellWatch' */
|
||||
case 0x0234: /* 'BrtColor' */
|
||||
@ -16695,6 +16840,11 @@ function parse_xlink(data, rel, name, opts) {
|
||||
return parse_xlink_xml((data), rel, name, opts);
|
||||
}
|
||||
|
||||
function parse_xlmeta(data, name, opts) {
|
||||
if(name.slice(-4)===".bin") return parse_xlmeta_bin((data), name, opts);
|
||||
return parse_xlmeta_xml((data), name, opts);
|
||||
}
|
||||
|
||||
function write_wb(wb, name, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
|
||||
}
|
||||
@ -16724,6 +16874,10 @@ function write_cc(data, name:string, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
|
||||
}
|
||||
*/
|
||||
|
||||
function write_xlmeta(name) {
|
||||
return (name.slice(-4)===".bin" ? write_xlmeta_bin : write_xlmeta_xml)();
|
||||
}
|
||||
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var attregex2=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
|
||||
function xlml_parsexmltag(tag, skip_root) {
|
||||
@ -18980,7 +19134,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" },
|
||||
@ -19206,7 +19360,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" },
|
||||
@ -19727,7 +19881,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" },
|
||||
@ -19767,9 +19921,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:"" }
|
||||
};
|
||||
|
||||
@ -22622,9 +22794,16 @@ function parse_zip(zip, opts) {
|
||||
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) {
|
||||
@ -22857,6 +23036,11 @@ f = "docProps/app.xml";
|
||||
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));
|
||||
@ -22865,7 +23049,7 @@ f = "docProps/app.xml";
|
||||
return zip;
|
||||
}
|
||||
|
||||
|
||||
/* this version does not reference XLSB write functions */
|
||||
function write_zip_xlsx(wb, opts) {
|
||||
_shapeid = 1024;
|
||||
if(wb && !wb.SSF) {
|
||||
@ -22986,6 +23170,11 @@ f = "docProps/app.xml";
|
||||
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));
|
||||
@ -23039,6 +23228,7 @@ function read_plaintext_raw(data, o) {
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str);
|
||||
o.type = "binary";
|
||||
return read_plaintext(str, o);
|
||||
}
|
||||
|
||||
@ -23157,14 +23347,15 @@ function write_zip_typeXLSX(wb, opts) {
|
||||
}
|
||||
function write_zip_denouement(z, o) {
|
||||
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"}[oopts.type] || oopts.type, compression: !!o.compression}) : z.generate(oopts);
|
||||
@ -23682,7 +23873,7 @@ utils.cell_add_comment = function(cell, text, author) {
|
||||
};
|
||||
|
||||
/* set array formula and flush related cells */
|
||||
utils.sheet_set_array_formula = function(ws, range, formula) {
|
||||
utils.sheet_set_array_formula = function(ws, range, formula, dynamic) {
|
||||
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) {
|
||||
@ -23690,7 +23881,10 @@ utils.sheet_set_array_formula = function(ws, range, formula) {
|
||||
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;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
989
xlsx.mini.js
989
xlsx.mini.js
File diff suppressed because it is too large
Load Diff
273
xlsx.mjs
generated
273
xlsx.mjs
generated
@ -3,7 +3,7 @@
|
||||
/*exported XLSX */
|
||||
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
|
||||
var XLSX = {};
|
||||
XLSX.version = '0.18.2';
|
||||
XLSX.version = '0.18.3';
|
||||
var current_codepage = 1200, current_ansi = 1252;
|
||||
|
||||
var VALID_ANSI = [ 874, 932, 936, 949, 950, 10000 ];
|
||||
@ -3186,7 +3186,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 */
|
||||
@ -4310,6 +4310,9 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
|
||||
}
|
||||
function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ { return sheet_add_aoa(null, data, opts); }
|
||||
|
||||
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);
|
||||
@ -4975,8 +4978,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",
|
||||
@ -5093,6 +5096,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"
|
||||
@ -5112,7 +5119,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*/);
|
||||
}
|
||||
|
||||
@ -5211,6 +5218,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("");
|
||||
}
|
||||
@ -5277,7 +5285,9 @@ var RELS_EXTERN = [RELS.HLINK, RELS.XPATH, RELS.XMISS];
|
||||
function add_rels(rels, rId/*:number*/, f, type, relobj, targetmode/*:?string*/)/*:number*/ {
|
||||
if(!relobj) relobj = {};
|
||||
if(!rels['!id']) rels['!id'] = {};
|
||||
if(rId < 0) for(rId = 1; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
if(!rels['!idx']) rels['!idx'] = 1;
|
||||
if(rId < 0) for(rId = rels['!idx']; rels['!id']['rId' + rId]; ++rId){/* empty */}
|
||||
rels['!idx'] = rId + 1;
|
||||
relobj.Id = 'rId' + rId;
|
||||
relobj.Type = type;
|
||||
relobj.Target = f;
|
||||
@ -11269,6 +11279,157 @@ function update_xfext(xf, xfext) {
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
/* 18.6 Calculation Chain */
|
||||
function parse_cc_xml(data/*::, name, opts*/)/*:Array<any>*/ {
|
||||
var d = [];
|
||||
@ -12595,7 +12756,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;
|
||||
|
||||
@ -14493,6 +14654,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);
|
||||
}
|
||||
|
||||
@ -14669,6 +14831,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] = [];
|
||||
@ -15383,6 +15549,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) {
|
||||
@ -15440,6 +15608,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;
|
||||
@ -15447,12 +15616,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;
|
||||
@ -15460,11 +15634,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) {
|
||||
@ -15552,7 +15735,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' */
|
||||
@ -16759,6 +16941,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);
|
||||
}
|
||||
@ -16788,6 +16975,10 @@ 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)();
|
||||
}
|
||||
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var attregex2=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
|
||||
function xlml_parsexmltag(tag/*:string*/, skip_root/*:?boolean*/) {
|
||||
@ -19056,7 +19247,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" },
|
||||
@ -19282,7 +19473,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" },
|
||||
@ -19803,7 +19994,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" },
|
||||
@ -19843,9 +20034,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:"" }
|
||||
};
|
||||
|
||||
@ -22699,9 +22908,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) {
|
||||
@ -22937,6 +23153,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));
|
||||
@ -22945,7 +23166,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) {
|
||||
@ -23067,6 +23288,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));
|
||||
@ -23120,6 +23346,7 @@ function read_plaintext_raw(data/*:RawData*/, o/*:ParseOpts*/)/*:Workbook*/ {
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str);
|
||||
o.type = "binary";
|
||||
return read_plaintext(str, o);
|
||||
}
|
||||
|
||||
@ -23239,14 +23466,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);
|
||||
@ -23770,7 +23998,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) {
|
||||
@ -23778,7 +24006,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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user