version bump 0.9.13: string formatting

- dateNF parse option controls interpretation of code 14
- SSF updated to 0.9.1
- SYLK write formulae
- DIF support Excel-style data storage
- ODS/FODS automatic styles for date formatting

Issues:
- Fixes #181 h/t @CharlesNo
- Fixes #200 h/t @JohnJeong123
- Fixes #208 h/t @jerryhe88
- Fixes #262 h/t @JohnJeong123
- Fixes #269 h/t @calebeaires
- Fixes #326 h/t @railty
- Fixes #392 h/t @FourLeafClover
- Fixes #449 h/t @dougschiller
- Fixes #560 h/t @dpackage
pull/309/merge v0.9.13
SheetJS 6 years ago
parent dcee744e4e
commit b9bc0a1627

@ -7,7 +7,7 @@
"plugins": [ "html", "json" ],
"rules": {
"no-use-before-define": [ 1, {
"functions":true, "classes":true, "variables":true
"functions":false, "classes":true, "variables":false
}],
"no-bitwise": 0,
"curly": 0,

@ -58,6 +58,7 @@ enhancements and additional features by request.
+ [Formulae](#formulae)
+ [Column Properties](#column-properties)
+ [Row Properties](#row-properties)
+ [Number Formats](#number-formats)
+ [Hyperlinks](#hyperlinks)
+ [Cell Comments](#cell-comments)
+ [Sheet Visibility](#sheet-visibility)
@ -898,6 +899,81 @@ follow the priority order:
1) use `hpx` pixel height if available
2) use `hpt` point height if available
#### Number Formats
The `cell.w` formatted text for each cell is produced from `cell.v` and `cell.z`
format. If the format is not specified, the Excel `General` format is used.
The format can either be specified as a string or as an index into the format
table. Parsers are expected to populate `workbook.SSF` with the number format
table. Writers are expected to serialize the table.
Custom tools should ensure that the local table has each used format string
somewhere in the table. Excel convention mandates that the custom formats start
at index 164. The following example creates a custom format from scratch:
```js
var tbl = {};
XLSX.SSF.init_table(tbl); // <-- load builtin formats
tbl[164] = "\"T\"\ #0.00";
var wb = {
SSF: tbl,
SheetNames: ["Sheet1"],
Sheets: {
Sheet1: {
"!ref":"A1:C1",
A1: { t:"n", v:10000 }, // <-- General format
B1: { t:"n", v:10000, z: tbl[4] }, // <-- Builtin format
C1: { t:"n", v:10000, z: tbl[164] } // <-- Custom format
}
}
}
```
The rules are slightly different from how Excel displays custom number formats.
In particular, literal characters must be wrapped in double quotes or preceded
by a backslash. For more info, see the Excel documentation article
[`Create or delete a custom number format`](https://support.office.com/en-us/article/78f2a361-936b-4c03-8772-09fab54be7f4)
or ECMA-376 18.8.31 (Number Formats)
The default formats are listed in ECMA-376 18.8.30:
| ID | Format |
|---:|:---------------------------|
| 0 | `General` |
| 1 | `0` |
| 2 | `0.00` |
| 3 | `#,##0` |
| 4 | `#,##0.00` |
| 9 | `0%` |
| 10 | `0.00%` |
| 11 | `0.00E+00` |
| 12 | `# ?/?` |
| 13 | `# ??/??` |
| 14 | `m/d/yy` (see below) |
| 15 | `d-mmm-yy` |
| 16 | `d-mmm` |
| 17 | `mmm-yy` |
| 18 | `h:mm AM/PM` |
| 19 | `h:mm:ss AM/PM` |
| 20 | `h:mm` |
| 21 | `h:mm:ss` |
| 22 | `m/d/yy h:mm` |
| 37 | `#,##0 ;(#,##0)` |
| 38 | `#,##0 ;[Red](#,##0)` |
| 39 | `#,##0.00;(#,##0.00)` |
| 40 | `#,##0.00;[Red](#,##0.00)` |
| 45 | `mm:ss` |
| 46 | `[h]:mm:ss` |
| 47 | `mmss.0` |
| 48 | `##0.0E+0` |
| 49 | `@` |
Format 14 (`m/d/yy`) is localized by Excel: even though the file specifies that
number format, it will be drawn differently based on system settings. It makes
sense when the producer and consumer of files are in the same locale, but that
is not always the case over the Internet. To get around this ambiguity, parse
functions accept the `dateNF` option to override the interpretation of that
specific format string.
#### Hyperlinks
Hyperlinks are stored in the `l` key of cell objects. The `Target` field of the
@ -976,6 +1052,7 @@ The exported `read` and `readFile` functions accept an options argument:
| cellStyles | false | Save style/theme info to the `.s` field |
| cellText | true | Generated formatted text to the `.w` field |
| cellDates | false | Store dates as type `d` (default is `n`) |
| dateNF | | If specified, use the string for date code 14 ** |
| sheetStubs | false | Create cell objects of type `z` for stub cells |
| sheetRows | 0 | If >0, read the first `sheetRows` rows ** |
| bookDeps | false | If true, parse calculation chains |
@ -1401,7 +1478,8 @@ specifications expand on serialization of features like properties.
Excel CSV deviates from RFC4180 in a number of important ways. The generated
CSV files should generally work in Excel although they may not work in RFC4180
compatible readers. The parser should generally understand Excel CSV.
compatible readers. The parser should generally understand Excel CSV. The
writer proactively generates cells for formulae if values are unavailable.
Excel TXT uses tab as the delimiter and codepage 1200.
@ -1428,7 +1506,8 @@ BIFF8 XLS.
ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to
SpreadsheetML. Both are detailed in the OASIS standard, but tools like LO/OO
add undocumented extensions.
add undocumented extensions. The parsers and writers do not implement the full
standard, instead focusing on parts necessary to extract and store raw data.
#### Uniform Office Spreadsheet (UOS1/2)
@ -1452,19 +1531,29 @@ limited by the general ability to read arbitrary files in the web browser.
#### Symbolic Link (SYLK)
There is no real documentation. All knowledge was gathered by saving files in
various versions of Excel to deduce the meaning of fields.
various versions of Excel to deduce the meaning of fields. Notes:
- Plain formulae are stored in the RC form.
- Column widths are rounded to integral characters.
#### Lotus Formatted Text (PRN)
There is no real documentation, and in fact Excel treats PRN as an output-only
file format. Nevertheless we can guess the column widths and reverse-engineer
the original layout.
the original layout. Excel's 240-character width limitation is not enforced.
#### Data Interchange Format (DIF)
There is no unified definition. Visicalc DIF differs from Lotus DIF, and both
differ from Excel DIF. Where ambiguous, the parser/writer follows the expected
behavior from Excel.
behavior from Excel. In particular, Excel extends DIF in incompatible ways:
- Since Excel automatically converts numbers-as-strings to numbers, numeric
string constants are converted to formulae: `"0.3" -> "=""0.3""`
- DIF technically expects numeric cells to hold the raw numeric data, but Excel
permits formatted numbers (including dates)
- DIF technically has no support for formulae, but Excel will automatically
convert plain formulae. Array formulae are not preserved.
#### HTML

@ -1 +1 @@
XLSX.version = '0.9.12';
XLSX.version = '0.9.13';

@ -0,0 +1,2 @@
var DENSE = null;
var DIF_XL = true;

@ -1 +0,0 @@
var DENSE = null;

@ -2,7 +2,7 @@
/*jshint -W041 */
var SSF = {};
var make_ssf = function make_ssf(SSF){
SSF.version = '0.9.0';
SSF.version = '0.9.1';
function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; }
function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
@ -23,38 +23,6 @@ function fixopts(o){
for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
}
SSF.opts = opts_fmt;
var table_fmt = {
/*::[*/0/*::]*/: 'General',
/*::[*/1/*::]*/: '0',
/*::[*/2/*::]*/: '0.00',
/*::[*/3/*::]*/: '#,##0',
/*::[*/4/*::]*/: '#,##0.00',
/*::[*/9/*::]*/: '0%',
/*::[*/10/*::]*/: '0.00%',
/*::[*/11/*::]*/: '0.00E+00',
/*::[*/12/*::]*/: '# ?/?',
/*::[*/13/*::]*/: '# ??/??',
/*::[*/14/*::]*/: 'm/d/yy',
/*::[*/15/*::]*/: 'd-mmm-yy',
/*::[*/16/*::]*/: 'd-mmm',
/*::[*/17/*::]*/: 'mmm-yy',
/*::[*/18/*::]*/: 'h:mm AM/PM',
/*::[*/19/*::]*/: 'h:mm:ss AM/PM',
/*::[*/20/*::]*/: 'h:mm',
/*::[*/21/*::]*/: 'h:mm:ss',
/*::[*/22/*::]*/: 'm/d/yy h:mm',
/*::[*/37/*::]*/: '#,##0 ;(#,##0)',
/*::[*/38/*::]*/: '#,##0 ;[Red](#,##0)',
/*::[*/39/*::]*/: '#,##0.00;(#,##0.00)',
/*::[*/40/*::]*/: '#,##0.00;[Red](#,##0.00)',
/*::[*/45/*::]*/: 'mm:ss',
/*::[*/46/*::]*/: '[h]:mm:ss',
/*::[*/47/*::]*/: 'mmss.0',
/*::[*/48/*::]*/: '##0.0E+0',
/*::[*/49/*::]*/: '@',
/*::[*/56/*::]*/: '"上午/δΈ‹εˆ "hh"ζ™‚"mm"εˆ†"ss"η§’ "',
/*::[*/65535/*::]*/: 'General'
};
var days/*:Array<Array<string> >*/ = [
['Sun', 'Sunday'],
['Mon', 'Monday'],
@ -78,7 +46,42 @@ var months/*:Array<Array<string> >*/ = [
['N', 'Nov', 'November'],
['D', 'Dec', 'December']
];
function frac(x, D, mixed) {
function init_table(t/*:any*/) {
t[0]= 'General';
t[1]= '0';
t[2]= '0.00';
t[3]= '#,##0';
t[4]= '#,##0.00';
t[9]= '0%';
t[10]= '0.00%';
t[11]= '0.00E+00';
t[12]= '# ?/?';
t[13]= '# ??/??';
t[14]= 'm/d/yy';
t[15]= 'd-mmm-yy';
t[16]= 'd-mmm';
t[17]= 'mmm-yy';
t[18]= 'h:mm AM/PM';
t[19]= 'h:mm:ss AM/PM';
t[20]= 'h:mm';
t[21]= 'h:mm:ss';
t[22]= 'm/d/yy h:mm';
t[37]= '#,##0 ;(#,##0)';
t[38]= '#,##0 ;[Red](#,##0)';
t[39]= '#,##0.00;(#,##0.00)';
t[40]= '#,##0.00;[Red](#,##0.00)';
t[45]= 'mm:ss';
t[46]= '[h]:mm:ss';
t[47]= 'mmss.0';
t[48]= '##0.0E+0';
t[49]= '@';
t[56]= '"上午/δΈ‹εˆ "hh"ζ™‚"mm"εˆ†"ss"η§’ "';
t[65535]= 'General';
}
var table_fmt = {};
init_table(table_fmt);
function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number>*/ {
var sgn = x < 0 ? -1 : 1;
var B = x * sgn;
var P_2 = 0, P_1 = 1, P = 0;
@ -88,15 +91,13 @@ function frac(x, D, mixed) {
A = Math.floor(B);
P = A * P_1 + P_2;
Q = A * Q_1 + Q_2;
if((B - A) < 0.0000000005) break;
if((B - A) < 0.00000005) break;
B = 1 / (B - A);
P_2 = P_1; P_1 = P;
Q_2 = Q_1; Q_1 = Q;
}
if(Q > D) { Q = Q_1; P = P_1; }
if(Q > D) { Q = Q_2; P = P_2; }
if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } }
if(!mixed) return [0, sgn * P, Q];
if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;
var q = Math.floor(sgn * P/Q);
return [q, sgn*P - q*Q, Q];
}
@ -138,6 +139,8 @@ function general_fmt(v/*:any*/, opts/*:?any*/) {
case 'string': return v;
case 'boolean': return v ? "TRUE" : "FALSE";
case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts);
case 'undefined': return "";
case 'object': if(v == null) return "";
}
throw new Error("unsupported value in General format: " + v);
}
@ -452,10 +455,10 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
if((r = fmt.match(frac1))) return write_num_f2(r, aval, sign);
if(fmt.match(/^#+0+$/)) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
if((r = fmt.match(dec1))) {
/*:: if(!Array.isArray(r)) throw "unreachable"; */
/*:: if(!Array.isArray(r)) throw new Error("unreachable"); */
o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]);
o = o.replace(/\.(\d*)$/,function($$, $1) {
/*:: if(!Array.isArray(r)) throw "unreachable"; */
/*:: if(!Array.isArray(r)) throw new Error("unreachable"); */
return "." + $1 + fill("0", r[1].length-$1.length); });
return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
}
@ -563,13 +566,13 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ {
case '.':
/* falls through */
case '0': case '#':
while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1));
while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){}
break;
case '?': while(fmt.charAt(++i) === c); break;
case '?': while(fmt.charAt(++i) === c){} break;
case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break;
case '(': case ')': ++i; break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1); break;
while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break;
case ' ': ++i; break;
default: ++i; break;
}
@ -625,7 +628,10 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};
} else { o=""; }
} else if(o.indexOf("$") > -1) {
o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$";
if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o};
}
break;
/* Numbers */
case '.':
@ -661,7 +667,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
/* falls through */
case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break;
case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break;
case 'X': if(out[i].v === "B2");
case 'X': /*if(out[i].v === "B2");*/
break;
case 'Z':
if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1;
@ -711,11 +717,20 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
}
var vv = "", myv, ostr;
if(nstr.length > 0) {
myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */
ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */
if(nstr.charCodeAt(0) == 40) /* '(' */ {
myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v);
ostr = write_num('(', nstr, myv);
} else {
myv = (v<0 && flen > 1 ? -v : v);
ostr = write_num('n', nstr, myv);
if(myv < 0 && out[0] && out[0].t == 't') {
ostr = ostr.substr(1);
out[0].v = "-" + out[0].v;
}
}
jj=ostr.length-1;
var decpt = out.length;
for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; }
for(i=0; i < out.length; ++i) if(out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) { decpt = i; break; }
var lasti=out.length;
if(decpt === out.length && ostr.indexOf("E") === -1) {
for(i=out.length-1; i>= 0;--i) {
@ -803,11 +818,18 @@ function choose_fmt(f/*:string*/, v) {
return [l, ff];
}
function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
fixopts(o != null ? o : (o=[]));
if(o == null) o = {};
//fixopts(o != null ? o : (o=[]));
var sfmt = "";
switch(typeof fmt) {
case "string": sfmt = fmt; break;
case "number": sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; break;
case "string":
if(fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF;
else sfmt = fmt;
break;
case "number":
if(fmt == 14 && o.dateNF) sfmt = o.dateNF;
else sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt];
break;
}
if(isgeneral(sfmt,0)) return general_fmt(v, o);
var f = choose_fmt(sfmt, v);
@ -821,5 +843,6 @@ SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx]
SSF.format = format;
SSF.get_table = function get_table() { return table_fmt; };
SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
SSF.init_table = init_table;
};
make_ssf(SSF);

