From 2ff31276b0184188dbc56f9766cb2bace38644db Mon Sep 17 00:00:00 2001 From: SheetJS Date: Mon, 9 May 2022 02:49:17 -0400 Subject: [PATCH] slk defined name parse --- README.md | 11 +++++++++++ bits/40_harb.js | 18 +++++++++++++----- bits/41_lotus.js | 2 +- docbits/57_wbbook.md | 11 +++++++++++ misc/docs/README.md | 8 ++++++++ test.js | 7 ++++++- test.ts | 10 ++++++++-- test_files | 2 +- tests/fixtures.lst | 1 + xlsx.flow.js | 40 +++++++++++++++++++++++++++++++--------- xlsx.js | 40 +++++++++++++++++++++++++++++++--------- xlsx.mjs | 40 +++++++++++++++++++++++++++++++--------- 12 files changed, 153 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 319b492..d0e1ae1 100644 --- a/README.md +++ b/README.md @@ -2588,6 +2588,17 @@ XLSX.write(wb, {Props:{Author:"SheetJS"}}); #### Defined Names +
+ Format Support (click to show) + +**Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK + +**Unicode Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML + +**Defined Name Comment**: XLSX/M, XLSB, BIFF8 XLS + +
+ `wb.Workbook.Names` is an array of defined name objects which have the keys:
diff --git a/bits/40_harb.js b/bits/40_harb.js index 895fc9d..e4096c7 100644 --- a/bits/40_harb.js +++ b/bits/40_harb.js @@ -391,7 +391,7 @@ var SYLK = /*#__PURE__*/(function() { var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; }; var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); }; sylk_escapes["|"] = 254; - /* TODO: find an actual specification */ + /* https://oss.sheetjs.com/notes/sylk/ for more details */ function sylk_to_aoa(d/*:RawData*/, opts)/*:[AOA, Worksheet]*/ { switch(opts.type) { case 'base64': return sylk_to_aoa_str(Base64_decode(d), opts); @@ -407,7 +407,7 @@ var SYLK = /*#__PURE__*/(function() { var next_cell_format/*:string|null*/ = null; var sht = {}, rowinfo/*:Array*/ = [], colinfo/*:Array*/ = [], cw/*:Array*/ = []; var Mval = 0, j; - var wb = { Workbook: { WBProps: {} } }; + var wb = { Workbook: { WBProps: {}, Names: [] } }; if(+opts.codepage >= 0) set_cp(+opts.codepage); for (; ri !== records.length; ++ri) { Mval = 0; @@ -426,12 +426,20 @@ var SYLK = /*#__PURE__*/(function() { if(d1904 >= 1 && d1904 <= 4) wb.Workbook.WBProps.date1904 = true; } break; } break; - case 'W': break; /* window? */ + case 'W': break; /* window */ case 'P': if(record[1].charAt(0) == 'P') formats.push(rstr.slice(3).replace(/;;/g, ";")); break; - case 'C': + case 'NN': { /* defined name */ + var nn = {Sheet: 0}; + for(rj=1; rj + Format Support (click to show) + +**Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK + +**Unicode Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML + +**Defined Name Comment**: XLSX/M, XLSB, BIFF8 XLS + +
+ `wb.Workbook.Names` is an array of defined name objects which have the keys:
diff --git a/misc/docs/README.md b/misc/docs/README.md index 3c5e6c6..25be7a2 100644 --- a/misc/docs/README.md +++ b/misc/docs/README.md @@ -2438,6 +2438,14 @@ XLSX.write(wb, {Props:{Author:"SheetJS"}}); #### Defined Names + +**Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML, SYLK + +**Unicode Defined Names**: XLSX/M, XLSB, BIFF8 XLS, XLML + +**Defined Name Comment**: XLSX/M, XLSB, BIFF8 XLS + + `wb.Workbook.Names` is an array of defined name objects which have the keys: diff --git a/test.js b/test.js index 46fb6d3..b44a691 100644 --- a/test.js +++ b/test.js @@ -132,6 +132,7 @@ var paths = { dnsxml: dir + 'defined_names_simple.xml', dnsxlsx: dir + 'defined_names_simple.xlsx', dnsxlsb: dir + 'defined_names_simple.xlsb', + dnsslk: dir + 'defined_names_simple.slk', dnuxls: dir + 'defined_names_unicode.xls', dnuxml: dir + 'defined_names_unicode.xml', @@ -1183,15 +1184,19 @@ describe('parse features', function() { ['xlsx', paths.dnsxlsx, true], ['xlsb', paths.dnsxlsb, true], ['xls', paths.dnsxls, true], - ['xlml', paths.dnsxml, false] + ['xlml', paths.dnsxml, false], + ['slk', paths.dnsslk, false] ].forEach(function(m) { it(m[0], function() { var wb = X.read(fs.readFileSync(m[1]), {type:TYPE}); var names = wb.Workbook.Names; + + if(m[0] != 'slk') { for(var i = 0; i < names.length; ++i) if(names[i].Name == "SheetJS") break; assert(i < names.length, "Missing name"); assert.equal(names[i].Sheet, null); assert.equal(names[i].Ref, "Sheet1!$A$1"); if(m[2]) assert.equal(names[i].Comment, "defined names just suck excel formulae are bad MS should feel bad"); + } for(i = 0; i < names.length; ++i) if(names[i].Name == "SHEETjs") break; assert(i < names.length, "Missing name"); diff --git a/test.ts b/test.ts index 068f8fa..e46509a 100644 --- a/test.ts +++ b/test.ts @@ -154,6 +154,7 @@ var paths: any = { dnsxml: dir + 'defined_names_simple.xml', dnsxlsx: dir + 'defined_names_simple.xlsx', dnsxlsb: dir + 'defined_names_simple.xlsb', + dnsslk: dir + 'defined_names_simple.slk', dnuxls: dir + 'defined_names_unicode.xls', dnuxml: dir + 'defined_names_unicode.xml', @@ -1158,15 +1159,20 @@ Deno.test('parse features', async function(t) { ['xlsx', paths.dnsxlsx, true], ['xlsb', paths.dnsxlsb, true], ['xls', paths.dnsxls, true], - ['xlml', paths.dnsxml, false] + ['xlml', paths.dnsxml, false], + ['slk', paths.dnsslk, false] ] as Array<[string, string, boolean]>; for(var i = 0; i < dnp.length; ++i) { let m: [string, string, boolean] = dnp[i]; await t.step(m[0], async function(t) { var wb = X.read(fs.readFileSync(m[1]), {type:TYPE}); var names = wb?.Workbook?.Names; - if(names) {for(var i = 0; i < names?.length; ++i) if(names[i].Name == "SheetJS") break; + + if(names) { + if(m[0] != 'slk') { + for(var i = 0; i < names?.length; ++i) if(names[i].Name == "SheetJS") break; assert.assert(i < names?.length, "Missing name"); assert.equal(names[i].Sheet, void 0); assert.equal(names[i].Ref, "Sheet1!$A$1"); if(m[2]) assert.equal(names[i].Comment, "defined names just suck excel formulae are bad MS should feel bad"); + } for(i = 0; i < names.length; ++i) if(names[i].Name == "SHEETjs") break; assert.assert(i < names.length, "Missing name"); diff --git a/test_files b/test_files index 59a8103..50158c2 160000 --- a/test_files +++ b/test_files @@ -1 +1 @@ -Subproject commit 59a810302a68b26d6c9c3f9c4e7f499b0fdd6d37 +Subproject commit 50158c2288492df6f6eb4037568023a3de3e0ef9 diff --git a/tests/fixtures.lst b/tests/fixtures.lst index c106a70..361e323 100644 --- a/tests/fixtures.lst +++ b/tests/fixtures.lst @@ -62,6 +62,7 @@ ./test_files/defined_names_simple.xml ./test_files/defined_names_simple.xlsx ./test_files/defined_names_simple.xlsb +./test_files/defined_names_simple.slk ./test_files/defined_names_unicode.xls ./test_files/defined_names_unicode.xml ./test_files/defined_names_unicode.ods diff --git a/xlsx.flow.js b/xlsx.flow.js index 2f35514..add347c 100644 --- a/xlsx.flow.js +++ b/xlsx.flow.js @@ -7942,7 +7942,7 @@ var SYLK = /*#__PURE__*/(function() { var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; }; var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); }; sylk_escapes["|"] = 254; - /* TODO: find an actual specification */ + /* https://oss.sheetjs.com/notes/sylk/ for more details */ function sylk_to_aoa(d/*:RawData*/, opts)/*:[AOA, Worksheet]*/ { switch(opts.type) { case 'base64': return sylk_to_aoa_str(Base64_decode(d), opts); @@ -7958,7 +7958,7 @@ var SYLK = /*#__PURE__*/(function() { var next_cell_format/*:string|null*/ = null; var sht = {}, rowinfo/*:Array*/ = [], colinfo/*:Array*/ = [], cw/*:Array*/ = []; var Mval = 0, j; - var wb = { Workbook: { WBProps: {} } }; + var wb = { Workbook: { WBProps: {}, Names: [] } }; if(+opts.codepage >= 0) set_cp(+opts.codepage); for (; ri !== records.length; ++ri) { Mval = 0; @@ -7977,12 +7977,20 @@ var SYLK = /*#__PURE__*/(function() { if(d1904 >= 1 && d1904 <= 4) wb.Workbook.WBProps.date1904 = true; } break; } break; - case 'W': break; /* window? */ + case 'W': break; /* window */ case 'P': if(record[1].charAt(0) == 'P') formats.push(rstr.slice(3).replace(/;;/g, ";")); break; - case 'C': + case 'NN': { /* defined name */ + var nn = {Sheet: 0}; + for(rj=1; rj*/ { /* 6.1.2 White Space Characters */ var fixed = text @@ -24084,6 +24101,11 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { case 0x7B: if(n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return RTF.to_workbook(d, o); break; case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o); case 0x89: if(n[1] === 0x50 && n[2] === 0x4E && n[3] === 0x47) throw new Error("PNG Image File is not a spreadsheet"); break; + case 0x08: if(n[1] === 0xE7) throw new Error("Unsupported Multiplan 1.x file!"); break; + case 0x0C: + if(n[1] === 0xEC) throw new Error("Unsupported Multiplan 2.x file!"); + if(n[1] === 0xED) throw new Error("Unsupported Multiplan 3.x file!"); + break; } if(DBF_SUPPORTED_VERSIONS.indexOf(n[0]) > -1 && n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); return read_prn(data, d, o, str); diff --git a/xlsx.js b/xlsx.js index e2979aa..5cc5758 100644 --- a/xlsx.js +++ b/xlsx.js @@ -7852,7 +7852,7 @@ var SYLK = (function() { var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; }; var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); }; sylk_escapes["|"] = 254; - /* TODO: find an actual specification */ + /* https://oss.sheetjs.com/notes/sylk/ for more details */ function sylk_to_aoa(d, opts) { switch(opts.type) { case 'base64': return sylk_to_aoa_str(Base64_decode(d), opts); @@ -7868,7 +7868,7 @@ var SYLK = (function() { var next_cell_format = null; var sht = {}, rowinfo = [], colinfo = [], cw = []; var Mval = 0, j; - var wb = { Workbook: { WBProps: {} } }; + var wb = { Workbook: { WBProps: {}, Names: [] } }; if(+opts.codepage >= 0) set_cp(+opts.codepage); for (; ri !== records.length; ++ri) { Mval = 0; @@ -7887,12 +7887,20 @@ var SYLK = (function() { if(d1904 >= 1 && d1904 <= 4) wb.Workbook.WBProps.date1904 = true; } break; } break; - case 'W': break; /* window? */ + case 'W': break; /* window */ case 'P': if(record[1].charAt(0) == 'P') formats.push(rstr.slice(3).replace(/;;/g, ";")); break; - case 'C': + case 'NN': { /* defined name */ + var nn = {Sheet: 0}; + for(rj=1; rj -1 && n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); return read_prn(data, d, o, str); diff --git a/xlsx.mjs b/xlsx.mjs index 37d4ac1..7d8798f 100644 --- a/xlsx.mjs +++ b/xlsx.mjs @@ -7930,7 +7930,7 @@ var SYLK = /*#__PURE__*/(function() { var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; }; var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); }; sylk_escapes["|"] = 254; - /* TODO: find an actual specification */ + /* https://oss.sheetjs.com/notes/sylk/ for more details */ function sylk_to_aoa(d/*:RawData*/, opts)/*:[AOA, Worksheet]*/ { switch(opts.type) { case 'base64': return sylk_to_aoa_str(Base64_decode(d), opts); @@ -7946,7 +7946,7 @@ var SYLK = /*#__PURE__*/(function() { var next_cell_format/*:string|null*/ = null; var sht = {}, rowinfo/*:Array*/ = [], colinfo/*:Array*/ = [], cw/*:Array*/ = []; var Mval = 0, j; - var wb = { Workbook: { WBProps: {} } }; + var wb = { Workbook: { WBProps: {}, Names: [] } }; if(+opts.codepage >= 0) set_cp(+opts.codepage); for (; ri !== records.length; ++ri) { Mval = 0; @@ -7965,12 +7965,20 @@ var SYLK = /*#__PURE__*/(function() { if(d1904 >= 1 && d1904 <= 4) wb.Workbook.WBProps.date1904 = true; } break; } break; - case 'W': break; /* window? */ + case 'W': break; /* window */ case 'P': if(record[1].charAt(0) == 'P') formats.push(rstr.slice(3).replace(/;;/g, ";")); break; - case 'C': + case 'NN': { /* defined name */ + var nn = {Sheet: 0}; + for(rj=1; rj*/ { /* 6.1.2 White Space Characters */ var fixed = text @@ -24072,6 +24089,11 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { case 0x7B: if(n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return RTF.to_workbook(d, o); break; case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o); case 0x89: if(n[1] === 0x50 && n[2] === 0x4E && n[3] === 0x47) throw new Error("PNG Image File is not a spreadsheet"); break; + case 0x08: if(n[1] === 0xE7) throw new Error("Unsupported Multiplan 1.x file!"); break; + case 0x0C: + if(n[1] === 0xEC) throw new Error("Unsupported Multiplan 2.x file!"); + if(n[1] === 0xED) throw new Error("Unsupported Multiplan 3.x file!"); + break; } if(DBF_SUPPORTED_VERSIONS.indexOf(n[0]) > -1 && n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o); return read_prn(data, d, o, str);