2017-05-13 18:21:22 +00:00
|
|
|
function get_sheet_type(n/*:string*/)/*:string*/ {
|
2017-03-28 04:41:01 +00:00
|
|
|
if(RELS.WS.indexOf(n) > -1) return "sheet";
|
|
|
|
if(RELS.CS && n == RELS.CS) return "chart";
|
|
|
|
if(RELS.DS && n == RELS.DS) return "dialog";
|
|
|
|
if(RELS.MS && n == RELS.MS) return "macro";
|
2017-09-30 06:18:11 +00:00
|
|
|
return (n && n.length) ? n : "sheet";
|
2017-03-28 04:41:01 +00:00
|
|
|
}
|
2014-06-05 07:06:20 +00:00
|
|
|
function safe_parse_wbrels(wbrels, sheets) {
|
|
|
|
if(!wbrels) return 0;
|
|
|
|
try {
|
2017-03-28 04:41:01 +00:00
|
|
|
wbrels = sheets.map(function pwbr(w) { if(!w.id) w.id = w.strRelID; return [w.name, wbrels['!id'][w.id].Target, get_sheet_type(wbrels['!id'][w.id].Type)]; });
|
2014-06-05 07:06:20 +00:00
|
|
|
} catch(e) { return null; }
|
|
|
|
return !wbrels || wbrels.length === 0 ? null : wbrels;
|
|
|
|
}
|
|
|
|
|
2017-11-20 01:51:14 +00:00
|
|
|
function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, idx/*:number*/, sheetRels, sheets, stype/*:string*/, opts, wb, themes, styles) {
|
2014-06-05 07:06:20 +00:00
|
|
|
try {
|
2017-02-10 19:23:01 +00:00
|
|
|
sheetRels[sheet]=parse_rels(getzipstr(zip, relsPath, true), path);
|
2017-03-27 21:35:15 +00:00
|
|
|
var data = getzipdata(zip, path);
|
2018-06-22 21:40:01 +00:00
|
|
|
var _ws;
|
2017-03-27 21:35:15 +00:00
|
|
|
switch(stype) {
|
2018-06-22 21:40:01 +00:00
|
|
|
case 'sheet': _ws = parse_ws(data, path, idx, opts, sheetRels[sheet], wb, themes, styles); break;
|
|
|
|
case 'chart': _ws = parse_cs(data, path, idx, opts, sheetRels[sheet], wb, themes, styles);
|
|
|
|
if(!_ws || !_ws['!chart']) break;
|
|
|
|
var dfile = resolve_path(_ws['!chart'].Target, path);
|
2017-03-27 21:35:15 +00:00
|
|
|
var drelsp = get_rels_path(dfile);
|
2017-11-20 01:51:14 +00:00
|
|
|
var draw = parse_drawing(getzipstr(zip, dfile, true), parse_rels(getzipstr(zip, drelsp, true), dfile));
|
2017-03-27 21:35:15 +00:00
|
|
|
var chartp = resolve_path(draw, dfile);
|
|
|
|
var crelsp = get_rels_path(chartp);
|
2018-06-22 21:40:01 +00:00
|
|
|
_ws = parse_chart(getzipstr(zip, chartp, true), chartp, opts, parse_rels(getzipstr(zip, crelsp, true), chartp), wb, _ws);
|
2017-03-27 21:35:15 +00:00
|
|
|
break;
|
2018-06-22 21:40:01 +00:00
|
|
|
case 'macro': _ws = parse_ms(data, path, idx, opts, sheetRels[sheet], wb, themes, styles); break;
|
|
|
|
case 'dialog': _ws = parse_ds(data, path, idx, opts, sheetRels[sheet], wb, themes, styles); break;
|
2019-11-01 03:09:14 +00:00
|
|
|
default: throw new Error("Unrecognized sheet type " + stype);
|
2017-03-27 21:35:15 +00:00
|
|
|
}
|
2018-06-22 21:40:01 +00:00
|
|
|
sheets[sheet] = _ws;
|
2019-11-01 03:09:14 +00:00
|
|
|
|
|
|
|
/* scan rels for comments */
|
|
|
|
var comments = [];
|
|
|
|
if(sheetRels && sheetRels[sheet]) keys(sheetRels[sheet]).forEach(function(n) {
|
|
|
|
if(sheetRels[sheet][n].Type == RELS.CMNT) {
|
|
|
|
var dfile = resolve_path(sheetRels[sheet][n].Target, path);
|
|
|
|
comments = parse_cmnt(getzipdata(zip, dfile, true), dfile, opts);
|
|
|
|
if(!comments || !comments.length) return;
|
|
|
|
sheet_insert_comments(_ws, comments);
|
|
|
|
}
|
|
|
|
});
|
2014-06-05 07:06:20 +00:00
|
|
|
} catch(e) { if(opts.WTF) throw e; }
|
|
|
|
}
|
|
|
|
|
2017-08-19 23:06:34 +00:00
|
|
|
function strip_front_slash(x/*:string*/)/*:string*/ { return x.charAt(0) == '/' ? x.slice(1) : x; }
|
|
|
|
|
2017-02-10 19:23:01 +00:00
|
|
|
function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
2014-05-16 00:33:34 +00:00
|
|
|
make_ssf(SSF);
|
2014-02-07 10:53:40 +00:00
|
|
|
opts = opts || {};
|
2014-05-16 00:33:34 +00:00
|
|
|
fix_read_opts(opts);
|
2014-10-10 02:22:38 +00:00
|
|
|
|
|
|
|
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
|
|
|
|
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
|
2017-02-22 06:57:59 +00:00
|
|
|
/* UOC */
|
|
|
|
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
|
2017-10-17 00:14:32 +00:00
|
|
|
/* Numbers */
|
|
|
|
if(safegetzipfile(zip, 'Index/Document.iwa')) throw new Error('Unsupported NUMBERS file');
|
2014-10-10 02:22:38 +00:00
|
|
|
|
2018-02-28 10:41:49 +00:00
|
|
|
var entries = zipentries(zip);
|
2018-01-23 09:07:51 +00:00
|
|
|
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')/*:?any*/));
|
2014-01-28 16:38:02 +00:00
|
|
|
var xlsb = false;
|
2014-04-03 22:51:54 +00:00
|
|
|
var sheets, binname;
|
2014-01-28 16:38:02 +00:00
|
|
|
if(dir.workbooks.length === 0) {
|
2014-04-03 22:51:54 +00:00
|
|
|
binname = "xl/workbook.xml";
|
|
|
|
if(getzipdata(zip,binname, true)) dir.workbooks.push(binname);
|
|
|
|
}
|
|
|
|
if(dir.workbooks.length === 0) {
|
|
|
|
binname = "xl/workbook.bin";
|
2017-07-05 22:27:54 +00:00
|
|
|
if(!getzipdata(zip,binname,true)) throw new Error("Could not find workbook");
|
2014-01-28 16:38:02 +00:00
|
|
|
dir.workbooks.push(binname);
|
|
|
|
xlsb = true;
|
|
|
|
}
|
2017-02-22 06:57:59 +00:00
|
|
|
if(dir.workbooks[0].slice(-3) == "bin") xlsb = true;
|
2014-01-28 16:38:02 +00:00
|
|
|
|
2017-03-18 23:25:50 +00:00
|
|
|
var themes = ({}/*:any*/);
|
|
|
|
var styles = ({}/*:any*/);
|
2014-02-14 06:25:46 +00:00
|
|
|
if(!opts.bookSheets && !opts.bookProps) {
|
2014-05-16 00:33:34 +00:00
|
|
|
strs = [];
|
2018-05-05 06:34:37 +00:00
|
|
|
if(dir.sst) try { strs=parse_sst(getzipdata(zip, strip_front_slash(dir.sst)), dir.sst, opts); } catch(e) { if(opts.WTF) throw e; }
|
2014-02-13 06:22:42 +00:00
|
|
|
|
2017-03-12 18:02:43 +00:00
|
|
|
if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipstr(zip, dir.themes[0].replace(/^\//,''), true)||"",dir.themes[0], opts);
|
2017-03-19 06:50:30 +00:00
|
|
|
|
2017-08-19 23:06:34 +00:00
|
|
|
if(dir.style) styles = parse_sty(getzipdata(zip, strip_front_slash(dir.style)), dir.style, themes, opts);
|
2014-02-13 06:22:42 +00:00
|
|
|
}
|
|
|
|
|
2018-01-23 09:07:51 +00:00
|
|
|
/*var externbooks = */dir.links.map(function(link) {
|
2019-11-01 03:09:14 +00:00
|
|
|
try {
|
|
|
|
var rels = parse_rels(getzipstr(zip, get_rels_path(strip_front_slash(link))), link);
|
|
|
|
return parse_xlink(getzipdata(zip, strip_front_slash(link)), rels, link, opts);
|
|
|
|
} catch(e) {}
|
2017-08-19 23:06:34 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
var wb = parse_wb(getzipdata(zip, strip_front_slash(dir.workbooks[0])), dir.workbooks[0], opts);
|
2014-01-28 16:38:02 +00:00
|
|
|
|
2014-02-04 00:00:44 +00:00
|
|
|
var props = {}, propdata = "";
|
2014-05-16 00:33:34 +00:00
|
|
|
|
2017-08-19 23:06:34 +00:00
|
|
|
if(dir.coreprops.length) {
|
2017-09-30 06:18:11 +00:00
|
|
|
propdata = getzipdata(zip, strip_front_slash(dir.coreprops[0]), true);
|
2014-05-16 00:33:34 +00:00
|
|
|
if(propdata) props = parse_core_props(propdata);
|
|
|
|
if(dir.extprops.length !== 0) {
|
2017-09-30 06:18:11 +00:00
|
|
|
propdata = getzipdata(zip, strip_front_slash(dir.extprops[0]), true);
|
2017-08-03 15:51:16 +00:00
|
|
|
if(propdata) parse_ext_props(propdata, props, opts);
|
2014-05-16 00:33:34 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-14 06:25:46 +00:00
|
|
|
|
2014-02-14 03:39:03 +00:00
|
|
|
var custprops = {};
|
2014-02-14 06:25:46 +00:00
|
|
|
if(!opts.bookSheets || opts.bookProps) {
|
2014-02-15 05:08:18 +00:00
|
|
|
if (dir.custprops.length !== 0) {
|
2017-08-19 23:06:34 +00:00
|
|
|
propdata = getzipstr(zip, strip_front_slash(dir.custprops[0]), true);
|
2014-05-16 00:33:34 +00:00
|
|
|
if(propdata) custprops = parse_cust_props(propdata, opts);
|
2014-02-15 05:08:18 +00:00
|
|
|
}
|
2014-02-14 03:39:03 +00:00
|
|
|
}
|
2014-02-13 06:22:42 +00:00
|
|
|
|
2017-03-12 18:02:43 +00:00
|
|
|
var out = ({}/*:any*/);
|
2014-02-14 06:25:46 +00:00
|
|
|
if(opts.bookSheets || opts.bookProps) {
|
2017-03-27 21:35:15 +00:00
|
|
|
if(wb.Sheets) sheets = wb.Sheets.map(function pluck(x){ return x.name; });
|
|
|
|
else if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
|
2014-02-14 06:25:46 +00:00
|
|
|
if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
|
2017-03-12 18:02:43 +00:00
|
|
|
if(opts.bookSheets && typeof sheets !== 'undefined') out.SheetNames = sheets;
|
2014-02-14 06:25:46 +00:00
|
|
|
if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
|
2014-02-13 06:22:42 +00:00
|
|
|
}
|
2014-02-15 05:08:18 +00:00
|
|
|
sheets = {};
|
2014-02-13 06:22:42 +00:00
|
|
|
|
2014-01-28 16:38:02 +00:00
|
|
|
var deps = {};
|
2017-08-19 23:06:34 +00:00
|
|
|
if(opts.bookDeps && dir.calcchain) deps=parse_cc(getzipdata(zip, strip_front_slash(dir.calcchain)),dir.calcchain,opts);
|
2014-02-15 05:08:18 +00:00
|
|
|
|
|
|
|
var i=0;
|
2017-02-10 19:23:01 +00:00
|
|
|
var sheetRels = ({}/*:any*/);
|
2014-01-29 06:00:09 +00:00
|
|
|
var path, relsPath;
|
2017-03-31 18:46:42 +00:00
|
|
|
|
2017-04-08 06:55:35 +00:00
|
|
|
{
|
2014-01-28 16:38:02 +00:00
|
|
|
var wbsheets = wb.Sheets;
|
|
|
|
props.Worksheets = wbsheets.length;
|
|
|
|
props.SheetNames = [];
|
|
|
|
for(var j = 0; j != wbsheets.length; ++j) {
|
|
|
|
props.SheetNames[j] = wbsheets[j].name;
|
|
|
|
}
|
2017-04-08 06:55:35 +00:00
|
|
|
}
|
2014-05-22 12:16:51 +00:00
|
|
|
|
2014-05-29 22:30:03 +00:00
|
|
|
var wbext = xlsb ? "bin" : "xml";
|
2018-04-13 04:48:21 +00:00
|
|
|
var wbrelsi = dir.workbooks[0].lastIndexOf("/");
|
|
|
|
var wbrelsfile = (dir.workbooks[0].slice(0, wbrelsi+1) + "_rels/" + dir.workbooks[0].slice(wbrelsi+1) + ".rels").replace(/^\//,"");
|
|
|
|
if(!safegetzipfile(zip, wbrelsfile)) wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
|
2017-02-10 19:23:01 +00:00
|
|
|
var wbrels = parse_rels(getzipstr(zip, wbrelsfile, true), wbrelsfile);
|
2014-06-05 07:06:20 +00:00
|
|
|
if(wbrels) wbrels = safe_parse_wbrels(wbrels, wb.Sheets);
|
2018-04-13 04:48:21 +00:00
|
|
|
|
2014-05-22 12:16:51 +00:00
|
|
|
/* Numbers iOS hack */
|
2014-02-21 16:41:37 +00:00
|
|
|
var nmode = (getzipdata(zip,"xl/worksheets/sheet.xml",true))?1:0;
|
|
|
|
for(i = 0; i != props.Worksheets; ++i) {
|
2017-03-27 21:35:15 +00:00
|
|
|
var stype = "sheet";
|
|
|
|
if(wbrels && wbrels[i]) {
|
|
|
|
path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
|
2018-04-13 04:48:21 +00:00
|
|
|
if(!safegetzipfile(zip, path)) path = wbrels[i][1];
|
|
|
|
if(!safegetzipfile(zip, path)) path = wbrelsfile.replace(/_rels\/.*$/,"") + wbrels[i][1];
|
2017-03-27 21:35:15 +00:00
|
|
|
stype = wbrels[i][2];
|
|
|
|
} else {
|
2014-06-05 07:06:20 +00:00
|
|
|
path = 'xl/worksheets/sheet'+(i+1-nmode)+"." + wbext;
|
|
|
|
path = path.replace(/sheet0\./,"sheet.");
|
|
|
|
}
|
|
|
|
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
|
2017-11-20 01:51:14 +00:00
|
|
|
safe_parse_sheet(zip, path, relsPath, props.SheetNames[i], i, sheetRels, sheets, stype, opts, wb, themes, styles);
|
2014-01-28 16:38:02 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 19:23:01 +00:00
|
|
|
out = ({
|
2014-01-28 16:38:02 +00:00
|
|
|
Directory: dir,
|
|
|
|
Workbook: wb,
|
|
|
|
Props: props,
|
2014-02-14 03:39:03 +00:00
|
|
|
Custprops: custprops,
|
2014-01-28 16:38:02 +00:00
|
|
|
Deps: deps,
|
|
|
|
Sheets: sheets,
|
|
|
|
SheetNames: props.SheetNames,
|
|
|
|
Strings: strs,
|
|
|
|
Styles: styles,
|
2014-05-29 06:18:23 +00:00
|
|
|
Themes: themes,
|
2014-05-16 00:33:34 +00:00
|
|
|
SSF: SSF.get_table()
|
2017-02-10 19:23:01 +00:00
|
|
|
}/*:any*/);
|
2014-02-17 08:44:22 +00:00
|
|
|
if(opts.bookFiles) {
|
2014-05-16 00:33:34 +00:00
|
|
|
out.keys = entries;
|
2014-02-21 16:41:37 +00:00
|
|
|
out.files = zip.files;
|
2014-02-17 08:44:22 +00:00
|
|
|
}
|
2014-04-03 22:51:54 +00:00
|
|
|
if(opts.bookVBA) {
|
2017-08-19 23:06:34 +00:00
|
|
|
if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,strip_front_slash(dir.vba[0]),true);
|
2018-02-04 23:58:00 +00:00
|
|
|
else if(dir.defaults && dir.defaults.bin === CT_VBA) out.vbaraw = getzipdata(zip, 'xl/vbaProject.bin',true);
|
2014-04-03 22:51:54 +00:00
|
|
|
}
|
2014-02-17 08:44:22 +00:00
|
|
|
return out;
|
2014-01-28 16:38:02 +00:00
|
|
|
}
|
2017-04-13 01:29:38 +00:00
|
|
|
|
2017-12-01 05:48:10 +00:00
|
|
|
/* [MS-OFFCRYPTO] 2.1.1 */
|
|
|
|
function parse_xlsxcfb(cfb, _opts/*:?ParseOpts*/)/*:Workbook*/ {
|
|
|
|
var opts = _opts || {};
|
2018-06-22 21:40:01 +00:00
|
|
|
var f = 'Workbook', data = CFB.find(cfb, f);
|
|
|
|
try {
|
|
|
|
f = '/!DataSpaces/Version';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
2018-01-23 09:07:51 +00:00
|
|
|
/*var version = */parse_DataSpaceVersionInfo(data.content);
|
2017-04-13 01:29:38 +00:00
|
|
|
|
|
|
|
/* 2.3.4.1 */
|
2017-12-01 05:48:10 +00:00
|
|
|
f = '/!DataSpaces/DataSpaceMap';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
2017-04-13 01:29:38 +00:00
|
|
|
var dsm = parse_DataSpaceMap(data.content);
|
2017-10-17 00:14:32 +00:00
|
|
|
if(dsm.length !== 1 || dsm[0].comps.length !== 1 || dsm[0].comps[0].t !== 0 || dsm[0].name !== "StrongEncryptionDataSpace" || dsm[0].comps[0].v !== "EncryptedPackage")
|
2017-04-13 01:29:38 +00:00
|
|
|
throw new Error("ECMA-376 Encrypted file bad " + f);
|
|
|
|
|
2017-12-01 05:48:10 +00:00
|
|
|
/* 2.3.4.2 */
|
|
|
|
f = '/!DataSpaces/DataSpaceInfo/StrongEncryptionDataSpace';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
2017-04-13 01:29:38 +00:00
|
|
|
var seds = parse_DataSpaceDefinition(data.content);
|
|
|
|
if(seds.length != 1 || seds[0] != "StrongEncryptionTransform")
|
|
|
|
throw new Error("ECMA-376 Encrypted file bad " + f);
|
|
|
|
|
|
|
|
/* 2.3.4.3 */
|
2017-12-01 05:48:10 +00:00
|
|
|
f = '/!DataSpaces/TransformInfo/StrongEncryptionTransform/!Primary';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
2018-01-23 09:07:51 +00:00
|
|
|
/*var hdr = */parse_Primary(data.content);
|
2018-06-22 21:40:01 +00:00
|
|
|
} catch(e) {}
|
2017-04-13 01:29:38 +00:00
|
|
|
|
2017-12-01 05:48:10 +00:00
|
|
|
f = '/EncryptionInfo';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
2017-04-13 01:29:38 +00:00
|
|
|
var einfo = parse_EncryptionInfo(data.content);
|
|
|
|
|
2017-12-01 05:48:10 +00:00
|
|
|
/* 2.3.4.4 */
|
|
|
|
f = '/EncryptedPackage';
|
|
|
|
data = CFB.find(cfb, f); if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
|
|
|
|
|
2018-01-23 09:07:51 +00:00
|
|
|
/*global decrypt_agile */
|
2017-12-01 05:48:10 +00:00
|
|
|
/*:: declare var decrypt_agile:any; */
|
|
|
|
if(einfo[0] == 0x04 && typeof decrypt_agile !== 'undefined') return decrypt_agile(einfo[1], data.content, opts.password || "", opts);
|
2018-01-23 09:07:51 +00:00
|
|
|
/*global decrypt_std76 */
|
2017-12-15 01:18:40 +00:00
|
|
|
/*:: declare var decrypt_std76:any; */
|
|
|
|
if(einfo[0] == 0x02 && typeof decrypt_std76 !== 'undefined') return decrypt_std76(einfo[1], data.content, opts.password || "", opts);
|
2017-04-13 01:29:38 +00:00
|
|
|
throw new Error("File is password-protected");
|
|
|
|
}
|
|
|
|
|