@ -273,7 +273,9 @@ var SYLK = (function() {
function write_ws_cell_sylk(cell/*:Cell*/, ws/*:Worksheet*/, R/*:number*/, C/*:number*/, opts)/*:string*/ {
var o = "C;Y" + (R+1) + ";X" + (C+1) + ";K";
switch(cell.t) {
case 'n': o += cell.v; break;
case 'n':
o += (cell.v||0);
if(cell.f && !cell.F) o += ";E" + a1_to_rc(cell.f, {r:R, c:C}); break;
case 'b': o += cell.v ? "TRUE" : "FALSE"; break;
case 'e': o += cell.w || cell.v; break;
case 'd': o += '"' + (cell.w || cell.v) + '"'; break;
@ -320,7 +322,7 @@ var SYLK = (function() {
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
cell = dense ? (ws[R]||[])[C]: ws[coord];
if(!cell || cell.v == null) continue;
if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
}
}
@ -362,7 +364,7 @@ var DIF = (function() {
if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(+value == +value) arr[R][C] = +value;
else if(!isNaN(new Date(value).getDate())) arr[R][C] = new Date(value);
else if(!isNaN(new Date(value).getDate())) arr[R][C] = parseDate(value);
else arr[R][C] = value;
++C; break;
case 1:
@ -401,11 +403,28 @@ var DIF = (function() {
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
cell = dense ? (ws[R]||[])[C] : ws[coord];
if(!cell || cell.v == null) { push_value(o, 1, 0, ""); continue;}
if(!cell) { push_value(o, 1, 0, ""); continue;}
switch(cell.t) {
case 'n': push_value(o, 0, (/*cell.w ||*/ cell.v), "V"); break;
case 'b': push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE"); break;
case 's': push_value(o, 1, 0, cell.v); break;
case 'n':
var val = DIF_XL ? cell.w : cell.v;
if(!val && cell.v != null) val = cell.v;
if(val == null) {
if(DIF_XL && cell.f && !cell.F) push_value(o, 1, 0, "=" + cell.f);
else push_value(o, 1, 0, "");
}
else push_value(o, 0, val, "V");
break;
case 'b':
push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE");
break;
case 's':
push_value(o, 1, 0, (!DIF_XL || isNaN(cell.v)) ? cell.v : '="' + cell.v + '"');
break;
case 'd':
if(!cell.w) cell.w = SSF.format(cell.z || SSF._table[14], datenum(parseDate(cell.v)));
if(DIF_XL) push_value(o, 0, cell.w, "V");
else push_value(o, 1, 0, cell.w);
break;
default: push_value(o, 1, 0, "");
}
}

@ -775,6 +775,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
return wb;
}
/* TODO: WTF */
function parse_props(cfb) {
/* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
var DSI = cfb.find('!DocumentSummaryInformation');
if(DSI) try { cfb.DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI); } catch(e) {}
/* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
var SI = cfb.find('!SummaryInformation');
if(SI) try { cfb.Summary = parse_PropertySetStream(SI, SummaryPIDSI); } catch(e) {}
}
function parse_xlscfb(cfb/*:any*/, options/*:?ParseOpts*/)/*:Workbook*/ {
if(!options) options = {};
fix_read_opts(options);
@ -814,14 +825,3 @@ if(options.bookFiles) WorkbookP.cfb = cfb;
return WorkbookP;
}
/* TODO: WTF */
function parse_props(cfb) {
/* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
var DSI = cfb.find('!DocumentSummaryInformation');
if(DSI) try { cfb.DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI); } catch(e) {}
/* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
var SI = cfb.find('!SummaryInformation');
if(SI) try { cfb.Summary = parse_PropertySetStream(SI, SummaryPIDSI); } catch(e) {}
}

@ -1,9 +1,6 @@
var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var covered_cell_xml = ' <table:covered-table-cell/>\n';
var cell_begin = ' <table:table-cell ', cell_end = '</table:table-cell>\n';
var vt = 'office:value-type=';
var p_begin = '<text:p>', p_end = '</text:p>';
var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ {
/* Section 9 Tables */
var o = [];
@ -16,37 +13,55 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var skip = false, mxml = "";
var skip = false, ct = {}, textp = "";
for(mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c > C) continue;
if(marr[mi].s.r > R) continue;
if(marr[mi].e.c < C) continue;
if(marr[mi].e.r < R) continue;
if(marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
mxml = 'table:number-columns-spanned="' + (marr[mi].e.c - marr[mi].s.c + 1) + '" table:number-rows-spanned="' + (marr[mi].e.r - marr[mi].s.r + 1) + '" ';
ct['table:number-columns-spanned'] = (marr[mi].e.c - marr[mi].s.c + 1);
ct['table:number-rows-spanned'] = (marr[mi].e.r - marr[mi].s.r + 1);
break;
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = dense ? (ws[R]||[])[C]: ws[ref];
var fmla = "";
if(cell && cell.f) {
fmla = ' table:formula="' + escapexml(csf_to_ods_formula(cell.f)) + '"';
ct['table:formula'] = escapexml(csf_to_ods_formula(cell.f));
if(cell.F) {
if(cell.F.substr(0, ref.length) == ref) {
var _Fref = decode_range(cell.F);
fmla += ' table:number-matrix-columns-spanned="' + (_Fref.e.c - _Fref.s.c + 1)+ '"';
fmla += ' table:number-matrix-rows-spanned="' + (_Fref.e.r - _Fref.s.r + 1) + '"';
} else fmla = "";
ct['table:number-matrix-columns-spanned'] = (_Fref.e.c - _Fref.s.c + 1);
ct['table:number-matrix-rows-spanned'] = (_Fref.e.r - _Fref.s.r + 1);
}
}
}
if(cell) switch(cell.t) {
case 'b': o.push(cell_begin + mxml + vt + '"boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"' + fmla + '>' + p_begin + (cell.v ? 'TRUE' : 'FALSE') + p_end + cell_end); break;
case 'n': o.push(cell_begin + mxml + vt + '"float" office:value="' + cell.v + '"' + fmla + '>' + p_begin + (cell.w||cell.v) + p_end + cell_end); break;
case 's': case 'str': o.push(cell_begin + mxml + vt + '"string"' + fmla + '>' + p_begin + escapexml(cell.v) + p_end + cell_end); break;
case 'd': o.push(cell_begin + mxml + vt + '"date" office:date-value="' + (parseDate(cell.v).toISOString()) + '"' + fmla + '>' + p_begin + (cell.w||(parseDate(cell.v).toISOString())) + p_end + cell_end); break;
if(!cell) { o.push(null_cell_xml); continue; }
switch(cell.t) {
case 'b':
textp = (cell.v ? 'TRUE' : 'FALSE');
ct['office:value-type'] = "boolean";
ct['office:boolean-value'] = (cell.v ? 'true' : 'false');
break;
case 'n':
textp = (cell.w||String(cell.v||0));
ct['office:value-type'] = "float";
ct['office:value'] = (cell.v||0);
break;
case 's': case 'str':
textp = escapexml(cell.v);
ct['office:value-type'] = "string";
break;
case 'd':
textp = (cell.w||(parseDate(cell.v).toISOString()));
ct['office:value-type'] = "date";
ct['office:date-value'] = (parseDate(cell.v).toISOString());
ct['table:style-name'] = "ce1";
break;
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
default: o.push(null_cell_xml); continue;
}
o.push(writextag('table:table-cell', writextag('text:p', textp, {}), ct));
}
o.push(' </table:table-row>\n');
}
@ -54,11 +69,70 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
return o.join("");
};
var write_automatic_styles_ods = function(o/*:Array<string>*/) {
o.push(' <office:automatic-styles>\n');
o.push(' <number:date-style style:name="N37" number:automatic-order="true">\n');
o.push(' <number:month number:style="long"/>\n');
o.push(' <number:text>/</number:text>\n');
o.push(' <number:day number:style="long"/>\n');
o.push(' <number:text>/</number:text>\n');
o.push(' <number:year/>\n');
o.push(' </number:date-style>\n');
o.push(' <style:style style:name="ce1" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N37"/>\n');
o.push(' </office:automatic-styles>\n');
};
return function wcx(wb, opts) {
var o = [XML_HEADER];
/* 3.1.3.2 */
if(opts.bookType == "fods") o.push('<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office: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:table="urn:oasis:names:tc:opendocument:xmlns:table: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:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">');
else o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2">\n'); // TODO
var attr = 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:meta': "urn:oasis:names:tc:opendocument:xmlns:meta:1.0",
'xmlns:number': "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0",
'xmlns:presentation': "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",
'xmlns:svg': "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
'xmlns:chart': "urn:oasis:names:tc:opendocument:xmlns:chart:1.0",
'xmlns:dr3d': "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0",
'xmlns:math': "http://www.w3.org/1998/Math/MathML",
'xmlns:form': "urn:oasis:names:tc:opendocument:xmlns:form:1.0",
'xmlns:script': "urn:oasis:names:tc:opendocument:xmlns:script:1.0",
'xmlns:ooo': "http://openoffice.org/2004/office",
'xmlns:ooow': "http://openoffice.org/2004/writer",
'xmlns:oooc': "http://openoffice.org/2004/calc",
'xmlns:dom': "http://www.w3.org/2001/xml-events",
'xmlns:xforms': "http://www.w3.org/2002/xforms",
'xmlns:xsd': "http://www.w3.org/2001/XMLSchema",
'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
'xmlns:sheet': "urn:oasis:names:tc:opendocument:sh33tjs:1.0",
'xmlns:rpt': "http://openoffice.org/2005/report",
'xmlns:of': "urn:oasis:names:tc:opendocument:xmlns:of:1.2",
'xmlns:xhtml': "http://www.w3.org/1999/xhtml",
'xmlns:grddl': "http://www.w3.org/2003/g/data-view#",
'xmlns:tableooo': "http://openoffice.org/2009/table",
'xmlns:drawooo': "http://openoffice.org/2010/draw",
'xmlns:calcext': "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0",
'xmlns:loext': "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
'xmlns:field': "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0",
'xmlns:formx': "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0",
'xmlns:css3t': "http://www.w3.org/TR/css3-text/",
'office:version': "1.2"
});
var fods = wxt_helper({
'xmlns:config':"urn:oasis:names:tc:opendocument:xmlns:config:1.0",
'office:mimetype':"application/vnd.oasis.opendocument.spreadsheet"
});
if(opts.bookType == "fods") o.push('<office:document' + attr + fods + '>\n');
else o.push('<office:document-content' + attr + '>\n');
write_automatic_styles_ods(o);
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));

@ -41,6 +41,8 @@ function read_utf16(data/*:RawData*/, o/*:ParseOpts*/)/*:Workbook*/ {
function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
var zip, d = data, n=[0];
var o = opts||{};
_ssfopts = {};
if(o.dateNF) _ssfopts.dateNF = o.dateNF;
if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
if(o.type == "file") { o.type = "buffer"; d = _fs.readFileSync(data); }
switch((n = firstbyte(d, o))[0]) {

@ -8,6 +8,7 @@ module.exports = {
},
node: {
fs: false,
process: false,
Buffer: false
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

720
dist/xlsx.js vendored

File diff suppressed because it is too large Load Diff

27
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

@ -0,0 +1,75 @@
#### Number Formats
The `cell.w` formatted text for each cell is produced from `cell.v` and `cell.z`
format. If the format is not specified, the Excel `General` format is used.
The format can either be specified as a string or as an index into the format
table. Parsers are expected to populate `workbook.SSF` with the number format
table. Writers are expected to serialize the table.
Custom tools should ensure that the local table has each used format string
somewhere in the table. Excel convention mandates that the custom formats start
at index 164. The following example creates a custom format from scratch:
```js
var tbl = {};
XLSX.SSF.init_table(tbl); // <-- load builtin formats
tbl[164] = "\"T\"\ #0.00";
var wb = {
SSF: tbl,
SheetNames: ["Sheet1"],
Sheets: {
Sheet1: {
"!ref":"A1:C1",
A1: { t:"n", v:10000 }, // <-- General format
B1: { t:"n", v:10000, z: tbl[4] }, // <-- Builtin format
C1: { t:"n", v:10000, z: tbl[164] } // <-- Custom format
}
}
}
```
The rules are slightly different from how Excel displays custom number formats.
In particular, literal characters must be wrapped in double quotes or preceded
by a backslash. For more info, see the Excel documentation article
[`Create or delete a custom number format`](https://support.office.com/en-us/article/78f2a361-936b-4c03-8772-09fab54be7f4)
or ECMA-376 18.8.31 (Number Formats)
The default formats are listed in ECMA-376 18.8.30:
| ID | Format |
|---:|:---------------------------|
| 0 | `General` |
| 1 | `0` |
| 2 | `0.00` |
| 3 | `#,##0` |
| 4 | `#,##0.00` |
| 9 | `0%` |
| 10 | `0.00%` |
| 11 | `0.00E+00` |
| 12 | `# ?/?` |
| 13 | `# ??/??` |
| 14 | `m/d/yy` (see below) |
| 15 | `d-mmm-yy` |
| 16 | `d-mmm` |
| 17 | `mmm-yy` |
| 18 | `h:mm AM/PM` |
| 19 | `h:mm:ss AM/PM` |
| 20 | `h:mm` |
| 21 | `h:mm:ss` |
| 22 | `m/d/yy h:mm` |
| 37 | `#,##0 ;(#,##0)` |
| 38 | `#,##0 ;[Red](#,##0)` |
| 39 | `#,##0.00;(#,##0.00)` |
| 40 | `#,##0.00;[Red](#,##0.00)` |
| 45 | `mm:ss` |
| 46 | `[h]:mm:ss` |
| 47 | `mmss.0` |
| 48 | `##0.0E+0` |
| 49 | `@` |
Format 14 (`m/d/yy`) is localized by Excel: even though the file specifies that
number format, it will be drawn differently based on system settings. It makes
sense when the producer and consumer of files are in the same locale, but that
is not always the case over the Internet. To get around this ambiguity, parse
functions accept the `dateNF` option to override the interpretation of that
specific format string.

@ -11,6 +11,7 @@ The exported `read` and `readFile` functions accept an options argument:
| cellStyles | false | Save style/theme info to the `.s` field |
| cellText | true | Generated formatted text to the `.w` field |
| cellDates | false | Store dates as type `d` (default is `n`) |
| dateNF | | If specified, use the string for date code 14 ** |
| sheetStubs | false | Create cell objects of type `z` for stub cells |
| sheetRows | 0 | If >0, read the first `sheetRows` rows ** |
| bookDeps | false | If true, parse calculation chains |

@ -80,7 +80,8 @@ specifications expand on serialization of features like properties.
Excel CSV deviates from RFC4180 in a number of important ways. The generated
CSV files should generally work in Excel although they may not work in RFC4180
compatible readers. The parser should generally understand Excel CSV.
compatible readers. The parser should generally understand Excel CSV. The
writer proactively generates cells for formulae if values are unavailable.
Excel TXT uses tab as the delimiter and codepage 1200.
@ -107,7 +108,8 @@ BIFF8 XLS.
ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to
SpreadsheetML. Both are detailed in the OASIS standard, but tools like LO/OO
add undocumented extensions.
add undocumented extensions. The parsers and writers do not implement the full
standard, instead focusing on parts necessary to extract and store raw data.
#### Uniform Office Spreadsheet (UOS1/2)
@ -131,19 +133,29 @@ limited by the general ability to read arbitrary files in the web browser.
#### Symbolic Link (SYLK)
There is no real documentation. All knowledge was gathered by saving files in
various versions of Excel to deduce the meaning of fields.
various versions of Excel to deduce the meaning of fields. Notes:
- Plain formulae are stored in the RC form.
- Column widths are rounded to integral characters.
#### Lotus Formatted Text (PRN)
There is no real documentation, and in fact Excel treats PRN as an output-only
file format. Nevertheless we can guess the column widths and reverse-engineer
the original layout.
the original layout. Excel's 240-character width limitation is not enforced.
#### Data Interchange Format (DIF)
There is no unified definition. Visicalc DIF differs from Lotus DIF, and both
differ from Excel DIF. Where ambiguous, the parser/writer follows the expected
behavior from Excel.
behavior from Excel. In particular, Excel extends DIF in incompatible ways:
- Since Excel automatically converts numbers-as-strings to numbers, numeric
string constants are converted to formulae: `"0.3" -> "=""0.3""`
- DIF technically expects numeric cells to hold the raw numeric data, but Excel
permits formatted numbers (including dates)
- DIF technically has no support for formulae, but Excel will automatically
convert plain formulae. Array formulae are not preserved.
#### HTML

@ -30,6 +30,7 @@
+ [Formulae](README.md#formulae)
+ [Column Properties](README.md#column-properties)
+ [Row Properties](README.md#row-properties)
+ [Number Formats](README.md#number-formats)
+ [Hyperlinks](README.md#hyperlinks)
+ [Cell Comments](README.md#cell-comments)
+ [Sheet Visibility](README.md#sheet-visibility)

@ -31,7 +31,7 @@
["#,##0.000000000", [1000000, "1,000,000.000000000"]],
["#,###", [1, "1"], [-1, "-1"], [0,""], [12345.6789, "12,346"], ["TODO", "TODO"]],
["#.##", [1, "1."], [-1, "-1."], [0,"."], ["sheetjs", "sheetjs"]],
["0;0", [1.1, "1"], [-1.1, "-1"], [0,"0"], ["sheetjs", "sheetjs"]],
["0;0", [1.1, "1"], [-1.1, "1"], [0,"0"], ["sheetjs", "sheetjs"]],
["0.0", [1, "1.0"], [-1, "-1.0"], [0,"0.0"], ["sheetjs", "sheetjs"]],
["0.00", [1.0001, "1.00"], [-1, "-1.00"], [0,"0.00"], ["sheetjs", "sheetjs"]],
["0.000", [1, "1.000"], [-1, "-1.000"], [0,"0.000"], ["sheetjs", "sheetjs"]],
@ -147,6 +147,14 @@
["β˜ƒ", [0], [1], [-1]],
["#0#######", [12345, "012345"], [12345.4321, "012345"], [12345.6789, "012346"]],
["##,##", [12345, "12,345", ""], [12345.4321, "12,345", ""], [12345.6789, "12,346", ""]],
[0, [12345,"12345"], [4294967296.5, 4294967297]],
[0, [12345,"12345"], [4294967296.5, "4294967297"]],
["\"Rs.\"#,##0.00", [-51968287, "-Rs.51,968,287.00"], [2000000, "Rs.2,000,000.00"]],
["$#.00", [3.14159, "$3.14"], [-3.14159, "-$3.14"]],
["\"This is a \".00\"test\"000", [-3.14159, "-This is a 3.14test159"], [3.14159, "This is a 3.14test159"]],
["[$INR]\\ #,##0.00", [3.14159, "INR 3.14"], [-3.14159, "-INR 3.14"]],
["[$β‚Ή-4009]\\ #,##0.00", [3.14159, "β‚Ή 3.14"], [-3.14159, "-β‚Ή 3.14"]],
["[$Β£-809]#,##0.0000;\\-[$Β£-809]#,##0.0000", [3.14159, "Β£3.1416"], [-3.14159, "-Β£3.1416"]],
["\"-\"0.00", [3.14159, "-3.14"], [-3.14159, "--3.14"]],
["[$-409]mmm\\-yy", [12345, "Oct-33"]],
["\"foo\";\"bar\";\"baz\";\"qux\";\"foobar\"", [1], [0], [-1], ["sheetjs"]]
]

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.9.12",
"version": "0.9.13",
"author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLSM/XLS/XML) and ODS (ODS/FODS/UOS) spreadsheet parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "office", "spreadsheet" ],
@ -12,14 +12,15 @@
"node": false,
"crypto": false,
"stream": false,
"process": false,
"fs": false
},
"dependencies": {
"exit-on-epipe":"~1.0.0",
"ssf":"~0.9.0",
"ssf":"~0.9.1",
"codepage":"~1.8.0",
"cfb":"~0.11.1",
"crc-32":"~1.0.0",
"crc-32":"~1.0.2",
"adler-32":"~1.0.0",
"commander":"~2.9.0"
},

@ -5,6 +5,7 @@ var modp = './';
//var modp = 'xlsx';
var fs = require('fs'), assert = require('assert');
describe('source',function(){it('should load',function(){X=require(modp);});});
var DIF_XL = true;
var opts = {cellNF: true};
if(process.env.WTF) {
@ -1683,11 +1684,12 @@ describe('js -> file -> js', function() {
eqcell(wb, newwb, 'Sheet1', 'D2');
eqcell(wb, newwb, 'Sheet1', 'A3');
eqcell(wb, newwb, 'Sheet1', 'B3');
eqcell(wb, newwb, 'Sheet1', 'D3');
eqcell(wb, newwb, 'Sheet1', 'A4');
eqcell(wb, newwb, 'Sheet1', 'C4');
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets["Sheet1"], 'D3').v, '=""0.3""');// dif forces string formula
else eqcell(wb, newwb, 'Sheet1', 'D3');
/* date */
eqcell(wb, newwb, 'Sheet1', 'C3');
if(!DIF_XL) eqcell(wb, newwb, 'Sheet1', 'C3');
});
});
});

@ -16,18 +16,19 @@ var ws_name = "SheetJS";
var wscols = [
{wch:6}, // "characters"
{wpx:50}, // "pixels"
/*{wch:10}*/,
{hidden:true}
,
{hidden:true} // hide column
];
/* At 96 PPI, 1 pt = 1 px */
var wsrows = [];
wsrows[0] = {hpt: 12}; // "points"
wsrows[1] = {hpx: 16}; // "pixels"
//wsrows[2] = {hpt: 18};
wsrows[3] = {hpx: 24};
wsrows[4] = {hidden:true}; // hide row
wsrows[5] = {hidden:false};
var wsrows = [
{hpt: 12}, // "points"
{hpx: 16}, // "pixels"
,
{hpx: 24},
{hidden:true}, // hide row
{hidden:false}
]
console.log("Sheet Name: " + ws_name);
console.log("Data: "); for(var i=0; i!=data.length; ++i) console.log(data[i]);
@ -42,7 +43,7 @@ if(typeof XLSX === "undefined") { try { XLSX = require('./'); } catch(e) { XLSX
var wb = { SheetNames: [], Sheets: {} };
/* convert an array of arrays in JS to a CSF spreadsheet */
var ws = XLSX.utils.aoa_to_sheet(data, {cellDates:true, dense:false});
var ws = XLSX.utils.aoa_to_sheet(data, {cellDates:true});
/* TEST: add worksheet to workbook */
wb.SheetNames.push(ws_name);
@ -75,7 +76,8 @@ ws['A3'].l = { Target: "http://sheetjs.com", Tooltip: "Visit us <SheetJS.com!>"
ws['B1'].z = "0%"; // Format Code 9
/* TEST: custom format */
//ws['B2'].z = "0.0"; wb.SSF[60] = "0.0"; // Custom
wb.SSF = XLSX.SSF.get_table(); // <-- this will change in the future
ws['C2'].z = "0.0"; wb.SSF[60] = "0.0"; // Custom
/* TEST: page margins */
ws['!margins'] = { left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5, footer:0.5 };

@ -5,7 +5,7 @@
/*exported XLSX */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.9.12';
XLSX.version = '0.9.13';
var current_codepage = 1200, current_cptable;
/*:: declare var cptable:any; */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -37,6 +37,7 @@ if(typeof cptable !== 'undefined') {
};
}
var DENSE = null;
var DIF_XL = true;
var Base64 = (function make_b64(){
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
return {
@ -117,7 +118,7 @@ type WriteObjStrFactory = {from_sheet(ws:Worksheet, o:any):string};
/*jshint -W041 */
var SSF = {};
var make_ssf = function make_ssf(SSF){
SSF.version = '0.9.0';
SSF.version = '0.9.1';
function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; }
function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
@ -138,38 +139,6 @@ function fixopts(o){
for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
}
SSF.opts = opts_fmt;
var table_fmt = {
/*::[*/0/*::]*/: 'General',
/*::[*/1/*::]*/: '0',
/*::[*/2/*::]*/: '0.00',
/*::[*/3/*::]*/: '#,##0',
/*::[*/4/*::]*/: '#,##0.00',
/*::[*/9/*::]*/: '0%',
/*::[*/10/*::]*/: '0.00%',
/*::[*/11/*::]*/: '0.00E+00',
/*::[*/12/*::]*/: '# ?/?',
/*::[*/13/*::]*/: '# ??/??',
/*::[*/14/*::]*/: 'm/d/yy',
/*::[*/15/*::]*/: 'd-mmm-yy',
/*::[*/16/*::]*/: 'd-mmm',
/*::[*/17/*::]*/: 'mmm-yy',
/*::[*/18/*::]*/: 'h:mm AM/PM',
/*::[*/19/*::]*/: 'h:mm:ss AM/PM',
/*::[*/20/*::]*/: 'h:mm',
/*::[*/21/*::]*/: 'h:mm:ss',
/*::[*/22/*::]*/: 'm/d/yy h:mm',
/*::[*/37/*::]*/: '#,##0 ;(#,##0)',
/*::[*/38/*::]*/: '#,##0 ;[Red](#,##0)',
/*::[*/39/*::]*/: '#,##0.00;(#,##0.00)',
/*::[*/40/*::]*/: '#,##0.00;[Red](#,##0.00)',
/*::[*/45/*::]*/: 'mm:ss',
/*::[*/46/*::]*/: '[h]:mm:ss',
/*::[*/47/*::]*/: 'mmss.0',
/*::[*/48/*::]*/: '##0.0E+0',
/*::[*/49/*::]*/: '@',
/*::[*/56/*::]*/: '"上午/δΈ‹εˆ "hh"ζ™‚"mm"εˆ†"ss"η§’ "',
/*::[*/65535/*::]*/: 'General'
};
var days/*:Array<Array<string> >*/ = [
['Sun', 'Sunday'],
['Mon', 'Monday'],
@ -193,7 +162,42 @@ var months/*:Array<Array<string> >*/ = [
['N', 'Nov', 'November'],
['D', 'Dec', 'December']
];
function frac(x, D, mixed) {
function init_table(t/*:any*/) {
t[0]= 'General';
t[1]= '0';
t[2]= '0.00';
t[3]= '#,##0';
t[4]= '#,##0.00';
t[9]= '0%';
t[10]= '0.00%';
t[11]= '0.00E+00';
t[12]= '# ?/?';
t[13]= '# ??/??';
t[14]= 'm/d/yy';
t[15]= 'd-mmm-yy';
t[16]= 'd-mmm';
t[17]= 'mmm-yy';
t[18]= 'h:mm AM/PM';
t[19]= 'h:mm:ss AM/PM';
t[20]= 'h:mm';
t[21]= 'h:mm:ss';
t[22]= 'm/d/yy h:mm';
t[37]= '#,##0 ;(#,##0)';
t[38]= '#,##0 ;[Red](#,##0)';
t[39]= '#,##0.00;(#,##0.00)';
t[40]= '#,##0.00;[Red](#,##0.00)';
t[45]= 'mm:ss';
t[46]= '[h]:mm:ss';
t[47]= 'mmss.0';
t[48]= '##0.0E+0';
t[49]= '@';
t[56]= '"上午/δΈ‹εˆ "hh"ζ™‚"mm"εˆ†"ss"η§’ "';
t[65535]= 'General';
}
var table_fmt = {};
init_table(table_fmt);
function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number>*/ {
var sgn = x < 0 ? -1 : 1;
var B = x * sgn;
var P_2 = 0, P_1 = 1, P = 0;
@ -203,15 +207,13 @@ function frac(x, D, mixed) {
A = Math.floor(B);
P = A * P_1 + P_2;
Q = A * Q_1 + Q_2;
if((B - A) < 0.0000000005) break;
if((B - A) < 0.00000005) break;
B = 1 / (B - A);
P_2 = P_1; P_1 = P;
Q_2 = Q_1; Q_1 = Q;
}
if(Q > D) { Q = Q_1; P = P_1; }
if(Q > D) { Q = Q_2; P = P_2; }
if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } }
if(!mixed) return [0, sgn * P, Q];
if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;
var q = Math.floor(sgn * P/Q);
return [q, sgn*P - q*Q, Q];
}
@ -253,6 +255,8 @@ function general_fmt(v/*:any*/, opts/*:?any*/) {
case 'string': return v;
case 'boolean': return v ? "TRUE" : "FALSE";
case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts);
case 'undefined': return "";
case 'object': if(v == null) return "";
}
throw new Error("unsupported value in General format: " + v);
}
@ -567,10 +571,10 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
if((r = fmt.match(frac1))) return write_num_f2(r, aval, sign);
if(fmt.match(/^#+0+$/)) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
if((r = fmt.match(dec1))) {
/*:: if(!Array.isArray(r)) throw "unreachable"; */
/*:: if(!Array.isArray(r)) throw new Error("unreachable"); */
o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]);
o = o.replace(/\.(\d*)$/,function($$, $1) {
/*:: if(!Array.isArray(r)) throw "unreachable"; */
/*:: if(!Array.isArray(r)) throw new Error("unreachable"); */
return "." + $1 + fill("0", r[1].length-$1.length); });
return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
}
@ -678,13 +682,13 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ {
case '.':
/* falls through */
case '0': case '#':
while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1));
while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){}
break;
case '?': while(fmt.charAt(++i) === c); break;
case '?': while(fmt.charAt(++i) === c){} break;
case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break;
case '(': case ')': ++i; break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1); break;
while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break;
case ' ': ++i; break;
default: ++i; break;
}
@ -740,7 +744,10 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};
} else { o=""; }
} else if(o.indexOf("$") > -1) {
o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$";
if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o};
}
break;
/* Numbers */
case '.':
@ -776,7 +783,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
/* falls through */
case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break;
case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break;
case 'X': if(out[i].v === "B2");
case 'X': /*if(out[i].v === "B2");*/
break;
case 'Z':
if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1;
@ -826,11 +833,20 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
}
var vv = "", myv, ostr;
if(nstr.length > 0) {
myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */
ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */
if(nstr.charCodeAt(0) == 40) /* '(' */ {
myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v);
ostr = write_num('(', nstr, myv);
} else {
myv = (v<0 && flen > 1 ? -v : v);
ostr = write_num('n', nstr, myv);
if(myv < 0 && out[0] && out[0].t == 't') {
ostr = ostr.substr(1);
out[0].v = "-" + out[0].v;
}
}
jj=ostr.length-1;
var decpt = out.length;
for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; }
for(i=0; i < out.length; ++i) if(out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) { decpt = i; break; }
var lasti=out.length;
if(decpt === out.length && ostr.indexOf("E") === -1) {
for(i=out.length-1; i>= 0;--i) {
@ -918,11 +934,18 @@ function choose_fmt(f/*:string*/, v) {
return [l, ff];
}
function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
fixopts(o != null ? o : (o=[]));
if(o == null) o = {};
//fixopts(o != null ? o : (o=[]));
var sfmt = "";
switch(typeof fmt) {
case "string": sfmt = fmt; break;
case "number": sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; break;
case "string":
if(fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF;
else sfmt = fmt;
break;
case "number":
if(fmt == 14 && o.dateNF) sfmt = o.dateNF;
else sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt];
break;
}
if(isgeneral(sfmt,0)) return general_fmt(v, o);
var f = choose_fmt(sfmt, v);
@ -936,6 +959,7 @@ SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx]
SSF.format = format;
</