diff --git a/README.md b/README.md
index 3b171ab..73fd798 100644
--- a/README.md
+++ b/README.md
@@ -484,7 +484,7 @@ will not be generated; the parser `sheetStubs` option must be set to `true`.
### Formulae
The A1-style formula string is stored in the `f` field. Even though different
-file formats store the formulae in different ways, the formats are converted.
+file formats store the formulae in different ways, the formats are translated.
Shared formulae are decompressed and each cell has the correct formula.
diff --git a/bits/10_ssf.js b/bits/10_ssf.js
index bca57a7..715a869 100644
--- a/bits/10_ssf.js
+++ b/bits/10_ssf.js
@@ -310,7 +310,18 @@ function hashq(str/*:string*/)/*:string*/ {
return o;
}
function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
-function dec(val/*:number*/, d/*:number*/)/*:number*/ { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+function dec(val/*:number*/, d/*:number*/)/*:number*/ {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 0;
+ }
+ return Math.round((val-Math.floor(val))*Math.pow(10,d));
+}
+function carry(val/*:number*/, d/*:number*/)/*:number*/ {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 1;
+ }
+ return 0;
+}
function flr(val/*:number*/)/*:string*/ { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/ {
if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
@@ -332,8 +343,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign);
if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
if((r = fmt.match(dec1))) {
- // $FlowIgnore
- o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
+ o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", /*::(*/r/*::||[""])*/[1].length-$1.length); });
return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
}
fmt = fmt.replace(/^#+([0.])/, "$1");
@@ -342,7 +352,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
}
if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0));
if((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
- return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val))) + "." + pad0(dec(val, r[1].length),r[1].length);
+ return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length);
}
if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) {
diff --git a/bits/65_fods.js b/bits/65_fods.js
index fee62c9..ec9bdf1 100644
--- a/bits/65_fods.js
+++ b/bits/65_fods.js
@@ -6,6 +6,16 @@ function ods_to_csf_formula(f/*:string*/)/*:string*/ {
f = f.substr(1);
if(f.charCodeAt(0) == 61) f = f.substr(1);
}
+ f = f.replace(/COM\.MICROSOFT\./g, "");
/* Part 3 Section 5.8 References */
- return f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, "$1").replace(/\./g, "");
+ f = f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, function($$, $1) { return $1.replace(/\./g,""); });
+ /* TODO: something other than this */
+ f = f.replace(/\[.(#[A-Z]*[?!])\]/g, "$1");
+ return f.replace(/[;~]/g,",").replace(/\|/g,";");
+}
+
+function csf_to_ods_formula(f/*:string*/)/*:string*/ {
+ var o = "of:=" + f.replace(crefregex, "$1[.$2$3$4$5]").replace(/\]:\[/g,":");
+ /* TODO: something other than this */
+ return o.replace(/;/g, "|").replace(/,/g,";");
}
diff --git a/bits/67_wsxml.js b/bits/67_wsxml.js
index 6ef817e..2218da9 100644
--- a/bits/67_wsxml.js
+++ b/bits/67_wsxml.js
@@ -126,7 +126,7 @@ function write_ws_xml_cols(ws, cols)/*:string*/ {
}
function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
- if(cell.v === undefined || cell.t === 'z') return "";
+ if(cell.v === undefined && cell.f === undefined || cell.t === 'z') return "";
var vv = "";
var oldt = cell.t, oldv = cell.v;
switch(cell.t) {
@@ -152,7 +152,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
case 'd': o.t = "d"; break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
- default:
+ default: if(cell.v == null) { delete cell.t; break; }
if(opts.bookSST) {
v = writetag('v', ''+get_sst_id(opts.Strings, cell.v));
o.t = "s"; break;
@@ -160,6 +160,10 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
o.t = "str"; break;
}
if(cell.t != oldt) { cell.t = oldt; cell.v = oldv; }
+ if(cell.f) {
+ var ff = cell.F && cell.F.substr(0, ref.length) == ref ? {t:"array", ref:cell.F} : null;
+ v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : "");
+ }
return writextag('c', v, o);
}
@@ -215,7 +219,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
if((cref=d.match(match_v))!= null && /*::cref != null && */cref[1] !== '') p.v=unescapexml(cref[1]);
if(opts.cellFormula) {
if((cref=d.match(match_f))!= null && /*::cref != null && */cref[1] !== '') {
- p.f=unescapexml(utf8read(cref[1]));
+ /* TODO: match against XLSXFutureFunctions */
+ p.f=unescapexml(utf8read(cref[1])).replace(/_xlfn\./,"");
if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="array"') > -1) {
p.F = (d.match(refregex)||[])[1];
if(p.F.indexOf(":") > -1) arrayf.push([safe_decode_range(p.F), p.F]);
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 351d4fb..992c9a8 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -108,7 +108,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
case 'DateTime':
cell.v = (Date.parse(xml) - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
if(cell.v !== cell.v) cell.v = unescapexml(xml);
- else if(cell.v >= 1 && cell.v<60) cell.v = cell.v -1;
+ else if(cell.v<60) cell.v = cell.v -1;
if(!nf || nf == "General") nf = "yyyy-mm-dd";
/* falls through */
case 'Number':
@@ -250,6 +250,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
sheetname = unescapexml(tmp.Name);
cursheet = {};
mergecells = [];
+ arrayf = [];
}
break;
case 'Table':
@@ -759,10 +760,14 @@ function write_sty_xlml(wb, opts)/*:string*/ {
}
/* TODO */
function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
- if(!cell || cell.v === undefined) return " | ";
+ if(!cell || cell.v == undefined && cell.f == undefined) return " | ";
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+ if(cell.F && cell.F.substr(0, ref.length) == ref) {
+ var end = decode_cell(cell.F.substr(ref.length + 1));
+ attr["ss:ArrayRange"] = "RC:R" + (end.r == addr.r ? "" : "[" + (end.r - addr.r) + "]") + "C" + (end.c == addr.c ? "" : "[" + (end.c - addr.c) + "]");
+ }
if(ws['!merges']) {
var marr = ws['!merges'];
@@ -780,9 +785,9 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
case 'e': t = 'Error'; p = BErr[cell.v]; break;
case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
- case 's': t = 'String'; p = escapexml(cell.v||""); break;
+ case 's': t = 'String'; p = escapexml(cell.v||""); break;
}
- var m = '' + p + '';
+ var m = '' + (cell.v != null ? p : "") + '';
return writextag("Cell", m, attr);
}
diff --git a/bits/76_xls.js b/bits/76_xls.js
index 370c8ca..5fff527 100644
--- a/bits/76_xls.js
+++ b/bits/76_xls.js
@@ -122,6 +122,19 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(cell.r + 1 > range.e.r) range.e.r = cell.r + 1;
if(cell.c + 1 > range.e.c) range.e.c = cell.c + 1;
}
+ if(options.cellFormula && line.f) {
+ for(var afi = 0; afi < array_formulae.length; ++afi) {
+ if(array_formulae[afi][0].s.c > cell.c) continue;
+ if(array_formulae[afi][0].s.r > cell.r) continue;
+ if(array_formulae[afi][0].e.c < cell.c) continue;
+ if(array_formulae[afi][0].e.r < cell.r) continue;
+ line.F = encode_range(array_formulae[afi][0]);
+ if(array_formulae[afi][0].s.c != cell.c) delete line.f;
+ if(array_formulae[afi][0].s.r != cell.r) delete line.f;
+ if(line.f) line.f = "" + stringify_formula(array_formulae[afi][1], range, cell, supbooks, opts);
+ break;
+ }
+ }
if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
else out[last_cell] = line;
};
@@ -274,7 +287,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
else cur_sheet = (Directory[s] || {name:""}).name;
mergecells = [];
objects = [];
+ array_formulae = []; opts.arrayf = array_formulae;
} break;
+
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
safe_format_xf(temp_val, options, wb.opts.Date1904);
@@ -299,44 +314,43 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
}
} break;
case 'Formula': {
- switch(val.val) {
- case 'String': last_formula = val; break;
- case 'Array Formula': throw "Array Formula unsupported";
- default:
- temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt}/*:any*/);
- temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) {
- var _f = val.formula;
- if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
- var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
- var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- }
- safe_format_xf(temp_val, options, wb.opts.Date1904);
- addcell(val.cell, temp_val, options);
- last_formula = val;
+ if(val.val == 'String') { last_formula = val; break; }
+ temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt}/*:any*/);
+ temp_val.XF = XFs[temp_val.ixfe];
+ if(options.cellFormula) {
+ var _f = val.formula;
+ if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
+ var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
+ var _fe = encode_cell({r:_fr, c:_fc});
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ else temp_val.F = (out[_fe] || {}).F;
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
+ safe_format_xf(temp_val, options, wb.opts.Date1904);
+ addcell(val.cell, temp_val, options);
+ last_formula = val;
} break;
case 'String': {
- if(last_formula) {
+ if(last_formula) { /* technically always true */
last_formula.val = val;
- temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
+ temp_val = ({v:val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) {
+ temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ }
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
- }
+ } else throw new Error("String record expects Formula");
} break;
case 'Array': {
array_formulae.push(val);
- if(options.cellFormula && out[last_cell]) {
+ var _arraystart = encode_cell(val[0].s);
+ if(options.cellFormula && out[_arraystart]) {
if(!last_formula) break; /* technically unreachable */
- if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
- out[last_cell].F = encode_range(val[0]);
+ if(!_arraystart || !out[_arraystart]) break;
+ out[_arraystart].f = ""+stringify_formula(val[1], range, val[0], supbooks, opts);
+ out[_arraystart].F = encode_range(val[0]);
}
} break;
case 'ShrFmla': {
@@ -375,6 +389,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options);
break;
+
case 'Dimensions': {
if(file_depth === 1) range = val; /* TODO: stack */
} break;
diff --git a/bits/78_writebiff.js b/bits/78_writebiff.js
index 4b67c2c..6e4b439 100644
--- a/bits/78_writebiff.js
+++ b/bits/78_writebiff.js
@@ -57,20 +57,20 @@ function write_BIFF2LABEL(r, c, val) {
}
function write_ws_biff_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
- switch(cell.t) {
+ if(cell.v != null) switch(cell.t) {
case 'n':
if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536))
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v));
else
write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v));
- break;
- case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); break;
+ return;
+ case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
/* TODO: codepage, sst */
case 's': case 'str':
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v));
- break;
- default: write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
+ return;
}
+ write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
}
function write_biff_ws(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
diff --git a/bits/80_parseods.js b/bits/80_parseods.js
index 71cdbc0..6a6af23 100644
--- a/bits/80_parseods.js
+++ b/bits/80_parseods.js
@@ -78,6 +78,7 @@ var parse_content_xml = (function() {
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
if(opts.cellFormula) {
+ if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-matrix-columns-spanned'],10) || 0;
diff --git a/bits/81_writeods.js b/bits/81_writeods.js
index 7d47f3a..cd23d56 100644
--- a/bits/81_writeods.js
+++ b/bits/81_writeods.js
@@ -27,11 +27,22 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
+ var fmla = "";
+ if(cell && cell.f) {
+ fmla = ' 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 = "";
+ }
+ }
if(cell) switch(cell.t) {
- case 'b': o.push(cell_begin + mxml + vt + '"boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '">' + p_begin + (cell.v ? 'TRUE' : 'FALSE') + p_end + cell_end); break;
- case 'n': o.push(cell_begin + mxml + vt + '"float" office:value="' + cell.v + '">' + p_begin + (cell.w||cell.v) + p_end + cell_end); break;
- case 's': case 'str': o.push(cell_begin + mxml + vt + '"string">' + p_begin + escapexml(cell.v) + p_end + cell_end); break;
- case 'd': o.push(cell_begin + mxml + vt + '"date" office:date-value="' + (new Date(cell.v).toISOString()) + '">' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
+ 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="' + (new Date(cell.v).toISOString()) + '"' + fmla + '>' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
@@ -46,7 +57,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var o = [XML_HEADER];
/* 3.1.3.2 */
if(opts.bookType == "fods") o.push('');
- else o.push('\n'); // TODO
+ else o.push('\n'); // TODO
o.push(' \n');
o.push(' \n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
diff --git a/bits/90_utils.js b/bits/90_utils.js
index b1f3b89..fccdd0c 100644
--- a/bits/90_utils.js
+++ b/bits/90_utils.js
@@ -156,9 +156,15 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
rr = encode_row(R);
for(C = r.s.c; C <= r.e.c; ++C) {
val = sheet[cols[C] + rr];
- txt = val !== undefined ? ''+format_cell(val) : "";
- for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
- txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ if(val == null) txt = "";
+ else if(val.v != null) {
+ txt = ''+format_cell(val);
+ for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
+ txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ } else if(val.f != null && !val.F) {
+ txt = '=' + val.f; if(txt.indexOf(",") >= 0) txt = '"' + txt.replace(qreg, '""') + '"';
+ } else txt = "";
+ /* NOTE: Excel CSV does not support array formulae */
row += (C === r.s.c ? "" : FS) + txt;
}
out += row + RS;
diff --git a/multiformat.lst b/multiformat.lst
index 1befc01..64d2900 100644
--- a/multiformat.lst
+++ b/multiformat.lst
@@ -10,13 +10,19 @@ cell_style_simple .xls .xlsb .xlsx .xml
comments_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
# yes-csv
custom_properties .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
+# no-formula (defined names)
defined_names_simple .xls .xlsb .xlsx .xml
-#formula_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
+# yes-formula
+# no-csv (randbetween) note: ODS does not support many XLSX functions
+formula_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
+# yes-csv
formulae_test_simple .xls .xlsb .xlsx .xml
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
merge_cells .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
+# no-formula (defined names)
named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
+# yes-formula
# no-csv (macro serialization in xml)
number_format .xls .xlsb .xlsm .xls.xml .xlsb.xml .xlsm.xml
number_format_entities .xls .xlsb .xlsx .xml
diff --git a/test.js b/test.js
index 30b72e5..4c55568 100644
--- a/test.js
+++ b/test.js
@@ -838,9 +838,7 @@ function seq(end, start) {
}
describe('roundtrip features', function() {
- var bef = (function() {
- X = require(modp);
- });
+ var bef = (function() { X = require(modp); });
if(typeof before != 'undefined') before(bef);
else it('before', bef);
describe('should parse core properties and custom properties', function() {
@@ -907,14 +905,18 @@ describe('roundtrip features', function() {
});
});
- describe('xls to xlsx conversions', function() { [
- ['XLS', 'formula_stress_test.xls'],
- ['XML', 'formula_stress_test.xls.xml']
+ describe('should preserve formulae', function() { [
+ ['xlml', paths.fstxml],
+ ['xlsx', paths.fstxlsx],
+ ['ods', paths.fstods]
].forEach(function(w) {
- it('should be able to write ' + w[0] + ' files', function() {
- var xls = X.readFile('./test_files/' + w[1], {cellNF:true});
- X.writeFile(xls, './tmp/' + w[1] + '.xlsx', {bookSST:true});
- X.writeFile(xls, './tmp/' + w[1] + '.xlsb', {bookSST:true});
+ it(w[0], function() {
+ var wb1 = X.readFile(w[1], {cellFormula:true});
+ if(w[0] == 'ods') X.writeFile(wb1, "./tmp/_.ods", {bookType:"ods"});
+ var wb2 = X.read(X.write(wb1, {bookType:w[0], type:"buffer"}), {cellFormula:true, type:"buffer"});
+ wb1.SheetNames.forEach(function(n) {
+ assert.equal( X.utils.sheet_to_formulae(wb1.Sheets[n]).sort().join("\n"), X.utils.sheet_to_formulae(wb2.Sheets[n]).sort().join("\n") );
+ });
});
});
});
@@ -1209,7 +1211,7 @@ describe('encryption', function() {
describe('multiformat tests', function() {
var mfopts = opts;
var mft = fs.readFileSync('multiformat.lst','utf-8').split("\n");
-var csv = true;
+var csv = true, formulae = false;
mft.forEach(function(x) {
if(x[0]!="#") describe('MFT ' + x, function() {
var fil = {}, f = [], r = x.split(/\s+/);
@@ -1239,10 +1241,20 @@ mft.forEach(function(x) {
cmparr(f.map(function(x) { return X.utils.sheet_to_csv(x.Sheets[name]); }));
});
} : null);
+ it('should have the same formulae', formulae ? function() {
+ cmparr(f.map(function(x) { return x.SheetNames; }));
+ var names = f[0].SheetNames;
+ names.forEach(function(name) {
+ cmparr(f.map(function(x) { return X.utils.sheet_to_formulae(x.Sheets[name]).sort(); }));
+ });
+ } : null);
+
});
else x.split(/\s+/).forEach(function(w) { switch(w) {
case "no-csv": csv = false; break;
case "yes-csv": csv = true; break;
+ case "no-formula": formulae = false; break;
+ case "yes-formula": formulae = true; break;
}});
});
});
diff --git a/test_files b/test_files
index 8ab3308..e0b7060 160000
--- a/test_files
+++ b/test_files
@@ -1 +1 @@
-Subproject commit 8ab3308622f83ac47a0ee584d61e11357b01f925
+Subproject commit e0b7060b10ef972ad48869faa4efffd089e1a428
diff --git a/tests.lst b/tests.lst
index 1e096d3..691e217 100644
--- a/tests.lst
+++ b/tests.lst
@@ -29,17 +29,6 @@ sushi.xlsb
text_and_numbers.xlsb
time_stress_test_1.xlsb.pending
xlsx-stream-d-date-cell.xlsb
-2013/apachepoi_29982.xls.xlsb
-2013/apachepoi_43251.xls.xlsb
-2013/apachepoi_44593.xls.xlsb ## xlsb loop timeout
-2013/apachepoi_44643.xls.xlsb
-2013/apachepoi_44958.xls.xlsb
-2013/apachepoi_46136-NoWarnings.xls.xlsb
-2013/apachepoi_48968.xls.xlsb
-2013/apachepoi_50939.xls.xlsb
-2013/apachepoi_54016.xls.xlsb
-2013/apachepoi_ReadOnlyRecommended.xls.xlsb
-2013/apachepoi_testArraysAndTables.xls.xlsb
AutoFilter.xlsx
ErrorTypes.xlsx
LONumbers-2010.xlsx
@@ -199,6 +188,7 @@ apachepoi_DataTableCities.xlsx
apachepoi_DataValidationEvaluations.xlsx
apachepoi_DataValidations-49244.xlsx
# apachepoi_DateFormatTests.xlsx # xlml
+apachepoi_DateFormatTests.xlsx.xlsx
apachepoi_ElapsedFormatTests.xlsx
apachepoi_ExcelTables.xlsx
apachepoi_ForShifting.xlsx
@@ -249,6 +239,7 @@ apachepoi_WithTwoCharts.xlsx
apachepoi_WithVariousData.xlsx
apachepoi_XSSFSheet.copyRows.xlsx
apachepoi_atp.xlsx
+apachepoi_bug60858.xlsx
apachepoi_chartTitle_noTitle.xlsx
apachepoi_chartTitle_withTitle.xlsx
apachepoi_chart_sheet.xlsx.pending
@@ -362,6 +353,8 @@ openpyxl_r_null_file.xlsx.pending
phonetic_text.xlsx
pivot_table_named_range.xlsx
rich_text_stress.xlsx
+roo-xls_numbers1.xlsx
+roo-xls_type_excel.xlsx
roo_1900_base.xlsx
roo_1904_base.xlsx
roo_Bibelbund.xlsx
@@ -404,6 +397,44 @@ roo_whitespace.xlsx
roo_x000D.xlsx
roo_zero-padded-number.xlsx
smart_tags_2007.xlsx
+spout-xlsx_attack_billion_laughs.xlsx
+spout-xlsx_attack_quadratic_blowup.xlsx
+# spout-xlsx_file_corrupted.xlsx
+spout-xlsx_file_with_no_sheets_in_workbook_xml.xlsx
+# spout-xlsx_file_with_sheet_xml_not_matching_content_types.xlsx
+spout-xlsx_one_sheet_with_inline_strings.xlsx
+spout-xlsx_one_sheet_with_invalid_xml_characters.xlsx
+spout-xlsx_one_sheet_with_shared_multiline_strings.xlsx
+spout-xlsx_one_sheet_with_shared_strings.xlsx
+spout-xlsx_one_sheet_with_shared_strings_containing_text_and_hyperlink_in_same_cell.xlsx
+spout-xlsx_one_sheet_with_shared_strings_missing_unique_count.xlsx
+spout-xlsx_one_sheet_with_shared_strings_missing_unique_count_and_count.xlsx
+spout-xlsx_sheet_with_all_cell_types.xlsx
+spout-xlsx_sheet_with_custom_date_formats_and_no_apply_number_format.xlsx
+spout-xlsx_sheet_with_dates_and_times.xlsx
+spout-xlsx_sheet_with_different_numeric_value_dates.xlsx
+spout-xlsx_sheet_with_different_numeric_value_times.xlsx
+spout-xlsx_sheet_with_dimensions_and_empty_cells.xlsx
+spout-xlsx_sheet_with_empty_cells.xlsx
+spout-xlsx_sheet_with_empty_rows_and_missing_row_index.xlsx
+spout-xlsx_sheet_with_empty_shared_string.xlsx
+spout-xlsx_sheet_with_formulas.xlsx
+spout-xlsx_sheet_with_lots_of_shared_strings.xlsx
+spout-xlsx_sheet_with_missing_cell_reference.xlsx
+spout-xlsx_sheet_with_no_cells.xlsx
+spout-xlsx_sheet_with_no_shared_strings_file.xlsx
+spout-xlsx_sheet_with_prefixed_xml_files.xlsx
+spout-xlsx_sheet_with_preserve_space_shared_strings.xlsx
+spout-xlsx_sheet_with_pronunciation.xlsx
+spout-xlsx_sheet_with_same_numeric_value_date_formatted_differently.xlsx
+spout-xlsx_sheet_with_untrimmed_inline_strings.xlsx
+spout-xlsx_sheet_with_zeros_in_row.xlsx
+spout-xlsx_sheet_without_dimensions_and_empty_cells.xlsx
+spout-xlsx_sheet_without_dimensions_but_spans_and_empty_cells.xlsx
+spout-xlsx_two_sheets_with_custom_names.xlsx
+spout-xlsx_two_sheets_with_inline_strings.xlsx
+spout-xlsx_two_sheets_with_shared_strings.xlsx
+spout-xlsx_two_sheets_with_sheets_definition_in_reverse_order.xlsx
spreadsheet-parsexlsx_Test.xlsx
spreadsheet-parsexlsx_bug-10.xlsx
spreadsheet-parsexlsx_bug-11.xlsx
@@ -426,7 +457,7 @@ spreadsheet-parsexlsx_bug-5.xlsx
spreadsheet-parsexlsx_bug-57.xlsx
spreadsheet-parsexlsx_bug-6-2.xlsx
spreadsheet-parsexlsx_bug-6.xlsx
-spreadsheet-parsexlsx_bug-61.xlsx
+# spreadsheet-parsexlsx_bug-61.xlsx
spreadsheet-parsexlsx_bug-7.xlsx
spreadsheet-parsexlsx_bug-8.xlsx
spreadsheet-parsexlsx_bug-lock.xlsx
@@ -469,9 +500,9 @@ hyperlink_no_rels.xlsm
number_format.xlsm
number_format_russian.xlsm
numfmt_1_russian.xlsm
-openpyxl_r_vba+comments.xlsm
-openpyxl_r_vba-comments-saved.xlsm
-openpyxl_r_vba-test.xlsm
+# openpyxl_r_vba+comments.xlsm
+# openpyxl_r_vba-comments-saved.xlsm
+# openpyxl_r_vba-test.xlsm
# pivot_table_test.xlsm # xlml
roo_1900_base.xlsm
roo_1904_base.xlsm
@@ -507,6 +538,8 @@ formula_stress_test.ods
merge_cells.ods
number_format.ods
rich_text_stress.ods
+roo-xls_numbers1.ods
+roo-xls_type_excel.ods
roo_Bibelbund.ods
roo_Bibelbund1.ods
roo_advanced_header.ods
@@ -534,6 +567,32 @@ roo_time-test.ods
roo_type_excel.ods
roo_type_excelx.ods
roo_whitespace.ods
+spout-ods_attack_billion_laughs.ods
+spout-ods_attack_quadratic_blowup.ods
+# spout-ods_file_corrupted.ods
+spout-ods_file_generated_by_excel_2010_windows.ods
+spout-ods_file_generated_by_excel_office_online.ods
+spout-ods_file_generated_by_libre_office.ods
+spout-ods_one_sheet_with_strings.ods
+spout-ods_sheet_with_all_cell_types.ods
+spout-ods_sheet_with_dates_and_times.ods
+spout-ods_sheet_with_empty_cells.ods
+spout-ods_sheet_with_empty_rows.ods
+spout-ods_sheet_with_formulas.ods
+spout-ods_sheet_with_hyperlinks.ods
+spout-ods_sheet_with_inline_font_formatting.ods
+spout-ods_sheet_with_invalid_date_time.ods.pending
+spout-ods_sheet_with_multiline_string.ods
+spout-ods_sheet_with_no_cells.ods
+spout-ods_sheet_with_number_columns_repeated.ods
+spout-ods_sheet_with_number_rows_repeated.ods
+spout-ods_sheet_with_only_one_cell.ods
+spout-ods_sheet_with_undefined_value_type.ods
+spout-ods_sheet_with_untrimmed_strings.ods
+spout-ods_sheet_with_various_spaces.ods
+spout-ods_sheet_with_zeros_in_row.ods
+spout-ods_two_sheets_with_custom_names.ods
+spout-ods_two_sheets_with_strings.ods
sushi.ods
biff5/NumberFormatCondition.xls
biff5/RkNumber.xls
@@ -612,6 +671,7 @@ apachepoi_33082.xls
apachepoi_34775.xls
apachepoi_35564.xls
apachepoi_35565.xls
+# apachepoi_35897-type4.xls ## password
apachepoi_36947.xls
apachepoi_37376.xls
apachepoi_37630.xls
@@ -649,6 +709,7 @@ apachepoi_44840.xls
apachepoi_44861.xls
apachepoi_44891.xls
apachepoi_44958.xls
+apachepoi_44958_1.xls
apachepoi_45129.xls
apachepoi_45290.xls
apachepoi_45322.xls
@@ -659,6 +720,7 @@ apachepoi_45538_classic_Footer.xls
apachepoi_45538_classic_Header.xls
apachepoi_45538_form_Footer.xls
apachepoi_45538_form_Header.xls
+apachepoi_45565.xls
apachepoi_45672.xls
apachepoi_45720.xls
apachepoi_45761.xls
@@ -669,12 +731,14 @@ apachepoi_46137.xls
apachepoi_46250.xls
apachepoi_46368.xls
apachepoi_46445.xls
+apachepoi_46515.xls
apachepoi_46670_http.xls
apachepoi_46670_local.xls
apachepoi_46670_ref_airline.xls
apachepoi_46904.xls
apachepoi_47034.xls
apachepoi_47154.xls
+apachepoi_47245_test.xls
apachepoi_47251.xls
apachepoi_47251_1.xls
apachepoi_47701.xls
@@ -690,6 +754,7 @@ apachepoi_49096.xls
apachepoi_49185.xls
apachepoi_49219.xls
apachepoi_49237.xls
+apachepoi_49423.xls
apachepoi_49524.xls
apachepoi_49529.xls
apachepoi_49581.xls
@@ -717,6 +782,7 @@ apachepoi_51832.xls.pending
apachepoi_52527.xls
apachepoi_52575_main.xls
apachepoi_52575_source.xls
+apachepoi_53109.xls
apachepoi_53404.xls
apachepoi_53433.xls
apachepoi_53446.xls
@@ -730,24 +796,44 @@ apachepoi_54206.xls
apachepoi_54500.xls
apachepoi_54686_fraction_formats.xls
apachepoi_55341_CellStyleBorder.xls
+apachepoi_55668.xls
apachepoi_55906-MultiSheetRefs.xls
+apachepoi_55982.xls
apachepoi_56325.xls
+apachepoi_56325a.xls
apachepoi_56450.xls
apachepoi_56482.xls
apachepoi_56563a.xls
apachepoi_56563b.xls
apachepoi_56737.xls
+apachepoi_57003-FixedFunctionTestCaseData.xls
+apachepoi_57074.xls
+apachepoi_57163.xls
+apachepoi_57231_MixedGasReport.xls.pending
+apachepoi_57456.xls.pending
+apachepoi_57798.xls
+apachepoi_57925.xls
+apachepoi_59074.xls
+apachepoi_59264.xls
+apachepoi_59830.xls
+apachepoi_59858.xls
+apachepoi_60273.xls
+# apachepoi_60284.xls
apachepoi_AbnormalSharedFormulaFlag.xls
apachepoi_AreaErrPtg.xls
apachepoi_BOOK_in_capitals.xls
+apachepoi_Basic_Expense_Template_2011.xls
apachepoi_CodeFunctionTestCaseData.xls
apachepoi_ColumnStyle1dp.xls
apachepoi_ColumnStyle1dpColoured.xls
apachepoi_ColumnStyleNone.xls
apachepoi_ComplexFunctionTestCaseData.xls
+apachepoi_ConditionalFormattingSamples.xls
apachepoi_ContinueRecordProblem.xls
apachepoi_DBCSHeader.xls
apachepoi_DBCSSheetName.xls
+apachepoi_DGet.xls
+apachepoi_DStar.xls
apachepoi_DateFormats.xls
apachepoi_DeltaFunctionTestCaseData.xls
apachepoi_DrawingAndComments.xls
@@ -758,6 +844,7 @@ apachepoi_ErrPtg.xls
apachepoi_FactDoubleFunctionTestCaseData.xls
apachepoi_ForShifting.xls
apachepoi_FormatChoiceTests.xls
+apachepoi_FormatKM.xls
apachepoi_Formatting.xls
apachepoi_FormulaEvalTestData.xls
apachepoi_FormulaRefs.xls
@@ -775,6 +862,7 @@ apachepoi_IrrNpvTestCaseData.xls
apachepoi_MRExtraLines.xls
apachepoi_MatchFunctionTestCaseData.xls
apachepoi_MissingBits.xls
+apachepoi_NewStyleConditionalFormattings.xls
apachepoi_NoGutsRecords.xls
apachepoi_OddStyleRecord.xls
apachepoi_PercentPtg.xls
@@ -791,7 +879,9 @@ apachepoi_SheetWithDrawing.xls
apachepoi_ShrinkToFit.xls
apachepoi_Simple.xls
apachepoi_SimpleChart.xls
+apachepoi_SimpleMacro.xls
apachepoi_SimpleMultiCell.xls
+apachepoi_SimpleScatterChart.xls
apachepoi_SimpleWithAutofilter.xls
apachepoi_SimpleWithChoose.xls
apachepoi_SimpleWithColours.xls
@@ -811,6 +901,7 @@ apachepoi_StringContinueRecords.xls
apachepoi_StringFormulas.xls
apachepoi_SubtotalsNested.xls
apachepoi_TestRandBetween.xls
+apachepoi_Themes2.xls
apachepoi_TwoSheetsNoneHidden.xls
apachepoi_TwoSheetsOneHidden.xls
# apachepoi_UncalcedRecord.xls # xlml
@@ -831,10 +922,13 @@ apachepoi_WithTwoHyperLinks.xls
apachepoi_WrongFormulaRecordType.xls
apachepoi_XRefCalc.xls
apachepoi_XRefCalcData.xls
+apachepoi_ar.org.apsme.www_Form%20Inscripcion%20Curso%20NO%20Socios.xls
+apachepoi_at.gv.land-oberoesterreich.www_cps_rde_xbcr_SID-4A1B954F-5C07F98E_ooe_stat_download_bp10.xls
apachepoi_atp.xls
apachepoi_blankworkbook.xls
apachepoi_bug_42794.xls
apachepoi_colwidth.xls
+apachepoi_com.aida-tour.www_SPO_files_maldives%20august%20october.xls
apachepoi_comments.xls
apachepoi_countblankExamples.xls
apachepoi_countifExamples.xls
@@ -861,24 +955,32 @@ apachepoi_excel_with_embeded.xls
apachepoi_excelant.xls.pending
apachepoi_externalFunctionExample.xls
# apachepoi_finance.xls # xlml
+apachepoi_florida_data.ashx.xls
apachepoi_intercept.xls
apachepoi_mirrTest.xls
apachepoi_missingFuncs44675.xls
apachepoi_mortgage-calculation.xls
apachepoi_multibookFormulaA.xls
apachepoi_multibookFormulaB.xls
+apachepoi_named-cell-in-formula-test.xls
+apachepoi_named-cell-test.xls
apachepoi_namedinput.xls
apachepoi_noHeaderFooter47244.xls
apachepoi_ole2-embedding.xls
apachepoi_overlapSharedFormula.xls
apachepoi_password.xls.pending
apachepoi_rank.xls
+apachepoi_resize_compare.xls
apachepoi_rk.xls
apachepoi_shared_formulas.xls
apachepoi_sumifformula.xls
# apachepoi_sumifs.xls # xlml
apachepoi_templateExcelWithAutofilter.xls
apachepoi_testArraysAndTables.xls
+apachepoi_testEXCEL_3.xls
+apachepoi_testEXCEL_4.xls
+apachepoi_testEXCEL_5.xls
+apachepoi_testEXCEL_95.xls
apachepoi_testNames.xls
apachepoi_testRRaC.xls
apachepoi_testRVA.xls
@@ -1091,40 +1193,41 @@ pyExcelerator_mini-mini.xls
pyExcelerator_mini.xls
pyExcelerator_oo14.xls
rich_text_stress.xls
-roo_1900_base.xls
-roo_1904_base.xls
-roo_Bibelbund.xls
-roo_bad_excel_date.xls
-roo_bbu.xls
-roo_boolean.xls
-roo_borders.xls
-roo_bug-row-column-fixnum-float.xls
-roo_comments.xls
-roo_datetime.xls
-roo_datetime_floatconv.xls
-roo_emptysheets.xls
-roo_false_encoding.xls
-roo_formula.xls
-roo_formula_parse_error.xls
-roo_link.xls
-roo_matrix.xls
-roo_named_cells.xls
-roo_numbers1.xls
-roo_only_one_sheet.xls
-roo_paragraph.xls
-roo_prova.xls
-roo_simple_spreadsheet.xls
-roo_simple_spreadsheet_from_italo.xls
-roo_style.xls
-roo_time-test.xls
-roo_type_excelx.xls
-roo_type_openoffice.xls
+roo-xls_1900_base.xls
+roo-xls_1904_base.xls
+roo-xls_Bibelbund.xls
+roo-xls_bad_excel_date.xls
+roo-xls_bbu.xls
+roo-xls_boolean.xls
+roo-xls_borders.xls
+roo-xls_bug-row-column-fixnum-float.xls
+roo-xls_comments.xls
+roo-xls_datetime.xls
+roo-xls_datetime_floatconv.xls
+roo-xls_emptysheets.xls
+roo-xls_false_encoding.xls
+roo-xls_formula.xls
+roo-xls_formula_parse_error.xls
+roo-xls_link.xls
+roo-xls_matrix.xls
+roo-xls_named_cells.xls
+roo-xls_numbers1.xls
+roo-xls_only_one_sheet.xls
+roo-xls_paragraph.xls
+roo-xls_prova.xls
+roo-xls_simple_spreadsheet.xls
+roo-xls_simple_spreadsheet_from_italo.xls
+roo-xls_style.xls
+roo-xls_time-test.xls
+roo-xls_type_excelx.xls
+roo-xls_type_openoffice.xls
roo_whitespace.xls
smart_tags_2007.xls
sushi.xls
text_and_numbers.xls
write.xls
xlrd_Formate.xls
+# xlrd_biff4_no_format_no_window2.xls
xlrd_formula_test_names.xls
xlrd_formula_test_sjmachin.xls
xlrd_issue20.xls
@@ -1135,7 +1238,6 @@ xlrd_xf_class.xls
xlsx-stream-d-date-cell.xls
AutoFilter.xml
BlankSheetTypes.xml
-ErrorTypes.xml
LONumbers-2010.xls.xml
LONumbers-2010.xlsx.xml
LONumbers-2011.xls.xml
@@ -1145,6 +1247,7 @@ NumberFormatCondition.xml
RkNumber.xls.xml
RkNumber.xlsb.xml
RkNumber.xlsx.xml
+apachepoi_SampleSS.xml
calendar_stress_test.xml.pending
cell_style_simple.xml
comments_stress_test.xls.xml
@@ -1197,26 +1300,28 @@ protect_stress_test_xml.xml
rich_text_stress.xls.xml
rich_text_stress.xlsb.xml
rich_text_stress.xlsx.xml
-roo_Bibelbund.xml
-roo_bbu.xml
-roo_boolean.xml
-roo_borders.xml
-roo_bug-row-column-fixnum-float.xml
-roo_datetime.xml
-roo_datetime_floatconv.xml
-roo_emptysheets.xml
-roo_excel2003.xml
-roo_false_encoding.xml
-roo_formula.xml
-roo_formula_parse_error.xml
-roo_numbers1.xml
-roo_only_one_sheet.xml
-roo_paragraph.xml
-roo_simple_spreadsheet.xml
-roo_simple_spreadsheet_from_italo.xml
-roo_style.xml
-roo_time-test.xml
-roo_whitespace.xml
+roo-xls_Bibelbund.xml
+roo-xls_bbu.xml
+roo-xls_boolean.xml
+roo-xls_borders.xml
+roo-xls_bug-row-column-fixnum-float.xml
+roo-xls_datetime.xml
+roo-xls_datetime_floatconv.xml
+roo-xls_emptysheets.xml
+roo-xls_excel2003.xml
+roo-xls_excel2003_namespace.xml
+roo-xls_false_encoding.xml
+roo-xls_formula.xml
+roo-xls_formula_parse_error.xml
+roo-xls_numbers1.xml
+roo-xls_only_one_sheet.xml
+roo-xls_paragraph.xml
+roo-xls_simple_spreadsheet.xml
+roo-xls_simple_spreadsheet_from_italo.xml
+roo-xls_style.xml
+roo-xls_time-test.xml
+roo-xls_whitespace.xml
+# roo_sheet1.xml
smart_tags_2007.xml
sushi.xml
text_and_numbers.xml
@@ -1229,3 +1334,15 @@ xlsx-stream-d-date-cell.xlsx.xml
2011/apachepoi_styles.xlsx.xml
2011/openpyxl_r_conditional-formatting.xlsx.xls
2011/roo_file_item_error.xlsx.xml
+2013/apachepoi_29982.xls.xlsb
+2013/apachepoi_43251.xls.xlsb
+2013/apachepoi_44593.xls.xlsb ## xlsb loop timeout
+2013/apachepoi_44643.xls.xlsb
+2013/apachepoi_44958.xls.xlsb
+2013/apachepoi_46136-NoWarnings.xls.xlsb
+2013/apachepoi_48968.xls.xlsb
+2013/apachepoi_50939.xls.xlsb
+2013/apachepoi_54016.xls.xlsb
+2013/apachepoi_ReadOnlyRecommended.xls.xlsb
+2013/apachepoi_testArraysAndTables.xls.xlsb
+
diff --git a/tests/write.js b/tests/write.js
index 73a267b..08cff7a 100644
--- a/tests/write.js
+++ b/tests/write.js
@@ -79,14 +79,32 @@ var ws = sheet_from_array_of_arrays(data);
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
+/* TEST: simple formula */
+ws['C1'].f = "A1+B1";
+ws['C2'] = {t:'n', f:"A1+B1"};
+
+/* TEST: single-cell array formula */
+ws['D1'] = {t:'n', f:"SUM(A1:C1*A1:C1)", F:"D1:D1"};
+
+/* TEST: multi-cell array formula */
+ws['E1'] = {t:'n', f:"TRANSPOSE(A1:D1)", F:"E1:E4"};
+ws['E2'] = {t:'n', F:"E1:E4"};
+ws['E3'] = {t:'n', F:"E1:E4"};
+ws['E4'] = {t:'n', F:"E1:E4"};
+ws["!ref"] = "A1:E4";
+
/* TEST: column widths */
ws['!cols'] = wscols;
+console.log("JSON Data: "); console.log(XLSX.utils.sheet_to_json(ws, {header:1}));
+
+
/* write file */
-XLSX.writeFile(wb, 'sheetjs.xlsx');
+XLSX.writeFile(wb, 'sheetjs.xlsx', {bookSST:true});
XLSX.writeFile(wb, 'sheetjs.xlsm');
-XLSX.writeFile(wb, 'sheetjs.xlsb');
-XLSX.writeFile(wb, 'sheetjs.xls', {bookType:'biff2'});
+XLSX.writeFile(wb, 'sheetjs.xlsb'); // no formula
+XLSX.writeFile(wb, 'sheetjs.xls', {bookType:'biff2'}); // no formula
+XLSX.writeFile(wb, 'sheetjs.xml.xls', {bookType:'xlml'});
XLSX.writeFile(wb, 'sheetjs.ods');
XLSX.writeFile(wb, 'sheetjs.fods');
XLSX.writeFile(wb, 'sheetjs.csv');
@@ -96,6 +114,7 @@ XLSX.readFile('sheetjs.xlsx');
XLSX.readFile('sheetjs.xlsm');
XLSX.readFile('sheetjs.xlsb');
XLSX.readFile('sheetjs.xls');
+XLSX.readFile('sheetjs.xml.xls');
XLSX.readFile('sheetjs.ods');
XLSX.readFile('sheetjs.fods');
//XLSX.readFile('sheetjs.csv');
diff --git a/xlsx.flow.js b/xlsx.flow.js
index 3f56588..bff77fc 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -422,7 +422,18 @@ function hashq(str/*:string*/)/*:string*/ {
return o;
}
function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
-function dec(val/*:number*/, d/*:number*/)/*:number*/ { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+function dec(val/*:number*/, d/*:number*/)/*:number*/ {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 0;
+ }
+ return Math.round((val-Math.floor(val))*Math.pow(10,d));
+}
+function carry(val/*:number*/, d/*:number*/)/*:number*/ {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 1;
+ }
+ return 0;
+}
function flr(val/*:number*/)/*:string*/ { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/ {
if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
@@ -444,8 +455,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign);
if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
if((r = fmt.match(dec1))) {
- // $FlowIgnore
- o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
+ o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", /*::(*/r/*::||[""])*/[1].length-$1.length); });
return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
}
fmt = fmt.replace(/^#+([0.])/, "$1");
@@ -454,7 +464,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string
}
if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0));
if((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
- return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val))) + "." + pad0(dec(val, r[1].length),r[1].length);
+ return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length);
}
if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) {
@@ -8201,8 +8211,18 @@ function ods_to_csf_formula(f/*:string*/)/*:string*/ {
f = f.substr(1);
if(f.charCodeAt(0) == 61) f = f.substr(1);
}
+ f = f.replace(/COM\.MICROSOFT\./g, "");
/* Part 3 Section 5.8 References */
- return f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, "$1").replace(/\./g, "");
+ f = f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, function($$, $1) { return $1.replace(/\./g,""); });
+ /* TODO: something other than this */
+ f = f.replace(/\[.(#[A-Z]*[?!])\]/g, "$1");
+ return f.replace(/[;~]/g,",").replace(/\|/g,";");
+}
+
+function csf_to_ods_formula(f/*:string*/)/*:string*/ {
+ var o = "of:=" + f.replace(crefregex, "$1[.$2$3$4$5]").replace(/\]:\[/g,":");
+ /* TODO: something other than this */
+ return o.replace(/;/g, "|").replace(/,/g,";");
}
var strs = {}; // shared strings
var _ssfopts = {}; // spreadsheet formatting options
@@ -8390,7 +8410,7 @@ function write_ws_xml_cols(ws, cols)/*:string*/ {
}
function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
- if(cell.v === undefined || cell.t === 'z') return "";
+ if(cell.v === undefined && cell.f === undefined || cell.t === 'z') return "";
var vv = "";
var oldt = cell.t, oldv = cell.v;
switch(cell.t) {
@@ -8416,7 +8436,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
case 'd': o.t = "d"; break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
- default:
+ default: if(cell.v == null) { delete cell.t; break; }
if(opts.bookSST) {
v = writetag('v', ''+get_sst_id(opts.Strings, cell.v));
o.t = "s"; break;
@@ -8424,6 +8444,10 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
o.t = "str"; break;
}
if(cell.t != oldt) { cell.t = oldt; cell.v = oldv; }
+ if(cell.f) {
+ var ff = cell.F && cell.F.substr(0, ref.length) == ref ? {t:"array", ref:cell.F} : null;
+ v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : "");
+ }
return writextag('c', v, o);
}
@@ -8479,7 +8503,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
if((cref=d.match(match_v))!= null && /*::cref != null && */cref[1] !== '') p.v=unescapexml(cref[1]);
if(opts.cellFormula) {
if((cref=d.match(match_f))!= null && /*::cref != null && */cref[1] !== '') {
- p.f=unescapexml(utf8read(cref[1]));
+ /* TODO: match against XLSXFutureFunctions */
+ p.f=unescapexml(utf8read(cref[1])).replace(/_xlfn\./,"");
if(/*::cref != null && cref[0] != null && */cref[0].indexOf('t="array"') > -1) {
p.F = (d.match(refregex)||[])[1];
if(p.F.indexOf(":") > -1) arrayf.push([safe_decode_range(p.F), p.F]);
@@ -9842,7 +9867,7 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
case 'DateTime':
cell.v = (Date.parse(xml) - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
if(cell.v !== cell.v) cell.v = unescapexml(xml);
- else if(cell.v >= 1 && cell.v<60) cell.v = cell.v -1;
+ else if(cell.v<60) cell.v = cell.v -1;
if(!nf || nf == "General") nf = "yyyy-mm-dd";
/* falls through */
case 'Number':
@@ -9984,6 +10009,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
sheetname = unescapexml(tmp.Name);
cursheet = {};
mergecells = [];
+ arrayf = [];
}
break;
case 'Table':
@@ -10493,10 +10519,14 @@ function write_sty_xlml(wb, opts)/*:string*/ {
}
/* TODO */
function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
- if(!cell || cell.v === undefined) return " | ";
+ if(!cell || cell.v == undefined && cell.f == undefined) return " | ";
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+ if(cell.F && cell.F.substr(0, ref.length) == ref) {
+ var end = decode_cell(cell.F.substr(ref.length + 1));
+ attr["ss:ArrayRange"] = "RC:R" + (end.r == addr.r ? "" : "[" + (end.r - addr.r) + "]") + "C" + (end.c == addr.c ? "" : "[" + (end.c - addr.c) + "]");
+ }
if(ws['!merges']) {
var marr = ws['!merges'];
@@ -10514,9 +10544,9 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
case 'e': t = 'Error'; p = BErr[cell.v]; break;
case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
- case 's': t = 'String'; p = escapexml(cell.v||""); break;
+ case 's': t = 'String'; p = escapexml(cell.v||""); break;
}
- var m = '' + p + '';
+ var m = '' + (cell.v != null ? p : "") + '';
return writextag("Cell", m, attr);
}
@@ -10699,6 +10729,19 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(cell.r + 1 > range.e.r) range.e.r = cell.r + 1;
if(cell.c + 1 > range.e.c) range.e.c = cell.c + 1;
}
+ if(options.cellFormula && line.f) {
+ for(var afi = 0; afi < array_formulae.length; ++afi) {
+ if(array_formulae[afi][0].s.c > cell.c) continue;
+ if(array_formulae[afi][0].s.r > cell.r) continue;
+ if(array_formulae[afi][0].e.c < cell.c) continue;
+ if(array_formulae[afi][0].e.r < cell.r) continue;
+ line.F = encode_range(array_formulae[afi][0]);
+ if(array_formulae[afi][0].s.c != cell.c) delete line.f;
+ if(array_formulae[afi][0].s.r != cell.r) delete line.f;
+ if(line.f) line.f = "" + stringify_formula(array_formulae[afi][1], range, cell, supbooks, opts);
+ break;
+ }
+ }
if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
else out[last_cell] = line;
};
@@ -10851,7 +10894,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
else cur_sheet = (Directory[s] || {name:""}).name;
mergecells = [];
objects = [];
+ array_formulae = []; opts.arrayf = array_formulae;
} break;
+
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
safe_format_xf(temp_val, options, wb.opts.Date1904);
@@ -10876,44 +10921,43 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
}
} break;
case 'Formula': {
- switch(val.val) {
- case 'String': last_formula = val; break;
- case 'Array Formula': throw "Array Formula unsupported";
- default:
- temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt}/*:any*/);
- temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) {
- var _f = val.formula;
- if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
- var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
- var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- }
- safe_format_xf(temp_val, options, wb.opts.Date1904);
- addcell(val.cell, temp_val, options);
- last_formula = val;
+ if(val.val == 'String') { last_formula = val; break; }
+ temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt}/*:any*/);
+ temp_val.XF = XFs[temp_val.ixfe];
+ if(options.cellFormula) {
+ var _f = val.formula;
+ if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
+ var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
+ var _fe = encode_cell({r:_fr, c:_fc});
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ else temp_val.F = (out[_fe] || {}).F;
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
+ safe_format_xf(temp_val, options, wb.opts.Date1904);
+ addcell(val.cell, temp_val, options);
+ last_formula = val;
} break;
case 'String': {
- if(last_formula) {
+ if(last_formula) { /* technically always true */
last_formula.val = val;
- temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
+ temp_val = ({v:val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) {
+ temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ }
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
- }
+ } else throw new Error("String record expects Formula");
} break;
case 'Array': {
array_formulae.push(val);
- if(options.cellFormula && out[last_cell]) {
+ var _arraystart = encode_cell(val[0].s);
+ if(options.cellFormula && out[_arraystart]) {
if(!last_formula) break; /* technically unreachable */
- if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
- out[last_cell].F = encode_range(val[0]);
+ if(!_arraystart || !out[_arraystart]) break;
+ out[_arraystart].f = ""+stringify_formula(val[1], range, val[0], supbooks, opts);
+ out[_arraystart].F = encode_range(val[0]);
}
} break;
case 'ShrFmla': {
@@ -10952,6 +10996,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options);
break;
+
case 'Dimensions': {
if(file_depth === 1) range = val; /* TODO: stack */
} break;
@@ -12602,20 +12647,20 @@ function write_BIFF2LABEL(r, c, val) {
}
function write_ws_biff_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
- switch(cell.t) {
+ if(cell.v != null) switch(cell.t) {
case 'n':
if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536))
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v));
else
write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v));
- break;
- case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); break;
+ return;
+ case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
/* TODO: codepage, sst */
case 's': case 'str':
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v));
- break;
- default: write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
+ return;
}
+ write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
}
function write_biff_ws(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
@@ -12761,6 +12806,7 @@ var parse_content_xml = (function() {
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
if(opts.cellFormula) {
+ if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-matrix-columns-spanned'],10) || 0;
@@ -13108,11 +13154,22 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
+ var fmla = "";
+ if(cell && cell.f) {
+ fmla = ' 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 = "";
+ }
+ }
if(cell) switch(cell.t) {
- case 'b': o.push(cell_begin + mxml + vt + '"boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '">' + p_begin + (cell.v ? 'TRUE' : 'FALSE') + p_end + cell_end); break;
- case 'n': o.push(cell_begin + mxml + vt + '"float" office:value="' + cell.v + '">' + p_begin + (cell.w||cell.v) + p_end + cell_end); break;
- case 's': case 'str': o.push(cell_begin + mxml + vt + '"string">' + p_begin + escapexml(cell.v) + p_end + cell_end); break;
- case 'd': o.push(cell_begin + mxml + vt + '"date" office:date-value="' + (new Date(cell.v).toISOString()) + '">' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
+ 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="' + (new Date(cell.v).toISOString()) + '"' + fmla + '>' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
@@ -13127,7 +13184,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var o = [XML_HEADER];
/* 3.1.3.2 */
if(opts.bookType == "fods") o.push('');
- else o.push('\n'); // TODO
+ else o.push('\n'); // TODO
o.push(' \n');
o.push(' \n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
@@ -13749,9 +13806,15 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
rr = encode_row(R);
for(C = r.s.c; C <= r.e.c; ++C) {
val = sheet[cols[C] + rr];
- txt = val !== undefined ? ''+format_cell(val) : "";
- for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
- txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ if(val == null) txt = "";
+ else if(val.v != null) {
+ txt = ''+format_cell(val);
+ for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
+ txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ } else if(val.f != null && !val.F) {
+ txt = '=' + val.f; if(txt.indexOf(",") >= 0) txt = '"' + txt.replace(qreg, '""') + '"';
+ } else txt = "";
+ /* NOTE: Excel CSV does not support array formulae */
row += (C === r.s.c ? "" : FS) + txt;
}
out += row + RS;
diff --git a/xlsx.js b/xlsx.js
index 3d88210..1684831 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -403,7 +403,18 @@ function hashq(str) {
return o;
}
function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
-function dec(val, d) { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+function dec(val, d) {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 0;
+ }
+ return Math.round((val-Math.floor(val))*Math.pow(10,d));
+}
+function carry(val, d) {
+ if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) {
+ return 1;
+ }
+ return 0;
+}
function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
function write_num_flt(type, fmt, val) {
if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
@@ -425,7 +436,6 @@ function write_num_flt(type, fmt, val) {
if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign);
if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
if((r = fmt.match(dec1))) {
- // $FlowIgnore
o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
}
@@ -435,7 +445,7 @@ function write_num_flt(type, fmt, val) {
}
if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0));
if((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
- return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val))) + "." + pad0(dec(val, r[1].length),r[1].length);
+ return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length);
}
if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) {
@@ -8148,8 +8158,18 @@ function ods_to_csf_formula(f) {
f = f.substr(1);
if(f.charCodeAt(0) == 61) f = f.substr(1);
}
+ f = f.replace(/COM\.MICROSOFT\./g, "");
/* Part 3 Section 5.8 References */
- return f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, "$1").replace(/\./g, "");
+ f = f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, function($$, $1) { return $1.replace(/\./g,""); });
+ /* TODO: something other than this */
+ f = f.replace(/\[.(#[A-Z]*[?!])\]/g, "$1");
+ return f.replace(/[;~]/g,",").replace(/\|/g,";");
+}
+
+function csf_to_ods_formula(f) {
+ var o = "of:=" + f.replace(crefregex, "$1[.$2$3$4$5]").replace(/\]:\[/g,":");
+ /* TODO: something other than this */
+ return o.replace(/;/g, "|").replace(/,/g,";");
}
var strs = {}; // shared strings
var _ssfopts = {}; // spreadsheet formatting options
@@ -8337,7 +8357,7 @@ function write_ws_xml_cols(ws, cols) {
}
function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
- if(cell.v === undefined || cell.t === 'z') return "";
+ if(cell.v === undefined && cell.f === undefined || cell.t === 'z') return "";
var vv = "";
var oldt = cell.t, oldv = cell.v;
switch(cell.t) {
@@ -8363,7 +8383,7 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
case 'd': o.t = "d"; break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
- default:
+ default: if(cell.v == null) { delete cell.t; break; }
if(opts.bookSST) {
v = writetag('v', ''+get_sst_id(opts.Strings, cell.v));
o.t = "s"; break;
@@ -8371,6 +8391,10 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
o.t = "str"; break;
}
if(cell.t != oldt) { cell.t = oldt; cell.v = oldv; }
+ if(cell.f) {
+ var ff = cell.F && cell.F.substr(0, ref.length) == ref ? {t:"array", ref:cell.F} : null;
+ v = writextag('f', escapexml(cell.f), ff) + (cell.v != null ? v : "");
+ }
return writextag('c', v, o);
}
@@ -8426,7 +8450,8 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
if((cref=d.match(match_v))!= null && cref[1] !== '') p.v=unescapexml(cref[1]);
if(opts.cellFormula) {
if((cref=d.match(match_f))!= null && cref[1] !== '') {
- p.f=unescapexml(utf8read(cref[1]));
+ /* TODO: match against XLSXFutureFunctions */
+ p.f=unescapexml(utf8read(cref[1])).replace(/_xlfn\./,"");
if(cref[0].indexOf('t="array"') > -1) {
p.F = (d.match(refregex)||[])[1];
if(p.F.indexOf(":") > -1) arrayf.push([safe_decode_range(p.F), p.F]);
@@ -9787,7 +9812,7 @@ function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, arrayf, o
case 'DateTime':
cell.v = (Date.parse(xml) - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
if(cell.v !== cell.v) cell.v = unescapexml(xml);
- else if(cell.v >= 1 && cell.v<60) cell.v = cell.v -1;
+ else if(cell.v<60) cell.v = cell.v -1;
if(!nf || nf == "General") nf = "yyyy-mm-dd";
/* falls through */
case 'Number':
@@ -9928,6 +9953,7 @@ for(var cma = c; cma <= cc; ++cma) {
sheetname = unescapexml(tmp.Name);
cursheet = {};
mergecells = [];
+ arrayf = [];
}
break;
case 'Table':
@@ -10436,10 +10462,14 @@ function write_sty_xlml(wb, opts) {
}
/* TODO */
function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){
- if(!cell || cell.v === undefined) return " | ";
+ if(!cell || cell.v == undefined && cell.f == undefined) return " | ";
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+ if(cell.F && cell.F.substr(0, ref.length) == ref) {
+ var end = decode_cell(cell.F.substr(ref.length + 1));
+ attr["ss:ArrayRange"] = "RC:R" + (end.r == addr.r ? "" : "[" + (end.r - addr.r) + "]") + "C" + (end.c == addr.c ? "" : "[" + (end.c - addr.c) + "]");
+ }
if(ws['!merges']) {
var marr = ws['!merges'];
@@ -10457,9 +10487,9 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){
case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
case 'e': t = 'Error'; p = BErr[cell.v]; break;
case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
- case 's': t = 'String'; p = escapexml(cell.v||""); break;
+ case 's': t = 'String'; p = escapexml(cell.v||""); break;
}
- var m = '' + p + '';
+ var m = '' + (cell.v != null ? p : "") + '';
return writextag("Cell", m, attr);
}
@@ -10642,6 +10672,19 @@ function parse_workbook(blob, options) {
if(cell.r + 1 > range.e.r) range.e.r = cell.r + 1;
if(cell.c + 1 > range.e.c) range.e.c = cell.c + 1;
}
+ if(options.cellFormula && line.f) {
+ for(var afi = 0; afi < array_formulae.length; ++afi) {
+ if(array_formulae[afi][0].s.c > cell.c) continue;
+ if(array_formulae[afi][0].s.r > cell.r) continue;
+ if(array_formulae[afi][0].e.c < cell.c) continue;
+ if(array_formulae[afi][0].e.r < cell.r) continue;
+ line.F = encode_range(array_formulae[afi][0]);
+ if(array_formulae[afi][0].s.c != cell.c) delete line.f;
+ if(array_formulae[afi][0].s.r != cell.r) delete line.f;
+ if(line.f) line.f = "" + stringify_formula(array_formulae[afi][1], range, cell, supbooks, opts);
+ break;
+ }
+ }
if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
else out[last_cell] = line;
};
@@ -10794,7 +10837,9 @@ function parse_workbook(blob, options) {
else cur_sheet = (Directory[s] || {name:""}).name;
mergecells = [];
objects = [];
+ array_formulae = []; opts.arrayf = array_formulae;
} break;
+
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
safe_format_xf(temp_val, options, wb.opts.Date1904);
@@ -10819,44 +10864,43 @@ function parse_workbook(blob, options) {
}
} break;
case 'Formula': {
- switch(val.val) {
- case 'String': last_formula = val; break;
- case 'Array Formula': throw "Array Formula unsupported";
- default:
- temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt});
- temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) {
- var _f = val.formula;
- if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
- var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
- var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
- }
- safe_format_xf(temp_val, options, wb.opts.Date1904);
- addcell(val.cell, temp_val, options);
- last_formula = val;
+ if(val.val == 'String') { last_formula = val; break; }
+ temp_val = ({v:val.val, ixfe:val.cell.ixfe, t:val.tt});
+ temp_val.XF = XFs[temp_val.ixfe];
+ if(options.cellFormula) {
+ var _f = val.formula;
+ if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
+ var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
+ var _fe = encode_cell({r:_fr, c:_fc});
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ else temp_val.F = (out[_fe] || {}).F;
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
+ safe_format_xf(temp_val, options, wb.opts.Date1904);
+ addcell(val.cell, temp_val, options);
+ last_formula = val;
} break;
case 'String': {
- if(last_formula) {
+ if(last_formula) { /* technically always true */
last_formula.val = val;
- temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'});
+ temp_val = ({v:val, ixfe:last_formula.cell.ixfe, t:'s'});
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) {
+ temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ }
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
- }
+ } else throw new Error("String record expects Formula");
} break;
case 'Array': {
array_formulae.push(val);
- if(options.cellFormula && out[last_cell]) {
+ var _arraystart = encode_cell(val[0].s);
+ if(options.cellFormula && out[_arraystart]) {
if(!last_formula) break; /* technically unreachable */
- if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
- out[last_cell].F = encode_range(val[0]);
+ if(!_arraystart || !out[_arraystart]) break;
+ out[_arraystart].f = ""+stringify_formula(val[1], range, val[0], supbooks, opts);
+ out[_arraystart].F = encode_range(val[0]);
}
} break;
case 'ShrFmla': {
@@ -10895,6 +10939,7 @@ function parse_workbook(blob, options) {
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options);
break;
+
case 'Dimensions': {
if(file_depth === 1) range = val; /* TODO: stack */
} break;
@@ -12545,20 +12590,20 @@ function write_BIFF2LABEL(r, c, val) {
}
function write_ws_biff_cell(ba, cell, R, C, opts) {
- switch(cell.t) {
+ if(cell.v != null) switch(cell.t) {
case 'n':
if((cell.v == (cell.v|0)) && (cell.v >= 0) && (cell.v < 65536))
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, cell.v));
else
write_biff_rec(ba, 0x0003, write_BIFF2NUMBER(R,C, cell.v));
- break;
- case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); break;
+ return;
+ case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
/* TODO: codepage, sst */
case 's': case 'str':
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v));
- break;
- default: write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
+ return;
}
+ write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
}
function write_biff_ws(ba, ws, idx, opts, wb) {
@@ -12704,6 +12749,7 @@ var parse_content_xml = (function() {
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null});
if(opts.cellFormula) {
+ if(ctag.formula) ctag.formula = unescapexml(ctag.formula);
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-matrix-columns-spanned'],10) || 0;
@@ -13051,11 +13097,22 @@ var write_content_xml = (function() {
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
+ var fmla = "";
+ if(cell && cell.f) {
+ fmla = ' 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 = "";
+ }
+ }
if(cell) switch(cell.t) {
- case 'b': o.push(cell_begin + mxml + vt + '"boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '">' + p_begin + (cell.v ? 'TRUE' : 'FALSE') + p_end + cell_end); break;
- case 'n': o.push(cell_begin + mxml + vt + '"float" office:value="' + cell.v + '">' + p_begin + (cell.w||cell.v) + p_end + cell_end); break;
- case 's': case 'str': o.push(cell_begin + mxml + vt + '"string">' + p_begin + escapexml(cell.v) + p_end + cell_end); break;
- case 'd': o.push(cell_begin + mxml + vt + '"date" office:date-value="' + (new Date(cell.v).toISOString()) + '">' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
+ 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="' + (new Date(cell.v).toISOString()) + '"' + fmla + '>' + p_begin + (cell.w||(new Date(cell.v).toISOString())) + p_end + cell_end); break;
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
@@ -13070,7 +13127,7 @@ var write_content_xml = (function() {
var o = [XML_HEADER];
/* 3.1.3.2 */
if(opts.bookType == "fods") o.push('');
- else o.push('\n'); // TODO
+ else o.push('\n'); // TODO
o.push(' \n');
o.push(' \n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
@@ -13684,9 +13741,15 @@ function sheet_to_csv(sheet, opts) {
rr = encode_row(R);
for(C = r.s.c; C <= r.e.c; ++C) {
val = sheet[cols[C] + rr];
- txt = val !== undefined ? ''+format_cell(val) : "";
- for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
- txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ if(val == null) txt = "";
+ else if(val.v != null) {
+ txt = ''+format_cell(val);
+ for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
+ txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+ } else if(val.f != null && !val.F) {
+ txt = '=' + val.f; if(txt.indexOf(",") >= 0) txt = '"' + txt.replace(qreg, '""') + '"';
+ } else txt = "";
+ /* NOTE: Excel CSV does not support array formulae */
row += (C === r.s.c ? "" : FS) + txt;
}
out += row + RS;