unified autofilter defined name sync on export

This commit is contained in:
SheetJS 2022-05-30 04:40:51 -04:00
parent 5d18f82664
commit 83ddb4c120
32 changed files with 510 additions and 508 deletions

@ -44,6 +44,17 @@ function encode_range(cs/*:CellAddrSpec|Range*/,ce/*:?CellAddrSpec*/)/*:string*/
/*:: if(typeof ce !== 'string') throw "unreachable"; */
return cs == ce ? cs : cs + ":" + ce;
}
function fix_range(a1/*:string*/)/*:string*/ {
var s = decode_range(a1);
return "$" + encode_col(s.s.c) + "$" + encode_row(s.s.r) + ":$" + encode_col(s.e.c) + "$" + encode_row(s.e.r);
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname.replace(/'/g, "''") + "'";
return sname;
}
function safe_decode_range(range/*:string*/)/*:Range*/ {
var o = {s:{c:0,r:0},e:{c:0,r:0}};

@ -304,3 +304,20 @@ var RBErr = {
"#WTF?": 0xFF
};
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];

@ -669,22 +669,6 @@ function parse_ExternName(blob, length, opts) {
}
/* [MS-XLS] 2.4.150 TODO */
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
function parse_Lbl(blob, length, opts) {
var target = blob.l + length;
var flags = blob.read_shift(2);

@ -29,8 +29,8 @@ var a1_to_rc = /*#__PURE__*/(function(){
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
var c = decode_col($3) - ($2 ? 0 : base.c);
var r = decode_row($5) - ($4 ? 0 : base.r);
var R = (r == 0 ? "" : !$4 ? "[" + r + "]" : (r+1));
var C = (c == 0 ? "" : !$2 ? "[" + c + "]" : (c+1));
var R = $4 == "$" ? (r+1) : (r == 0 ? "" : "[" + r + "]");
var C = $2 == "$" ? (c+1) : (c == 0 ? "" : "[" + c + "]");
return $1 + "R" + R + "C" + C;
});
};

@ -697,12 +697,6 @@ function make_3d_range(start, end) {
return start + ":" + end;
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname + "'";
return sname;
}
function get_ixti_raw(supbooks, ixti/*:number*/, opts)/*:string*/ {
if(!supbooks) return "SH33TJSERR0";
if(opts.biff > 8 && (!supbooks.XTI || !supbooks.XTI[ixti])) return supbooks.SheetNames[ixti];

@ -228,7 +228,7 @@ function write_ws_xml_autofilter(data, ws, wb, idx)/*:string*/ {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
return writextag("autoFilter", null, {ref:ref});

@ -938,9 +938,9 @@ function write_AUTOFILTER(ba, ws, wb, idx) {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref) });
write_record(ba, 0x00A1 /* BrtBeginAFilter */, write_UncheckedRfX(safe_decode_range(ref)));
/* *FILTERCOLUMN */

@ -140,5 +140,16 @@ function check_wb(wb) {
var Sheets = (wb.Workbook && wb.Workbook.Sheets) || [];
check_wb_names(wb.SheetNames, Sheets, !!wb.vbaraw);
for(var i = 0; i < wb.SheetNames.length; ++i) check_ws(wb.Sheets[wb.SheetNames[i]], wb.SheetNames[i], i);
wb.SheetNames.forEach(function(n, i) {
var ws = wb.Sheets[n];
if(!ws || !ws["!autofilter"]) return;
var DN;
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
wb.Workbook.Names.forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == i) DN = dn; });
var nn = formula_quote_sheet_name(n) + "!" + fix_range(ws["!autofilter"].ref);
if(DN) DN.Ref = nn;
else wb.Workbook.Names.push({Name: "_xlnm._FilterDatabase", Sheet: i, Ref: nn});
});
/* TODO: validate workbook */
}

