BIFF3/4 write support

- more precise support for unknown BIFF type (see )
- `format_cell` render error strings
This commit is contained in:
SheetJS 2021-09-26 18:51:19 -04:00
parent 88e662af99
commit 8658054872
20 changed files with 218 additions and 154 deletions

1
.gitignore vendored

@ -4,6 +4,7 @@ package-lock.json
_book/ _book/
misc/coverage.html misc/coverage.html
misc/prof.js misc/prof.js
misc/*.[sS][vV][gG]
v8.log v8.log
tmp tmp
*.[tT][xX][tT] *.[tT][xX][tT]

@ -102,10 +102,13 @@ bytes: ## Display minified and gzipped file sizes
.PHONY: graph .PHONY: graph
graph: formats.png legend.png ## Rebuild format conversion graph graph: formats.png legend.png ## Rebuild format conversion graph
formats.png: formats.dot misc/formats.svg: misc/formats.dot
circo -Tpng -o$@ $< circo -Tsvg -o$@ $<
legend.png: misc/legend.dot misc/legend.svg: misc/legend.dot
dot -Tpng -o$@ $< dot -Tsvg -o$@ $<
formats.png legend.png: %.png: misc/%.svg
node misc/coarsify.js misc/$*.svg misc/$*.svg.svg
npx svgexport misc/$*.svg.svg $@ 0.5x
.PHONY: nexe .PHONY: nexe

111
README.md

@ -26,27 +26,26 @@ Community Translations of this README:
[**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues) [**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues)
[**File format support for known spreadsheet data formats:**](#file-formats) ![License](https://img.shields.io/github/license/SheetJS/sheetjs)
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/SheetJS/sheetjs)](https://snyk.io/test/github/SheetJS/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://npmjs.org/package/xlsx)
[![jsDelivr Downloads](https://data.jsdelivr.com/v1/package/npm/xlsx/badge)](https://www.jsdelivr.com/package/npm/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
<details> [**Browser Test and Support Matrix**](https://oss.sheetjs.com/sheetjs/tests/)
<summary><b>Graph of supported formats</b> (click to show)</summary>
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
**Supported File Formats**
![circo graph of format support](formats.png) ![circo graph of format support](formats.png)
<details><summary><b>Diagram Legend</b> (click to show)</summary>
![graph legend](legend.png) ![graph legend](legend.png)
</details> </details>
[**Browser Test**](https://oss.sheetjs.com/sheetjs/tests/)
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs)
[![Coverage Status](https://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dt/xlsx.svg)](https://npmjs.org/package/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
## Table of Contents ## Table of Contents
<details> <details>
@ -162,7 +161,7 @@ In the browser, just add a script tag:
|-----------:|:-------------------------------------------| |-----------:|:-------------------------------------------|
| `unpkg` | <https://unpkg.com/xlsx/> | | `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> | | `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <https://cdnjs.com/libraries/xlsx> | | `CDNjs` | <https://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx@latest?name=XLSX> | | `packd` | <https://bundle.run/xlsx@latest?name=XLSX> |
`unpkg` makes the latest version available at: `unpkg` makes the latest version available at:
@ -245,7 +244,11 @@ An appropriate version for each dependency is included in the dist/ directory.
The complete single-file version is generated at `dist/xlsx.full.min.js` The complete single-file version is generated at `dist/xlsx.full.min.js`
A slimmer build with XLSX / HTML support is generated at `dist/xlsx.mini.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
- node stream utils removed
Webpack and Browserify builds include optional modules by default. Webpack can Webpack and Browserify builds include optional modules by default. Webpack can
be configured to remove support with `resolve.alias`: be configured to remove support with `resolve.alias`:
@ -846,7 +849,7 @@ Parse options are described in the [Parsing Options](#parsing-options) section.
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`. `XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
In browser-based environments, it will attempt to force a client-side download. In browser-based environments, it will attempt to force a client-side download.
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`. `XLSX.writeFileAsync(wb, filename, o, cb)` attempts to write `wb` to `filename`.
If `o` is omitted, the writer will use the third argument as the callback. If `o` is omitted, the writer will use the third argument as the callback.
`XLSX.stream` contains a set of streaming write functions. `XLSX.stream` contains a set of streaming write functions.
@ -1347,10 +1350,10 @@ prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
| Storage Representation | Formats | Read | Write | | Storage Representation | Formats | Read | Write |
|:-----------------------|:-------------------------|:-----:|:-----:| |:-----------------------|:-------------------------|:-----:|:-----:|
| A1-style strings | XLSX | :o: | :o: | | A1-style strings | XLSX | ✔ | ✔ |
| RC-style strings | XLML and plain text | :o: | :o: | | RC-style strings | XLML and plain text | ✔ | ✔ |
| BIFF Parsed formulae | XLSB and all XLS formats | :o: | | | BIFF Parsed formulae | XLSB and all XLS formats | | |
| OpenFormula formulae | ODS/FODS/UOS | :o: | :o: | | OpenFormula formulae | ODS/FODS/UOS | ✔ | ✔ |
Since Excel prohibits named cells from colliding with names of A1 or RC style 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 cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
@ -1374,6 +1377,7 @@ type ColInfo = {
wch?: number; // width in characters wch?: number; // width in characters
/* other fields for preserving features from files */ /* other fields for preserving features from files */
level?: number; // 0-indexed outline / group level
MDW?: number; // Excel's "Max Digit Width" unit, always integral MDW?: number; // Excel's "Max Digit Width" unit, always integral
}; };
``` ```
@ -1852,6 +1856,8 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format | | `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format | | `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format | | `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | none | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | none | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format | | `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) | | `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet | | `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
@ -1990,14 +1996,19 @@ XLSX.utils.sheet_add_aoa(ws, [[4,5,6,7,8,9,0]], {origin: -1});
`XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet `XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet
with automatically-generated "headers" based on the keys of the objects. The with automatically-generated "headers" based on the keys of the objects. The
default column order is determined by the first appearance of the field using default column order is determined by the first appearance of the field using
`Object.keys`, but can be overridden using the options argument: `Object.keys`. The function accepts an options argument:
| Option Name | Default | Description | | Option Name | Default | Description |
| :---------- | :------: | :-------------------------------------------------- | | :---------- | :-----: | :--------------------------------------------------- |
|`header` | | Use specified column order (default `Object.keys`) | |`header` | | Use specified field order (default `Object.keys`) ** |
|`dateNF` | FMT 14 | Use specified date format in string output | |`dateNF` | FMT 14 | Use specified date format in string output |
|`cellDates` | false | Store dates as type `d` (default is `n`) | |`cellDates` | false | Store dates as type `d` (default is `n`) |
|`skipHeader` | false | If true, do not include header row in output | |`skipHeader` | false | If true, do not include header row in output |
- All fields from each row will be written. If `header` is an array and it does
not contain a particular field, the key will be appended to the array.
- Cell types are deduced from the type of each value. For example, a `Date`
object will generate a Date cell, while a string will generate a Text cell.
<details> <details>
<summary><b>Examples</b> (click to show)</summary> <summary><b>Examples</b> (click to show)</summary>
@ -2368,31 +2379,31 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Format | Read | Write | | Format | Read | Write |
|:-------------------------------------------------------------|:-----:|:-----:| |:-------------------------------------------------------------|:-----:|:-----:|
| **Excel Worksheet/Workbook Formats** |:-----:|:-----:| | **Excel Worksheet/Workbook Formats** |:-----:|:-----:|
| Excel 2007+ XML Formats (XLSX/XLSM) | :o: | :o: | | Excel 2007+ XML Formats (XLSX/XLSM) | ✔ | ✔ |
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: | | Excel 2007+ Binary Format (XLSB BIFF12) | ✔ | ✔ |
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | ✔ | ✔ |
| Excel 97-2004 (XLS BIFF8) | :o: | :o: | | Excel 97-2004 (XLS BIFF8) | ✔ | ✔ |
| Excel 5.0/95 (XLS BIFF5) | :o: | :o: | | Excel 5.0/95 (XLS BIFF5) | ✔ | ✔ |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | | | Excel 4.0 (XLS/XLW BIFF4) | ✔ | ✔ |
| Excel 3.0 (XLS BIFF3) | :o: | | | Excel 3.0 (XLS BIFF3) | ✔ | ✔ |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: | | Excel 2.0/2.1 (XLS BIFF2) | ✔ | ✔ |
| **Excel Supported Text Formats** |:-----:|:-----:| | **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TXT) | :o: | :o: | | Delimiter-Separated Values (CSV/TXT) | ✔ | ✔ |
| Data Interchange Format (DIF) | :o: | :o: | | Data Interchange Format (DIF) | ✔ | ✔ |
| Symbolic Link (SYLK/SLK) | :o: | :o: | | Symbolic Link (SYLK/SLK) | ✔ | ✔ |
| Lotus Formatted Text (PRN) | :o: | :o: | | Lotus Formatted Text (PRN) | ✔ | ✔ |
| UTF-16 Unicode Text (TXT) | :o: | :o: | | UTF-16 Unicode Text (TXT) | ✔ | ✔ |
| **Other Workbook/Worksheet Formats** |:-----:|:-----:| | **Other Workbook/Worksheet Formats** |:-----:|:-----:|
| OpenDocument Spreadsheet (ODS) | :o: | :o: | | OpenDocument Spreadsheet (ODS) | ✔ | ✔ |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: | | Flat XML ODF Spreadsheet (FODS) | ✔ | ✔ |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | | | Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | :o: | | dBASE II/III/IV / Visual FoxPro (DBF) | ✔ | ✔ |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | :o: | | | Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | | | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: | | HTML Tables | ✔ | ✔ |
| Rich Text Format tables (RTF) | | :o: | | Rich Text Format tables (RTF) | | |
| Ethercalc Record Format (ETH) | :o: | :o: | | Ethercalc Record Format (ETH) | ✔ | ✔ |
Features not supported by a given file format will not be written. Formats with Features not supported by a given file format will not be written. Formats with
range limits will be silently truncated: range limits will be silently truncated:
@ -2403,6 +2414,8 @@ range limits will be silently truncated:
| Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 | | Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 |
| Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 | | Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 |
| Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 | | Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 |
| Excel 4.0 (XLS BIFF4) | IV16384 | 256 | 16384 |
| Excel 3.0 (XLS BIFF3) | IV16384 | 256 | 16384 |
| Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 | | Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 |
Excel 2003 SpreadsheetML range limits are governed by the version of Excel and Excel 2003 SpreadsheetML range limits are governed by the version of Excel and

