version bump 0.11.6: ancillary format update

- BIFF5 XLS write (bookType "biff5")
- DBF Level 7 read
- ODS whitespace and repeated rows
- flow and lint cleanup
This commit is contained in:
SheetJS 2017-10-16 20:14:32 -04:00
parent ef9f3f9ca5
commit f968dfe4ed
63 changed files with 2867 additions and 2261 deletions

@ -2,7 +2,6 @@
.*/node_modules/.* .*/node_modules/.*
.*/dist/.* .*/dist/.*
.*/tmp/.* .*/tmp/.*
.*/test.js
.*/bits/.* .*/bits/.*
.*/ctest/.* .*/ctest/.*
@ -25,6 +24,7 @@ xlsxworker.flow.js
xlsx.flow.js xlsx.flow.js
.*/bin/.*.njs .*/bin/.*.njs
.*/demo/browser.flow.js .*/demo/browser.flow.js
test.js
[libs] [libs]
bits/09_types.js bits/09_types.js

1
.gitignore vendored

@ -27,3 +27,4 @@ tmp
*.html *.html
*.sheetjs *.sheetjs
*.exe *.exe
*.img

@ -29,6 +29,7 @@ tmp
*.html *.html
*.sheetjs *.sheetjs
*.exe *.exe
*.img
.gitignore .gitignore
.fossaignore .fossaignore
.spelling .spelling

@ -70,6 +70,8 @@ filesystem
javascript javascript
metadata metadata
natively natively
pre-built
pre-generated
prepend prepend
prepended prepended
repo repo

@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code. changes may not be included if they are not expected to break existing code.
## 0.11.6 (2017-10-16)
* Semicolon-delimited files are detected
## 0.11.5 (2017-09-30) ## 0.11.5 (2017-09-30)
* Bower main script shifted to full version * Bower main script shifted to full version

@ -213,7 +213,7 @@ book: readme graph ## Update summary for documentation
<README.md grep -vE "(details|summary)>" > misc/docs/README.md <README.md grep -vE "(details|summary)>" > misc/docs/README.md
DEMOMDS=$(sort $(wildcard demos/*/README.md)) DEMOMDS=$(sort $(wildcard demos/*/README.md))
MDLINT=$(DEMODS) $(READEPS) demos/README.md MDLINT=$(DEMOMDS) $(READEPS) demos/README.md
.PHONY: mdlint .PHONY: mdlint
mdlint: $(MDLINT) ## Check markdown documents mdlint: $(MDLINT) ## Check markdown documents
alex $^ alex $^

@ -1565,8 +1565,9 @@ Plain text format guessing follows the priority order:
| XML | starts with `<` | | XML | starts with `<` |
| RTF | starts with `{\rt` | | RTF | starts with `{\rt` |
| DSV | starts with `/sep=.$/`, separator is the specified character | | DSV | starts with `/sep=.$/`, separator is the specified character |
| CSV | more unquoted `","` characters than `"\t"` chars in the first 1024 | | DSV | more unquoted `";"` chars than `"\t"` or `","` in the first 1024 |
| TSV | one of the first 1024 characters is a tab char `"\t"` | | TSV | more unquoted `"\t"` chars than `","` chars in the first 1024 |
| CSV | one of the first 1024 characters is a comma `","` |
| PRN | (default) | | PRN | (default) |
- HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div` - HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div`
@ -1629,6 +1630,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format | | `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `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 |
| `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 |
@ -1638,7 +1640,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) | | `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document | | `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) | | `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `rtf` | `.rtf` | none | single | Rich Text Format | | `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text | | `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers. - `compression` only applies to formats with ZIP containers.
@ -1957,7 +1959,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: | | Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: |
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: |
| Excel 97-2004 (XLS BIFF8) | :o: | :o: | | Excel 97-2004 (XLS BIFF8) | :o: | :o: |
| Excel 5.0/95 (XLS BIFF5) | :o: | | | Excel 5.0/95 (XLS BIFF5) | :o: | :o: |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | | | Excel 4.0 (XLS/XLW BIFF4) | :o: | |
| Excel 3.0 (XLS BIFF3) | :o: | | | Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: | | Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
@ -1976,7 +1978,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | | | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: | | HTML Tables | :o: | :o: |
| RTF Tables | | :o: | | Rich Text Format tables (RTF) | | :o: |
### Excel 2007+ XML (XLSX/XLSM) ### Excel 2007+ XML (XLSX/XLSM)