@ -159,6 +159,10 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
if(cell.StyleID !== undefined) cell.ixfe = cell.StyleID;
}
function xlml_prefix_dname(dname) {
return XLSLblBuiltIn.indexOf("_xlnm." + dname) > -1 ? "_xlnm." + dname : dname;
}
function xlml_clean_comment(comment/*:any*/) {
comment.t = comment.v || "";
comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
@ -366,7 +370,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
if(!Workbook.Names) Workbook.Names = [];
var _NamedRange = parsexmltag(Rn[0]);
var _DefinedName/*:DefinedName*/ = ({
Name: _NamedRange.Name,
Name: xlml_prefix_dname(_NamedRange.Name),
Ref: rc_to_a1(_NamedRange.RefersTo.slice(1), {r:0, c:0})
}/*:any*/);
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
@ -956,7 +960,7 @@ function write_sty_xlml(wb, opts)/*:string*/ {
});
return writextag("Styles", styles.join(""));
}
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name.slice(0,6) == "_xlnm." ? n.Name.slice(6) : n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_names_xlml(wb/*::, opts*/)/*:string*/ {
if(!((wb||{}).Workbook||{}).Names) return "";
/*:: if(!wb || !wb.Workbook || !wb.Workbook.Names) throw new Error("unreachable"); */
@ -1209,6 +1213,8 @@ function write_ws_xlml(idx/*:number*/, opts, wb/*:Workbook*/)/*:string*/ {
/* WorksheetOptions */
o.push(write_ws_xlml_wsopts(ws, opts, idx, wb));
if(ws["!autofilter"]) o.push('<AutoFilter x:Range="' + a1_to_rc(fix_range(ws["!autofilter"].ref), {r:0,c:0}) + '" xmlns="urn:schemas-microsoft-com:office:excel"></AutoFilter>');
return o.join("");
}
function write_xlml(wb, opts)/*:string*/ {

@ -44,7 +44,7 @@ can be installed with Bash on Windows or with `cygwin`.
- [`nw.js application`](nwjs/)
- [`Chrome / Chromium extensions`](chrome/)
- [`Google Sheets API`](https://docs.sheetjs.com/docs/getting-started/demos/gsheet)
- [`Adobe ExtendScript`](extendscript/)
- [`Adobe Apps`](https://docs.sheetjs.com/docs/getting-started/demos/extendscript)
- [`Headless Browsers`](headless/)
- [`canvas-datagrid`](datagrid/)
- [`x-spreadsheet`](xspreadsheet/)

@ -1 +0,0 @@
xlsx.extendscript.js

@ -1,16 +0,0 @@
APPS= aftereffects estoolkit illustrator indesign photoshop
TARGETS=$(patsubst %,%.jsx,$(APPS))
.PHONY: all
all: deps $(TARGETS)
.PHONY: deps
deps:
cp ../../dist/xlsx.extendscript.js .
%.base:
echo "#target $*" > $@
.PHONY: $(TARGETS)
$(TARGETS):%.jsx:%.base test.jsx
cat $^ > $@

@ -1,89 +1,7 @@
# Adobe ExtendScript
ExtendScript adds some features to a limited form of ECMAScript version 3. With
the included shim, the library can run within Photoshop and other Adobe apps!
The main file is `test.jsx`. Target-specific files prepend target directives.
Copy the `test.jsx` file as well as the `xlsx.extendscript.js` library script
to wherever you want the scripts to reside.
## ExtendScript Quirks
There are numerous quirks in ExtendScript code parsing, especially related to
Boolean and bit operations. Most JS tooling will generate code that is not
compatible with ExtendScript. It is highly recommended to `#include` the `dist`
file directly and avoid trying to minify or pack as part of a larger project.
## File I/O
Using the `"binary"` encoding, file operations will work with binary strings
that play nice with the `"binary"` type of this library. The `readFile` and
`writeFile` library functions wrap the File logic:
```js
/* Read file from disk */
var workbook = XLSX.readFile(filename);
/* Write file to disk */
XLSX.writeFile(workbook, filename);
```
<details><summary><b>Implementation Details</b> (click to show)</summary>
The `readFile` and `writeFile` functions use `"binary"` encoding under the hood:
```js
/* Read file from disk without using readFile */
var infile = File(filename);
infile.open("r");
infile.encoding = "binary";
var data = infile.read();
var workbook = XLSX.read(data, {type:"binary"});
infile.close();
/* Write file to disk without using writeFile */
var outFile = File(filename);
outFile.open("w");
outFile.encoding = "binary";
outFile.write(workbook);
outFile.close();
```
</details>
## Demo
The demo shows:
- loading the library in ExtendScript using `#include`:
```js
#include "xlsx.extendscript.js"
```
- opening a file with `XLSX.readFile`:
```js
var workbook = XLSX.readFile("sheetjs.xlsx");
```
- converting a worksheet to an array of arrays:
```js
var first_sheet_name = workbook.SheetNames[0];
var first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
```
- writing a new workbook file:
```js
XLSX.writeFile(workbook, "sheetjs.slk");
```
[The new demo](https://docs.sheetjs.com/docs/getting-started/demos/extendscript)
has a more focused Photoshop example as well as notes about other extensibility
frameworks shipping with newer versions of Creative Cloud apps.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

@ -1 +0,0 @@
#target aftereffects

@ -1,45 +0,0 @@
#target aftereffects
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

@ -1 +0,0 @@
#target estoolkit

@ -1,45 +0,0 @@
#target estoolkit
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

@ -1 +0,0 @@
#target illustrator

@ -1,45 +0,0 @@
#target illustrator
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

@ -1 +0,0 @@
#target indesign

@ -1,45 +0,0 @@
#target indesign
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

@ -1 +0,0 @@
#target photoshop

@ -1,45 +0,0 @@
#target photoshop
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

Binary file not shown.

@ -1,44 +0,0 @@
var thisFile = new File($.fileName);
var basePath = thisFile.path;
#include "xlsx.extendscript.js";
var filename = "/sheetjs.xlsx";
/* Read file from disk */
var workbook = XLSX.readFile(basePath + filename, {cellDates:true});
/* Display first worksheet */
var first_sheet_name = workbook.SheetNames[0], first_worksheet = workbook.Sheets[first_sheet_name];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
alert(data);
var outfmts = [
["xlsb", "testw.xlsb"],
["biff8", "testw.xls"],
["xlml", "testw.xml"],
["fods", "testw.fods"],
["csv", "testw.csv"],
["txt", "testw.txt"],
["slk", "testw.slk"],
["eth", "testw.eth"],
["htm", "testw.htm"],
["dif", "testw.dif"],
["ods", "testw.ods"],
["xlsx", "testw.xlsx"]
];
for(var i = 0; i < outfmts.length; ++i) {
alert(outfmts[i][0]);
var fname = basePath + "/" + outfmts[i][1];
/* Write file to disk */
XLSX.writeFile(workbook, fname);
/* Read new file */
var wb = XLSX.readFile(fname, {cellDates:true});
/* Display first worksheet */
var f_sheet_name = wb.SheetNames[0], f_worksheet = wb.Sheets[f_sheet_name];
var data = XLSX.utils.sheet_to_json(f_worksheet, {header:1, cellDates:true});
alert(data);
}

38
test.js

@ -1483,6 +1483,27 @@ describe('write features', function() {
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
});
it('should create/update autofilter defined name on write', function() {[
"xlsx", "xlsb", /* "xls", */ "xlml" /*, "ods" */
].forEach(function(fmt) {
var wb = X.utils.book_new();
var ws = X.utils.aoa_to_sheet([["A","B","C"],[1,2,3]]);
ws["!autofilter"] = { ref: ws["!ref"] };
X.utils.book_append_sheet(wb, ws, "Sheet1");
var wb2 = X.read(X.write(wb, {bookType:fmt, type:TYPE}), {type:TYPE});
var Name = void 0;
((wb2.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert(!!Name, "Could not find _xlnm._FilterDatabases name WR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$2");
X.utils.sheet_add_aoa(wb2.Sheets.Sheet1, [[4,5,6]], { origin: -1 });
wb2.Sheets.Sheet1["!autofilter"].ref = wb2.Sheets.Sheet1["!ref"];
var wb3 = X.read(X.write(wb2, {bookType:fmt, type:TYPE}), {type:TYPE});
Name = void 0;
((wb3.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert(!!Name, "Could not find _xlnm._FilterDatabases name WRWR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$3");
assert.equal(wb2.Workbook.Names.length, wb3.Workbook.Names.length);
}); });
});
function seq(end/*:number*/, start/*:?number*/)/*:Array<number>*/ {
@ -1742,6 +1763,23 @@ describe('roundtrip features', function() {
Object.keys(row).forEach(function(k) { assert.equal(row[k], o[i][k]); });
});
});
it('should preserve autofilter settings', function() {[
['xlsx', paths.afxlsx],
['xlsb', paths.afxlsb],
// TODO:
//['xls', paths.afxls],
['xlml', paths.afxml]
//['ods', paths.afods]
].forEach(function(w) {
var wb = X.read(fs.readFileSync(w[1]), {type:TYPE});
var wb2 = X.read(X.write(wb, {bookType:w[0], type: TYPE}), {type:TYPE});
assert(!wb2.Sheets[wb2.SheetNames[0]]['!autofilter']);
for(var i = 1; i < wb2.SheetNames.length; ++i) {
assert(wb2.Sheets[wb2.SheetNames[i]]['!autofilter']);
assert.equal(wb2.Sheets[wb2.SheetNames[i]]['!autofilter'].ref,"A1:E22");
}
}); });
});
//function password_file(x){return x.match(/^password.*\.xls$/); }

40
test.mjs generated

@ -1473,6 +1473,27 @@ describe('write features', function() {
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
});
it('should create/update autofilter defined name on write', function() {[
"xlsx", "xlsb", /* "xls", */ "xlml" /*, "ods" */
].forEach(function(fmt) {
var wb = X.utils.book_new();
var ws = X.utils.aoa_to_sheet([["A","B","C"],[1,2,3]]);
ws["!autofilter"] = { ref: ws["!ref"] };
X.utils.book_append_sheet(wb, ws, "Sheet1");
var wb2 = X.read(X.write(wb, {bookType:fmt, type:TYPE}), {type:TYPE});
var Name = void 0;
((wb2.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert.ok(!!Name, "Could not find _xlnm._FilterDatabases name WR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$2");
X.utils.sheet_add_aoa(wb2.Sheets.Sheet1, [[4,5,6]], { origin: -1 });
wb2.Sheets.Sheet1["!autofilter"].ref = wb2.Sheets.Sheet1["!ref"];
var wb3 = X.read(X.write(wb2, {bookType:fmt, type:TYPE}), {type:TYPE});
Name = void 0;
((wb3.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert.ok(!!Name, "Could not find _xlnm._FilterDatabases name WRWR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$3");
assert.equal(wb2.Workbook.Names.length, wb3.Workbook.Names.length);
}); });
});
function seq(end/*:number*/, start/*:?number*/)/*:Array<number>*/ {
@ -1703,7 +1724,7 @@ describe('roundtrip features', function() {
describe('should preserve cell comments', function() { [
['xlsx', paths.cstxlsx],
['xlsb', paths.cstxlsb],
//['xls', paths.cstxlsx],
//['xls', paths.cstxls],
['xlml', paths.cstxml]
//['ods', paths.cstods]
].forEach(function(w) {
@ -1729,6 +1750,23 @@ describe('roundtrip features', function() {
Object.keys(row).forEach(function(k) { assert.equal(row[k], o[i][k]); });
});
});
it('should preserve autofilter settings', function() {[
['xlsx', paths.afxlsx],
['xlsb', paths.afxlsb],
// TODO:
//['xls', paths.afxls],
['xlml', paths.afxml]
//['ods', paths.afods]
].forEach(function(w) {
var wb = X.read(fs.readFileSync(w[1]), {type:TYPE});
var wb2 = X.read(X.write(wb, {bookType:w[0], type: TYPE}), {type:TYPE});
assert.ok(!wb2.Sheets[wb2.SheetNames[0]]['!autofilter']);
for(var i = 1; i < wb2.SheetNames.length; ++i) {
assert.ok(wb2.Sheets[wb2.SheetNames[i]]['!autofilter']);
assert.equal(wb2.Sheets[wb2.SheetNames[i]]['!autofilter'].ref,"A1:E22");
}
}); });
});
//function password_file(x){return x.match(/^password.*\.xls$/); }

38
test.ts

@ -1433,6 +1433,27 @@ Deno.test('write features', async function(t) {
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
});
await t.step('should create/update autofilter defined name on write', async function() {([
"xlsx", "xlsb", /* "xls", */ "xlml" /*, "ods" */
] as Array<X.BookType>).forEach(function(fmt) {
var wb = X.utils.book_new();
var ws = X.utils.aoa_to_sheet([["A","B","C"],[1,2,3]]);
ws["!autofilter"] = { ref: (ws["!ref"] as string) };
X.utils.book_append_sheet(wb, ws, "Sheet1");
var wb2 = X.read(X.write(wb, {bookType:fmt, type:TYPE}), {type:TYPE});
var Name: X.DefinedName = (null as any);
((wb2.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert.assert(!!Name, "Could not find _xlnm._FilterDatabases name WR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$2");
X.utils.sheet_add_aoa(wb2.Sheets.Sheet1, [[4,5,6]], { origin: -1 });
(wb2.Sheets.Sheet1["!autofilter"] as any).ref = wb2.Sheets.Sheet1["!ref"];
var wb3 = X.read(X.write(wb2, {bookType:fmt, type:TYPE}), {type:TYPE});
Name = (null as any);
((wb3.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert.assert(!!Name, "Could not find _xlnm._FilterDatabases name WRWR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$3");
assert.equal(((wb2.Workbook as any).Names as any).length, ((wb3.Workbook as any).Names as any).length);
}); });
});
function seq(end: number, start?: number): Array<number> {
@ -1689,6 +1710,23 @@ Deno.test('roundtrip features', async function(t) {
Object.keys(row).forEach(function(k) { assert.equal(row[k], o[i][k]); });
});
});
await t.step('should preserve autofilter settings', async function() {[
['xlsx', paths.afxlsx],
['xlsb', paths.afxlsb],
// TODO:
//['xls', paths.afxls],
['xlml', paths.afxml]
//['ods', paths.afods]
].forEach(function(w) {
var wb = X.read(fs.readFileSync(w[1]), {type:TYPE});
var wb2 = X.read(X.write(wb, {bookType:w[0], type: TYPE}), {type:TYPE});
assert.assert(!wb2.Sheets[wb2.SheetNames[0]]['!autofilter']);
for(var i = 1; i < wb2.SheetNames.length; ++i) {
assert.assert(wb2.Sheets[wb2.SheetNames[i]]['!autofilter']);
assert.equal((wb2.Sheets[wb2.SheetNames[i]]['!autofilter'] as any).ref,"A1:E22");
}
}); });
});
//function password_file(x){return x.match(/^password.*\.xls$/); }

40
tests/core.js generated

@ -1483,6 +1483,27 @@ describe('write features', function() {
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
});
it('should create/update autofilter defined name on write', function() {[
"xlsx", "xlsb", /* "xls", */ "xlml" /*, "ods" */
].forEach(function(fmt) {
var wb = X.utils.book_new();
var ws = X.utils.aoa_to_sheet([["A","B","C"],[1,2,3]]);
ws["!autofilter"] = { ref: ws["!ref"] };
X.utils.book_append_sheet(wb, ws, "Sheet1");
var wb2 = X.read(X.write(wb, {bookType:fmt, type:TYPE}), {type:TYPE});
var Name = void 0;
((wb2.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert(!!Name, "Could not find _xlnm._FilterDatabases name WR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$2");
X.utils.sheet_add_aoa(wb2.Sheets.Sheet1, [[4,5,6]], { origin: -1 });
wb2.Sheets.Sheet1["!autofilter"].ref = wb2.Sheets.Sheet1["!ref"];
var wb3 = X.read(X.write(wb2, {bookType:fmt, type:TYPE}), {type:TYPE});
Name = void 0;
((wb3.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert(!!Name, "Could not find _xlnm._FilterDatabases name WRWR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$3");
assert.equal(wb2.Workbook.Names.length, wb3.Workbook.Names.length);
}); });
});
function seq(end/*:number*/, start/*:?number*/)/*:Array<number>*/ {
@ -1716,7 +1737,7 @@ describe('roundtrip features', function() {
describe('should preserve cell comments', function() { [
['xlsx', paths.cstxlsx],
['xlsb', paths.cstxlsb],
//['xls', paths.cstxlsx],
//['xls', paths.cstxls],
['xlml', paths.cstxml]
//['ods', paths.cstods]
].forEach(function(w) {
@ -1742,6 +1763,23 @@ describe('roundtrip features', function() {
Object.keys(row).forEach(function(k) { assert.equal(row[k], o[i][k]); });
});
});
it('should preserve autofilter settings', function() {[
['xlsx', paths.afxlsx],
['xlsb', paths.afxlsb],
// TODO:
//['xls', paths.afxls],
['xlml', paths.afxml]
//['ods', paths.afods]
].forEach(function(w) {
var wb = X.read(fs.readFileSync(w[1]), {type:TYPE});
var wb2 = X.read(X.write(wb, {bookType:w[0], type: TYPE}), {type:TYPE});
assert(!wb2.Sheets[wb2.SheetNames[0]]['!autofilter']);
for(var i = 1; i < wb2.SheetNames.length; ++i) {
assert(wb2.Sheets[wb2.SheetNames[i]]['!autofilter']);
assert.equal(wb2.Sheets[wb2.SheetNames[i]]['!autofilter'].ref,"A1:E22");
}
}); });
});
//function password_file(x){return x.match(/^password.*\.xls$/); }

@ -4259,6 +4259,17 @@ function encode_range(cs/*:CellAddrSpec|Range*/,ce/*:?CellAddrSpec*/)/*:string*/
/*:: if(typeof ce !== 'string') throw "unreachable"; */
return cs == ce ? cs : cs + ":" + ce;
}
function fix_range(a1/*:string*/)/*:string*/ {
var s = decode_range(a1);
return "$" + encode_col(s.s.c) + "$" + encode_row(s.s.r) + ":$" + encode_col(s.e.c) + "$" + encode_row(s.e.r);
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname.replace(/'/g, "''") + "'";
return sname;
}
function safe_decode_range(range/*:string*/)/*:Range*/ {
var o = {s:{c:0,r:0},e:{c:0,r:0}};
@ -4999,6 +5010,23 @@ var RBErr = {
"#WTF?": 0xFF
};
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
/* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
/* 12.3 Part Summary <SpreadsheetML> */
/* 14.2 Part Summary <DrawingML> */
@ -7143,22 +7171,6 @@ function parse_ExternName(blob, length, opts) {
}
/* [MS-XLS] 2.4.150 TODO */
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
function parse_Lbl(blob, length, opts) {
var target = blob.l + length;
var flags = blob.read_shift(2);
@ -12417,8 +12429,8 @@ var a1_to_rc = /*#__PURE__*/(function(){
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
var c = decode_col($3) - ($2 ? 0 : base.c);
var r = decode_row($5) - ($4 ? 0 : base.r);
var R = (r == 0 ? "" : !$4 ? "[" + r + "]" : (r+1));
var C = (c == 0 ? "" : !$2 ? "[" + c + "]" : (c+1));
var R = $4 == "$" ? (r+1) : (r == 0 ? "" : "[" + r + "]");
var C = $2 == "$" ? (c+1) : (c == 0 ? "" : "[" + c + "]");
return $1 + "R" + R + "C" + C;
});
};
@ -13145,12 +13157,6 @@ function make_3d_range(start, end) {
return start + ":" + end;
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname + "'";
return sname;
}
function get_ixti_raw(supbooks, ixti/*:number*/, opts)/*:string*/ {
if(!supbooks) return "SH33TJSERR0";
if(opts.biff > 8 && (!supbooks.XTI || !supbooks.XTI[ixti])) return supbooks.SheetNames[ixti];
@ -15346,7 +15352,7 @@ function write_ws_xml_autofilter(data, ws, wb, idx)/*:string*/ {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
return writextag("autoFilter", null, {ref:ref});
@ -16711,9 +16717,9 @@ function write_AUTOFILTER(ba, ws, wb, idx) {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref) });
write_record(ba, 0x00A1 /* BrtBeginAFilter */, write_UncheckedRfX(safe_decode_range(ref)));
/* *FILTERCOLUMN */
@ -17092,6 +17098,17 @@ function check_wb(wb) {
var Sheets = (wb.Workbook && wb.Workbook.Sheets) || [];
check_wb_names(wb.SheetNames, Sheets, !!wb.vbaraw);
for(var i = 0; i < wb.SheetNames.length; ++i) check_ws(wb.Sheets[wb.SheetNames[i]], wb.SheetNames[i], i);
wb.SheetNames.forEach(function(n, i) {
var ws = wb.Sheets[n];
if(!ws || !ws["!autofilter"]) return;
var DN;
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
wb.Workbook.Names.forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == i) DN = dn; });
var nn = formula_quote_sheet_name(n) + "!" + fix_range(ws["!autofilter"].ref);
if(DN) DN.Ref = nn;
else wb.Workbook.Names.push({Name: "_xlnm._FilterDatabase", Sheet: i, Ref: nn});
});
/* TODO: validate workbook */
}
/* 18.2 Workbook */
@ -17911,6 +17928,10 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
if(cell.StyleID !== undefined) cell.ixfe = cell.StyleID;
}
function xlml_prefix_dname(dname) {
return XLSLblBuiltIn.indexOf("_xlnm." + dname) > -1 ? "_xlnm." + dname : dname;
}
function xlml_clean_comment(comment/*:any*/) {
comment.t = comment.v || "";
comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
@ -18118,7 +18139,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
if(!Workbook.Names) Workbook.Names = [];
var _NamedRange = parsexmltag(Rn[0]);
var _DefinedName/*:DefinedName*/ = ({
Name: _NamedRange.Name,
Name: xlml_prefix_dname(_NamedRange.Name),
Ref: rc_to_a1(_NamedRange.RefersTo.slice(1), {r:0, c:0})
}/*:any*/);
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
@ -18708,7 +18729,7 @@ function write_sty_xlml(wb, opts)/*:string*/ {
});
return writextag("Styles", styles.join(""));
}
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name.slice(0,6) == "_xlnm." ? n.Name.slice(6) : n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_names_xlml(wb/*::, opts*/)/*:string*/ {
if(!((wb||{}).Workbook||{}).Names) return "";
/*:: if(!wb || !wb.Workbook || !wb.Workbook.Names) throw new Error("unreachable"); */
@ -18961,6 +18982,8 @@ function write_ws_xlml(idx/*:number*/, opts, wb/*:Workbook*/)/*:string*/ {
/* WorksheetOptions */
o.push(write_ws_xlml_wsopts(ws, opts, idx, wb));
if(ws["!autofilter"]) o.push('<AutoFilter x:Range="' + a1_to_rc(fix_range(ws["!autofilter"].ref), {r:0,c:0}) + '" xmlns="urn:schemas-microsoft-com:office:excel"></AutoFilter>');
return o.join("");
}
function write_xlml(wb, opts)/*:string*/ {

81
xlsx.js generated

@ -4174,6 +4174,17 @@ if(typeof cs !== 'string') cs = encode_cell((cs));
if(typeof ce !== 'string') ce = encode_cell((ce));
return cs == ce ? cs : cs + ":" + ce;
}
function fix_range(a1) {
var s = decode_range(a1);
return "$" + encode_col(s.s.c) + "$" + encode_row(s.s.r) + ":$" + encode_col(s.e.c) + "$" + encode_row(s.e.r);
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname, opts) {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname.replace(/'/g, "''") + "'";
return sname;
}
function safe_decode_range(range) {
var o = {s:{c:0,r:0},e:{c:0,r:0}};
@ -4914,6 +4925,23 @@ var RBErr = {
"#WTF?": 0xFF
};
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
/* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
/* 12.3 Part Summary <SpreadsheetML> */
/* 14.2 Part Summary <DrawingML> */
@ -7054,22 +7082,6 @@ function parse_ExternName(blob, length, opts) {
}
/* [MS-XLS] 2.4.150 TODO */
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
function parse_Lbl(blob, length, opts) {
var target = blob.l + length;
var flags = blob.read_shift(2);
@ -12324,8 +12336,8 @@ var a1_to_rc = (function(){
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
var c = decode_col($3) - ($2 ? 0 : base.c);
var r = decode_row($5) - ($4 ? 0 : base.r);
var R = (r == 0 ? "" : !$4 ? "[" + r + "]" : (r+1));
var C = (c == 0 ? "" : !$2 ? "[" + c + "]" : (c+1));
var R = $4 == "$" ? (r+1) : (r == 0 ? "" : "[" + r + "]");
var C = $2 == "$" ? (c+1) : (c == 0 ? "" : "[" + c + "]");
return $1 + "R" + R + "C" + C;
});
};
@ -13051,12 +13063,6 @@ function make_3d_range(start, end) {
return start + ":" + end;
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname, opts) {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname + "'";
return sname;
}
function get_ixti_raw(supbooks, ixti, opts) {
if(!supbooks) return "SH33TJSERR0";
if(opts.biff > 8 && (!supbooks.XTI || !supbooks.XTI[ixti])) return supbooks.SheetNames[ixti];
@ -15252,7 +15258,7 @@ function write_ws_xml_autofilter(data, ws, wb, idx) {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
return writextag("autoFilter", null, {ref:ref});
@ -16616,9 +16622,9 @@ function write_AUTOFILTER(ba, ws, wb, idx) {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref) });
write_record(ba, 0x00A1 /* BrtBeginAFilter */, write_UncheckedRfX(safe_decode_range(ref)));
/* *FILTERCOLUMN */
@ -16997,6 +17003,17 @@ function check_wb(wb) {
var Sheets = (wb.Workbook && wb.Workbook.Sheets) || [];
check_wb_names(wb.SheetNames, Sheets, !!wb.vbaraw);
for(var i = 0; i < wb.SheetNames.length; ++i) check_ws(wb.Sheets[wb.SheetNames[i]], wb.SheetNames[i], i);
wb.SheetNames.forEach(function(n, i) {
var ws = wb.Sheets[n];
if(!ws || !ws["!autofilter"]) return;
var DN;
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
wb.Workbook.Names.forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == i) DN = dn; });
var nn = formula_quote_sheet_name(n) + "!" + fix_range(ws["!autofilter"].ref);
if(DN) DN.Ref = nn;
else wb.Workbook.Names.push({Name: "_xlnm._FilterDatabase", Sheet: i, Ref: nn});
});
/* TODO: validate workbook */
}
/* 18.2 Workbook */
@ -17812,6 +17829,10 @@ function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, arrayf, o
if(cell.StyleID !== undefined) cell.ixfe = cell.StyleID;
}
function xlml_prefix_dname(dname) {
return XLSLblBuiltIn.indexOf("_xlnm." + dname) > -1 ? "_xlnm." + dname : dname;
}
function xlml_clean_comment(comment) {
comment.t = comment.v || "";
comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
@ -18018,7 +18039,7 @@ for(var cma = c; cma <= cc; ++cma) {
if(!Workbook.Names) Workbook.Names = [];
var _NamedRange = parsexmltag(Rn[0]);
var _DefinedName = ({
Name: _NamedRange.Name,
Name: xlml_prefix_dname(_NamedRange.Name),
Ref: rc_to_a1(_NamedRange.RefersTo.slice(1), {r:0, c:0})
});
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
@ -18606,7 +18627,7 @@ function write_sty_xlml(wb, opts) {
});
return writextag("Styles", styles.join(""));
}
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name.slice(0,6) == "_xlnm." ? n.Name.slice(6) : n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_names_xlml(wb) {
if(!((wb||{}).Workbook||{}).Names) return "";
var names = wb.Workbook.Names;
@ -18857,6 +18878,8 @@ function write_ws_xlml(idx, opts, wb) {
/* WorksheetOptions */
o.push(write_ws_xlml_wsopts(ws, opts, idx, wb));
if(ws["!autofilter"]) o.push('<AutoFilter x:Range="' + a1_to_rc(fix_range(ws["!autofilter"].ref), {r:0,c:0}) + '" xmlns="urn:schemas-microsoft-com:office:excel"></AutoFilter>');
return o.join("");
}
function write_xlml(wb, opts) {

238
xlsx.mjs generated

@ -4254,6 +4254,17 @@ function encode_range(cs/*:CellAddrSpec|Range*/,ce/*:?CellAddrSpec*/)/*:string*/
/*:: if(typeof ce !== 'string') throw "unreachable"; */
return cs == ce ? cs : cs + ":" + ce;
}
function fix_range(a1/*:string*/)/*:string*/ {
var s = decode_range(a1);
return "$" + encode_col(s.s.c) + "$" + encode_row(s.s.r) + ":$" + encode_col(s.e.c) + "$" + encode_row(s.e.r);
}
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname.replace(/'/g, "''") + "'";
return sname;
}
function safe_decode_range(range/*:string*/)/*:Range*/ {
var o = {s:{c:0,r:0},e:{c:0,r:0}};
@ -4994,6 +5005,23 @@ var RBErr = {
"#WTF?": 0xFF
};
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
/* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
/* 12.3 Part Summary <SpreadsheetML> */
/* 14.2 Part Summary <DrawingML> */
@ -7138,22 +7166,6 @@ function parse_ExternName(blob, length, opts) {
}
/* [MS-XLS] 2.4.150 TODO */
var XLSLblBuiltIn = [
"_xlnm.Consolidate_Area",
"_xlnm.Auto_Open",
"_xlnm.Auto_Close",
"_xlnm.Extract",
"_xlnm.Database",
"_xlnm.Criteria",
"_xlnm.Print_Area",
"_xlnm.Print_Titles",
"_xlnm.Recorder",
"_xlnm.Data_Form",
"_xlnm.Auto_Activate",
"_xlnm.Auto_Deactivate",
"_xlnm.Sheet_Title",
"_xlnm._FilterDatabase"
];
function parse_Lbl(blob, length, opts) {
var target = blob.l + length;
var flags = blob.read_shift(2);
@ -12412,8 +12424,8 @@ var a1_to_rc = /*#__PURE__*/(function(){
return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) {
var c = decode_col($3) - ($2 ? 0 : base.c);
var r = decode_row($5) - ($4 ? 0 : base.r);
var R = (r == 0 ? "" : !$4 ? "[" + r + "]" : (r+1));
var C = (c == 0 ? "" : !$2 ? "[" + c + "]" : (c+1));
var R = $4 == "$" ? (r+1) : (r == 0 ? "" : "[" + r + "]");
var C = $2 == "$" ? (c+1) : (c == 0 ? "" : "[" + c + "]");
return $1 + "R" + R + "C" + C;
});
};
@ -13131,12 +13143,15 @@ var PtgBinOp = {
PtgSub: "-"
};
// List of invalid characters needs to be tested further
function formula_quote_sheet_name(sname/*:string*/, opts)/*:string*/ {
if(!sname && !(opts && opts.biff <= 5 && opts.biff >= 2)) throw new Error("empty sheet name");
if (/[^\w\u4E00-\u9FFF\u3040-\u30FF]/.test(sname)) return "'" + sname + "'";
return sname;
// TODO: explore space
function make_3d_range(start, end) {
var s = start.lastIndexOf("!"), e = end.lastIndexOf("!");
if(s == -1 && e == -1) return start + ":" + end;
if(s > 0 && e > 0 && start.slice(0, s).toLowerCase() == end.slice(0, e).toLowerCase()) return start + ":" + end.slice(e+1);
console.error("Cannot hydrate range", start, end);
return start + ":" + end;
}
function get_ixti_raw(supbooks, ixti/*:number*/, opts)/*:string*/ {
if(!supbooks) return "SH33TJSERR0";
if(opts.biff > 8 && (!supbooks.XTI || !supbooks.XTI[ixti])) return supbooks.SheetNames[ixti];
@ -13233,7 +13248,7 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
break;
case 'PtgRange': /* [MS-XLS] 2.5.198.83 */
e1 = stack.pop(); e2 = stack.pop();
stack.push(e2+":"+e1);
stack.push(make_3d_range(e2,e1));
break;
case 'PtgAttrChoose': /* [MS-XLS] 2.5.198.34 */
@ -13679,7 +13694,7 @@ function write_XLSBFormulaRef3D(str, wb) {
var out = new_buf(17);
out.write_shift(4, 9);
out.write_shift(1, 0x1A | ((1)<<5));
out.write_shift(2, 1 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(2, 2 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(4, cell.r);
out.write_shift(2, cell.c | ((str.charAt(0) == "$" ? 0 : 1)<<14) | ((str.match(/\$\d/) ? 0 : 1)<<15)); // <== ColRelShort
out.write_shift(4, 0);
@ -13687,13 +13702,121 @@ function write_XLSBFormulaRef3D(str, wb) {
return out;
}
/* Writes a PtgRefErr3d */
function write_XLSBFormulaRefErr3D(str, wb) {
var lastbang = str.lastIndexOf("!");
var sname = str.slice(0, lastbang);
str = str.slice(lastbang+1);
if(sname.charAt(0) == "'") sname = sname.slice(1, -1).replace(/''/g, "'");
var out = new_buf(17);
out.write_shift(4, 9);
out.write_shift(1, 0x1C | ((1)<<5));
out.write_shift(2, 2 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(4, 0);
out.write_shift(2, 0); // <== ColRelShort
out.write_shift(4, 0);
return out;
}
/* Writes a single sheet range [PtgRef PtgRef PtgRange] */
function write_XLSBFormulaRange(_str) {
var parts = _str.split(":"), str = parts[0];
var out = new_buf(23);
out.write_shift(4, 15);
/* start cell */
str = parts[0]; var cell = decode_cell(str);
out.write_shift(1, 0x04 | ((1)<<5));
out.write_shift(4, cell.r);
out.write_shift(2, cell.c | ((str.charAt(0) == "$" ? 0 : 1)<<14) | ((str.match(/\$\d/) ? 0 : 1)<<15)); // <== ColRelShort
out.write_shift(4, 0);
/* end cell */
str = parts[1]; cell = decode_cell(str);
out.write_shift(1, 0x04 | ((1)<<5));
out.write_shift(4, cell.r);
out.write_shift(2, cell.c | ((str.charAt(0) == "$" ? 0 : 1)<<14) | ((str.match(/\$\d/) ? 0 : 1)<<15)); // <== ColRelShort
out.write_shift(4, 0);
/* PtgRange */
out.write_shift(1, 0x11);
out.write_shift(4, 0);
return out;
}
/* Writes a range with explicit sheet name [PtgRef3D PtgRef3D PtgRange] */
function write_XLSBFormulaRangeWS(_str, wb) {
var lastbang = _str.lastIndexOf("!");
var sname = _str.slice(0, lastbang);
_str = _str.slice(lastbang+1);
if(sname.charAt(0) == "'") sname = sname.slice(1, -1).replace(/''/g, "'");
var parts = _str.split(":"); str = parts[0];
var out = new_buf(27);
out.write_shift(4, 19);
/* start cell */
var str = parts[0], cell = decode_cell(str);
out.write_shift(1, 0x1A | ((1)<<5));
out.write_shift(2, 2 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(4, cell.r);
out.write_shift(2, cell.c | ((str.charAt(0) == "$" ? 0 : 1)<<14) | ((str.match(/\$\d/) ? 0 : 1)<<15)); // <== ColRelShort
/* end cell */
str = parts[1]; cell = decode_cell(str);
out.write_shift(1, 0x1A | ((1)<<5));
out.write_shift(2, 2 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(4, cell.r);
out.write_shift(2, cell.c | ((str.charAt(0) == "$" ? 0 : 1)<<14) | ((str.match(/\$\d/) ? 0 : 1)<<15)); // <== ColRelShort
/* PtgRange */
out.write_shift(1, 0x11);
out.write_shift(4, 0);
return out;
}
/* Writes a range with explicit sheet name [PtgArea3d] */
function write_XLSBFormulaArea3D(_str, wb) {
var lastbang = _str.lastIndexOf("!");
var sname = _str.slice(0, lastbang);
_str = _str.slice(lastbang+1);
if(sname.charAt(0) == "'") sname = sname.slice(1, -1).replace(/''/g, "'");
var range = decode_range(_str);
var out = new_buf(23);
out.write_shift(4, 15);
out.write_shift(1, 0x1B | ((1)<<5));
out.write_shift(2, 2 + wb.SheetNames.map(function(n) { return n.toLowerCase(); }).indexOf(sname.toLowerCase()));
out.write_shift(4, range.s.r);
out.write_shift(4, range.e.r);
out.write_shift(2, range.s.c);
out.write_shift(2, range.e.c);
out.write_shift(4, 0);
return out;
}
/* General Formula */
function write_XLSBFormula(val/*:string*/, wb) {
if(/^#(DIV\/0!|GETTING_DATA|N\/A|NAME\?|NULL!|NUM!|REF!|VALUE!)$/.test(val)) return write_XLSBFormulaErr(+RBErr[val]);
if(val.match(/^\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaRef(val);
if(val.match(/^(?:'[^\\\/?*\[\]:]*'|[^'][^\\\/?*\[\]:'`~!@#$%^()\-_=+{}|;,<.>]*)!\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaRef3D(val, wb);
if(val.match(/^".*"$/)) return write_XLSBFormulaStr(val);
if(val.match(/^\d+$/)) return write_XLSBFormulaNum(parseInt(val, 10));
if(val.match(/^\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5}):\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaRange(val);
if(val.match(/^#REF!\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5}):\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaArea3D(val, wb);
if(val.match(/^(?:'[^\\\/?*\[\]:]*'|[^'][^\\\/?*\[\]:'`~!@#$%^()\-=+{}|;,<.>]*)!\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaRef3D(val, wb);
if(val.match(/^(?:'[^\\\/?*\[\]:]*'|[^'][^\\\/?*\[\]:'`~!@#$%^()\-=+{}|;,<.>]*)!\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5}):\$?(?:[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D]|[A-Z]{1,2})\$?(?:10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})$/)) return write_XLSBFormulaRangeWS(val, wb);
if(/^(?:'[^\\\/?*\[\]:]*'|[^'][^\\\/?*\[\]:'`~!@#$%^()\-=+{}|;,<.>]*)!#REF!$/.test(val)) return write_XLSBFormulaRefErr3D(val, wb);
if(/^".*"$/.test(val)) return write_XLSBFormulaStr(val);
if(/^[+-]\d+$/.test(val)) return write_XLSBFormulaNum(parseInt(val, 10));
throw "Formula |" + val + "| not supported for XLSB";
}
var write_XLSBNameParsedFormula = write_XLSBFormula;
@ -14841,6 +14964,8 @@ function ods_to_csf_formula(f/*:string*/)/*:string*/ {
f = f.replace(/COM\.MICROSOFT\./g, "");
/* Part 3 Section 5.8 References */
f = f.replace(/\[((?:\.[A-Z]+[0-9]+)(?::\.[A-Z]+[0-9]+)?)\]/g, function($$, $1) { return $1.replace(/\./g,""); });
f = f.replace(/\$'([^']|'')+'/g, function($$) { return $$.slice(1); });
f = f.replace(/\$([^\]\. #$]+)/g, function($$, $1) { return ($1).match(/^([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])?(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})?$/) ? $$ : $1; });
/* TODO: something other than this */
f = f.replace(/\[.(#[A-Z]*[?!])\]/g, "$1");
return f.replace(/[;~]/g,",").replace(/\|/g,";");
@ -14853,6 +14978,8 @@ function csf_to_ods_formula(f/*:string*/)/*:string*/ {
}
function ods_to_csf_3D(r/*:string*/)/*:[string, string]*/ {
r = r.replace(/\$'([^']|'')+'/g, function($$) { return $$.slice(1); });
r = r.replace(/\$([^\]\. #$]+)/g, function($$, $1) { return ($1).match(/^([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])?(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})?$/) ? $$ : $1; });
var a = r.split(":");
var s = a[0].split(".")[0];
return [s, a[0].split(".")[1] + (a.length > 1 ? (":" + (a[1].split(".")[1] || a[1].split(".")[0])) : "")];
@ -15220,7 +15347,7 @@ function write_ws_xml_autofilter(data, ws, wb, idx)/*:string*/ {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
return writextag("autoFilter", null, {ref:ref});
@ -16585,9 +16712,9 @@ function write_AUTOFILTER(ba, ws, wb, idx) {
var name = names[i];
if(name.Name != '_xlnm._FilterDatabase') continue;
if(name.Sheet != idx) continue;
name.Ref = "'" + wb.SheetNames[idx] + "'!" + ref; break;
name.Ref = formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref); break;
}
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: "'" + wb.SheetNames[idx] + "'!" + ref });
if(i == names.length) names.push({ Name: '_xlnm._FilterDatabase', Sheet: idx, Ref: formula_quote_sheet_name(wb.SheetNames[idx]) + "!" + fix_range(ref) });
write_record(ba, 0x00A1 /* BrtBeginAFilter */, write_UncheckedRfX(safe_decode_range(ref)));
/* *FILTERCOLUMN */
@ -16966,6 +17093,17 @@ function check_wb(wb) {
var Sheets = (wb.Workbook && wb.Workbook.Sheets) || [];
check_wb_names(wb.SheetNames, Sheets, !!wb.vbaraw);
for(var i = 0; i < wb.SheetNames.length; ++i) check_ws(wb.Sheets[wb.SheetNames[i]], wb.SheetNames[i], i);
wb.SheetNames.forEach(function(n, i) {
var ws = wb.Sheets[n];
if(!ws || !ws["!autofilter"]) return;
var DN;
if(!wb.Workbook) wb.Workbook = {};
if(!wb.Workbook.Names) wb.Workbook.Names = [];
wb.Workbook.Names.forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == i) DN = dn; });
var nn = formula_quote_sheet_name(n) + "!" + fix_range(ws["!autofilter"].ref);
if(DN) DN.Ref = nn;
else wb.Workbook.Names.push({Name: "_xlnm._FilterDatabase", Sheet: i, Ref: nn});
});
/* TODO: validate workbook */
}
/* 18.2 Workbook */
@ -17281,12 +17419,13 @@ function parse_BrtFRTArchID$(data, length) {
/* [MS-XLSB] 2.4.687 BrtName */
function parse_BrtName(data, length, opts) {
var end = data.l + length;
data.l += 4; //var flags = data.read_shift(4);
var flags = data.read_shift(4);
data.l += 1; //var chKey = data.read_shift(1);
var itab = data.read_shift(4);
var name = parse_XLNameWideString(data);
var formula = parse_XLSBNameParsedFormula(data, 0, opts);
var comment = parse_XLNullableWideString(data);
if(flags & 0x20) name = "_xlnm." + name;
//if(0 /* fProc */) {
// unusedstring1: XLNullableWideString
// description: XLNullableWideString
@ -17294,23 +17433,26 @@ function parse_BrtName(data, length, opts) {
// unusedstring2: XLNullableWideString
//}
data.l = end;
var out = ({Name:name, Ptg:formula}/*:any*/);
var out = ({Name:name, Ptg:formula, Flags: flags}/*:any*/);
if(itab < 0xFFFFFFF) out.Sheet = itab;
if(comment) out.Comment = comment;
return out;
}
function write_BrtName(name, wb) {
var o = new_buf(9);
o.write_shift(4, 0); // flags
var flags = 0;
var dname = name.Name;
if(XLSLblBuiltIn.indexOf(dname) > -1) { flags |= 0x20; dname = dname.slice(6); }
o.write_shift(4, flags); // flags
o.write_shift(1, 0); // chKey
o.write_shift(4, name.Sheet == null ? 0xFFFFFFFF : name.Sheet);
var arr = [
o,
write_XLWideString(name.Name),
write_XLSBNameParsedFormula(name.Ref, wb),
write_XLWideString(dname),
write_XLSBNameParsedFormula(name.Ref, wb)
];
if(name.Comment) arr.push(write_XLNullableWideString(name.Comment))
if(name.Comment) arr.push(write_XLNullableWideString(name.Comment));
else {
var x = new_buf(4);
x.write_shift(4, 0xFFFFFFFF);
@ -17323,7 +17465,7 @@ function write_BrtName(name, wb) {
// write_XLNullableWideString(helpTopic)
// write_shift(4, 0xFFFFFFFF);
return bconcat(arr);
return bconcat(arr);
}
/* [MS-XLSB] 2.1.7.61 Workbook */
@ -17492,6 +17634,7 @@ function write_BOOKVIEWS(ba, wb/*::, opts*/) {
function write_BRTNAMES(ba, wb) {
if(!wb.Workbook || !wb.Workbook.Names) return;
wb.Workbook.Names.forEach(function(name) { try {
if(name.Flags & 0x0e) return; // TODO: macro name write
write_record(ba, 0x0027 /* BrtName */, write_BrtName(name, wb));
} catch(e) {
console.error("Could not serialize defined name " + JSON.stringify(name));
@ -17500,9 +17643,10 @@ function write_BRTNAMES(ba, wb) {
function write_SELF_EXTERNS_xlsb(wb) {
var L = wb.SheetNames.length;
var o = new_buf(12 * L + 16);
o.write_shift(4, L + 1);
var o = new_buf(12 * L + 28);
o.write_shift(4, L + 2);
o.write_shift(4, 0); o.write_shift(4, -2); o.write_shift(4, -2); // workbook-level reference
o.write_shift(4, 0); o.write_shift(4, -1); o.write_shift(4, -1); // #REF!...
for(var i = 0; i < L; ++i) {
o.write_shift(4, 0); o.write_shift(4, i); o.write_shift(4, i);
}
@ -17779,6 +17923,10 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a
if(cell.StyleID !== undefined) cell.ixfe = cell.StyleID;
}
function xlml_prefix_dname(dname) {
return XLSLblBuiltIn.indexOf("_xlnm." + dname) > -1 ? "_xlnm." + dname : dname;
}
function xlml_clean_comment(comment/*:any*/) {
comment.t = comment.v || "";
comment.t = comment.t.replace(/\r\n/g,"\n").replace(/\r/g,"\n");
@ -17986,7 +18134,7 @@ function parse_xlml_xml(d, _opts)/*:Workbook*/ {
if(!Workbook.Names) Workbook.Names = [];
var _NamedRange = parsexmltag(Rn[0]);
var _DefinedName/*:DefinedName*/ = ({
Name: _NamedRange.Name,
Name: xlml_prefix_dname(_NamedRange.Name),
Ref: rc_to_a1(_NamedRange.RefersTo.slice(1), {r:0, c:0})
}/*:any*/);
if(Workbook.Sheets.length>0) _DefinedName.Sheet=Workbook.Sheets.length-1;
@ -18576,7 +18724,7 @@ function write_sty_xlml(wb, opts)/*:string*/ {
});
return writextag("Styles", styles.join(""));
}
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_name_xlml(n) { return writextag("NamedRange", null, {"ss:Name": n.Name.slice(0,6) == "_xlnm." ? n.Name.slice(6) : n.Name, "ss:RefersTo":"=" + a1_to_rc(n.Ref, {r:0,c:0})}); }
function write_names_xlml(wb/*::, opts*/)/*:string*/ {
if(!((wb||{}).Workbook||{}).Names) return "";
/*:: if(!wb || !wb.Workbook || !wb.Workbook.Names) throw new Error("unreachable"); */
@ -18829,6 +18977,8 @@ function write_ws_xlml(idx/*:number*/, opts, wb/*:Workbook*/)/*:string*/ {
/* WorksheetOptions */
o.push(write_ws_xlml_wsopts(ws, opts, idx, wb));
if(ws["!autofilter"]) o.push('<AutoFilter x:Range="' + a1_to_rc(fix_range(ws["!autofilter"].ref), {r:0,c:0}) + '" xmlns="urn:schemas-microsoft-com:office:excel"></AutoFilter>');
return o.join("");
}
function write_xlml(wb, opts)/*:string*/ {
@ -21164,6 +21314,7 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
/* ... */
if(b8) ws['!links'] = [];
var comments = [];
for(var R = range.s.r; R <= range.e.r; ++R) {
rr = encode_row(R);
for(var C = range.s.c; C <= range.e.c; ++C) {
@ -21174,10 +21325,13 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
/* write cell */
write_ws_biff8_cell(ba, cell, R, C, opts);
if(b8 && cell.l) ws['!links'].push([ref, cell.l]);
if(b8 && cell.c) comments.push([ref, cell.c]);
}
}
var cname/*:string*/ = _sheet.CodeName || _sheet.name || s;
/* ... */
// if(b8) comments.forEach(function(comment) { write_biff_rec(ba, 0x001c /* Note */, write_NoteSh(comment)); });
/* ... */
if(b8) write_biff_rec(ba, 0x023e /* Window2 */, write_Window2((_WB.Views||[])[0]));
/* ... */
if(b8 && (ws['!merges']||[]).length) write_biff_rec(ba, 0x00e5 /* MergeCells */, write_MergeCells(ws['!merges']));