forked from sheetjs/sheetjs
Defined Names
- XLSX read/write defined names - XLSB/XLS/XLML read defined names Issues: - fixes #83 h/t @developergdd - fixes #6 , fixes #599
This commit is contained in:
parent
5187bc0b63
commit
0189bc23ca
22
README.md
22
README.md
@ -45,6 +45,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
|
|||||||
+ [Chartsheet Object](#chartsheet-object)
|
+ [Chartsheet Object](#chartsheet-object)
|
||||||
* [Workbook Object](#workbook-object)
|
* [Workbook Object](#workbook-object)
|
||||||
+ [Workbook File Properties](#workbook-file-properties)
|
+ [Workbook File Properties](#workbook-file-properties)
|
||||||
|
* [Workbook-Level Attributes](#workbook-level-attributes)
|
||||||
|
+ [Defined Names](#defined-names)
|
||||||
* [Document Features](#document-features)
|
* [Document Features](#document-features)
|
||||||
+ [Formulae](#formulae)
|
+ [Formulae](#formulae)
|
||||||
+ [Column Properties](#column-properties)
|
+ [Column Properties](#column-properties)
|
||||||
@ -669,6 +671,26 @@ Writers will process the `Props` key of the options object:
|
|||||||
/* force the Author to be "SheetJS" */
|
/* force the Author to be "SheetJS" */
|
||||||
XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Workbook-Level Attributes
|
||||||
|
|
||||||
|
`wb.Workbook` stores workbook level attributes.
|
||||||
|
|
||||||
|
#### Defined Names
|
||||||
|
|
||||||
|
`wb.Workbook.Names` is an array of defined name objects which have the keys:
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|:----------|:-----------------------------------------------------------------|
|
||||||
|
| `Sheet` | Name scope. Sheet Index (0 = first sheet) or `null` (Workbook) |
|
||||||
|
| `Name` | Case-sensitive name. Standard rules apply ** |
|
||||||
|
| `Ref` | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`) |
|
||||||
|
| `Comment` | Comment (only applicable for XLS/XLSX/XLSB) |
|
||||||
|
|
||||||
|
Excel allows two sheet-scoped defined names to share the same name. However, a
|
||||||
|
sheet-scoped name cannot collide with a workbook-scope name. Workbook writers
|
||||||
|
may not enforce this constraint.
|
||||||
|
|
||||||
### Document Features
|
### Document Features
|
||||||
|
|
||||||
Even for basic features like date storage, the official Excel formats store the
|
Even for basic features like date storage, the official Excel formats store the
|
||||||
|
@ -39,7 +39,7 @@ function xlml_write_docprops(Props, opts) {
|
|||||||
case 'date': m = new Date(m).toISOString(); break;
|
case 'date': m = new Date(m).toISOString(); break;
|
||||||
}
|
}
|
||||||
if(typeof m == 'number') m = String(m);
|
if(typeof m == 'number') m = String(m);
|
||||||
else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
|
else if(m === true || m === false) { m = m ? "1" : "0"; }
|
||||||
else if(m instanceof Date) m = new Date(m).toISOString();
|
else if(m instanceof Date) m = new Date(m).toISOString();
|
||||||
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
||||||
});
|
});
|
||||||
|
@ -278,13 +278,6 @@ function parslurp(blob, length, cb) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parslurp2(blob, length, cb) {
|
|
||||||
var arr = [], target = blob.l + length, len = blob.read_shift(2);
|
|
||||||
while(len-- !== 0) arr.push(cb(blob, target - blob.l));
|
|
||||||
if(target !== blob.l) throw new Error("Slurp error");
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
||||||
|
|
||||||
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
||||||
|
@ -442,6 +442,7 @@ function parse_ExternName(blob, length, opts) {
|
|||||||
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
||||||
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
||||||
o.body = body || blob.read_shift(length-2);
|
o.body = body || blob.read_shift(length-2);
|
||||||
|
if(typeof body === "string") o.Name = body;
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,13 +473,21 @@ function parse_Lbl(blob, length, opts) {
|
|||||||
/* 2.4.106 TODO: verify supbook manipulation */
|
/* 2.4.106 TODO: verify supbook manipulation */
|
||||||
function parse_ExternSheet(blob, length, opts) {
|
function parse_ExternSheet(blob, length, opts) {
|
||||||
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
||||||
var o = parslurp2(blob,length,parse_XTI);
|
var o = [], target = blob.l + length, len = blob.read_shift(2);
|
||||||
|
while(len-- !== 0) o.push(parse_XTI(blob, 6));
|
||||||
|
// [iSupBook, itabFirst, itabLast];
|
||||||
var oo = [];
|
var oo = [];
|
||||||
if(opts.sbcch === 0x0401) {
|
return o;
|
||||||
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
|
}
|
||||||
return oo;
|
|
||||||
}
|
/* 2.4.176 TODO: check older biff */
|
||||||
else return o;
|
function parse_NameCmt(blob, length, opts) {
|
||||||
|
if(opts.biff < 8) { blob.l += length; return; }
|
||||||
|
var cchName = blob.read_shift(2);
|
||||||
|
var cchComment = blob.read_shift(2);
|
||||||
|
var name = parse_XLUnicodeStringNoCch(blob, cchName, opts);
|
||||||
|
var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts);
|
||||||
|
return [name, comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2.4.260 */
|
/* 2.4.260 */
|
||||||
@ -894,7 +903,6 @@ var parse_TableStyles = parsenoop;
|
|||||||
var parse_TableStyle = parsenoop;
|
var parse_TableStyle = parsenoop;
|
||||||
var parse_TableStyleElement = parsenoop;
|
var parse_TableStyleElement = parsenoop;
|
||||||
var parse_NamePublish = parsenoop;
|
var parse_NamePublish = parsenoop;
|
||||||
var parse_NameCmt = parsenoop;
|
|
||||||
var parse_SortData = parsenoop;
|
var parse_SortData = parsenoop;
|
||||||
var parse_GUIDTypeLib = parsenoop;
|
var parse_GUIDTypeLib = parsenoop;
|
||||||
var parse_FnGrp12 = parsenoop;
|
var parse_FnGrp12 = parsenoop;
|
||||||
|
@ -769,13 +769,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
break;
|
break;
|
||||||
/* 2.5.198.88 */
|
/* 2.5.198.88 */
|
||||||
case 'PtgRefN':
|
case 'PtgRefN':
|
||||||
type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts);
|
type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1];
|
||||||
stack.push(encode_cell_xls(c));
|
stack.push(encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
case 'PtgRef3d': // TODO: lots of stuff
|
case 'PtgRef3d': // TODO: lots of stuff
|
||||||
type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts);
|
type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts);
|
||||||
sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**");
|
sname = supbooks.SheetNames[ixti];
|
||||||
stack.push(sname + "!" + encode_cell(c));
|
stack.push(sname + "!" + encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.62 */
|
/* 2.5.198.62 */
|
||||||
@ -831,7 +831,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
case 'PtgName':
|
case 'PtgName':
|
||||||
/* f[1] = type, 0, nameindex */
|
/* f[1] = type, 0, nameindex */
|
||||||
nameidx = f[1][2];
|
nameidx = f[1][2];
|
||||||
var lbl = supbooks[0][nameidx];
|
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
|
||||||
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
||||||
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
||||||
stack.push(name);
|
stack.push(name);
|
||||||
@ -843,15 +843,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook;
|
var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook;
|
||||||
/* TODO: Properly handle missing values */
|
/* TODO: Properly handle missing values */
|
||||||
//console.log(bookidx, supbooks);
|
//console.log(bookidx, supbooks);
|
||||||
if(opts.biff == 5) {
|
if(opts.biff <= 5) {
|
||||||
if(bookidx < 0) bookidx = -bookidx;
|
if(bookidx < 0) bookidx = -bookidx;
|
||||||
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
||||||
} else {
|
} else {
|
||||||
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
|
var pnxname = supbooks.SheetNames[bookidx];
|
||||||
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
|
var o = "";
|
||||||
|
if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){}
|
||||||
|
else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){
|
||||||
|
if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) {
|
||||||
|
o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else o = supbooks.SheetNames[nameidx-1]+ "!";
|
||||||
|
if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name;
|
||||||
|
else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name;
|
||||||
|
else o += "??NAMEX??";
|
||||||
|
stack.push(o);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(!externbook) externbook = {body: "??NAMEX??"};
|
if(!externbook) externbook = {Name: "??NAMEX??"};
|
||||||
stack.push(externbook.body);
|
stack.push(externbook.Name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.80 */
|
/* 2.5.198.80 */
|
||||||
@ -937,6 +949,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
/* 2.5.198.29 */
|
/* 2.5.198.29 */
|
||||||
case 'PtgAreaErr': stack.push("#REF!"); break;
|
case 'PtgAreaErr': stack.push("#REF!"); break;
|
||||||
|
|
||||||
|
/* 2.5.198.30 */
|
||||||
|
case 'PtgAreaErr3d': stack.push("#REF!"); break;
|
||||||
|
|
||||||
/* 2.5.198.72 TODO */
|
/* 2.5.198.72 TODO */
|
||||||
case 'PtgMemFunc': break;
|
case 'PtgMemFunc': break;
|
||||||
|
|
||||||
|
@ -342,9 +342,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
|||||||
var supbooks = ([[]]/*:any*/);
|
var supbooks = ([[]]/*:any*/);
|
||||||
supbooks.sharedf = shared_formulae;
|
supbooks.sharedf = shared_formulae;
|
||||||
supbooks.arrayf = array_formulae;
|
supbooks.arrayf = array_formulae;
|
||||||
|
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
|
||||||
opts.supbooks = supbooks;
|
opts.supbooks = supbooks;
|
||||||
|
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
|
||||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
|
||||||
|
|
||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
var wbnsregex = /<\w+:workbook/;
|
var wbnsregex = /<\w+:workbook/;
|
||||||
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
||||||
if(!data) throw new Error("Could not find file");
|
if(!data) throw new Error("Could not find file");
|
||||||
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" };
|
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" };
|
||||||
var pass = false, xmlns = "xmlns";
|
var pass = false, xmlns = "xmlns";
|
||||||
var dname = {}, dnstart = 0;
|
var dname = {}, dnstart = 0;
|
||||||
/*(data.match(tagregex)||[]).forEach */
|
/*(data.match(tagregex)||[]).forEach */
|
||||||
@ -73,12 +73,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
|||||||
dname = {};
|
dname = {};
|
||||||
dname.Name = y.name;
|
dname.Name = y.name;
|
||||||
if(y.comment) dname.Comment = y.comment;
|
if(y.comment) dname.Comment = y.comment;
|
||||||
|
if(y.localSheetId) dname.Sheet = +y.localSheetId;
|
||||||
dnstart = idx + x.length;
|
dnstart = idx + x.length;
|
||||||
} break;
|
} break;
|
||||||
case '</definedName>': {
|
case '</definedName>': {
|
||||||
dname.Ref = data.slice(dnstart, idx);
|
dname.Ref = data.slice(dnstart, idx);
|
||||||
wb.Names[dname.Name] = dname;
|
wb.Names.push(dname);
|
||||||
wb.Names['!names'].push(dname.Name);
|
|
||||||
} break;
|
} break;
|
||||||
case '<definedName/>': break;
|
case '<definedName/>': break;
|
||||||
|
|
||||||
@ -184,7 +184,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
|
|||||||
|
|
||||||
/* functionGroups */
|
/* functionGroups */
|
||||||
/* externalReferences */
|
/* externalReferences */
|
||||||
/* definedNames */
|
|
||||||
|
if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) {
|
||||||
|
o[o.length] = "<definedNames>";
|
||||||
|
wb.Workbook.Names.forEach(function(n) {
|
||||||
|
var d = {name:n.Name};
|
||||||
|
if(n.Comment) d.comment = n.Comment;
|
||||||
|
if(n.Sheet != null) d.localSheetId = ""+n.Sheet;
|
||||||
|
if(!n.Ref) return;
|
||||||
|
o[o.length] = writextag('definedName', String(n.Ref), d);
|
||||||
|
});
|
||||||
|
o[o.length] = "</definedNames>";
|
||||||
|
}
|
||||||
|
|
||||||
/* calcPr */
|
/* calcPr */
|
||||||
/* oleSize */
|
/* oleSize */
|
||||||
/* customWorkbookViews */
|
/* customWorkbookViews */
|
||||||
|
@ -55,7 +55,9 @@ function parse_BrtName(data, length, opts) {
|
|||||||
// unusedstring2: XLNullableWideString
|
// unusedstring2: XLNullableWideString
|
||||||
//}
|
//}
|
||||||
data.l = end;
|
data.l = end;
|
||||||
return {Name:name, Ptg:formula, Comment:comment};
|
var out = ({Name:name, Ptg:formula, Comment:comment}/*:any*/);
|
||||||
|
if(itab < 0xFFFFFFF) out.Sheet = itab;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [MS-XLSB] 2.1.7.60 Workbook */
|
/* [MS-XLSB] 2.1.7.60 Workbook */
|
||||||
@ -66,15 +68,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
|||||||
if(!opts) opts = {};
|
if(!opts) opts = {};
|
||||||
opts.biff = 12;
|
opts.biff = 12;
|
||||||
|
|
||||||
var Names = {}, NameList = [];
|
var Names = [];
|
||||||
|
var supbooks = [];
|
||||||
|
supbooks.SheetNames = [];
|
||||||
|
|
||||||
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
||||||
switch(RT) {
|
switch(RT) {
|
||||||
case 0x009C: /* 'BrtBundleSh' */
|
case 0x009C: /* 'BrtBundleSh' */
|
||||||
|
supbooks.SheetNames.push(val.name);
|
||||||
wb.Sheets.push(val); break;
|
wb.Sheets.push(val); break;
|
||||||
|
|
||||||
case 0x0027: /* 'BrtName' */
|
case 0x0027: /* 'BrtName' */
|
||||||
Names[val.Name] = val; NameList.push(val.Name);
|
val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts);
|
||||||
|
delete val.Ptg;
|
||||||
|
Names.push(val);
|
||||||
break;
|
break;
|
||||||
case 0x040C: /* 'BrtNameExt' */ break;
|
case 0x040C: /* 'BrtNameExt' */ break;
|
||||||
|
|
||||||
@ -133,7 +140,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
|||||||
|
|
||||||
parse_wb_defaults(wb);
|
parse_wb_defaults(wb);
|
||||||
|
|
||||||
Names['!names'] = NameList;
|
|
||||||
// $FlowIgnore
|
// $FlowIgnore
|
||||||
wb.Names = Names;
|
wb.Names = Names;
|
||||||
|
|
||||||
|
@ -312,7 +312,17 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
|||||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'NamedRange': break;
|
case 'NamedRange':
|
||||||
|
if(!Workbook.Names) Workbook.Names = [];
|
||||||
|
var _NamedRange = parsexmltag(Rn[0]);
|
||||||
|
var _DefinedName = {
|
||||||
|
Name: _NamedRange.Name,
|
||||||
|
Ref: rc_to_a1(_NamedRange.RefersTo.substr(1))
|
||||||
|
};
|
||||||
|
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
|
||||||
|
Workbook.Names.push(_DefinedName);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'NamedCell': break;
|
case 'NamedCell': break;
|
||||||
case 'B': break;
|
case 'B': break;
|
||||||
case 'I': break;
|
case 'I': break;
|
||||||
@ -796,7 +806,7 @@ function write_props_xlml(wb, opts) {
|
|||||||
/* DocumentProperties */
|
/* DocumentProperties */
|
||||||
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
||||||
/* CustomDocumentProperties */
|
/* CustomDocumentProperties */
|
||||||
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
|
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts));
|
||||||
return o.join("");
|
return o.join("");
|
||||||
}
|
}
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
@ -169,15 +169,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
var seencol = false;
|
var seencol = false;
|
||||||
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
|
var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
|
||||||
var sbc = 0, sbci = 0, sbcli = 0;
|
|
||||||
supbooks.SheetNames = opts.snames;
|
supbooks.SheetNames = opts.snames;
|
||||||
supbooks.sharedf = opts.sharedf;
|
supbooks.sharedf = opts.sharedf;
|
||||||
supbooks.arrayf = opts.arrayf;
|
supbooks.arrayf = opts.arrayf;
|
||||||
|
supbooks.names = [];
|
||||||
|
supbooks.XTI = [];
|
||||||
var last_Rn = '';
|
var last_Rn = '';
|
||||||
var file_depth = 0; /* TODO: make a real stack */
|
var file_depth = 0; /* TODO: make a real stack */
|
||||||
var BIFF2Fmt = 0;
|
var BIFF2Fmt = 0;
|
||||||
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
||||||
|
var last_lbl;
|
||||||
|
|
||||||
/* explicit override for some broken writers */
|
/* explicit override for some broken writers */
|
||||||
opts.codepage = 1200;
|
opts.codepage = 1200;
|
||||||
@ -250,18 +252,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
case 'RichTextStream': break;
|
case 'RichTextStream': break;
|
||||||
case 'BkHim': break;
|
case 'BkHim': break;
|
||||||
|
|
||||||
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
|
case 'SupBook':
|
||||||
case 'ExternName': supbooks[sbc][++sbci] = val; break;
|
supbooks.push([val]);
|
||||||
|
supbooks[supbooks.length-1].XTI = [];
|
||||||
|
break;
|
||||||
|
case 'ExternName':
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
|
break;
|
||||||
case 'Index': break; // TODO
|
case 'Index': break; // TODO
|
||||||
case 'Lbl':
|
case 'Lbl':
|
||||||
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
|
last_lbl = {
|
||||||
if(!supbooks[val.itab]) supbooks[val.itab] = [];
|
Name: val.Name,
|
||||||
supbooks[val.itab].push(val);
|
Ref: stringify_formula(val.rgce,range,null,supbooks,opts)
|
||||||
|
};
|
||||||
|
if(val.itab > 0) last_lbl.Sheet = val.itab - 1;
|
||||||
|
supbooks.names.push(last_lbl);
|
||||||
|
if(!supbooks[0]) supbooks[0] = [];
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
if(val.Name == "\r" && val.itab > 0)
|
if(val.Name == "\r" && val.itab > 0)
|
||||||
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
||||||
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
||||||
break;
|
break;
|
||||||
case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break;
|
case 'ExternSheet':
|
||||||
|
if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; }
|
||||||
|
supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break;
|
||||||
|
case 'NameCmt':
|
||||||
|
/* TODO: search for correct name */
|
||||||
|
if(opts.biff < 8) break;
|
||||||
|
last_lbl.Comment = val[1];
|
||||||
|
break;
|
||||||
|
|
||||||
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
||||||
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
||||||
@ -484,7 +503,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
} break;
|
} break;
|
||||||
case 'Row': break; // TODO
|
case 'Row': break; // TODO
|
||||||
|
|
||||||
case 'NameCmt': break;
|
|
||||||
case 'Header': break; // TODO
|
case 'Header': break; // TODO
|
||||||
case 'Footer': break; // TODO
|
case 'Footer': break; // TODO
|
||||||
case 'HCenter': break; // TODO
|
case 'HCenter': break; // TODO
|
||||||
@ -727,6 +745,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
if(opts.enc) wb.Encryption = opts.enc;
|
if(opts.enc) wb.Encryption = opts.enc;
|
||||||
wb.Metadata = {};
|
wb.Metadata = {};
|
||||||
if(country !== undefined) wb.Metadata.Country = country;
|
if(country !== undefined) wb.Metadata.Country = country;
|
||||||
|
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
|
||||||
wb.Workbook = Workbook;
|
wb.Workbook = Workbook;
|
||||||
return wb;
|
return wb;
|
||||||
}
|
}
|
||||||
|
@ -1103,7 +1103,7 @@ var XLSRecordEnum = {
|
|||||||
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
|
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
|
||||||
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
|
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
|
||||||
/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish },
|
/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish },
|
||||||
/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt },
|
/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt, r:12 },
|
||||||
/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData },
|
/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData },
|
||||||
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
|
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
|
||||||
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
||||||
|
@ -36,3 +36,4 @@ Writers will process the `Props` key of the options object:
|
|||||||
/* force the Author to be "SheetJS" */
|
/* force the Author to be "SheetJS" */
|
||||||
XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
XLSX.write(wb, {Props:{Author:"SheetJS"}});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
19
docbits/57_wbbook.md
Normal file
19
docbits/57_wbbook.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
### Workbook-Level Attributes
|
||||||
|
|
||||||
|
`wb.Workbook` stores workbook level attributes.
|
||||||
|
|
||||||
|
#### Defined Names
|
||||||
|
|
||||||
|
`wb.Workbook.Names` is an array of defined name objects which have the keys:
|
||||||
|
|
||||||
|
| Key | Description |
|
||||||
|
|:----------|:-----------------------------------------------------------------|
|
||||||
|
| `Sheet` | Name scope. Sheet Index (0 = first sheet) or `null` (Workbook) |
|
||||||
|
| `Name` | Case-sensitive name. Standard rules apply ** |
|
||||||
|
| `Ref` | A1-style Reference (e.g. `"Sheet1!$A$1:$D$20"`) |
|
||||||
|
| `Comment` | Comment (only applicable for XLS/XLSX/XLSB) |
|
||||||
|
|
||||||
|
Excel allows two sheet-scoped defined names to share the same name. However, a
|
||||||
|
sheet-scoped name cannot collide with a workbook-scope name. Workbook writers
|
||||||
|
may not enforce this constraint.
|
||||||
|
|
@ -22,6 +22,8 @@
|
|||||||
+ [Chartsheet Object](README.md#chartsheet-object)
|
+ [Chartsheet Object](README.md#chartsheet-object)
|
||||||
* [Workbook Object](README.md#workbook-object)
|
* [Workbook Object](README.md#workbook-object)
|
||||||
+ [Workbook File Properties](README.md#workbook-file-properties)
|
+ [Workbook File Properties](README.md#workbook-file-properties)
|
||||||
|
* [Workbook-Level Attributes](README.md#workbook-level-attributes)
|
||||||
|
+ [Defined Names](README.md#defined-names)
|
||||||
* [Document Features](README.md#document-features)
|
* [Document Features](README.md#document-features)
|
||||||
+ [Formulae](README.md#formulae)
|
+ [Formulae](README.md#formulae)
|
||||||
+ [Column Properties](README.md#column-properties)
|
+ [Column Properties](README.md#column-properties)
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
# This file controls the multiformat tests
|
# This file controls the multiformat tests
|
||||||
|
# vim: set ts=4:
|
||||||
# Format: <basename> <ext> <ext> [ext..]
|
# Format: <basename> <ext> <ext> [ext..]
|
||||||
|
# yes-formula
|
||||||
AutoFilter .xls .xlsb .xlsx .xml
|
AutoFilter .xls .xlsb .xlsx .xml
|
||||||
#BlankSheetTypes .xls .xlsb .xlsx .xml
|
# note: XLML only supports sheets, ods does not support dialog
|
||||||
|
BlankSheetTypes .xls .xlsb .xlsm
|
||||||
NumberFormatCondition .xls .xlsb .xlsm .xml
|
NumberFormatCondition .xls .xlsb .xlsm .xml
|
||||||
RkNumber .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
RkNumber .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
#calendar_stress_test .xls .xlsb .xlsx .xml
|
#calendar_stress_test .xls .xlsb .xlsx .xml
|
||||||
@ -10,17 +13,15 @@ cell_style_simple .xls .xlsb .xlsx .xml
|
|||||||
comments_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
comments_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
# yes-csv
|
# yes-csv
|
||||||
custom_properties .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
custom_properties .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
# no-formula (defined names)
|
|
||||||
defined_names_simple .xls .xlsb .xlsx .xml
|
defined_names_simple .xls .xlsb .xlsx .xml
|
||||||
# yes-formula
|
|
||||||
# no-csv (randbetween) note: ODS does not support many XLSX functions
|
# no-csv (randbetween) note: ODS does not support many XLSX functions
|
||||||
formula_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
formula_stress_test .xls .xlsb .xlsx .xlsb.xml
|
||||||
# yes-csv
|
# yes-csv
|
||||||
formulae_test_simple .xls .xlsb .xlsx .xml
|
formulae_test_simple .xls .xlsb .xlsx .xml
|
||||||
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
|
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
|
||||||
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
merge_cells .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
merge_cells .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
# no-formula (defined names)
|
# no-formula (filename-references in XLSX encoding as [0])
|
||||||
named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
|
||||||
# yes-formula
|
# yes-formula
|
||||||
# no-csv (macro serialization in xml)
|
# no-csv (macro serialization in xml)
|
||||||
|
27
test.js
27
test.js
@ -50,6 +50,10 @@ var paths = {
|
|||||||
cstxlsx: dir + 'comments_stress_test.xlsx',
|
cstxlsx: dir + 'comments_stress_test.xlsx',
|
||||||
cstxlsb: dir + 'comments_stress_test.xlsb',
|
cstxlsb: dir + 'comments_stress_test.xlsb',
|
||||||
cstods: dir + 'comments_stress_test.ods',
|
cstods: dir + 'comments_stress_test.ods',
|
||||||
|
dnsxls: dir + 'defined_names_simple.xls',
|
||||||
|
dnsxml: dir + 'defined_names_simple.xml',
|
||||||
|
dnsxlsx: dir + 'defined_names_simple.xlsx',
|
||||||
|
dnsxlsb: dir + 'defined_names_simple.xlsb',
|
||||||
fstxls: dir + 'formula_stress_test.xls',
|
fstxls: dir + 'formula_stress_test.xls',
|
||||||
fstxml: dir + 'formula_stress_test.xls.xml',
|
fstxml: dir + 'formula_stress_test.xls.xml',
|
||||||
fstxlsx: dir + 'formula_stress_test.xlsx',
|
fstxlsx: dir + 'formula_stress_test.xlsx',
|
||||||
@ -893,6 +897,29 @@ describe('parse features', function() {
|
|||||||
}); });
|
}); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('defined names', function() {
|
||||||
|
[
|
||||||
|
/* desc path cmnt */
|
||||||
|
['xlsx', paths.dnsxlsx, true],
|
||||||
|
['xlsb', paths.dnsxlsb, true],
|
||||||
|
['xls', paths.dnsxls, true],
|
||||||
|
['xlml', paths.dnsxml, false],
|
||||||
|
].forEach(function(m) { it(m[0], function() {
|
||||||
|
var wb = X.readFile(m[1]);
|
||||||
|
var names = wb.Workbook.Names;
|
||||||
|
for(var i = 0; i < names.length; ++i) if(names[i].Name == "SheetJS") break;
|
||||||
|
assert(i < names.length, "Missing name");
|
||||||
|
assert.equal(names[i].Sheet, null);
|
||||||
|
assert.equal(names[i].Ref, "Sheet1!$A$1");
|
||||||
|
if(m[2]) assert.equal(names[i].Comment, "defined names just suck excel formulae are bad MS should feel bad");
|
||||||
|
|
||||||
|
for(i = 0; i < names.length; ++i) if(names[i].Name == "SHEETjs") break;
|
||||||
|
assert(i < names.length, "Missing name");
|
||||||
|
assert.equal(names[i].Sheet, 0);
|
||||||
|
assert.equal(names[i].Ref, "Sheet1!$A$2");
|
||||||
|
}); });
|
||||||
|
});
|
||||||
|
|
||||||
describe('auto filter', function() {
|
describe('auto filter', function() {
|
||||||
[
|
[
|
||||||
['xlsx', paths.afxlsx],
|
['xlsx', paths.afxlsx],
|
||||||
|
155
xlsx.flow.js
155
xlsx.flow.js
@ -3273,7 +3273,7 @@ function xlml_write_docprops(Props, opts) {
|
|||||||
case 'date': m = new Date(m).toISOString(); break;
|
case 'date': m = new Date(m).toISOString(); break;
|
||||||
}
|
}
|
||||||
if(typeof m == 'number') m = String(m);
|
if(typeof m == 'number') m = String(m);
|
||||||
else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
|
else if(m === true || m === false) { m = m ? "1" : "0"; }
|
||||||
else if(m instanceof Date) m = new Date(m).toISOString();
|
else if(m instanceof Date) m = new Date(m).toISOString();
|
||||||
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
||||||
});
|
});
|
||||||
@ -3590,13 +3590,6 @@ function parslurp(blob, length, cb) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parslurp2(blob, length, cb) {
|
|
||||||
var arr = [], target = blob.l + length, len = blob.read_shift(2);
|
|
||||||
while(len-- !== 0) arr.push(cb(blob, target - blob.l));
|
|
||||||
if(target !== blob.l) throw new Error("Slurp error");
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
||||||
|
|
||||||
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
||||||
@ -4200,6 +4193,7 @@ function parse_ExternName(blob, length, opts) {
|
|||||||
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
||||||
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
||||||
o.body = body || blob.read_shift(length-2);
|
o.body = body || blob.read_shift(length-2);
|
||||||
|
if(typeof body === "string") o.Name = body;
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4230,13 +4224,21 @@ function parse_Lbl(blob, length, opts) {
|
|||||||
/* 2.4.106 TODO: verify supbook manipulation */
|
/* 2.4.106 TODO: verify supbook manipulation */
|
||||||
function parse_ExternSheet(blob, length, opts) {
|
function parse_ExternSheet(blob, length, opts) {
|
||||||
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
||||||
var o = parslurp2(blob,length,parse_XTI);
|
var o = [], target = blob.l + length, len = blob.read_shift(2);
|
||||||
|
while(len-- !== 0) o.push(parse_XTI(blob, 6));
|
||||||
|
// [iSupBook, itabFirst, itabLast];
|
||||||
var oo = [];
|
var oo = [];
|
||||||
if(opts.sbcch === 0x0401) {
|
return o;
|
||||||
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
|
}
|
||||||
return oo;
|
|
||||||
}
|
/* 2.4.176 TODO: check older biff */
|
||||||
else return o;
|
function parse_NameCmt(blob, length, opts) {
|
||||||
|
if(opts.biff < 8) { blob.l += length; return; }
|
||||||
|
var cchName = blob.read_shift(2);
|
||||||
|
var cchComment = blob.read_shift(2);
|
||||||
|
var name = parse_XLUnicodeStringNoCch(blob, cchName, opts);
|
||||||
|
var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts);
|
||||||
|
return [name, comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2.4.260 */
|
/* 2.4.260 */
|
||||||
@ -4652,7 +4654,6 @@ var parse_TableStyles = parsenoop;
|
|||||||
var parse_TableStyle = parsenoop;
|
var parse_TableStyle = parsenoop;
|
||||||
var parse_TableStyleElement = parsenoop;
|
var parse_TableStyleElement = parsenoop;
|
||||||
var parse_NamePublish = parsenoop;
|
var parse_NamePublish = parsenoop;
|
||||||
var parse_NameCmt = parsenoop;
|
|
||||||
var parse_SortData = parsenoop;
|
var parse_SortData = parsenoop;
|
||||||
var parse_GUIDTypeLib = parsenoop;
|
var parse_GUIDTypeLib = parsenoop;
|
||||||
var parse_FnGrp12 = parsenoop;
|
var parse_FnGrp12 = parsenoop;
|
||||||
@ -8130,13 +8131,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
break;
|
break;
|
||||||
/* 2.5.198.88 */
|
/* 2.5.198.88 */
|
||||||
case 'PtgRefN':
|
case 'PtgRefN':
|
||||||
type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts);
|
type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1];
|
||||||
stack.push(encode_cell_xls(c));
|
stack.push(encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
case 'PtgRef3d': // TODO: lots of stuff
|
case 'PtgRef3d': // TODO: lots of stuff
|
||||||
type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts);
|
type = f[1][0]; ixti = /*::Number(*/f[1][1]/*::)*/; c = shift_cell_xls(f[1][2], _range, opts);
|
||||||
sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**");
|
sname = supbooks.SheetNames[ixti];
|
||||||
stack.push(sname + "!" + encode_cell(c));
|
stack.push(sname + "!" + encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.62 */
|
/* 2.5.198.62 */
|
||||||
@ -8192,7 +8193,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
case 'PtgName':
|
case 'PtgName':
|
||||||
/* f[1] = type, 0, nameindex */
|
/* f[1] = type, 0, nameindex */
|
||||||
nameidx = f[1][2];
|
nameidx = f[1][2];
|
||||||
var lbl = supbooks[0][nameidx];
|
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
|
||||||
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
||||||
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
||||||
stack.push(name);
|
stack.push(name);
|
||||||
@ -8204,15 +8205,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook;
|
var bookidx/*:number*/ = (f[1][1]/*:any*/); nameidx = f[1][2]; var externbook;
|
||||||
/* TODO: Properly handle missing values */
|
/* TODO: Properly handle missing values */
|
||||||
//console.log(bookidx, supbooks);
|
//console.log(bookidx, supbooks);
|
||||||
if(opts.biff == 5) {
|
if(opts.biff <= 5) {
|
||||||
if(bookidx < 0) bookidx = -bookidx;
|
if(bookidx < 0) bookidx = -bookidx;
|
||||||
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
||||||
} else {
|
} else {
|
||||||
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
|
var pnxname = supbooks.SheetNames[bookidx];
|
||||||
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
|
var o = "";
|
||||||
|
if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){}
|
||||||
|
else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){
|
||||||
|
if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) {
|
||||||
|
o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else o = supbooks.SheetNames[nameidx-1]+ "!";
|
||||||
|
if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name;
|
||||||
|
else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name;
|
||||||
|
else o += "??NAMEX??";
|
||||||
|
stack.push(o);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(!externbook) externbook = {body: "??NAMEX??"};
|
if(!externbook) externbook = {Name: "??NAMEX??"};
|
||||||
stack.push(externbook.body);
|
stack.push(externbook.Name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.80 */
|
/* 2.5.198.80 */
|
||||||
@ -8298,6 +8311,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
|
|||||||
/* 2.5.198.29 */
|
/* 2.5.198.29 */
|
||||||
case 'PtgAreaErr': stack.push("#REF!"); break;
|
case 'PtgAreaErr': stack.push("#REF!"); break;
|
||||||
|
|
||||||
|
/* 2.5.198.30 */
|
||||||
|
case 'PtgAreaErr3d': stack.push("#REF!"); break;
|
||||||
|
|
||||||
/* 2.5.198.72 TODO */
|
/* 2.5.198.72 TODO */
|
||||||
case 'PtgMemFunc': break;
|
case 'PtgMemFunc': break;
|
||||||
|
|
||||||
@ -10528,9 +10544,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
|||||||
var supbooks = ([[]]/*:any*/);
|
var supbooks = ([[]]/*:any*/);
|
||||||
supbooks.sharedf = shared_formulae;
|
supbooks.sharedf = shared_formulae;
|
||||||
supbooks.arrayf = array_formulae;
|
supbooks.arrayf = array_formulae;
|
||||||
|
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
|
||||||
opts.supbooks = supbooks;
|
opts.supbooks = supbooks;
|
||||||
|
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
|
||||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
|
||||||
|
|
||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
@ -11161,7 +11177,7 @@ function check_wb(wb) {
|
|||||||
var wbnsregex = /<\w+:workbook/;
|
var wbnsregex = /<\w+:workbook/;
|
||||||
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
||||||
if(!data) throw new Error("Could not find file");
|
if(!data) throw new Error("Could not find file");
|
||||||
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" };
|
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" };
|
||||||
var pass = false, xmlns = "xmlns";
|
var pass = false, xmlns = "xmlns";
|
||||||
var dname = {}, dnstart = 0;
|
var dname = {}, dnstart = 0;
|
||||||
/*(data.match(tagregex)||[]).forEach */
|
/*(data.match(tagregex)||[]).forEach */
|
||||||
@ -11232,12 +11248,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
|
|||||||
dname = {};
|
dname = {};
|
||||||
dname.Name = y.name;
|
dname.Name = y.name;
|
||||||
if(y.comment) dname.Comment = y.comment;
|
if(y.comment) dname.Comment = y.comment;
|
||||||
|
if(y.localSheetId) dname.Sheet = +y.localSheetId;
|
||||||
dnstart = idx + x.length;
|
dnstart = idx + x.length;
|
||||||
} break;
|
} break;
|
||||||
case '</definedName>': {
|
case '</definedName>': {
|
||||||
dname.Ref = data.slice(dnstart, idx);
|
dname.Ref = data.slice(dnstart, idx);
|
||||||
wb.Names[dname.Name] = dname;
|
wb.Names.push(dname);
|
||||||
wb.Names['!names'].push(dname.Name);
|
|
||||||
} break;
|
} break;
|
||||||
case '<definedName/>': break;
|
case '<definedName/>': break;
|
||||||
|
|
||||||
@ -11343,7 +11359,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
|
|||||||
|
|
||||||
/* functionGroups */
|
/* functionGroups */
|
||||||
/* externalReferences */
|
/* externalReferences */
|
||||||
/* definedNames */
|
|
||||||
|
if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) {
|
||||||
|
o[o.length] = "<definedNames>";
|
||||||
|
wb.Workbook.Names.forEach(function(n) {
|
||||||
|
var d = {name:n.Name};
|
||||||
|
if(n.Comment) d.comment = n.Comment;
|
||||||
|
if(n.Sheet != null) d.localSheetId = ""+n.Sheet;
|
||||||
|
if(!n.Ref) return;
|
||||||
|
o[o.length] = writextag('definedName', String(n.Ref), d);
|
||||||
|
});
|
||||||
|
o[o.length] = "</definedNames>";
|
||||||
|
}
|
||||||
|
|
||||||
/* calcPr */
|
/* calcPr */
|
||||||
/* oleSize */
|
/* oleSize */
|
||||||
/* customWorkbookViews */
|
/* customWorkbookViews */
|
||||||
@ -11415,7 +11443,9 @@ function parse_BrtName(data, length, opts) {
|
|||||||
// unusedstring2: XLNullableWideString
|
// unusedstring2: XLNullableWideString
|
||||||
//}
|
//}
|
||||||
data.l = end;
|
data.l = end;
|
||||||
return {Name:name, Ptg:formula, Comment:comment};
|
var out = ({Name:name, Ptg:formula, Comment:comment}/*:any*/);
|
||||||
|
if(itab < 0xFFFFFFF) out.Sheet = itab;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [MS-XLSB] 2.1.7.60 Workbook */
|
/* [MS-XLSB] 2.1.7.60 Workbook */
|
||||||
@ -11426,15 +11456,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
|||||||
if(!opts) opts = {};
|
if(!opts) opts = {};
|
||||||
opts.biff = 12;
|
opts.biff = 12;
|
||||||
|
|
||||||
var Names = {}, NameList = [];
|
var Names = [];
|
||||||
|
var supbooks = [];
|
||||||
|
supbooks.SheetNames = [];
|
||||||
|
|
||||||
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
||||||
switch(RT) {
|
switch(RT) {
|
||||||
case 0x009C: /* 'BrtBundleSh' */
|
case 0x009C: /* 'BrtBundleSh' */
|
||||||
|
supbooks.SheetNames.push(val.name);
|
||||||
wb.Sheets.push(val); break;
|
wb.Sheets.push(val); break;
|
||||||
|
|
||||||
case 0x0027: /* 'BrtName' */
|
case 0x0027: /* 'BrtName' */
|
||||||
Names[val.Name] = val; NameList.push(val.Name);
|
val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts);
|
||||||
|
delete val.Ptg;
|
||||||
|
Names.push(val);
|
||||||
break;
|
break;
|
||||||
case 0x040C: /* 'BrtNameExt' */ break;
|
case 0x040C: /* 'BrtNameExt' */ break;
|
||||||
|
|
||||||
@ -11493,7 +11528,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
|
|||||||
|
|
||||||
parse_wb_defaults(wb);
|
parse_wb_defaults(wb);
|
||||||
|
|
||||||
Names['!names'] = NameList;
|
|
||||||
// $FlowIgnore
|
// $FlowIgnore
|
||||||
wb.Names = Names;
|
wb.Names = Names;
|
||||||
|
|
||||||
@ -11995,7 +12029,17 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
|
|||||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'NamedRange': break;
|
case 'NamedRange':
|
||||||
|
if(!Workbook.Names) Workbook.Names = [];
|
||||||
|
var _NamedRange = parsexmltag(Rn[0]);
|
||||||
|
var _DefinedName = {
|
||||||
|
Name: _NamedRange.Name,
|
||||||
|
Ref: rc_to_a1(_NamedRange.RefersTo.substr(1))
|
||||||
|
};
|
||||||
|
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
|
||||||
|
Workbook.Names.push(_DefinedName);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'NamedCell': break;
|
case 'NamedCell': break;
|
||||||
case 'B': break;
|
case 'B': break;
|
||||||
case 'I': break;
|
case 'I': break;
|
||||||
@ -12479,7 +12523,7 @@ function write_props_xlml(wb, opts) {
|
|||||||
/* DocumentProperties */
|
/* DocumentProperties */
|
||||||
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
||||||
/* CustomDocumentProperties */
|
/* CustomDocumentProperties */
|
||||||
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
|
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts));
|
||||||
return o.join("");
|
return o.join("");
|
||||||
}
|
}
|
||||||
/* TODO */
|
/* TODO */
|
||||||
@ -12797,15 +12841,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
var seencol = false;
|
var seencol = false;
|
||||||
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
|
var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
|
||||||
var sbc = 0, sbci = 0, sbcli = 0;
|
|
||||||
supbooks.SheetNames = opts.snames;
|
supbooks.SheetNames = opts.snames;
|
||||||
supbooks.sharedf = opts.sharedf;
|
supbooks.sharedf = opts.sharedf;
|
||||||
supbooks.arrayf = opts.arrayf;
|
supbooks.arrayf = opts.arrayf;
|
||||||
|
supbooks.names = [];
|
||||||
|
supbooks.XTI = [];
|
||||||
var last_Rn = '';
|
var last_Rn = '';
|
||||||
var file_depth = 0; /* TODO: make a real stack */
|
var file_depth = 0; /* TODO: make a real stack */
|
||||||
var BIFF2Fmt = 0;
|
var BIFF2Fmt = 0;
|
||||||
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
||||||
|
var last_lbl;
|
||||||
|
|
||||||
/* explicit override for some broken writers */
|
/* explicit override for some broken writers */
|
||||||
opts.codepage = 1200;
|
opts.codepage = 1200;
|
||||||
@ -12878,18 +12924,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
case 'RichTextStream': break;
|
case 'RichTextStream': break;
|
||||||
case 'BkHim': break;
|
case 'BkHim': break;
|
||||||
|
|
||||||
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
|
case 'SupBook':
|
||||||
case 'ExternName': supbooks[sbc][++sbci] = val; break;
|
supbooks.push([val]);
|
||||||
|
supbooks[supbooks.length-1].XTI = [];
|
||||||
|
break;
|
||||||
|
case 'ExternName':
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
|
break;
|
||||||
case 'Index': break; // TODO
|
case 'Index': break; // TODO
|
||||||
case 'Lbl':
|
case 'Lbl':
|
||||||
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
|
last_lbl = {
|
||||||
if(!supbooks[val.itab]) supbooks[val.itab] = [];
|
Name: val.Name,
|
||||||
supbooks[val.itab].push(val);
|
Ref: stringify_formula(val.rgce,range,null,supbooks,opts)
|
||||||
|
};
|
||||||
|
if(val.itab > 0) last_lbl.Sheet = val.itab - 1;
|
||||||
|
supbooks.names.push(last_lbl);
|
||||||
|
if(!supbooks[0]) supbooks[0] = [];
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
if(val.Name == "\r" && val.itab > 0)
|
if(val.Name == "\r" && val.itab > 0)
|
||||||
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
||||||
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
||||||
break;
|
break;
|
||||||
case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break;
|
case 'ExternSheet':
|
||||||
|
if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; }
|
||||||
|
supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break;
|
||||||
|
case 'NameCmt':
|
||||||
|
/* TODO: search for correct name */
|
||||||
|
if(opts.biff < 8) break;
|
||||||
|
last_lbl.Comment = val[1];
|
||||||
|
break;
|
||||||
|
|
||||||
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
||||||
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
||||||
@ -13112,7 +13175,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
} break;
|
} break;
|
||||||
case 'Row': break; // TODO
|
case 'Row': break; // TODO
|
||||||
|
|
||||||
case 'NameCmt': break;
|
|
||||||
case 'Header': break; // TODO
|
case 'Header': break; // TODO
|
||||||
case 'Footer': break; // TODO
|
case 'Footer': break; // TODO
|
||||||
case 'HCenter': break; // TODO
|
case 'HCenter': break; // TODO
|
||||||
@ -13355,6 +13417,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
|||||||
if(opts.enc) wb.Encryption = opts.enc;
|
if(opts.enc) wb.Encryption = opts.enc;
|
||||||
wb.Metadata = {};
|
wb.Metadata = {};
|
||||||
if(country !== undefined) wb.Metadata.Country = country;
|
if(country !== undefined) wb.Metadata.Country = country;
|
||||||
|
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
|
||||||
wb.Workbook = Workbook;
|
wb.Workbook = Workbook;
|
||||||
return wb;
|
return wb;
|
||||||
}
|
}
|
||||||
@ -14515,7 +14578,7 @@ var XLSRecordEnum = {
|
|||||||
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
|
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
|
||||||
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
|
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
|
||||||
/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish },
|
/*::[*/0x0893/*::]*/: { n:"NamePublish", f:parse_NamePublish },
|
||||||
/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt },
|
/*::[*/0x0894/*::]*/: { n:"NameCmt", f:parse_NameCmt, r:12 },
|
||||||
/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData },
|
/*::[*/0x0895/*::]*/: { n:"SortData", f:parse_SortData },
|
||||||
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
|
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
|
||||||
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
||||||
|
155
xlsx.js
155
xlsx.js
@ -3219,7 +3219,7 @@ function xlml_write_docprops(Props, opts) {
|
|||||||
case 'date': m = new Date(m).toISOString(); break;
|
case 'date': m = new Date(m).toISOString(); break;
|
||||||
}
|
}
|
||||||
if(typeof m == 'number') m = String(m);
|
if(typeof m == 'number') m = String(m);
|
||||||
else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
|
else if(m === true || m === false) { m = m ? "1" : "0"; }
|
||||||
else if(m instanceof Date) m = new Date(m).toISOString();
|
else if(m instanceof Date) m = new Date(m).toISOString();
|
||||||
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
|
||||||
});
|
});
|
||||||
@ -3534,13 +3534,6 @@ function parslurp(blob, length, cb) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parslurp2(blob, length, cb) {
|
|
||||||
var arr = [], target = blob.l + length, len = blob.read_shift(2);
|
|
||||||
while(len-- !== 0) arr.push(cb(blob, target - blob.l));
|
|
||||||
if(target !== blob.l) throw new Error("Slurp error");
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
|
||||||
|
|
||||||
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
function parseuint16(blob) { return blob.read_shift(2, 'u'); }
|
||||||
@ -4144,6 +4137,7 @@ function parse_ExternName(blob, length, opts) {
|
|||||||
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
|
||||||
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
|
||||||
o.body = body || blob.read_shift(length-2);
|
o.body = body || blob.read_shift(length-2);
|
||||||
|
if(typeof body === "string") o.Name = body;
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4174,13 +4168,21 @@ function parse_Lbl(blob, length, opts) {
|
|||||||
/* 2.4.106 TODO: verify supbook manipulation */
|
/* 2.4.106 TODO: verify supbook manipulation */
|
||||||
function parse_ExternSheet(blob, length, opts) {
|
function parse_ExternSheet(blob, length, opts) {
|
||||||
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
|
||||||
var o = parslurp2(blob,length,parse_XTI);
|
var o = [], target = blob.l + length, len = blob.read_shift(2);
|
||||||
|
while(len-- !== 0) o.push(parse_XTI(blob, 6));
|
||||||
|
// [iSupBook, itabFirst, itabLast];
|
||||||
var oo = [];
|
var oo = [];
|
||||||
if(opts.sbcch === 0x0401) {
|
return o;
|
||||||
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
|
}
|
||||||
return oo;
|
|
||||||
}
|
/* 2.4.176 TODO: check older biff */
|
||||||
else return o;
|
function parse_NameCmt(blob, length, opts) {
|
||||||
|
if(opts.biff < 8) { blob.l += length; return; }
|
||||||
|
var cchName = blob.read_shift(2);
|
||||||
|
var cchComment = blob.read_shift(2);
|
||||||
|
var name = parse_XLUnicodeStringNoCch(blob, cchName, opts);
|
||||||
|
var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts);
|
||||||
|
return [name, comment];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2.4.260 */
|
/* 2.4.260 */
|
||||||
@ -4596,7 +4598,6 @@ var parse_TableStyles = parsenoop;
|
|||||||
var parse_TableStyle = parsenoop;
|
var parse_TableStyle = parsenoop;
|
||||||
var parse_TableStyleElement = parsenoop;
|
var parse_TableStyleElement = parsenoop;
|
||||||
var parse_NamePublish = parsenoop;
|
var parse_NamePublish = parsenoop;
|
||||||
var parse_NameCmt = parsenoop;
|
|
||||||
var parse_SortData = parsenoop;
|
var parse_SortData = parsenoop;
|
||||||
var parse_GUIDTypeLib = parsenoop;
|
var parse_GUIDTypeLib = parsenoop;
|
||||||
var parse_FnGrp12 = parsenoop;
|
var parse_FnGrp12 = parsenoop;
|
||||||
@ -8073,13 +8074,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
|
|||||||
break;
|
break;
|
||||||
/* 2.5.198.88 */
|
/* 2.5.198.88 */
|
||||||
case 'PtgRefN':
|
case 'PtgRefN':
|
||||||
type = f[1][0]; c = shift_cell_xls(f[1][1], cell, opts);
|
type = f[1][0]; c = cell ? shift_cell_xls(f[1][1], cell, opts) : f[1][1];
|
||||||
stack.push(encode_cell_xls(c));
|
stack.push(encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
case 'PtgRef3d': // TODO: lots of stuff
|
case 'PtgRef3d': // TODO: lots of stuff
|
||||||
type = f[1][0]; ixti = f[1][1]; c = shift_cell_xls(f[1][2], _range, opts);
|
type = f[1][0]; ixti = f[1][1]; c = shift_cell_xls(f[1][2], _range, opts);
|
||||||
sname = (supbooks && supbooks[1] ? supbooks[1][ixti+1] : "**MISSING**");
|
sname = supbooks.SheetNames[ixti];
|
||||||
stack.push(sname + "!" + encode_cell(c));
|
stack.push(sname + "!" + encode_cell_xls(c));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.62 */
|
/* 2.5.198.62 */
|
||||||
@ -8135,7 +8136,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
|
|||||||
case 'PtgName':
|
case 'PtgName':
|
||||||
/* f[1] = type, 0, nameindex */
|
/* f[1] = type, 0, nameindex */
|
||||||
nameidx = f[1][2];
|
nameidx = f[1][2];
|
||||||
var lbl = supbooks[0][nameidx];
|
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
|
||||||
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
var name = lbl ? lbl.Name : "**MISSING**" + String(nameidx);
|
||||||
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
|
||||||
stack.push(name);
|
stack.push(name);
|
||||||
@ -8147,15 +8148,27 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
|
|||||||
var bookidx = (f[1][1]); nameidx = f[1][2]; var externbook;
|
var bookidx = (f[1][1]); nameidx = f[1][2]; var externbook;
|
||||||
/* TODO: Properly handle missing values */
|
/* TODO: Properly handle missing values */
|
||||||
//console.log(bookidx, supbooks);
|
//console.log(bookidx, supbooks);
|
||||||
if(opts.biff == 5) {
|
if(opts.biff <= 5) {
|
||||||
if(bookidx < 0) bookidx = -bookidx;
|
if(bookidx < 0) bookidx = -bookidx;
|
||||||
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
|
||||||
} else {
|
} else {
|
||||||
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
|
var pnxname = supbooks.SheetNames[bookidx];
|
||||||
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
|
var o = "";
|
||||||
|
if(((supbooks[bookidx]||[])[0]||[])[0] == 0x3A01){}
|
||||||
|
else if(((supbooks[bookidx]||[])[0]||[])[0] == 0x0401){
|
||||||
|
if(supbooks[bookidx][nameidx] && supbooks[bookidx][nameidx].itab > 0) {
|
||||||
|
o = supbooks.SheetNames[supbooks[bookidx][nameidx].itab-1] + "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else o = supbooks.SheetNames[nameidx-1]+ "!";
|
||||||
|
if(supbooks[bookidx] && supbooks[bookidx][nameidx]) o += supbooks[bookidx][nameidx].Name;
|
||||||
|
else if(supbooks[0] && supbooks[0][nameidx]) o += supbooks[0][nameidx].Name;
|
||||||
|
else o += "??NAMEX??";
|
||||||
|
stack.push(o);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if(!externbook) externbook = {body: "??NAMEX??"};
|
if(!externbook) externbook = {Name: "??NAMEX??"};
|
||||||
stack.push(externbook.body);
|
stack.push(externbook.Name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* 2.5.198.80 */
|
/* 2.5.198.80 */
|
||||||
@ -8241,6 +8254,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
|
|||||||
/* 2.5.198.29 */
|
/* 2.5.198.29 */
|
||||||
case 'PtgAreaErr': stack.push("#REF!"); break;
|
case 'PtgAreaErr': stack.push("#REF!"); break;
|
||||||
|
|
||||||
|
/* 2.5.198.30 */
|
||||||
|
case 'PtgAreaErr3d': stack.push("#REF!"); break;
|
||||||
|
|
||||||
/* 2.5.198.72 TODO */
|
/* 2.5.198.72 TODO */
|
||||||
case 'PtgMemFunc': break;
|
case 'PtgMemFunc': break;
|
||||||
|
|
||||||
@ -10471,9 +10487,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
|
|||||||
var supbooks = ([[]]);
|
var supbooks = ([[]]);
|
||||||
supbooks.sharedf = shared_formulae;
|
supbooks.sharedf = shared_formulae;
|
||||||
supbooks.arrayf = array_formulae;
|
supbooks.arrayf = array_formulae;
|
||||||
|
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
|
||||||
opts.supbooks = supbooks;
|
opts.supbooks = supbooks;
|
||||||
|
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
|
||||||
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
|
|
||||||
|
|
||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
@ -11104,7 +11120,7 @@ function check_wb(wb) {
|
|||||||
var wbnsregex = /<\w+:workbook/;
|
var wbnsregex = /<\w+:workbook/;
|
||||||
function parse_wb_xml(data, opts) {
|
function parse_wb_xml(data, opts) {
|
||||||
if(!data) throw new Error("Could not find file");
|
if(!data) throw new Error("Could not find file");
|
||||||
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:{'!names':[]}, xmlns: "" };
|
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, Names:[], xmlns: "" };
|
||||||
var pass = false, xmlns = "xmlns";
|
var pass = false, xmlns = "xmlns";
|
||||||
var dname = {}, dnstart = 0;
|
var dname = {}, dnstart = 0;
|
||||||
/*(data.match(tagregex)||[]).forEach */
|
/*(data.match(tagregex)||[]).forEach */
|
||||||
@ -11175,12 +11191,12 @@ function parse_wb_xml(data, opts) {
|
|||||||
dname = {};
|
dname = {};
|
||||||
dname.Name = y.name;
|
dname.Name = y.name;
|
||||||
if(y.comment) dname.Comment = y.comment;
|
if(y.comment) dname.Comment = y.comment;
|
||||||
|
if(y.localSheetId) dname.Sheet = +y.localSheetId;
|
||||||
dnstart = idx + x.length;
|
dnstart = idx + x.length;
|
||||||
} break;
|
} break;
|
||||||
case '</definedName>': {
|
case '</definedName>': {
|
||||||
dname.Ref = data.slice(dnstart, idx);
|
dname.Ref = data.slice(dnstart, idx);
|
||||||
wb.Names[dname.Name] = dname;
|
wb.Names.push(dname);
|
||||||
wb.Names['!names'].push(dname.Name);
|
|
||||||
} break;
|
} break;
|
||||||
case '<definedName/>': break;
|
case '<definedName/>': break;
|
||||||
|
|
||||||
@ -11286,7 +11302,19 @@ function write_wb_xml(wb, opts) {
|
|||||||
|
|
||||||
/* functionGroups */
|
/* functionGroups */
|
||||||
/* externalReferences */
|
/* externalReferences */
|
||||||
/* definedNames */
|
|
||||||
|
if(wb.Workbook && (wb.Workbook.Names||[]).length > 0) {
|
||||||
|
o[o.length] = "<definedNames>";
|
||||||
|
wb.Workbook.Names.forEach(function(n) {
|
||||||
|
var d = {name:n.Name};
|
||||||
|
if(n.Comment) d.comment = n.Comment;
|
||||||
|
if(n.Sheet != null) d.localSheetId = ""+n.Sheet;
|
||||||
|
if(!n.Ref) return;
|
||||||
|
o[o.length] = writextag('definedName', String(n.Ref), d);
|
||||||
|
});
|
||||||
|
o[o.length] = "</definedNames>";
|
||||||
|
}
|
||||||
|
|
||||||
/* calcPr */
|
/* calcPr */
|
||||||
/* oleSize */
|
/* oleSize */
|
||||||
/* customWorkbookViews */
|
/* customWorkbookViews */
|
||||||
@ -11358,7 +11386,9 @@ function parse_BrtName(data, length, opts) {
|
|||||||
// unusedstring2: XLNullableWideString
|
// unusedstring2: XLNullableWideString
|
||||||
//}
|
//}
|
||||||
data.l = end;
|
data.l = end;
|
||||||
return {Name:name, Ptg:formula, Comment:comment};
|
var out = ({Name:name, Ptg:formula, Comment:comment});
|
||||||
|
if(itab < 0xFFFFFFF) out.Sheet = itab;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [MS-XLSB] 2.1.7.60 Workbook */
|
/* [MS-XLSB] 2.1.7.60 Workbook */
|
||||||
@ -11369,15 +11399,20 @@ function parse_wb_bin(data, opts) {
|
|||||||
if(!opts) opts = {};
|
if(!opts) opts = {};
|
||||||
opts.biff = 12;
|
opts.biff = 12;
|
||||||
|
|
||||||
var Names = {}, NameList = [];
|
var Names = [];
|
||||||
|
var supbooks = [];
|
||||||
|
supbooks.SheetNames = [];
|
||||||
|
|
||||||
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
recordhopper(data, function hopper_wb(val, R_n, RT) {
|
||||||
switch(RT) {
|
switch(RT) {
|
||||||
case 0x009C: /* 'BrtBundleSh' */
|
case 0x009C: /* 'BrtBundleSh' */
|
||||||
|
supbooks.SheetNames.push(val.name);
|
||||||
wb.Sheets.push(val); break;
|
wb.Sheets.push(val); break;
|
||||||
|
|
||||||
case 0x0027: /* 'BrtName' */
|
case 0x0027: /* 'BrtName' */
|
||||||
Names[val.Name] = val; NameList.push(val.Name);
|
val.Ref = stringify_formula(val.Ptg, null, null, supbooks, opts);
|
||||||
|
delete val.Ptg;
|
||||||
|
Names.push(val);
|
||||||
break;
|
break;
|
||||||
case 0x040C: /* 'BrtNameExt' */ break;
|
case 0x040C: /* 'BrtNameExt' */ break;
|
||||||
|
|
||||||
@ -11436,7 +11471,6 @@ function parse_wb_bin(data, opts) {
|
|||||||
|
|
||||||
parse_wb_defaults(wb);
|
parse_wb_defaults(wb);
|
||||||
|
|
||||||
Names['!names'] = NameList;
|
|
||||||
// $FlowIgnore
|
// $FlowIgnore
|
||||||
wb.Names = Names;
|
wb.Names = Names;
|
||||||
|
|
||||||
@ -11935,7 +11969,17 @@ for(var cma = c; cma <= cc; ++cma) {
|
|||||||
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'NamedRange': break;
|
case 'NamedRange':
|
||||||
|
if(!Workbook.Names) Workbook.Names = [];
|
||||||
|
var _NamedRange = parsexmltag(Rn[0]);
|
||||||
|
var _DefinedName = {
|
||||||
|
Name: _NamedRange.Name,
|
||||||
|
Ref: rc_to_a1(_NamedRange.RefersTo.substr(1))
|
||||||
|
};
|
||||||
|
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
|
||||||
|
Workbook.Names.push(_DefinedName);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'NamedCell': break;
|
case 'NamedCell': break;
|
||||||
case 'B': break;
|
case 'B': break;
|
||||||
case 'I': break;
|
case 'I': break;
|
||||||
@ -12418,7 +12462,7 @@ function write_props_xlml(wb, opts) {
|
|||||||
/* DocumentProperties */
|
/* DocumentProperties */
|
||||||
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
|
||||||
/* CustomDocumentProperties */
|
/* CustomDocumentProperties */
|
||||||
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
|
if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops, opts));
|
||||||
return o.join("");
|
return o.join("");
|
||||||
}
|
}
|
||||||
/* TODO */
|
/* TODO */
|
||||||
@ -12736,15 +12780,17 @@ function parse_workbook(blob, options) {
|
|||||||
var colinfo = [], rowinfo = [];
|
var colinfo = [], rowinfo = [];
|
||||||
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
var defwidth = 0, defheight = 0; // twips / MDW respectively
|
||||||
var seencol = false;
|
var seencol = false;
|
||||||
var supbooks = ([[]]); // 1-indexed, will hold extern names
|
var supbooks = ([]); // 1-indexed, will hold extern names
|
||||||
var sbc = 0, sbci = 0, sbcli = 0;
|
|
||||||
supbooks.SheetNames = opts.snames;
|
supbooks.SheetNames = opts.snames;
|
||||||
supbooks.sharedf = opts.sharedf;
|
supbooks.sharedf = opts.sharedf;
|
||||||
supbooks.arrayf = opts.arrayf;
|
supbooks.arrayf = opts.arrayf;
|
||||||
|
supbooks.names = [];
|
||||||
|
supbooks.XTI = [];
|
||||||
var last_Rn = '';
|
var last_Rn = '';
|
||||||
var file_depth = 0; /* TODO: make a real stack */
|
var file_depth = 0; /* TODO: make a real stack */
|
||||||
var BIFF2Fmt = 0;
|
var BIFF2Fmt = 0;
|
||||||
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
|
||||||
|
var last_lbl;
|
||||||
|
|
||||||
/* explicit override for some broken writers */
|
/* explicit override for some broken writers */
|
||||||
opts.codepage = 1200;
|
opts.codepage = 1200;
|
||||||
@ -12817,18 +12863,35 @@ function parse_workbook(blob, options) {
|
|||||||
case 'RichTextStream': break;
|
case 'RichTextStream': break;
|
||||||
case 'BkHim': break;
|
case 'BkHim': break;
|
||||||
|
|
||||||
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
|
case 'SupBook':
|
||||||
case 'ExternName': supbooks[sbc][++sbci] = val; break;
|
supbooks.push([val]);
|
||||||
|
supbooks[supbooks.length-1].XTI = [];
|
||||||
|
break;
|
||||||
|
case 'ExternName':
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
|
break;
|
||||||
case 'Index': break; // TODO
|
case 'Index': break; // TODO
|
||||||
case 'Lbl':
|
case 'Lbl':
|
||||||
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
|
last_lbl = {
|
||||||
if(!supbooks[val.itab]) supbooks[val.itab] = [];
|
Name: val.Name,
|
||||||
supbooks[val.itab].push(val);
|
Ref: stringify_formula(val.rgce,range,null,supbooks,opts)
|
||||||
|
};
|
||||||
|
if(val.itab > 0) last_lbl.Sheet = val.itab - 1;
|
||||||
|
supbooks.names.push(last_lbl);
|
||||||
|
if(!supbooks[0]) supbooks[0] = [];
|
||||||
|
supbooks[supbooks.length-1].push(val);
|
||||||
if(val.Name == "\r" && val.itab > 0)
|
if(val.Name == "\r" && val.itab > 0)
|
||||||
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
if(val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
|
||||||
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
FilterDatabases[val.itab - 1] = { ref: encode_range(val.rgce[0][0][1][2]) };
|
||||||
break;
|
break;
|
||||||
case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break;
|
case 'ExternSheet':
|
||||||
|
if(supbooks.length == 0) { supbooks[0] = []; supbooks[0].XTI = []; }
|
||||||
|
supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val); supbooks.XTI = supbooks.XTI.concat(val); break;
|
||||||
|
case 'NameCmt':
|
||||||
|
/* TODO: search for correct name */
|
||||||
|
if(opts.biff < 8) break;
|
||||||
|
last_lbl.Comment = val[1];
|
||||||
|
break;
|
||||||
|
|
||||||
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
case 'Protect': out["!protect"] = val; break; /* for sheet or book */
|
||||||
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
|
||||||
@ -13051,7 +13114,6 @@ function parse_workbook(blob, options) {
|
|||||||
} break;
|
} break;
|
||||||
case 'Row': break; // TODO
|
case 'Row': break; // TODO
|
||||||
|
|
||||||
case 'NameCmt': break;
|
|
||||||
case 'Header': break; // TODO
|
case 'Header': break; // TODO
|
||||||
case 'Footer': break; // TODO
|
case 'Footer': break; // TODO
|
||||||
case 'HCenter': break; // TODO
|
case 'HCenter': break; // TODO
|
||||||
@ -13294,6 +13356,7 @@ function parse_workbook(blob, options) {
|
|||||||
if(opts.enc) wb.Encryption = opts.enc;
|
if(opts.enc) wb.Encryption = opts.enc;
|
||||||
wb.Metadata = {};
|
wb.Metadata = {};
|
||||||
if(country !== undefined) wb.Metadata.Country = country;
|
if(country !== undefined) wb.Metadata.Country = country;
|
||||||
|
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
|
||||||
wb.Workbook = Workbook;
|
wb.Workbook = Workbook;
|
||||||
return wb;
|
return wb;
|
||||||
}
|
}
|
||||||
@ -14454,7 +14517,7 @@ var XLSRecordEnum = {
|
|||||||
0x0890: { n:"TableStyleElement", f:parse_TableStyleElement },
|
0x0890: { n:"TableStyleElement", f:parse_TableStyleElement },
|
||||||
0x0892: { n:"StyleExt", f:parse_StyleExt },
|
0x0892: { n:"StyleExt", f:parse_StyleExt },
|
||||||
0x0893: { n:"NamePublish", f:parse_NamePublish },
|
0x0893: { n:"NamePublish", f:parse_NamePublish },
|
||||||
0x0894: { n:"NameCmt", f:parse_NameCmt },
|
0x0894: { n:"NameCmt", f:parse_NameCmt, r:12 },
|
||||||
0x0895: { n:"SortData", f:parse_SortData },
|
0x0895: { n:"SortData", f:parse_SortData },
|
||||||
0x0896: { n:"Theme", f:parse_Theme, r:12 },
|
0x0896: { n:"Theme", f:parse_Theme, r:12 },
|
||||||
0x0897: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
0x0897: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
|
||||||
|
Loading…
Reference in New Issue
Block a user