diff --git a/bits/27_csfutils.js b/bits/27_csfutils.js
index a2e83e9..4be4099 100644
--- a/bits/27_csfutils.js
+++ b/bits/27_csfutils.js
@@ -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}};
diff --git a/bits/29_xlsenum.js b/bits/29_xlsenum.js
index bb5cec5..50c7b4c 100644
--- a/bits/29_xlsenum.js
+++ b/bits/29_xlsenum.js
@@ -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"
+];
+
diff --git a/bits/39_xlsbiff.js b/bits/39_xlsbiff.js
index fbe5c00..f4435b3 100644
--- a/bits/39_xlsbiff.js
+++ b/bits/39_xlsbiff.js
@@ -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);
diff --git a/bits/61_fcommon.js b/bits/61_fcommon.js
index 0e3d109..3171974 100644
--- a/bits/61_fcommon.js
+++ b/bits/61_fcommon.js
@@ -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;
});
};
diff --git a/bits/62_fxls.js b/bits/62_fxls.js
index 651f2e5..9174651 100644
--- a/bits/62_fxls.js
+++ b/bits/62_fxls.js
@@ -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];
diff --git a/bits/67_wsxml.js b/bits/67_wsxml.js
index 7ff9dba..9d61139 100644
--- a/bits/67_wsxml.js
+++ b/bits/67_wsxml.js
@@ -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});
diff --git a/bits/68_wsbin.js b/bits/68_wsbin.js
index 7d96929..245c97f 100644
--- a/bits/68_wsbin.js
+++ b/bits/68_wsbin.js
@@ -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 */
diff --git a/bits/71_wbcommon.js b/bits/71_wbcommon.js
index fa513e6..17d0f4f 100644
--- a/bits/71_wbcommon.js
+++ b/bits/71_wbcommon.js
@@ -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 */
}
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 92cef13..bf40b35 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -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('');
+
return o.join("");
}
function write_xlml(wb, opts)/*:string*/ {
diff --git a/demos/README.md b/demos/README.md
index 187077e..8efbb6f 100644
--- a/demos/README.md
+++ b/demos/README.md
@@ -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/)
diff --git a/demos/extendscript/.gitignore b/demos/extendscript/.gitignore
deleted file mode 100644
index 6b54784..0000000
--- a/demos/extendscript/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-xlsx.extendscript.js
diff --git a/demos/extendscript/Makefile b/demos/extendscript/Makefile
deleted file mode 100644
index 1906827..0000000
--- a/demos/extendscript/Makefile
+++ /dev/null
@@ -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 $^ > $@
diff --git a/demos/extendscript/README.md b/demos/extendscript/README.md
index 6a8c6e5..8e61928 100644
--- a/demos/extendscript/README.md
+++ b/demos/extendscript/README.md
@@ -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);
-```
-
-Implementation Details (click to show)
-
-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();
-```
-
-
-
-## 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)
diff --git a/demos/extendscript/aftereffects.base b/demos/extendscript/aftereffects.base
deleted file mode 100644
index 38ff0cc..0000000
--- a/demos/extendscript/aftereffects.base
+++ /dev/null
@@ -1 +0,0 @@
-#target aftereffects
diff --git a/demos/extendscript/aftereffects.jsx b/demos/extendscript/aftereffects.jsx
deleted file mode 100644
index f0aac56..0000000
--- a/demos/extendscript/aftereffects.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/demos/extendscript/estoolkit.base b/demos/extendscript/estoolkit.base
deleted file mode 100644
index 6ba53fa..0000000
--- a/demos/extendscript/estoolkit.base
+++ /dev/null
@@ -1 +0,0 @@
-#target estoolkit
diff --git a/demos/extendscript/estoolkit.jsx b/demos/extendscript/estoolkit.jsx
deleted file mode 100644
index e3e30ec..0000000
--- a/demos/extendscript/estoolkit.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/demos/extendscript/illustrator.base b/demos/extendscript/illustrator.base
deleted file mode 100644
index 9eb88bc..0000000
--- a/demos/extendscript/illustrator.base
+++ /dev/null
@@ -1 +0,0 @@
-#target illustrator
diff --git a/demos/extendscript/illustrator.jsx b/demos/extendscript/illustrator.jsx
deleted file mode 100644
index c1a16c4..0000000
--- a/demos/extendscript/illustrator.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/demos/extendscript/indesign.base b/demos/extendscript/indesign.base
deleted file mode 100644
index b6bab99..0000000
--- a/demos/extendscript/indesign.base
+++ /dev/null
@@ -1 +0,0 @@
-#target indesign
diff --git a/demos/extendscript/indesign.jsx b/demos/extendscript/indesign.jsx
deleted file mode 100644
index c95d017..0000000
--- a/demos/extendscript/indesign.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/demos/extendscript/photoshop.base b/demos/extendscript/photoshop.base
deleted file mode 100644
index 0da016f..0000000
--- a/demos/extendscript/photoshop.base
+++ /dev/null
@@ -1 +0,0 @@
-#target photoshop
diff --git a/demos/extendscript/photoshop.jsx b/demos/extendscript/photoshop.jsx
deleted file mode 100644
index 9e5ad45..0000000
--- a/demos/extendscript/photoshop.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/demos/extendscript/sheetjs.xlsx b/demos/extendscript/sheetjs.xlsx
deleted file mode 100755
index 19fe7a3..0000000
Binary files a/demos/extendscript/sheetjs.xlsx and /dev/null differ
diff --git a/demos/extendscript/test.jsx b/demos/extendscript/test.jsx
deleted file mode 100755
index b483229..0000000
--- a/demos/extendscript/test.jsx
+++ /dev/null
@@ -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);
-}
diff --git a/test.js b/test.js
index 88ff368..812c0c6 100644
--- a/test.js
+++ b/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*/ {
@@ -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$/); }
diff --git a/test.mjs b/test.mjs
index 3507769..ffee929 100644
--- a/test.mjs
+++ b/test.mjs
@@ -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*/ {
@@ -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$/); }
diff --git a/test.ts b/test.ts
index 592b367..6283f12 100644
--- a/test.ts
+++ b/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).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 {
@@ -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$/); }
diff --git a/tests/core.js b/tests/core.js
index 07ef735..cc6a8a7 100644
--- a/tests/core.js
+++ b/tests/core.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*/ {
@@ -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$/); }
diff --git a/xlsx.flow.js b/xlsx.flow.js
index 3c256fb..94adeaa 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -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 */
/* 14.2 Part Summary */
@@ -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('');
+
return o.join("");
}
function write_xlml(wb, opts)/*:string*/ {
diff --git a/xlsx.js b/xlsx.js
index 430fe33..8eb7742 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -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 */
/* 14.2 Part Summary */
@@ -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('');
+
return o.join("");
}
function write_xlml(wb, opts) {
diff --git a/xlsx.mjs b/xlsx.mjs
index 8a5a110..45c0ab6 100644
--- a/xlsx.mjs
+++ b/xlsx.mjs
@@ -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 */
/* 14.2 Part Summary */
@@ -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*/, 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('');
+
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']));