@ -24,8 +24,8 @@ program
.option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods') .option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods')
.option('-8, --xls', 'emit XLS to <sheetname> or <file>.xls (BIFF8)') .option('-8, --xls', 'emit XLS to <sheetname> or <file>.xls (BIFF8)')
.option('-5, --biff5','emit XLS to <sheetname> or <file>.xls (BIFF5)') .option('-5, --biff5','emit XLS to <sheetname> or <file>.xls (BIFF5)')
//.option('-4, --biff4','emit XLS to <sheetname> or <file>.xls (BIFF4)') .option('-4, --biff4','emit XLS to <sheetname> or <file>.xls (BIFF4)')
//.option('-3, --biff3','emit XLS to <sheetname> or <file>.xls (BIFF3)') .option('-3, --biff3','emit XLS to <sheetname> or <file>.xls (BIFF3)')
.option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)') .option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)')
.option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla') .option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)') .option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
@ -256,7 +256,7 @@ switch(true) {
default: default:
if(!program.book) { if(!program.book) {
var stream = X.stream.to_csv(ws, {FS:program.fieldSep, RS:program.rowSep}); var stream = X.stream.to_csv(ws, {FS:program.fieldSep||",", RS:program.rowSep||"\n"});
if(program.output) stream.pipe(fs.createWriteStream(program.output)); if(program.output) stream.pipe(fs.createWriteStream(program.output));
else stream.pipe(process.stdout); else stream.pipe(process.stdout);
} else doit(function(ws) { return X.utils.sheet_to_csv(ws,{FS:program.fieldSep, RS:program.rowSep}); }); } else doit(function(ws) { return X.utils.sheet_to_csv(ws,{FS:program.fieldSep, RS:program.rowSep}); });

@ -87,6 +87,7 @@ function format_cell(cell/*:Cell*/, v/*:any*/, o/*:any*/) {
if(cell == null || cell.t == null || cell.t == 'z') return ""; if(cell == null || cell.t == null || cell.t == 'z') return "";
if(cell.w !== undefined) return cell.w; if(cell.w !== undefined) return cell.w;
if(cell.t == 'd' && !cell.z && o && o.dateNF) cell.z = o.dateNF; if(cell.t == 'd' && !cell.z && o && o.dateNF) cell.z = o.dateNF;
if(cell.t == "e") return BErr[cell.v] || cell.v;
if(v == undefined) return safe_format_cell(cell, cell.v); if(v == undefined) return safe_format_cell(cell, cell.v);
return safe_format_cell(cell, v); return safe_format_cell(cell, v);
} }

