version bump 0.9.3: multiformat alignment

- merge cells and stubs consistency
- character entity decoding
- cellDates processing for other formats
This commit is contained in:
SheetJS 2017-03-15 21:17:24 -04:00
parent d3eaa62d45
commit ea1873e572
21 changed files with 631 additions and 123 deletions

@ -5,7 +5,7 @@ but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
## Unreleased
## 0.9.3 (2017-03-15)
* XLML property names are more closely mapped to the XLSX equivalent
* Stub cells are now cell type `z`

@ -479,7 +479,7 @@ avoid possible confusion.
Type `z` represents blank stub cells. These do not have any data or type, and
are not processed by any of the core library functions. By default these cells
will not be generated; the parser `cellStubs` option must be set to `true`.
will not be generated; the parser `sheetStubs` option must be set to `true`.
### Formulae

@ -103,6 +103,8 @@ if(program.all) {
opts.cellFormula = true;
opts.cellNF = true;
opts.cellStyles = true;
opts.sheetStubs = true;
opts.cellDates = true;
}
if(program.dev) {

@ -1 +1 @@
XLSX.version = '0.9.2';
XLSX.version = '0.9.3';

@ -40,15 +40,15 @@ var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var unescapexml/*:StringConv*/ = (function() {
/* 22.4.2.4 bstr (Basic String) */
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]{4})_/g;
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/g, coderegex = /_x([\da-fA-F]{4})_/g;
return function unescapexml(text/*:string*/)/*:string*/ {
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
};
})();
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text/*:string*/)/*:string*/{
function escapexml(text/*:string*/, xml/*:?boolean*/)/*:string*/{
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}

@ -209,6 +209,13 @@ function parse_BrtFmlaString(data, length, opts) {
/* [MS-XLSB] 2.4.676 BrtMergeCell */
var parse_BrtMergeCell = parse_UncheckedRfX;
var write_BrtMergeCell = write_UncheckedRfX;
/* [MS-XLSB] 2.4.108 BrtBeginMergeCells */
function write_BrtBeginMergeCells(cnt, o) {
if(o == null) o = new_buf(4);
o.write_shift(4, cnt);
return o;
}
/* [MS-XLSB] 2.4.656 BrtHLink */
function parse_BrtHLink(data, length, opts) {
@ -550,7 +557,14 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
write_record(ba, 'BrtEndSheetData');
}
function write_ws_bin(idx, opts, wb) {
function write_MERGECELLS(ba, ws/*:Worksheet*/) {
if(!ws || !ws['!merges']) return;
write_record(ba, 'BrtBeginMergeCells', write_BrtBeginMergeCells(ws['!merges'].length));
ws['!merges'].forEach(function(m) { write_record(ba, 'BrtMergeCell', write_BrtMergeCell(m)); });
write_record(ba, 'BrtEndMergeCells');
}
function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/) {
var ba = buf_array();
var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
var r = safe_decode_range(ws['!ref'] || "A1");
@ -569,7 +583,7 @@ function write_ws_bin(idx, opts, wb) {
/* [SORTSTATE] */
/* [DCON] */
/* [USERSHVIEWS] */
/* [MERGECELLS] */
write_MERGECELLS(ba, ws);
/* [BrtPhoneticInfo] */
/* *CONDITIONALFORMATTING */
/* [DVALS] */

@ -764,6 +764,15 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
if(ws['!merges']) {
var marr = ws['!merges'];
for(var mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c != addr.c || marr[mi].s.r != addr.r) continue;
if(marr[mi].e.c > marr[mi].s.c) attr['ss:MergeAcross'] = marr[mi].e.c - marr[mi].s.c;
if(marr[mi].e.r > marr[mi].s.r) attr['ss:MergeDown'] = marr[mi].e.r - marr[mi].s.r;
}
}
var t = "", p = "";
switch(cell.t) {
case 'z': return "";
@ -771,7 +780,7 @@ 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;
default: t = 'String'; p = escapexml(cell.v||"");
case 's': t = 'String'; p = escapexml(cell.v||""); break;
}
var m = '<Data ss:Type="' + t + '">' + p + '</Data>';
@ -781,16 +790,27 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/)/*:string*/ {
if(!ws['!ref']) return "";
var range = safe_decode_range(ws['!ref']);
var marr = ws['!merges'] || [], mi = 0;
var o = [];
for(var R = range.s.r; R <= range.e.r; ++R) {
var row = ["<Row>"];
var row = ['<Row ss:Index="' + (R+1) + '">'];
for(var C = range.s.c; C <= range.e.c; ++C) {
var skip = false;
for(mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c > C) continue;
if(marr[mi].s.r > R) continue;
if(marr[mi].e.c < C) continue;
if(marr[mi].e.r < R) continue;
if(marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
break;
}
if(skip) continue;
var addr = {r:R,c:C};
var ref = encode_cell(addr), cell = ws[ref];
row.push(write_ws_xlml_cell(ws[ref], ref, ws, opts, idx, wb, addr));
}
row.push("</Row>");
o.push(row.join(""));
if(row.length > 2) o.push(row.join(""));
}
return o.join("");
}

@ -107,13 +107,15 @@ var parse_content_xml = (function() {
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(new Date(ctag['date-value'])); q.z = 'm/d/yy'; break;
case 'date': q.t = 'd'; q.v = new Date(ctag['date-value']);
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v); }
q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
if(ctag['string-value'] != null) textp = unescapexml(ctag['string-value']);
} else throw new Error('Unsupported value type ' + q.t);
}
} else {

@ -1,21 +1,37 @@
var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var covered_cell_xml = ' <table:covered-table-cell/>\n';
var cell_begin = ' <table:table-cell ', cell_end = '</table:table-cell>\n';
var vt = 'office:value-type=';
var p_begin = '<text:p>', p_end = '</text:p>';
var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = decode_range(ws['!ref']);
var marr = ws['!merges'] || [], mi = 0;
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var skip = false, mxml = "";
for(mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c > C) continue;
if(marr[mi].s.r > R) continue;
if(marr[mi].e.c < C) continue;
if(marr[mi].e.r < R) continue;
if(marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
mxml = 'table:number-columns-spanned="' + (marr[mi].e.c - marr[mi].s.c + 1) + '" table:number-rows-spanned="' + (marr[mi].e.r - marr[mi].s.r + 1) + '" ';
break;
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
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 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);

23
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

24
dist/xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

347
dist/xlsx.js vendored

@ -5,7 +5,7 @@
/*exported XLSX */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.9.2';
XLSX.version = '0.9.3';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel.js');
@ -1453,18 +1453,19 @@ var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var unescapexml = (function() {
/* 22.4.2.4 bstr (Basic String) */
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]{4})_/g;
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/g, coderegex = /_x([\da-fA-F]{4})_/g;
return function unescapexml(text) {
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
};
})();
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text){
function escapexml(text, xml){
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
function escapexmltag(text){ return escapexml(text).replace(/ /g,"_x0020_"); }
/* TODO: handle codepages */
var xlml_fixstr = (function() {
@ -1591,6 +1592,13 @@ XMLNS.main = [
'http://schemas.microsoft.com/office/excel/2006/2'
];
var XLMLNS = ({
'o': 'urn:schemas-microsoft-com:office:office',
'x': 'urn:schemas-microsoft-com:office:excel',
'ss': 'urn:schemas-microsoft-com:office:spreadsheet',
'dt': 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882',
'html': 'http://www.w3.org/TR/REC-html40'
});
function read_double_le(b, idx) {
var s = 1 - 2 * (b[idx + 7] >>> 7);
var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f);
@ -2968,10 +2976,67 @@ function xlml_set_prop(Props, tag, val) {
/* TODO: Normalize the properties */
switch(tag) {
case 'Description': tag = 'Comments'; break;
case 'Created': tag = 'CreatedDate'; break;
case 'LastSaved': tag = 'ModifiedDate'; break;
}
Props[tag] = val;
}
var XLMLDocumentProperties = [
['Title', 'Title'],
['Subject', 'Subject'],
['Author', 'Author'],
['Keywords', 'Keywords'],
['Comments', 'Description'],
['LastAuthor', 'LastAuthor'],
['CreatedDate', 'Created', 'date'],
['ModifiedDate', 'LastSaved', 'date'],
['Category', 'Category'],
['Manager', 'Manager'],
['Company', 'Company'],
['AppVersion', 'Version']
];
/* TODO: verify */
function xlml_write_docprops(Props) {
var T = 'DocumentProperties';
var o = [];
XLMLDocumentProperties.forEach(function(p) {
if(!Props[p[0]]) return;
var m = Props[p[0]];
switch(p[2]) {
case 'date': m = new Date(m).toISOString(); break;
}
o.push(writetag(p[1], m));
});
return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '</' + T + '>';
}
function xlml_write_custprops(Props, Custprops) {
var T = 'CustomDocumentProperties';
var o = [];
if(Props) keys(Props).forEach(function(k) {
if(!Props.hasOwnProperty(k)) return;
for(var i = 0; i < XLMLDocumentProperties.length; ++i)
if(k == XLMLDocumentProperties[i][0]) return;
var m = Props[k];
var t = "string";
if(typeof m == 'number') { t = "float"; m = String(m); }
else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
else m = String(m);
o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
});
if(Custprops) keys(Custprops).forEach(function(k) {
if(!Custprops.hasOwnProperty(k)) return;
var m = Custprops[k];
var t = "string";
if(typeof m == 'number') { t = "float"; m = String(m); }
else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
else if(m instanceof Date) { t = "dateTime.tz"; m = m.toISOString(); }
else m = String(m);
o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
});
return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '</' + T + '>';
}
/* [MS-DTYP] 2.3.3 FILETIME */
/* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
/* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
@ -3735,15 +3800,56 @@ function parse_MulRk(blob, length) {
if(rkrecs.length != lastcol - col + 1) throw "MulRK length mismatch";
return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
}
/* 2.4.174 */
function parse_MulBlank(blob, length) {
var target = blob.l + length - 2;
var rw = blob.read_shift(2), col = blob.read_shift(2);
var ixfes = [];
while(blob.l < target) ixfes.push(blob.read_shift(2));
if(blob.l !== target) throw "MulBlank read error";
var lastcol = blob.read_shift(2);
if(ixfes.length != lastcol - col + 1) throw "MulBlank length mismatch";
return {r:rw, c:col, C:lastcol, ixfe:ixfes};
}
/* 2.5.20 2.5.249 TODO */
/* 2.5.20 2.5.249 TODO: interpret values here */
function parse_CellStyleXF(blob, length, style) {
var o = {};
var a = blob.read_shift(4), b = blob.read_shift(4);
var c = blob.read_shift(4), d = blob.read_shift(2);
o.patternType = XLSFillPattern[c >> 26];
o.alc = a & 0x07;
o.fWrap = (a >> 3) & 0x01;
o.alcV = (a >> 4) & 0x07;
o.fJustLast = (a >> 7) & 0x01;
o.trot = (a >> 8) & 0xFF;
o.cIndent = (a >> 16) & 0x0F;
o.fShrinkToFit = (a >> 20) & 0x01;
o.iReadOrder = (a >> 22) & 0x02;
o.fAtrNum = (a >> 26) & 0x01;
o.fAtrFnt = (a >> 27) & 0x01;
o.fAtrAlc = (a >> 28) & 0x01;
o.fAtrBdr = (a >> 29) & 0x01;
o.fAtrPat = (a >> 30) & 0x01;
o.fAtrProt = (a >> 31) & 0x01;
o.dgLeft = b & 0x0F;
o.dgRight = (b >> 4) & 0x0F;
o.dgTop = (b >> 8) & 0x0F;
o.dgBottom = (b >> 12) & 0x0F;
o.icvLeft = (b >> 16) & 0x7F;
o.icvRight = (b >> 23) & 0x7F;
o.grbitDiag = (b >> 30) & 0x03;
o.icvTop = c & 0x7F;
o.icvBottom = (c >> 7) & 0x7F;
o.icvDiag = (c >> 14) & 0x7F;
o.dgDiag = (c >> 21) & 0x0F;
o.icvFore = d & 0x7F;
o.icvBack = (d >> 7) & 0x7F;
o.fsxButton = (d >> 14) & 0x01;
return o;
}
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
@ -4101,7 +4207,6 @@ var parse_SXLI = parsenoop;
var parse_SXPI = parsenoop;
var parse_DocRoute = parsenoop;
var parse_RecipName = parsenoop;
var parse_MulBlank = parsenoop;
var parse_SXDI = parsenoop;
var parse_SXDB = parsenoop;
var parse_SXFDB = parsenoop;
@ -5766,8 +5871,19 @@ var rc_to_a1 = (function(){
};
})();
/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)([1-9]\d{0,5}|10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6])(?![_.\(A-Za-z0-9])/g;
var a1_to_rc =(function(){
return function a1_to_rc(fstr, base) {
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
/* TODO: handle fixcol / fixrow */
var c = decode_col($3) - base.c;
var r = decode_row($5) - base.r;
return $1 + "R" + (r == 0 ? "" : "[" + r + "]") + "C" + (c == 0 ? "" : "[" + c + "]");
});
};
})();
/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
function shift_formula_str(f, delta) {
return f.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
return $1+($2=="$" ? $2+$3 : encode_col(decode_col($3)+delta.c))+($4=="$" ? $4+$5 : encode_row(decode_row($5) + delta.r));
@ -8060,6 +8176,7 @@ function get_cell_style(styles, cell, opts) {
}
function safe_format(p, fmtid, fillid, opts) {
if(p.t === 'z') return;
if(p.t === 'd' && typeof p.v === 'string') p.v = new Date(p.v);
try {
if(p.t === 'e') p.w = p.w || BErr[p.v];
@ -8181,7 +8298,7 @@ function parse_ws_xml_hlinks(s, data, rels) {
var rng = safe_decode_range(val.ref);
for(var R=rng.s.r;R<=rng.e.r;++R) for(var C=rng.s.c;C<=rng.e.c;++C) {
var addr = encode_cell({c:C,r:R});
if(!s[addr]) s[addr] = {t:"stub",v:undefined};
if(!s[addr]) s[addr] = {t:"z",v:undefined};
s[addr].l = val;
}
}
@ -8220,7 +8337,7 @@ function write_ws_xml_cols(ws, cols) {
}
function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
if(cell.v === undefined) return "";
if(cell.v === undefined || cell.t === 'z') return "";
var vv = "";
var oldt = cell.t, oldv = cell.v;
switch(cell.t) {
@ -8333,7 +8450,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* SCHEMA IS ACTUALLY INCORRECT HERE. IF A CELL HAS NO T, EMIT "" */
if(tag.t === undefined && p.v === undefined) {
if(!opts.sheetStubs) continue;
p.t = "stub";
p.t = "z";
}
else p.t = tag.t || "n";
if(guess.s.c > idx) guess.s.c = idx;
@ -8345,7 +8462,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
sstr = strs[parseInt(p.v, 10)];
if(typeof p.v == 'undefined') {
if(!opts.sheetStubs) continue;
p.t = "stub";
p.t = 'z';
}
p.v = sstr.t;
p.r = sstr.r;
@ -8636,6 +8753,13 @@ function parse_BrtFmlaString(data, length, opts) {
/* [MS-XLSB] 2.4.676 BrtMergeCell */
var parse_BrtMergeCell = parse_UncheckedRfX;
var write_BrtMergeCell = write_UncheckedRfX;
/* [MS-XLSB] 2.4.108 BrtBeginMergeCells */
function write_BrtBeginMergeCells(cnt, o) {
if(o == null) o = new_buf(4);
o.write_shift(4, cnt);
return o;
}
/* [MS-XLSB] 2.4.656 BrtHLink */
function parse_BrtHLink(data, length, opts) {
@ -8751,7 +8875,7 @@ function parse_ws_bin(data, opts, rels, wb) {
break;
case 'BrtCellBlank': if(!opts.sheetStubs) break;
p = ({t:'s',v:undefined});
p = ({t:'z',v:undefined});
s[encode_col(C=val[0].c) + rr] = p;
if(refguess.s.r > row.r) refguess.s.r = row.r;
if(refguess.s.c > C) refguess.s.c = C;
@ -8977,6 +9101,13 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
write_record(ba, 'BrtEndSheetData');
}
function write_MERGECELLS(ba, ws) {
if(!ws || !ws['!merges']) return;
write_record(ba, 'BrtBeginMergeCells', write_BrtBeginMergeCells(ws['!merges'].length));
ws['!merges'].forEach(function(m) { write_record(ba, 'BrtMergeCell', write_BrtMergeCell(m)); });
write_record(ba, 'BrtEndMergeCells');
}
function write_ws_bin(idx, opts, wb) {
var ba = buf_array();
var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
@ -8996,7 +9127,7 @@ function write_ws_bin(idx, opts, wb) {
/* [SORTSTATE] */
/* [DCON] */
/* [USERSHVIEWS] */
/* [MERGECELLS] */
write_MERGECELLS(ba, ws);
/* [BrtPhoneticInfo] */
/* *CONDITIONALFORMATTING */
/* [DVALS] */
@ -9606,6 +9737,7 @@ function xlml_set_custprop(Custprops, Rn, cp, val) {
}
function safe_format_xlml(cell, nf, o) {
if(cell.t === 'z') return;
try {
if(cell.t === 'e') { cell.w = cell.w || BErr[cell.v]; }
else if(nf === "General") {
@ -9748,8 +9880,16 @@ function parse_xlml_xml(d, opts) {
var rr = r + (parseInt(cell.MergeDown,10)|0);
mergecells.push({s:{c:c,r:r},e:{c:cc,r:rr}});
}
++c;
if(cell.MergeAcross) c += +cell.MergeAcross;
if(!opts.sheetStubs) { if(cell.MergeAcross) c = cc + 1; else ++c; }
else if(cell.MergeAcross || cell.MergeDown) {
for(var cma = c; cma <= cc; ++cma) {
for(var cmd = r; cmd <= rr; ++cmd) {
if(cma > c || cmd > r) cursheet[encode_col(cma) + encode_row(cmd)] = {t:'z'};
}
}
c = cc + 1;
}
else ++c;
} else {
cell = xlml_parsexmltagobj(Rn[0]);
if(cell.Index) c = +cell.Index - 1;
@ -10275,10 +10415,109 @@ function parse_xlml(data, opts) {
}
/* TODO */
function write_xlml(wb, opts) {
var o = [XML_HEADER];
function write_props_xlml(wb, opts) {
var o = [];
/* DocumentProperties */
if(wb.Props) o.push(xlml_write_docprops(wb.Props));
/* CustomDocumentProperties */
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
return o.join("");
}
/* TODO */
function write_wb_xlml(wb, opts) {
/* OfficeDocumentSettings */
/* ExcelWorkbook */
return "";
}
/* TODO */
function write_sty_xlml(wb, opts) {
/* Styles */
return "";
}
/* TODO */
function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){
if(!cell || cell.v === undefined) return "<Cell></Cell>";
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
if(ws['!merges']) {
var marr = ws['!merges'];
for(var mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c != addr.c || marr[mi].s.r != addr.r) continue;
if(marr[mi].e.c > marr[mi].s.c) attr['ss:MergeAcross'] = marr[mi].e.c - marr[mi].s.c;
if(marr[mi].e.r > marr[mi].s.r) attr['ss:MergeDown'] = marr[mi].e.r - marr[mi].s.r;
}
}
var t = "", p = "";
switch(cell.t) {
case 'z': return "";
case 'n': t = 'Number'; p = String(cell.v); break;
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;
}
var m = '<Data ss:Type="' + t + '">' + p + '</Data>';
return writextag("Cell", m, attr);
}
/* TODO */
function write_ws_xlml_table(ws, opts, idx, wb) {
if(!ws['!ref']) return "";
var range = safe_decode_range(ws['!ref']);
var marr = ws['!merges'] || [], mi = 0;
var o = [];
for(var R = range.s.r; R <= range.e.r; ++R) {
var row = ['<Row ss:Index="' + (R+1) + '">'];
for(var C = range.s.c; C <= range.e.c; ++C) {
var skip = false;
for(mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c > C) continue;
if(marr[mi].s.r > R) continue;
if(marr[mi].e.c < C) continue;
if(marr[mi].e.r < R) continue;
if(marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
break;
}
if(skip) continue;
var addr = {r:R,c:C};
var ref = encode_cell(addr), cell = ws[ref];
row.push(write_ws_xlml_cell(ws[ref], ref, ws, opts, idx, wb, addr));
}
row.push("</Row>");
if(row.length > 2) o.push(row.join(""));
}
return o.join("");
}
function write_ws_xlml(idx, opts, wb) {
var o = [];
var s = wb.SheetNames[idx];
var ws = wb.Sheets[s];
/* Table */
var t = ws ? write_ws_xlml_table(ws, opts, idx, wb) : "";
if(t.length > 0) o.push("<Table>" + t + "</Table>");
/* WorksheetOptions */
return o.join("");
}
function write_xlml(wb, opts) {
var d = [];
d.push(write_props_xlml(wb, opts));
d.push(write_wb_xlml(wb, opts));
d.push(write_sty_xlml(wb, opts));
for(var i = 0; i < wb.SheetNames.length; ++i)
d.push(writextag("Worksheet", write_ws_xlml(i, opts, wb), {"ss:Name":escapexml(wb.SheetNames[i])}));
return XML_HEADER + writextag("Workbook", d.join(""), {
'xmlns': XLMLNS.ss,
'xmlns:o': XLMLNS.o,
'xmlns:x': XLMLNS.x,
'xmlns:ss': XLMLNS.ss,
'xmlns:dt': XLMLNS.dt,
'xmlns:html': XLMLNS.html
});
}
/* [MS-OLEDS] 2.3.8 CompObjStream */
function parse_compobj(obj) {
var v = {};
@ -10334,6 +10573,7 @@ function slurp(R, blob, length, opts) {
}
function safe_format_xf(p, opts, date1904) {
if(p.t === 'z') return;
if(p.t === 'e') { p.w = p.w || BErr[p.v]; }
if(!p.XF) return;
try {
@ -10590,9 +10830,9 @@ function parse_workbook(blob, options) {
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);
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);
} 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);
@ -10604,7 +10844,7 @@ function parse_workbook(blob, options) {
last_formula.val = val;
temp_val = ({v:last_formula.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;
@ -10615,7 +10855,7 @@ function parse_workbook(blob, options) {
if(options.cellFormula && out[last_cell]) {
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 = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
out[last_cell].F = encode_range(val[0]);
}
} break;
@ -10626,7 +10866,7 @@ function parse_workbook(blob, options) {
/* TODO: capture range */
if(!last_formula) break; /* technically unreachable */
shared_formulae[encode_cell(last_formula.cell)]= val[0];
(out[encode_cell(last_formula.cell)]||{}).f = stringify_formula(val[0], range, lastcell, supbooks, opts);
(out[encode_cell(last_formula.cell)]||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
}
} break;
case 'LabelSst':
@ -10635,6 +10875,19 @@ 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 'Blank': if(options.sheetStubs) {
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], t:'z'};
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:val.c, r:val.r}, temp_val, options);
} break;
case 'MulBlank': if(options.sheetStubs) {
for(var _j = val.c; _j <= val.C; ++_j) {
var _ixfe = val.ixfe[_j-val.c];
temp_val= {ixfe:_ixfe, XF:XFs[_ixfe], t:'z'};
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell({c:_j, r:val.r}, temp_val, options);
}
} break;
case 'RString':
case 'Label': case 'BIFF2STR':
temp_val=make_cell(val.val, val.ixfe, 's');
@ -10701,7 +10954,6 @@ function parse_workbook(blob, options) {
case 'ColInfo': break; // TODO
case 'Row': break; // TODO
case 'DBCell': break; // TODO
case 'MulBlank': break; // TODO
case 'EntExU2': break; // TODO
case 'SxView': break; // TODO
case 'Sxvd': break; // TODO
@ -10716,7 +10968,6 @@ function parse_workbook(blob, options) {
case 'Feat': break;
case 'FeatHdr': case 'FeatHdr11': break;
case 'Feature11': case 'Feature12': case 'List12': break;
case 'Blank': break;
case 'Country': country = val; break;
case 'RecalcId': break;
case 'DefaultRowHeight': case 'DxGCol': break; // TODO: htmlify
@ -12433,8 +12684,10 @@ var parse_content_xml = (function() {
rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R;
C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'covered-table-cell': // 9.1.5 <table:covered-table-cell>
++C;
if(opts.sheetStubs) ws[encode_cell({r:R,c:C})] = {t:'z'};
break; /* stub */
case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
@ -12480,13 +12733,15 @@ var parse_content_xml = (function() {
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(new Date(ctag['date-value'])); q.z = 'm/d/yy'; break;
case 'date': q.t = 'd'; q.v = new Date(ctag['date-value']);
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v); }
q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
if(ctag['string-value'] != null) textp = unescapexml(ctag['string-value']);
} else throw new Error('Unsupported value type ' + q.t);
}
} else {
@ -12496,7 +12751,7 @@ var parse_content_xml = (function() {
isstub = textpidx == 0;
}
if(textp) q.w = textp;
if(!isstub || opts.cellStubs) {
if(!isstub || opts.sheetStubs) {
if(!(opts.sheetRows && opts.sheetRows < R)) {
ws[encode_cell({r:R,c:C})] = q;
while(--rept > 0) ws[encode_cell({r:R,c:++C})] = dup(q);
@ -12769,22 +13024,38 @@ var parse_content_xml = (function() {
})();
var write_content_xml = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var covered_cell_xml = ' <table:covered-table-cell/>\n';
var cell_begin = ' <table:table-cell ', cell_end = '</table:table-cell>\n';
var vt = 'office:value-type=';
var p_begin = '<text:p>', p_end = '</text:p>';
var write_ws = function(ws, wb, i, opts) {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = decode_range(ws['!ref']);
var marr = ws['!merges'] || [], mi = 0;
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var skip = false, mxml = "";
for(mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c > C) continue;
if(marr[mi].s.r > R) continue;
if(marr[mi].e.c < C) continue;
if(marr[mi].e.r < R) continue;
if(marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
mxml = 'table:number-columns-spanned="' + (marr[mi].e.c - marr[mi].s.c + 1) + '" table:number-rows-spanned="' + (marr[mi].e.r - marr[mi].s.r + 1) + '" ';
break;
}
if(skip) { o.push(covered_cell_xml); continue; }
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
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 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
@ -13039,7 +13310,7 @@ function parse_zip(zip, opts) {
out.files = zip.files;
}
if(opts.bookVBA) {
if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,dir.vba[0],true);
if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,dir.vba[0].replace(/^\//,''),true);
else if(dir.defaults && dir.defaults.bin === 'application/vnd.ms-office.vbaProject') out.vbaraw = getzipdata(zip,'xl/vbaProject.bin',true);
}
return out;
@ -13228,7 +13499,8 @@ 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);
case 'xml':
case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
@ -13248,6 +13520,7 @@ function writeFileSync(wb, filename, opts) {
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
case '.xlml': o.bookType = 'xlml'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
@ -13321,7 +13594,7 @@ function safe_format_cell(cell, v) {
}
function format_cell(cell, v) {
if(cell == null || cell.t == null) return "";
if(cell == null || cell.t == null || cell.t == 'z') return "";
if(cell.w !== undefined) return cell.w;
if(v === undefined) return safe_format_cell(cell, cell.v);
return safe_format_cell(cell, v);
@ -13376,6 +13649,7 @@ function sheet_to_json(sheet, opts){
if(val === undefined || val.t === undefined) continue;
v = val.v;
switch(val.t){
case 'z': continue;
case 'e': continue;
case 's': break;
case 'b': case 'n': break;
@ -13442,6 +13716,7 @@ function sheet_to_formulae(sheet) {
if(y.indexOf(":") == -1) y = y + ":" + y;
}
if(x.f != null) val = x.f;
else if(x.t == 'z') continue;
else if(x.t == 'n' && x.v != null) val = "" + x.v;
else if(x.t == 'b') val = x.v ? "TRUE" : "FALSE";
else if(x.w !== undefined) val = "'" + x.w;

22
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

31
multiformat.lst Normal file

@ -0,0 +1,31 @@
# This file controls the multiformat tests
# Format: <basename> <ext> <ext> [ext..]
AutoFilter .xls .xlsb .xlsx .xml
#BlankSheetTypes .xls .xlsb .xlsx .xml
NumberFormatCondition .xls .xlsb .xlsm .xml
RkNumber .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
#calendar_stress_test .xls .xlsb .xlsx .xml
cell_style_simple .xls .xlsb .xlsx .xml
# no-csv (newline character \r vs \n)
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
defined_names_simple .xls .xlsb .xlsx .xml
#formula_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
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
named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
# no-csv (macro serialization in xml)
number_format .xls .xlsb .xlsm .xls.xml .xlsb.xml .xlsm.xml
number_format_entities .xls .xlsb .xlsx .xml
# yes-csv
pivot_table_named_range .xls .xlsb .xlsx .xml
pivot_table_test .xls .xlsb .xlsm
rich_text_stress .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
smart_tags_2007 .xls .xlsb .xlsx .xml
sushi .xls .xlsb .xlsx .xml
text_and_numbers .xls .xlsb .xlsx .xml
#time_stress_test_1 .xls .xlsb .xlsx .xml
xlsx-stream-d-date-cell .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.9.2",
"version": "0.9.3",
"author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLSM/XLS/XML) and ODS (ODS/FODS/UOS) spreadsheet parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "office", "spreadsheet" ],

49
test.js

@ -866,13 +866,14 @@ describe('roundtrip features', function() {
describe('should preserve features', function() {
it('merge cells', function() {
["xlsx", "xlsb", "xlml", "ods"].forEach(function(f) {
var wb1 = X.readFile(paths.mcxlsx);
var wb2 = X.read(X.write(wb1, {type:'binary'}), {type:'binary'});
var wb2 = X.read(X.write(wb1,{bookType:f,type:'binary'}),{type:'binary'});
var m1 = wb1.Sheets.Merge['!merges'].map(X.utils.encode_range);
var m2 = wb2.Sheets.Merge['!merges'].map(X.utils.encode_range);
assert.equal(m1.length, m2.length);
for(var i = 0; i < m1.length; ++i) assert.equal(m1[i], m2[i]);
});
for(var i = 0; i < m1.length; ++i) assert(m1.indexOf(m2[i]) > -1);
}); });
});
describe('should preserve dates', function() {
@ -1204,3 +1205,45 @@ describe('encryption', function() {
});
});
});
describe('multiformat tests', function() {
var mfopts = opts;
var mft = fs.readFileSync('multiformat.lst','utf-8').split("\n");
var csv = true;
mft.forEach(function(x) {
if(x[0]!="#") describe('MFT ' + x, function() {
var fil = {}, f = [], r = x.split(/\s+/);
if(r.length < 3) return;
it('should parse all', function() {
for(var j = 1; j != r.length; ++j) f[j-1] = X.readFile(dir + r[0] + r[j], mfopts);
});
it('should have the same sheetnames', function() {
cmparr(f.map(function(x) { return x.SheetNames; }));
});
it('should have the same ranges', function() {
f[0].SheetNames.forEach(function(s) {
var ss = f.map(function(x) { return x.Sheets[s]; });
cmparr(ss.map(function(s) { return s['!ref']; }));
});
});
it('should have the same merges', function() {
f[0].SheetNames.forEach(function(s) {
var ss = f.map(function(x) { return x.Sheets[s]; });
cmparr(ss.map(function(s) { return (s['!merges']||[]).map(function(y) { return X.utils.encode_range(y); }).sort(); }));
});
});
it('should have the same CSV', csv ? 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_csv(x.Sheets[name]); }));
});
} : null);
});
else x.split(/\s+/).forEach(function(w) { switch(w) {
case "no-csv": csv = false; break;
case "yes-csv": csv = true; break;
}});
});
});

@ -5,7 +5,7 @@
/*exported XLSX */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.9.2';
XLSX.version = '0.9.3';
var current_codepage = 1200, current_cptable;
/*:: declare var cptable:any; */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -1500,15 +1500,15 @@ var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var unescapexml/*:StringConv*/ = (function() {
/* 22.4.2.4 bstr (Basic String) */
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]{4})_/g;
var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/g, coderegex = /_x([\da-fA-F]{4})_/g;
return function unescapexml(text/*:string*/)/*:string*/ {
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
};
})();
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text/*:string*/)/*:string*/{
function escapexml(text/*:string*/, xml/*:?boolean*/)/*:string*/{
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
@ -8806,6 +8806,13 @@ function parse_BrtFmlaString(data, length, opts) {
/* [MS-XLSB] 2.4.676 BrtMergeCell */
var parse_BrtMergeCell = parse_UncheckedRfX;
var write_BrtMergeCell = write_UncheckedRfX;
/* [MS-XLSB] 2.4.108 BrtBeginMergeCells */
function write_BrtBeginMergeCells(cnt, o) {
if(o == null) o = new_buf(4);
o.write_shift(4, cnt);
return o;
}
/* [MS-XLSB] 2.4.656 BrtHLink */
function parse_BrtHLink(data, length, opts) {
@ -9147,7 +9154,14 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
write_record(ba, 'BrtEndSheetData');
}
function write_ws_bin(idx, opts, wb) {
function write_MERGECELLS(ba, ws/*:Worksheet*/) {
if(!ws || !ws['!merges']) return;
write_record(ba, 'BrtBeginMergeCells', write_BrtBeginMergeCells(ws['!merges'].length));
ws['!merges'].forEach(function(m) { write_record(ba, 'BrtMergeCell', write_BrtMergeCell(m)); });
write_record(ba, 'BrtEndMergeCells');
}
function write_ws_bin(idx/*:number*/, opts, wb/*:Workbook*/) {
var ba = buf_array();
var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
var r = safe_decode_range(ws['!ref'] || "A1");
@ -9166,7 +9180,7 @@ function write_ws_bin(idx, opts, wb) {
/* [SORTSTATE] */
/* [DCON] */
/* [USERSHVIEWS] */
/* [MERGECELLS] */
write_MERGECELLS(ba, ws);
/* [BrtPhoneticInfo] */
/* *CONDITIONALFORMATTING */
/* [DVALS] */
@ -10484,6 +10498,15 @@ function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
var attr = {};
if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
if(ws['!merges']) {
var marr = ws['!merges'];
for(var mi = 0; mi != marr.length; ++mi) {
if(marr[mi].s.c != addr.c || marr[mi].s.r != addr.r) continue;
if(marr[mi].e.c > marr[mi].s.c) attr['ss:MergeAcross'] = marr[mi].e.c - marr[mi].s.c;
if(marr[mi].e.r > marr[mi].s.r) attr['ss:MergeDown'] = marr[mi].e.r - marr[mi].s.r;
}
}
var t = "", p = "";
switch(cell.t) {
case 'z': return "";
@ -10491,7 +10514,7 @@ 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;