From f215b8f79f8919dbf73cf230d3c5495f619750af Mon Sep 17 00:00:00 2001 From: SheetJS Date: Thu, 11 Jul 2024 01:33:25 -0400 Subject: [PATCH] merge nits (fixes #3142 h/t @s-ashwin ) --- bits/47_styxml.js | 22 ++++-- bits/80_parseods.js | 29 +++++--- bits/83_numbers.js | 34 ++++++++- modules/83_numbers.js | 34 ++++++++- modules/83_numbers.ts | 45 ++++++++++++ test.mts | 29 ++++---- test.ts | 29 ++++---- testnocp.ts | 165 +++++++++++++++++++++++++++++++++++++++++- 8 files changed, 338 insertions(+), 49 deletions(-) diff --git a/bits/47_styxml.js b/bits/47_styxml.js index 4a671a1..72f20d3 100644 --- a/bits/47_styxml.js +++ b/bits/47_styxml.js @@ -163,10 +163,12 @@ function parse_fonts(t, styles, themes, opts) { /* 18.8.2 b CT_BooleanProperty */ case '': font.bold = 1; break; + case '': case '': font.italic = 1; break; + case '': case '': font.underline = 1; break; + case '': case '': font.strike = 1; break; + case '': case '': font.outline = 1; break; + case '': case '': font.shadow = 1; break; + case '': case '': font.condense = 1; break; + case '': case '': font.extend = 1; break; + case '': case '': case '': break; + case '': case '': case '': case '': break; + case '': case '': case '': case '': break; + case '': case '': case '': case '': break; + case '': case '': case '': case '': break; + case '': case '': case '': case '': break; + case '': case '': case '': pass = false; break; + case '': case '': case '': break; diff --git a/bits/80_parseods.js b/bits/80_parseods.js index 688f315..769d88e 100644 --- a/bits/80_parseods.js +++ b/bits/80_parseods.js @@ -306,10 +306,18 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ { if(rowpeat < 10) for(i = 0; i < rowpeat; ++i) if(row_ol > 0) rowinfo[R + i] = {level: row_ol}; C = -1; break; case 'covered-table-cell': // 9.1.5 - if(Rn[1] !== '/') ++C; - if(opts.sheetStubs) { - if(opts.dense) { if(!ws["!data"][R]) ws["!data"][R] = []; ws["!data"][R][C] = {t:'z'}; } - else ws[encode_cell({r:R,c:C})] = {t:'z'}; + if(Rn[1] !== '/') { + ++C; + ctag = parsexmltag(Rn[0], false); + colpeat = parseInt(ctag['number-columns-repeated']||"1",10) || 1; + if(opts.sheetStubs) { + while(colpeat-- > 0) { + if(opts.dense) { if(!ws["!data"][R]) ws["!data"][R] = []; ws["!data"][R][C] = {t:'z'}; } + else ws[encode_cell({r:R,c:C})] = {t:'z'}; + ++C; + } --C; + } + else C += colpeat - 1; } textp = ""; textR = []; break; /* stub */ @@ -317,7 +325,7 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ { if(Rn[0].charAt(Rn[0].length-2) === '/') { ++C; ctag = parsexmltag(Rn[0], false); - colpeat = parseInt(ctag['number-columns-repeated']||"1", 10); + colpeat = parseInt(ctag['number-columns-repeated']||"1", 10)||1; q = ({t:'z', v:null/*:: , z:null, w:"",c:[]*/}/*:any*/); if(ctag.formula && opts.cellFormula != false) q.f = ods_to_csf_formula(unescapexml(ctag.formula)); if(ctag["style-name"] && styles[ctag["style-name"]]) q.z = styles[ctag["style-name"]]; @@ -361,10 +369,12 @@ function parse_content_xml(d/*:string*/, _opts, _nfm)/*:Workbook*/ { q.F = arrayf[i][1]; } if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) { - mR = parseInt(ctag['number-rows-spanned'],10) || 0; - mC = parseInt(ctag['number-columns-spanned'],10) || 0; - mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}}; - merges.push(mrange); + mR = parseInt(ctag['number-rows-spanned']||"1",10) || 1; + mC = parseInt(ctag['number-columns-spanned']||"1",10) || 1; + if(mR * mC > 1) { + mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}}; + merges.push(mrange); + } } /* 19.675.2 table:number-columns-repeated */ @@ -796,4 +806,3 @@ function parse_fods(data/*:string*/, opts/*:?ParseOpts*/)/*:Workbook*/ { wb.bookType = "fods"; return wb; } - diff --git a/bits/83_numbers.js b/bits/83_numbers.js index fc7b32c..e29f95e 100644 --- a/bits/83_numbers.js +++ b/bits/83_numbers.js @@ -1117,7 +1117,7 @@ function s5s_to_iwa_comment(s5s) { return out; } function parse_TST_TableModelArchive(M, root, ws, opts) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; var pb = parse_shallow(root.data); var range = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } }; range.e.r = (varint_to_i32(pb[6][0].data) >>> 0) - 1; @@ -1196,6 +1196,38 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { }; }); } + if ((_k = pb[47]) == null ? void 0 : _k[0]) { + var merge_owner = parse_shallow(pb[47][0].data); + if ((_l = merge_owner[2]) == null ? void 0 : _l[0]) { + var formula_store = parse_shallow(merge_owner[2][0].data); + if (((_m = formula_store[3]) == null ? void 0 : _m[0]) && !ws["!merges"]) { + ws["!merges"] = mappa(formula_store[3], function(u) { + var _a2, _b2, _c2, _d2, _e2; + var formula_pair = parse_shallow(u); + var formula = parse_shallow(formula_pair[2][0].data); + var AST_node_array = parse_shallow(formula[1][0].data); + if (!((_a2 = AST_node_array[1]) == null ? void 0 : _a2[0])) + return; + var AST_node0 = parse_shallow(AST_node_array[1][0].data); + var AST_node_type = varint_to_i32(AST_node0[1][0].data); + if (AST_node_type != 67) + return; + var AST_colon_tract = parse_shallow(AST_node0[40][0].data); + if (!((_b2 = AST_colon_tract[3]) == null ? void 0 : _b2[0]) || !((_c2 = AST_colon_tract[4]) == null ? void 0 : _c2[0])) + return; + var colrange = parse_shallow(AST_colon_tract[3][0].data); + var rowrange = parse_shallow(AST_colon_tract[4][0].data); + var c = varint_to_i32(colrange[1][0].data); + var C = ((_d2 = colrange[2]) == null ? void 0 : _d2[0]) ? varint_to_i32(colrange[2][0].data) : c; + var r = varint_to_i32(rowrange[1][0].data); + var R = ((_e2 = rowrange[2]) == null ? void 0 : _e2[0]) ? varint_to_i32(rowrange[2][0].data) : r; + return { s: { r: r, c: c }, e: { r: R, c: C } }; + }).filter(function(x) { + return x != null; + }); + } + } + } } function parse_TST_TableInfoArchive(M, root, opts) { var pb = parse_shallow(root.data); diff --git a/modules/83_numbers.js b/modules/83_numbers.js index fc7b32c..e29f95e 100644 --- a/modules/83_numbers.js +++ b/modules/83_numbers.js @@ -1117,7 +1117,7 @@ function s5s_to_iwa_comment(s5s) { return out; } function parse_TST_TableModelArchive(M, root, ws, opts) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; var pb = parse_shallow(root.data); var range = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } }; range.e.r = (varint_to_i32(pb[6][0].data) >>> 0) - 1; @@ -1196,6 +1196,38 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { }; }); } + if ((_k = pb[47]) == null ? void 0 : _k[0]) { + var merge_owner = parse_shallow(pb[47][0].data); + if ((_l = merge_owner[2]) == null ? void 0 : _l[0]) { + var formula_store = parse_shallow(merge_owner[2][0].data); + if (((_m = formula_store[3]) == null ? void 0 : _m[0]) && !ws["!merges"]) { + ws["!merges"] = mappa(formula_store[3], function(u) { + var _a2, _b2, _c2, _d2, _e2; + var formula_pair = parse_shallow(u); + var formula = parse_shallow(formula_pair[2][0].data); + var AST_node_array = parse_shallow(formula[1][0].data); + if (!((_a2 = AST_node_array[1]) == null ? void 0 : _a2[0])) + return; + var AST_node0 = parse_shallow(AST_node_array[1][0].data); + var AST_node_type = varint_to_i32(AST_node0[1][0].data); + if (AST_node_type != 67) + return; + var AST_colon_tract = parse_shallow(AST_node0[40][0].data); + if (!((_b2 = AST_colon_tract[3]) == null ? void 0 : _b2[0]) || !((_c2 = AST_colon_tract[4]) == null ? void 0 : _c2[0])) + return; + var colrange = parse_shallow(AST_colon_tract[3][0].data); + var rowrange = parse_shallow(AST_colon_tract[4][0].data); + var c = varint_to_i32(colrange[1][0].data); + var C = ((_d2 = colrange[2]) == null ? void 0 : _d2[0]) ? varint_to_i32(colrange[2][0].data) : c; + var r = varint_to_i32(rowrange[1][0].data); + var R = ((_e2 = rowrange[2]) == null ? void 0 : _e2[0]) ? varint_to_i32(rowrange[2][0].data) : r; + return { s: { r: r, c: c }, e: { r: R, c: C } }; + }).filter(function(x) { + return x != null; + }); + } + } + } } function parse_TST_TableInfoArchive(M, root, opts) { var pb = parse_shallow(root.data); diff --git a/modules/83_numbers.ts b/modules/83_numbers.ts index 7de5520..188a4e0 100644 --- a/modules/83_numbers.ts +++ b/modules/83_numbers.ts @@ -960,6 +960,7 @@ function parse_TST_TableModelArchive(M: MessageSpace, root: IWAMessage, ws: Work _R += _tile.nrows; }); + /* old-style merges */ if(store[13]?.[0]) { var ref = M[parse_TSP_Reference(store[13][0].data)][0]; var mtype = varint_to_i32(ref.meta[1][0].data); @@ -976,6 +977,50 @@ function parse_TST_TableModelArchive(M: MessageSpace, root: IWAMessage, ws: Work }; }); } + + /* new-style merges */ + if(!ws["!merges"]?.length && pb[47]?.[0]) { + // .TST.MergeOwnerArchive + var merge_owner = parse_shallow(pb[47][0].data); + if(merge_owner[2]?.[0]) { + // .TST.FormulaStoreArchive + var formula_store = parse_shallow(merge_owner[2][0].data); + if(formula_store[3]?.[0]) { + ws["!merges"] = mappa(formula_store[3], (u) => { + var formula_pair = parse_shallow(u); + + /* TODO: this should eventually use a proper formula parser */ + // .TSCE.FormulaArchive + var formula = parse_shallow(formula_pair[2][0].data); + + // .TSCE.ASTNodeArrayArchive + var AST_node_array = parse_shallow(formula[1][0].data); + + // .TSCE.ASTNodeArrayArchive.ASTNodeArchive + if(!AST_node_array[1]?.[0]) return; + var AST_node0 = parse_shallow(AST_node_array[1][0].data); + + // .TSCE.ASTNodeArrayArchive.ASTNodeType + var AST_node_type = varint_to_i32(AST_node0[1][0].data); + if(AST_node_type != 67) return; // COLON_TRACT_NODE + + // .TSCE.ASTNodeArrayArchive.ASTColonTractArchive + var AST_colon_tract = parse_shallow(AST_node0[40][0].data); + if(!AST_colon_tract[3]?.[0] || !AST_colon_tract[4]?.[0]) return; + + // ASTColonTractAbsoluteRangeArchive + var colrange = parse_shallow(AST_colon_tract[3][0].data); + var rowrange = parse_shallow(AST_colon_tract[4][0].data); + var c = varint_to_i32(colrange[1][0].data); + var C = colrange[2]?.[0] ? varint_to_i32(colrange[2][0].data) : c; + var r = varint_to_i32(rowrange[1][0].data); + var R = rowrange[2]?.[0] ? varint_to_i32(rowrange[2][0].data) : r; + return { s: { r, c }, e: { r: R, c: C }} as Range; + }).filter(x => x != null) as Range[]; + // .TST.FormulaStoreArchive.FormulaStorePair + } + } + } } /** Parse .TST.TableInfoArchive (6000) */ diff --git a/test.mts b/test.mts index de7c6e3..d6b596c 100644 --- a/test.mts +++ b/test.mts @@ -1851,9 +1851,9 @@ describe('roundtrip features', function() { assert.ok(wb7.Workbook?.WBProps?.date1904); }); }); - it('should handle numeric NaN and Infinity', function() {[ + it('should handle numeric NaN and Infinity', function() {([ "xlsx", "xlsm", "xlsb", "xls", "biff5", "biff4", "biff3", "biff2", "xlml", "csv", "txt", "sylk", "html", "rtf", "prn", "eth", "ods", "fods" - ].forEach(function(ext) { + ] as Array).forEach(function(ext) { var ws: X.DenseWorkSheet = { "!data": [ [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], @@ -2630,7 +2630,7 @@ describe('HTML', function() { }); }); if(domtest) it('should handle numeric NaN and Infinity', function() { - var ws = { + var ws: X.DenseWorkSheet = { "!data": [ [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], [ { t: "s", v: "Inf-" }, { t: "n", v: -Infinity } ], @@ -2717,14 +2717,14 @@ describe('dense mode', function() { it('read', function() { ILPaths.forEach(function(p) { var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); - var ws = wb.Sheets[wb.SheetNames[0]]; + var ws: X.WorkSheet = wb.Sheets[wb.SheetNames[0]]; assert.equal(ws["A1"].v, "Link to Sheet2"); assert.ok(!ws["!data"]); wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); ws = wb.Sheets[wb.SheetNames[0]]; assert.ok(!ws["A1"]); - assert.equal(ws["!data"][0][0].v, "Link to Sheet2"); + assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2"); }); if(!browser) artifax.forEach(function(p) { var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); @@ -2735,7 +2735,8 @@ describe('dense mode', function() { wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); ws = wb.Sheets[wb.SheetNames[0]]; assert.ok(!ws["A1"]); - assert.ok(ws["!data"][0][0]); + assert.ok(!!ws["!data"]); + assert.ok(ws["!data"]?.[0][0]); }); }); it('aoa_to_sheet', function() { @@ -2743,28 +2744,28 @@ describe('dense mode', function() { var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.ok(!ds["A2"]); + var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); }); it('json_to_sheet', function() { var json = [{"SheetJS": 5433795}]; var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); - var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.ok(!ds["A2"]); + var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); }); it('sheet_add_aoa', function() { var aoa = [["SheetJS"]]; var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"][1][0].v, 5433795); assert.ok(!ds["A2"]); - ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.ok(!ds["A2"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); }); it('sheet_add_json', function() { var aoa = [["SheetJS"]]; - var sp: X.SparseSheet = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:1}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); - sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: 1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: 1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); - ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: 1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); + var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.ok(!sp["!data"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.ok(!ds["A2"]); }); for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti]; it('write ' + f, function() { diff --git a/test.ts b/test.ts index 9447f9f..22ba8c9 100644 --- a/test.ts +++ b/test.ts @@ -1851,9 +1851,9 @@ Deno.test('roundtrip features', async function(t) { assert.assert(wb7.Workbook?.WBProps?.date1904); }); }); - await t.step('should handle numeric NaN and Infinity', async function(t) {[ + await t.step('should handle numeric NaN and Infinity', async function(t) {([ "xlsx", "xlsm", "xlsb", "xls", "biff5", "biff4", "biff3", "biff2", "xlml", "csv", "txt", "sylk", "html", "rtf", "prn", "eth", "ods", "fods" - ].forEach(function(ext) { + ] as Array).forEach(function(ext) { var ws: X.DenseWorkSheet = { "!data": [ [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], @@ -2630,7 +2630,7 @@ Deno.test('HTML', async function(t) { }); }); if(domtest) await t.step('should handle numeric NaN and Infinity', async function(t) { - var ws = { + var ws: X.DenseWorkSheet = { "!data": [ [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], [ { t: "s", v: "Inf-" }, { t: "n", v: -Infinity } ], @@ -2717,14 +2717,14 @@ Deno.test('dense mode', async function(t) { await t.step('read', async function(t) { ILPaths.forEach(function(p) { var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); - var ws = wb.Sheets[wb.SheetNames[0]]; + var ws: X.WorkSheet = wb.Sheets[wb.SheetNames[0]]; assert.equal(ws["A1"].v, "Link to Sheet2"); assert.assert(!ws["!data"]); wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); ws = wb.Sheets[wb.SheetNames[0]]; assert.assert(!ws["A1"]); - assert.equal(ws["!data"][0][0].v, "Link to Sheet2"); + assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2"); }); if(!browser) artifax.forEach(function(p) { var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); @@ -2735,7 +2735,8 @@ Deno.test('dense mode', async function(t) { wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); ws = wb.Sheets[wb.SheetNames[0]]; assert.assert(!ws["A1"]); - assert.assert(ws["!data"][0][0]); + assert.assert(!!ws["!data"]); + assert.assert(ws["!data"]?.[0][0]); }); }); await t.step('aoa_to_sheet', async function(t) { @@ -2743,28 +2744,28 @@ Deno.test('dense mode', async function(t) { var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.assert(!ds["A2"]); + var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); }); await t.step('json_to_sheet', async function(t) { var json = [{"SheetJS": 5433795}]; var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); - var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.assert(!ds["A2"]); + var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); }); await t.step('sheet_add_aoa', async function(t) { var aoa = [["SheetJS"]]; var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"][1][0].v, 5433795); assert.assert(!ds["A2"]); - ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"][1][0].v, 5433795); assert.assert(!ds["A2"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); }); await t.step('sheet_add_json', async function(t) { var aoa = [["SheetJS"]]; - var sp: X.SparseSheet = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:1}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); - sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: 1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); - var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: 1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); - ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: 1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); }); for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti]; await t.step('write ' + f, async function(t) { diff --git a/testnocp.ts b/testnocp.ts index d494d19..a0f364e 100644 --- a/testnocp.ts +++ b/testnocp.ts @@ -387,9 +387,9 @@ Deno.test('should parse test files', async function(t) { }); function get_cell(ws: X.WorkSheet, addr: string) { - if(!Array.isArray(ws)) return ws[addr]; + if(!Array.isArray(ws["!data"])) return ws[addr]; var a = X.utils.decode_cell(addr); - return (ws[a.r]||[])[a.c]; + return (ws["!data"][a.r]||[])[a.c]; } function each_cell(ws: X.WorkSheet, f: (c: X.CellObject) => any) { @@ -1034,6 +1034,8 @@ Deno.test('parse features', async function(t) { var wbs: X.WorkBook[] = [], wbs_no_slk: X.WorkBook[] = []; wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(n), {type:TYPE, cellStyles:true}); }); wbs_no_slk = wbs.slice(0, 5); + /* */ + var ols = OLPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, cellStyles:true}); }); await t.step('should have "!cols"', async function(t) { wbs.forEach(function(wb) { assert.assert(wb.Sheets["Sheet1"]['!cols']); }); }); @@ -1065,6 +1067,22 @@ Deno.test('parse features', async function(t) { assert.equal(x?.[7].wpx, 101); }); }); + await t.step('should have correct outline levels', async function(t) { + ols.map(function(x) { return x.Sheets["Sheet1"]; }).forEach(function(ws) { + var cols = ws['!cols']; + if(!cols) return; // TODO: ODS!!! + for(var i = 0; i < 29; ++i) { + var cell = get_cell(ws, X.utils.encode_col(i) + "1"); + var lvl = (cols[i]||{}).level||0; + if(!cell || cell.t == 's') assert.equal(lvl, 0); + else if(cell.t == 'n') { + if(cell.v === 0) assert.equal(lvl, 0); + else assert.equal(lvl, cell.v); + } + } + assert.equal(cols[29].level, 7); + }); + }); }); await t.step('row properties', async function(t) { @@ -1832,6 +1850,32 @@ Deno.test('roundtrip features', async function(t) { assert.assert(wb7.Workbook?.WBProps?.date1904); }); }); + await t.step('should handle numeric NaN and Infinity', async function(t) {([ + "xlsx", "xlsm", "xlsb", "xls", "biff5", "biff4", "biff3", "biff2", "xlml", "csv", "txt", "sylk", "html", "rtf", "prn", "eth", "ods", "fods" + ] as Array).forEach(function(ext) { + var ws: X.DenseWorkSheet = { + "!data": [ + [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], + [ { t: "s", v: "Inf-" }, { t: "n", v: -Infinity } ], + [ { t: "s", v: "NaN" }, { t: "n", v: NaN } ], + ], + "!ref": "A1:B3" + }; + var wb = X.utils.book_new(ws, "Sheet1"); + var buf = X.write(wb, { type: TYPE, bookType: ext, numbers: XLSX_ZAHL }); + var wb2 = X.read(buf, { type: TYPE, PRN: true }); + var csv = X.utils.sheet_to_csv(wb2.Sheets.Sheet1).split(/[\r\n]+/); + assert.equal(csv.length, 3); + assert.equal(csv[0], "Inf+,#DIV/0!"); + assert.equal(csv[1], "Inf-,#DIV/0!"); + assert.equal(csv[2], "NaN,#NUM!"); + assert.equal(wb2.Sheets.Sheet1.B1.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B2.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B3.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B1.v, 0x07); + assert.equal(wb2.Sheets.Sheet1.B2.v, 0x07); + assert.equal(wb2.Sheets.Sheet1.B3.v, 0x24); + }); }); }); //function password_file(x){return x.match(/^password.*\.xls$/); } @@ -2584,6 +2628,30 @@ Deno.test('HTML', async function(t) { assert.equal(range.e.c, expectedCellCount - 1); }); }); + if(domtest) await t.step('should handle numeric NaN and Infinity', async function(t) { + var ws: X.DenseWorkSheet = { + "!data": [ + [ { t: "s", v: "Inf+" }, { t: "n", v: Infinity } ], + [ { t: "s", v: "Inf-" }, { t: "n", v: -Infinity } ], + [ { t: "s", v: "NaN" }, { t: "n", v: NaN } ], + ], + "!ref": "A1:B3" + }; + var wb = X.utils.book_new(ws, "Sheet1"); + var str = X.write(wb, { type: "string", bookType: "html" }); + var wb2 = X.utils.table_to_book(get_dom_element(str)); + var csv = X.utils.sheet_to_csv(wb2.Sheets.Sheet1).split(/[\r\n]+/); + assert.equal(csv.length, 3); + assert.equal(csv[0], "Inf+,#DIV/0!"); + assert.equal(csv[1], "Inf-,#DIV/0!"); + assert.equal(csv[2], "NaN,#NUM!"); + assert.equal(wb2.Sheets.Sheet1.B1.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B2.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B3.t, "e"); + assert.equal(wb2.Sheets.Sheet1.B1.v, 0x07); + assert.equal(wb2.Sheets.Sheet1.B2.v, 0x07); + assert.equal(wb2.Sheets.Sheet1.B3.v, 0x24); + }); }); Deno.test('js -> file -> js', async function(t) { @@ -2637,6 +2705,99 @@ Deno.test('rtf', async function(t) { }); }); +Deno.test('dense mode', async function(t) { + await t.step('sheet_new', async function(t) { + var sp = X.utils.sheet_new(); assert.assert(!sp["!data"]); + sp = X.utils.sheet_new({}); assert.assert(!sp["!data"]); + sp = X.utils.sheet_new({dense: false}); assert.assert(!sp["!data"]); + sp = X.utils.sheet_new({dense: true}); assert.assert(!!sp["!data"]); + }); + + await t.step('read', async function(t) { + ILPaths.forEach(function(p) { + var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); + var ws: X.WorkSheet = wb.Sheets[wb.SheetNames[0]]; + assert.equal(ws["A1"].v, "Link to Sheet2"); + assert.assert(!ws["!data"]); + + wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); + ws = wb.Sheets[wb.SheetNames[0]]; + assert.assert(!ws["A1"]); + assert.equal(ws["!data"]?.[0][0].v, "Link to Sheet2"); + }); + if(!browser) artifax.forEach(function(p) { + var wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true}); + var ws = wb.Sheets[wb.SheetNames[0]]; + assert.assert(ws["A1"]); + assert.assert(!ws["!data"]); + + wb = X.read(fs.readFileSync(p), {type: TYPE, WTF: true, dense: true}); + ws = wb.Sheets[wb.SheetNames[0]]; + assert.assert(!ws["A1"]); + assert.assert(!!ws["!data"]); + assert.assert(ws["!data"]?.[0][0]); + }); + }); + await t.step('aoa_to_sheet', async function(t) { + var aoa = [["SheetJS"],[5433795]]; + var sp = X.utils.aoa_to_sheet(aoa); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + var ds = X.utils.aoa_to_sheet(aoa, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + }); + await t.step('json_to_sheet', async function(t) { + var json = [{"SheetJS": 5433795}]; + var sp = X.utils.json_to_sheet(json); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.json_to_sheet(json, {}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.json_to_sheet(json, {dense: false}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + var ds = X.utils.json_to_sheet(json, {dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + }); + await t.step('sheet_add_aoa', async function(t) { + var aoa = [["SheetJS"]]; + var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_aoa(sp, [[5433795]], {origin:-1, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_aoa(ds, [[5433795]], {origin:-1, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + }); + await t.step('sheet_add_json', async function(t) { + var aoa = [["SheetJS"]]; + var sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader:true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + sp = X.utils.aoa_to_sheet(aoa); X.utils.sheet_add_json(sp, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(sp["A2"].v, 5433795); assert.assert(!sp["!data"]); + var ds:X.WorkSheet = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + ds = X.utils.aoa_to_sheet(aoa, {dense: true}); X.utils.sheet_add_json(ds, [{X:5433795}], {origin:-1, skipHeader: true, dense: true}); assert.equal(ds["!data"]?.[1][0].v, 5433795); assert.assert(!ds["A2"]); + }); + for(var ofmti = 0; ofmti < ofmt.length; ++ofmti) { var f = ofmt[ofmti]; + await t.step('write ' + f, async function(t) { + var aoa = [["SheetJS"],[5433795]]; + var wb = X.utils.book_new(X.utils.aoa_to_sheet(aoa)); + var newwb = X.read(X.write(wb, {type:"binary", bookType:f}), {type:"binary"}); + assert.equal(get_cell(newwb.Sheets["Sheet1"], "A1").v, "SheetJS"); + assert.equal(get_cell(newwb.Sheets["Sheet1"], "A2").v, 5433795); + }); } + await t.step('sheet_to_formulae', async function(t) { + var w = ['xlsx', paths.fstxlsx]; + var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:true, dense: false}); + var wb2 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:true, dense: true}); + wb1.SheetNames.forEach(function(n) { + assert.equal( + X.utils.sheet_to_formulae(wb1.Sheets[n]).sort().join("\n"), + X.utils.sheet_to_formulae(wb2.Sheets[n]).sort().join("\n") + ); + }); + }); + await t.step('sheet_to_csv', async function(t) { + var w = ['xlsx', paths.fstxlsx]; + var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:true, dense: false}); + var wb2 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:true, dense: true}); + wb1.SheetNames.forEach(function(n) { + assert.equal( + X.utils.sheet_to_csv(wb1.Sheets[n]), + X.utils.sheet_to_csv(wb2.Sheets[n]) + ); + }); + }); +}); + Deno.test('corner cases', async function(t) { await t.step('output functions', async function(t) { var ws = X.utils.aoa_to_sheet([