@ -403,7 +403,7 @@ function parse_Bes(blob/*::, length*/) {
} }
function write_Bes(v, t/*:string*/, o) { function write_Bes(v, t/*:string*/, o) {
if(!o) o = new_buf(2); if(!o) o = new_buf(2);
o.write_shift(1, +v); o.write_shift(1, ((t == 'e') ? +v : +!!v));
o.write_shift(1, ((t == 'e') ? 1 : 0)); o.write_shift(1, ((t == 'e') ? 1 : 0));
return o; return o;
} }

@ -422,6 +422,7 @@ function write_LabelSst(R/*:number*/, C/*:number*/, v/*:number*/, os/*:number*/
/* [MS-XLS] 2.4.148 */ /* [MS-XLS] 2.4.148 */
function parse_Label(blob, length, opts) { function parse_Label(blob, length, opts) {
if(opts.biffguess && opts.biff == 2) opts.biff = 5;
var target = blob.l + length; var target = blob.l + length;
var cell = parse_XLSCell(blob, 6); var cell = parse_XLSCell(blob, 6);
if(opts.biff == 2) blob.l++; if(opts.biff == 2) blob.l++;
@ -573,7 +574,9 @@ function write_XF(data, ixfeP, opts, o) {
o.write_shift(2, (data.numFmtId||0)); o.write_shift(2, (data.numFmtId||0));
o.write_shift(2, (ixfeP<<4)); o.write_shift(2, (ixfeP<<4));
} }
o.write_shift(4, 0); var f = 0;
if(data.numFmtId > 0 && b5) f |= 0x0400;
o.write_shift(4, f);
o.write_shift(4, 0); o.write_shift(4, 0);
if(!b5) o.write_shift(4, 0); if(!b5) o.write_shift(4, 0);
o.write_shift(2, 0); o.write_shift(2, 0);
@ -600,7 +603,7 @@ function write_Guts(guts/*:Array<number>*/) {
/* [MS-XLS] 2.4.24 */ /* [MS-XLS] 2.4.24 */
function parse_BoolErr(blob, length, opts) { function parse_BoolErr(blob, length, opts) {
var cell = parse_XLSCell(blob, 6); var cell = parse_XLSCell(blob, 6);
if(opts.biff == 2) ++blob.l; if(opts.biff == 2 || length == 9) ++blob.l;
var val = parse_Bes(blob, 2); var val = parse_Bes(blob, 2);
cell.val = val; cell.val = val;
cell.t = (val === true || val === false) ? 'b' : 'e'; cell.t = (val === true || val === false) ? 'b' : 'e';
@ -614,7 +617,8 @@ function write_BoolErr(R/*:number*/, C/*:number*/, v, os/*:number*/, opts, t/*:s
} }
/* [MS-XLS] 2.4.180 Number */ /* [MS-XLS] 2.4.180 Number */
function parse_Number(blob) { function parse_Number(blob, length, opts) {
if(opts.biffguess && opts.biff == 2) opts.biff = 5;
var cell = parse_XLSCell(blob, 6); var cell = parse_XLSCell(blob, 6);
var xnum = parse_Xnum(blob, 8); var xnum = parse_Xnum(blob, 8);
cell.val = xnum; cell.val = xnum;
@ -1025,6 +1029,7 @@ function parse_ImData(blob) {
/* BIFF2_??? where ??? is the name from [XLS] */ /* BIFF2_??? where ??? is the name from [XLS] */
function parse_BIFF2STR(blob, length, opts) { function parse_BIFF2STR(blob, length, opts) {
if(opts.biffguess && opts.biff == 5) opts.biff = 2;
var cell = parse_XLSCell(blob, 6); var cell = parse_XLSCell(blob, 6);
++blob.l; ++blob.l;
var str = parse_XLUnicodeString2(blob, length-7, opts); var str = parse_XLUnicodeString2(blob, length-7, opts);

@ -935,8 +935,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
if(supbooks.sharedf[encode_cell(c)]) { if(supbooks.sharedf[encode_cell(c)]) {
var parsedf = (supbooks.sharedf[encode_cell(c)]); var parsedf = (supbooks.sharedf[encode_cell(c)]);
stack.push(stringify_formula(parsedf, _range, q, supbooks, opts)); stack.push(stringify_formula(parsedf, _range, q, supbooks, opts));
} } else {
else {
var fnd = false; var fnd = false;
for(e1=0;e1!=supbooks.arrayf.length; ++e1) { for(e1=0;e1!=supbooks.arrayf.length; ++e1) {
/* TODO: should be something like range_has */ /* TODO: should be something like range_has */

@ -217,7 +217,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(R.r === 2 || R.r == 12) { if(R.r === 2 || R.r == 12) {
var rt = blob.read_shift(2); length -= 2; var rt = blob.read_shift(2); length -= 2;
if(!opts.enc && rt !== RecordType && (((rt&0xFF)<<8)|(rt>>8)) !== RecordType) throw new Error("rt mismatch: " + rt + "!=" + RecordType); if(!opts.enc && rt !== RecordType && (((rt&0xFF)<<8)|(rt>>8)) !== RecordType) throw new Error("rt mismatch: " + rt + "!=" + RecordType);
if(R.r == 12){ blob.l += 10; length -= 10; } // skip FRT if(R.r == 12){
blob.l += 10; length -= 10;
} // skip FRT
} }
//console.error(R,blob.l,length,blob.length); //console.error(R,blob.l,length,blob.length);
var val/*:any*/ = ({}/*:any*/); var val/*:any*/ = ({}/*:any*/);
@ -359,13 +361,16 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/*::[*/0x0002/*::]*/:2, /*::[*/0x0002/*::]*/:2,
/*::[*/0x0007/*::]*/:2 /*::[*/0x0007/*::]*/:2
}[val.BIFFVer] || 8; }[val.BIFFVer] || 8;
opts.biffguess = val.BIFFVer == 0;
if(val.BIFFVer == 0 && val.dt == 0x1000) { opts.biff = 5; seen_codepage = true; set_cp(opts.codepage = 28591); }
if(opts.biff == 8 && val.BIFFVer == 0 && val.dt == 16) opts.biff = 2; if(opts.biff == 8 && val.BIFFVer == 0 && val.dt == 16) opts.biff = 2;
if(file_depth++) break; if(file_depth++) break;
cell_valid = true; cell_valid = true;
out = ((options.dense ? [] : {})/*:any*/); out = ((options.dense ? [] : {})/*:any*/);
if(opts.biff < 8 && !seen_codepage) { seen_codepage = true; set_cp(opts.codepage = options.codepage || 1252); } if(opts.biff < 8 && !seen_codepage) { seen_codepage = true; set_cp(opts.codepage = options.codepage || 1252); }
if(opts.biff < 5) {
if(opts.biff < 5 || val.BIFFVer == 0 && val.dt == 0x1000) {
if(cur_sheet === "") cur_sheet = "Sheet1"; if(cur_sheet === "") cur_sheet = "Sheet1";
range = {s:{r:0,c:0},e:{r:0,c:0}}; range = {s:{r:0,c:0},e:{r:0,c:0}};
/* fake BoundSheet8 */ /* fake BoundSheet8 */
@ -388,19 +393,19 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': { case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
if(out["!type"] == "chart") if(options.dense ? (out[val.r]||[])[val.c]: out[encode_cell({c:val.c, r:val.r})]) ++val.c; if(out["!type"] == "chart") if(options.dense ? (out[val.r]||[])[val.c]: out[encode_cell({c:val.c, r:val.r})]) ++val.c;
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe]||{}, v:val.val, t:'n'}/*:any*/); temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe]||{}, v:val.val, t:'n'}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
} break; } break;
case 'BoolErr': { case 'BoolErr': {
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:val.t}/*:any*/); temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:val.t}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
} break; } break;
case 'RK': { case 'RK': {
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'}/*:any*/); temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
} break; } break;
@ -408,7 +413,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
for(var j = val.c; j <= val.C; ++j) { for(var j = val.c; j <= val.C; ++j) {
var ixfe = val.rkrec[j-val.c][0]; var ixfe = val.rkrec[j-val.c][0];
temp_val= ({ixfe:ixfe, XF:XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'}/*:any*/); temp_val= ({ixfe:ixfe, XF:XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:j, r:val.r}, temp_val, options); addcell({c:j, r:val.r}, temp_val, options);
} }
@ -426,7 +431,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
else temp_val.F = ((options.dense ? (out[_fr]||[])[_fc]: out[_fe]) || {}).F; else temp_val.F = ((options.dense ? (out[_fr]||[])[_fc]: out[_fe]) || {}).F;
} else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts); } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
} }
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(val.cell, temp_val, options); addcell(val.cell, temp_val, options);
last_formula = val; last_formula = val;
@ -439,7 +444,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(options.cellFormula) { if(options.cellFormula) {
temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts); temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
} }
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options); addcell(last_formula.cell, temp_val, options);
last_formula = null; last_formula = null;
@ -471,13 +476,13 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
temp_val=make_cell(sst[val.isst].t, val.ixfe, 's'); temp_val=make_cell(sst[val.isst].t, val.ixfe, 's');
if(sst[val.isst].h) temp_val.h = sst[val.isst].h; if(sst[val.isst].h) temp_val.h = sst[val.isst].h;
temp_val.XF = XFs[temp_val.ixfe]; temp_val.XF = XFs[temp_val.ixfe];
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
break; break;
case 'Blank': if(options.sheetStubs) { case 'Blank': if(options.sheetStubs) {
temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], t:'z'}/*:any*/); temp_val = ({ixfe: val.ixfe, XF: XFs[val.ixfe], t:'z'}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
} break; } break;
@ -485,7 +490,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
for(var _j = val.c; _j <= val.C; ++_j) { for(var _j = val.c; _j <= val.C; ++_j) {
var _ixfe = val.ixfe[_j-val.c]; var _ixfe = val.ixfe[_j-val.c];
temp_val= ({ixfe:_ixfe, XF:XFs[_ixfe], t:'z'}/*:any*/); temp_val= ({ixfe:_ixfe, XF:XFs[_ixfe], t:'z'}/*:any*/);
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:_j, r:val.r}, temp_val, options); addcell({c:_j, r:val.r}, temp_val, options);
} }
@ -494,7 +499,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'Label': case 'BIFF2STR': case 'Label': case 'BIFF2STR':
temp_val=make_cell(val.val, val.ixfe, 's'); temp_val=make_cell(val.val, val.ixfe, 's');
temp_val.XF = XFs[temp_val.ixfe]; temp_val.XF = XFs[temp_val.ixfe];
if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x1F]; if(BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe>>8) & 0x3F];
safe_format_xf(temp_val, options, wb.opts.Date1904); safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options); addcell({c:val.c, r:val.r}, temp_val, options);
break; break;
@ -832,6 +837,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/* BIFF2-4 records */ /* BIFF2-4 records */
case 'BIFF2FONTCLR': case 'BIFF2FMTCNT': case 'BIFF2FONTXTRA': break; case 'BIFF2FONTCLR': case 'BIFF2FMTCNT': case 'BIFF2FONTXTRA': break;
case 'BIFF2XF': case 'BIFF3XF': case 'BIFF4XF': break; case 'BIFF2XF': case 'BIFF3XF': case 'BIFF4XF': break;
case 'BIFF2XFINDEX': break;
case 'BIFF4FMTCNT': case 'BIFF2ROW': case 'BIFF2WINDOW2': break; case 'BIFF4FMTCNT': case 'BIFF2ROW': case 'BIFF2WINDOW2': break;
/* Miscellaneous */ /* Miscellaneous */