@ -21,7 +21,7 @@ program
.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx') .option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx')
.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)')
@ -44,11 +44,10 @@ program
.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)') .option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)')
.option('--sst', 'generate shared string table for XLS* formats') .option('--sst', 'generate shared string table for XLS* formats')
.option('--compress', 'use compression when writing XLSX/M/B and ODS') .option('--compress', 'use compression when writing XLSX/M/B and ODS')
.option('--read-only', 'do not generate output') .option('--read', 'read but do not generate output')
.option('--all', 'parse everything; write as much as possible') .option('--all', 'parse everything; write as much as possible')
.option('--dev', 'development mode') .option('--dev', 'development mode')
.option('--sparse', 'sparse mode') .option('--sparse', 'sparse mode')
.option('--read', 'read but do not print out contents')
.option('-q, --quiet', 'quiet mode'); .option('-q, --quiet', 'quiet mode');
program.on('--help', function() { program.on('--help', function() {
@ -176,9 +175,8 @@ try {
process.exit(4); process.exit(4);
} }
if(program.readOnly) process.exit(0);
/* single worksheet formats */ /* single worksheet file formats */
[ [
['biff2', '.xls'], ['biff2', '.xls'],
['biff3', '.xls'], ['biff3', '.xls'],

@ -1,7 +1,5 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */ /* vim: set ts=2: */
/*jshint -W041 */
/*jshint funcscope:true, eqnull:true, loopfunc:true */
/*exported XLSX */ /*exported XLSX */
/*global global, exports, module, require:false, process:false, Buffer:false */ /*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {}; var XLSX = {};

@ -1 +1 @@
XLSX.version = '0.11.5'; XLSX.version = '0.11.6';

@ -28,14 +28,14 @@ var debom = function(data/*:string*/)/*:string*/ {
return data; return data;
}; };
var _getchar = function _gc1(x) { return String.fromCharCode(x); }; var _getchar = function _gc1(x/*:number*/)/*:string*/ { return String.fromCharCode(x); };
if(typeof cptable !== 'undefined') { if(typeof cptable !== 'undefined') {
set_cp = function(cp) { current_codepage = cp; }; set_cp = function(cp/*:number*/) { current_codepage = cp; };
debom = function(data) { debom = function(data/*:string*/) {
if(data.charCodeAt(0) === 0xFF && data.charCodeAt(1) === 0xFE) { return cptable.utils.decode(1200, char_codes(data.substr(2))); } if(data.charCodeAt(0) === 0xFF && data.charCodeAt(1) === 0xFE) { return cptable.utils.decode(1200, char_codes(data.substr(2))); }
return data; return data;
}; };
_getchar = function _gc2(x) { _getchar = function _gc2(x/*:number*/)/*:string*/ {
if(current_codepage === 1200) return String.fromCharCode(x); if(current_codepage === 1200) return String.fromCharCode(x);
return cptable.utils.decode(current_codepage, [x&255,x>>8])[0]; return cptable.utils.decode(current_codepage, [x&255,x>>8])[0];
}; };

@ -102,7 +102,7 @@ var utf8read/*:StringConv*/ = function utf8reada(orig) {
}; };
var utf8write/*:StringConv*/ = function(orig) { var utf8write/*:StringConv*/ = function(orig) {
var out = [], i = 0, c = 0, d = 0; var out/*:Array<string>*/ = [], i = 0, c = 0, d = 0;
while(i < orig.length) { while(i < orig.length) {
c = orig.charCodeAt(i++); c = orig.charCodeAt(i++);
switch(true) { switch(true) {
@ -197,7 +197,7 @@ function writextag(f,g,h) { return '<' + f + (isval(h) /*:: && h */? wxt_helper(
function write_w3cdtf(d/*:Date*/, t/*:?boolean*/)/*:string*/ { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } return ""; } function write_w3cdtf(d/*:Date*/, t/*:?boolean*/)/*:string*/ { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } return ""; }
function write_vt(s) { function write_vt(s)/*:string*/ {
switch(typeof s) { switch(typeof s) {
case 'string': return writextag('vt:lpwstr', s); case 'string': return writextag('vt:lpwstr', s);
case 'number': return writextag((s|0)==s?'vt:i4':'vt:r8', String(s)); case 'number': return writextag((s|0)==s?'vt:i4':'vt:r8', String(s));

@ -74,6 +74,7 @@ var __readUInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/
var __readInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { var u = b[idx+1]*(1<<8)+b[idx]; return (u < 0x8000) ? u : (0xffff - u + 1) * -1; }; var __readInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { var u = b[idx+1]*(1<<8)+b[idx]; return (u < 0x8000) ? u : (0xffff - u + 1) * -1; };
var __readUInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; }; var __readUInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; };
var __readInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; }; var __readInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; };
var __readInt32BE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx]<<24)|(b[idx+1]<<16)|(b[idx+2]<<8)|b[idx+3]; };
var ___unhexlify = function(s/*:string*/)/*:Array<number>*/ { return (s.match(/../g)||[]).map(function(x) { return parseInt(x,16);}); }; var ___unhexlify = function(s/*:string*/)/*:Array<number>*/ { return (s.match(/../g)||[]).map(function(x) { return parseInt(x,16);}); };
var __unhexlify = typeof Buffer !== "undefined" ? function(s/*:string*/)/*:Array<number>|Buffer*/ { return Buffer.isBuffer(s) ? new Buffer(s, 'hex') : ___unhexlify(s); } : ___unhexlify; var __unhexlify = typeof Buffer !== "undefined" ? function(s/*:string*/)/*:Array<number>|Buffer*/ { return Buffer.isBuffer(s) ? new Buffer(s, 'hex') : ___unhexlify(s); } : ___unhexlify;
@ -141,10 +142,15 @@ function ReadShift(size/*:number*/, t/*:?string*/)/*:number|string*/ {
switch(size) { switch(size) {
case 1: oI = __readUInt8(this, this.l); this.l++; return oI; case 1: oI = __readUInt8(this, this.l); this.l++; return oI;
case 2: oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); this.l += 2; return oI; case 2: oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); this.l += 2; return oI;
case 4: case 4: case -4:
if(t === 'i' || (this[this.l+3] & 0x80)===0) { oI = __readInt32LE(this, this.l); this.l += 4; return oI; } if(t === 'i' || (this[this.l+3] & 0x80)===0) { oI = (size > 0 ? __readInt32LE : __readInt32BE)(this, this.l); this.l += 4; return oI; }
else { oR = __readUInt32LE(this, this.l); this.l += 4; } return oR; else { oR = __readUInt32LE(this, this.l); this.l += 4; } return oR;
case 8: if(t === 'f') { oR = __double(this, this.l); this.l += 8; return oR; } case 8: case -8:
if(t === 'f') {
if(size == 8) oR = __double(this, this.l);
else oR = __double([this[this.l+7],this[this.l+6],this[this.l+5],this[this.l+4],this[this.l+3],this[this.l+2],this[this.l+1],this[this.l+0]], 0);
this.l += 8; return oR;
} else size = 8;
/* falls through */ /* falls through */
case 16: o = __hexlify(this, this.l, size); break; case 16: o = __hexlify(this, this.l, size); break;
}} }}
@ -162,6 +168,9 @@ function WriteShift(t/*:number*/, val/*:string|number*/, f/*:?string*/)/*:any*/
for(i = 0; i != val.length; ++i) __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i); for(i = 0; i != val.length; ++i) __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i);
size = 2 * val.length; size = 2 * val.length;
} else if(f === 'sbcs') { } else if(f === 'sbcs') {
/* TODO: codepage */
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
val = val.replace(/[^\x00-\x7F]/g, "_");
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */ /*:: if(typeof val !== 'string') throw new Error("unreachable"); */
for(i = 0; i != val.length; ++i) this[this.l + i] = val.charCodeAt(i) & 0xFF; for(i = 0; i != val.length; ++i) this[this.l + i] = val.charCodeAt(i) & 0xFF;
size = val.length; size = val.length;

@ -1,45 +1,43 @@
/* [MS-OLEPS] 2.2 PropertyType */ /* [MS-OLEPS] 2.2 PropertyType */
{ //var VT_EMPTY = 0x0000;
//var VT_EMPTY = 0x0000; //var VT_NULL = 0x0001;
//var VT_NULL = 0x0001; var VT_I2 = 0x0002;
var VT_I2 = 0x0002; var VT_I4 = 0x0003;
var VT_I4 = 0x0003; //var VT_R4 = 0x0004;
//var VT_R4 = 0x0004; //var VT_R8 = 0x0005;
//var VT_R8 = 0x0005; //var VT_CY = 0x0006;
//var VT_CY = 0x0006; //var VT_DATE = 0x0007;
//var VT_DATE = 0x0007; //var VT_BSTR = 0x0008;
//var VT_BSTR = 0x0008; //var VT_ERROR = 0x000A;
//var VT_ERROR = 0x000A; var VT_BOOL = 0x000B;
var VT_BOOL = 0x000B; var VT_VARIANT = 0x000C;
var VT_VARIANT = 0x000C; //var VT_DECIMAL = 0x000E;
//var VT_DECIMAL = 0x000E; //var VT_I1 = 0x0010;
//var VT_I1 = 0x0010; //var VT_UI1 = 0x0011;
//var VT_UI1 = 0x0011; //var VT_UI2 = 0x0012;
//var VT_UI2 = 0x0012; var VT_UI4 = 0x0013;
var VT_UI4 = 0x0013; //var VT_I8 = 0x0014;
//var VT_I8 = 0x0014; var VT_UI8 = 0x0015;
var VT_UI8 = 0x0015; //var VT_INT = 0x0016;
//var VT_INT = 0x0016; //var VT_UINT = 0x0017;
//var VT_UINT = 0x0017; var VT_LPSTR = 0x001E;
var VT_LPSTR = 0x001E; //var VT_LPWSTR = 0x001F;
//var VT_LPWSTR = 0x001F; var VT_FILETIME = 0x0040;
var VT_FILETIME = 0x0040; //var VT_BLOB = 0x0041;
//var VT_BLOB = 0x0041; //var VT_STREAM = 0x0042;
//var VT_STREAM = 0x0042; //var VT_STORAGE = 0x0043;
//var VT_STORAGE = 0x0043; //var VT_STREAMED_Object = 0x0044;
//var VT_STREAMED_Object = 0x0044; //var VT_STORED_Object = 0x0045;
//var VT_STORED_Object = 0x0045; //var VT_BLOB_Object = 0x0046;
//var VT_BLOB_Object = 0x0046; var VT_CF = 0x0047;
var VT_CF = 0x0047; //var VT_CLSID = 0x0048;
//var VT_CLSID = 0x0048; //var VT_VERSIONED_STREAM = 0x0049;
//var VT_VERSIONED_STREAM = 0x0049; var VT_VECTOR = 0x1000;
var VT_VECTOR = 0x1000; //var VT_ARRAY = 0x2000;
//var VT_ARRAY = 0x2000;
var VT_STRING = 0x0050; // 2.3.3.1.11 VtString var VT_STRING = 0x0050; // 2.3.3.1.11 VtString
var VT_USTR = 0x0051; // 2.3.3.1.12 VtUnalignedString var VT_USTR = 0x0051; // 2.3.3.1.12 VtUnalignedString
var VT_CUSTOM = [VT_STRING, VT_USTR]; var VT_CUSTOM = [VT_STRING, VT_USTR];
}
/* [MS-OSHARED] 2.3.3.2.2.1 Document Summary Information PIDDSI */ /* [MS-OSHARED] 2.3.3.2.2.1 Document Summary Information PIDDSI */
var DocSummaryPIDDSI = { var DocSummaryPIDDSI = {

@ -56,9 +56,9 @@ function write_rdf(rdf, opts) {
return o.join(""); return o.join("");
} }
/* TODO: pull properties */ /* TODO: pull properties */
var write_meta_ods/*:{(wb:any, opts:any):string}*/ = (function() { var write_meta_ods/*:{(wb:Workbook, opts:any):string}*/ = (function() {
var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>';
return function wmo(wb, opts) { return function wmo(wb/*:Workbook*/, opts) {
return payload; return payload;
}; };
})(); })();

@ -108,7 +108,7 @@ function parse_VtVector(blob, cb) {
} }
/* [MS-OLEPS] 2.15 TypedPropertyValue */ /* [MS-OLEPS] 2.15 TypedPropertyValue */
function parse_TypedPropertyValue(blob, type/*:number*/, _opts) { function parse_TypedPropertyValue(blob, type/*:number*/, _opts)/*:any*/ {
var t = blob.read_shift(2), ret, opts = _opts||{}; var t = blob.read_shift(2), ret, opts = _opts||{};
blob.l += 2; blob.l += 2;
if(type !== VT_VARIANT) if(type !== VT_VARIANT)
@ -193,12 +193,12 @@ function parse_PropertySet(blob, PIDSI) {
case 1252: case 1252:
case 65000: case -536: case 65000: case -536:
case 65001: case -535: case 65001: case -535:
set_cp(CodePage = PropH[piddsi.n]>>>0 & 0xFFFF); break; set_cp(CodePage = (PropH[piddsi.n]>>>0) & 0xFFFF); break;
default: throw new Error("Unsupported CodePage: " + PropH[piddsi.n]); default: throw new Error("Unsupported CodePage: " + PropH[piddsi.n]);
} }
} else { } else {
if(Props[i][0] === 0x1) { if(Props[i][0] === 0x1) {
CodePage = PropH.CodePage = parse_TypedPropertyValue(blob, VT_I2); CodePage = PropH.CodePage = (parse_TypedPropertyValue(blob, VT_I2)/*:number*/);
set_cp(CodePage); set_cp(CodePage);
if(Dictionary !== -1) { if(Dictionary !== -1) {
var oldpos = blob.l; var oldpos = blob.l;

@ -201,7 +201,8 @@ function parse_WriteAccess(blob, length, opts) {
return UserName; return UserName;
} }
function write_WriteAccess(s/*:string*/, opts) { function write_WriteAccess(s/*:string*/, opts) {
var o = new_buf(112); var b8 = !opts || opts.biff == 8;
var o = new_buf(b8 ? 112 : 54);
o.write_shift(opts.biff == 8 ? 2 : 1, 7); o.write_shift(opts.biff == 8 ? 2 : 1, 7);
o.write_shift(1, 0); o.write_shift(1, 0);
o.write_shift(4, 0x33336853); o.write_shift(4, 0x33336853);
@ -226,14 +227,15 @@ function parse_BoundSheet8(blob, length, opts) {
return { pos:pos, hs:hidden, dt:dt, name:name }; return { pos:pos, hs:hidden, dt:dt, name:name };
} }
function write_BoundSheet8(data, opts) { function write_BoundSheet8(data, opts) {
var o = new_buf(8 + 2 * data.name.length); var w = (!opts || opts.biff >= 8 ? 2 : 1);
var o = new_buf(8 + w * data.name.length);
o.write_shift(4, data.pos); o.write_shift(4, data.pos);
o.write_shift(1, data.hs || 0); o.write_shift(1, data.hs || 0);
o.write_shift(1, data.dt); o.write_shift(1, data.dt);
o.write_shift(1, data.name.length); o.write_shift(1, data.name.length);
o.write_shift(1, 1); if(opts.biff >= 8) o.write_shift(1, 1);
o.write_shift(2 * data.name.length, data.name, 'utf16le'); o.write_shift(w * data.name.length, data.name, opts.biff < 8 ? 'sbcs' : 'utf16le');
return o; return o.slice(0, o.l);
} }
/* 2.4.265 TODO */ /* 2.4.265 TODO */
@ -362,11 +364,12 @@ function parse_Label(blob, length, opts) {
return cell; return cell;
} }
function write_Label(R/*:number*/, C/*:number*/, v/*:string*/, opts) { function write_Label(R/*:number*/, C/*:number*/, v/*:string*/, opts) {
var o = new_buf(6 + 3 + 2 * v.length); var b8 = !opts || opts.biff == 8;
var o = new_buf(6 + 2 + (+b8) + (1 + b8) * v.length);
write_XLSCell(R, C, 0, o); write_XLSCell(R, C, 0, o);
o.write_shift(2, v.length); o.write_shift(2, v.length);
o.write_shift(1, 1); if(b8) o.write_shift(1, 1);
o.write_shift(2 * v.length, v, 'utf16le'); o.write_shift((1 + b8) * v.length, v, b8 ? 'utf16le' : 'sbcs');
return o; return o;
} }
@ -389,9 +392,10 @@ function parse_Dimensions(blob, length, opts) {
return {s: {r:r, c:c}, e: {r:R, c:C}}; return {s: {r:r, c:c}, e: {r:R, c:C}};
} }
function write_Dimensions(range, opts) { function write_Dimensions(range, opts) {
var o = new_buf(14); var w = opts.biff == 8 || !opts.biff ? 4 : 2;
o.write_shift(4, range.s.r); var o = new_buf(2*w + 6);
o.write_shift(4, range.e.r + 1); o.write_shift(w, range.s.r);
o.write_shift(w, range.e.r + 1);
o.write_shift(2, range.s.c); o.write_shift(2, range.s.c);
o.write_shift(2, range.e.c + 1); o.write_shift(2, range.e.c + 1);
o.write_shift(2, 0); o.write_shift(2, 0);
@ -598,7 +602,7 @@ function parse_Lbl(blob, length, opts) {
var name = parse_XLUnicodeStringNoCch(blob, cch, opts); var name = parse_XLUnicodeStringNoCch(blob, cch, opts);
if(flags & 0x20) name = XLSLblBuiltIn[name.charCodeAt(0)]; if(flags & 0x20) name = XLSLblBuiltIn[name.charCodeAt(0)];
var npflen = target - blob.l; if(opts && opts.biff == 2) --npflen; var npflen = target - blob.l; if(opts && opts.biff == 2) --npflen;
var rgce = target == blob.l || cce == 0 ? [] : parse_NameParsedFormula(blob, npflen, opts, cce); var rgce = target == blob.l || cce === 0 ? [] : parse_NameParsedFormula(blob, npflen, opts, cce);
return { return {
chKey: chKey, chKey: chKey,
Name: name, Name: name,

@ -55,13 +55,14 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
/* header */ /* header */
var ft = d.read_shift(1); var ft = d.read_shift(1);
var memo = false; var memo = false;
var vfp = false; var vfp = false, l7 = false;
switch(ft) { switch(ft) {
case 0x02: case 0x03: break; case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break; case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break; case 0x31: vfp = true; break;
case 0x83: memo = true; break; case 0x83: memo = true; break;
case 0x8B: memo = true; break; case 0x8B: memo = true; break;
case 0x8C: memo = true; l7 = true; break;
case 0xF5: memo = true; break; case 0xF5: memo = true; break;
default: throw new Error("DBF Unsupported Version: " + ft.toString(16)); default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
} }
@ -84,36 +85,42 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
d.l+=2; d.l+=2;
} }
if(l7) d.l += 36;
var fields = [], field = {}; var fields = [], field = {};
var hend = fpos - 10 - (vfp ? 264 : 0); var hend = fpos - 10 - (vfp ? 264 : 0), ww = l7 ? 32 : 11;
while(ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d: d.l < hend) { while(ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d: d.l < hend) {
field = {}; field = {};
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+10)).replace(/[\u0000\r\n].*$/g,""); field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)).replace(/[\u0000\r\n].*$/g,"");
d.l += 11; d.l += ww;
field.type = String.fromCharCode(d.read_shift(1)); field.type = String.fromCharCode(d.read_shift(1));
if(ft != 0x02) field.offset = d.read_shift(4); if(ft != 0x02 && !l7) field.offset = d.read_shift(4);
field.len = d.read_shift(1); field.len = d.read_shift(1);
if(ft == 0x02) field.offset = d.read_shift(2); if(ft == 0x02) field.offset = d.read_shift(2);
field.dec = d.read_shift(1); field.dec = d.read_shift(1);
if(field.name.length) fields.push(field); if(field.name.length) fields.push(field);
if(ft != 0x02) d.l += 14; if(ft != 0x02) d.l += l7 ? 13 : 14;
switch(field.type) { switch(field.type) {
// case 'B': break; // Binary case 'B': // VFP Double
case 'C': break; // character if((!vfp || field.len != 8) && opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
case 'D': break; // date break;
case 'F': break; // floating point case 'G': // General
// case 'G': break; // General case 'P': // Picture
case 'I': break; // long if(opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
case 'L': break; // boolean break;
case 'M': break; // memo case 'C': // character
case 'N': break; // number case 'D': // date
// case 'O': break; // double case 'F': // floating point
// case 'P': break; // Picture case 'I': // long
case 'T': break; // datetime case 'L': // boolean
case 'Y': break; // currency case 'M': // memo
case '0': break; // null ? case 'N': // number
case '+': break; // autoincrement case 'O': // double
case '@': break; // timestamp case 'T': // datetime
case 'Y': // currency
case '0': // VFP _NullFlags
case '@': // timestamp
case '+': // autoincrement
break;
default: throw new Error('Unknown Field Type: ' + field.type); default: throw new Error('Unknown Field Type: ' + field.type);
} }
} }
@ -145,7 +152,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
else out[R][C] = s; else out[R][C] = s;
break; break;
case 'F': out[R][C] = parseFloat(s.trim()); break; case 'F': out[R][C] = parseFloat(s.trim()); break;
case 'I': out[R][C] = dd.read_shift(4, 'i'); break; case '+': case 'I': out[R][C] = l7 ? dd.read_shift(-4, 'i') ^ 0x80000000 : dd.read_shift(4, 'i'); break;
case 'L': switch(s.toUpperCase()) { case 'L': switch(s.toUpperCase()) {
case 'Y': case 'T': out[R][C] = true; break; case 'Y': case 'T': out[R][C] = true; break;
case 'N': case 'F': out[R][C] = false; break; case 'N': case 'F': out[R][C] = false; break;
@ -154,15 +161,16 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
} break; } break;
case 'M': /* TODO: handle memo files */ case 'M': /* TODO: handle memo files */
if(!memo) throw new Error("DBF Unexpected MEMO for type " + ft.toString(16)); if(!memo) throw new Error("DBF Unexpected MEMO for type " + ft.toString(16));
out[R][C] = "##MEMO##" + dd.read_shift(4); out[R][C] = "##MEMO##" + (l7 ? parseInt(s.trim(), 10): dd.read_shift(4));
break; break;
case 'N': out[R][C] = +s.replace(/\u0000/g,"").trim(); break; case 'N': out[R][C] = +s.replace(/\u0000/g,"").trim(); break;
case 'T': case '@': out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400); break;
var day = dd.read_shift(4), ms = dd.read_shift(4); case 'T': out[R][C] = new Date((dd.read_shift(4) - 0x253D8C) * 0x5265C00 + dd.read_shift(4)); break;
throw new Error(day + " | " + ms); case 'Y': out[R][C] = dd.read_shift(4,'i')/1e4; break;
//out[R][C] = new Date(); // TODO case 'O': out[R][C] = -dd.read_shift(-8, 'f'); break;
//break; case 'B': if(vfp && fields[C].len == 8) { out[R][C] = dd.read_shift(8,'f'); break; }
case 'Y': out[R][C] = dd.read(4,'i')/1e4; break; /* falls through */
case 'G': case 'P': dd.l += fields[C].len; break;
case '0': case '0':
if(fields[C].name === '_NullFlags') break; if(fields[C].name === '_NullFlags') break;
/* falls through */ /* falls through */
@ -269,7 +277,7 @@ var SYLK = (function() {
cw = record[rj].substr(1).split(" "); cw = record[rj].substr(1).split(" ");
for(j = parseInt(cw[0], 10); j <= parseInt(cw[1], 10); ++j) { for(j = parseInt(cw[0], 10); j <= parseInt(cw[1], 10); ++j) {
Mval = parseInt(cw[2], 10); Mval = parseInt(cw[2], 10);
colinfo[j-1] = Mval == 0 ? {hidden:true}: {wch:Mval}; process_col(colinfo[j-1]); colinfo[j-1] = Mval === 0 ? {hidden:true}: {wch:Mval}; process_col(colinfo[j-1]);
} break; } break;
case 'C': /* default column format */ case 'C': /* default column format */
C = parseInt(record[rj].substr(1))-1; C = parseInt(record[rj].substr(1))-1;
@ -279,7 +287,7 @@ var SYLK = (function() {
R = parseInt(record[rj].substr(1))-1; R = parseInt(record[rj].substr(1))-1;
if(!rowinfo[R]) rowinfo[R] = {}; if(!rowinfo[R]) rowinfo[R] = {};
if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); } if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); }
else if(Mval == 0) rowinfo[R].hidden = true; else if(Mval === 0) rowinfo[R].hidden = true;
break; break;
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
} }
@ -514,16 +522,16 @@ var PRN = (function() {
// List of accepted CSV separators // List of accepted CSV separators
var guess_seps = { var guess_seps = {
0x2C: ',', /*::[*/0x2C/*::]*/: ',',
0x09: "\t", /*::[*/0x09/*::]*/: "\t",
0x3B: ';' /*::[*/0x3B/*::]*/: ';'
}; };
// CSV separator weights to be used in case of equal numbers // CSV separator weights to be used in case of equal numbers
var guess_sep_weights = { var guess_sep_weights = {
0x2C: 3, /*::[*/0x2C/*::]*/: 3,
0x09: 2, /*::[*/0x09/*::]*/: 2,
0x3B: 1 /*::[*/0x3B/*::]*/: 1
}; };
function guess_sep(str) { function guess_sep(str) {
@ -567,9 +575,9 @@ var PRN = (function() {
var s = str.slice(start, end); var s = str.slice(start, end);
var cell = ({}/*:any*/); var cell = ({}/*:any*/);
if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"'); if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"');
if(s.length == 0) cell.t = 'z'; if(s.length === 0) cell.t = 'z';
else if(o.raw) { cell.t = 's'; cell.v = s; } else if(o.raw) { cell.t = 's'; cell.v = s; }
else if(s.trim().length == 0) { cell.t = 's'; cell.v = s; } else if(s.trim().length === 0) { cell.t = 's'; cell.v = s; }
else if(s.charCodeAt(0) == 0x3D) { else if(s.charCodeAt(0) == 0x3D) {
if(s.charCodeAt(1) == 0x22 && s.charCodeAt(s.length - 1) == 0x22) { cell.t = 's'; cell.v = s.slice(2,-1).replace(/""/g,'"'); } if(s.charCodeAt(1) == 0x22 && s.charCodeAt(s.length - 1) == 0x22) { cell.t = 's'; cell.v = s.slice(2,-1).replace(/""/g,'"'); }
else if(fuzzyfmla(s)) { cell.t = 'n'; cell.f = s.substr(1); } else if(fuzzyfmla(s)) { cell.t = 'n'; cell.f = s.substr(1); }
@ -642,7 +650,7 @@ var PRN = (function() {
if(!cell || cell.v == null) { oo.push(" "); continue; } if(!cell || cell.v == null) { oo.push(" "); continue; }
var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10); var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10);
while(w.length < 10) w += " "; while(w.length < 10) w += " ";
oo.push(w + (C == 0 ? " " : "")); oo.push(w + (C === 0 ? " " : ""));
} }
o.push(oo.join("")); o.push(oo.join(""));
} }

@ -127,9 +127,9 @@ function parse_EncryptionVerifier(blob, length/*:number*/) {
function parse_EncryptionInfo(blob, length/*:?number*/) { function parse_EncryptionInfo(blob, length/*:?number*/) {
var vers = parse_CRYPTOVersion(blob); var vers = parse_CRYPTOVersion(blob);
switch(vers.Minor) { switch(vers.Minor) {
case 0x02: return parse_EncInfoStd(blob, vers); case 0x02: return [vers.Minor, parse_EncInfoStd(blob, vers)];
case 0x03: return parse_EncInfoExt(blob, vers); case 0x03: return [vers.Minor, parse_EncInfoExt(blob, vers)];
case 0x04: return parse_EncInfoAgl(blob, vers); case 0x04: return [vers.Minor, parse_EncInfoAgl(blob, vers)];
} }
throw new Error("ECMA-376 Encrypted file unrecognized Version: " + vers.Minor); throw new Error("ECMA-376 Encrypted file unrecognized Version: " + vers.Minor);
} }
@ -147,7 +147,10 @@ function parse_EncInfoStd(blob, vers) {
/* [MS-OFFCRYPTO] 2.3.4.6 EncryptionInfo Stream (Extensible Encryption) */ /* [MS-OFFCRYPTO] 2.3.4.6 EncryptionInfo Stream (Extensible Encryption) */
function parse_EncInfoExt(blob, vers) { throw new Error("File is password-protected: ECMA-376 Extensible"); } function parse_EncInfoExt(blob, vers) { throw new Error("File is password-protected: ECMA-376 Extensible"); }
/* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */ /* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */
function parse_EncInfoAgl(blob, vers) { throw new Error("File is password-protected: ECMA-376 Agile"); } function parse_EncInfoAgl(blob, vers) {
blob.l+=4;
return blob.read_shift(blob.length - blob.l, 'utf8');
}
@ -282,7 +285,7 @@ function parse_XORObfuscation(blob, length, opts, out) {
var o = ({ key: parseuint16(blob), verificationBytes: parseuint16(blob) }/*:any*/); var o = ({ key: parseuint16(blob), verificationBytes: parseuint16(blob) }/*:any*/);
if(opts.password) o.verifier = crypto_CreatePasswordVerifier_Method1(opts.password); if(opts.password) o.verifier = crypto_CreatePasswordVerifier_Method1(opts.password);
out.valid = o.verificationBytes === o.verifier; out.valid = o.verificationBytes === o.verifier;
if(out.valid) out.insitu_decrypt = crypto_MakeXorDecryptor(opts.password); if(out.valid) out.insitu = crypto_MakeXorDecryptor(opts.password);
return o; return o;
} }

@ -53,6 +53,13 @@ function parse_RgceLoc_BIFF2(blob, length, opts) {
return {r:r[0], c:c, cRel:r[1], rRel:r[2]}; return {r:r[0], c:c, cRel:r[1], rRel:r[2]};
} }
/* 2.5.198.107 , 2.5.47 */
function parse_RgceElfLoc(blob, length, opts) {
var r = blob.read_shift(2);
var c = blob.read_shift(2);
return {r:r, c:c & 0xFF, fQuoted:!!(c & 0x4000), cRel:c>>15, rRel:c>>15 };
}
/* [MS-XLS] 2.5.198.111 TODO */ /* [MS-XLS] 2.5.198.111 TODO */
/* [MS-XLSB] 2.5.97.92 TODO */ /* [MS-XLSB] 2.5.97.92 TODO */
function parse_RgceLocRel(blob, length, opts) { function parse_RgceLocRel(blob, length, opts) {
@ -446,6 +453,51 @@ var parse_PtgMemNoMem = parsenoop;
/* 2.5.198.92 */ /* 2.5.198.92 */
var parse_PtgTbl = parsenoop; var parse_PtgTbl = parsenoop;
function parse_PtgElfLoc(blob, length, opts) {
blob.l += 2;
return [parse_RgceElfLoc(blob, 4, opts)];
}
function parse_PtgElfNoop(blob, length, opts) {
blob.l += 6;
return [];
}
/* 2.5.198.46 */
var parse_PtgElfCol = parse_PtgElfLoc;
/* 2.5.198.47 */
var parse_PtgElfColS = parse_PtgElfNoop;
/* 2.5.198.48 */
var parse_PtgElfColSV = parse_PtgElfNoop;
/* 2.5.198.49 */
var parse_PtgElfColV = parse_PtgElfLoc;
/* 2.5.198.50 */
function parse_PtgElfLel(blob, length, opts) {
blob.l += 2;
return [parseuint16(blob), blob.read_shift(2) & 0x01];
}
/* 2.5.198.51 */
var parse_PtgElfRadical = parse_PtgElfLoc;
/* 2.5.198.52 */
var parse_PtgElfRadicalLel = parse_PtgElfLel;
/* 2.5.198.53 */
var parse_PtgElfRadicalS = parse_PtgElfNoop;
/* 2.5.198.54 */
var parse_PtgElfRw = parse_PtgElfLoc;
/* 2.5.198.55 */
var parse_PtgElfRwV = parse_PtgElfLoc;
/* [MS-XLSB] 2.5.97.52 */
function parse_PtgList(blob, length, opts) {
blob.l += 2;
var ixti = blob.read_shift(2);
blob.l += 10;
return {};
}
/* 2.5.198.91 */
function parse_PtgSxName(blob, length, opts) {
blob.l += 2;
return [blob.read_shift(4)];
}
/* 2.5.198.25 */ /* 2.5.198.25 */
var PtgTypes = { var PtgTypes = {
/*::[*/0x01/*::]*/: { n:'PtgExp', f:parse_PtgExp }, /*::[*/0x01/*::]*/: { n:'PtgExp', f:parse_PtgExp },
@ -521,8 +573,19 @@ var PtgDupes = {
(function(){for(var y in PtgDupes) PtgTypes[y] = PtgTypes[PtgDupes[y]];})(); (function(){for(var y in PtgDupes) PtgTypes[y] = PtgTypes[PtgDupes[y]];})();
var Ptg18 = { var Ptg18 = {
// /*::[*/0x19/*::]*/: { n:'PtgList', f:parse_PtgList }, // TODO /*::[*/0x01/*::]*/: { n:'PtgElfLel', f:parse_PtgElfLel },
// /*::[*/0x1D/*::]*/: { n:'PtgSxName', f:parse_PtgSxName }, // TODO /*::[*/0x02/*::]*/: { n:'PtgElfRw', f:parse_PtgElfRw },
/*::[*/0x03/*::]*/: { n:'PtgElfCol', f:parse_PtgElfCol },
/*::[*/0x06/*::]*/: { n:'PtgElfRwV', f:parse_PtgElfRwV },
/*::[*/0x07/*::]*/: { n:'PtgElfColV', f:parse_PtgElfColV },
/*::[*/0x0A/*::]*/: { n:'PtgElfRadical', f:parse_PtgElfRadical },
/*::[*/0x0B/*::]*/: { n:'PtgElfRadicalS', f:parse_PtgElfRadicalS },
/*::[*/0x0D/*::]*/: { n:'PtgElfColS', f:parse_PtgElfColS },
/*::[*/0x0F/*::]*/: { n:'PtgElfColSV', f:parse_PtgElfColSV },
/*::[*/0x10/*::]*/: { n:'PtgElfRadicalLel', f:parse_PtgElfRadicalLel },
/*::[*/0x19/*::]*/: { n:'PtgList', f:parse_PtgList },
/*::[*/0x1D/*::]*/: { n:'PtgSxName', f:parse_PtgSxName },
/*::[*/0xFF/*::]*/: {}
}; };
var Ptg19 = { var Ptg19 = {
/*::[*/0x01/*::]*/: { n:'PtgAttrSemi', f:parse_PtgAttrSemi }, /*::[*/0x01/*::]*/: { n:'PtgAttrSemi', f:parse_PtgAttrSemi },
@ -536,36 +599,7 @@ var Ptg19 = {
/*::[*/0x80/*::]*/: { n:'PtgAttrIfError', f:parse_PtgAttrIfError }, /*::[*/0x80/*::]*/: { n:'PtgAttrIfError', f:parse_PtgAttrIfError },
/*::[*/0xFF/*::]*/: {} /*::[*/0xFF/*::]*/: {}
}; };
Ptg19[0x21] = Ptg19[0x20];
/* 2.4.127 TODO */
function parse_Formula(blob, length, opts) {
var end = blob.l + length;
var cell = parse_XLSCell(blob, 6);
if(opts.biff == 2) ++blob.l;
var val = parse_FormulaValue(blob,8);
var flags = blob.read_shift(1);
if(opts.biff != 2) {
blob.read_shift(1);
if(opts.biff >= 5) {
var chn = blob.read_shift(4);
}
}
var cbf = parse_XLSCellParsedFormula(blob, end - blob.l, opts);
return {cell:cell, val:val[0], formula:cbf, shared: (flags >> 3) & 1, tt:val[1]};
}
/* 2.5.133 TODO: how to emit empty strings? */
function parse_FormulaValue(blob/*::, length*/) {
var b;
if(__readUInt16LE(blob,blob.l + 6) !== 0xFFFF) return [parse_Xnum(blob),'n'];
switch(blob[blob.l]) {
case 0x00: blob.l += 8; return ["String", 's'];
case 0x01: b = blob[blob.l+2] === 0x1; blob.l += 8; return [b,'b'];
case 0x02: b = blob[blob.l+2]; blob.l += 8; return [b,'e'];
case 0x03: blob.l += 8; return ["",'s'];
}
return [];
}
/* 2.5.198.103 */ /* 2.5.198.103 */
function parse_RgbExtra(blob, length, rgce, opts) { function parse_RgbExtra(blob, length, rgce, opts) {
@ -587,6 +621,11 @@ function parse_RgbExtra(blob, length, rgce, opts) {
rgce[i][1][1] = blob.read_shift(4); rgce[i][1][1] = blob.read_shift(4);
o.push(rgce[i][1]); o.push(rgce[i][1]);
} break; } break;
case 'PtgList': /* TODO: PtgList -> PtgExtraList */
case 'PtgElfRadicalS': /* TODO: PtgElfRadicalS -> PtgExtraElf */
case 'PtgElfColS': /* TODO: PtgElfColS -> PtgExtraElf */
case 'PtgElfColSV': /* TODO: PtgElfColSV -> PtgExtraElf */
throw "Unsupported " + rgce[i][0];
default: break; default: break;
} }
} }
@ -597,45 +636,6 @@ function parse_RgbExtra(blob, length, rgce, opts) {
return o; return o;
} }
/* 2.5.198.21 */
function parse_NameParsedFormula(blob, length, opts, cce) {
var target = blob.l + length;
var rgce = parse_Rgce(blob, cce, opts);
var rgcb;
if(target !== blob.l) rgcb = parse_RgbExtra(blob, target - blob.l, rgce, opts);
return [rgce, rgcb];
}
/* 2.5.198.3 TODO */
function parse_XLSCellParsedFormula(blob, length, opts) {
var target = blob.l + length, len = opts.biff == 2 ? 1 : 2;
var rgcb, cce = blob.read_shift(len); // length of rgce
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
var rgce = parse_Rgce(blob, cce, opts);
if(length !== cce + len) rgcb = parse_RgbExtra(blob, length - cce - len, rgce, opts);
return [rgce, rgcb];
}
/* 2.5.198.118 TODO */
function parse_SharedParsedFormula(blob, length, opts) {
var target = blob.l + length;
var rgcb, cce = blob.read_shift(2); // length of rgce
var rgce = parse_Rgce(blob, cce, opts);
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
if(length !== cce + 2) rgcb = parse_RgbExtra(blob, target - cce - 2, rgce, opts);
return [rgce, rgcb];
}
/* 2.5.198.1 TODO */
function parse_ArrayParsedFormula(blob, length, opts, ref) {
var target = blob.l + length, len = opts.biff == 2 ? 1 : 2;
var rgcb, cce = blob.read_shift(len); // length of rgce
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
var rgce = parse_Rgce(blob, cce, opts);
if(length !== cce + len) rgcb = parse_RgbExtra(blob, length - cce - len, rgce, opts);
return [rgce, rgcb];
}
/* 2.5.198.104 */ /* 2.5.198.104 */
function parse_Rgce(blob, length, opts) { function parse_Rgce(blob, length, opts) {
var target = blob.l + length; var target = blob.l + length;
@ -655,7 +655,7 @@ function parse_Rgce(blob, length, opts) {
return ptgs; return ptgs;
} }
function stringify_array(f)/*:string*/ { function stringify_array(f/*:Array<Array<any>>*/)/*:string*/ {
var o = []; var o = [];
for(var i = 0; i < f.length; ++i) { for(var i = 0; i < f.length; ++i) {
var x = f[i], r = []; var x = f[i], r = [];
@ -702,15 +702,12 @@ function get_ixti(supbooks, ixti/*:number*/, opts)/*:string*/ {
return formula_quote_sheet_name(get_ixti_raw(supbooks, ixti, opts)); return formula_quote_sheet_name(get_ixti_raw(supbooks, ixti, opts));
} }
function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, opts)/*:string*/ { function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks, opts)/*:string*/ {
//console.log(formula);
var _range = /*range != null ? range :*/ {s:{c:0, r:0},e:{c:0, r:0}}; var _range = /*range != null ? range :*/ {s:{c:0, r:0},e:{c:0, r:0}};
var stack/*:Array<string>*/ = [], e1, e2, type, c/*:CellAddress*/, ixti=0, nameidx=0, r, sname=""; var stack/*:Array<string>*/ = [], e1, e2, type, c/*:CellAddress*/, ixti=0, nameidx=0, r, sname="";
if(!formula[0] || !formula[0][0]) return ""; if(!formula[0] || !formula[0][0]) return "";
var last_sp = -1, sp = ""; var last_sp = -1, sp = "";
//console.log("--",cell,formula[0])
for(var ff = 0, fflen = formula[0].length; ff < fflen; ++ff) { for(var ff = 0, fflen = formula[0].length; ff < fflen; ++ff) {
var f = formula[0][ff]; var f = formula[0][ff];
//console.log("++",f, stack)
switch(f[0]) { switch(f[0]) {
case 'PtgUminus': /* 2.5.198.93 */ case 'PtgUminus': /* 2.5.198.93 */
stack.push("-" + stack.pop()); break; stack.push("-" + stack.pop()); break;
@ -791,7 +788,6 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
case 'PtgFunc': /* 2.5.198.62 */ case 'PtgFunc': /* 2.5.198.62 */
case 'PtgFuncVar': /* 2.5.198.63 */ case 'PtgFuncVar': /* 2.5.198.63 */
//console.log(f[1]);
/* f[1] = [argc, func, type] */ /* f[1] = [argc, func, type] */
var argc/*:number*/ = (f[1][0]/*:any*/), func/*:string*/ = (f[1][1]/*:any*/); var argc/*:number*/ = (f[1][0]/*:any*/), func/*:string*/ = (f[1][1]/*:any*/);
if(!argc) argc = 0; if(!argc) argc = 0;
@ -845,7 +841,6 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
/* f[1] = type, ixti, nameindex */ /* f[1] = type, ixti, nameindex */
var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = (f[1][2]/*:any*/); var externbook; var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = (f[1][2]/*:any*/); var externbook;
/* TODO: Properly handle missing values */ /* TODO: Properly handle missing values */
//console.log(bookidx, supbooks);
if(opts.biff <= 5) { if(opts.biff <= 5) {
if(bookidx < 0) bookidx = -bookidx; if(bookidx < 0) bookidx = -bookidx;
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx]; if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
@ -949,6 +944,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
case 'PtgMemFunc': /* 2.5.198.72 TODO */ case 'PtgMemFunc': /* 2.5.198.72 TODO */
break; break;
case 'PtgMemNoMem': /* 2.5.198.73 TODO -- find a test case */
throw new Error('Unrecognized Formula Token: ' + String(f));
case 'PtgElfCol': /* 2.5.198.46 */
case 'PtgElfColS': /* 2.5.198.47 */
case 'PtgElfColSV': /* 2.5.198.48 */
case 'PtgElfColV': /* 2.5.198.49 */
case 'PtgElfLel': /* 2.5.198.50 */
case 'PtgElfRadical': /* 2.5.198.51 */
case 'PtgElfRadicalLel': /* 2.5.198.52 */
case 'PtgElfRadicalS': /* 2.5.198.53 */
case 'PtgElfRw': /* 2.5.198.54 */
case 'PtgElfRwV': /* 2.5.198.55 */
throw new Error("Unsupported ELFs");
case 'PtgAttrBaxcel': /* 2.5.198.33 TODO -- find a test case*/
throw new Error('Unrecognized Formula Token: ' + String(f));
case 'PtgSxName': /* 2.5.198.91 TODO -- find a test case */
throw new Error('Unrecognized Formula Token: ' + String(f));
case 'PtgList': /* [MS-XLSB] 2.5.97.52 TODO -- find a test case */
throw new Error('Unrecognized Formula Token: ' + String(f));
default: throw new Error('Unrecognized Formula Token: ' + String(f)); default: throw new Error('Unrecognized Formula Token: ' + String(f));
} }
@ -976,9 +992,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
stack.push((_left ? sp : "") + stack.pop() + (_left ? "" : sp)); stack.push((_left ? sp : "") + stack.pop() + (_left ? "" : sp));
last_sp = -1; last_sp = -1;
} }
//console.log("::",f, stack)
} }
//console.log("--",stack);
if(stack.length > 1 && opts.WTF) throw new Error("bad formula stack"); if(stack.length > 1 && opts.WTF) throw new Error("bad formula stack");
return stack[0]; return stack[0];
} }

@ -1,3 +1,72 @@
/* [MS-XLS] 2.5.198.1 TODO */
function parse_ArrayParsedFormula(blob, length, opts, ref) {
var target = blob.l + length, len = opts.biff == 2 ? 1 : 2;
var rgcb, cce = blob.read_shift(len); // length of rgce
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
var rgce = parse_Rgce(blob, cce, opts);
if(length !== cce + len) rgcb = parse_RgbExtra(blob, length - cce - len, rgce, opts);
return [rgce, rgcb];
}
/* [MS-XLS] 2.5.198.3 TODO */
function parse_XLSCellParsedFormula(blob, length, opts) {
var target = blob.l + length, len = opts.biff == 2 ? 1 : 2;
var rgcb, cce = blob.read_shift(len); // length of rgce
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
var rgce = parse_Rgce(blob, cce, opts);
if(length !== cce + len) rgcb = parse_RgbExtra(blob, length - cce - len, rgce, opts);
return [rgce, rgcb];
}
/* [MS-XLS] 2.5.198.21 */
function parse_NameParsedFormula(blob, length, opts, cce) {
var target = blob.l + length;
var rgce = parse_Rgce(blob, cce, opts);
var rgcb;
if(target !== blob.l) rgcb = parse_RgbExtra(blob, target - blob.l, rgce, opts);
return [rgce, rgcb];
}
/* [MS-XLS] 2.5.198.118 TODO */
function parse_SharedParsedFormula(blob, length, opts) {
var target = blob.l + length;
var rgcb, cce = blob.read_shift(2); // length of rgce
var rgce = parse_Rgce(blob, cce, opts);
if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
if(length !== cce + 2) rgcb = parse_RgbExtra(blob, target - cce - 2, rgce, opts);
return [rgce, rgcb];
}
/* [MS-XLS] 2.5.133 TODO: how to emit empty strings? */
function parse_FormulaValue(blob/*::, length*/) {
var b;
if(__readUInt16LE(blob,blob.l + 6) !== 0xFFFF) return [parse_Xnum(blob),'n'];
switch(blob[blob.l]) {
case 0x00: blob.l += 8; return ["String", 's'];
case 0x01: b = blob[blob.l+2] === 0x1; blob.l += 8; return [b,'b'];
case 0x02: b = blob[blob.l+2]; blob.l += 8; return [b,'e'];
case 0x03: blob.l += 8; return ["",'s'];
}
return [];
}
/* [MS-XLS] 2.4.127 TODO */
function parse_Formula(blob, length, opts) {
var end = blob.l + length;
var cell = parse_XLSCell(blob, 6);
if(opts.biff == 2) ++blob.l;
var val = parse_FormulaValue(blob,8);
var flags = blob.read_shift(1);
if(opts.biff != 2) {
blob.read_shift(1);
if(opts.biff >= 5) {
var chn = blob.read_shift(4);
}
}
var cbf = parse_XLSCellParsedFormula(blob, end - blob.l, opts);
return {cell:cell, val:val[0], formula:cbf, shared: (flags >> 3) & 1, tt:val[1]};
}
/* XLSB Parsed Formula records have the same shape */ /* XLSB Parsed Formula records have the same shape */
function parse_XLSBParsedFormula(data, length, opts) { function parse_XLSBParsedFormula(data, length, opts) {
var end = data.l + length; var end = data.l + length;

@ -81,7 +81,7 @@ function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksh
} }
function write_ws_xml_merges(merges) { function write_ws_xml_merges(merges) {
if(merges.length == 0) return ""; if(merges.length === 0) return "";
var o = '<mergeCells count="' + merges.length + '">'; var o = '<mergeCells count="' + merges.length + '">';
for(var i = 0; i != merges.length; ++i) o += '<mergeCell ref="' + encode_range(merges[i]) + '"/>'; for(var i = 0; i != merges.length; ++i) o += '<mergeCell ref="' + encode_range(merges[i]) + '"/>';
return o + '</mergeCells>'; return o + '</mergeCells>';
@ -196,6 +196,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
case 'd': case 'd':
if(opts.cellDates) vv = parseDate(cell.v, -1).toISOString(); if(opts.cellDates) vv = parseDate(cell.v, -1).toISOString();
else { else {
cell = dup(cell);
cell.t = 'n'; cell.t = 'n';
vv = ''+(cell.v = datenum(parseDate(cell.v))); vv = ''+(cell.v = datenum(parseDate(cell.v)));
} }
@ -384,6 +385,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/, rels)/*:string*/ { function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/, rels)/*:string*/ {
var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R=0, C=0, rows = ws['!rows']; var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R=0, C=0, rows = ws['!rows'];
var dense = Array.isArray(ws); var dense = Array.isArray(ws);
var params = ({r:rr}/*:any*/), row/*:RowInfo*/, height = -1;
for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C); for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
for(R = range.s.r; R <= range.e.r; ++R) { for(R = range.s.r; R <= range.e.r; ++R) {
r = []; r = [];
@ -395,11 +397,11 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell); if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
} }
if(r.length > 0 || rows && rows[R]) { if(r.length > 0 || rows && rows[R]) {
var params = ({r:rr}/*:any*/); params = ({r:rr}/*:any*/);
if(rows && rows[R]) { if(rows && rows[R]) {
var row = rows[R]; row = rows[R];
if(row.hidden) params.hidden = 1; if(row.hidden) params.hidden = 1;
var height = -1; height = -1;
if (row.hpx) height = px2pt(row.hpx); if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt; else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; } if (height > -1) { params.ht = height; params.customHeight = 1; }

@ -261,7 +261,9 @@ function parse_BrtHLink(data, length, opts) {
var tooltip = parse_XLWideString(data); var tooltip = parse_XLWideString(data);
var display = parse_XLWideString(data); var display = parse_XLWideString(data);
data.l = end; data.l = end;
return {rfx:rfx, relId:relId, loc:loc, Tooltip:tooltip, display:display}; var o = ({rfx:rfx, relId:relId, loc:loc, display:display}/*:any*/);
if(tooltip) o.Tooltip = tooltip;
return o;
} }
function write_BrtHLink(l, rId, o) { function write_BrtHLink(l, rId, o) {
if(o == null) o = new_buf(50+4*l[1].Target.length); if(o == null) o = new_buf(50+4*l[1].Target.length);
@ -319,25 +321,16 @@ function write_BrtColInfo(C/*:number*/, col, o) {
} }
/* [MS-XLSB] 2.4.672 BrtMargins */ /* [MS-XLSB] 2.4.672 BrtMargins */
function parse_BrtMargins(data, length, opts) { var BrtMarginKeys = ["left","right","top","bottom","header","footer"];
return { function parse_BrtMargins(data, length, opts)/*:Margins*/ {
left: parse_Xnum(data, 8), var margins = ({}/*:any*/);
right: parse_Xnum(data, 8), BrtMarginKeys.forEach(function(k) { margins[k] = parse_Xnum(data, 8); });
top: parse_Xnum(data, 8), return margins;
bottom: parse_Xnum(data, 8),
header: parse_Xnum(data, 8),
footer: parse_Xnum(data, 8)
};
} }
function write_BrtMargins(margins, o) { function write_BrtMargins(margins/*:Margins*/, o) {
if(o == null) o = new_buf(6*8); if(o == null) o = new_buf(6*8);
default_margins(margins); default_margins(margins);
write_Xnum(margins.left, o); BrtMarginKeys.forEach(function(k) { write_Xnum((margins/*:any*/)[k], o); });
write_Xnum(margins.right, o);
write_Xnum(margins.top, o);
write_Xnum(margins.bottom, o);
write_Xnum(margins.header, o);
write_Xnum(margins.footer, o);
return o; return o;
} }
@ -641,13 +634,13 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
/* TODO: something useful -- this is a stub */ /* TODO: something useful -- this is a stub */
function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, ws/*:Worksheet*/) { function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts, ws/*:Worksheet*/) {
if(cell.v === undefined) return ""; if(cell.v === undefined) return "";
var vv = ""; var olddate = null; var vv = "";
switch(cell.t) { switch(cell.t) {
case 'b': vv = cell.v ? "1" : "0"; break; case 'b': vv = cell.v ? "1" : "0"; break;
case 'd': // no BrtCellDate :( case 'd': // no BrtCellDate :(
cell = dup(cell);
cell.z = cell.z || SSF._table[14]; cell.z = cell.z || SSF._table[14];
olddate = cell.v; cell.v = datenum(parseDate(cell.v)); cell.t = 'n';
cell.v = datenum((cell.v/*:any*/)); cell.t = 'n';
break; break;
/* falls through */ /* falls through */
case 'n': case 'e': vv = ''+cell.v; break; case 'n': case 'e': vv = ''+cell.v; break;
@ -673,7 +666,6 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
/* TODO: determine threshold for Real vs RK */ /* TODO: determine threshold for Real vs RK */
if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) write_record(ba, "BrtCellRk", write_BrtCellRk(cell, o)); if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) write_record(ba, "BrtCellRk", write_BrtCellRk(cell, o));
else write_record(ba, "BrtCellReal", write_BrtCellReal(cell, o)); else write_record(ba, "BrtCellReal", write_BrtCellReal(cell, o));
if(olddate) { cell.t = 'd'; cell.v = olddate; }
return; return;
case 'b': case 'b':
o.t = "b"; o.t = "b";

@ -196,7 +196,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
var comments = [], comment = {}; var comments = [], comment = {};
var cstys = [], csty, seencol = false; var cstys = [], csty, seencol = false;
var arrayf = []; var arrayf = [];
var rowinfo = [], rowobj = {}; var rowinfo = [], rowobj = {}, cc = 0, rr = 0;
var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {}; var Workbook/*:WBWBProps*/ = ({ Sheets:[], WBProps:{date1904:false} }/*:any*/), wsprops = {};
xlmlregex.lastIndex = 0; xlmlregex.lastIndex = 0;
str = str.replace(/<!--([\s\S]*?)-->/mg,""); str = str.replace(/<!--([\s\S]*?)-->/mg,"");
@ -220,8 +220,8 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
delete cell.HRef; delete cell.HRefScreenTip; delete cell.HRef; delete cell.HRefScreenTip;
} }
if(cell.MergeAcross || cell.MergeDown) { if(cell.MergeAcross || cell.MergeDown) {
var cc = c + (parseInt(cell.MergeAcross,10)|0); cc = c + (parseInt(cell.MergeAcross,10)|0);
var rr = r + (parseInt(cell.MergeDown,10)|0); rr = r + (parseInt(cell.MergeDown,10)|0);
mergecells.push({s:{c:c,r:r},e:{c:cc,r:rr}}); mergecells.push({s:{c:c,r:r},e:{c:cc,r:rr}});
} }
if(!opts.sheetStubs) { if(cell.MergeAcross) c = cc + 1; else ++c; } if(!opts.sheetStubs) { if(cell.MergeAcross) c = cc + 1; else ++c; }

@ -38,11 +38,11 @@ function slurp(R, blob, length/*:number*/, opts) {
var l = length; var l = length;
var bufs = []; var bufs = [];
var d = blob.slice(blob.l,blob.l+l); var d = blob.slice(blob.l,blob.l+l);
if(opts && opts.enc && opts.enc.insitu_decrypt) switch(R.n) { if(opts && opts.enc && opts.enc.insitu) switch(R.n) {
case 'BOF': case 'FilePass': case 'FileLock': case 'InterfaceHdr': case 'RRDInfo': case 'RRDHead': case 'UsrExcl': break; case 'BOF': case 'FilePass': case 'FileLock': case 'InterfaceHdr': case 'RRDInfo': case 'RRDHead': case 'UsrExcl': break;
default: default:
if(d.length === 0) break; if(d.length === 0) break;
opts.enc.insitu_decrypt(d); opts.enc.insitu(d);
} }
bufs.push(d); bufs.push(d);
blob.l += l; blob.l += l;

@ -38,7 +38,7 @@ function write_BIFF2LABEL(r/*:number*/, c/*:number*/, val) {
function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) { function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
if(cell.v != null) switch(cell.t) { if(cell.v != null) switch(cell.t) {
case 'd': case 'n': case 'd': case 'n':
var v = cell.t == 'd' ? datenum(cell.v) : cell.v; var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
if((v == (v|0)) && (v >= 0) && (v < 65536)) if((v == (v|0)) && (v >= 0) && (v < 65536))
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v)); write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v));
else else
@ -88,7 +88,7 @@ function write_biff2_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) { function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
if(cell.v != null) switch(cell.t) { if(cell.v != null) switch(cell.t) {
case 'd': case 'n': case 'd': case 'n':
var v = cell.t == 'd' ? datenum(cell.v) : cell.v; var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
/* TODO: emit RK as appropriate */ /* TODO: emit RK as appropriate */
write_biff_rec(ba, "Number", write_Number(R, C, v, opts)); write_biff_rec(ba, "Number", write_Number(R, C, v, opts));
return; return;
@ -148,16 +148,16 @@ function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
var A = buf_array(); var A = buf_array();
var b8 = opts.biff == 8, b5 = opts.biff == 5; var b8 = opts.biff == 8, b5 = opts.biff == 5;
write_biff_rec(A, 0x0809, write_BOF(wb, 0x05, opts)); write_biff_rec(A, 0x0809, write_BOF(wb, 0x05, opts));
write_biff_rec(A, "InterfaceHdr", writeuint16(0x04b0)); write_biff_rec(A, "InterfaceHdr", b8 ? writeuint16(0x04b0) : null);
write_biff_rec(A, "Mms", writezeroes(2)); write_biff_rec(A, "Mms", writezeroes(2));
if(b5) write_biff_rec(A, "ToolbarHdr"); if(b5) write_biff_rec(A, "ToolbarHdr");
if(b5) write_biff_rec(A, "ToolbarEnd"); if(b5) write_biff_rec(A, "ToolbarEnd");
write_biff_rec(A, "InterfaceEnd"); write_biff_rec(A, "InterfaceEnd");
write_biff_rec(A, "WriteAccess", write_WriteAccess("SheetJS", opts)); write_biff_rec(A, "WriteAccess", write_WriteAccess("SheetJS", opts));
write_biff_rec(A, "CodePage", writeuint16(0x04b0)); write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
if(b8) write_biff_rec(A, "DSF", writeuint16(0)); if(b8) write_biff_rec(A, "DSF", writeuint16(0));
if(b8) write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length)); write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
if(b8) write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11)); write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
write_biff_rec(A, "WinProtect", writebool(false)); write_biff_rec(A, "WinProtect", writebool(false));
write_biff_rec(A, "Protect", writebool(false)); write_biff_rec(A, "Protect", writebool(false));
write_biff_rec(A, "Password", writeuint16(0)); write_biff_rec(A, "Password", writeuint16(0));
@ -168,21 +168,21 @@ function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
write_biff_rec(A, "HideObj", writeuint16(0)); write_biff_rec(A, "HideObj", writeuint16(0));
write_biff_rec(A, "Date1904", writebool(safe1904(wb)=="true")); write_biff_rec(A, "Date1904", writebool(safe1904(wb)=="true"));
write_biff_rec(A, "CalcPrecision", writebool(true)); write_biff_rec(A, "CalcPrecision", writebool(true));
write_biff_rec(A, "RefreshAll", writebool(false)); if(b8) write_biff_rec(A, "RefreshAll", writebool(false));
write_biff_rec(A, "BookBool", writeuint16(0)); write_biff_rec(A, "BookBool", writeuint16(0));
/* ... */ /* ... */
write_biff_rec(A, "UsesELFs", writebool(false)); if(b8) write_biff_rec(A, "UsesELFs", writebool(false));
var a = A.end(); var a = A.end();
var C = buf_array(); var C = buf_array();
write_biff_rec(C, "Country", write_Country()); if(b8) write_biff_rec(C, "Country", write_Country());
/* BIFF8: [SST *Continue] ExtSST */ /* BIFF8: [SST *Continue] ExtSST */
write_biff_rec(C, "EOF"); write_biff_rec(C, "EOF");
var c = C.end(); var c = C.end();
var B = buf_array(); var B = buf_array();
var blen = 0, j = 0; var blen = 0, j = 0;
for(j = 0; j < wb.SheetNames.length; ++j) blen += 12 + (b8 ? 2 : 1) * wb.SheetNames[j].length; for(j = 0; j < wb.SheetNames.length; ++j) blen += (b8 ? 12 : 11) + (b8 ? 2 : 1) * wb.SheetNames[j].length;
var start = a.length + blen + c.length; var start = a.length + blen + c.length;
for(j = 0; j < wb.SheetNames.length; ++j) { for(j = 0; j < wb.SheetNames.length; ++j) {
write_biff_rec(B, "BoundSheet8", write_BoundSheet8({pos:start, hs:0, dt:0, name:wb.SheetNames[j]}, opts)); write_biff_rec(B, "BoundSheet8", write_BoundSheet8({pos:start, hs:0, dt:0, name:wb.SheetNames[j]}, opts));

@ -1,20 +1,31 @@
/* OpenDocument */ /* OpenDocument */
var parse_content_xml = (function() { var parse_content_xml = (function() {
var parse_text_p = function(text, tag) { /* 6.1.2 White Space Characters */
return unescapexml(text.replace(/<text:s\/>/g," ").replace(/<text:s text:c="(\d+)"\/>/g, function($$,$1) { return Array(parseInt($1)+1).join(" "); }).replace(/<[^>]*>/g,"")); var parse_text_p = function(text/*:string*/, tag)/*:string*/ {
return unescapexml(text
.replace(/[\t\r\n]/g, " ").trim().replace(/ +/g, " ")
.replace(/<text:s\/>/g," ")
.replace(/<text:s text:c="(\d+)"\/>/g, function($$,$1) { return Array(parseInt($1,10)+1).join(" "); })
.replace(/<text:tab[^>]*\/>/g,"\t")
.replace(/<text:line-break\/>/g,"\n")
.replace(/<[^>]*>/g,"")
);
}; };
var number_formats = { var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */ /* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"], day: ["d", "dd"],
month: ["m", "mm"], month: ["m", "mm"],
year: ["y", "yy"], year: ["y", "yy"],
hours: ["h", "hh"], hours: ["h", "hh"],
minutes: ["m", "mm"], minutes: ["m", "mm"],
seconds: ["s", "ss"], seconds: ["s", "ss"],
"am-pm": ["A/P", "AM/PM"], "am-pm": ["A/P", "AM/PM"],
"day-of-week": ["ddd", "dddd"] "day-of-week": ["ddd", "dddd"],
era: ["e", "ee"],
/* there is no native representation of LO "Q" format */
quarter: ["\\Qm", "m\\\"th quarter\""]
}; };
return function pcx(d/*:string*/, _opts)/*:Workbook*/ { return function pcx(d/*:string*/, _opts)/*:Workbook*/ {
@ -29,15 +40,17 @@ var parse_content_xml = (function() {
var Sheets = {}, SheetNames/*:Array<string>*/ = []; var Sheets = {}, SheetNames/*:Array<string>*/ = [];
var ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); var ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
var Rn, q/*:: = ({t:"", v:null, z:null, w:"",c:[]}:any)*/; var Rn, q/*:: = ({t:"", v:null, z:null, w:"",c:[]}:any)*/;
var ctag = {value:""}; var ctag = ({value:""}/*:any*/);
var textp = "", textpidx = 0, textptag/*:: = {}*/; var textp = "", textpidx = 0, textptag/*:: = {}*/;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}}; var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var row_ol = 0;
var number_format_map = {}; var number_format_map = {};
var merges = [], mrange = {}, mR = 0, mC = 0; var merges = [], mrange = {}, mR = 0, mC = 0;
var rowinfo = [], rowpeat = 1, colpeat = 1;
var arrayf = []; var arrayf = [];
var comments = [], comment = {}; var comments = [], comment = {};
var creator = "", creatoridx = 0; var creator = "", creatoridx = 0;
var rept = 1, isstub = false; var isstub = false;
var i = 0; var i = 0;
xlmlregex.lastIndex = 0; xlmlregex.lastIndex = 0;
str = str.replace(/<!--([\s\S]*?)-->/mg,"").replace(/<!DOCTYPE[^\[]*\[[^\]]*\]>/gm,""); str = str.replace(/<!--([\s\S]*?)-->/mg,"").replace(/<!DOCTYPE[^\[]*\[[^\]]*\]>/gm,"");
@ -47,6 +60,7 @@ var parse_content_xml = (function() {
if(Rn[1]==='/') { if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = encode_range(range); if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = encode_range(range);
if(merges.length) ws['!merges'] = merges; if(merges.length) ws['!merges'] = merges;
if(rowinfo.length) ws["!rows"] = rowinfo;
sheetag.name = utf8read(sheetag['名称'] || sheetag.name); sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name); SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws; Sheets[sheetag.name] = ws;
@ -56,13 +70,20 @@ var parse_content_xml = (function() {
R = C = -1; R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0; range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); merges = []; ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); merges = [];
rowinfo = [];
} }
break; break;
case 'table-row-group': // 9.1.9 <table:table-row-group>
if(Rn[1] === "/") --row_ol; else ++row_ol;
break;
case 'table-row': case '行': // 9.1.3 <table:table-row> case 'table-row': case '行': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break; if(Rn[1] === '/') { R+=rowpeat; rowpeat = 1; break; }
rowtag = parsexmltag(Rn[0], false); rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R; if(rowtag['行号']) R = rowtag['行号'] - 1; else if(R == -1) R = 0;
rowpeat = +rowtag['number-rows-repeated'] || 1;
/* TODO: remove magic */
if(rowpeat < 10) for(i = 0; i < rowpeat; ++i) if(row_ol > 0) rowinfo[R + i] = {level: row_ol};
C = -1; break; C = -1; break;
case 'covered-table-cell': // 9.1.5 <table:covered-table-cell> case 'covered-table-cell': // 9.1.5 <table:covered-table-cell>
++C; ++C;
@ -73,13 +94,24 @@ var parse_content_xml = (function() {
break; /* stub */ break; /* stub */
case 'table-cell': case '数据': case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') { if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
else ++C;
}
else if(Rn[1]!=='/') {
++C; ++C;
rept = 1; ctag = parsexmltag(Rn[0], false);
colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
q = ({t:'z', v:null/*:: , z:null, w:"",c:[]*/}/*:any*/);
if(ctag.formula && opts.cellFormula != false) q.f = ods_to_csf_formula(unescapexml(ctag.formula));
if((ctag['数据类型'] || ctag['value-type']) == "string") {
q.t = "s"; q.v = unescapexml(ctag['string-value'] || "");
if(opts.dense) {
if(!ws[R]) ws[R] = [];
ws[R][C] = q;
} else {
ws[encode_cell({r:R,c:C})] = q;
}
}
C+= colpeat-1;
} else if(Rn[1]!=='/') {
++C;
colpeat = 1;
if(C > range.e.c) range.e.c = C; if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R; if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C; if(C < range.s.c) range.s.c = C;
@ -110,7 +142,7 @@ var parse_content_xml = (function() {
} }
/* 19.675.2 table:number-columns-repeated */ /* 19.675.2 table:number-columns-repeated */
if(ctag['number-columns-repeated']) rept = parseInt(ctag['number-columns-repeated'], 10); if(ctag['number-columns-repeated']) colpeat = parseInt(ctag['number-columns-repeated'], 10);
/* 19.385 office:value-type */ /* 19.385 office:value-type */
switch(q.t) { switch(q.t) {
@ -139,17 +171,22 @@ var parse_content_xml = (function() {
if(textp && opts.cellText !== false) q.w = textp; if(textp && opts.cellText !== false) q.w = textp;
if(!isstub || opts.sheetStubs) { if(!isstub || opts.sheetStubs) {
if(!(opts.sheetRows && opts.sheetRows < R)) { if(!(opts.sheetRows && opts.sheetRows < R)) {
if(opts.dense) { for(var rpt = 0; rpt < rowpeat; ++rpt) {
if(!ws[R]) ws[R] = []; colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
ws[R][C] = q; if(opts.dense) {
while(--rept > 0) ws[R][++C] = dup(q); if(!ws[R + rpt]) ws[R + rpt] = [];
} else { ws[R + rpt][C] = rpt == 0 ? q : dup(q);
ws[encode_cell({r:R,c:C})] = q; while(--colpeat > 0) ws[R + rpt][C + colpeat] = dup(q);
while(--rept > 0) ws[encode_cell({r:R,c:++C})] = dup(q); } else {
ws[encode_cell({r:R + rpt,c:C})] = q;
while(--colpeat > 0) ws[encode_cell({r:R + rpt,c:C + colpeat})] = dup(q);
}
if(range.e.c <= C) range.e.c = C;
} }
if(range.e.c <= C) range.e.c = C;
} }
} else { C += rept; rept = 0; } }
colpeat = parseInt(ctag['number-columns-repeated']||"1", 10);
C += colpeat-1; colpeat = 0;
q = {/*:: t:"", v:null, z:null, w:"",c:[]*/}; q = {/*:: t:"", v:null, z:null, w:"",c:[]*/};
textp = ""; textp = "";
} }
@ -199,6 +236,7 @@ var parse_content_xml = (function() {
case 'form': // 13.13 <form:form> case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links> case 'dde-links': // 9.8 <table:dde-links>
case 'event-listeners': // TODO case 'event-listeners': // TODO
case 'chart': // TODO
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;} if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]); else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
textp = ""; textpidx = 0; textp = ""; textpidx = 0;
@ -226,7 +264,7 @@ var parse_content_xml = (function() {
case 'script': break; // 3.13 <office:script> case 'script': break; // 3.13 <office:script>
case 'libraries': break; // TODO: <ooo:libraries> case 'libraries': break; // TODO: <ooo:libraries>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles> case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'master-styles': break; // TODO: <office:automatic-styles> case 'master-styles': break; // TODO: <office:master-styles>
case 'default-style': // TODO: <style:default-style> case 'default-style': // TODO: <style:default-style>
case 'page-layout': break; // TODO: <style:page-layout> case 'page-layout': break; // TODO: <style:page-layout>
@ -284,16 +322,18 @@ var parse_content_xml = (function() {
break; break;
case 'text-content': break; // 16.27.27 <number:text-content> case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties> case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'embedded-text': break; // 16.27.4 <number:embedded-text>
case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3 case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2 case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column> case 'table-column': break; // 9.1.6 <table:table-column>
case 'table-header-rows': break; // 9.1.7 <table:table-header-rows> case 'table-header-rows': break; // 9.1.7 <table:table-header-rows>
case 'table-rows': break; // 9.1.12 <table:table-rows>
/* TODO: outline levels */ /* TODO: outline levels */
case 'table-row-group': break; // 9.1.9 <table:table-row-group>
case 'table-column-group': break; // 9.1.10 <table:table-column-group> case 'table-column-group': break; // 9.1.10 <table:table-column-group>
case 'table-header-columns': break; // 9.1.11 <table:table-header-columns> case 'table-header-columns': break; // 9.1.11 <table:table-header-columns>
case 'table-columns': break; // 9.1.12 <table:table-columns>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904 case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
@ -312,9 +352,10 @@ var parse_content_xml = (function() {
case 'line-break': break; // 6.1.5 <text:line-break> case 'line-break': break; // 6.1.5 <text:line-break>
case 'span': break; // 6.1.7 <text:span> case 'span': break; // 6.1.7 <text:span>
case 'p': case '文本串': // 5.1.3 <text:p> case 'p': case '文本串': // 5.1.3 <text:p>
if(Rn[1]==='/') textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag); if(Rn[1]==='/' && (!ctag || !ctag['string-value'])) textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; } else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p> break; // <text:p>
case 's': break; // <text:s>
case 'database-range': // 9.4.15 <table:database-range> case 'database-range': // 9.4.15 <table:database-range>
if(Rn[1]==='/') break; if(Rn[1]==='/') break;
@ -324,12 +365,12 @@ var parse_content_xml = (function() {
} catch(e) {/* empty */} } catch(e) {/* empty */}
break; break;
case 's': break; // <text:s>
case 'date': break; // <*:date> case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object> case 'object': break; // 10.4.6.2 <draw:object>
case 'title': case '标题': break; // <*:title> OR <uof:标题> case 'title': case '标题': break; // <*:title> OR <uof:标题>
case 'desc': break; // <*:desc> case 'desc': break; // <*:desc>
case 'binary-data': break; // 10.4.5 TODO: b64 blob
/* 9.2 Advanced Tables */ /* 9.2 Advanced Tables */
case 'table-source': break; // 9.2.6 case 'table-source': break; // 9.2.6
@ -379,9 +420,12 @@ var parse_content_xml = (function() {
/* TODO: FODS Properties */ /* TODO: FODS Properties */
case 'initial-creator': case 'initial-creator':
case 'creation-date': case 'creation-date':
case 'print-date':
case 'generator': case 'generator':
case 'document-statistic': case 'document-statistic':
case 'user-defined': case 'user-defined':
case 'editing-duration':
case 'editing-cycles':
break; break;
/* TODO: FODS Config */ /* TODO: FODS Config */
@ -438,27 +482,33 @@ var parse_content_xml = (function() {
case 'table-protection': break; case 'table-protection': break;
case 'data-pilot-grand-total': break; // <table: case 'data-pilot-grand-total': break; // <table:
case 'office-document-common-attrs': break; // bare case 'office-document-common-attrs': break; // bare
default: default: switch(Rn[2]) {
if(Rn[2] === 'dc:') break; // TODO: properties case 'dc:': // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing case 'calcext:': // ignore undocumented extensions
if(Rn[2] === 'style:') break; // TODO: styles case 'loext:': // ignore undocumented extensions
if(Rn[2] === 'form:') break; // TODO: forms case 'ooo:': // ignore undocumented extensions
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions case 'chartooo:': // ignore undocumented extensions
if(Rn[2] === 'loext:') break; // ignore undocumented extensions case 'draw:': // TODO: drawing
if(Rn[2] === 'uof:') break; // TODO: uof case 'style:': // TODO: styles
if(Rn[2] === '表:') break; // TODO: uof case 'chart:': // TODO: charts
if(Rn[2] === '字:') break; // TODO: uof case 'form:': // TODO: forms
if(opts.WTF) throw new Error(Rn); case 'uof:': // TODO: uof
case '表:': // TODO: uof
case '字:': // TODO: uof
break;
default: if(opts.WTF) throw new Error(Rn);
}
} }
var out = { var out = {
Sheets: Sheets, Sheets: Sheets,
SheetNames: SheetNames SheetNames: SheetNames
}; };
if(opts.bookSheets) delete out.Sheets;
return out; return out;
}; };
})(); })();
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) { function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
opts = opts || ({}/*:any*/); opts = opts || ({}/*:any*/);
var ods = !!safegetzipfile(zip, 'objectdata'); var ods = !!safegetzipfile(zip, 'objectdata');
if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
@ -468,7 +518,7 @@ function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
if(safegetzipfile(zip, 'meta.xml')) wb.Props = parse_core_props(getzipdata(zip, 'meta.xml')); if(safegetzipfile(zip, 'meta.xml')) wb.Props = parse_core_props(getzipdata(zip, 'meta.xml'));
return wb; return wb;
} }
function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) { function parse_fods(data/*:string*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
return parse_content_xml(data, opts); return parse_content_xml(data, opts);
} }

@ -1,14 +1,36 @@
/* OpenDocument */ /* OpenDocument */
var write_styles_ods/*:{(wb:any, opts:any):string}*/ = (function() { var write_styles_ods/*:{(wb:any, opts:any):string}*/ = (function() {
var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-styles xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2"></office:document-styles>'; var payload = '<office:document-styles ' + wxt_helper({
'xmlns:office': "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
'xmlns:table': "urn:oasis:names:tc:opendocument:xmlns:table:1.0",
'xmlns:style': "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
'xmlns:text': "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
'xmlns:draw': "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
'xmlns:fo': "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
'xmlns:xlink': "http://www.w3.org/1999/xlink",
'xmlns:dc': "http://purl.org/dc/elements/1.1/",
'xmlns:number': "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0",
'xmlns:svg': "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
'xmlns:of': "urn:oasis:names:tc:opendocument:xmlns:of:1.2",
'office:version': "1.2"
}) + '></office:document-styles>';
return function wso(wb, opts) { return function wso(wb, opts) {
return payload; return XML_HEADER + payload;
}; };
})(); })();
var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
/* 6.1.2 White Space Characters */
var write_text_p = function(text/*:string*/)/*:string*/ {
return escapexml(text)
.replace(/ +/g, function($$){return '<text:s text:c="'+$$.length+'"/>';})
.replace(/\t/g, "<text:tab/>")
.replace(/\n/g, "<text:line-break/>")
.replace(/^ /, "<text:s/>").replace(/ $/, "<text:s/>");
};
var null_cell_xml = ' <table:table-cell />\n'; var null_cell_xml = ' <table:table-cell />\n';
var covered_cell_xml = ' <table:covered-table-cell/>\n'; var covered_cell_xml = ' <table:covered-table-cell/>\n';
var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ { var write_ws = function(ws, wb/*:Workbook*/, i/*:number*/, opts)/*:string*/ {
/* Section 9 Tables */ /* Section 9 Tables */
var o = []; var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n'); o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
@ -56,7 +78,7 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
ct['office:value'] = (cell.v||0); ct['office:value'] = (cell.v||0);
break; break;
case 's': case 'str': case 's': case 'str':
textp = escapexml(cell.v); textp = cell.v;
ct['office:value-type'] = "string"; ct['office:value-type'] = "string";
break; break;
case 'd': case 'd':
@ -68,7 +90,7 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
//case 'e': //case 'e':
default: o.push(null_cell_xml); continue; default: o.push(null_cell_xml); continue;
} }
o.push(' ' + writextag('table:table-cell', writextag('text:p', textp, {}), ct) + '\n'); o.push(' ' + writextag('table:table-cell', writextag('text:p', write_text_p(textp), {}), ct) + '\n');
} }
o.push(' </table:table-row>\n'); o.push(' </table:table-row>\n');
} }
@ -133,8 +155,8 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() {
}); });
var fods = wxt_helper({ var fods = wxt_helper({
'xmlns:config':"urn:oasis:names:tc:opendocument:xmlns:config:1.0", 'xmlns:config': "urn:oasis:names:tc:opendocument:xmlns:config:1.0",
'office:mimetype':"application/vnd.oasis.opendocument.spreadsheet" 'office:mimetype': "application/vnd.oasis.opendocument.spreadsheet"
}); });
if(opts.bookType == "fods") o.push('<office:document' + attr + fods + '>\n'); if(opts.bookType == "fods") o.push('<office:document' + attr + fods + '>\n');
@ -161,7 +183,7 @@ function write_ods(wb/*:any*/, opts/*:any*/) {
var manifest/*:Array<Array<string> >*/ = []; var manifest/*:Array<Array<string> >*/ = [];
var rdf = []; var rdf = [];
/* 3:3.3 and 2:2.2.4 */ /* Part 3 Section 3.3 MIME Media Type */
f = "mimetype"; f = "mimetype";
zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
@ -177,17 +199,17 @@ function write_ods(wb/*:any*/, opts/*:any*/) {
manifest.push([f, "text/xml"]); manifest.push([f, "text/xml"]);
rdf.push([f, "StylesFile"]); rdf.push([f, "StylesFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* TODO: this is hard-coded to satiate excel */ /* TODO: this is hard-coded to satiate excel */
f = "meta.xml"; f = "meta.xml";
zip.file(f, write_meta_ods(wb, opts)); zip.file(f, write_meta_ods(wb, opts));
manifest.push([f, "text/xml"]); manifest.push([f, "text/xml"]);
rdf.push([f, "MetadataFile"]); rdf.push([f, "MetadataFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* Part 3 Section 4 Manifest File */ /* Part 3 Section 4 Manifest File */
f = "META-INF/manifest.xml"; f = "META-INF/manifest.xml";
zip.file(f, write_manifest(manifest, opts)); zip.file(f, write_manifest(manifest, opts));

@ -49,6 +49,8 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts); if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
/* UOC */ /* UOC */
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts); if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
/* Numbers */
if(safegetzipfile(zip, 'Index/Document.iwa')) throw new Error('Unsupported NUMBERS file');
var entries = keys(zip.files).filter(nodirs).sort(); var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')/*:?any*/), opts); var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')/*:?any*/), opts);
@ -186,7 +188,7 @@ function parse_xlsxcfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
data = CFB.find(cfb, f); data = CFB.find(cfb, f);
if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
var dsm = parse_DataSpaceMap(data.content); var dsm = parse_DataSpaceMap(data.content);
if(dsm.length != 1 || dsm[0].comps.length != 1 || dsm[0].comps[0].t != 0 || dsm[0].name != "StrongEncryptionDataSpace" || dsm[0].comps[0].v != "EncryptedPackage") if(dsm.length !== 1 || dsm[0].comps.length !== 1 || dsm[0].comps[0].t !== 0 || dsm[0].name !== "StrongEncryptionDataSpace" || dsm[0].comps[0].v !== "EncryptedPackage")
throw new Error("ECMA-376 Encrypted file bad " + f); throw new Error("ECMA-376 Encrypted file bad " + f);
f = 'StrongEncryptionDataSpace'; f = 'StrongEncryptionDataSpace';
@ -207,6 +209,7 @@ function parse_xlsxcfb(cfb, opts/*:?ParseOpts*/)/*:Workbook*/ {
if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
var einfo = parse_EncryptionInfo(data.content); var einfo = parse_EncryptionInfo(data.content);
if(einfo[0] == 0x04) throw new Error("File is password-protected: ECMA-376 Agile");
throw new Error("File is password-protected"); throw new Error("File is password-protected");
} }

@ -70,7 +70,7 @@ function read_prn(data, d, o, str) {
} }
function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
var zip, d = data, n=[0], str = false; var zip, d = data, n = [0,0,0,0], str = false;
var o = opts||{}; var o = opts||{};
_ssfopts = {}; _ssfopts = {};
if(o.dateNF) _ssfopts.dateNF = o.dateNF; if(o.dateNF) _ssfopts.dateNF = o.dateNF;
@ -81,14 +81,14 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
case 0xD0: return read_cfb(CFB.read(d, o), o); case 0xD0: return read_cfb(CFB.read(d, o), o);
case 0x09: return parse_xlscfb(d, o); case 0x09: return parse_xlscfb(d, o);
case 0x3C: return parse_xlml(d, o); case 0x3C: return parse_xlml(d, o);
case 0x49: if(n[1] == 0x44) return read_wb_ID(d, o); break; case 0x49: if(n[1] === 0x44) return read_wb_ID(d, o); break;
case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break; case 0x54: if(n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return DIF.to_workbook(d, o); break;
case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; case 0x50: if(n[1] === 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
case 0xEF: return n[3] == 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str); case 0xEF: return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);
case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; case 0xFF: if(n[1] === 0xFE) { return read_utf16(d, o); } break;
case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; case 0x00: if(n[1] === 0x00 && n[2] >= 0x02 && n[3] === 0x00) return WK_.to_workbook(d, o); break;
case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); case 0x03: case 0x83: case 0x8B: case 0x8C: return DBF.to_workbook(d, o);
case 0x7B: if(n[1] == 0x5C && n[2] == 0x72 && n[3] == 0x74) return RTF.to_workbook(d, o); break; case 0x7B: if(n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return RTF.to_workbook(d, o); break;
case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o); case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o);
} }
if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);

@ -175,10 +175,10 @@ function json_to_sheet(js/*:Array<any>*/, opts)/*:Worksheet*/ {
var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:js.length}}/*:any*/); var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:js.length}}/*:any*/);
var hdr = o.header || [], C = 0; var hdr = o.header || [], C = 0;
for(var R = 0; R != js.length; ++R) { js.forEach(function (JS, R) {
Object.keys(js[R]).filter(function(x) { return js[R].hasOwnProperty(x); }).forEach(function(k) { keys(JS).filter(function(x) { return JS.hasOwnProperty(x); }).forEach(function(k) {
if((C=hdr.indexOf(k)) == -1) hdr[C=hdr.length] = k; if((C=hdr.indexOf(k)) == -1) hdr[C=hdr.length] = k;
var v = js[R][k]; var v = JS[k];
var t = 'z'; var t = 'z';
var z = ""; var z = "";
if(typeof v == 'number') t = 'n'; if(typeof v == 'number') t = 'n';
@ -192,7 +192,7 @@ function json_to_sheet(js/*:Array<any>*/, opts)/*:Worksheet*/ {
ws[encode_cell({c:C,r:R+1})] = cell = ({t:t, v:v}/*:any*/); ws[encode_cell({c:C,r:R+1})] = cell = ({t:t, v:v}/*:any*/);
if(z) cell.z = z; if(z) cell.z = z;
}); });
} });
range.e.c = hdr.length - 1; range.e.c = hdr.length - 1;
for(C = 0; C < hdr.length; ++C) ws[encode_col(C) + "1"] = {t:'s', v:hdr[C]}; for(C = 0; C < hdr.length; ++C) ws[encode_col(C) + "1"] = {t:'s', v:hdr[C]};
ws['!ref'] = encode_range(range); ws['!ref'] = encode_range(range);

@ -11,7 +11,7 @@
<!-- ui-grid --> <!-- ui-grid -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.css"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.css"/>
<!-- FileSaver shim for exporting files --> <!-- FileSaver shim for exporting files -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"></script>

28
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

25
dist/xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

836
dist/xlsx.js vendored

File diff suppressed because it is too large Load Diff

24
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

@ -36,76 +36,3 @@ With [bower](http://bower.io/search/?q=js-xlsx):
$ bower install js-xlsx $ bower install js-xlsx
``` ```
### JS Ecosystem Demos
The [`demos` directory](demos/) includes sample projects for:
**Frameworks and APIs**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`meteor`](demos/meteor/)
- [`react and react-native`](demos/react/)
- [`vue 2.x and weex`](demos/vue/)
- [`XMLHttpRequest and fetch`](demos/xhr/)
- [`nodejs server`](demos/server/)
**Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`webpack 2.x`](demos/webpack/)
**Platforms and Integrations**
- [`electron application`](demos/electron/)
- [`nw.js application`](demos/nwjs/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Swift JSC and other engines`](demos/altjs/)
### Optional Modules
<details>
<summary><b>Optional features</b> (click to show)</summary>
The node version automatically requires modules for additional features. Some
of these modules are rather large in size and are only needed in special
circumstances, so they do not ship with the core. For browser use, they must
be included directly:
```html
<!-- international support from js-codepage -->
<script src="dist/cpexcel.js"></script>
```
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`
Webpack and Browserify builds include optional modules by default. Webpack can
be configured to remove support with `resolve.alias`:
```js
/* uncomment the lines below to remove support */
resolve: {
alias: { "./dist/cpexcel.js": "" } // <-- omit international support
}
```
</details>
### ECMAScript 5 Compatibility
Since the library uses functions like `Array#forEach`, older browsers require
[shims to provide missing functions](http://oss.sheetjs.com/js-xlsx/shim.js).
To use the shim, add the shim before the script tag that loads `xlsx.js`:
```html
<!-- add the shim first -->
<script type="text/javascript" src="shim.js"></script>
<!-- after the shim is referenced, add the library -->
<script type="text/javascript" src="xlsx.full.min.js"></script>
```

28
docbits/11_demos.md Normal file

@ -0,0 +1,28 @@
### JS Ecosystem Demos
The [`demos` directory](demos/) includes sample projects for:
**Frameworks and APIs**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`meteor`](demos/meteor/)
- [`react and react-native`](demos/react/)
- [`vue 2.x and weex`](demos/vue/)
- [`XMLHttpRequest and fetch`](demos/xhr/)
- [`nodejs server`](demos/server/)
**Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`webpack 2.x`](demos/webpack/)
**Platforms and Integrations**
- [`electron application`](demos/electron/)
- [`nw.js application`](demos/nwjs/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Swift JSC and other engines`](demos/altjs/)

45
docbits/12_optional.md Normal file

@ -0,0 +1,45 @@
### Optional Modules
<details>
<summary><b>Optional features</b> (click to show)</summary>
The node version automatically requires modules for additional features. Some
of these modules are rather large in size and are only needed in special
circumstances, so they do not ship with the core. For browser use, they must
be included directly:
```html
<!-- international support from js-codepage -->
<script src="dist/cpexcel.js"></script>
```
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`
Webpack and Browserify builds include optional modules by default. Webpack can
be configured to remove support with `resolve.alias`:
```js
/* uncomment the lines below to remove support */
resolve: {
alias: { "./dist/cpexcel.js": "" } // <-- omit international support
}
```
</details>
### ECMAScript 5 Compatibility
Since the library uses functions like `Array#forEach`, older browsers require
[shims to provide missing functions](http://oss.sheetjs.com/js-xlsx/shim.js).
To use the shim, add the shim before the script tag that loads `xlsx.js`:
```html
<!-- add the shim first -->
<script type="text/javascript" src="shim.js"></script>
<!-- after the shim is referenced, add the library -->
<script type="text/javascript" src="xlsx.full.min.js"></script>
```

@ -97,8 +97,9 @@ Plain text format guessing follows the priority order:
| XML | starts with `<` | | XML | starts with `<` |
| RTF | starts with `{\rt` | | RTF | starts with `{\rt` |
| DSV | starts with `/sep=.$/`, separator is the specified character | | DSV | starts with `/sep=.$/`, separator is the specified character |
| CSV | more unquoted `","` characters than `"\t"` chars in the first 1024 | | DSV | more unquoted `";"` chars than `"\t"` or `","` in the first 1024 |
| TSV | one of the first 1024 characters is a tab char `"\t"` | | TSV | more unquoted `"\t"` chars than `","` chars in the first 1024 |
| CSV | one of the first 1024 characters is a comma `","` |
| PRN | (default) | | PRN | (default) |
- HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div` - HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div`

@ -36,6 +36,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format | | `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `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 |
| `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 |
@ -45,7 +46,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) | | `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document | | `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) | | `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `rtf` | `.rtf` | none | single | Rich Text Format | | `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text | | `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers. - `compression` only applies to formats with ZIP containers.

@ -9,7 +9,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: | | Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: |
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: |
| Excel 97-2004 (XLS BIFF8) | :o: | :o: | | Excel 97-2004 (XLS BIFF8) | :o: | :o: |
| Excel 5.0/95 (XLS BIFF5) | :o: | | | Excel 5.0/95 (XLS BIFF5) | :o: | :o: |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | | | Excel 4.0 (XLS/XLW BIFF4) | :o: | |
| Excel 3.0 (XLS BIFF3) | :o: | | | Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: | | Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
@ -28,7 +28,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | | | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: | | HTML Tables | :o: | :o: |
| RTF Tables | | :o: | | Rich Text Format tables (RTF) | | :o: |
### Excel 2007+ XML (XLSX/XLSM) ### Excel 2007+ XML (XLSX/XLSM)

@ -40,6 +40,7 @@ digraph G {
xlsb -> csf xlsb -> csf
csf -> xlml csf -> xlml
xlml -> csf xlml -> csf
csf -> xls5
xls5 -> csf xls5 -> csf
csf -> xls8 csf -> xls8
xls8 -> csf xls8 -> csf

Binary file not shown.

Before

(image error) Size: 179 KiB

After

(image error) Size: 181 KiB

@ -54,7 +54,7 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<script> <script>
/*jshint browser:true */ /*jshint browser:true */
/* eslint-env browser */ /* eslint-env browser */
/*global Uint8Array, Uint16Array, ArrayBuffer */ /*global Uint8Array, console */
/*global XLSX */ /*global XLSX */
var X = XLSX; var X = XLSX;
var XW = { var XW = {

@ -1437,8 +1437,9 @@ Plain text format guessing follows the priority order:
| XML | starts with `<` | | XML | starts with `<` |
| RTF | starts with `{\rt` | | RTF | starts with `{\rt` |
| DSV | starts with `/sep=.$/`, separator is the specified character | | DSV | starts with `/sep=.$/`, separator is the specified character |
| CSV | more unquoted `","` characters than `"\t"` chars in the first 1024 | | DSV | more unquoted `";"` chars than `"\t"` or `","` in the first 1024 |
| TSV | one of the first 1024 characters is a tab char `"\t"` | | TSV | more unquoted `"\t"` chars than `","` chars in the first 1024 |
| CSV | one of the first 1024 characters is a comma `","` |
| PRN | (default) | | PRN | (default) |
- HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div` - HTML tags include: `html`, `table`, `head`, `meta`, `script`, `style`, `div`
@ -1497,6 +1498,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format | | `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `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 |
| `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 |
@ -1506,7 +1508,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) | | `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document | | `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) | | `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `rtf` | `.rtf` | none | single | Rich Text Format | | `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text | | `prn` | `.prn` | none | single | Lotus Formatted Text |
- `compression` only applies to formats with ZIP containers. - `compression` only applies to formats with ZIP containers.
@ -1804,7 +1806,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: | | Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: |
| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: | | Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: |
| Excel 97-2004 (XLS BIFF8) | :o: | :o: | | Excel 97-2004 (XLS BIFF8) | :o: | :o: |
| Excel 5.0/95 (XLS BIFF5) | :o: | | | Excel 5.0/95 (XLS BIFF5) | :o: | :o: |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | | | Excel 4.0 (XLS/XLW BIFF4) | :o: | |
| Excel 3.0 (XLS BIFF3) | :o: | | | Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: | | Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
@ -1823,7 +1825,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | | | Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:| | **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | :o: | :o: | | HTML Tables | :o: | :o: |
| RTF Tables | | :o: | | Rich Text Format tables (RTF) | | :o: |
### Excel 2007+ XML (XLSX/XLSM) ### Excel 2007+ XML (XLSX/XLSM)

@ -2,25 +2,26 @@
# vim: set ts=4: # vim: set ts=4:
# Format: <basename> <ext> <ext> [ext..] # Format: <basename> <ext> <ext> [ext..]
# yes-formula # yes-formula
# note: Excel ODS converter does not support spaces in Sheet Names
AutoFilter .xls .xlsb .xlsx .xml AutoFilter .xls .xlsb .xlsx .xml
# note: XLML only supports sheets, ods does not support dialog # note: XLML only supports sheets, ods does not support dialog
BlankSheetTypes .xls .xlsb .xlsm BlankSheetTypes .xls .xlsb .xlsm
NumberFormatCondition .xls .xlsb .xlsm .xml NumberFormatCondition .xls .xlsb .xlsm .xml
RkNumber .xls .xlsb .xlsx .xls.xml RkNumber .xls .xlsb .xlsx .xls.xml
#calendar_stress_test .xls .xlsb .xlsx .xml #calendar_stress_test .xls .xlsb .xlsx .xml
cell_style_simple .xls .xlsb .xlsx .xml cell_style_simple .xls .xlsb .xlsx .xml .ods
# no-csv (newline character \r vs \n) # no-csv (newline character \r vs \n)
comments_stress_test .xls .xlsb .xlsx .xls.xml comments_stress_test .xls .xlsb .xlsx .xls.xml .ods
# yes-csv # yes-csv
custom_properties .xls .xlsb .xlsx .xls.xml custom_properties .xls .xlsb .xlsx .xls.xml
defined_names_simple .xls .xlsb .xlsx .xml defined_names_simple .xls .xlsb .xlsx .xml
# no-csv (randbetween) note: ODS does not support many XLSX functions # no-csv (randbetween) note: ODS does not support many XLSX functions
formula_stress_test .xls .xlsb .xlsx .xls.xml formula_stress_test .xls .xlsb .xlsx .xls.xml
# yes-csv # yes-csv
formulae_test_simple .xls .xlsb .xlsx .xml formulae_test_simple .xls .xlsb .xlsx .xml
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml #large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
merge_cells .xls .xlsb .xlsx .xls.xml merge_cells .xls .xlsb .xlsx .xls.xml .ods
# no-formula (filename-references in XLSX encoding as [0]) # no-formula (filename-references in XLSX encoding as [0])
named_ranges_2011 .xls .xlsb .xlsx .xls.xml named_ranges_2011 .xls .xlsb .xlsx .xls.xml
# yes-formula # yes-formula
@ -34,7 +35,7 @@ pivot_table_named_range .xls .xlsb .xlsx .xml
pivot_table_test .xls .xlsb .xlsm pivot_table_test .xls .xlsb .xlsm
rich_text_stress .xls .xlsb .xlsx .xls.xml rich_text_stress .xls .xlsb .xlsx .xls.xml
smart_tags_2007 .xls .xlsb .xlsx .xml smart_tags_2007 .xls .xlsb .xlsx .xml
sushi .xls .xlsb .xlsx .xml sushi .xls .xlsb .xlsx .xml .ods
text_and_numbers .xls .xlsb .xlsx .xml text_and_numbers .xls .xlsb .xlsx .xml
#time_stress_test_1 .xls .xlsb .xlsx .xml #time_stress_test_1 .xls .xlsb .xlsx .xml
# no-formula (rounding issue in IE8) # no-formula (rounding issue in IE8)

@ -1,8 +1,8 @@
{ {
"name": "xlsx", "name": "xlsx",
"version": "0.11.5", "version": "0.11.6",
"author": "sheetjs", "author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLS/XML) ODS and other spreadsheet format (CSV/DIF/DBF/SYLK) parser and writer", "description": "SheetJS Spreadsheet data parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "csv", "dbf", "dif", "sylk", "office", "spreadsheet" ], "keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "csv", "dbf", "dif", "sylk", "office", "spreadsheet" ],
"bin": { "bin": {
"xlsx": "./bin/xlsx.njs" "xlsx": "./bin/xlsx.njs"
@ -17,20 +17,20 @@
"fs": false "fs": false
}, },
"dependencies": { "dependencies": {
"exit-on-epipe":"~1.0.1", "exit-on-epipe": "~1.0.1",
"ssf":"~0.10.1", "ssf": "~0.10.1",
"codepage":"~1.11.0", "codepage": "~1.11.0",
"cfb":"~0.13.1", "cfb": "~0.13.1",
"crc-32":"~1.1.1", "crc-32": "~1.1.1",
"adler-32":"~1.1.0", "adler-32": "~1.1.0",
"commander":"~2.11.0" "commander": "~2.11.0"
}, },
"devDependencies": { "devDependencies": {
"mocha":"~2.5.3", "mocha": "~2.5.3",
"blanket": "~1.2.3", "blanket": "~1.2.3",
"@sheetjs/uglify-js":"~2.7.3", "@sheetjs/uglify-js": "~2.7.3",
"@types/node":"^8.0.7", "@types/node": "^8.0.7",
"@types/commander":"^2.9.0", "@types/commander": "^2.9.0",
"jsdom": "~11.1.0", "jsdom": "~11.1.0",
"dtslint": "^0.1.2", "dtslint": "^0.1.2",
"typescript": "2.2.0" "typescript": "2.2.0"
@ -39,6 +39,8 @@
"scripts": { "scripts": {
"pretest": "git submodule init && git submodule update", "pretest": "git submodule init && git submodule update",
"test": "make travis", "test": "make travis",
"build": "make",
"lint": "make fullint",
"dtslint": "dtslint types" "dtslint": "dtslint types"
}, },
"config": { "config": {

797
test.js

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
Subproject commit 936478fc713e5ee59c35a49bce4f61ea397284e5 Subproject commit e16abc3908a12be7c7293518e5fed1e31e374605

@ -1441,3 +1441,5 @@ artifacts/wps/write.et
# artifacts/wps/write.xls ## bad sheet name # artifacts/wps/write.xls ## bad sheet name
artifacts/wps/write.xlsx artifacts/wps/write.xlsx
artifacts/wps/write.xml artifacts/wps/write.xml
dbf/d11.dbf
dbf/vfp3.dbf

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -39,6 +39,7 @@
./test_files/custom_properties.xlsb ./test_files/custom_properties.xlsb
./test_files/cell_style_simple.xls ./test_files/cell_style_simple.xls
./test_files/cell_style_simple.xml ./test_files/cell_style_simple.xml
./test_files/cell_style_simple.ods
./test_files/cell_style_simple.xlsx ./test_files/cell_style_simple.xlsx
./test_files/cell_style_simple.xlsb ./test_files/cell_style_simple.xlsb
./test_files/comments_stress_test.xls ./test_files/comments_stress_test.xls
@ -106,3 +107,5 @@
./test_files/apachepoi_SimpleWithComments.xlsx ./test_files/apachepoi_SimpleWithComments.xlsx
./test_files/2013/apachepoi_SimpleWithComments.xlsx.xlsb ./test_files/2013/apachepoi_SimpleWithComments.xlsx.xlsb
./test_files/password_2002_40_xor.xls ./test_files/password_2002_40_xor.xls
./test_files/dbf/d11.dbf
./test_files/dbf/vfp3.dbf

@ -5,7 +5,7 @@
var data = [ var data = [
[1, 2, 3], [1, 2, 3],
[true, false, null, "sheetjs"], [true, false, null, "sheetjs"],
["foo", "bar", new Date("2014-02-19T14:30Z"), "0.3"], ["foo bar", "baz", new Date("2014-02-19T14:30Z"), "0.3"],
["baz", null, "\u0BEE", 3.14159], ["baz", null, "\u0BEE", 3.14159],
["hidden"], ["hidden"],
["visible"] ["visible"]
@ -162,8 +162,9 @@ var filenames = [
['sheetjs.xlsx', {bookSST:true}], ['sheetjs.xlsx', {bookSST:true}],
['sheetjs.xlsm'], ['sheetjs.xlsm'],
['sheetjs.xlsb'], ['sheetjs.xlsb'],
['sheetjs.xls', {bookType:'xls'}], ['sheetjs.biff8.xls', {bookType:'xls'}],
['sheetjs.biff2', {bookType:'biff2'}], ['sheetjs.biff5.xls', {bookType:'biff5'}],
['sheetjs.biff2.xls', {bookType:'biff2'}],
['sheetjs.xml.xls', {bookType:'xlml'}], ['sheetjs.xml.xls', {bookType:'xlml'}],
['sheetjs.ods'], ['sheetjs.ods'],
['sheetjs.fods'], ['sheetjs.fods'],

2
types/index.d.ts vendored

@ -477,7 +477,7 @@ export type ExcelDataType = 'b' | 'n' | 'e' | 's' | 'd' | 'z';
* Type of generated workbook * Type of generated workbook
* @default 'xlsx' * @default 'xlsx'
*/ */
export type BookType = 'xlsx' | 'xlsm' | 'xlsb' | 'xls' | 'biff8' | 'biff2' | 'xlml' | 'ods' | 'fods' | 'csv' | 'txt' | 'sylk' | 'html' | 'dif' | 'rtf' | 'prn'; export type BookType = 'xlsx' | 'xlsm' | 'xlsb' | 'xls' | 'biff8' | 'biff5' | 'biff2' | 'xlml' | 'ods' | 'fods' | 'csv' | 'txt' | 'sylk' | 'html' | 'dif' | 'rtf' | 'prn';
/** Comment element */ /** Comment element */
export interface Comment { export interface Comment {

@ -5,7 +5,7 @@
let data: any[][] = [ let data: any[][] = [
[1, 2, 3], [1, 2, 3],
[true, false, null, "sheetjs"], [true, false, null, "sheetjs"],
["foo", "bar", new Date("2014-02-19T14:30Z"), "0.3"], ["foo bar", "baz", new Date("2014-02-19T14:30Z"), "0.3"],
["baz", null, "qux", 3.14159], ["baz", null, "qux", 3.14159],
["hidden"], ["hidden"],
["visible"] ["visible"]
@ -150,8 +150,9 @@ const filenames: Array<[string]|[string, XLSX.WritingOptions]> = [
['sheetjs.xlsx', {bookSST:true}], ['sheetjs.xlsx', {bookSST:true}],
['sheetjs.xlsm'], ['sheetjs.xlsm'],
['sheetjs.xlsb'], ['sheetjs.xlsb'],
['sheetjs.xls', {bookType:'xls'}], ['sheetjs.biff8.xls', {bookType:'xls'}],
['sheetjs.biff2', {bookType:'biff2'}], ['sheetjs.biff5.xls', {bookType:'biff5'}],
['sheetjs.biff2.xls', {bookType:'biff2'}],
['sheetjs.xml.xls', {bookType:'xlml'}], ['sheetjs.xml.xls', {bookType:'xlml'}],
['sheetjs.ods'], ['sheetjs.ods'],
['sheetjs.fods'], ['sheetjs.fods'],
@ -160,6 +161,7 @@ const filenames: Array<[string]|[string, XLSX.WritingOptions]> = [
['sheetjs.slk'], ['sheetjs.slk'],
['sheetjs.htm'], ['sheetjs.htm'],
['sheetjs.dif'], ['sheetjs.dif'],
['sheetjs.rtf'],
['sheetjs.prn'] ['sheetjs.prn']
]; ];

@ -10,9 +10,7 @@ const otherworkbook: XLSX.WorkBook = XLSX.readFile('test.xlsx', {type: 'file'});
const author: string = workbook.Props.Author; const author: string = workbook.Props.Author;
const firstsheet: string = workbook.SheetNames[0]; const firstsheet: string = workbook.SheetNames[0];
const firstworksheet: XLSX.WorkSheet = workbook.Sheets[firstsheet]; const firstworksheet: XLSX.WorkSheet = workbook.Sheets[firstsheet];
const WB1A1: XLSX.CellObject = (firstworksheet["A1"]); const WB1A1: XLSX.CellObject = (firstworksheet["A1"]);
interface Tester { interface Tester {
@ -41,3 +39,8 @@ const WBSheet0 = WBSheets[0];
console.log(WBSheet0.Hidden); console.log(WBSheet0.Hidden);
const fmt14 = XLSX.SSF._table[14]; const fmt14 = XLSX.SSF._table[14];
const newwb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(newwb, aoa2, "AOA");
XLSX.utils.book_append_sheet(newwb, js2ws, "JSON");
const bstrxlsx: string = XLSX.write(newwb, {type: "binary", bookType: "xlsx" });

File diff suppressed because it is too large Load Diff

755
xlsx.js

File diff suppressed because it is too large Load Diff