forked from sheetjs/sheetjs
experimental dense representation
- browser demo save file in global (fixes #573 h/t @WildDusk) - flesh out XLSB record expectations (fixes #624 h/t @pgeeh) - `dense` mode (fixes #34 h/t @schleumer)
This commit is contained in:
parent
d086dbecbf
commit
f43cacaf5e
@ -40,6 +40,7 @@ program
|
||||
.option('--perf', 'do not generate output')
|
||||
.option('--all', 'parse everything; write as much as possible')
|
||||
.option('--dev', 'development mode')
|
||||
.option('--sparse', 'sparse mode')
|
||||
.option('--read', 'read but do not print out contents')
|
||||
.option('-q, --quiet', 'quiet mode');
|
||||
|
||||
@ -111,6 +112,7 @@ if(program.all) {
|
||||
opts.sheetStubs = true;
|
||||
opts.cellDates = true;
|
||||
}
|
||||
if(program.sparse) opts.dense = false; else opts.dense = true;
|
||||
|
||||
if(program.dev) {
|
||||
X.verbose = 2;
|
||||
|
1
bits/03_dense.js
Normal file
1
bits/03_dense.js
Normal file
@ -0,0 +1 @@
|
||||
var DENSE = false;
|
@ -6,7 +6,8 @@ function sheet_to_workbook(sheet/*:Worksheet*/, opts)/*:Workbook*/ {
|
||||
|
||||
function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
var o = opts || {};
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/);
|
||||
if(DENSE != null) o.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = o.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var range/*:Range*/ = ({s: {c:10000000, r:10000000}, e: {c:0, r:0}}/*:any*/);
|
||||
for(var R = 0; R != data.length; ++R) {
|
||||
for(var C = 0; C != data[R].length; ++C) {
|
||||
@ -16,7 +17,6 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
if(range.s.c > C) range.s.c = C;
|
||||
if(range.e.r < R) range.e.r = R;
|
||||
if(range.e.c < C) range.e.c = C;
|
||||
var cell_ref = encode_cell(({c:C,r:R}/*:any*/));
|
||||
if(cell.v === null) { if(!o.cellStubs) continue; cell.t = 'z'; }
|
||||
else if(typeof cell.v === 'number') cell.t = 'n';
|
||||
else if(typeof cell.v === 'boolean') cell.t = 'b';
|
||||
@ -26,7 +26,13 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF.format(cell.z, cell.v); }
|
||||
}
|
||||
else cell.t = 's';
|
||||
ws[cell_ref] = cell;
|
||||
if(o.dense) {
|
||||
if(!ws[R]) ws[R] = [];
|
||||
ws[R][C] = cell;
|
||||
} else {
|
||||
var cell_ref = encode_cell(({c:C,r:R}/*:any*/));
|
||||
ws[cell_ref] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(range.s.c < 10000000) ws['!ref'] = encode_range(range);
|
||||
|
@ -122,7 +122,7 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
"application/vnd.ms-excel.Survey+xml": "TODO",
|
||||
|
||||
/* Drawing */
|
||||
"application/vnd.openxmlformats-officedocument.drawing+xml": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.drawing+xml": "drawings",
|
||||
"application/vnd.openxmlformats-officedocument.drawingml.chart+xml": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml": "TODO",
|
||||
@ -193,7 +193,7 @@ function parse_ct(data/*:?string*/, opts) {
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
|
||||
rels:[], strs:[], comments:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [],
|
||||
calcchains:[], vba: [], drawings: [],
|
||||
TODO:[], xmlns: "" }/*:any*/);
|
||||
if(!data || !data.match) return ct;
|
||||
var ctext = {};
|
||||
@ -256,7 +256,7 @@ function write_ct(ct, opts)/*:string*/ {
|
||||
}
|
||||
};
|
||||
var f2 = function(w) {
|
||||
ct[w].forEach(function(v) {
|
||||
(ct[w]||[]).forEach(function(v) {
|
||||
o[o.length] = (writextag('Override', null, {
|
||||
'PartName': (v[0] == '/' ? "":"/") + v,
|
||||
'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
|
||||
@ -273,11 +273,13 @@ function write_ct(ct, opts)/*:string*/ {
|
||||
};
|
||||
f1('workbooks');
|
||||
f2('sheets');
|
||||
f2('charts');
|
||||
f3('themes');
|
||||
['strs', 'styles'].forEach(f1);
|
||||
['coreprops', 'extprops', 'custprops'].forEach(f3);
|
||||
f3('vba');
|
||||
f2('comments');
|
||||
f3('comments');
|
||||
f3('drawings');
|
||||
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
|
@ -44,11 +44,9 @@ var RELS_ROOT = writextag('Relationships', null, {
|
||||
|
||||
/* TODO */
|
||||
function write_rels(rels)/*:string*/ {
|
||||
var o = [];
|
||||
o[o.length] = (XML_HEADER);
|
||||
o[o.length] = (RELS_ROOT);
|
||||
keys(rels['!id']).forEach(function(rid) { var rel = rels['!id'][rid];
|
||||
o[o.length] = (writextag('Relationship', null, rel));
|
||||
var o = [XML_HEADER, RELS_ROOT];
|
||||
keys(rels['!id']).forEach(function(rid) {
|
||||
o[o.length] = (writextag('Relationship', null, rels['!id'][rid]));
|
||||
});
|
||||
if(o.length>2){ o[o.length] = ('</Relationships>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
|
@ -261,10 +261,12 @@ var SYLK = (function() {
|
||||
var preamble/*:Array<string>*/ = ["ID;PWXL;N;E"], o/*:Array<string>*/ = [];
|
||||
preamble.push("P;PGeneral");
|
||||
var r = decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
if(!(cell = ws[coord]) || cell.v == null) continue;
|
||||
cell = dense ? (ws[R]||[])[C]: ws[coord];
|
||||
if(!cell || cell.v == null) continue;
|
||||
o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
|
||||
}
|
||||
}
|
||||
@ -337,6 +339,7 @@ var DIF = (function() {
|
||||
return function sheet_to_dif(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
var r = decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
push_field(o, "TABLE", 0, 1, "sheetjs");
|
||||
push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1,"");
|
||||
push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1,"");
|
||||
@ -345,7 +348,8 @@ var DIF = (function() {
|
||||
push_value(o, -1, 0, "BOT");
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
if(!(cell = ws[coord]) || cell.v == null) { push_value(o, 1, 0, ""); continue;}
|
||||
cell = dense ? (ws[R]||[])[C] : ws[coord];
|
||||
if(!cell || cell.v == null) { push_value(o, 1, 0, ""); continue;}
|
||||
switch(cell.t) {
|
||||
case 'n': push_value(o, 0, (/*cell.w ||*/ cell.v), "V"); break;
|
||||
case 'b': push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE"); break;
|
||||
@ -402,8 +406,10 @@ var PRN = (function() {
|
||||
}
|
||||
|
||||
function dsv_to_sheet_str(str/*:string*/, opts)/*:Worksheet*/ {
|
||||
var o = opts || {};
|
||||
var sep = "";
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/);
|
||||
if(DENSE != null) o.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = o.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var range/*:Range*/ = ({s: {c:0, r:0}, e: {c:0, r:0}}/*:any*/);
|
||||
|
||||
/* known sep */
|
||||
@ -423,7 +429,8 @@ var PRN = (function() {
|
||||
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
|
||||
else if(!isNaN(v = parseFloat(s))) { cell.t = 'n'; cell.w = s; cell.v = v; }
|
||||
else { cell.t = 's'; cell.v = s.replace(/^"/,"").replace(/"$/,"").replace(/""/g,'"'); }
|
||||
ws[encode_cell({c:C,r:R})] = cell;
|
||||
if(o.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = cell; }
|
||||
else ws[encode_cell({c:C,r:R})] = cell;
|
||||
start = end+1;
|
||||
if(range.e.c < C) range.e.c = C;
|
||||
if(range.e.r < R) range.e.r = R;
|
||||
@ -456,11 +463,13 @@ var PRN = (function() {
|
||||
function sheet_to_prn(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
|
||||
var o/*:Array<string>*/ = [];
|
||||
var r = decode_range(ws['!ref']), cell/*:Cell*/;
|
||||
var dense = Array.isArray(ws);
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
var oo = [];
|
||||
for(var C = r.s.c; C <= r.e.c; ++C) {
|
||||
var coord = encode_cell({r:R,c:C});
|
||||
if(!(cell = ws[coord]) || cell.v == null) { oo.push(" "); continue; }
|
||||
cell = dense ? (ws[R]||[])[C] : ws[coord];
|
||||
if(!cell || cell.v == null) { oo.push(" "); continue; }
|
||||
var w = (cell.w || (format_cell(cell), cell.w) || "").substr(0,10);
|
||||
while(w.length < 10) w += " ";
|
||||
oo.push(w + (C == 0 ? " " : ""));
|
||||
|
@ -27,8 +27,8 @@ var WK_ = (function() {
|
||||
function lotus_to_workbook_buf(d,opts)/*:Workbook*/ {
|
||||
if(!d) return d;
|
||||
var o = opts || {};
|
||||
|
||||
var s = {}, n = "Sheet1", sidx = 0;
|
||||
if(DENSE != null) o.dense = DENSE;
|
||||
var s = (o.dense ? [] : {}), n = "Sheet1", sidx = 0;
|
||||
var sheets = {}, snames = [n];
|
||||
|
||||
var refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
|
||||
@ -45,13 +45,16 @@ var WK_ = (function() {
|
||||
break;
|
||||
case 0x06: refguess = val; break; /* RANGE */
|
||||
case 0x0F: /* LABEL */
|
||||
if(!opts.qpro) val[1].v = val[1].v.substr(1);
|
||||
if(!o.qpro) val[1].v = val[1].v.substr(1);
|
||||
/* falls through */
|
||||
case 0x0D: /* INTEGER */
|
||||
case 0x0E: /* NUMBER */
|
||||
case 0x10: /* FORMULA */
|
||||
case 0x33: /* STRING */
|
||||
s[encode_cell(val[0])] = val[1];
|
||||
if(o.dense) {
|
||||
if(!s[val[0].r]) s[val[0].r] = [];
|
||||
s[val[0].r][val[0].c] = val[1];
|
||||
} else s[encode_cell(val[0])] = val[1];
|
||||
/* TODO: FORMAT */
|
||||
break;
|
||||
} else switch(RT) {
|
||||
@ -67,7 +70,7 @@ var WK_ = (function() {
|
||||
if(val[3] > sidx) {
|
||||
s["!ref"] = encode_range(refguess);
|
||||
sheets[n] = s;
|
||||
s = {};
|
||||
s = (o.dense ? [] : {});
|
||||
refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
|
||||
sidx = val[3]; n = "Sheet" + (sidx + 1);
|
||||
snames.push(n);
|
||||
|
@ -12,7 +12,6 @@ function parse_sst_bin(data, opts)/*:SST*/ {
|
||||
case 'BrtBeginSst': s.Count = val[0]; s.Unique = val[1]; break;
|
||||
case 'BrtSSTItem': s.push(val); break;
|
||||
case 'BrtEndSst': return true;
|
||||
/* TODO: produce a test case with a future record */
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
@ -34,6 +33,7 @@ function write_sst_bin(sst, opts) {
|
||||
var ba = buf_array();
|
||||
write_record(ba, "BrtBeginSst", write_BrtBeginSst(sst));
|
||||
for(var i = 0; i < sst.length; ++i) write_record(ba, "BrtSSTItem", write_BrtSSTItem(sst[i]));
|
||||
/* FRTSST */
|
||||
write_record(ba, "BrtEndSst");
|
||||
return ba.end();
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ function parse_sty_bin(data, themes, opts) {
|
||||
for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
|
||||
|
||||
styles.CellXf = [];
|
||||
var state = ""; /* TODO: this should be a stack */
|
||||
var state = [];
|
||||
var pass = false;
|
||||
recordhopper(data, function hopper_sty(val, R, RT) {
|
||||
switch(R.n) {
|
||||
@ -59,7 +59,7 @@ function parse_sty_bin(data, themes, opts) {
|
||||
case 'BrtFill': break; /* TODO */
|
||||
case 'BrtBorder': break; /* TODO */
|
||||
case 'BrtXF':
|
||||
if(state === "CELLXFS") {
|
||||
if(state[state.length - 1] == "BrtBeginCellXFs") {
|
||||
styles.CellXf.push(val);
|
||||
}
|
||||
break; /* TODO */
|
||||
@ -67,48 +67,23 @@ function parse_sty_bin(data, themes, opts) {
|
||||
case 'BrtDXF': break; /* TODO */
|
||||
case 'BrtMRUColor': break; /* TODO */
|
||||
case 'BrtIndexedColor': break; /* TODO */
|
||||
case 'BrtBeginStyleSheet': break;
|
||||
case 'BrtEndStyleSheet': break;
|
||||
case 'BrtBeginTableStyle': break;
|
||||
|
||||
case 'BrtDXF14': break;
|
||||
case 'BrtDXF15': break;
|
||||
case 'BrtUid': break;
|
||||
case 'BrtSlicerStyleElement': break;
|
||||
case 'BrtTableStyleElement': break;
|
||||
case 'BrtEndTableStyle': break;
|
||||
case 'BrtBeginFmts': state = "FMTS"; break;
|
||||
case 'BrtEndFmts': state = ""; break;
|
||||
case 'BrtBeginFonts': state = "FONTS"; break;
|
||||
case 'BrtEndFonts': state = ""; break;
|
||||
case 'BrtACBegin': state = "ACFONTS"; break;
|
||||
case 'BrtACEnd': state = ""; break;
|
||||
case 'BrtBeginFills': state = "FILLS"; break;
|
||||
case 'BrtEndFills': state = ""; break;
|
||||
case 'BrtBeginBorders': state = "BORDERS"; break;
|
||||
case 'BrtEndBorders': state = ""; break;
|
||||
case 'BrtBeginCellStyleXFs': state = "CELLSTYLEXFS"; break;
|
||||
case 'BrtEndCellStyleXFs': state = ""; break;
|
||||
case 'BrtBeginCellXFs': state = "CELLXFS"; break;
|
||||
case 'BrtEndCellXFs': state = ""; break;
|
||||
case 'BrtBeginStyles': state = "STYLES"; break;
|
||||
case 'BrtEndStyles': state = ""; break;
|
||||
case 'BrtBeginDXFs': state = "DXFS"; break;
|
||||
case 'BrtEndDXFs': state = ""; break;
|
||||
case 'BrtBeginTableStyles': state = "TABLESTYLES"; break;
|
||||
case 'BrtEndTableStyles': state = ""; break;
|
||||
case 'BrtBeginColorPalette': state = "COLORPALETTE"; break;
|
||||
case 'BrtEndColorPalette': state = ""; break;
|
||||
case 'BrtBeginIndexedColors': state = "INDEXEDCOLORS"; break;
|
||||
case 'BrtEndIndexedColors': state = ""; break;
|
||||
case 'BrtBeginMRUColors': state = "MRUCOLORS"; break;
|
||||
case 'BrtEndMRUColors': state = ""; break;
|
||||
case 'BrtTimelineStyleElement': break;
|
||||
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
case 'BrtBeginStyleSheetExt14': break;
|
||||
case 'BrtBeginSlicerStyles': break;
|
||||
case 'BrtEndSlicerStyles': break;
|
||||
case 'BrtBeginTimelineStylesheetExt15': break;
|
||||
case 'BrtEndTimelineStylesheetExt15': break;
|
||||
case 'BrtBeginTimelineStyles': break;
|
||||
case 'BrtEndTimelineStyles': break;
|
||||
case 'BrtEndStyleSheetExt14': break;
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
case 'BrtACBegin': state.push(R.n); break;
|
||||
case 'BrtACEnd': state.pop(); break;
|
||||
|
||||
default:
|
||||
if((R.n||"").indexOf("Begin") > 0) state.push(R.n);
|
||||
else if((R.n||"").indexOf("End") > 0) state.pop();
|
||||
else if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
}
|
||||
});
|
||||
return styles;
|
||||
|
@ -1,3 +1,5 @@
|
||||
RELS.IMG = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
||||
RELS.DRAW = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
|
||||
/* 20.5 DrawingML - SpreadsheetML Drawing */
|
||||
function parse_drawing(data, rels/*:any*/) {
|
||||
if(!data) return "??";
|
||||
@ -16,3 +18,4 @@ function parse_drawing(data, rels/*:any*/) {
|
||||
|
||||
return rels['!id'][id].Target;
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,18 @@ function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
|
||||
}
|
||||
|
||||
function insertCommentsIntoSheet(sheetName, sheet, comments) {
|
||||
var dense = Array.isArray(sheet);
|
||||
var cell, r;
|
||||
comments.forEach(function(comment) {
|
||||
var cell = sheet[comment.ref];
|
||||
if(dense) {
|
||||
r = decode_cell(comment.ref);
|
||||
if(!sheet[r.r]) sheet[r.r] = [];
|
||||
cell = sheet[r.r][r.c];
|
||||
} else cell = sheet[comment.ref];
|
||||
if (!cell) {
|
||||
cell = {};
|
||||
sheet[comment.ref] = cell;
|
||||
if(dense) sheet[r.r][r.c] = cell;
|
||||
else sheet[comment.ref] = cell;
|
||||
var range = safe_decode_range(sheet["!ref"]||"BDWGO1000001:A1");
|
||||
var thisCell = decode_cell(comment.ref);
|
||||
if(range.s.r > thisCell.r) range.s.r = thisCell.r;
|
||||
|
@ -42,13 +42,17 @@ function parse_comments_bin(data, opts) {
|
||||
if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break;
|
||||
if(!c.t) c.t = "";
|
||||
delete c.rfx; out.push(c); break;
|
||||
case 'BrtBeginComments': break;
|
||||
case 'BrtEndComments': break;
|
||||
case 'BrtBeginCommentAuthors': break;
|
||||
case 'BrtEndCommentAuthors': break;
|
||||
case 'BrtBeginCommentList': break;
|
||||
case 'BrtEndCommentList': break;
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
|
||||
case 'BrtUid': break;
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
case 'BrtACBegin': break;
|
||||
case 'BrtACEnd': break;
|
||||
|
||||
default:
|
||||
if((R.n||"").indexOf("Begin") > 0){}
|
||||
else if((R.n||"").indexOf("End") > 0){}
|
||||
else if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
|
@ -10,8 +10,9 @@ var colregex = /<(?:\w*:)?col[^>]*[\/]?>/g;
|
||||
/* 18.3 Worksheets */
|
||||
function parse_ws_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
/* 18.3.1.99 worksheet CT_Worksheet */
|
||||
var s = ({}/*:any*/);
|
||||
var s = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
|
||||
/* 18.3.1.35 dimension CT_SheetDimension ? */
|
||||
// $FlowIgnore
|
||||
@ -89,6 +90,7 @@ function write_ws_xml_protection(sp)/*:string*/ {
|
||||
}
|
||||
|
||||
function parse_ws_xml_hlinks(s, data/*:Array<string>*/, rels) {
|
||||
var dense = Array.isArray(s);
|
||||
for(var i = 0; i != data.length; ++i) {
|
||||
var val = parsexmltag(data[i], true);
|
||||
if(!val.ref) return;
|
||||
@ -106,8 +108,14 @@ function parse_ws_xml_hlinks(s, data/*:Array<string>*/, 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:"z",v:undefined};
|
||||
s[addr].l = val;
|
||||
if(dense) {
|
||||
if(!s[R]) s[R] = [];
|
||||
if(!s[R][C]) s[R][C] = {t:"z",v:undefined};
|
||||
s[R][C].l = val;
|
||||
} else {
|
||||
if(!s[addr]) s[addr] = {t:"z",v:undefined};
|
||||
s[addr].l = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,6 +199,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
|
||||
var arrayf = [];
|
||||
var sharedf = [];
|
||||
var dense = Array.isArray(s);
|
||||
for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
|
||||
x = marr[mt].trim();
|
||||
var xlen = x.length;
|
||||
@ -305,21 +314,27 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) {
|
||||
if(opts.cellDates && do_format && p.t == 'n' && SSF.is_date(SSF._table[fmtid])) {
|
||||
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(Date.UTC(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u)); }
|
||||
}
|
||||
s[tag.r] = p;
|
||||
if(dense) {
|
||||
var _r = decode_cell(tag.r);
|
||||
if(!s[_r.r]) s[_r.r] = [];
|
||||
s[_r.r][_r.c] = p;
|
||||
} else s[tag.r] = p;
|
||||
}
|
||||
}
|
||||
}; })();
|
||||
|
||||
function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/, rels)/*:string*/ {
|
||||
var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R=0, C=0, rows = ws['!rows'];
|
||||
var dense = Array.isArray(ws);
|
||||
for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(R = range.s.r; R <= range.e.r; ++R) {
|
||||
r = [];
|
||||
rr = encode_row(R);
|
||||
for(C = range.s.c; C <= range.e.c; ++C) {
|
||||
ref = cols[C] + rr;
|
||||
if(ws[ref] === undefined) continue;
|
||||
if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb)) != null) r.push(cell);
|
||||
var _cell = dense ? (ws[R]||[])[C]: ws[ref];
|
||||
if(_cell === undefined) continue;
|
||||
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
|
||||
}
|
||||
if(r.length > 0) {
|
||||
var params = ({r:rr}/*:any*/);
|
||||
@ -350,6 +365,7 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
|
||||
var ref = ws['!ref']; if(ref === undefined) ref = 'A1';
|
||||
if(!rels) rels = {};
|
||||
ws['!comments'] = [];
|
||||
ws['!drawing'] = [];
|
||||
|
||||
o[o.length] = (writextag('sheetPr', null, {'codeName': escapexml(wb.SheetNames[idx])}));
|
||||
o[o.length] = (writextag('dimension', null, {'ref': ref}));
|
||||
@ -385,12 +401,20 @@ function write_ws_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
|
||||
}
|
||||
delete ws['!links'];
|
||||
|
||||
var hfidx = o.length;
|
||||
o[o.length] = "";
|
||||
|
||||
if(ws['!drawing'].length > 0) {
|
||||
rId = add_rels(rels, -1, "../drawings/drawing" + (idx+1) + ".xml", RELS.DRAW);
|
||||
ws['!drawing'].rid = rId;
|
||||
o[o.length] = writextag("drawing", null, {"r:id":"rId" + rId});
|
||||
}
|
||||
else delete ws['!drawing'];
|
||||
if(ws['!comments'].length > 0) {
|
||||
rId = add_rels(rels, -1, "../drawings/vmlDrawing" + (idx+1) + ".vml", RELS.VML);
|
||||
o[o.length] = writextag("legacyDrawing", null, {"r:id":"rId" + rId});
|
||||
ws['!legacy'] = rId;
|
||||
}
|
||||
// <legacyDrawing r:id="rId1"/>
|
||||
|
||||
if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
|
235
bits/68_wsbin.js
235
bits/68_wsbin.js
@ -26,7 +26,8 @@ function write_BrtRowHdr(R/*:number*/, range, ws) {
|
||||
var first = -1, last = -1;
|
||||
for(var j = (i<<10); j < ((i+1)<<10); ++j) {
|
||||
caddr.c = j;
|
||||
if(ws[encode_cell(caddr)]) { if(first < 0) first = j; last = j; }
|
||||
var cell = Array.isArray(ws) ? (ws[caddr.r]||[])[caddr.c] : ws[encode_cell(caddr)];
|
||||
if(cell) { if(first < 0) first = j; last = j; }
|
||||
}
|
||||
if(first < 0) continue;
|
||||
++ncolspan;
|
||||
@ -289,18 +290,19 @@ function write_BrtColInfo(C/*:number*/, col, o) {
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.1.7.61 Worksheet */
|
||||
function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
var opts = _opts || {};
|
||||
if(!rels) rels = {'!id':{}};
|
||||
var s = {};
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var s = opts.dense ? [] : {};
|
||||
|
||||
var ref;
|
||||
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
|
||||
var pass = false, end = false;
|
||||
var row, p, cf, R, C, addr, sstr, rr;
|
||||
var row, p, cf, R, C, addr, sstr, rr, cell;
|
||||
var mergecells = [];
|
||||
if(!opts) opts = {};
|
||||
opts.biff = 12;
|
||||
opts['!row'] = 0;
|
||||
|
||||
@ -319,14 +321,14 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||
var seencol = false;
|
||||
|
||||
recordhopper(data, function ws_parse(val, Record) {
|
||||
recordhopper(data, function ws_parse(val, Record, RT) {
|
||||
if(end) return;
|
||||
switch(Record.n) {
|
||||
case 'BrtWsDim': ref = val; break;
|
||||
case 'BrtRowHdr':
|
||||
row = val;
|
||||
if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
|
||||
rr = encode_row(row.r);
|
||||
rr = encode_row(R = row.r);
|
||||
opts['!row'] = row.r;
|
||||
break;
|
||||
|
||||
@ -349,7 +351,9 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
case 'str': p.t = 's'; p.v = utf8read(val[1]); break;
|
||||
}
|
||||
if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.ifmt,null,opts, themes, styles);
|
||||
s[encode_col(C=val[0].c) + rr] = p;
|
||||
C = val[0].c;
|
||||
if(opts.dense) { if(!s[R]) s[R] = []; s[R][C] = p; }
|
||||
else s[encode_col(C) + rr] = p;
|
||||
if(opts.cellFormula) {
|
||||
af = false;
|
||||
for(ai = 0; ai < array_formulae.length; ++ai) {
|
||||
@ -370,18 +374,18 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'BrtCellBlank': if(!opts.sheetStubs) break;
|
||||
case 'BrtCellBlank':
|
||||
if(!opts.sheetStubs) break;
|
||||
p = ({t:'z',v:undefined}/*:any*/);
|
||||
s[encode_col(C=val[0].c) + rr] = p;
|
||||
C = val[0].c;
|
||||
if(opts.dense) { if(!s[R]) s[R] = []; s[R][C] = p; }
|
||||
else s[encode_col(C) + rr] = p;
|
||||
if(refguess.s.r > row.r) refguess.s.r = row.r;
|
||||
if(refguess.s.c > C) refguess.s.c = C;
|
||||
if(refguess.e.r < row.r) refguess.e.r = row.r;
|
||||
if(refguess.e.c < C) refguess.e.c = C;
|
||||
break;
|
||||
|
||||
/* Merge Cells */
|
||||
case 'BrtBeginMergeCells': break;
|
||||
case 'BrtEndMergeCells': break;
|
||||
case 'BrtMergeCell': mergecells.push(val); break;
|
||||
|
||||
case 'BrtHLink':
|
||||
@ -392,140 +396,109 @@ function parse_ws_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
val.Rel = rel;
|
||||
}
|
||||
for(R=val.rfx.s.r;R<=val.rfx.e.r;++R) for(C=val.rfx.s.c;C<=val.rfx.e.c;++C) {
|
||||
addr = encode_cell({c:C,r:R});
|
||||
if(!s[addr]) s[addr] = {t:'s',v:undefined};
|
||||
s[addr].l = val;
|
||||
if(opts.dense) {
|
||||
if(!s[R]) s[R] = [];
|
||||
if(!s[R][C]) s[R][C] = {t:'z',v:undefined};
|
||||
s[R][C].l = val;
|
||||
} else {
|
||||
addr = encode_cell({c:C,r:R});
|
||||
if(!s[addr]) s[addr] = {t:'z',v:undefined};
|
||||
s[addr].l = val;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'BrtArrFmla': if(!opts.cellFormula) break;
|
||||
case 'BrtArrFmla':
|
||||
if(!opts.cellFormula) break;
|
||||
array_formulae.push(val);
|
||||
s[encode_col(C) + rr].f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
s[encode_col(C) + rr].F = encode_range(val[0]);
|
||||
cell = (opts.dense ? s[R][C] : s[encode_col(C) + rr]);
|
||||
cell.f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
cell.F = encode_range(val[0]);
|
||||
break;
|
||||
case 'BrtShrFmla': if(!opts.cellFormula) break;
|
||||
// TODO
|
||||
case 'BrtShrFmla':
|
||||
if(!opts.cellFormula) break;
|
||||
shared_formulae[encode_cell(val[0].s)] = val[1];
|
||||
s[encode_col(C) + rr].f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
cell = (opts.dense ? s[R][C] : s[encode_col(C) + rr]);
|
||||
cell.f = stringify_formula(val[1], refguess, {r:row.r, c:C}, supbooks, opts);
|
||||
break;
|
||||
|
||||
/* identical to 'ColInfo' in XLS */
|
||||
case 'BrtColInfo': {
|
||||
case 'BrtColInfo':
|
||||
if(!opts.cellStyles) break;
|
||||
while(val.e >= val.s) {
|
||||
colinfo[val.e--] = { width: val.w/256 };
|
||||
if(!seencol) { seencol = true; find_mdw_colw(val.w/256); }
|
||||
process_col(colinfo[val.e+1]);
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
|
||||
case 'BrtAFilterDateGroupItem': break;
|
||||
case 'BrtActiveX': break;
|
||||
case 'BrtBigName': break;
|
||||
case 'BrtBkHim': break;
|
||||
case 'BrtBrk': break;
|
||||
case 'BrtCFIcon': break;
|
||||
case 'BrtCFRuleExt': break;
|
||||
case 'BrtCFVO': break;
|
||||
case 'BrtCFVO14': break;
|
||||
case 'BrtCellIgnoreEC': break;
|
||||
case 'BrtCellIgnoreEC14': break;
|
||||
case 'BrtCellMeta': break;
|
||||
case 'BrtCellSmartTagProperty': break;
|
||||
case 'BrtCellWatch': break;
|
||||
case 'BrtColor': break;
|
||||
case 'BrtColor14': break;
|
||||
case 'BrtColorFilter': break;
|
||||
case 'BrtCustomFilter': break;
|
||||
case 'BrtCustomFilter14': break;
|
||||
case 'BrtDRef': break;
|
||||
case 'BrtDVal': break;
|
||||
case 'BrtDVal14': break;
|
||||
case 'BrtDValList': break;
|
||||
case 'BrtDrawing': break;
|
||||
case 'BrtDynamicFilter': break;
|
||||
case 'BrtFilter': break;
|
||||
case 'BrtFilter14': break;
|
||||
case 'BrtIconFilter': break;
|
||||
case 'BrtIconFilter14': break;
|
||||
case 'BrtLegacyDrawing': break;
|
||||
case 'BrtLegacyDrawingHF': break;
|
||||
case 'BrtListPart': break;
|
||||
case 'BrtMargins': break;
|
||||
case 'BrtOleObject': break;
|
||||
case 'BrtPageSetup': break;
|
||||
case 'BrtPane': break;
|
||||
case 'BrtPhoneticInfo': break;
|
||||
case 'BrtPrintOptions': break;
|
||||
case 'BrtRangeProtection': break;
|
||||
case 'BrtRangeProtection14': break;
|
||||
case 'BrtRangeProtectionIso': break;
|
||||
case 'BrtRangeProtectionIso14': break;
|
||||
case 'BrtRwDescent': break;
|
||||
case 'BrtSel': break;
|
||||
case 'BrtSheetCalcProp': break;
|
||||
case 'BrtSheetProtection': break;
|
||||
case 'BrtSheetProtectionIso': break;
|
||||
case 'BrtSlc': break;
|
||||
case 'BrtSparkline': break;
|
||||
case 'BrtTable': break;
|
||||
case 'BrtTop10Filter': break;
|
||||
case 'BrtUid': break;
|
||||
case 'BrtValueMeta': break;
|
||||
case 'BrtWebExtension': break;
|
||||
case 'BrtWsFmtInfo': break;
|
||||
case 'BrtWsFmtInfoEx14': break;
|
||||
case 'BrtWsProp': break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtWsProp': break; // TODO
|
||||
case 'BrtSheetCalcProp': break; // TODO
|
||||
case 'BrtBeginWsViews': break; // TODO
|
||||
case 'BrtBeginWsView': break; // TODO
|
||||
case 'BrtPane': break; // TODO
|
||||
case 'BrtSel': break; // TODO
|
||||
case 'BrtEndWsView': break; // TODO
|
||||
case 'BrtEndWsViews': break; // TODO
|
||||
case 'BrtACBegin': break; // TODO
|
||||
case 'BrtRwDescent': break; // TODO
|
||||
case 'BrtACEnd': break; // TODO
|
||||
case 'BrtWsFmtInfoEx14': break; // TODO
|
||||
case 'BrtWsFmtInfo': break; // TODO
|
||||
case 'BrtBeginColInfos': break; // TODO
|
||||
case 'BrtEndColInfos': break; // TODO
|
||||
case 'BrtBeginSheetData': break; // TODO
|
||||
case 'BrtEndSheetData': break; // TODO
|
||||
case 'BrtSheetProtection': break; // TODO
|
||||
case 'BrtPrintOptions': break; // TODO
|
||||
case 'BrtMargins': break; // TODO
|
||||
case 'BrtPageSetup': break; // TODO
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
case 'BrtEndSheet': break; // TODO
|
||||
case 'BrtDrawing': break; // TODO
|
||||
case 'BrtLegacyDrawing': break; // TODO
|
||||
case 'BrtLegacyDrawingHF': break; // TODO
|
||||
case 'BrtPhoneticInfo': break; // TODO
|
||||
case 'BrtBeginHeaderFooter': break; // TODO
|
||||
case 'BrtEndHeaderFooter': break; // TODO
|
||||
case 'BrtBrk': break; // TODO
|
||||
case 'BrtBeginRwBrk': break; // TODO
|
||||
case 'BrtEndRwBrk': break; // TODO
|
||||
case 'BrtBeginColBrk': break; // TODO
|
||||
case 'BrtEndColBrk': break; // TODO
|
||||
case 'BrtBeginUserShViews': break; // TODO
|
||||
case 'BrtBeginUserShView': break; // TODO
|
||||
case 'BrtEndUserShView': break; // TODO
|
||||
case 'BrtEndUserShViews': break; // TODO
|
||||
case 'BrtBkHim': break; // TODO
|
||||
case 'BrtBeginOleObjects': break; // TODO
|
||||
case 'BrtOleObject': break; // TODO
|
||||
case 'BrtEndOleObjects': break; // TODO
|
||||
case 'BrtBeginListParts': break; // TODO
|
||||
case 'BrtListPart': break; // TODO
|
||||
case 'BrtEndListParts': break; // TODO
|
||||
case 'BrtBeginSortState': break; // TODO
|
||||
case 'BrtBeginSortCond': break; // TODO
|
||||
case 'BrtEndSortCond': break; // TODO
|
||||
case 'BrtEndSortState': break; // TODO
|
||||
case 'BrtBeginConditionalFormatting': break; // TODO
|
||||
case 'BrtEndConditionalFormatting': break; // TODO
|
||||
case 'BrtBeginCFRule': break; // TODO
|
||||
case 'BrtEndCFRule': break; // TODO
|
||||
case 'BrtBeginDVals': break; // TODO
|
||||
case 'BrtDVal': break; // TODO
|
||||
case 'BrtEndDVals': break; // TODO
|
||||
case 'BrtRangeProtection': break; // TODO
|
||||
case 'BrtBeginDCon': break; // TODO
|
||||
case 'BrtEndDCon': break; // TODO
|
||||
case 'BrtBeginDRefs': break;
|
||||
case 'BrtDRef': break;
|
||||
case 'BrtEndDRefs': break;
|
||||
case 'BrtACBegin': break;
|
||||
case 'BrtACEnd': break;
|
||||
|
||||
/* ActiveX */
|
||||
case 'BrtBeginActiveXControls': break;
|
||||
case 'BrtActiveX': break;
|
||||
case 'BrtEndActiveXControls': break;
|
||||
|
||||
/* AutoFilter */
|
||||
case 'BrtBeginAFilter': break;
|
||||
case 'BrtEndAFilter': break;
|
||||
case 'BrtBeginFilterColumn': break;
|
||||
case 'BrtBeginFilters': break;
|
||||
case 'BrtFilter': break;
|
||||
case 'BrtEndFilters': break;
|
||||
case 'BrtEndFilterColumn': break;
|
||||
case 'BrtDynamicFilter': break;
|
||||
case 'BrtTop10Filter': break;
|
||||
case 'BrtBeginCustomFilters': break;
|
||||
case 'BrtCustomFilter': break;
|
||||
case 'BrtEndCustomFilters': break;
|
||||
|
||||
/* Smart Tags */
|
||||
case 'BrtBeginSmartTags': break;
|
||||
case 'BrtBeginCellSmartTags': break;
|
||||
case 'BrtBeginCellSmartTag': break;
|
||||
case 'BrtCellSmartTagProperty': break;
|
||||
case 'BrtEndCellSmartTag': break;
|
||||
case 'BrtEndCellSmartTags': break;
|
||||
case 'BrtEndSmartTags': break;
|
||||
|
||||
/* Cell Watch */
|
||||
case 'BrtBeginCellWatches': break;
|
||||
case 'BrtCellWatch': break;
|
||||
case 'BrtEndCellWatches': break;
|
||||
|
||||
/* Table */
|
||||
case 'BrtTable': break;
|
||||
|
||||
/* Ignore Cell Errors */
|
||||
case 'BrtBeginCellIgnoreECs': break;
|
||||
case 'BrtCellIgnoreEC': break;
|
||||
case 'BrtEndCellIgnoreECs': break;
|
||||
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + Record.n);
|
||||
default:
|
||||
if((Record.n||"").indexOf("Begin") > 0){}
|
||||
else if((Record.n||"").indexOf("End") > 0){}
|
||||
else if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + Record.n);
|
||||
}
|
||||
}, opts);
|
||||
|
||||
@ -600,6 +573,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
|
||||
function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
|
||||
write_record(ba, 'BrtBeginSheetData');
|
||||
var dense = Array.isArray(ws);
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
rr = encode_row(R);
|
||||
/* [ACCELLTABLE] */
|
||||
@ -609,9 +583,10 @@ function write_CELLTABLE(ba, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbo
|
||||
/* *16384CELL */
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
ref = cols[C] + rr;
|
||||
if(!ws[ref]) continue;
|
||||
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
if(!cell) continue;
|
||||
/* write cell */
|
||||
write_ws_bin_cell(ba, ws[ref], R, C, opts, ws);
|
||||
write_ws_bin_cell(ba, cell, R, C, opts, ws);
|
||||
}
|
||||
}
|
||||
write_record(ba, 'BrtEndSheetData');
|
||||
|
@ -1,5 +1,10 @@
|
||||
RELS.CS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet";
|
||||
|
||||
var CS_XML_ROOT = writextag('chartsheet', null, {
|
||||
'xmlns': XMLNS.main[0],
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
/* 18.3 Worksheets also covers Chartsheets */
|
||||
function parse_cs_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
@ -14,34 +19,67 @@ function parse_cs_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksh
|
||||
if(rels['!id'][s['!rel']]) s['!chart'] = rels['!id'][s['!rel']];
|
||||
return s;
|
||||
}
|
||||
function write_cs_xml(idx/*:number*/, opts, wb/*:Workbook*/, rels)/*:string*/ {
|
||||
var o = [XML_HEADER, CS_XML_ROOT];
|
||||
o[o.length] = writextag("drawing", null, {"r:id": "rId1"});
|
||||
add_rels(rels, -1, "../drawings/drawing" + (idx+1) + ".xml", RELS.DRAW);
|
||||
if(o.length>2) { o[o.length] = ('</chartsheet>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.1.7.7 Chart Sheet */
|
||||
function parse_cs_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
if(!rels) rels = {'!id':{}};
|
||||
var s = {'!type':"chart", '!chart':null, '!rel':""};
|
||||
var state = [];
|
||||
var pass = false;
|
||||
recordhopper(data, function cs_parse(val, Record) {
|
||||
recordhopper(data, function cs_parse(val, Record, RT) {
|
||||
switch(Record.n) {
|
||||
|
||||
case 'BrtDrawing': s['!rel'] = val; break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtCsProp': break; // TODO
|
||||
case 'BrtBeginCsViews': break; // TODO
|
||||
case 'BrtBeginCsView': break; // TODO
|
||||
case 'BrtEndCsView': break; // TODO
|
||||
case 'BrtEndCsViews': break; // TODO
|
||||
case 'BrtCsProtection': break; // TODO
|
||||
case 'BrtUid': break;
|
||||
case 'BrtMargins': break; // TODO
|
||||
case 'BrtLegacyDrawing': break; // TODO
|
||||
case 'BrtLegacyDrawingHF': break; // TODO
|
||||
case 'BrtBkHim': break; // TODO
|
||||
case 'BrtCsProp': break; // TODO
|
||||
case 'BrtCsProtection': break; // TODO
|
||||
case 'BrtCsProtectionIso': break; // TODO
|
||||
case 'BrtCsPageSetup': break; // TODO
|
||||
case 'BrtEndSheet': break; // TODO
|
||||
case 'BrtBeginHeaderFooter': break; // TODO
|
||||
case 'BrtEndHeaderFooter': break; // TODO
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + Record.n);
|
||||
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
case 'BrtACBegin': state.push(R.n); break;
|
||||
case 'BrtACEnd': state.pop(); break;
|
||||
|
||||
default:
|
||||
if((Record.n||"").indexOf("Begin") > 0) state.push(Record.n);
|
||||
else if((Record.n||"").indexOf("End") > 0) state.pop();
|
||||
else if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + Record.n);
|
||||
}
|
||||
}, opts);
|
||||
|
||||
if(rels['!id'][s['!rel']]) s['!chart'] = rels['!id'][s['!rel']];
|
||||
return s;
|
||||
}
|
||||
function write_cs_bin(idx/*:number*/, opts, wb/*:Workbook*/, rels) {
|
||||
var ba = buf_array();
|
||||
write_record(ba, "BrtBeginSheet");
|
||||
/* [BrtCsProp] */
|
||||
/* CSVIEWS */
|
||||
/* [[BrtCsProtectionIso] BrtCsProtection] */
|
||||
/* [USERCSVIEWS] */
|
||||
/* [BrtMargins] */
|
||||
/* [BrtCsPageSetup] */
|
||||
/* [HEADERFOOTER] */
|
||||
/* BrtDrawing */
|
||||
/* [BrtLegacyDrawing] */
|
||||
/* [BrtLegacyDrawingHF] */
|
||||
/* [BrtBkHim] */
|
||||
/* [WEBPUBITEMS] */
|
||||
/* FRTCHARTSHEET */
|
||||
write_record(ba, "BrtEndSheet");
|
||||
return ba.end();
|
||||
}
|
||||
|
@ -68,60 +68,61 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
||||
|
||||
var Names = {}, NameList = [];
|
||||
|
||||
recordhopper(data, function hopper_wb(val, R) {
|
||||
recordhopper(data, function hopper_wb(val, R, RT) {
|
||||
switch(R.n) {
|
||||
case 'BrtBundleSh': wb.Sheets.push(val); break;
|
||||
|
||||
case 'BrtName':
|
||||
Names[val.Name] = val; NameList.push(val.Name);
|
||||
break;
|
||||
case 'BrtNameExt': break;
|
||||
|
||||
case 'BrtBeginBook': break;
|
||||
case 'BrtFileVersion': break;
|
||||
case 'BrtWbProp14': case 'BrtWbProp': break;
|
||||
case 'BrtACBegin': break;
|
||||
case 'BrtAbsPath15': break;
|
||||
case 'BrtACEnd': break;
|
||||
case 'BrtWbFactoid': break;
|
||||
/*case 'BrtBookProtectionIso': break;*/
|
||||
case 'BrtBookProtection': break;
|
||||
case 'BrtBeginBookViews': break;
|
||||
case 'BrtBookProtectionIso': break;
|
||||
case 'BrtBookView': break;
|
||||
case 'BrtEndBookViews': break;
|
||||
case 'BrtBeginBundleShs': break;
|
||||
case 'BrtEndBundleShs': break;
|
||||
case 'BrtBeginFnGroup': break;
|
||||
case 'BrtEndFnGroup': break;
|
||||
case 'BrtBeginExternals': break;
|
||||
case 'BrtSupSelf': break;
|
||||
case 'BrtSupBookSrc': break;
|
||||
case 'BrtExternSheet': break;
|
||||
case 'BrtEndExternals': break;
|
||||
case 'BrtCalcProp': break;
|
||||
case 'BrtUserBookView': break;
|
||||
case 'BrtBeginPivotCacheIDs': break;
|
||||
case 'BrtBeginPivotCacheID': break;
|
||||
case 'BrtEndPivotCacheID': break;
|
||||
case 'BrtEndPivotCacheIDs': break;
|
||||
case 'BrtWebOpt': break;
|
||||
case 'BrtCrashRecErr': break;
|
||||
case 'BrtDecoupledPivotCacheID': break;
|
||||
case 'BrtExternSheet': break;
|
||||
case 'BrtFileRecover': break;
|
||||
case 'BrtFileSharing': break;
|
||||
/*case 'BrtBeginWebPubItems': break;
|
||||
case 'BrtBeginWebPubItem': break;
|
||||
case 'BrtEndWebPubItem': break;
|
||||
case 'BrtEndWebPubItems': break;*/
|
||||
|
||||
/* Smart Tags */
|
||||
case 'BrtBeginSmartTagTypes': break;
|
||||
case 'BrtFileSharingIso': break;
|
||||
case 'BrtFileVersion': break;
|
||||
case 'BrtFnGroup': break;
|
||||
case 'BrtModelRelationship': break;
|
||||
case 'BrtModelTable': break;
|
||||
case 'BrtModelTimeGroupingCalcCol': break;
|
||||
case 'BrtOleSize': break;
|
||||
case 'BrtPivotTableRef': break;
|
||||
case 'BrtPlaceholderName': break;
|
||||
case 'BrtRevisionPtr': break;
|
||||
case 'BrtSmartTagType': break;
|
||||
case 'BrtEndSmartTagTypes': break;
|
||||
case 'BrtSupAddin': break;
|
||||
case 'BrtSupBookSrc': break;
|
||||
case 'BrtSupSame': break;
|
||||
case 'BrtSupSelf': break;
|
||||
case 'BrtTableSlicerCacheID': break;
|
||||
case 'BrtTableSlicerCacheIDs': break;
|
||||
case 'BrtTimelineCachePivotCacheID': break;
|
||||
case 'BrtUid': break;
|
||||
case 'BrtUserBookView': break;
|
||||
case 'BrtWbFactoid': break;
|
||||
case 'BrtWbProp': break;
|
||||
case 'BrtWbProp14': break;
|
||||
case 'BrtWebOpt': break;
|
||||
case 'BrtWorkBookPr15': break;
|
||||
|
||||
case 'BrtFRTBegin': pass = true; break;
|
||||
case 'BrtFRTArchID$': break;
|
||||
case 'BrtWorkBookPr15': break;
|
||||
case 'BrtFRTEnd': pass = false; break;
|
||||
case 'BrtEndBook': break;
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
|
||||
case 'BrtACBegin': break;
|
||||
case 'BrtACEnd': break;
|
||||
|
||||
case 'BrtFRTArchID$': break;
|
||||
default:
|
||||
if((R.n||"").indexOf("Begin") > 0){}
|
||||
else if((R.n||"").indexOf("End") > 0){}
|
||||
else if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
|
||||
}
|
||||
}, opts);
|
||||
|
||||
|
@ -51,10 +51,14 @@ function write_wb(wb, name/*:string*/, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
|
||||
}
|
||||
|
||||
function write_ws(data/*:Worksheet*/, name/*:string*/, opts, wb/*:Workbook*/, rels) {
|
||||
function write_ws(data/*:number*/, name/*:string*/, opts, wb/*:Workbook*/, rels) {
|
||||
return (name.slice(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb, rels);
|
||||
}
|
||||
|
||||
function write_cs(data/*:number*/, name/*:string*/, opts, wb/*:Workbook*/, rels) {
|
||||
return (name.slice(-4)===".bin" ? write_cs_bin : write_cs_xml)(data, opts, wb, rels);
|
||||
}
|
||||
|
||||
function write_sty(data, name/*:string*/, opts) {
|
||||
return (name.slice(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
|
||||
}
|
||||
|
@ -174,7 +174,8 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
if(str.substr(0,1000).indexOf("<html") >= 0) return parse_html(str, opts);
|
||||
var Rn;
|
||||
var state = [], tmp;
|
||||
var sheets = {}, sheetnames = [], cursheet = {}, sheetname = "";
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var sheets = {}, sheetnames = [], cursheet = (opts.dense ? [] : {}), sheetname = "";
|
||||
var table = {}, cell = ({}/*:any*/), row = {};
|
||||
var dtag = xlml_parsexmltag('<Data ss:Type="String">'), didx = 0;
|
||||
var c = 0, r = 0;
|
||||
@ -199,7 +200,12 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
case 'Cell':
|
||||
if(Rn[1]==='/'){
|
||||
if(comments.length > 0) cell.c = comments;
|
||||
if((!opts.sheetRows || opts.sheetRows > r) && cell.v !== undefined) cursheet[encode_col(c) + encode_row(r)] = cell;
|
||||
if((!opts.sheetRows || opts.sheetRows > r) && cell.v !== undefined) {
|
||||
if(opts.dense) {
|
||||
if(!cursheet[r]) cursheet[r] = [];
|
||||
cursheet[r][c] = cell;
|
||||
} else cursheet[encode_col(c) + encode_row(r)] = cell;
|
||||
}
|
||||
if(cell.HRef) {
|
||||
cell.l = {Target:cell.HRef, Tooltip:cell.HRefScreenTip};
|
||||
delete cell.HRef; delete cell.HRefScreenTip;
|
||||
@ -214,7 +220,12 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
/*:: if(!cc) cc = 0; if(!rr) rr = 0; */
|
||||
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'};
|
||||
if(cma > c || cmd > r) {
|
||||
if(opts.dense) {
|
||||
if(!cursheet[cmd]) cursheet[cmd] = [];
|
||||
cursheet[cmd][cma] = {t:'z'};
|
||||
} else cursheet[encode_col(cma) + encode_row(cmd)] = {t:'z'};
|
||||
}
|
||||
}
|
||||
}
|
||||
c = cc + 1;
|
||||
@ -258,7 +269,7 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
||||
state.push([Rn[3], false]);
|
||||
tmp = xlml_parsexmltag(Rn[0]);
|
||||
sheetname = unescapexml(tmp.Name);
|
||||
cursheet = {};
|
||||
cursheet = (opts.dense ? [] : {});
|
||||
mergecells = [];
|
||||
arrayf = [];
|
||||
rowinfo = [];
|
||||
@ -868,6 +879,7 @@ function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbo
|
||||
var p = col_obj_w(i, n);
|
||||
o.push(writextag("Column",null, {"ss:Index":i+1, "ss:Width":width2px(p.width)}));
|
||||
});
|
||||
var dense = Array.isArray(ws);
|
||||
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) {
|
||||
@ -882,8 +894,8 @@ function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbo
|
||||
}
|
||||
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));
|
||||
var ref = encode_cell(addr), cell = dense ? (ws[R]||[])[C] : ws[ref];
|
||||
row.push(write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr));
|
||||
}
|
||||
row.push("</Row>");
|
||||
if(row.length > 2) o.push(row.join(""));
|
||||
|
@ -82,7 +82,8 @@ function make_cell(val, ixfe, t)/*:any*/ {
|
||||
function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
var wb = ({opts:{}}/*:any*/);
|
||||
var Sheets = {};
|
||||
var out = {};
|
||||
if(DENSE != null) options.dense = DENSE;
|
||||
var out = (options.dense ? [] : {});
|
||||
var Directory = {};
|
||||
var found_sheet = false;
|
||||
var range/*:Range*/ = ({}/*:any*/);
|
||||
@ -141,7 +142,12 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
}
|
||||
}
|
||||
if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
|
||||
else out[last_cell] = line;
|
||||
else {
|
||||
if(options.dense) {
|
||||
if(!out[cell.r]) out[cell.r] = [];
|
||||
out[cell.r][cell.c] = line;
|
||||
} else out[last_cell] = line;
|
||||
}
|
||||
};
|
||||
var opts = ({
|
||||
enc: false, // encrypted
|
||||
@ -175,7 +181,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
/* explicit override for some broken writers */
|
||||
opts.codepage = 1200;
|
||||
set_cp(1200);
|
||||
|
||||
while(blob.l < blob.length - 1) {
|
||||
var s = blob.l;
|
||||
var RecordType = blob.read_shift(2);
|
||||
@ -273,7 +278,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
Workbook.Sheets.push(wsprops);
|
||||
}
|
||||
if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
|
||||
out = {};
|
||||
out = options.dense ? [] : {};
|
||||
} break;
|
||||
case 'BOF': {
|
||||
if(opts.biff !== 8){}
|
||||
@ -286,7 +291,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
else if(val.BIFFVer === 0x0007) opts.biff = 2;
|
||||
if(file_depth++) break;
|
||||
cell_valid = true;
|
||||
out = {};
|
||||
out = (options.dense ? [] : {});
|
||||
|
||||
if(opts.biff < 5) {
|
||||
if(cur_sheet === "") cur_sheet = "Sheet1";
|
||||
@ -308,7 +313,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
} break;
|
||||
|
||||
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
|
||||
if(out["!type"] == "chart" && out[encode_cell({c:val.c, r:val.r})]) ++val.c;
|
||||
if(out["!type"] == "chart") if(options.dense ? (out[val.r]||[])[val.c]: out[encode_cell({c:val.c, r:val.r})]) ++val.c;
|
||||
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
@ -341,7 +346,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
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 = ((options.dense ? (out[_fr]||[])[_fc]: 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);
|
||||
@ -364,11 +369,12 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'Array': {
|
||||
array_formulae.push(val);
|
||||
var _arraystart = encode_cell(val[0].s);
|
||||
if(options.cellFormula && out[_arraystart]) {
|
||||
cc = options.dense ? (out[val[0].s.r]||[])[val[0].s.c] : out[_arraystart];
|
||||
if(options.cellFormula && cc) {
|
||||
if(!last_formula) break; /* technically unreachable */
|
||||
if(!_arraystart || !out[_arraystart]) break;
|
||||
out[_arraystart].f = ""+stringify_formula(val[1], range, val[0], supbooks, opts);
|
||||
out[_arraystart].F = encode_range(val[0]);
|
||||
if(!_arraystart || !cc) break;
|
||||
cc.f = ""+stringify_formula(val[1], range, val[0], supbooks, opts);
|
||||
cc.F = encode_range(val[0]);
|
||||
}
|
||||
} break;
|
||||
case 'ShrFmla': {
|
||||
@ -378,7 +384,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
/* 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);
|
||||
cc = options.dense ? (out[last_formula.cell.r]||[])[last_formula.cell.c] : out[encode_cell(last_formula.cell)];
|
||||
(cc||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
|
||||
}
|
||||
} break;
|
||||
case 'LabelSst':
|
||||
@ -428,21 +435,23 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
|
||||
case 'HLink': {
|
||||
for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC)
|
||||
if(out[encode_cell({c:rngC,r:rngR})])
|
||||
out[encode_cell({c:rngC,r:rngR})].l = val[1];
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
|
||||
cc = options.dense ? (out[rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
if(cc) cc.l = val[1];
|
||||
}
|
||||
} break;
|
||||
case 'HLinkTooltip': {
|
||||
for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC)
|
||||
if(out[encode_cell({c:rngC,r:rngR})])
|
||||
out[encode_cell({c:rngC,r:rngR})].l.Tooltip = val[1];
|
||||
for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
|
||||
cc = options.dense ? (out[rngR]||[])[rngC] : out[encode_cell({c:rngC,r:rngR})];
|
||||
if(cc) cc.l.Tooltip = val[1];
|
||||
}
|
||||
} break;
|
||||
|
||||
/* Comments */
|
||||
case 'Note': {
|
||||
if(opts.biff <= 5 && opts.biff >= 2) break; /* TODO: BIFF5 */
|
||||
cc = out[encode_cell(val[0])];
|
||||
cc = options.dense ? (out[val[0].r]||[])[val[0].c] : out[encode_cell(val[0])];
|
||||
var noteobj = objects[val[2]];
|
||||
if(!cc) break;
|
||||
if(!cc.c) cc.c = [];
|
||||
|
@ -75,21 +75,25 @@ function write_ws_biff_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:nu
|
||||
}
|
||||
|
||||
function write_biff_ws(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
|
||||
var dense = Array.isArray(ws);
|
||||
var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
rr = encode_row(R);
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
if(R === range.s.r) cols[C] = encode_col(C);
|
||||
ref = cols[C] + rr;
|
||||
if(!ws[ref]) continue;
|
||||
var cell = dense ? ws[R][C] : ws[ref];
|
||||
if(!cell) continue;
|
||||
/* write cell */
|
||||
write_ws_biff_cell(ba, ws[ref], R, C, opts);
|
||||
write_ws_biff_cell(ba, cell, R, C, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Based on test files */
|
||||
function write_biff_buf(wb/*:Workbook*/, o/*:WriteOpts*/) {
|
||||
function write_biff_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
||||
var o = opts || {};
|
||||
if(DENSE != null) o.dense = DENSE;
|
||||
var ba = buf_array();
|
||||
var idx = 0;
|
||||
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* TODO: in browser attach to DOM; in node use an html parser */
|
||||
function parse_html(str/*:string*/, opts)/*:Workbook*/ {
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/);
|
||||
function parse_html(str/*:string*/, _opts)/*:Workbook*/ {
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var o/*:Workbook*/ = { SheetNames: ["Sheet1"], Sheets: {Sheet1:ws} };
|
||||
var i = str.indexOf("<table"), j = str.indexOf("</table");
|
||||
if(i == -1 || j == -1) throw new Error("Invalid HTML: missing <table> / </table> pair");
|
||||
@ -23,10 +25,16 @@ function parse_html(str/*:string*/, opts)/*:Workbook*/ {
|
||||
if(range.e.r < R) range.e.r = R;
|
||||
if(range.s.c > C) range.s.c = C;
|
||||
if(range.e.c < C) range.e.c = C;
|
||||
var coord/*:string*/ = encode_cell({r:R, c:C});
|
||||
/* TODO: value parsing */
|
||||
if(Number(m) == Number(m)) ws[coord] = {t:'n', v:+m};
|
||||
else ws[coord] = {t:'s', v:m};
|
||||
if(opts.dense) {
|
||||
if(!ws[R]) ws[R] = [];
|
||||
if(Number(m) == Number(m)) ws[R][C] = {t:'n', v:+m};
|
||||
else ws[R][C] = {t:'s', v:m};
|
||||
} else {
|
||||
var coord/*:string*/ = encode_cell({r:R, c:C});
|
||||
/* TODO: value parsing */
|
||||
if(Number(m) == Number(m)) ws[coord] = {t:'n', v:+m};
|
||||
else ws[coord] = {t:'s', v:m};
|
||||
}
|
||||
}
|
||||
++R; C = 0;
|
||||
}
|
||||
@ -34,8 +42,10 @@ function parse_html(str/*:string*/, opts)/*:Workbook*/ {
|
||||
return o;
|
||||
}
|
||||
|
||||
function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
var ws/*:Worksheet*/ = ({}/*:any*/);
|
||||
function parse_dom_table(table/*:HTMLElement*/, _opts/*:?any*/)/*:Worksheet*/ {
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var ws/*:Worksheet*/ = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var rows = table.getElementsByTagName('tr');
|
||||
var range = {s:{r:0,c:0},e:{r:rows.length - 1,c:0}};
|
||||
var merges = [], midx = 0;
|
||||
@ -54,7 +64,8 @@ function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ {
|
||||
if((RS = +elt.getAttribute("rowspan"))>0) merges.push({s:{r:R,c:C},e:{r:R + RS - 1, c:C + CS - 1}});
|
||||
var o = {t:'s', v:v};
|
||||
if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)};
|
||||
ws[encode_cell({c:C, r:R})] = o;
|
||||
if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = o; }
|
||||
else ws[encode_cell({c:C, r:R})] = o;
|
||||
if(range.e.c < C) range.e.c = C;
|
||||
C += CS;
|
||||
}
|
||||
|
@ -18,13 +18,15 @@ var parse_content_xml = (function() {
|
||||
|
||||
return function pcx(d/*:string*/, _opts)/*:Workbook*/ {
|
||||
var opts = _opts || {};
|
||||
if(DENSE != null) opts.dense = DENSE;
|
||||
var str = xlml_normalize(d);
|
||||
var state/*:Array<any>*/ = [], tmp;
|
||||
var tag/*:: = {}*/;
|
||||
var NFtag = {name:""}, NF = "", pidx = 0;
|
||||
var sheetag/*:: = {name:"", '名称':""}*/;
|
||||
var rowtag/*:: = {'行号':""}*/;
|
||||
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
|
||||
var Sheets = {}, SheetNames/*:Array<string>*/ = [];
|
||||
var ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/);
|
||||
var Rn, q/*:: = ({t:"", v:null, z:null, w:"",c:[]}:any)*/;
|
||||
var ctag = {value:""};
|
||||
var textp = "", textpidx = 0, textptag/*:: = {}*/;
|
||||
@ -52,7 +54,7 @@ var parse_content_xml = (function() {
|
||||
sheetag = parsexmltag(Rn[0], false);
|
||||
R = C = -1;
|
||||
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
|
||||
ws = {}; merges = [];
|
||||
ws = opts.dense ? ([]/*:any*/) : ({}/*:any*/); merges = [];
|
||||
}
|
||||
break;
|
||||
|
||||
@ -63,7 +65,10 @@ var parse_content_xml = (function() {
|
||||
C = -1; break;
|
||||
case 'covered-table-cell': // 9.1.5 <table:covered-table-cell>
|
||||
++C;
|
||||
if(opts.sheetStubs) ws[encode_cell({r:R,c:C})] = {t:'z'};
|
||||
if(opts.sheetStubs) {
|
||||
if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = {t:'z'}; }
|
||||
else ws[encode_cell({r:R,c:C})] = {t:'z'};
|
||||
}
|
||||
break; /* stub */
|
||||
case 'table-cell': case '数据':
|
||||
if(Rn[0].charAt(Rn[0].length-2) === '/') {
|
||||
@ -133,8 +138,14 @@ var parse_content_xml = (function() {
|
||||
if(textp) q.w = textp;
|
||||
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);
|
||||
if(opts.dense) {
|
||||
if(!ws[R]) ws[R] = [];
|
||||
ws[R][C] = q;
|
||||
while(--rept > 0) ws[R][++C] = dup(q);
|
||||
} else {
|
||||
ws[encode_cell({r:R,c:C})] = q;
|
||||
while(--rept > 0) ws[encode_cell({r:R,c:++C})] = dup(q);
|
||||
}
|
||||
if(range.e.c <= C) range.e.c = C;
|
||||
}
|
||||
} else { C += rept; rept = 0; }
|
||||
|
@ -10,6 +10,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
|
||||
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;
|
||||
var dense = Array.isArray(ws);
|
||||
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');
|
||||
@ -26,7 +27,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
|
||||
break;
|
||||
}
|
||||
if(skip) { o.push(covered_cell_xml); continue; }
|
||||
var ref = encode_cell({r:R, c:C}), cell = ws[ref];
|
||||
var ref = encode_cell({r:R, c:C}), cell = dense ? (ws[R]||[])[C]: ws[ref];
|
||||
var fmla = "";
|
||||
if(cell && cell.f) {
|
||||
fmla = ' table:formula="' + escapexml(csf_to_ods_formula(cell.f)) + '"';
|
||||
|
@ -115,14 +115,14 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
var sheetRels = ({}/*:any*/);
|
||||
var path, relsPath;
|
||||
|
||||
//if(!props.Worksheets) {
|
||||
{
|
||||
var wbsheets = wb.Sheets;
|
||||
props.Worksheets = wbsheets.length;
|
||||
props.SheetNames = [];
|
||||
for(var j = 0; j != wbsheets.length; ++j) {
|
||||
props.SheetNames[j] = wbsheets[j].name;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
var wbext = xlsb ? "bin" : "xml";
|
||||
var wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
|
||||
|
@ -14,9 +14,12 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
opts.Strings = /*::((*/[]/*:: :any):SST)*/; opts.Strings.Count = 0; opts.Strings.Unique = 0;
|
||||
var wbext = opts.bookType == "xlsb" ? "bin" : "xml";
|
||||
var vbafmt = opts.bookType == "xlsb" || opts.bookType == "xlsm";
|
||||
var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
|
||||
TODO:[], rels:[], xmlns: "" };
|
||||
var ct = ({
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[], macros:[],
|
||||
rels:[], strs:[], comments:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [], drawings: [],
|
||||
TODO:[], xmlns: "" }/*:any*/);
|
||||
fix_write_opts(opts = opts || {});
|
||||
/*:: if(!jszip) throw new Error("JSZip is not available"); */
|
||||
var zip = new jszip();
|
||||
@ -56,14 +59,24 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
add_rels(opts.rels, 1, f, RELS.WB);
|
||||
|
||||
for(rId=1;rId <= wb.SheetNames.length; ++rId) {
|
||||
f = "xl/worksheets/sheet" + rId + "." + wbext;
|
||||
var wsrels = {'!id':{}};
|
||||
zip.file(f, write_ws(rId-1, f, opts, wb, wsrels));
|
||||
|
||||
ct.sheets.push(f);
|
||||
add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]);
|
||||
|
||||
var ws = wb.Sheets[wb.SheetNames[rId-1]];
|
||||
var _type = (ws || {})["!type"] || "sheet";
|
||||
switch(_type) {
|
||||
case "chart": /*
|
||||
f = "xl/chartsheets/sheet" + rId + "." + wbext;
|
||||
zip.file(f, write_cs(rId-1, f, opts, wb, wsrels));
|
||||
ct.charts.push(f);
|
||||
add_rels(wsrels, -1, "chartsheets/sheet" + rId + "." + wbext, RELS.CS);
|
||||
break; */
|
||||
/* falls through */
|
||||
default:
|
||||
f = "xl/worksheets/sheet" + rId + "." + wbext;
|
||||
zip.file(f, write_ws(rId-1, f, opts, wb, wsrels));
|
||||
ct.sheets.push(f);
|
||||
add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]);
|
||||
}
|
||||
|
||||
if(ws) {
|
||||
var comments = ws['!comments'];
|
||||
if(comments && comments.length > 0) {
|
||||
@ -79,7 +92,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
delete ws['!legacy'];
|
||||
}
|
||||
|
||||
if(wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels)); // get_rels_path('')
|
||||
if(wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels));
|
||||
}
|
||||
|
||||
if(opts.Strings != null && opts.Strings.length > 0) {
|
||||
@ -111,7 +124,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
}
|
||||
|
||||
zip.file("[Content_Types].xml", write_ct(ct, opts));
|
||||
zip.file('_rels/.rels', write_rels(opts.rels)); // get_rels_path('')
|
||||
zip.file('xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels)); // get_rels_path("xl/workbook." + wbext)
|
||||
zip.file('_rels/.rels', write_rels(opts.rels));
|
||||
zip.file('xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
|
||||
return zip;
|
||||
}
|
||||
|
@ -94,9 +94,10 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/){
|
||||
var cols = new Array(r.e.c-r.s.c+1);
|
||||
var out = new Array(r.e.r-r.s.r-offset+1);
|
||||
var outi = 0;
|
||||
var dense = Array.isArray(sheet);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
cols[C] = encode_col(C);
|
||||
val = sheet[cols[C] + rr];
|
||||
val = dense ? (sheet[r.s.r] || [])[C] : sheet[cols[C] + rr];
|
||||
switch(header) {
|
||||
case 1: hdr[C] = C; break;
|
||||
case 2: hdr[C] = cols[C]; break;
|
||||
@ -120,7 +121,7 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/){
|
||||
else row.__rowNum__ = R;
|
||||
}
|
||||
for (C = r.s.c; C <= r.e.c; ++C) {
|
||||
val = sheet[cols[C] + rr];
|
||||
val = dense ? (sheet[R] || [])[C] : sheet[cols[C] + rr];
|
||||
if(val === undefined || val.t === undefined) {
|
||||
if(defval === undefined) continue;
|
||||
if(hdr[C] != null) { row[hdr[C]] = defval; isempty = false; }
|
||||
@ -161,13 +162,14 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
var row = "", rr = "", cols = [];
|
||||
var i = 0, cc = 0, val;
|
||||
var R = 0, C = 0;
|
||||
var dense = Array.isArray(sheet);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(R = r.s.r; R <= r.e.r; ++R) {
|
||||
var isempty = true;
|
||||
row = "";
|
||||
rr = encode_row(R);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
val = sheet[cols[C] + rr];
|
||||
val = dense ? (sheet[R]||[])[C]: sheet[cols[C] + rr];
|
||||
if(val == null) txt = "";
|
||||
else if(val.v != null) {
|
||||
isempty = false;
|
||||
@ -187,7 +189,7 @@ function sheet_to_csv(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
var make_csv = sheet_to_csv;
|
||||
|
||||
function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
|
||||
if(!opts) opts = {}; opts.FS = "\t"; opts.RS = "\n";
|
||||
var s = sheet_to_csv(sheet, opts);
|
||||
@ -202,12 +204,13 @@ function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {
|
||||
var r = safe_decode_range(sheet['!ref']), rr = "", cols = [], C;
|
||||
var cmds = new Array((r.e.r-r.s.r+1)*(r.e.c-r.s.c+1));
|
||||
var i = 0;
|
||||
var dense = Array.isArray(sheet);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
|
||||
for(var R = r.s.r; R <= r.e.r; ++R) {
|
||||
rr = encode_row(R);
|
||||
for(C = r.s.c; C <= r.e.c; ++C) {
|
||||
y = cols[C] + rr;
|
||||
x = sheet[y];
|
||||
x = dense ? (sheet[R]||[])[C] : sheet[y];
|
||||
val = "";
|
||||
if(x === undefined) continue;
|
||||
else if(x.F != null) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
<body>
|
||||
<b>JS-XLSX Live Demo</b><br />
|
||||
Output Format:
|
||||
<select name="format">
|
||||
<select name="format" onchange="setfmt()">
|
||||
<option value="csv" selected> CSV</option>
|
||||
<option value="json"> JSON</option>
|
||||
<option value="form"> FORMULAE</option>
|
||||
@ -186,7 +186,9 @@ function b64it() {
|
||||
process_wb(wb);
|
||||
}
|
||||
|
||||
var global_wb;
|
||||
function process_wb(wb) {
|
||||
global_wb = wb;
|
||||
var output = "";
|
||||
switch(get_radio_value("format")) {
|
||||
case "json":
|
||||
@ -202,6 +204,7 @@ function process_wb(wb) {
|
||||
else out.innerText = output;
|
||||
if(typeof console !== 'undefined') console.log("output", new Date());
|
||||
}
|
||||
function setfmt() {if(global_wb) process_wb(global_wb); }
|
||||
|
||||
var drop = document.getElementById('drop');
|
||||
function handleDrop(e) {
|
||||
|
207
test.js
207
test.js
@ -222,19 +222,30 @@ describe('should parse test files', function() {
|
||||
});
|
||||
});
|
||||
|
||||
function get_cell(ws/*:Worksheet*/, addr/*:string*/) {
|
||||
if(!Array.isArray(ws)) return ws[addr];
|
||||
var a = X.utils.decode_cell(addr);
|
||||
return (ws[a.r]||[])[a.c];
|
||||
}
|
||||
|
||||
function each_cell(ws, f) {
|
||||
if(Array.isArray(ws)) ws.forEach(function(row) { if(row) row.forEach(f); });
|
||||
else Object.keys(ws).forEach(function(addr) { if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return; f(ws[addr]); });
|
||||
}
|
||||
|
||||
/* comments_stress_test family */
|
||||
function check_comments(wb) {
|
||||
var ws0 = wb.Sheets.Sheet2;
|
||||
assert.equal(ws0.A1.c[0].a, 'Author');
|
||||
assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good');
|
||||
assert.equal(ws0.C1.c[0].a, 'Author');
|
||||
assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
|
||||
assert.equal(get_cell(ws0,"A1").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws0,"A1").c[0].t, 'Author:\nGod thinks this is good');
|
||||
assert.equal(get_cell(ws0,"C1").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws0,"C1").c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
|
||||
|
||||
var ws3 = wb.Sheets.Sheet4;
|
||||
assert.equal(ws3.B1.c[0].a, 'Author');
|
||||
assert.equal(ws3.B1.c[0].t, 'The next comment is empty');
|
||||
assert.equal(ws3.B2.c[0].a, 'Author');
|
||||
assert.equal(ws3.B2.c[0].t, '');
|
||||
assert.equal(get_cell(ws3,"B1").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws3,"B1").c[0].t, 'The next comment is empty');
|
||||
assert.equal(get_cell(ws3,"B2").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws3,"B2").c[0].t, '');
|
||||
}
|
||||
|
||||
describe('parse options', function() {
|
||||
@ -248,17 +259,15 @@ describe('parse options', function() {
|
||||
it('XLSX should generate HTML by default', function() {
|
||||
var wb = X.readFile(paths.cstxlsx);
|
||||
var ws = wb.Sheets.Sheet1;
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(html_cell_types.indexOf(ws[addr].t) === -1 || ws[addr].h);
|
||||
each_cell(ws, function(cell) {
|
||||
assert(html_cell_types.indexOf(cell.t) === -1 || cell.h);
|
||||
});
|
||||
});
|
||||
it('XLSX should not generate HTML when requested', function() {
|
||||
var wb = X.readFile(paths.cstxlsx, {cellHTML:false});
|
||||
var ws = wb.Sheets.Sheet1;
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(typeof ws[addr].h === 'undefined');
|
||||
each_cell(ws, function(cell) {
|
||||
assert(typeof cell.h === 'undefined');
|
||||
});
|
||||
});
|
||||
it('should generate formulae by default', function() {
|
||||
@ -267,9 +276,8 @@ describe('parse options', function() {
|
||||
var found = false;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
if(typeof ws[addr].f !== 'undefined') return (found = true);
|
||||
each_cell(ws, function(cell) {
|
||||
if(typeof cell.f !== 'undefined') return (found = true);
|
||||
});
|
||||
});
|
||||
assert(found);
|
||||
@ -280,98 +288,95 @@ describe('parse options', function() {
|
||||
var wb =X.readFile(p,{cellFormula:false});
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(typeof ws[addr].f === 'undefined');
|
||||
each_cell(ws, function(cell) {
|
||||
assert(typeof cell.f === 'undefined');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should not generate number formats by default', function() {
|
||||
[paths.nfxls, paths.nfxlsx].forEach(function(p) {
|
||||
[paths.nfxls, paths.nfxlsx, paths.nfxlsb].forEach(function(p) {
|
||||
var wb = X.readFile(p);
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(typeof ws[addr].z === 'undefined');
|
||||
each_cell(ws, function(cell) {
|
||||
assert(typeof cell.z === 'undefined');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should generate number formats when requested', function() {
|
||||
[paths.nfxls, paths.nfxlsx].forEach(function(p) {
|
||||
[paths.nfxls, paths.nfxlsx, paths.nfxlsb].forEach(function(p) {
|
||||
var wb = X.readFile(p, {cellNF: true});
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(ws[addr].t!== 'n' || typeof ws[addr].z !== 'undefined');
|
||||
each_cell(ws, function(cell) {
|
||||
assert(cell.t!== 'n' || typeof cell.z !== 'undefined');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should not generate cell styles by default', function() {
|
||||
[paths.cssxlsx, paths.cssxls, paths.cssxml].forEach(function(p) {
|
||||
var wb = X.readFile(p);
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(typeof ws[addr].s === 'undefined');
|
||||
[paths.cssxlsx, paths.cssxlsb, paths.cssxls, paths.cssxml].forEach(function(p) {
|
||||
var wb = X.readFile(p);
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
each_cell(ws, function(cell) {
|
||||
assert(typeof cell.s === 'undefined');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should generate cell styles when requested', function() {
|
||||
/* TODO: XLS / XLML */
|
||||
[paths.cssxlsx /*,paths.cssxls, paths.cssxml*/].forEach(function(p) {
|
||||
var wb = X.readFile(p, {cellStyles:true});
|
||||
var found = false;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
if(typeof ws[addr].s !== 'undefined') return (found = true);
|
||||
[paths.cssxlsx /*, paths.cssxlsb, paths.cssxls, paths.cssxml*/].forEach(function(p) {
|
||||
var wb = X.readFile(p, {cellStyles:true});
|
||||
var found = false;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
each_cell(ws, function(cell) {
|
||||
if(typeof cell.s !== 'undefined') return (found = true);
|
||||
});
|
||||
});
|
||||
});
|
||||
assert(found);
|
||||
assert(found);
|
||||
});
|
||||
});
|
||||
it('should not generate cell dates by default', function() {
|
||||
var wb = X.readFile(paths.dtxlsx);
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
assert(ws[addr].t !== 'd');
|
||||
[paths.dtxlsx, paths.dtxlsb, paths.dtxls, paths.dtxml].forEach(function(p) {
|
||||
var wb = X.readFile(p);
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
each_cell(ws, function(cell) {
|
||||
assert(cell.t !== 'd');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('XLSX should generate cell dates when requested', function() {
|
||||
var wb = X.readFile(paths.dtxlsx, {cellDates: true});
|
||||
var found = false;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
Object.keys(ws).forEach(function(addr) {
|
||||
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
|
||||
if(ws[addr].t === 'd') return (found = true);
|
||||
[paths.dtxlsx, paths.dtxlsb, paths.dtxls, paths.dtxml].forEach(function(p) {
|
||||
var wb = X.readFile(paths.dtxlsx, {cellDates: true});
|
||||
var found = false;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
var ws = wb.Sheets[s];
|
||||
each_cell(ws, function(cell) {
|
||||
if(cell.t === 'd') return (found = true);
|
||||
});
|
||||
});
|
||||
assert(found);
|
||||
});
|
||||
assert(found);
|
||||
});
|
||||
});
|
||||
describe('sheet', function() {
|
||||
it('should not generate sheet stubs by default', function() {
|
||||
[paths.mcxlsx, paths.mcxlsb, paths.mcods, paths.mcxls, paths.mcxml].forEach(function(p) {
|
||||
var wb = X.readFile(p);
|
||||
assert.throws(function() { return wb.Sheets.Merge.A2.v; });
|
||||
assert.throws(function() { return get_cell(wb.Sheets.Merge, "A2").v; });
|
||||
});
|
||||
});
|
||||
it('should generate sheet stubs when requested', function() {
|
||||
[paths.mcxlsx, paths.mcxlsb, paths.mcods, paths.mcxls, paths.mcxml].forEach(function(p) {
|
||||
var wb = X.readFile(p, {sheetStubs:true});
|
||||
assert(wb.Sheets.Merge.A2.t == 'z');
|
||||
assert(get_cell(wb.Sheets.Merge, "A2").t == 'z');
|
||||
});
|
||||
});
|
||||
it('should handle stub cells', function() {
|
||||
@ -384,10 +389,10 @@ describe('parse options', function() {
|
||||
});
|
||||
});
|
||||
function checkcells(wb, A46, B26, C16, D2) {
|
||||
assert((typeof wb.Sheets.Text.A46 !== 'undefined') == A46);
|
||||
assert((typeof wb.Sheets.Text.B26 !== 'undefined') == B26);
|
||||
assert((typeof wb.Sheets.Text.C16 !== 'undefined') == C16);
|
||||
assert((typeof wb.Sheets.Text.D2 !== 'undefined') == D2);
|
||||
assert((typeof get_cell(wb.Sheets.Text, "A46") !== 'undefined') == A46);
|
||||
assert((typeof get_cell(wb.Sheets.Text, "B26") !== 'undefined') == B26);
|
||||
assert((typeof get_cell(wb.Sheets.Text, "C16") !== 'undefined') == C16);
|
||||
assert((typeof get_cell(wb.Sheets.Text, "D2") !== 'undefined') == D2);
|
||||
}
|
||||
it('should read all cells by default', function() {
|
||||
[paths.fstxlsx, paths.fstxlsb, paths.fstods, paths.fstxls, paths.fstxml].forEach(function(p) {
|
||||
@ -604,7 +609,7 @@ var stykeys = [
|
||||
"bgColor.rgb"
|
||||
];
|
||||
function diffsty(ws, r1,r2) {
|
||||
var c1 = ws[r1].s, c2 = ws[r2].s;
|
||||
var c1 = get_cell(ws,r1).s, c2 = get_cell(ws,r2).s;
|
||||
stykeys.forEach(function(m) {
|
||||
var c = -1;
|
||||
if(styexc.indexOf(r1+"|"+r2+"|"+m) > -1) c = 1;
|
||||
@ -615,14 +620,14 @@ function diffsty(ws, r1,r2) {
|
||||
|
||||
function hlink(wb) {
|
||||
var ws = wb.Sheets.Sheet1;
|
||||
assert.equal(ws.A1.l.Target, "http://www.sheetjs.com");
|
||||
assert.equal(ws.A2.l.Target, "http://oss.sheetjs.com");
|
||||
assert.equal(ws.A3.l.Target, "http://oss.sheetjs.com#foo");
|
||||
assert.equal(ws.A4.l.Target, "mailto:dev@sheetjs.com");
|
||||
assert.equal(ws.A5.l.Target, "mailto:dev@sheetjs.com?subject=hyperlink");
|
||||
assert.equal(ws.A6.l.Target, "../../sheetjs/Documents/Test.xlsx");
|
||||
assert.equal(ws.A7.l.Target, "http://sheetjs.com");
|
||||
assert.equal(ws.A7.l.Tooltip, "foo bar baz");
|
||||
assert.equal(get_cell(ws, "A1").l.Target, "http://www.sheetjs.com");
|
||||
assert.equal(get_cell(ws, "A2").l.Target, "http://oss.sheetjs.com");
|
||||
assert.equal(get_cell(ws, "A3").l.Target, "http://oss.sheetjs.com#foo");
|
||||
assert.equal(get_cell(ws, "A4").l.Target, "mailto:dev@sheetjs.com");
|
||||
assert.equal(get_cell(ws, "A5").l.Target, "mailto:dev@sheetjs.com?subject=hyperlink");
|
||||
assert.equal(get_cell(ws, "A6").l.Target, "../../sheetjs/Documents/Test.xlsx");
|
||||
assert.equal(get_cell(ws, "A7").l.Target, "http://sheetjs.com");
|
||||
assert.equal(get_cell(ws, "A7").l.Tooltip, "foo bar baz");
|
||||
}
|
||||
|
||||
|
||||
@ -668,12 +673,12 @@ describe('parse features', function() {
|
||||
var wb4=X.readFile(paths.swcxml);
|
||||
|
||||
[wb1,wb2,wb3,wb4].map(function(wb) { return wb.Sheets[sheet]; }).forEach(function(ws, i) {
|
||||
assert.equal(ws.B1.c.length, 1,"must have 1 comment");
|
||||
assert.equal(ws.B1.c[0].a, "Yegor Kozlov","must have the same author");
|
||||
assert.equal(ws.B1.c[0].t, "Yegor Kozlov:\nfirst cell", "must have the concatenated texts");
|
||||
assert.equal(get_cell(ws, "B1").c.length, 1,"must have 1 comment");
|
||||
assert.equal(get_cell(ws, "B1").c[0].a, "Yegor Kozlov","must have the same author");
|
||||
assert.equal(get_cell(ws, "B1").c[0].t, "Yegor Kozlov:\nfirst cell", "must have the concatenated texts");
|
||||
if(i > 0) return;
|
||||
assert.equal(ws.B1.c[0].r, '<r><rPr><b/><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t>Yegor Kozlov:</t></r><r><rPr><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t xml:space="preserve">\r\nfirst cell</t></r>', "must have the rich text representation");
|
||||
assert.equal(ws.B1.c[0].h, '<span style="font-weight: bold;">Yegor Kozlov:</span><span style=""><br/>first cell</span>', "must have the html representation");
|
||||
assert.equal(get_cell(ws, "B1").c[0].r, '<r><rPr><b/><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t>Yegor Kozlov:</t></r><r><rPr><sz val="8"/><color indexed="81"/><rFont val="Tahoma"/></rPr><t xml:space="preserve">\r\nfirst cell</t></r>', "must have the rich text representation");
|
||||
assert.equal(get_cell(ws, "B1").c[0].h, '<span style="font-weight: bold;">Yegor Kozlov:</span><span style=""><br/>first cell</span>', "must have the html representation");
|
||||
});
|
||||
});
|
||||
[
|
||||
@ -686,10 +691,10 @@ describe('parse features', function() {
|
||||
var wb = X.readFile(m[1]);
|
||||
check_comments(wb);
|
||||
var ws0 = wb.Sheets.Sheet2;
|
||||
assert.equal(ws0.A1.c[0].a, 'Author');
|
||||
assert.equal(ws0.A1.c[0].t, 'Author:\nGod thinks this is good');
|
||||
assert.equal(ws0.C1.c[0].a, 'Author');
|
||||
assert.equal(ws0.C1.c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
|
||||
assert.equal(get_cell(ws0,"A1").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws0,"A1").c[0].t, 'Author:\nGod thinks this is good');
|
||||
assert.equal(get_cell(ws0,"C1").c[0].a, 'Author');
|
||||
assert.equal(get_cell(ws0,"C1").c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
|
||||
}); });
|
||||
});
|
||||
|
||||
@ -871,15 +876,15 @@ describe('parse features', function() {
|
||||
var wb, ws;
|
||||
wb = X.readFile(f[1]);
|
||||
ws = wb.Sheets[f[2]];
|
||||
assert.equal(ws[f[3]].w, f[4]);
|
||||
assert.equal(ws[f[3]].t, 'n');
|
||||
assert.equal(get_cell(ws, f[3]).w, f[4]);
|
||||
assert.equal(get_cell(ws, f[3]).t, 'n');
|
||||
}); });
|
||||
it('should generate date cells if cellDates is true', function() { fmts.forEach(function(f) {
|
||||
var wb, ws;
|
||||
wb = X.readFile(f[1], {cellDates:true});
|
||||
ws = wb.Sheets[f[2]];
|
||||
assert.equal(ws[f[3]].w, f[4]);
|
||||
assert.equal(ws[f[3]].t, 'd');
|
||||
assert.equal(get_cell(ws, f[3]).w, f[4]);
|
||||
assert.equal(get_cell(ws, f[3]).t, 'd');
|
||||
}); });
|
||||
});
|
||||
|
||||
@ -931,8 +936,8 @@ describe('parse features', function() {
|
||||
bgColor: { theme: 7, raw_rgb: '8064A2' } }
|
||||
];
|
||||
ranges.forEach(function(rng) {
|
||||
it('XLS | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return wsxls[x].s; }));});
|
||||
it('XLSX | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return wsxlsx[x].s; }));});
|
||||
it('XLS | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxls,x).s; }));});
|
||||
it('XLSX | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxlsx,x).s; }));});
|
||||
});
|
||||
it('different styles', function() {
|
||||
for(var i = 0; i != ranges.length-1; ++i) {
|
||||
@ -944,8 +949,8 @@ describe('parse features', function() {
|
||||
}
|
||||
});
|
||||
it('correct styles', function() {
|
||||
var stylesxls = ranges.map(function(r) { return rn2(r)[0]; }).map(function(r) { return wsxls[r].s; });
|
||||
var stylesxlsx = ranges.map(function(r) { return rn2(r)[0]; }).map(function(r) { return wsxlsx[r].s; });
|
||||
var stylesxls = ranges.map(function(r) { return rn2(r)[0]; }).map(function(r) { return get_cell(wsxls,r).s; });
|
||||
var stylesxlsx = ranges.map(function(r) { return rn2(r)[0]; }).map(function(r) { return get_cell(wsxlsx,r).s; });
|
||||
for(var i = 0; i != exp.length; ++i) {
|
||||
[
|
||||
"fgColor.theme","fgColor.raw_rgb",
|
||||
@ -1023,7 +1028,7 @@ describe('roundtrip features', function() {
|
||||
var wb1 = X.readFile(f, {cellNF: true, cellDates: di, WTF: opts.WTF});
|
||||
var _f = X.write(wb1, {type:'binary', cellDates:dj, WTF:opts.WTF});
|
||||
var wb2 = X.read(_f, {type:'binary', cellDates: dk, WTF: opts.WTF});
|
||||
var m = [wb1,wb2].map(function(x) { return x.Sheets[sheet][addr]; });
|
||||
var m = [wb1,wb2].map(function(x) { return get_cell(x.Sheets[sheet], addr); });
|
||||
assert.equal(m[0].w, m[1].w);
|
||||
|
||||
assert.equal(m[0].t, b);
|
||||
@ -1320,8 +1325,8 @@ describe('csv output', function() {
|
||||
it('should handle dateNF', function() {
|
||||
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,\n";
|
||||
var _ws = X.utils.aoa_to_sheet(data, {cellDates:true});
|
||||
delete _ws.C3.w;
|
||||
delete _ws.C3.z;
|
||||
delete get_cell(_ws,"C3").w;
|
||||
delete get_cell(_ws,"C3").z;
|
||||
assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"}));
|
||||
});
|
||||
it('should handle strip', function() {
|
||||
@ -1349,8 +1354,8 @@ describe('js -> file -> js', function() {
|
||||
if(typeof before != 'undefined') before(bef);
|
||||
else it('before', bef);
|
||||
function eqcell(wb1, wb2, s, a) {
|
||||
assert.equal(wb1.Sheets[s][a].v, wb2.Sheets[s][a].v);
|
||||
assert.equal(wb1.Sheets[s][a].t, wb2.Sheets[s][a].t);
|
||||
assert.equal(get_cell(wb1.Sheets[s], a).v, get_cell(wb2.Sheets[s], a).v);
|
||||
assert.equal(get_cell(wb1.Sheets[s], a).t, get_cell(wb2.Sheets[s], a).t);
|
||||
}
|
||||
ofmt.forEach(function(f) {
|
||||
it(f, function() {
|
||||
@ -1386,9 +1391,9 @@ describe('corner cases', function() {
|
||||
["baz", null, "q\"ux"]
|
||||
];
|
||||
var ws = X.utils.aoa_to_sheet(data);
|
||||
ws.A1.f = ""; ws.A1.w = "";
|
||||
delete ws.C3.w; delete ws.C3.z; ws.C3.XF = {ifmt:14};
|
||||
ws.A4.t = "e";
|
||||
get_cell(ws,"A1").f = ""; get_cell(ws,"A1").w = "";
|
||||
delete get_cell(ws,"C3").w; delete get_cell(ws,"C3").z; get_cell(ws,"C3").XF = {ifmt:14};
|
||||
get_cell(ws,"A4").t = "e";
|
||||
X.utils.get_formulae(ws);
|
||||
X.utils.make_csv(ws);
|
||||
X.utils.make_json(ws);
|
||||
@ -1400,7 +1405,7 @@ describe('corner cases', function() {
|
||||
X.write(wb, {type: "base64", bookType: 'xlsb'});
|
||||
X.write(wb, {type: "binary", bookType: 'ods'});
|
||||
X.write(wb, {type: "binary", bookType: 'biff2'});
|
||||
ws.A2.t = "f";
|
||||
get_cell(ws,"A2").t = "f";
|
||||
assert.throws(function() { X.utils.make_json(ws); });
|
||||
});
|
||||
it('SSF', function() {
|
||||
|
758
xlsx.flow.js
758
xlsx.flow.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user