SYLK shared formulae

This commit is contained in:
SheetJS 2021-08-10 22:53:38 -04:00
parent c56347eff5
commit 81b7614e45
19 changed files with 550 additions and 348 deletions

3
.gitattributes vendored

@ -1,3 +1,6 @@
*.*s linguist-documentation
*.html linguist-documentation
*.md text eol=lf
bits/*.js text eol=lf
test.js text eol=lf

@ -1,4 +1,4 @@
# [SheetJS js-xlsx](http://sheetjs.com)
# [SheetJS](http://sheetjs.com)
Parser and writer for various spreadsheet formats. Pure-JS cleanroom
implementation from official specifications, related documents, and test files.
@ -36,7 +36,6 @@ enhancements, additional features like styling, and dedicated support.
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://travis-ci.org/SheetJS/sheetjs.svg?branch=master)](https://travis-ci.org/SheetJS/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs)
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs)
@ -258,7 +257,7 @@ be configured to remove support with `resolve.alias`:
### 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).
[shims to provide missing functions](http://oss.sheetjs.com/sheetjs/shim.js).
To use the shim, add the shim before the script tag that loads `xlsx.js`:
@ -283,7 +282,7 @@ Third-party libraries either supported one format, or they involved a separate
set of classes for each supported file type. Even though XLSB was introduced in
Excel 2007, nothing outside of SheetJS or Excel supported the format.
To promote a format-agnostic view, js-xlsx starts from a pure-JS representation
To promote a format-agnostic view, SheetJS starts from a pure-JS representation
that we call the ["Common Spreadsheet Format"](#common-spreadsheet-format).
Emphasizing a uniform object representation enables new features like format
conversion (reading an XLSX template and saving as XLS) and circumvents the mess
@ -389,7 +388,7 @@ var workbook = XLSX.read(htmlstr, {type:'string'});
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>. The [`xhr` demo](demos/xhr/)
at <http://oss.sheetjs.com/sheetjs/ajax.html>. The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -465,7 +464,7 @@ More specialized cases, including mobile app file processing, are covered in the
### Parsing Examples
- <http://oss.sheetjs.com/js-xlsx/> HTML5 File API / Base64 Text / Web Workers
- <http://oss.sheetjs.com/sheetjs/> HTML5 File API / Base64 Text / Web Workers
Note that older versions of IE do not support HTML5 File API, so the Base64 mode
is used for testing.
@ -489,7 +488,7 @@ On Windows XP and up you can get the Base64 encoding using `certutil`:
</details>
- <http://oss.sheetjs.com/js-xlsx/ajax.html> XMLHttpRequest
- <http://oss.sheetjs.com/sheetjs/ajax.html> XMLHttpRequest
### Streaming Read
@ -627,7 +626,7 @@ error if the workbook is empty.
- <http://sheetjs.com/demos/modify.html> read + modify + write files
- <https://github.com/SheetJS/js-xlsx/blob/master/bin/xlsx.njs> node
- <https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs> node
The node version installs a command line tool `xlsx` which can read spreadsheet
files and output the contents in various formats. The source is available at
@ -882,7 +881,7 @@ Utilities are available in the `XLSX.utils` object and are described in the
## Common Spreadsheet Format
js-xlsx conforms to the Common Spreadsheet Format (CSF):
SheetJS conforms to the Common Spreadsheet Format (CSF):
### General Structures
@ -1605,7 +1604,7 @@ The visibility setting is stored in the `Hidden` property of sheet props array.
| 1 | Hidden |
| 2 | Very Hidden |
With <https://rawgit.com/SheetJS/test_files/master/sheet_visibility.xlsx>:
With <https://rawgit.com/SheetJS/test_files/HEAD/sheet_visibility.xlsx>:
```js
> wb.Workbook.Sheets.map(function(x) { return [x.name, x.Hidden] })
@ -2728,14 +2727,11 @@ $ open -a Chromium.app http://localhost:8000/stress.html
- Safari 6+ (iOS and Desktop)
- Edge 13+, FF 18+, and Opera 12+
Tests utilize the mocha testing framework. Travis-CI and Sauce Labs links:
Tests utilize the mocha testing framework.
- <https://travis-ci.org/SheetJS/js-xlsx> for XLSX module in nodejs
- <https://semaphoreci.com/sheetjs/js-xlsx> for XLSX module in nodejs
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS\* modules
- <https://saucelabs.com/u/sheetjs> for XLS\* modules using Sauce Labs
The Travis-CI test suite also includes tests for various time zones. To change
The test suite also includes tests for various time zones. To change
the timezone locally, set the TZ environment variable:
```bash

@ -35,7 +35,9 @@ var dbf_codepage_map = {
/*::[*/0x4D/*::]*/: 936, /*::[*/0x4E/*::]*/: 949,
/*::[*/0x4F/*::]*/: 950, /*::[*/0x50/*::]*/: 874,
/*::[*/0x57/*::]*/: 1252, /*::[*/0x58/*::]*/: 1252,
/*::[*/0x59/*::]*/: 1252,
/*::[*/0x59/*::]*/: 1252, /*::[*/0x6C/*::]*/: 863,
/*::[*/0x86/*::]*/: 737, /*::[*/0x87/*::]*/: 852,
/*::[*/0x88/*::]*/: 857, /*::[*/0xCC/*::]*/: 1257,
/*::[*/0xFF/*::]*/: 16969
};
@ -59,7 +61,6 @@ var DBF_SUPPORTED_VERSIONS = [0x02, 0x03, 0x30, 0x31, 0x83, 0x8B, 0x8C, 0xF5];
/* TODO: find an actual specification */
function dbf_to_aoa(buf, opts)/*:AOA*/ {
var out/*:AOA*/ = [];
/* TODO: browser based */
var d/*:Block*/ = (new_raw_buf(1)/*:any*/);
switch(opts.type) {
case 'base64': d = s2a(Base64.decode(buf)); break;
@ -68,44 +69,55 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
case 'array': d = buf; break;
}
prep_blob(d, 0);
/* header */
var ft = d.read_shift(1);
var memo = false;
var memo = !!(ft & 0x88);
var vfp = false, l7 = false;
switch(ft) {
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
case 0x8B: memo = true; break;
case 0x8C: memo = true; l7 = true; break;
case 0xF5: memo = true; break;
case 0x02: break; // dBASE II
case 0x03: break; // dBASE III
case 0x30: vfp = true; memo = true; break; // VFP
case 0x31: vfp = true; memo = true; break; // VFP with autoincrement
// 0x43 dBASE IV SQL table files
// 0x63 dBASE IV SQL system files
case 0x83: break; // dBASE III with memo
case 0x8B: break; // dBASE IV with memo
case 0x8C: l7 = true; break; // dBASE Level 7 with memo
// case 0xCB dBASE IV SQL table files with memo
case 0xF5: break; // FoxPro 2.x with memo
// case 0xFB FoxBASE
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var /*filedate = new Date(),*/ nrow = 0, fpos = 0;
var nrow = 0, fpos = 0x0209;
if(ft == 0x02) nrow = d.read_shift(2);
/*filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));*/d.l += 3;
if(ft != 0x02) nrow = d.read_shift(4); if(nrow > 1048576) nrow = 1e6;
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l += 3; // dBASE II stores DDMMYY date, others use YYMMDD
if(ft != 0x02) nrow = d.read_shift(4);
if(nrow > 1048576) nrow = 1e6;
if(ft != 0x02) fpos = d.read_shift(2); // header length
var rlen = d.read_shift(2); // record length
var /*flags = 0,*/ current_cp = opts.codepage || 1252;
if(ft != 0x02) {
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
if(ft != 0x02) { // 20 reserved bytes
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
/* codepage present in FoxPro */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
/* codepage present in FoxPro and dBASE Level 7 */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
d.l+=2;
}
if(l7) d.l += 36;
if(l7) d.l += 36; // Level 7: 32 byte "Language driver name", 4 byte reserved
/*:: type DBFField = { name:string; len:number; type:string; } */
var fields/*:Array<DBFField>*/ = [], field/*:DBFField*/ = ({}/*:any*/);
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) {
var hend = Math.min(d.length, (ft == 0x02 ? 0x209 : (fpos - 10 - (vfp ? 264 : 0))));
var ww = l7 ? 32 : 11;
while(d.l < hend && d[d.l] != 0x0d) {
field = ({}/*:any*/);
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)).replace(/[\u0000\r\n].*$/g,"");
d.l += ww;
@ -117,42 +129,45 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
if(field.name.length) fields.push(field);
if(ft != 0x02) d.l += l7 ? 13 : 14;
switch(field.type) {
case 'B': // VFP Double
case 'B': // Double (VFP) / Binary (dBASE L7)
if((!vfp || field.len != 8) && opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'G': // General
case 'P': // Picture
case 'G': // General (FoxPro and dBASE L7)
case 'P': // Picture (FoxPro and dBASE L7)
if(opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'C': // character
case 'D': // date
case 'F': // floating point
case 'I': // long
case 'L': // boolean
case 'M': // memo
case 'N': // number
case 'O': // double
case 'T': // datetime
case 'Y': // currency
case '0': // VFP _NullFlags
case '@': // timestamp
case '+': // autoincrement
case '+': // Autoincrement (dBASE L7 only)
case '0': // _NullFlags (VFP only)
case '@': // Timestamp (dBASE L7 only)
case 'C': // Character (dBASE II)
case 'D': // Date (dBASE III)
case 'F': // Float (dBASE IV)
case 'I': // Long (VFP and dBASE L7)
case 'L': // Logical (dBASE II)
case 'M': // Memo (dBASE III)
case 'N': // Number (dBASE II)
case 'O': // Double (dBASE L7 only)
case 'T': // Datetime (VFP only)
case 'Y': // Currency (VFP only)
break;
default: throw new Error('Unknown Field Type: ' + field.type);
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
/* data */
var R = 0, C = 0;
out[0] = [];
for(C = 0; C != fields.length; ++C) out[0][C] = fields[C].name;
while(nrow-- > 0) {
if(d[d.l] === 0x2A) { d.l+=rlen; continue; }
if(d[d.l] === 0x2A) {
// TODO: record marked as deleted -- create a hidden row?
d.l+=rlen;
continue;
}
++d.l;
out[++R] = []; C = 0;
for(C = 0; C != fields.length; ++C) {
@ -161,8 +176,8 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
var s = cptable.utils.decode(current_cp, dd);
switch(fields[C].type) {
case 'C':
out[R][C] = cptable.utils.decode(current_cp, dd);
out[R][C] = out[R][C].trim();
// NOTE: it is conventional to write ' / / ' for empty dates
if(s.trim().length) out[R][C] = s.replace(/\s+$/,"");
break;
case 'D':
if(s.length === 8) out[R][C] = new Date(+s.slice(0,4), +s.slice(4,6)-1, +s.slice(6,8));
@ -170,18 +185,24 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
break;
case 'F': out[R][C] = parseFloat(s.trim()); 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.trim().toUpperCase()) {
case 'Y': case 'T': out[R][C] = true; break;
case 'N': case 'F': out[R][C] = false; break;
case ' ': case '?': out[R][C] = false; break; /* NOTE: technically uninitialized */
case '': case '?': break;
default: throw new Error("DBF Unrecognized L:|" + s + "|");
} break;
case 'M': /* TODO: handle memo files */
if(!memo) throw new Error("DBF Unexpected MEMO for type " + ft.toString(16));
out[R][C] = "##MEMO##" + (l7 ? parseInt(s.trim(), 10): dd.read_shift(4));
break;
case 'N': out[R][C] = +s.replace(/\u0000/g,"").trim(); break;
case '@': out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400); break;
case 'N':
s = s.replace(/\u0000/g,"").trim();
// NOTE: dBASE II interprets " . " as 0
if(s && s != ".") out[R][C] = +s || 0; break;
case '@':
// NOTE: dBASE specs appear to be incorrect
out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400);
break;
case 'T': out[R][C] = new Date((dd.read_shift(4) - 0x253D8C) * 0x5265C00 + dd.read_shift(4)); break;
case 'Y': out[R][C] = dd.read_shift(4,'i')/1e4; break;
case 'O': out[R][C] = -dd.read_shift(-8, 'f'); break;
@ -374,7 +395,7 @@ var SYLK = (function() {
formats.push(rstr.slice(3).replace(/;;/g, ";"));
break;
case 'C':
var C_seen_K = false, C_seen_X = false;
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1;
for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
case 'X': C = parseInt(record[rj].slice(1))-1; C_seen_X = true; break;
case 'Y':
@ -396,12 +417,30 @@ var SYLK = (function() {
C_seen_K = true;
break;
case 'E':
C_seen_E = true;
var formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
arr[R][C] = [arr[R][C], formula];
break;
case 'S':
C_seen_S = true;
arr[R][C] = [arr[R][C], "S5S"];
break;
case 'G': break; // unknown
case 'R': _R = parseInt(record[rj].slice(1))-1; break;
case 'C': _C = parseInt(record[rj].slice(1))-1; break;
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
}
if(C_seen_K) { arr[R][C] = val; next_cell_format = null; }
if(C_seen_K) {
if(arr[R][C] && arr[R][C].length == 2) arr[R][C][0] = val;
else arr[R][C] = val;
next_cell_format = null;
}
if(C_seen_S) {
if(C_seen_E) throw new Error("SYLK shared formula cannot have own formula");
var shrbase = _R > -1 && arr[_R][_C];
if(!shrbase || !shrbase[1]) throw new Error("SYLK shared formula cannot find base");
arr[R][C][1] = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
}
break;
case 'F':
var F_seen = 0;
@ -736,9 +775,9 @@ var ETH = (function() {
var PRN = (function() {
function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/, o/*:any*/) {
if(o.raw) arr[R][C] = data;
else if(data === ""){/* empty */}
else if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){/* empty */}
else if(!isNaN(fuzzynum(data))) arr[R][C] = fuzzynum(data);
else if(!isNaN(fuzzydate(data).getDate())) arr[R][C] = parseDate(data);
else arr[R][C] = data;
@ -897,7 +936,7 @@ var PRN = (function() {
default: throw new Error("Unrecognized type " + opts.type);
}
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str.slice(3));
else if((opts.type == 'binary') && typeof cptable !== 'undefined' && opts.codepage) str = cptable.utils.decode(opts.codepage, cptable.utils.encode(1252,str));
else if((opts.type == 'binary') && typeof cptable !== 'undefined' && opts.codepage) str = cptable.utils.decode(opts.codepage, cptable.utils.encode(28591,str));
if(str.slice(0,19) == "socialcalc:version:") return ETH.to_sheet(opts.type == 'string' ? str : utf8read(str), opts);
return prn_to_sheet_str(str, opts);
}

@ -85,7 +85,10 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
case 0xD0: if(n[1] === 0xCF && n[2] === 0x11 && n[3] === 0xE0 && n[4] === 0xA1 && n[5] === 0xB1 && n[6] === 0x1A && n[7] === 0xE1) return read_cfb(CFB.read(d, o), o); break;
case 0x09: if(n[1] <= 0x04) return parse_xlscfb(d, o); break;
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] === 0x49 && n[2] === 0x2a && n[3] === 0x00) throw new Error("TIFF Image File is not a spreadsheet");
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 0x50: return (n[1] === 0x4B && n[2] < 0x09 && n[3] < 0x09) ? read_zip(d, o) : read_prn(data, d, o, str);
case 0xEF: return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);

@ -116,8 +116,8 @@ in the ZIP file.
When reading form data, be sure to include the necessary binary types on the AWS API Gateway console.
To do this, navigate to the "Binary Media Types" section in the settings tab of the console.
For reading a file, you may need to add "multipart/form-data".
For downloading a file, you may need to add "application/vnd.ms-excel".
For reading a file, you may need to add `"multipart/form-data"`.
For downloading a file, you may need to add `"application/vnd.ms-excel"`.
#### Azure Functions

7
dist/cpexcel.js generated vendored

@ -1,6 +1,6 @@
/* cpexcel.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*! cpexcel.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint -W100 */
var cptable = {version:"1.14.0"};
var cptable = {version:"1.15.0"};
cptable[437] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
cptable[620] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàąçêëèïîćÄĄĘęłôöĆûùŚÖÜ¢Ł¥śƒŹŻóÓńŃźż¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
cptable[737] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρσςτυφχψ░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀ωάέήϊίόύϋώΆΈΉΊΌΎΏ±≥≤ΪΫ÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
@ -973,9 +973,10 @@ return {"enc": e, "dec": d }; })();
cptable[10029] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
cptable[10079] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüÝ°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ—“”÷◊ÿŸ¤ÐðÞþý·„‰ÂÊÁËÈÍÎÏÌÓÔ<C393>ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
cptable[10081] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ—“”÷◊ÿŸĞğİıŞş‡·„‰ÂÊÁËÈÍÎÏÌÓÔ<C393>ÒÚÛÙ<C39B>ˆ˜¯˘˙˚¸˝˛ˇ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
cptable[28591] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
// eslint-disable-next-line no-undef
if (typeof module !== 'undefined' && module.exports && typeof DO_NOT_EXPORT_CODEPAGE === 'undefined') module.exports = cptable;
/* cputils.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*! cputils.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ft=javascript: */
/*jshint newcap: false */
(function(root, factory) {

@ -1,4 +1,4 @@
# [SheetJS js-xlsx](http://sheetjs.com)
# [SheetJS](http://sheetjs.com)
Parser and writer for various spreadsheet formats. Pure-JS cleanroom
implementation from official specifications, related documents, and test files.
@ -36,7 +36,6 @@ enhancements, additional features like styling, and dedicated support.
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://travis-ci.org/SheetJS/sheetjs.svg?branch=master)](https://travis-ci.org/SheetJS/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs)
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs)

@ -34,7 +34,7 @@ be configured to remove support with `resolve.alias`:
### 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).
[shims to provide missing functions](http://oss.sheetjs.com/sheetjs/shim.js).
To use the shim, add the shim before the script tag that loads `xlsx.js`:

@ -8,7 +8,7 @@ Third-party libraries either supported one format, or they involved a separate
set of classes for each supported file type. Even though XLSB was introduced in
Excel 2007, nothing outside of SheetJS or Excel supported the format.
To promote a format-agnostic view, js-xlsx starts from a pure-JS representation
To promote a format-agnostic view, SheetJS starts from a pure-JS representation
that we call the ["Common Spreadsheet Format"](#common-spreadsheet-format).
Emphasizing a uniform object representation enables new features like format
conversion (reading an XLSX template and saving as XLS) and circumvents the mess

@ -75,7 +75,7 @@ var workbook = XLSX.read(htmlstr, {type:'string'});
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>. The [`xhr` demo](demos/xhr/)
at <http://oss.sheetjs.com/sheetjs/ajax.html>. The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -151,7 +151,7 @@ More specialized cases, including mobile app file processing, are covered in the
### Parsing Examples
- <http://oss.sheetjs.com/js-xlsx/> HTML5 File API / Base64 Text / Web Workers
- <http://oss.sheetjs.com/sheetjs/> HTML5 File API / Base64 Text / Web Workers
Note that older versions of IE do not support HTML5 File API, so the Base64 mode
is used for testing.
@ -175,5 +175,5 @@ On Windows XP and up you can get the Base64 encoding using `certutil`:
</details>
- <http://oss.sheetjs.com/js-xlsx/ajax.html> XMLHttpRequest
- <http://oss.sheetjs.com/sheetjs/ajax.html> XMLHttpRequest

@ -67,7 +67,7 @@ error if the workbook is empty.
- <http://sheetjs.com/demos/modify.html> read + modify + write files
- <https://github.com/SheetJS/js-xlsx/blob/HEAD/bin/xlsx.njs> node
- <https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs> node
The node version installs a command line tool `xlsx` which can read spreadsheet
files and output the contents in various formats. The source is available at

@ -1,6 +1,6 @@
## Common Spreadsheet Format
js-xlsx conforms to the Common Spreadsheet Format (CSF):
SheetJS conforms to the Common Spreadsheet Format (CSF):
### General Structures

@ -71,14 +71,11 @@ $ open -a Chromium.app http://localhost:8000/stress.html
- Safari 6+ (iOS and Desktop)
- Edge 13+, FF 18+, and Opera 12+
Tests utilize the mocha testing framework. Travis-CI and Sauce Labs links:
Tests utilize the mocha testing framework.
- <https://travis-ci.org/SheetJS/js-xlsx> for XLSX module in nodejs
- <https://semaphoreci.com/sheetjs/js-xlsx> for XLSX module in nodejs
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS\* modules
- <https://saucelabs.com/u/sheetjs> for XLS\* modules using Sauce Labs
The Travis-CI test suite also includes tests for various time zones. To change
The test suite also includes tests for various time zones. To change
the timezone locally, set the TZ environment variable:
```bash

@ -1,4 +1,4 @@
# [SheetJS js-xlsx](http://sheetjs.com)
# [SheetJS](http://sheetjs.com)
Parser and writer for various spreadsheet formats. Pure-JS cleanroom
implementation from official specifications, related documents, and test files.
@ -33,7 +33,6 @@ enhancements, additional features like styling, and dedicated support.
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
[![Build Status](https://travis-ci.org/SheetJS/sheetjs.svg?branch=master)](https://travis-ci.org/SheetJS/sheetjs)
[![Build Status](https://semaphoreci.com/api/v1/sheetjs/sheetjs/branches/master/shields_badge.svg)](https://semaphoreci.com/sheetjs/sheetjs)
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/sheetjs/master.svg)](https://coveralls.io/r/SheetJS/sheetjs?branch=master)
[![Dependencies Status](https://david-dm.org/sheetjs/sheetjs/status.svg)](https://david-dm.org/sheetjs/sheetjs)
@ -246,7 +245,7 @@ be configured to remove support with `resolve.alias`:
### 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).
[shims to provide missing functions](http://oss.sheetjs.com/sheetjs/shim.js).
To use the shim, add the shim before the script tag that loads `xlsx.js`:
@ -269,7 +268,7 @@ Third-party libraries either supported one format, or they involved a separate
set of classes for each supported file type. Even though XLSB was introduced in
Excel 2007, nothing outside of SheetJS or Excel supported the format.
To promote a format-agnostic view, js-xlsx starts from a pure-JS representation
To promote a format-agnostic view, SheetJS starts from a pure-JS representation
that we call the ["Common Spreadsheet Format"](#common-spreadsheet-format).
Emphasizing a uniform object representation enables new features like format
conversion (reading an XLSX template and saving as XLS) and circumvents the mess
@ -363,7 +362,7 @@ var workbook = XLSX.read(htmlstr, {type:'string'});
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>. The [`xhr` demo](demos/xhr/)
at <http://oss.sheetjs.com/sheetjs/ajax.html>. The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -432,7 +431,7 @@ More specialized cases, including mobile app file processing, are covered in the
### Parsing Examples
- <http://oss.sheetjs.com/js-xlsx/> HTML5 File API / Base64 Text / Web Workers
- <http://oss.sheetjs.com/sheetjs/> HTML5 File API / Base64 Text / Web Workers
Note that older versions of IE do not support HTML5 File API, so the Base64 mode
is used for testing.
@ -453,7 +452,7 @@ On Windows XP and up you can get the Base64 encoding using `certutil`:
(note: You have to open the file and remove the header and footer lines)
- <http://oss.sheetjs.com/js-xlsx/ajax.html> XMLHttpRequest
- <http://oss.sheetjs.com/sheetjs/ajax.html> XMLHttpRequest
### Streaming Read
@ -573,7 +572,7 @@ error if the workbook is empty.
- <http://sheetjs.com/demos/modify.html> read + modify + write files
- <https://github.com/SheetJS/js-xlsx/blob/master/bin/xlsx.njs> node
- <https://github.com/SheetJS/sheetjs/blob/HEAD/bin/xlsx.njs> node
The node version installs a command line tool `xlsx` which can read spreadsheet
files and output the contents in various formats. The source is available at
@ -804,7 +803,7 @@ Utilities are available in the `XLSX.utils` object and are described in the
## Common Spreadsheet Format
js-xlsx conforms to the Common Spreadsheet Format (CSF):
SheetJS conforms to the Common Spreadsheet Format (CSF):
### General Structures
@ -1471,7 +1470,7 @@ The visibility setting is stored in the `Hidden` property of sheet props array.
| 1 | Hidden |
| 2 | Very Hidden |
With <https://rawgit.com/SheetJS/test_files/master/sheet_visibility.xlsx>:
With <https://rawgit.com/SheetJS/test_files/HEAD/sheet_visibility.xlsx>:
```js
> wb.Workbook.Sheets.map(function(x) { return [x.name, x.Hidden] })
@ -2486,14 +2485,11 @@ $ open -a Chromium.app http://localhost:8000/stress.html
- Safari 6+ (iOS and Desktop)
- Edge 13+, FF 18+, and Opera 12+
Tests utilize the mocha testing framework. Travis-CI and Sauce Labs links:
Tests utilize the mocha testing framework.
- <https://travis-ci.org/SheetJS/js-xlsx> for XLSX module in nodejs
- <https://semaphoreci.com/sheetjs/js-xlsx> for XLSX module in nodejs
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS\* modules
- <https://saucelabs.com/u/sheetjs> for XLS\* modules using Sauce Labs
The Travis-CI test suite also includes tests for various time zones. To change
The test suite also includes tests for various time zones. To change
the timezone locally, set the TZ environment variable:
```bash

@ -34,7 +34,7 @@
"dependencies": {
"adler-32": "~1.2.0",
"cfb": "^1.1.4",
"codepage": "~1.14.0",
"codepage": "~1.15.0",
"commander": "~2.17.1",
"crc-32": "~1.2.0",
"exit-on-epipe": "~1.0.1",

@ -7035,7 +7035,9 @@ var dbf_codepage_map = {
/*::[*/0x4D/*::]*/: 936, /*::[*/0x4E/*::]*/: 949,
/*::[*/0x4F/*::]*/: 950, /*::[*/0x50/*::]*/: 874,
/*::[*/0x57/*::]*/: 1252, /*::[*/0x58/*::]*/: 1252,
/*::[*/0x59/*::]*/: 1252,
/*::[*/0x59/*::]*/: 1252, /*::[*/0x6C/*::]*/: 863,
/*::[*/0x86/*::]*/: 737, /*::[*/0x87/*::]*/: 852,
/*::[*/0x88/*::]*/: 857, /*::[*/0xCC/*::]*/: 1257,
/*::[*/0xFF/*::]*/: 16969
};
@ -7059,7 +7061,6 @@ var DBF_SUPPORTED_VERSIONS = [0x02, 0x03, 0x30, 0x31, 0x83, 0x8B, 0x8C, 0xF5];
/* TODO: find an actual specification */
function dbf_to_aoa(buf, opts)/*:AOA*/ {
var out/*:AOA*/ = [];
/* TODO: browser based */
var d/*:Block*/ = (new_raw_buf(1)/*:any*/);
switch(opts.type) {
case 'base64': d = s2a(Base64.decode(buf)); break;
@ -7068,44 +7069,55 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
case 'array': d = buf; break;
}
prep_blob(d, 0);
/* header */
var ft = d.read_shift(1);
var memo = false;
var memo = !!(ft & 0x88);
var vfp = false, l7 = false;
switch(ft) {
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
case 0x8B: memo = true; break;
case 0x8C: memo = true; l7 = true; break;
case 0xF5: memo = true; break;
case 0x02: break; // dBASE II
case 0x03: break; // dBASE III
case 0x30: vfp = true; memo = true; break; // VFP
case 0x31: vfp = true; memo = true; break; // VFP with autoincrement
// 0x43 dBASE IV SQL table files
// 0x63 dBASE IV SQL system files
case 0x83: break; // dBASE III with memo
case 0x8B: break; // dBASE IV with memo
case 0x8C: l7 = true; break; // dBASE Level 7 with memo
// case 0xCB dBASE IV SQL table files with memo
case 0xF5: break; // FoxPro 2.x with memo
// case 0xFB FoxBASE
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var /*filedate = new Date(),*/ nrow = 0, fpos = 0;
var nrow = 0, fpos = 0x0209;
if(ft == 0x02) nrow = d.read_shift(2);
/*filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));*/d.l += 3;
if(ft != 0x02) nrow = d.read_shift(4); if(nrow > 1048576) nrow = 1e6;
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l += 3; // dBASE II stores DDMMYY date, others use YYMMDD
if(ft != 0x02) nrow = d.read_shift(4);
if(nrow > 1048576) nrow = 1e6;
var /*flags = 0,*/ current_cp = 1252;
if(ft != 0x02) {
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
if(ft != 0x02) fpos = d.read_shift(2); // header length
var rlen = d.read_shift(2); // record length
/* codepage present in FoxPro */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
var /*flags = 0,*/ current_cp = opts.codepage || 1252;
if(ft != 0x02) { // 20 reserved bytes
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
d.l+=2;
/* codepage present in FoxPro and dBASE Level 7 */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
}
if(l7) d.l += 36;
if(l7) d.l += 36; // Level 7: 32 byte "Language driver name", 4 byte reserved
/*:: type DBFField = { name:string; len:number; type:string; } */
var fields/*:Array<DBFField>*/ = [], field/*:DBFField*/ = ({}/*:any*/);
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) {
var hend = Math.min(d.length, (ft == 0x02 ? 0x209 : (fpos - 10 - (vfp ? 264 : 0))));
var ww = l7 ? 32 : 11;
while(d.l < hend && d[d.l] != 0x0d) {
field = ({}/*:any*/);
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)).replace(/[\u0000\r\n].*$/g,"");
d.l += ww;
@ -7117,42 +7129,45 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
if(field.name.length) fields.push(field);
if(ft != 0x02) d.l += l7 ? 13 : 14;
switch(field.type) {
case 'B': // VFP Double
case 'B': // Double (VFP) / Binary (dBASE L7)
if((!vfp || field.len != 8) && opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'G': // General
case 'P': // Picture
case 'G': // General (FoxPro and dBASE L7)
case 'P': // Picture (FoxPro and dBASE L7)
if(opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'C': // character
case 'D': // date
case 'F': // floating point
case 'I': // long
case 'L': // boolean
case 'M': // memo
case 'N': // number
case 'O': // double
case 'T': // datetime
case 'Y': // currency
case '0': // VFP _NullFlags
case '@': // timestamp
case '+': // autoincrement
case '+': // Autoincrement (dBASE L7 only)
case '0': // _NullFlags (VFP only)
case '@': // Timestamp (dBASE L7 only)
case 'C': // Character (dBASE II)
case 'D': // Date (dBASE III)
case 'F': // Float (dBASE IV)
case 'I': // Long (VFP and dBASE L7)
case 'L': // Logical (dBASE II)
case 'M': // Memo (dBASE III)
case 'N': // Number (dBASE II)
case 'O': // Double (dBASE L7 only)
case 'T': // Datetime (VFP only)
case 'Y': // Currency (VFP only)
break;
default: throw new Error('Unknown Field Type: ' + field.type);
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
/* data */
var R = 0, C = 0;
out[0] = [];
for(C = 0; C != fields.length; ++C) out[0][C] = fields[C].name;
while(nrow-- > 0) {
if(d[d.l] === 0x2A) { d.l+=rlen; continue; }
if(d[d.l] === 0x2A) {
// TODO: record marked as deleted -- create a hidden row?
d.l+=rlen;
continue;
}
++d.l;
out[++R] = []; C = 0;
for(C = 0; C != fields.length; ++C) {
@ -7161,8 +7176,8 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
var s = cptable.utils.decode(current_cp, dd);
switch(fields[C].type) {
case 'C':
out[R][C] = cptable.utils.decode(current_cp, dd);
out[R][C] = out[R][C].trim();
// NOTE: it is conventional to write ' / / ' for empty dates
if(s.trim().length) out[R][C] = s.replace(/\s+$/,"");
break;
case 'D':
if(s.length === 8) out[R][C] = new Date(+s.slice(0,4), +s.slice(4,6)-1, +s.slice(6,8));
@ -7170,18 +7185,24 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ {
break;
case 'F': out[R][C] = parseFloat(s.trim()); 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.trim().toUpperCase()) {
case 'Y': case 'T': out[R][C] = true; break;
case 'N': case 'F': out[R][C] = false; break;
case ' ': case '?': out[R][C] = false; break; /* NOTE: technically uninitialized */
case '': case '?': break;
default: throw new Error("DBF Unrecognized L:|" + s + "|");
} break;
case 'M': /* TODO: handle memo files */
if(!memo) throw new Error("DBF Unexpected MEMO for type " + ft.toString(16));
out[R][C] = "##MEMO##" + (l7 ? parseInt(s.trim(), 10): dd.read_shift(4));
break;
case 'N': out[R][C] = +s.replace(/\u0000/g,"").trim(); break;
case '@': out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400); break;
case 'N':
s = s.replace(/\u0000/g,"").trim();
// NOTE: dBASE II interprets " . " as 0
if(s && s != ".") out[R][C] = +s || 0; break;
case '@':
// NOTE: dBASE specs appear to be incorrect
out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400);
break;
case 'T': out[R][C] = new Date((dd.read_shift(4) - 0x253D8C) * 0x5265C00 + dd.read_shift(4)); break;
case 'Y': out[R][C] = dd.read_shift(4,'i')/1e4; break;
case 'O': out[R][C] = -dd.read_shift(-8, 'f'); break;
@ -7374,7 +7395,7 @@ var SYLK = (function() {
formats.push(rstr.slice(3).replace(/;;/g, ";"));
break;
case 'C':
var C_seen_K = false, C_seen_X = false;
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1;
for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
case 'X': C = parseInt(record[rj].slice(1))-1; C_seen_X = true; break;
case 'Y':
@ -7396,12 +7417,30 @@ var SYLK = (function() {
C_seen_K = true;
break;
case 'E':
C_seen_E = true;
var formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
arr[R][C] = [arr[R][C], formula];
break;
case 'S':
C_seen_S = true;
arr[R][C] = [arr[R][C], "S5S"];
break;
case 'G': break; // unknown
case 'R': _R = parseInt(record[rj].slice(1))-1; break;
case 'C': _C = parseInt(record[rj].slice(1))-1; break;
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
}
if(C_seen_K) { arr[R][C] = val; next_cell_format = null; }
if(C_seen_K) {
if(arr[R][C] && arr[R][C].length == 2) arr[R][C][0] = val;
else arr[R][C] = val;
next_cell_format = null;
}
if(C_seen_S) {
if(C_seen_E) throw new Error("SYLK shared formula cannot have own formula");
var shrbase = _R > -1 && arr[_R][_C];
if(!shrbase || !shrbase[1]) throw new Error("SYLK shared formula cannot find base");
arr[R][C][1] = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
}
break;
case 'F':
var F_seen = 0;
@ -7736,9 +7775,9 @@ var ETH = (function() {
var PRN = (function() {
function set_text_arr(data/*:string*/, arr/*:AOA*/, R/*:number*/, C/*:number*/, o/*:any*/) {
if(o.raw) arr[R][C] = data;
else if(data === ""){/* empty */}
else if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){/* empty */}
else if(!isNaN(fuzzynum(data))) arr[R][C] = fuzzynum(data);
else if(!isNaN(fuzzydate(data).getDate())) arr[R][C] = parseDate(data);
else arr[R][C] = data;
@ -7897,7 +7936,7 @@ var PRN = (function() {
default: throw new Error("Unrecognized type " + opts.type);
}
if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str.slice(3));
else if((opts.type == 'binary') && typeof cptable !== 'undefined' && opts.codepage) str = cptable.utils.decode(opts.codepage, cptable.utils.encode(1252,str));
else if((opts.type == 'binary') && typeof cptable !== 'undefined' && opts.codepage) str = cptable.utils.decode(opts.codepage, cptable.utils.encode(28591,str));
if(str.slice(0,19) == "socialcalc:version:") return ETH.to_sheet(opts.type == 'string' ? str : utf8read(str), opts);
return prn_to_sheet_str(str, opts);
}
@ -21151,7 +21190,10 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
case 0xD0: if(n[1] === 0xCF && n[2] === 0x11 && n[3] === 0xE0 && n[4] === 0xA1 && n[5] === 0xB1 && n[6] === 0x1A && n[7] === 0xE1) return read_cfb(CFB.read(d, o), o); break;
case 0x09: if(n[1] <= 0x04) return parse_xlscfb(d, o); break;
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] === 0x49 && n[2] === 0x2a && n[3] === 0x00) throw new Error("TIFF Image File is not a spreadsheet");
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 0x50: return (n[1] === 0x4B && n[2] < 0x09 && n[3] < 0x09) ? read_zip(d, o) : read_prn(data, d, o, str);
case 0xEF: return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);

160
xlsx.js generated

@ -6938,7 +6938,9 @@ var dbf_codepage_map = {
0x4D: 936, 0x4E: 949,
0x4F: 950, 0x50: 874,
0x57: 1252, 0x58: 1252,
0x59: 1252,
0x59: 1252, 0x6C: 863,
0x86: 737, 0x87: 852,
0x88: 857, 0xCC: 1257,
0xFF: 16969
};
@ -6962,7 +6964,6 @@ var DBF_SUPPORTED_VERSIONS = [0x02, 0x03, 0x30, 0x31, 0x83, 0x8B, 0x8C, 0xF5];
/* TODO: find an actual specification */
function dbf_to_aoa(buf, opts) {
var out = [];
/* TODO: browser based */
var d = (new_raw_buf(1));
switch(opts.type) {
case 'base64': d = s2a(Base64.decode(buf)); break;
@ -6971,43 +6972,54 @@ function dbf_to_aoa(buf, opts) {
case 'array': d = buf; break;
}
prep_blob(d, 0);
/* header */
var ft = d.read_shift(1);
var memo = false;
var memo = !!(ft & 0x88);
var vfp = false, l7 = false;
switch(ft) {
case 0x02: case 0x03: break;
case 0x30: vfp = true; memo = true; break;
case 0x31: vfp = true; break;
case 0x83: memo = true; break;
case 0x8B: memo = true; break;
case 0x8C: memo = true; l7 = true; break;
case 0xF5: memo = true; break;
case 0x02: break; // dBASE II
case 0x03: break; // dBASE III
case 0x30: vfp = true; memo = true; break; // VFP
case 0x31: vfp = true; memo = true; break; // VFP with autoincrement
// 0x43 dBASE IV SQL table files
// 0x63 dBASE IV SQL system files
case 0x83: break; // dBASE III with memo
case 0x8B: break; // dBASE IV with memo
case 0x8C: l7 = true; break; // dBASE Level 7 with memo
// case 0xCB dBASE IV SQL table files with memo
case 0xF5: break; // FoxPro 2.x with memo
// case 0xFB FoxBASE
default: throw new Error("DBF Unsupported Version: " + ft.toString(16));
}
var /*filedate = new Date(),*/ nrow = 0, fpos = 0;
var nrow = 0, fpos = 0x0209;
if(ft == 0x02) nrow = d.read_shift(2);
/*filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));*/d.l += 3;
if(ft != 0x02) nrow = d.read_shift(4); if(nrow > 1048576) nrow = 1e6;
if(ft != 0x02) fpos = d.read_shift(2);
var rlen = d.read_shift(2);
d.l += 3; // dBASE II stores DDMMYY date, others use YYMMDD
if(ft != 0x02) nrow = d.read_shift(4);
if(nrow > 1048576) nrow = 1e6;
if(ft != 0x02) fpos = d.read_shift(2); // header length
var rlen = d.read_shift(2); // record length
var /*flags = 0,*/ current_cp = opts.codepage || 1252;
if(ft != 0x02) {
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
if(ft != 0x02) { // 20 reserved bytes
d.l+=16;
/*flags = */d.read_shift(1);
//if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
/* codepage present in FoxPro */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
/* codepage present in FoxPro and dBASE Level 7 */
if(d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
d.l+=1;
d.l+=2;
d.l+=2;
}
if(l7) d.l += 36;
if(l7) d.l += 36; // Level 7: 32 byte "Language driver name", 4 byte reserved
var fields = [], field = ({});
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) {
var hend = Math.min(d.length, (ft == 0x02 ? 0x209 : (fpos - 10 - (vfp ? 264 : 0))));
var ww = l7 ? 32 : 11;
while(d.l < hend && d[d.l] != 0x0d) {
field = ({});
field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l+ww)).replace(/[\u0000\r\n].*$/g,"");
d.l += ww;
@ -7019,42 +7031,45 @@ var fields = [], field = ({});
if(field.name.length) fields.push(field);
if(ft != 0x02) d.l += l7 ? 13 : 14;
switch(field.type) {
case 'B': // VFP Double
case 'B': // Double (VFP) / Binary (dBASE L7)
if((!vfp || field.len != 8) && opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'G': // General
case 'P': // Picture
case 'G': // General (FoxPro and dBASE L7)
case 'P': // Picture (FoxPro and dBASE L7)
if(opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
break;
case 'C': // character
case 'D': // date
case 'F': // floating point
case 'I': // long
case 'L': // boolean
case 'M': // memo
case 'N': // number
case 'O': // double
case 'T': // datetime
case 'Y': // currency
case '0': // VFP _NullFlags
case '@': // timestamp
case '+': // autoincrement
case '+': // Autoincrement (dBASE L7 only)
case '0': // _NullFlags (VFP only)
case '@': // Timestamp (dBASE L7 only)
case 'C': // Character (dBASE II)
case 'D': // Date (dBASE III)
case 'F': // Float (dBASE IV)
case 'I': // Long (VFP and dBASE L7)
case 'L': // Logical (dBASE II)
case 'M': // Memo (dBASE III)
case 'N': // Number (dBASE II)
case 'O': // Double (dBASE L7 only)
case 'T': // Datetime (VFP only)
case 'Y': // Currency (VFP only)
break;
default: throw new Error('Unknown Field Type: ' + field.type);
}
}
if(d[d.l] !== 0x0D) d.l = fpos-1;
else if(ft == 0x02) d.l = 0x209;
if(ft != 0x02) {
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
}
if(d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
d.l = fpos;
/* data */
var R = 0, C = 0;
out[0] = [];
for(C = 0; C != fields.length; ++C) out[0][C] = fields[C].name;
while(nrow-- > 0) {
if(d[d.l] === 0x2A) { d.l+=rlen; continue; }
if(d[d.l] === 0x2A) {
// TODO: record marked as deleted -- create a hidden row?
d.l+=rlen;
continue;
}
++d.l;
out[++R] = []; C = 0;
for(C = 0; C != fields.length; ++C) {
@ -7063,8 +7078,8 @@ var fields = [], field = ({});
var s = cptable.utils.decode(current_cp, dd);
switch(fields[C].type) {
case 'C':
out[R][C] = cptable.utils.decode(current_cp, dd);
out[R][C] = out[R][C].trim();
// NOTE: it is conventional to write ' / / ' for empty dates
if(s.trim().length) out[R][C] = s.replace(/\s+$/,"");
break;
case 'D':
if(s.length === 8) out[R][C] = new Date(+s.slice(0,4), +s.slice(4,6)-1, +s.slice(6,8));
@ -7072,18 +7087,24 @@ var fields = [], field = ({});
break;
case 'F': out[R][C] = parseFloat(s.trim()); break;