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:
SheetJS 2017-04-11 18:15:36 -04:00
parent 5187bc0b63
commit 0189bc23ca
18 changed files with 404 additions and 143 deletions

View File

@ -45,6 +45,8 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
+ [Chartsheet Object](#chartsheet-object)
* [Workbook Object](#workbook-object)
+ [Workbook File Properties](#workbook-file-properties)
* [Workbook-Level Attributes](#workbook-level-attributes)
+ [Defined Names](#defined-names)
* [Document Features](#document-features)
+ [Formulae](#formulae)
+ [Column Properties](#column-properties)
@ -669,6 +671,26 @@ Writers will process the `Props` key of the options object:
/* force the Author to be "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
Even for basic features like date storage, the official Excel formats store the

View File

@ -39,7 +39,7 @@ function xlml_write_docprops(Props, opts) {
case 'date': m = new Date(m).toISOString(); break;
}
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();
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
});

View File

@ -278,13 +278,6 @@ function parslurp(blob, length, cb) {
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 parseuint16(blob) { return blob.read_shift(2, 'u'); }

View File

@ -442,6 +442,7 @@ function parse_ExternName(blob, length, opts) {
if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
o.body = body || blob.read_shift(length-2);
if(typeof body === "string") o.Name = body;
return o;
}
@ -472,13 +473,21 @@ function parse_Lbl(blob, length, opts) {
/* 2.4.106 TODO: verify supbook manipulation */
function parse_ExternSheet(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 = [];
if(opts.sbcch === 0x0401) {
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
return oo;
}
else return o;
return o;
}
/* 2.4.176 TODO: check older biff */
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 */
@ -894,7 +903,6 @@ var parse_TableStyles = parsenoop;
var parse_TableStyle = parsenoop;
var parse_TableStyleElement = parsenoop;
var parse_NamePublish = parsenoop;
var parse_NameCmt = parsenoop;
var parse_SortData = parsenoop;
var parse_GUIDTypeLib = parsenoop;
var parse_FnGrp12 = parsenoop;

View File

@ -769,13 +769,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
break;
/* 2.5.198.88 */
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));
break;
case 'PtgRef3d': // TODO: lots of stuff
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**");
stack.push(sname + "!" + encode_cell(c));
sname = supbooks.SheetNames[ixti];
stack.push(sname + "!" + encode_cell_xls(c));
break;
/* 2.5.198.62 */
@ -831,7 +831,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
case 'PtgName':
/* f[1] = type, 0, nameindex */
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);
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[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;
/* TODO: Properly handle missing values */
//console.log(bookidx, supbooks);
if(opts.biff == 5) {
if(opts.biff <= 5) {
if(bookidx < 0) bookidx = -bookidx;
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
} else {
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
var pnxname = supbooks.SheetNames[bookidx];
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??"};
stack.push(externbook.body);
if(!externbook) externbook = {Name: "??NAMEX??"};
stack.push(externbook.Name);
break;
/* 2.5.198.80 */
@ -937,6 +949,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
/* 2.5.198.29 */
case 'PtgAreaErr': stack.push("#REF!"); break;
/* 2.5.198.30 */
case 'PtgAreaErr3d': stack.push("#REF!"); break;
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;

View File

@ -342,9 +342,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
var supbooks = ([[]]/*:any*/);
supbooks.sharedf = shared_formulae;
supbooks.arrayf = array_formulae;
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
opts.supbooks = supbooks;
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively

View File

@ -2,7 +2,7 @@
var wbnsregex = /<\w+:workbook/;
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
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 dname = {}, dnstart = 0;
/*(data.match(tagregex)||[]).forEach */
@ -73,12 +73,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
dname = {};
dname.Name = y.name;
if(y.comment) dname.Comment = y.comment;
if(y.localSheetId) dname.Sheet = +y.localSheetId;
dnstart = idx + x.length;
} break;
case '</definedName>': {
dname.Ref = data.slice(dnstart, idx);
wb.Names[dname.Name] = dname;
wb.Names['!names'].push(dname.Name);
wb.Names.push(dname);
} break;
case '<definedName/>': break;
@ -184,7 +184,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
/* functionGroups */
/* 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 */
/* oleSize */
/* customWorkbookViews */

View File

@ -55,7 +55,9 @@ function parse_BrtName(data, length, opts) {
// unusedstring2: XLNullableWideString
//}
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 */
@ -66,15 +68,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
if(!opts) opts = {};
opts.biff = 12;
var Names = {}, NameList = [];
var Names = [];
var supbooks = [];
supbooks.SheetNames = [];
recordhopper(data, function hopper_wb(val, R_n, RT) {
switch(RT) {
case 0x009C: /* 'BrtBundleSh' */
supbooks.SheetNames.push(val.name);
wb.Sheets.push(val); break;
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;
case 0x040C: /* 'BrtNameExt' */ break;
@ -133,7 +140,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
parse_wb_defaults(wb);
Names['!names'] = NameList;
// $FlowIgnore
wb.Names = Names;

View File

@ -312,7 +312,17 @@ function parse_xlml_xml(d, opts)/*:Workbook*/ {
for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = dup(csty);
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 'B': break;
case 'I': break;
@ -796,7 +806,7 @@ function write_props_xlml(wb, opts) {
/* DocumentProperties */
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
/* 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("");
}
/* TODO */

View File

@ -169,15 +169,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively
var seencol = false;
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
var sbc = 0, sbci = 0, sbcli = 0;
var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
supbooks.SheetNames = opts.snames;
supbooks.sharedf = opts.sharedf;
supbooks.arrayf = opts.arrayf;
supbooks.names = [];
supbooks.XTI = [];
var last_Rn = '';
var file_depth = 0; /* TODO: make a real stack */
var BIFF2Fmt = 0;
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
var last_lbl;
/* explicit override for some broken writers */
opts.codepage = 1200;
@ -250,18 +252,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'RichTextStream': break;
case 'BkHim': break;
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
case 'ExternName': supbooks[sbc][++sbci] = val; break;
case 'SupBook':
supbooks.push([val]);
supbooks[supbooks.length-1].XTI = [];
break;
case 'ExternName':
supbooks[supbooks.length-1].push(val);
break;
case 'Index': break; // TODO
case 'Lbl':
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
if(!supbooks[val.itab]) supbooks[val.itab] = [];
supbooks[val.itab].push(val);
last_lbl = {
Name: val.Name,
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.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]) };
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 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
@ -484,7 +503,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
} break;
case 'Row': break; // TODO
case 'NameCmt': break;
case 'Header': break; // TODO
case 'Footer': break; // TODO
case 'HCenter': break; // TODO
@ -727,6 +745,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(opts.enc) wb.Encryption = opts.enc;
wb.Metadata = {};
if(country !== undefined) wb.Metadata.Country = country;
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
wb.Workbook = Workbook;
return wb;
}

View File

@ -1103,7 +1103,7 @@ var XLSRecordEnum = {
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
/*::[*/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 },
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },

View File

@ -36,3 +36,4 @@ Writers will process the `Props` key of the options object:
/* force the Author to be "SheetJS" */
XLSX.write(wb, {Props:{Author:"SheetJS"}});
```

19
docbits/57_wbbook.md Normal file
View 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.

View File

@ -22,6 +22,8 @@
+ [Chartsheet Object](README.md#chartsheet-object)
* [Workbook Object](README.md#workbook-object)
+ [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)
+ [Formulae](README.md#formulae)
+ [Column Properties](README.md#column-properties)

View File

@ -1,7 +1,10 @@
# This file controls the multiformat tests
# vim: set ts=4:
# Format: <basename> <ext> <ext> [ext..]
# yes-formula
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
RkNumber .xls .xlsb .xlsx .xls.xml .xlsb.xml .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
# yes-csv
custom_properties .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
# no-formula (defined names)
defined_names_simple .xls .xlsb .xlsx .xml
# yes-formula
# no-csv (randbetween) note: ODS does not support many XLSX functions
formula_stress_test .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
formula_stress_test .xls .xlsb .xlsx .xlsb.xml
# yes-csv
formulae_test_simple .xls .xlsb .xlsx .xml
hyperlink_stress_test_2011 .xls .xlsb .xlsx .xml
#large_strings .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
merge_cells .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
# no-formula (defined names)
# no-formula (filename-references in XLSX encoding as [0])
named_ranges_2011 .xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml
# yes-formula
# no-csv (macro serialization in xml)

27
test.js
View File

@ -50,6 +50,10 @@ var paths = {
cstxlsx: dir + 'comments_stress_test.xlsx',
cstxlsb: dir + 'comments_stress_test.xlsb',
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',
fstxml: dir + 'formula_stress_test.xls.xml',
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() {
[
['xlsx', paths.afxlsx],

View File

@ -3273,7 +3273,7 @@ function xlml_write_docprops(Props, opts) {
case 'date': m = new Date(m).toISOString(); break;
}
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();
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
});
@ -3590,13 +3590,6 @@ function parslurp(blob, length, cb) {
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 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);
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
o.body = body || blob.read_shift(length-2);
if(typeof body === "string") o.Name = body;
return o;
}
@ -4230,13 +4224,21 @@ function parse_Lbl(blob, length, opts) {
/* 2.4.106 TODO: verify supbook manipulation */
function parse_ExternSheet(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 = [];
if(opts.sbcch === 0x0401) {
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
return oo;
}
else return o;
return o;
}
/* 2.4.176 TODO: check older biff */
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 */
@ -4652,7 +4654,6 @@ var parse_TableStyles = parsenoop;
var parse_TableStyle = parsenoop;
var parse_TableStyleElement = parsenoop;
var parse_NamePublish = parsenoop;
var parse_NameCmt = parsenoop;
var parse_SortData = parsenoop;
var parse_GUIDTypeLib = parsenoop;
var parse_FnGrp12 = parsenoop;
@ -8130,13 +8131,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
break;
/* 2.5.198.88 */
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));
break;
case 'PtgRef3d': // TODO: lots of stuff
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**");
stack.push(sname + "!" + encode_cell(c));
sname = supbooks.SheetNames[ixti];
stack.push(sname + "!" + encode_cell_xls(c));
break;
/* 2.5.198.62 */
@ -8192,7 +8193,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
case 'PtgName':
/* f[1] = type, 0, nameindex */
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);
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[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;
/* TODO: Properly handle missing values */
//console.log(bookidx, supbooks);
if(opts.biff == 5) {
if(opts.biff <= 5) {
if(bookidx < 0) bookidx = -bookidx;
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
} else {
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
var pnxname = supbooks.SheetNames[bookidx];
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??"};
stack.push(externbook.body);
if(!externbook) externbook = {Name: "??NAMEX??"};
stack.push(externbook.Name);
break;
/* 2.5.198.80 */
@ -8298,6 +8311,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
/* 2.5.198.29 */
case 'PtgAreaErr': stack.push("#REF!"); break;
/* 2.5.198.30 */
case 'PtgAreaErr3d': stack.push("#REF!"); break;
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;
@ -10528,9 +10544,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles)/*:Worksheet*/ {
var supbooks = ([[]]/*:any*/);
supbooks.sharedf = shared_formulae;
supbooks.arrayf = array_formulae;
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
opts.supbooks = supbooks;
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively
@ -11161,7 +11177,7 @@ function check_wb(wb) {
var wbnsregex = /<\w+:workbook/;
function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
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 dname = {}, dnstart = 0;
/*(data.match(tagregex)||[]).forEach */
@ -11232,12 +11248,12 @@ function parse_wb_xml(data, opts)/*:WorkbookFile*/ {
dname = {};
dname.Name = y.name;
if(y.comment) dname.Comment = y.comment;
if(y.localSheetId) dname.Sheet = +y.localSheetId;
dnstart = idx + x.length;
} break;
case '</definedName>': {
dname.Ref = data.slice(dnstart, idx);
wb.Names[dname.Name] = dname;
wb.Names['!names'].push(dname.Name);
wb.Names.push(dname);
} break;
case '<definedName/>': break;
@ -11343,7 +11359,19 @@ function write_wb_xml(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:string*/ {
/* functionGroups */
/* 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 */
/* oleSize */
/* customWorkbookViews */
@ -11415,7 +11443,9 @@ function parse_BrtName(data, length, opts) {
// unusedstring2: XLNullableWideString
//}
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 */
@ -11426,15 +11456,20 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
if(!opts) opts = {};
opts.biff = 12;
var Names = {}, NameList = [];
var Names = [];
var supbooks = [];
supbooks.SheetNames = [];
recordhopper(data, function hopper_wb(val, R_n, RT) {
switch(RT) {
case 0x009C: /* 'BrtBundleSh' */
supbooks.SheetNames.push(val.name);
wb.Sheets.push(val); break;
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;
case 0x040C: /* 'BrtNameExt' */ break;
@ -11493,7 +11528,6 @@ function parse_wb_bin(data, opts)/*:WorkbookFile*/ {
parse_wb_defaults(wb);
Names['!names'] = NameList;
// $FlowIgnore
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);
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 'B': break;
case 'I': break;
@ -12479,7 +12523,7 @@ function write_props_xlml(wb, opts) {
/* DocumentProperties */
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
/* 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("");
}
/* TODO */
@ -12797,15 +12841,17 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively
var seencol = false;
var supbooks = ([[]]/*:any*/); // 1-indexed, will hold extern names
var sbc = 0, sbci = 0, sbcli = 0;
var supbooks = ([]/*:any*/); // 1-indexed, will hold extern names
supbooks.SheetNames = opts.snames;
supbooks.sharedf = opts.sharedf;
supbooks.arrayf = opts.arrayf;
supbooks.names = [];
supbooks.XTI = [];
var last_Rn = '';
var file_depth = 0; /* TODO: make a real stack */
var BIFF2Fmt = 0;
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
var last_lbl;
/* explicit override for some broken writers */
opts.codepage = 1200;
@ -12878,18 +12924,35 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
case 'RichTextStream': break;
case 'BkHim': break;
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
case 'ExternName': supbooks[sbc][++sbci] = val; break;
case 'SupBook':
supbooks.push([val]);
supbooks[supbooks.length-1].XTI = [];
break;
case 'ExternName':
supbooks[supbooks.length-1].push(val);
break;
case 'Index': break; // TODO
case 'Lbl':
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
if(!supbooks[val.itab]) supbooks[val.itab] = [];
supbooks[val.itab].push(val);
last_lbl = {
Name: val.Name,
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.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]) };
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 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
@ -13112,7 +13175,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
} break;
case 'Row': break; // TODO
case 'NameCmt': break;
case 'Header': break; // TODO
case 'Footer': break; // TODO
case 'HCenter': break; // TODO
@ -13355,6 +13417,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(opts.enc) wb.Encryption = opts.enc;
wb.Metadata = {};
if(country !== undefined) wb.Metadata.Country = country;
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
wb.Workbook = Workbook;
return wb;
}
@ -14515,7 +14578,7 @@ var XLSRecordEnum = {
/*::[*/0x0890/*::]*/: { n:"TableStyleElement", f:parse_TableStyleElement },
/*::[*/0x0892/*::]*/: { n:"StyleExt", f:parse_StyleExt },
/*::[*/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 },
/*::[*/0x0896/*::]*/: { n:"Theme", f:parse_Theme, r:12 },
/*::[*/0x0897/*::]*/: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },

155
xlsx.js
View File

@ -3219,7 +3219,7 @@ function xlml_write_docprops(Props, opts) {
case 'date': m = new Date(m).toISOString(); break;
}
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();
o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
});
@ -3534,13 +3534,6 @@ function parslurp(blob, length, cb) {
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 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);
//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
o.body = body || blob.read_shift(length-2);
if(typeof body === "string") o.Name = body;
return o;
}
@ -4174,13 +4168,21 @@ function parse_Lbl(blob, length, opts) {
/* 2.4.106 TODO: verify supbook manipulation */
function parse_ExternSheet(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 = [];
if(opts.sbcch === 0x0401) {
for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
return oo;
}
else return o;
return o;
}
/* 2.4.176 TODO: check older biff */
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 */
@ -4596,7 +4598,6 @@ var parse_TableStyles = parsenoop;
var parse_TableStyle = parsenoop;
var parse_TableStyleElement = parsenoop;
var parse_NamePublish = parsenoop;
var parse_NameCmt = parsenoop;
var parse_SortData = parsenoop;
var parse_GUIDTypeLib = parsenoop;
var parse_FnGrp12 = parsenoop;
@ -8073,13 +8074,13 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
break;
/* 2.5.198.88 */
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));
break;
case 'PtgRef3d': // TODO: lots of stuff
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**");
stack.push(sname + "!" + encode_cell(c));
sname = supbooks.SheetNames[ixti];
stack.push(sname + "!" + encode_cell_xls(c));
break;
/* 2.5.198.62 */
@ -8135,7 +8136,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
case 'PtgName':
/* f[1] = type, 0, nameindex */
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);
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[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;
/* TODO: Properly handle missing values */
//console.log(bookidx, supbooks);
if(opts.biff == 5) {
if(opts.biff <= 5) {
if(bookidx < 0) bookidx = -bookidx;
if(supbooks[bookidx]) externbook = supbooks[bookidx][nameidx];
} else {
if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
var pnxname = supbooks.SheetNames[bookidx];
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??"};
stack.push(externbook.body);
if(!externbook) externbook = {Name: "??NAMEX??"};
stack.push(externbook.Name);
break;
/* 2.5.198.80 */
@ -8241,6 +8254,9 @@ function stringify_formula(formula/*Array<any>*/, range, cell, supbooks, opts) {
/* 2.5.198.29 */
case 'PtgAreaErr': stack.push("#REF!"); break;
/* 2.5.198.30 */
case 'PtgAreaErr3d': stack.push("#REF!"); break;
/* 2.5.198.72 TODO */
case 'PtgMemFunc': break;
@ -10471,9 +10487,9 @@ function parse_ws_bin(data, _opts, rels, wb, themes, styles) {
var supbooks = ([[]]);
supbooks.sharedf = shared_formulae;
supbooks.arrayf = array_formulae;
supbooks.SheetNames = wb.SheetNames || wb.Sheets.map(function(x) { return x.name; });
opts.supbooks = supbooks;
for(var i = 0; i < wb.Names['!names'].length; ++i) supbooks[0][i+1] = wb.Names[wb.Names['!names'][i]];
for(var i = 0; i < wb.Names.length; ++i) supbooks[0][i+1] = wb.Names[i];
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively
@ -11104,7 +11120,7 @@ function check_wb(wb) {
var wbnsregex = /<\w+:workbook/;
function parse_wb_xml(data, opts) {
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 dname = {}, dnstart = 0;
/*(data.match(tagregex)||[]).forEach */
@ -11175,12 +11191,12 @@ function parse_wb_xml(data, opts) {
dname = {};
dname.Name = y.name;
if(y.comment) dname.Comment = y.comment;
if(y.localSheetId) dname.Sheet = +y.localSheetId;
dnstart = idx + x.length;
} break;
case '</definedName>': {
dname.Ref = data.slice(dnstart, idx);
wb.Names[dname.Name] = dname;
wb.Names['!names'].push(dname.Name);
wb.Names.push(dname);
} break;
case '<definedName/>': break;
@ -11286,7 +11302,19 @@ function write_wb_xml(wb, opts) {
/* functionGroups */
/* 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 */
/* oleSize */
/* customWorkbookViews */
@ -11358,7 +11386,9 @@ function parse_BrtName(data, length, opts) {
// unusedstring2: XLNullableWideString
//}
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 */
@ -11369,15 +11399,20 @@ function parse_wb_bin(data, opts) {
if(!opts) opts = {};
opts.biff = 12;
var Names = {}, NameList = [];
var Names = [];
var supbooks = [];
supbooks.SheetNames = [];
recordhopper(data, function hopper_wb(val, R_n, RT) {
switch(RT) {
case 0x009C: /* 'BrtBundleSh' */
supbooks.SheetNames.push(val.name);
wb.Sheets.push(val); break;
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;
case 0x040C: /* 'BrtNameExt' */ break;
@ -11436,7 +11471,6 @@ function parse_wb_bin(data, opts) {
parse_wb_defaults(wb);
Names['!names'] = NameList;
// $FlowIgnore
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);
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 'B': break;
case 'I': break;
@ -12418,7 +12462,7 @@ function write_props_xlml(wb, opts) {
/* DocumentProperties */
if(wb.Props) o.push(xlml_write_docprops(wb.Props, opts));
/* 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("");
}
/* TODO */
@ -12736,15 +12780,17 @@ function parse_workbook(blob, options) {
var colinfo = [], rowinfo = [];
var defwidth = 0, defheight = 0; // twips / MDW respectively
var seencol = false;
var supbooks = ([[]]); // 1-indexed, will hold extern names
var sbc = 0, sbci = 0, sbcli = 0;
var supbooks = ([]); // 1-indexed, will hold extern names
supbooks.SheetNames = opts.snames;
supbooks.sharedf = opts.sharedf;
supbooks.arrayf = opts.arrayf;
supbooks.names = [];
supbooks.XTI = [];
var last_Rn = '';
var file_depth = 0; /* TODO: make a real stack */
var BIFF2Fmt = 0;
var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
var last_lbl;
/* explicit override for some broken writers */
opts.codepage = 1200;
@ -12817,18 +12863,35 @@ function parse_workbook(blob, options) {
case 'RichTextStream': break;
case 'BkHim': break;
case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
case 'ExternName': supbooks[sbc][++sbci] = val; break;
case 'SupBook':
supbooks.push([val]);
supbooks[supbooks.length-1].XTI = [];
break;
case 'ExternName':
supbooks[supbooks.length-1].push(val);
break;
case 'Index': break; // TODO
case 'Lbl':
supbooks[0][++sbcli] = val; // TODO: local formula storage in stringify_formula
if(!supbooks[val.itab]) supbooks[val.itab] = [];
supbooks[val.itab].push(val);
last_lbl = {
Name: val.Name,
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.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]) };
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 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
@ -13051,7 +13114,6 @@ function parse_workbook(blob, options) {
} break;
case 'Row': break; // TODO
case 'NameCmt': break;
case 'Header': break; // TODO
case 'Footer': break; // TODO
case 'HCenter': break; // TODO
@ -13294,6 +13356,7 @@ function parse_workbook(blob, options) {
if(opts.enc) wb.Encryption = opts.enc;
wb.Metadata = {};
if(country !== undefined) wb.Metadata.Country = country;
if(supbooks.names.length > 0) Workbook.Names = supbooks.names;
wb.Workbook = Workbook;
return wb;
}
@ -14454,7 +14517,7 @@ var XLSRecordEnum = {
0x0890: { n:"TableStyleElement", f:parse_TableStyleElement },
0x0892: { n:"StyleExt", f:parse_StyleExt },
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 },
0x0896: { n:"Theme", f:parse_Theme, r:12 },
0x0897: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },