diff --git a/Makefile b/Makefile
index 4e50086..36094a3 100644
--- a/Makefile
+++ b/Makefile
@@ -73,7 +73,7 @@ aux: $(AUXTARGETS)
nexe: xlsx.exe
xlsx.exe: bin/xlsx.js xlsx.js
- nexe -i bin/xlsx.njs -o xlsx.exe
+ nexe -i $< -o $@ --flags
## Testing
diff --git a/bits/23_binutils.js b/bits/23_binutils.js
index 3f84bcc..00ad8a2 100644
--- a/bits/23_binutils.js
+++ b/bits/23_binutils.js
@@ -85,7 +85,7 @@ function ReadShift(size, t) {
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
- o = size = 2 * size; break;
+ size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
diff --git a/bits/25_cellutils.js b/bits/25_cellutils.js
index b308244..c439b76 100644
--- a/bits/25_cellutils.js
+++ b/bits/25_cellutils.js
@@ -15,10 +15,11 @@ function shift_cell_xls(cell, tgt, opts) {
return out;
}
-function shift_range_xls(cell, range) {
- cell.s = shift_cell_xls(cell.s, range.s);
- cell.e = shift_cell_xls(cell.e, range.s);
- return cell;
+function shift_range_xls(cell, range, opts) {
+ var out = dup(cell);
+ out.s = shift_cell_xls(out.s, range.s, opts);
+ out.e = shift_cell_xls(out.e, range.s, opts);
+ return out;
}
function encode_cell_xls(c)/*:string*/ {
@@ -28,6 +29,16 @@ function encode_cell_xls(c)/*:string*/ {
return s;
}
-function encode_range_xls(r)/*:string*/ {
+function encode_range_xls(r, opts)/*:string*/ {
+ if(r.s.r == 0 && !r.s.rRel) {
+ if(r.e.r == opts.biff >= 12 ? 0xFFFFF : 0xFFFF && !r.e.rRel) {
+ return (r.s.cRel ? "" : "$") + encode_col(r.s.c) + ":" + (r.e.cRel ? "" : "$") + encode_col(r.e.c);
+ }
+ }
+ if(r.s.c == 0 && !r.s.cRel) {
+ if(r.e.c == opts.biff >= 12 ? 0xFFFF : 0xFF && !r.e.cRel) {
+ return (r.s.rRel ? "" : "$") + encode_row(r.s.r) + ":" + (r.e.rRel ? "" : "$") + encode_row(r.e.r);
+ }
+ }
return encode_cell_xls(r.s) + ":" + encode_cell_xls(r.e);
}
diff --git a/bits/47_styxml.js b/bits/47_styxml.js
index 31eea7e..6967935 100644
--- a/bits/47_styxml.js
+++ b/bits/47_styxml.js
@@ -13,6 +13,7 @@ function parse_fills(t, opts) {
/* 18.8.32 patternFill CT_PatternFill */
case '':
if(y.patternType) fill.patternType = y.patternType;
break;
case '': case '': break;
@@ -58,6 +59,7 @@ function parse_numFmts(t, opts) {
var f=unescapexml(utf8read(y.formatCode)), j=parseInt(y.numFmtId,10);
styles.NumberFmt[j] = f; if(j>0) SSF.load(f,j);
} break;
+ case '': break;
default: if(opts.WTF) throw new Error('unrecognized ' + y[0] + ' in numFmts');
}
}
@@ -90,7 +92,7 @@ function parse_cellXfs(t, opts) {
case '': break;
/* 18.8.1 alignment CT_CellAlignment */
- case '': break;
+ case '': case '': break;
/* 18.8.33 protection CT_CellProtection */
case '': case '': break;
diff --git a/bits/62_fxls.js b/bits/62_fxls.js
index 32ec63c..4402adb 100644
--- a/bits/62_fxls.js
+++ b/bits/62_fxls.js
@@ -33,8 +33,8 @@ function parse_RgceArea_BIFF2(blob, length, opts) {
}
/* 2.5.198.105 TODO */
-function parse_RgceAreaRel(blob, length) {
- var r=blob.read_shift(2), R=blob.read_shift(2);
+function parse_RgceAreaRel(blob, length, opts) {
+ var r=blob.read_shift(length == 12 ? 4 : 2), R=blob.read_shift(length == 12 ? 4 : 2);
var c=parse_ColRelU(blob, 2);
var C=parse_ColRelU(blob, 2);
return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
@@ -100,9 +100,9 @@ function parse_PtgArea3d(blob, length, opts) {
}
/* 2.5.198.29 */
-function parse_PtgAreaErr(blob, length) {
+function parse_PtgAreaErr(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- blob.l += 8;
+ blob.l += opts && opts.biff > 8 ? 12 : 8;
return [type];
}
/* 2.5.198.30 */
@@ -119,9 +119,9 @@ function parse_PtgAreaErr3d(blob, length, opts) {
}
/* 2.5.198.31 */
-function parse_PtgAreaN(blob, length) {
+function parse_PtgAreaN(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- var area = parse_RgceAreaRel(blob, 8);
+ var area = parse_RgceAreaRel(blob, opts && opts.biff > 8 ? 12 : 8, opts);
return [type, area];
}
@@ -386,6 +386,19 @@ function parse_PtgRefErr(blob, length, opts) {
return [type];
}
+/* 2.5.198.87 */
+function parse_PtgRefErr3d(blob, length, opts) {
+ var type = (blob[blob.l++] & 0x60) >> 5;
+ var ixti = blob.read_shift(2);
+ var w = 4;
+ if(opts) switch(opts.biff) {
+ case 5: throw new Error("PtgRefErr3d -- 5"); // TODO: find test case
+ case 12: w = 6; break;
+ }
+ blob.l += w;
+ return [type, ixti];
+}
+
/* 2.5.198.26 */
var parse_PtgAdd = parseread1;
/* 2.5.198.45 */
@@ -429,8 +442,6 @@ var parse_PtgUplus = parseread1;
var parse_PtgMemErr = parsenoop;
/* 2.5.198.73 */
var parse_PtgMemNoMem = parsenoop;
-/* 2.5.198.87 */
-var parse_PtgRefErr3d = parsenoop;
/* 2.5.198.92 */
var parse_PtgTbl = parsenoop;
@@ -676,6 +687,7 @@ var PtgBinOp = {
PtgSub: "-"
};
function stringify_formula(formula, range, cell, supbooks, opts) {
+ //console.log(formula);
var _range = /*range != null ? range :*/ {s:{c:0, r:0},e:{c:0, r:0}};
var stack = [], e1, e2, type, c, ixti, nameidx, r, sname="";
if(!formula[0] || !formula[0][0]) return "";
@@ -685,7 +697,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
var f = formula[0][ff];
//console.log("++",f, stack)
switch(f[0]) {
- /* 2.2.2.1 Unary Operator Tokens */
/* 2.5.198.93 */
case 'PtgUminus': stack.push("-" + stack.pop()); break;
/* 2.5.198.95 */
@@ -693,7 +704,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.81 */
case 'PtgPercent': stack.push(stack.pop() + "%"); break;
- /* 2.2.2.1 Binary Value Operator Token */
case 'PtgAdd': /* 2.5.198.26 */
case 'PtgConcat': /* 2.5.198.43 */
case 'PtgDiv': /* 2.5.198.45 */
@@ -713,7 +723,7 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 1: sp = fill("\r", formula[0][last_sp][1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
e2 = e2 + sp;
last_sp = -1;
@@ -721,7 +731,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+PtgBinOp[f[0]]+e1);
break;
- /* 2.2.2.1 Binary Reference Operator Token */
/* 2.5.198.67 */
case 'PtgIsect':
e1 = stack.pop(); e2 = stack.pop();
@@ -736,7 +745,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+":"+e1);
break;
- /* 2.2.2.3 Control Tokens "can be ignored" */
/* 2.5.198.34 */
case 'PtgAttrChoose': break;
/* 2.5.198.35 */
@@ -763,11 +771,11 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(sname + "!" + encode_cell(c));
break;
- /* Function Call */
/* 2.5.198.62 */
case 'PtgFunc':
/* 2.5.198.63 */
case 'PtgFuncVar':
+ //console.log(f[1]);
/* f[1] = [argc, func, type] */
var argc = f[1][0], func = f[1][1];
if(!argc) argc = 0;
@@ -787,10 +795,15 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 'PtgStr': stack.push('"' + f[1] + '"'); break;
/* 2.5.198.57 */
case 'PtgErr': stack.push(f[1]); break;
+ /* 2.5.198.31 TODO */
+ case 'PtgAreaN':
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
+ break;
/* 2.5.198.27 TODO: fixed points */
case 'PtgArea':
- type = f[1][0]; r = shift_range_xls(f[1][1], _range);
- stack.push(encode_range_xls(r));
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
break;
/* 2.5.198.28 */
case 'PtgArea3d': // TODO: lots of stuff
@@ -803,7 +816,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("SUM(" + stack.pop() + ")");
break;
- /* Expression Prefixes */
/* 2.5.198.37 */
case 'PtgAttrSemi': break;
@@ -834,7 +846,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(externbook.body);
break;
- /* 2.2.2.4 Display Tokens */
/* 2.5.198.80 */
case 'PtgParen':
var lp = '(', rp = ')';
@@ -843,10 +854,10 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
switch(formula[0][last_sp][1][0]) {
case 2: lp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
case 3: lp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
- case 4: rp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
- case 5: rp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
+ case 4: rp = fill(" ", formula[0][last_sp][1][1]) + rp; break;
+ case 5: rp = fill("\r", formula[0][last_sp][1][1]) + rp; break;
default:
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
last_sp = -1;
}
@@ -855,6 +866,9 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.86 */
case 'PtgRefErr': stack.push('#REF!'); break;
+ /* 2.5.198.87 */
+ case 'PtgRefErr3d': stack.push('#REF!'); break;
+
/* */
/* 2.5.198.58 TODO */
case 'PtgExp':
@@ -884,7 +898,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("{" + stringify_array(f[1]) + "}");
break;
- /* 2.2.2.5 Mem Tokens */
/* 2.5.198.70 TODO: confirm this is a non-display */
case 'PtgMemArea':
//stack.push("(" + f[2].map(encode_range).join(",") + ")");
@@ -908,31 +921,31 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("");
break;
- /* 2.5.198.29 TODO */
- case 'PtgAreaErr': break;
-
- /* 2.5.198.31 TODO */
- case 'PtgAreaN': stack.push(""); break;
-
- /* 2.5.198.87 TODO */
- case 'PtgRefErr3d': break;
+ /* 2.5.198.29 */
+ case 'PtgAreaErr': stack.push("#REF!");
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;
- default: throw 'Unrecognized Formula Token: ' + f;
+ default: throw new Error('Unrecognized Formula Token: ' + f);
}
var PtgNonDisp = ['PtgAttrSpace', 'PtgAttrSpaceSemi', 'PtgAttrGoto'];
if(last_sp >= 0 && PtgNonDisp.indexOf(formula[0][ff][0]) == -1) {
f = formula[0][last_sp];
+ var _left = true;
switch(f[1][0]) {
+ /* note: some bad XLSB files omit the PtgParen */
+ case 4: _left = false;
+ /* falls through */
case 0: sp = fill(" ", f[1][1]); break;
+ case 5: _left = false;
+ /* falls through */
case 1: sp = fill("\r", f[1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + f[1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + f[1][0]);
}
- stack.push(sp + stack.pop());
+ stack.push((_left ? sp : "") + stack.pop() + (_left ? "" : sp));
last_sp = -1;
}
//console.log("::",f, stack)
diff --git a/bits/64_ftab.js b/bits/64_ftab.js
index 36beab5..502cf7d 100644
--- a/bits/64_ftab.js
+++ b/bits/64_ftab.js
@@ -870,7 +870,8 @@ var Ftab = {
/*::[*/0x01D1/*::]*/: 'WEEKNUM',
/*::[*/0x01D2/*::]*/: 'AMORDEGRC',
/*::[*/0x01D3/*::]*/: 'AMORLINC',
- /*::[*/0x01D4/*::]*/: 'SHEETJS',
+ /*::[*/0x01D4/*::]*/: 'CONVERT',
+ /*::[*/0x02D4/*::]*/: 'SHEETJS',
/*::[*/0x01D5/*::]*/: 'ACCRINT',
/*::[*/0x01D6/*::]*/: 'ACCRINTM',
/*::[*/0x01D7/*::]*/: 'WORKDAY',
@@ -1079,9 +1080,54 @@ var FtabArgc = {
/*::[*/0x0178/*::]*/: 1, /* ROUNDBAHTDOWN */
/*::[*/0x0179/*::]*/: 1, /* ROUNDBAHTUP */
/*::[*/0x017A/*::]*/: 1, /* THAIYEAR */
+ /*::[*/0x017E/*::]*/: 3, /* CUBEMEMBERPROPERTY */
+ /*::[*/0x0181/*::]*/: 1, /* HEX2DEC */
+ /*::[*/0x0188/*::]*/: 1, /* OCT2DEC */
+ /*::[*/0x0189/*::]*/: 1, /* BIN2DEC */
+ /*::[*/0x018C/*::]*/: 2, /* IMSUB */
+ /*::[*/0x018D/*::]*/: 2, /* IMDIV */
+ /*::[*/0x018E/*::]*/: 2, /* IMPOWER */
+ /*::[*/0x018F/*::]*/: 1, /* IMABS */
+ /*::[*/0x0190/*::]*/: 1, /* IMSQRT */
+ /*::[*/0x0191/*::]*/: 1, /* IMLN */
+ /*::[*/0x0192/*::]*/: 1, /* IMLOG2 */
+ /*::[*/0x0193/*::]*/: 1, /* IMLOG10 */
+ /*::[*/0x0194/*::]*/: 1, /* IMSIN */
+ /*::[*/0x0195/*::]*/: 1, /* IMCOS */
+ /*::[*/0x0196/*::]*/: 1, /* IMEXP */
+ /*::[*/0x0197/*::]*/: 1, /* IMARGUMENT */
+ /*::[*/0x0198/*::]*/: 1, /* IMCONJUGATE */
+ /*::[*/0x0199/*::]*/: 1, /* IMAGINARY */
+ /*::[*/0x019A/*::]*/: 1, /* IMREAL */
+ /*::[*/0x019E/*::]*/: 4, /* SERIESSUM */
+ /*::[*/0x019F/*::]*/: 1, /* FACTDOUBLE */
/*::[*/0x01A0/*::]*/: 1, /* SQRTPI */
+ /*::[*/0x01A1/*::]*/: 2, /* QUOTIENT */
+ /*::[*/0x01A4/*::]*/: 1, /* ISEVEN */
+ /*::[*/0x01A5/*::]*/: 1, /* ISODD */
+ /*::[*/0x01A6/*::]*/: 2, /* MROUND */
+ /*::[*/0x01A8/*::]*/: 1, /* ERFC */
+ /*::[*/0x01A9/*::]*/: 2, /* BESSELJ */
+ /*::[*/0x01AA/*::]*/: 2, /* BESSELK */
+ /*::[*/0x01AB/*::]*/: 2, /* BESSELY */
+ /*::[*/0x01AC/*::]*/: 2, /* BESSELI */
+ /*::[*/0x01AE/*::]*/: 3, /* XNPV */
+ /*::[*/0x01B6/*::]*/: 3, /* TBILLEQ */
+ /*::[*/0x01B7/*::]*/: 3, /* TBILLPRICE */
+ /*::[*/0x01B8/*::]*/: 3, /* TBILLYIELD */
+ /*::[*/0x01BB/*::]*/: 2, /* DOLLARDE */
+ /*::[*/0x01BC/*::]*/: 2, /* DOLLARFR */
+ /*::[*/0x01BD/*::]*/: 2, /* NOMINAL */
+ /*::[*/0x01BE/*::]*/: 2, /* EFFECT */
+ /*::[*/0x01BF/*::]*/: 6, /* CUMPRINC */
+ /*::[*/0x01C0/*::]*/: 6, /* CUMIPMT */
/*::[*/0x01C1/*::]*/: 2, /* EDATE */
/*::[*/0x01C2/*::]*/: 2, /* EOMONTH */
+ /*::[*/0x01D0/*::]*/: 2, /* RANDBETWEEN */
+ /*::[*/0x01D4/*::]*/: 3, /* CONVERT */
+ /*::[*/0x01DC/*::]*/: 2, /* FVSCHEDULE */
+ /*::[*/0x01DF/*::]*/: 1, /* CUBESETCOUNT */
+ /*::[*/0x01E0/*::]*/: 2, /* IFERROR */
/*::[*/0xFFFF/*::]*/: 0
};
/* [MS-XLSX] 2.2.3 Functions */
diff --git a/bits/71_wbcommon.js b/bits/71_wbcommon.js
index a70ddc0..2ffee7d 100644
--- a/bits/71_wbcommon.js
+++ b/bits/71_wbcommon.js
@@ -98,3 +98,10 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
+
+/* TODO: validate workbook */
+function check_wb(wb) {
+ if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
+ for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
+ if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
+}
diff --git a/bits/72_wbxml.js b/bits/72_wbxml.js
index 32c12d7..e2cc436 100644
--- a/bits/72_wbxml.js
+++ b/bits/72_wbxml.js
@@ -18,7 +18,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.13 fileVersion CT_FileVersion ? */
case '': break;
+ case '': case '': break;
/* 18.2.12 fileSharing CT_FileSharing ? */
case '': break;
@@ -26,6 +26,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.28 workbookPr CT_WorkbookPr ? */
case '': delete y[0]; wb.WBProps = y; break;
+ case '': break;
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
case '': case '': break;
/* 18.2.30 workbookView CT_BookView + */
case '': break;
/* 18.2.20 sheets CT_Sheets 1 */
case '': case '': break; // aggregate sheet
/* 18.2.19 sheet CT_Sheet + */
case '': break;
/* 18.2.15 functionGroups CT_FunctionGroups ? */
case '': break;
@@ -61,6 +64,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.2 calcPr CT_CalcPr ? */
case '': delete y[0]; wb.CalcPr = y; break;
+ case '': break;
/* 18.2.16 oleSize CT_OleSize ? (ref required) */
case '': pass=false; break;
- default: if(!pass && opts.WTF) throw 'unrecognized ' + y[0] + ' in workbook';
+ default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in workbook');
}
});
if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 99e4747..af43de7 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -158,7 +158,7 @@ function xlml_normalize(d)/*:string*/ {
/* TODO: Everything */
/* UOS uses CJK in tags */
-var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
+var xlmlregex = /<(\/?)([^\s?>!\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
//var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
function parse_xlml_xml(d, opts)/*:Workbook*/ {
var str = debom(xlml_normalize(d));
@@ -178,6 +178,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
var cstys = [], csty;
var arrayf = [];
xlmlregex.lastIndex = 0;
+ str = str.replace(//mg,"");
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'Data':
if(state[state.length-1][1]) break;
diff --git a/bits/76_xls.js b/bits/76_xls.js
index c245f4c..55b4e28 100644
--- a/bits/76_xls.js
+++ b/bits/76_xls.js
@@ -204,9 +204,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'RefreshAll': wb.opts.RefreshAll = val; break;
case 'BookBool': break; // TODO
case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break;
- case 'MTRSettings': {
- if(val[0] && val[1]) throw "Unsupported threads: " + val;
- } break; // TODO: actually support threads
+ case 'MTRSettings': break;
case 'CalcCount': wb.opts.CalcCount = val; break;
case 'CalcDelta': wb.opts.CalcDelta = val; break;
case 'CalcIter': wb.opts.CalcIter = val; break;
diff --git a/bits/88_write.js b/bits/88_write.js
index dbf526d..8abcbf2 100644
--- a/bits/88_write.js
+++ b/bits/88_write.js
@@ -43,6 +43,7 @@ function write_binary_type(out, opts/*:WriteOpts*/) {
}
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
+ check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
case 'xml': return write_string_type(write_xlml(wb, o), o);
diff --git a/test.js b/test.js
index e36edd1..27dfd16 100644
--- a/test.js
+++ b/test.js
@@ -13,7 +13,7 @@ if(process.env.WTF) {
}
var fullex = [".xlsb", ".xlsm", ".xlsx"];
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2"];
-var ex = fullex.slice(); ex.push(".ods"); ex.push(".xls"); ex.push("xml");
+var ex = fullex.slice(); ex = ex.concat([".ods", ".xls", ".xml", ".fods"]);
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
var exp = ex.map(function(x){ return x + ".pending"; });
@@ -952,6 +952,13 @@ describe('invalid files', function() {
});
});
});
+ it('should fail if SheetNames has duplicate entries', function() {
+ var wb = X.readFile(paths.fstxlsx);
+ wb.SheetNames.push(wb.SheetNames[0]);
+ assert.throws(function() {
+ X.write(wb, {type:'binary'});
+ });
+ });
});
});
diff --git a/tests.lst b/tests.lst
index a096830..f12ad1b 100644
--- a/tests.lst
+++ b/tests.lst
@@ -517,7 +517,7 @@ roo_comments.ods
roo_datetime.ods
roo_dreimalvier.ods
roo_emptysheets.ods
-roo_encrypted-letmein.ods
+# roo_encrypted-letmein.ods
roo_formula.ods
roo_hidden_sheets.ods
roo_html-escape.ods
@@ -1161,9 +1161,9 @@ formulae_test_simple.xml
hyperlink_stress_test_2011.xml
interview.xlsx.xml
issue.xlsx.xml
-large_strings.xls.xml
-large_strings.xlsb.xml
-large_strings.xlsx.xml
+large_strings.xls.xml.pending
+large_strings.xlsb.xml.pending
+large_strings.xlsx.xml.pending
merge_cells.xls.xml
merge_cells.xlsb.xml
merge_cells.xlsx.xml
diff --git a/xlsx.flow.js b/xlsx.flow.js
index a6eab77..ca03b88 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -1717,7 +1717,7 @@ function ReadShift(size, t) {
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
- o = size = 2 * size; break;
+ size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
@@ -1904,10 +1904,11 @@ function shift_cell_xls(cell, tgt, opts) {
return out;
}
-function shift_range_xls(cell, range) {
- cell.s = shift_cell_xls(cell.s, range.s);
- cell.e = shift_cell_xls(cell.e, range.s);
- return cell;
+function shift_range_xls(cell, range, opts) {
+ var out = dup(cell);
+ out.s = shift_cell_xls(out.s, range.s, opts);
+ out.e = shift_cell_xls(out.e, range.s, opts);
+ return out;
}
function encode_cell_xls(c)/*:string*/ {
@@ -1917,7 +1918,17 @@ function encode_cell_xls(c)/*:string*/ {
return s;
}
-function encode_range_xls(r)/*:string*/ {
+function encode_range_xls(r, opts)/*:string*/ {
+ if(r.s.r == 0 && !r.s.rRel) {
+ if(r.e.r == opts.biff >= 12 ? 0xFFFFF : 0xFFFF && !r.e.rRel) {
+ return (r.s.cRel ? "" : "$") + encode_col(r.s.c) + ":" + (r.e.cRel ? "" : "$") + encode_col(r.e.c);
+ }
+ }
+ if(r.s.c == 0 && !r.s.cRel) {
+ if(r.e.c == opts.biff >= 12 ? 0xFFFF : 0xFF && !r.e.cRel) {
+ return (r.s.rRel ? "" : "$") + encode_row(r.s.r) + ":" + (r.e.rRel ? "" : "$") + encode_row(r.e.r);
+ }
+ }
return encode_cell_xls(r.s) + ":" + encode_cell_xls(r.e);
}
var OFFCRYPTO = {};
@@ -4950,6 +4961,7 @@ function parse_fills(t, opts) {
/* 18.8.32 patternFill CT_PatternFill */
case '':
if(y.patternType) fill.patternType = y.patternType;
break;
case '': case '': break;
@@ -4995,6 +5007,7 @@ function parse_numFmts(t, opts) {
var f=unescapexml(utf8read(y.formatCode)), j=parseInt(y.numFmtId,10);
styles.NumberFmt[j] = f; if(j>0) SSF.load(f,j);
} break;
+ case '': break;
default: if(opts.WTF) throw new Error('unrecognized ' + y[0] + ' in numFmts');
}
}
@@ -5027,7 +5040,7 @@ function parse_cellXfs(t, opts) {
case '': break;
/* 18.8.1 alignment CT_CellAlignment */
- case '': break;
+ case '': case '': break;
/* 18.8.33 protection CT_CellProtection */
case '': case '': break;
@@ -5832,8 +5845,8 @@ function parse_RgceArea_BIFF2(blob, length, opts) {
}
/* 2.5.198.105 TODO */
-function parse_RgceAreaRel(blob, length) {
- var r=blob.read_shift(2), R=blob.read_shift(2);
+function parse_RgceAreaRel(blob, length, opts) {
+ var r=blob.read_shift(length == 12 ? 4 : 2), R=blob.read_shift(length == 12 ? 4 : 2);
var c=parse_ColRelU(blob, 2);
var C=parse_ColRelU(blob, 2);
return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
@@ -5899,9 +5912,9 @@ function parse_PtgArea3d(blob, length, opts) {
}
/* 2.5.198.29 */
-function parse_PtgAreaErr(blob, length) {
+function parse_PtgAreaErr(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- blob.l += 8;
+ blob.l += opts && opts.biff > 8 ? 12 : 8;
return [type];
}
/* 2.5.198.30 */
@@ -5918,9 +5931,9 @@ function parse_PtgAreaErr3d(blob, length, opts) {
}
/* 2.5.198.31 */
-function parse_PtgAreaN(blob, length) {
+function parse_PtgAreaN(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- var area = parse_RgceAreaRel(blob, 8);
+ var area = parse_RgceAreaRel(blob, opts && opts.biff > 8 ? 12 : 8, opts);
return [type, area];
}
@@ -6185,6 +6198,19 @@ function parse_PtgRefErr(blob, length, opts) {
return [type];
}
+/* 2.5.198.87 */
+function parse_PtgRefErr3d(blob, length, opts) {
+ var type = (blob[blob.l++] & 0x60) >> 5;
+ var ixti = blob.read_shift(2);
+ var w = 4;
+ if(opts) switch(opts.biff) {
+ case 5: throw new Error("PtgRefErr3d -- 5"); // TODO: find test case
+ case 12: w = 6; break;
+ }
+ blob.l += w;
+ return [type, ixti];
+}
+
/* 2.5.198.26 */
var parse_PtgAdd = parseread1;
/* 2.5.198.45 */
@@ -6228,8 +6254,6 @@ var parse_PtgUplus = parseread1;
var parse_PtgMemErr = parsenoop;
/* 2.5.198.73 */
var parse_PtgMemNoMem = parsenoop;
-/* 2.5.198.87 */
-var parse_PtgRefErr3d = parsenoop;
/* 2.5.198.92 */
var parse_PtgTbl = parsenoop;
@@ -6475,6 +6499,7 @@ var PtgBinOp = {
PtgSub: "-"
};
function stringify_formula(formula, range, cell, supbooks, opts) {
+ //console.log(formula);
var _range = /*range != null ? range :*/ {s:{c:0, r:0},e:{c:0, r:0}};
var stack = [], e1, e2, type, c, ixti, nameidx, r, sname="";
if(!formula[0] || !formula[0][0]) return "";
@@ -6484,7 +6509,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
var f = formula[0][ff];
//console.log("++",f, stack)
switch(f[0]) {
- /* 2.2.2.1 Unary Operator Tokens */
/* 2.5.198.93 */
case 'PtgUminus': stack.push("-" + stack.pop()); break;
/* 2.5.198.95 */
@@ -6492,7 +6516,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.81 */
case 'PtgPercent': stack.push(stack.pop() + "%"); break;
- /* 2.2.2.1 Binary Value Operator Token */
case 'PtgAdd': /* 2.5.198.26 */
case 'PtgConcat': /* 2.5.198.43 */
case 'PtgDiv': /* 2.5.198.45 */
@@ -6512,7 +6535,7 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 1: sp = fill("\r", formula[0][last_sp][1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
e2 = e2 + sp;
last_sp = -1;
@@ -6520,7 +6543,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+PtgBinOp[f[0]]+e1);
break;
- /* 2.2.2.1 Binary Reference Operator Token */
/* 2.5.198.67 */
case 'PtgIsect':
e1 = stack.pop(); e2 = stack.pop();
@@ -6535,7 +6557,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+":"+e1);
break;
- /* 2.2.2.3 Control Tokens "can be ignored" */
/* 2.5.198.34 */
case 'PtgAttrChoose': break;
/* 2.5.198.35 */
@@ -6562,11 +6583,11 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(sname + "!" + encode_cell(c));
break;
- /* Function Call */
/* 2.5.198.62 */
case 'PtgFunc':
/* 2.5.198.63 */
case 'PtgFuncVar':
+ //console.log(f[1]);
/* f[1] = [argc, func, type] */
var argc = f[1][0], func = f[1][1];
if(!argc) argc = 0;
@@ -6586,10 +6607,15 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 'PtgStr': stack.push('"' + f[1] + '"'); break;
/* 2.5.198.57 */
case 'PtgErr': stack.push(f[1]); break;
+ /* 2.5.198.31 TODO */
+ case 'PtgAreaN':
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
+ break;
/* 2.5.198.27 TODO: fixed points */
case 'PtgArea':
- type = f[1][0]; r = shift_range_xls(f[1][1], _range);
- stack.push(encode_range_xls(r));
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
break;
/* 2.5.198.28 */
case 'PtgArea3d': // TODO: lots of stuff
@@ -6602,7 +6628,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("SUM(" + stack.pop() + ")");
break;
- /* Expression Prefixes */
/* 2.5.198.37 */
case 'PtgAttrSemi': break;
@@ -6633,7 +6658,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(externbook.body);
break;
- /* 2.2.2.4 Display Tokens */
/* 2.5.198.80 */
case 'PtgParen':
var lp = '(', rp = ')';
@@ -6642,10 +6666,10 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
switch(formula[0][last_sp][1][0]) {
case 2: lp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
case 3: lp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
- case 4: rp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
- case 5: rp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
+ case 4: rp = fill(" ", formula[0][last_sp][1][1]) + rp; break;
+ case 5: rp = fill("\r", formula[0][last_sp][1][1]) + rp; break;
default:
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
last_sp = -1;
}
@@ -6654,6 +6678,9 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.86 */
case 'PtgRefErr': stack.push('#REF!'); break;
+ /* 2.5.198.87 */
+ case 'PtgRefErr3d': stack.push('#REF!'); break;
+
/* */
/* 2.5.198.58 TODO */
case 'PtgExp':
@@ -6683,7 +6710,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("{" + stringify_array(f[1]) + "}");
break;
- /* 2.2.2.5 Mem Tokens */
/* 2.5.198.70 TODO: confirm this is a non-display */
case 'PtgMemArea':
//stack.push("(" + f[2].map(encode_range).join(",") + ")");
@@ -6707,31 +6733,31 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("");
break;
- /* 2.5.198.29 TODO */
- case 'PtgAreaErr': break;
-
- /* 2.5.198.31 TODO */
- case 'PtgAreaN': stack.push(""); break;
-
- /* 2.5.198.87 TODO */
- case 'PtgRefErr3d': break;
+ /* 2.5.198.29 */
+ case 'PtgAreaErr': stack.push("#REF!");
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;
- default: throw 'Unrecognized Formula Token: ' + f;
+ default: throw new Error('Unrecognized Formula Token: ' + f);
}
var PtgNonDisp = ['PtgAttrSpace', 'PtgAttrSpaceSemi', 'PtgAttrGoto'];
if(last_sp >= 0 && PtgNonDisp.indexOf(formula[0][ff][0]) == -1) {
f = formula[0][last_sp];
+ var _left = true;
switch(f[1][0]) {
+ /* note: some bad XLSB files omit the PtgParen */
+ case 4: _left = false;
+ /* falls through */
case 0: sp = fill(" ", f[1][1]); break;
+ case 5: _left = false;
+ /* falls through */
case 1: sp = fill("\r", f[1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + f[1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + f[1][0]);
}
- stack.push(sp + stack.pop());
+ stack.push((_left ? sp : "") + stack.pop() + (_left ? "" : sp));
last_sp = -1;
}
//console.log("::",f, stack)
@@ -7631,7 +7657,8 @@ var Ftab = {
/*::[*/0x01D1/*::]*/: 'WEEKNUM',
/*::[*/0x01D2/*::]*/: 'AMORDEGRC',
/*::[*/0x01D3/*::]*/: 'AMORLINC',
- /*::[*/0x01D4/*::]*/: 'SHEETJS',
+ /*::[*/0x01D4/*::]*/: 'CONVERT',
+ /*::[*/0x02D4/*::]*/: 'SHEETJS',
/*::[*/0x01D5/*::]*/: 'ACCRINT',
/*::[*/0x01D6/*::]*/: 'ACCRINTM',
/*::[*/0x01D7/*::]*/: 'WORKDAY',
@@ -7840,9 +7867,54 @@ var FtabArgc = {
/*::[*/0x0178/*::]*/: 1, /* ROUNDBAHTDOWN */
/*::[*/0x0179/*::]*/: 1, /* ROUNDBAHTUP */
/*::[*/0x017A/*::]*/: 1, /* THAIYEAR */
+ /*::[*/0x017E/*::]*/: 3, /* CUBEMEMBERPROPERTY */
+ /*::[*/0x0181/*::]*/: 1, /* HEX2DEC */
+ /*::[*/0x0188/*::]*/: 1, /* OCT2DEC */
+ /*::[*/0x0189/*::]*/: 1, /* BIN2DEC */
+ /*::[*/0x018C/*::]*/: 2, /* IMSUB */
+ /*::[*/0x018D/*::]*/: 2, /* IMDIV */
+ /*::[*/0x018E/*::]*/: 2, /* IMPOWER */
+ /*::[*/0x018F/*::]*/: 1, /* IMABS */
+ /*::[*/0x0190/*::]*/: 1, /* IMSQRT */
+ /*::[*/0x0191/*::]*/: 1, /* IMLN */
+ /*::[*/0x0192/*::]*/: 1, /* IMLOG2 */
+ /*::[*/0x0193/*::]*/: 1, /* IMLOG10 */
+ /*::[*/0x0194/*::]*/: 1, /* IMSIN */
+ /*::[*/0x0195/*::]*/: 1, /* IMCOS */
+ /*::[*/0x0196/*::]*/: 1, /* IMEXP */
+ /*::[*/0x0197/*::]*/: 1, /* IMARGUMENT */
+ /*::[*/0x0198/*::]*/: 1, /* IMCONJUGATE */
+ /*::[*/0x0199/*::]*/: 1, /* IMAGINARY */
+ /*::[*/0x019A/*::]*/: 1, /* IMREAL */
+ /*::[*/0x019E/*::]*/: 4, /* SERIESSUM */
+ /*::[*/0x019F/*::]*/: 1, /* FACTDOUBLE */
/*::[*/0x01A0/*::]*/: 1, /* SQRTPI */
+ /*::[*/0x01A1/*::]*/: 2, /* QUOTIENT */
+ /*::[*/0x01A4/*::]*/: 1, /* ISEVEN */
+ /*::[*/0x01A5/*::]*/: 1, /* ISODD */
+ /*::[*/0x01A6/*::]*/: 2, /* MROUND */
+ /*::[*/0x01A8/*::]*/: 1, /* ERFC */
+ /*::[*/0x01A9/*::]*/: 2, /* BESSELJ */
+ /*::[*/0x01AA/*::]*/: 2, /* BESSELK */
+ /*::[*/0x01AB/*::]*/: 2, /* BESSELY */
+ /*::[*/0x01AC/*::]*/: 2, /* BESSELI */
+ /*::[*/0x01AE/*::]*/: 3, /* XNPV */
+ /*::[*/0x01B6/*::]*/: 3, /* TBILLEQ */
+ /*::[*/0x01B7/*::]*/: 3, /* TBILLPRICE */
+ /*::[*/0x01B8/*::]*/: 3, /* TBILLYIELD */
+ /*::[*/0x01BB/*::]*/: 2, /* DOLLARDE */
+ /*::[*/0x01BC/*::]*/: 2, /* DOLLARFR */
+ /*::[*/0x01BD/*::]*/: 2, /* NOMINAL */
+ /*::[*/0x01BE/*::]*/: 2, /* EFFECT */
+ /*::[*/0x01BF/*::]*/: 6, /* CUMPRINC */
+ /*::[*/0x01C0/*::]*/: 6, /* CUMIPMT */
/*::[*/0x01C1/*::]*/: 2, /* EDATE */
/*::[*/0x01C2/*::]*/: 2, /* EOMONTH */
+ /*::[*/0x01D0/*::]*/: 2, /* RANDBETWEEN */
+ /*::[*/0x01D4/*::]*/: 3, /* CONVERT */
+ /*::[*/0x01DC/*::]*/: 2, /* FVSCHEDULE */
+ /*::[*/0x01DF/*::]*/: 1, /* CUBESETCOUNT */
+ /*::[*/0x01E0/*::]*/: 2, /* IFERROR */
/*::[*/0xFFFF/*::]*/: 0
};
/* [MS-XLSX] 2.2.3 Functions */
@@ -9067,6 +9139,13 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
+
+/* TODO: validate workbook */
+function check_wb(wb) {
+ if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
+ for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
+ if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
+}
/* 18.2 Workbook */
var wbnsregex = /<\w+:workbook/;
function parse_wb_xml(data, opts) {
@@ -9087,7 +9166,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.13 fileVersion CT_FileVersion ? */
case '': break;
+ case '': case '': break;
/* 18.2.12 fileSharing CT_FileSharing ? */
case '': break;
@@ -9095,6 +9174,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.28 workbookPr CT_WorkbookPr ? */
case '': delete y[0]; wb.WBProps = y; break;
+ case '': break;
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
case '': case '': break;
/* 18.2.30 workbookView CT_BookView + */
case '': break;
/* 18.2.20 sheets CT_Sheets 1 */
case '': case '': break; // aggregate sheet
/* 18.2.19 sheet CT_Sheet + */
case '': break;
/* 18.2.15 functionGroups CT_FunctionGroups ? */
case '': break;
@@ -9130,6 +9212,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.2 calcPr CT_CalcPr ? */
case '': delete y[0]; wb.CalcPr = y; break;
+ case '': break;
/* 18.2.16 oleSize CT_OleSize ? (ref required) */
case '': pass=false; break;
- default: if(!pass && opts.WTF) throw 'unrecognized ' + y[0] + ' in workbook';
+ default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in workbook');
}
});
if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
@@ -9637,7 +9720,7 @@ function xlml_normalize(d)/*:string*/ {
/* TODO: Everything */
/* UOS uses CJK in tags */
-var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
+var xlmlregex = /<(\/?)([^\s?>!\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
//var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
function parse_xlml_xml(d, opts)/*:Workbook*/ {
var str = debom(xlml_normalize(d));
@@ -9657,6 +9740,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
var cstys = [], csty;
var arrayf = [];
xlmlregex.lastIndex = 0;
+ str = str.replace(//mg,"");
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'Data':
if(state[state.length-1][1]) break;
@@ -10413,9 +10497,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'RefreshAll': wb.opts.RefreshAll = val; break;
case 'BookBool': break; // TODO
case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break;
- case 'MTRSettings': {
- if(val[0] && val[1]) throw "Unsupported threads: " + val;
- } break; // TODO: actually support threads
+ case 'MTRSettings': break;
case 'CalcCount': wb.opts.CalcCount = val; break;
case 'CalcDelta': wb.opts.CalcDelta = val; break;
case 'CalcIter': wb.opts.CalcIter = val; break;
@@ -13153,6 +13235,7 @@ function write_binary_type(out, opts/*:WriteOpts*/) {
}
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
+ check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
case 'xml': return write_string_type(write_xlml(wb, o), o);
diff --git a/xlsx.js b/xlsx.js
index ef24385..3900205 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -1675,7 +1675,7 @@ function ReadShift(size, t) {
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
- o = size = 2 * size; break;
+ size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
@@ -1862,10 +1862,11 @@ function shift_cell_xls(cell, tgt, opts) {
return out;
}
-function shift_range_xls(cell, range) {
- cell.s = shift_cell_xls(cell.s, range.s);
- cell.e = shift_cell_xls(cell.e, range.s);
- return cell;
+function shift_range_xls(cell, range, opts) {
+ var out = dup(cell);
+ out.s = shift_cell_xls(out.s, range.s, opts);
+ out.e = shift_cell_xls(out.e, range.s, opts);
+ return out;
}
function encode_cell_xls(c) {
@@ -1875,7 +1876,17 @@ function encode_cell_xls(c) {
return s;
}
-function encode_range_xls(r) {
+function encode_range_xls(r, opts) {
+ if(r.s.r == 0 && !r.s.rRel) {
+ if(r.e.r == opts.biff >= 12 ? 0xFFFFF : 0xFFFF && !r.e.rRel) {
+ return (r.s.cRel ? "" : "$") + encode_col(r.s.c) + ":" + (r.e.cRel ? "" : "$") + encode_col(r.e.c);
+ }
+ }
+ if(r.s.c == 0 && !r.s.cRel) {
+ if(r.e.c == opts.biff >= 12 ? 0xFFFF : 0xFF && !r.e.cRel) {
+ return (r.s.rRel ? "" : "$") + encode_row(r.s.r) + ":" + (r.e.rRel ? "" : "$") + encode_row(r.e.r);
+ }
+ }
return encode_cell_xls(r.s) + ":" + encode_cell_xls(r.e);
}
var OFFCRYPTO = {};
@@ -4908,6 +4919,7 @@ function parse_fills(t, opts) {
/* 18.8.32 patternFill CT_PatternFill */
case '':
if(y.patternType) fill.patternType = y.patternType;
break;
case '': case '': break;
@@ -4953,6 +4965,7 @@ function parse_numFmts(t, opts) {
var f=unescapexml(utf8read(y.formatCode)), j=parseInt(y.numFmtId,10);
styles.NumberFmt[j] = f; if(j>0) SSF.load(f,j);
} break;
+ case '': break;
default: if(opts.WTF) throw new Error('unrecognized ' + y[0] + ' in numFmts');
}
}
@@ -4985,7 +4998,7 @@ function parse_cellXfs(t, opts) {
case '': break;
/* 18.8.1 alignment CT_CellAlignment */
- case '': break;
+ case '': case '': break;
/* 18.8.33 protection CT_CellProtection */
case '': case '': break;
@@ -5790,8 +5803,8 @@ function parse_RgceArea_BIFF2(blob, length, opts) {
}
/* 2.5.198.105 TODO */
-function parse_RgceAreaRel(blob, length) {
- var r=blob.read_shift(2), R=blob.read_shift(2);
+function parse_RgceAreaRel(blob, length, opts) {
+ var r=blob.read_shift(length == 12 ? 4 : 2), R=blob.read_shift(length == 12 ? 4 : 2);
var c=parse_ColRelU(blob, 2);
var C=parse_ColRelU(blob, 2);
return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
@@ -5857,9 +5870,9 @@ function parse_PtgArea3d(blob, length, opts) {
}
/* 2.5.198.29 */
-function parse_PtgAreaErr(blob, length) {
+function parse_PtgAreaErr(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- blob.l += 8;
+ blob.l += opts && opts.biff > 8 ? 12 : 8;
return [type];
}
/* 2.5.198.30 */
@@ -5876,9 +5889,9 @@ function parse_PtgAreaErr3d(blob, length, opts) {
}
/* 2.5.198.31 */
-function parse_PtgAreaN(blob, length) {
+function parse_PtgAreaN(blob, length, opts) {
var type = (blob[blob.l++] & 0x60) >> 5;
- var area = parse_RgceAreaRel(blob, 8);
+ var area = parse_RgceAreaRel(blob, opts && opts.biff > 8 ? 12 : 8, opts);
return [type, area];
}
@@ -6143,6 +6156,19 @@ function parse_PtgRefErr(blob, length, opts) {
return [type];
}
+/* 2.5.198.87 */
+function parse_PtgRefErr3d(blob, length, opts) {
+ var type = (blob[blob.l++] & 0x60) >> 5;
+ var ixti = blob.read_shift(2);
+ var w = 4;
+ if(opts) switch(opts.biff) {
+ case 5: throw new Error("PtgRefErr3d -- 5"); // TODO: find test case
+ case 12: w = 6; break;
+ }
+ blob.l += w;
+ return [type, ixti];
+}
+
/* 2.5.198.26 */
var parse_PtgAdd = parseread1;
/* 2.5.198.45 */
@@ -6186,8 +6212,6 @@ var parse_PtgUplus = parseread1;
var parse_PtgMemErr = parsenoop;
/* 2.5.198.73 */
var parse_PtgMemNoMem = parsenoop;
-/* 2.5.198.87 */
-var parse_PtgRefErr3d = parsenoop;
/* 2.5.198.92 */
var parse_PtgTbl = parsenoop;
@@ -6433,6 +6457,7 @@ var PtgBinOp = {
PtgSub: "-"
};
function stringify_formula(formula, range, cell, supbooks, opts) {
+ //console.log(formula);
var _range = /*range != null ? range :*/ {s:{c:0, r:0},e:{c:0, r:0}};
var stack = [], e1, e2, type, c, ixti, nameidx, r, sname="";
if(!formula[0] || !formula[0][0]) return "";
@@ -6442,7 +6467,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
var f = formula[0][ff];
//console.log("++",f, stack)
switch(f[0]) {
- /* 2.2.2.1 Unary Operator Tokens */
/* 2.5.198.93 */
case 'PtgUminus': stack.push("-" + stack.pop()); break;
/* 2.5.198.95 */
@@ -6450,7 +6474,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.81 */
case 'PtgPercent': stack.push(stack.pop() + "%"); break;
- /* 2.2.2.1 Binary Value Operator Token */
case 'PtgAdd': /* 2.5.198.26 */
case 'PtgConcat': /* 2.5.198.43 */
case 'PtgDiv': /* 2.5.198.45 */
@@ -6470,7 +6493,7 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 1: sp = fill("\r", formula[0][last_sp][1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
e2 = e2 + sp;
last_sp = -1;
@@ -6478,7 +6501,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+PtgBinOp[f[0]]+e1);
break;
- /* 2.2.2.1 Binary Reference Operator Token */
/* 2.5.198.67 */
case 'PtgIsect':
e1 = stack.pop(); e2 = stack.pop();
@@ -6493,7 +6515,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(e2+":"+e1);
break;
- /* 2.2.2.3 Control Tokens "can be ignored" */
/* 2.5.198.34 */
case 'PtgAttrChoose': break;
/* 2.5.198.35 */
@@ -6520,11 +6541,11 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(sname + "!" + encode_cell(c));
break;
- /* Function Call */
/* 2.5.198.62 */
case 'PtgFunc':
/* 2.5.198.63 */
case 'PtgFuncVar':
+ //console.log(f[1]);
/* f[1] = [argc, func, type] */
var argc = f[1][0], func = f[1][1];
if(!argc) argc = 0;
@@ -6544,10 +6565,15 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
case 'PtgStr': stack.push('"' + f[1] + '"'); break;
/* 2.5.198.57 */
case 'PtgErr': stack.push(f[1]); break;
+ /* 2.5.198.31 TODO */
+ case 'PtgAreaN':
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
+ break;
/* 2.5.198.27 TODO: fixed points */
case 'PtgArea':
- type = f[1][0]; r = shift_range_xls(f[1][1], _range);
- stack.push(encode_range_xls(r));
+ type = f[1][0]; r = shift_range_xls(f[1][1], _range, opts);
+ stack.push(encode_range_xls(r, opts));
break;
/* 2.5.198.28 */
case 'PtgArea3d': // TODO: lots of stuff
@@ -6560,7 +6586,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("SUM(" + stack.pop() + ")");
break;
- /* Expression Prefixes */
/* 2.5.198.37 */
case 'PtgAttrSemi': break;
@@ -6591,7 +6616,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push(externbook.body);
break;
- /* 2.2.2.4 Display Tokens */
/* 2.5.198.80 */
case 'PtgParen':
var lp = '(', rp = ')';
@@ -6600,10 +6624,10 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
switch(formula[0][last_sp][1][0]) {
case 2: lp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
case 3: lp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
- case 4: rp = fill(" ", formula[0][last_sp][1][1]) + lp; break;
- case 5: rp = fill("\r", formula[0][last_sp][1][1]) + lp; break;
+ case 4: rp = fill(" ", formula[0][last_sp][1][1]) + rp; break;
+ case 5: rp = fill("\r", formula[0][last_sp][1][1]) + rp; break;
default:
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + formula[0][last_sp][1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + formula[0][last_sp][1][0]);
}
last_sp = -1;
}
@@ -6612,6 +6636,9 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
/* 2.5.198.86 */
case 'PtgRefErr': stack.push('#REF!'); break;
+ /* 2.5.198.87 */
+ case 'PtgRefErr3d': stack.push('#REF!'); break;
+
/* */
/* 2.5.198.58 TODO */
case 'PtgExp':
@@ -6641,7 +6668,6 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("{" + stringify_array(f[1]) + "}");
break;
- /* 2.2.2.5 Mem Tokens */
/* 2.5.198.70 TODO: confirm this is a non-display */
case 'PtgMemArea':
//stack.push("(" + f[2].map(encode_range).join(",") + ")");
@@ -6665,31 +6691,31 @@ function stringify_formula(formula, range, cell, supbooks, opts) {
stack.push("");
break;
- /* 2.5.198.29 TODO */
- case 'PtgAreaErr': break;
-
- /* 2.5.198.31 TODO */
- case 'PtgAreaN': stack.push(""); break;
-
- /* 2.5.198.87 TODO */
- case 'PtgRefErr3d': break;
+ /* 2.5.198.29 */
+ case 'PtgAreaErr': stack.push("#REF!");
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;
- default: throw 'Unrecognized Formula Token: ' + f;
+ default: throw new Error('Unrecognized Formula Token: ' + f);
}
var PtgNonDisp = ['PtgAttrSpace', 'PtgAttrSpaceSemi', 'PtgAttrGoto'];
if(last_sp >= 0 && PtgNonDisp.indexOf(formula[0][ff][0]) == -1) {
f = formula[0][last_sp];
+ var _left = true;
switch(f[1][0]) {
+ /* note: some bad XLSB files omit the PtgParen */
+ case 4: _left = false;
+ /* falls through */
case 0: sp = fill(" ", f[1][1]); break;
+ case 5: _left = false;
+ /* falls through */
case 1: sp = fill("\r", f[1][1]); break;
default:
sp = "";
- if(opts.WTF) throw new Error("Unexpected PtgSpace type " + f[1][0]);
+ if(opts.WTF) throw new Error("Unexpected PtgAttrSpaceType " + f[1][0]);
}
- stack.push(sp + stack.pop());
+ stack.push((_left ? sp : "") + stack.pop() + (_left ? "" : sp));
last_sp = -1;
}
//console.log("::",f, stack)
@@ -7589,7 +7615,8 @@ var Ftab = {
0x01D1: 'WEEKNUM',
0x01D2: 'AMORDEGRC',
0x01D3: 'AMORLINC',
-0x01D4: 'SHEETJS',
+0x01D4: 'CONVERT',
+0x02D4: 'SHEETJS',
0x01D5: 'ACCRINT',
0x01D6: 'ACCRINTM',
0x01D7: 'WORKDAY',
@@ -7798,9 +7825,54 @@ var FtabArgc = {
0x0178: 1, /* ROUNDBAHTDOWN */
0x0179: 1, /* ROUNDBAHTUP */
0x017A: 1, /* THAIYEAR */
+0x017E: 3, /* CUBEMEMBERPROPERTY */
+0x0181: 1, /* HEX2DEC */
+0x0188: 1, /* OCT2DEC */
+0x0189: 1, /* BIN2DEC */
+0x018C: 2, /* IMSUB */
+0x018D: 2, /* IMDIV */
+0x018E: 2, /* IMPOWER */
+0x018F: 1, /* IMABS */
+0x0190: 1, /* IMSQRT */
+0x0191: 1, /* IMLN */
+0x0192: 1, /* IMLOG2 */
+0x0193: 1, /* IMLOG10 */
+0x0194: 1, /* IMSIN */
+0x0195: 1, /* IMCOS */
+0x0196: 1, /* IMEXP */
+0x0197: 1, /* IMARGUMENT */
+0x0198: 1, /* IMCONJUGATE */
+0x0199: 1, /* IMAGINARY */
+0x019A: 1, /* IMREAL */
+0x019E: 4, /* SERIESSUM */
+0x019F: 1, /* FACTDOUBLE */
0x01A0: 1, /* SQRTPI */
+0x01A1: 2, /* QUOTIENT */
+0x01A4: 1, /* ISEVEN */
+0x01A5: 1, /* ISODD */
+0x01A6: 2, /* MROUND */
+0x01A8: 1, /* ERFC */
+0x01A9: 2, /* BESSELJ */
+0x01AA: 2, /* BESSELK */
+0x01AB: 2, /* BESSELY */
+0x01AC: 2, /* BESSELI */
+0x01AE: 3, /* XNPV */
+0x01B6: 3, /* TBILLEQ */
+0x01B7: 3, /* TBILLPRICE */
+0x01B8: 3, /* TBILLYIELD */
+0x01BB: 2, /* DOLLARDE */
+0x01BC: 2, /* DOLLARFR */
+0x01BD: 2, /* NOMINAL */
+0x01BE: 2, /* EFFECT */
+0x01BF: 6, /* CUMPRINC */
+0x01C0: 6, /* CUMIPMT */
0x01C1: 2, /* EDATE */
0x01C2: 2, /* EOMONTH */
+0x01D0: 2, /* RANDBETWEEN */
+0x01D4: 3, /* CONVERT */
+0x01DC: 2, /* FVSCHEDULE */
+0x01DF: 1, /* CUBESETCOUNT */
+0x01E0: 2, /* IFERROR */
0xFFFF: 0
};
/* [MS-XLSX] 2.2.3 Functions */
@@ -9025,6 +9097,13 @@ function parse_wb_defaults(wb) {
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
}
+
+/* TODO: validate workbook */
+function check_wb(wb) {
+ if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
+ for(var i = 0; i < wb.SheetNames.length; ++i) for(var j = 0; j < i; ++j)
+ if(wb.SheetNames[i] == wb.SheetNames[j]) throw new Error("Duplicate Sheet Name: " + wb.SheetNames[i]);
+}
/* 18.2 Workbook */
var wbnsregex = /<\w+:workbook/;
function parse_wb_xml(data, opts) {
@@ -9045,7 +9124,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.13 fileVersion CT_FileVersion ? */
case '': break;
+ case '': case '': break;
/* 18.2.12 fileSharing CT_FileSharing ? */
case '': break;
@@ -9053,6 +9132,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.28 workbookPr CT_WorkbookPr ? */
case '': delete y[0]; wb.WBProps = y; break;
+ case '': break;
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
case '': case '': break;
/* 18.2.30 workbookView CT_BookView + */
case '': break;
/* 18.2.20 sheets CT_Sheets 1 */
case '': case '': break; // aggregate sheet
/* 18.2.19 sheet CT_Sheet + */
case '': break;
/* 18.2.15 functionGroups CT_FunctionGroups ? */
case '': break;
@@ -9088,6 +9170,7 @@ function parse_wb_xml(data, opts) {
/* 18.2.2 calcPr CT_CalcPr ? */
case '': delete y[0]; wb.CalcPr = y; break;
+ case '': break;
/* 18.2.16 oleSize CT_OleSize ? (ref required) */
case '': pass=false; break;
- default: if(!pass && opts.WTF) throw 'unrecognized ' + y[0] + ' in workbook';
+ default: if(!pass && opts.WTF) throw new Error('unrecognized ' + y[0] + ' in workbook');
}
});
if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
@@ -9593,7 +9676,7 @@ function xlml_normalize(d) {
/* TODO: Everything */
/* UOS uses CJK in tags */
-var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
+var xlmlregex = /<(\/?)([^\s?>!\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
//var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
function parse_xlml_xml(d, opts) {
var str = debom(xlml_normalize(d));
@@ -9613,6 +9696,7 @@ function parse_xlml_xml(d, opts) {
var cstys = [], csty;
var arrayf = [];
xlmlregex.lastIndex = 0;
+ str = str.replace(//mg,"");
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'Data':
if(state[state.length-1][1]) break;
@@ -10369,9 +10453,7 @@ function parse_workbook(blob, options) {
case 'RefreshAll': wb.opts.RefreshAll = val; break;
case 'BookBool': break; // TODO
case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break;
- case 'MTRSettings': {
- if(val[0] && val[1]) throw "Unsupported threads: " + val;
- } break; // TODO: actually support threads
+ case 'MTRSettings': break;
case 'CalcCount': wb.opts.CalcCount = val; break;
case 'CalcDelta': wb.opts.CalcDelta = val; break;
case 'CalcIter': wb.opts.CalcIter = val; break;
@@ -13106,6 +13188,7 @@ function write_binary_type(out, opts) {
}
function writeSync(wb, opts) {
+ check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
case 'xml': return write_string_type(write_xlml(wb, o), o);