@ -1222,6 +1222,7 @@ var XLSRecordEnum = {
/*::[*/0x0034/*::]*/: { n:"DDEObjName" }, /*::[*/0x0034/*::]*/: { n:"DDEObjName" },
/*::[*/0x003e/*::]*/: { n:"BIFF2WINDOW2" }, /*::[*/0x003e/*::]*/: { n:"BIFF2WINDOW2" },
/*::[*/0x0043/*::]*/: { n:"BIFF2XF" }, /*::[*/0x0043/*::]*/: { n:"BIFF2XF" },
/*::[*/0x0044/*::]*/: { n:"BIFF2XFINDEX", f:parseuint16 },
/*::[*/0x0045/*::]*/: { n:"BIFF2FONTCLR" }, /*::[*/0x0045/*::]*/: { n:"BIFF2FONTCLR" },
/*::[*/0x0056/*::]*/: { n:"BIFF4FMTCNT" }, /* 16-bit cnt, similar to BIFF2 */ /*::[*/0x0056/*::]*/: { n:"BIFF4FMTCNT" }, /* 16-bit cnt, similar to BIFF2 */
/*::[*/0x007e/*::]*/: { n:"RK" }, /* Not necessarily same as 0x027e */ /*::[*/0x007e/*::]*/: { n:"RK" }, /* Not necessarily same as 0x027e */

@ -43,8 +43,7 @@ function write_BIFF2Cell(out, r/*:number*/, c/*:number*/) {
function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:?string*/) { function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:?string*/) {
var out = new_buf(9); var out = new_buf(9);
write_BIFF2Cell(out, r, c); write_BIFF2Cell(out, r, c);
if(t == 'e') { out.write_shift(1, val); out.write_shift(1, 1); } write_Bes(val, t || 'b', out);
else { out.write_shift(1, val?1:0); out.write_shift(1, 0); }
return out; return out;
} }
@ -105,7 +104,7 @@ function write_biff2_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
var idx = 0; var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i; for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet); if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
write_biff_rec(ba, 0x0009, write_BOF(wb, 0x10, o)); write_biff_rec(ba, (o.biff == 4 ? 0x0409 : (o.biff == 3 ? 0x0209 : 0x0009)), write_BOF(wb, 0x10, o));
/* ... */ /* ... */
write_ws_biff2(ba, wb.Sheets[wb.SheetNames[idx]], idx, o, wb); write_ws_biff2(ba, wb.Sheets[wb.SheetNames[idx]], idx, o, wb);
/* ... */ /* ... */
@ -195,7 +194,7 @@ function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:n
if(opts.bookSST) { if(opts.bookSST) {
var isst = get_sst_id(opts.Strings, cell.v, opts.revStrings); var isst = get_sst_id(opts.Strings, cell.v, opts.revStrings);
write_biff_rec(ba, "LabelSst", write_LabelSst(R, C, isst, os, opts)); write_biff_rec(ba, "LabelSst", write_LabelSst(R, C, isst, os, opts));
} else write_biff_rec(ba, "Label", write_Label(R, C, cell.v, os, opts)); } else write_biff_rec(ba, "Label", write_Label(R, C, (cell.v||"").slice(0,255), os, opts));
break; break;
default: default:
write_biff_rec(ba, "Blank", write_XLSCell(R, C, os)); write_biff_rec(ba, "Blank", write_XLSCell(R, C, os));

@ -26,24 +26,23 @@ Community Translations of this README:
[**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues) [**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues)
[**File format support for known spreadsheet data formats:**](#file-formats) ![License](https://img.shields.io/github/license/SheetJS/sheetjs)
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/SheetJS/sheetjs)](https://snyk.io/test/github/SheetJS/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://npmjs.org/package/xlsx)
[![jsDelivr Downloads](https://data.jsdelivr.com/v1/package/npm/xlsx/badge)](https://www.jsdelivr.com/package/npm/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
<details> [**Browser Test and Support Matrix**](https://oss.sheetjs.com/sheetjs/tests/)
<summary><b>Graph of supported formats</b> (click to show)</summary>
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
**Supported File Formats**
![circo graph of format support](formats.png) ![circo graph of format support](formats.png)
<details><summary><b>Diagram Legend</b> (click to show)</summary>
![graph legend](legend.png) ![graph legend](legend.png)
</details> </details>
[**Browser Test**](https://oss.sheetjs.com/sheetjs/tests/)
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs)
[![Coverage Status](https://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dt/xlsx.svg)](https://npmjs.org/package/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)

@ -41,6 +41,8 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format | | `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format | | `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format | | `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | none | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | none | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format | | `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) | | `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet | | `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |

@ -10,8 +10,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | ✔ | ✔ | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | ✔ | ✔ |
| Excel 97-2004 (XLS BIFF8) | ✔ | ✔ | | Excel 97-2004 (XLS BIFF8) | ✔ | ✔ |
| Excel 5.0/95 (XLS BIFF5) | ✔ | ✔ | | Excel 5.0/95 (XLS BIFF5) | ✔ | ✔ |
| Excel 4.0 (XLS/XLW BIFF4) | ✔ | | | Excel 4.0 (XLS/XLW BIFF4) | ✔ | |
| Excel 3.0 (XLS BIFF3) | ✔ | | | Excel 3.0 (XLS BIFF3) | ✔ | |
| Excel 2.0/2.1 (XLS BIFF2) | ✔ | ✔ | | Excel 2.0/2.1 (XLS BIFF2) | ✔ | ✔ |
| **Excel Supported Text Formats** |:-----:|:-----:| | **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TXT) | ✔ | ✔ | | Delimiter-Separated Values (CSV/TXT) | ✔ | ✔ |
@ -40,6 +40,8 @@ range limits will be silently truncated:
| Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 | | Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 |
| Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 | | Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 |
| Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 | | Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 |
| Excel 4.0 (XLS BIFF4) | IV16384 | 256 | 16384 |
| Excel 3.0 (XLS BIFF3) | IV16384 | 256 | 16384 |
| Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 | | Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 |
Excel 2003 SpreadsheetML range limits are governed by the version of Excel and Excel 2003 SpreadsheetML range limits are governed by the version of Excel and

Binary file not shown.

Before

(image error) Size: 196 KiB

After

(image error) Size: 439 KiB

Binary file not shown.

Before

(image error) Size: 45 KiB

After

(image error) Size: 111 KiB

9
misc/coarsify.js Normal file

@ -0,0 +1,9 @@
/* based on the `coarse` project README */
const fs = require('fs');
const coarse = require('coarse');
const svg = fs.readFileSync(process.argv[2]);
const roughened = coarse(svg);
fs.writeFileSync(process.argv[3], roughened);

@ -26,23 +26,23 @@ Community Translations of this README:
[**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues) [**Issues and Bug Reports**](https://github.com/sheetjs/sheetjs/issues)
[**File format support for known spreadsheet data formats:**](#file-formats) ![License](https://img.shields.io/github/license/SheetJS/sheetjs)
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/SheetJS/sheetjs)](https://snyk.io/test/github/SheetJS/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://npmjs.org/package/xlsx)
[![jsDelivr Downloads](https://data.jsdelivr.com/v1/package/npm/xlsx/badge)](https://www.jsdelivr.com/package/npm/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
[**Browser Test and Support Matrix**](https://oss.sheetjs.com/sheetjs/tests/)
![circo graph of format support](formats.png)
![graph legend](legend.png)
[**Browser Test**](https://oss.sheetjs.com/sheetjs/tests/)
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs) [![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs) **Supported File Formats**
[![Coverage Status](https://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs) ![circo graph of format support](formats.png)
[![npm Downloads](https://img.shields.io/npm/dt/xlsx.svg)](https://npmjs.org/package/xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
![graph legend](legend.png)
## Table of Contents ## Table of Contents
@ -154,7 +154,7 @@ In the browser, just add a script tag:
|-----------:|:-------------------------------------------| |-----------:|:-------------------------------------------|
| `unpkg` | <https://unpkg.com/xlsx/> | | `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> | | `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <https://cdnjs.com/libraries/xlsx> | | `CDNjs` | <https://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx@latest?name=XLSX> | | `packd` | <https://bundle.run/xlsx@latest?name=XLSX> |
`unpkg` makes the latest version available at: `unpkg` makes the latest version available at:
@ -234,7 +234,11 @@ An appropriate version for each dependency is included in the dist/ directory.
The complete single-file version is generated at `dist/xlsx.full.min.js` The complete single-file version is generated at `dist/xlsx.full.min.js`
A slimmer build with XLSX / HTML support is generated at `dist/xlsx.mini.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
- node stream utils removed
Webpack and Browserify builds include optional modules by default. Webpack can Webpack and Browserify builds include optional modules by default. Webpack can
be configured to remove support with `resolve.alias`: be configured to remove support with `resolve.alias`:
@ -768,7 +772,7 @@ Parse options are described in the [Parsing Options](#parsing-options) section.
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`. `XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
In browser-based environments, it will attempt to force a client-side download. In browser-based environments, it will attempt to force a client-side download.
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`. `XLSX.writeFileAsync(wb, filename, o, cb)` attempts to write `wb` to `filename`.
If `o` is omitted, the writer will use the third argument as the callback. If `o` is omitted, the writer will use the third argument as the callback.
`XLSX.stream` contains a set of streaming write functions. `XLSX.stream` contains a set of streaming write functions.
@ -1231,10 +1235,10 @@ prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
| Storage Representation | Formats | Read | Write | | Storage Representation | Formats | Read | Write |
|:-----------------------|:-------------------------|:-----:|:-----:| |:-----------------------|:-------------------------|:-----:|:-----:|
| A1-style strings | XLSX | :o: | :o: | | A1-style strings | XLSX | ✔ | ✔ |
| RC-style strings | XLML and plain text | :o: | :o: | | RC-style strings | XLML and plain text | ✔ | ✔ |
| BIFF Parsed formulae | XLSB and all XLS formats | :o: | | | BIFF Parsed formulae | XLSB and all XLS formats | | |
| OpenFormula formulae | ODS/FODS/UOS | :o: | :o: | | OpenFormula formulae | ODS/FODS/UOS | ✔ | ✔ |
Since Excel prohibits named cells from colliding with names of A1 or RC style 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 cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
@ -1257,6 +1261,7 @@ type ColInfo = {
wch?: number; // width in characters wch?: number; // width in characters
/* other fields for preserving features from files */ /* other fields for preserving features from files */
level?: number; // 0-indexed outline / group level
MDW?: number; // Excel's "Max Digit Width" unit, always integral MDW?: number; // Excel's "Max Digit Width" unit, always integral
}; };
``` ```
@ -1702,6 +1707,8 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format | | `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format | | `biff8` | `.xls` | CFB | multi | Excel 97-2004 Workbook Format |
| `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format | | `biff5` | `.xls` | CFB | multi | Excel 5.0/95 Workbook Format |
| `biff4` | `.xls` | none | single | Excel 4.0 Worksheet Format |
| `biff3` | `.xls` | none | single | Excel 3.0 Worksheet Format |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format | | `biff2` | `.xls` | none | single | Excel 2.0 Worksheet Format |
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) | | `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet | | `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
@ -1834,14 +1841,19 @@ XLSX.utils.sheet_add_aoa(ws, [[4,5,6,7,8,9,0]], {origin: -1});
`XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet `XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet
with automatically-generated "headers" based on the keys of the objects. The with automatically-generated "headers" based on the keys of the objects. The
default column order is determined by the first appearance of the field using default column order is determined by the first appearance of the field using
`Object.keys`, but can be overridden using the options argument: `Object.keys`. The function accepts an options argument:
| Option Name | Default | Description | | Option Name | Default | Description |
| :---------- | :------: | :-------------------------------------------------- | | :---------- | :-----: | :--------------------------------------------------- |
|`header` | | Use specified column order (default `Object.keys`) | |`header` | | Use specified field order (default `Object.keys`) ** |
|`dateNF` | FMT 14 | Use specified date format in string output | |`dateNF` | FMT 14 | Use specified date format in string output |
|`cellDates` | false | Store dates as type `d` (default is `n`) | |`cellDates` | false | Store dates as type `d` (default is `n`) |
|`skipHeader` | false | If true, do not include header row in output | |`skipHeader` | false | If true, do not include header row in output |
- All fields from each row will be written. If `header` is an array and it does
not contain a particular field, the key will be appended to the array.
- Cell types are deduced from the type of each value. For example, a `Date`
object will generate a Date cell, while a string will generate a Text cell.
The original sheet cannot be reproduced using plain objects since JS object keys The original sheet cannot be reproduced using plain objects since JS object keys
@ -2188,31 +2200,31 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Format | Read | Write | | Format | Read | Write |
|:-------------------------------------------------------------|:-----:|:-----:| |:-------------------------------------------------------------|:-----:|:-----:|
| **Excel Worksheet/Workbook Formats** |:-----:|:-----:| | **Excel Worksheet/Workbook Formats** |:-----:|:-----:|
| Excel 2007+ XML Formats (XLSX/XLSM) | :o: | :o: | | Excel 2007+ XML Formats (XLSX/XLSM) | ✔ | ✔ |
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: | | Excel 2007+ Binary Format (XLSB BIFF12) | ✔ | ✔ |
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | ✔ | ✔ |
| Excel 97-2004 (XLS BIFF8) | :o: | :o: | | Excel 97-2004 (XLS BIFF8) | ✔ | ✔ |
| Excel 5.0/95 (XLS BIFF5) | :o: | :o: | | Excel 5.0/95 (XLS BIFF5) | ✔ | ✔ |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | | | Excel 4.0 (XLS/XLW BIFF4) | ✔ | ✔ |
| Excel 3.0 (XLS BIFF3) | :o: | | | Excel 3.0 (XLS BIFF3) | ✔ | ✔ |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: | | Excel 2.0/2.1 (XLS BIFF2) | ✔ | ✔ |
| **Excel Supported Text Formats** |:-----:|:-----:| | **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TXT) | :o: | :o: | | Delimiter-Separated Values (CSV/TXT) | ✔ | ✔ |
| Data Interchange Format (DIF) | :o: | :o: | | Data Interchange Format (DIF) | ✔ | ✔ |
| Symbolic Link (SYLK/SLK) | :o: | :o: | | Symbolic Link (SYLK/SLK) | ✔ | ✔ |
| Lotus Formatted Text (PRN) | :o: | :o: | | Lotus Formatted Text (PRN) | ✔ | ✔ |
| UTF-16 Unicode Text (TXT) | :o: | :o: | | UTF-16 Unicode Text (TXT) | ✔ | ✔ |
| **Other Workbook/Worksheet Formats** |:-----:|:-----:| | **Other Workbook/Worksheet Formats** |:-----:|:-----:|
| OpenDocument Spreadsheet (ODS) | :o: | :o: | | OpenDocument Spreadsheet (ODS) | ✔ | ✔ |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: | | Flat XML ODF Spreadsheet (FODS) | ✔ | ✔ |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | | | Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | :o: | | dBASE II/III/IV / Visual FoxPro (DBF) | ✔ | ✔ |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | :o: | | | Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | | | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: | | HTML Tables | ✔ | ✔ |
| Rich Text Format tables (RTF) | | :o: | | Rich Text Format tables (RTF) | | |
| Ethercalc Record Format (ETH) | :o: | :o: | | Ethercalc Record Format (ETH) | ✔ | ✔ |
Features not supported by a given file format will not be written. Formats with Features not supported by a given file format will not be written. Formats with
range limits will be silently truncated: range limits will be silently truncated:
@ -2223,6 +2235,8 @@ range limits will be silently truncated:
| Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 | | Excel 2007+ Binary Format (XLSB BIFF12) | XFD1048576 | 16384 | 1048576 |
| Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 | | Excel 97-2004 (XLS BIFF8) | IV65536 | 256 | 65536 |
| Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 | | Excel 5.0/95 (XLS BIFF5) | IV16384 | 256 | 16384 |
| Excel 4.0 (XLS BIFF4) | IV16384 | 256 | 16384 |
| Excel 3.0 (XLS BIFF3) | IV16384 | 256 | 16384 |
| Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 | | Excel 2.0/2.1 (XLS BIFF2) | IV16384 | 256 | 16384 |
Excel 2003 SpreadsheetML range limits are governed by the version of Excel and Excel 2003 SpreadsheetML range limits are governed by the version of Excel and

@ -1,8 +1,9 @@
digraph G { digraph G {
graph [mindist=0.1]; graph [mindist=0.1];
node [fontname="Indie Flower"];
csf [shape=doublecircle,label="Common\nSpreadsheet\nFormat\n(JS Object)"]; csf [shape=doublecircle,label="Common\nSpreadsheet\nFormat\n(JS Object)"];
subgraph XL { subgraph XL {
node [style=filled,color=green]; node [style=filled,color="#00FF00"];
xlsx [label="XLSX\nXLSM"]; xlsx [label="XLSX\nXLSM"];
xlsb [label="XLSB\nBIFF12"]; xlsb [label="XLSB\nBIFF12"];
xlml [label="SSML\n(2003/4)"]; xlml [label="SSML\n(2003/4)"];
@ -58,7 +59,9 @@ digraph G {
xls2 -> csf xls2 -> csf
csf -> xls2 csf -> xls2
xls3 -> csf xls3 -> csf
csf -> xls3
xls4 -> csf xls4 -> csf
csf -> xls4
csf -> slk csf -> slk
slk -> csf slk -> csf
csf -> dif csf -> dif

@ -1,38 +1,45 @@
digraph G { digraph G {
graph [mindist=0]; graph [mindist=0];
node [fontname="Indie Flower"];
labelloc=t; labelloc=t;
label="Legend" //label="Legend"
fontname="Indie Flower"
style=filled
fillcolor="transparent"
subgraph cluster_0 { subgraph cluster_0 {
label="Supported Format Types" label="Supported Format Types"
color="white" color="transparent"
XL[label="Excel",style=filled,color=green]; fontname="Indie Flower"
XL[label="Excel",style=filled,color="#00FF00"];
CSF[label="JS",shape=doublecircle]; CSF[label="JS",shape=doublecircle];
OLD[label="Other",style=filled,color=cyan]; OLD[label="Other",style=filled,color=cyan];
{ edge[style=invis] XL -> CSF -> OLD[constraint=false]} { edge[style=invis] XL -> CSF -> OLD[constraint=false]}
} }
subgraph cluster_1 { subgraph cluster_1 {
label="Workbook Format Conversions (blue arrow)" label="Workbook Format Conversions\n(blue arrow)"
color="white" color="transparent"
fontname="Indie Flower"
x1i[label="XLSX"] x1i[label="XLSX"]
c1[shape=doublecircle,label="JS"]; c1[shape=doublecircle,label="JS"];
x1o[label="XLSB"] x1o[label="XLSB"]
{ edge[color=blue] { edge[color=blue]
x1i->c1[constraint=false,label="read"] x1i->c1[constraint=false,label="read",fontname="Indie Flower"]
c1->x1o[constraint=false,label="write"]; c1->x1o[constraint=false,label="write",fontname="Indie Flower"];
} }
} }
subgraph cluster_2 { subgraph cluster_2 {
label="Single-Worksheet Format Conversions (green arrow)" label="Single-Worksheet Format Conversions\n(green arrow)"
color="white" color="transparent"
fontname="Indie Flower"
x2i[label="SYLK"] x2i[label="SYLK"]
c2[shape=doublecircle,label="JS"]; c2[shape=doublecircle,label="JS"];
x2o[label="CSV"] x2o[label="CSV"]
{ edge[color=aquamarine4] { edge[color=aquamarine4]
x2i->c2[constraint=false,label="read"] x2i->c2[constraint=false,label="read",fontname="Indie Flower"]
c2->x2o[constraint=false,label="write"]; c2->x2o[constraint=false,label="write",fontname="Indie Flower"];
} }
} }