From d4d4ff3da275986d458089981c458a52019de188 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Wed, 10 Jan 2024 04:54:10 -0500 Subject: [PATCH] SYLK error cells read/write (fixes #3049) --- CHANGELOG.md | 4 ++ bits/00_header.js | 2 +- bits/18_cfb.js | 6 +- bits/40_harb.js | 3 +- bits/49_theme.js | 8 ++- bits/75_xlml.js | 9 +-- bits/76_xls.js | 4 +- bits/78_writebiff.js | 6 +- bits/81_writeods.js | 4 +- bits/85_parsezip.js | 2 +- packages/dta/README.md | 2 +- test.js | 125 +++++++++++++++++++++++++++++++++++++++-- tests/core.js | 103 +++++++++++++++++++++++++++++++-- 13 files changed, 246 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dab92f..5f64661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including but not limited to API changes and file location changes. Minor behavioral changes may not be included if they are not expected to break existing code. +## v0.20.2 + +* SYLK read and write error cells + ## v0.20.1 * `init` use packaged test files to work around GitHub breaking changes diff --git a/bits/00_header.js b/bits/00_header.js index ee9bc26..bdfd677 100644 --- a/bits/00_header.js +++ b/bits/00_header.js @@ -1,6 +1,6 @@ /*! xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* vim: set ts=2: */ /*exported XLSX */ -/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false, DataView:false, Deno:false, Float32Array:false */ +/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false, DataView:false, Deno:false, Set:false, Float32Array:false */ var XLSX = {}; function make_xlsx_lib(XLSX){ diff --git a/bits/18_cfb.js b/bits/18_cfb.js index 7b4b3a8..a19f00d 100644 --- a/bits/18_cfb.js +++ b/bits/18_cfb.js @@ -769,9 +769,9 @@ function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|strin file = cfb.FileIndex[i]; if(i === 0) file.start = file.size ? file.start - 1 : ENDOFCHAIN; var _nm/*:string*/ = (i === 0 && _opts.root) || file.name; - if(_nm.length > 32) { - console.error("Name " + _nm + " will be truncated to " + _nm.slice(0,32)); - _nm = _nm.slice(0, 32); + if(_nm.length > 31) { + console.error("Name " + _nm + " will be truncated to " + _nm.slice(0,31)); + _nm = _nm.slice(0, 31); } flen = 2*(_nm.length+1); o.write_shift(64, _nm, "utf16le"); diff --git a/bits/40_harb.js b/bits/40_harb.js index 84fef48..bce1b3b 100644 --- a/bits/40_harb.js +++ b/bits/40_harb.js @@ -483,6 +483,7 @@ var SYLK = /*#__PURE__*/(function() { val = record[rj].slice(1); if(val.charAt(0) === '"') { val = val.slice(1,val.length - 1); cell_t = "s"; } else if(val === 'TRUE' || val === 'FALSE') { val = val === 'TRUE'; cell_t = "b"; } + else if(val.charAt(0) == "#" && RBErr[val] != null) { cell_t = "e"; val = RBErr[val]; } else if(!isNaN(fuzzynum(val))) { val = fuzzynum(val); cell_t = "n"; if(next_cell_format !== null && fmt_is_date(next_cell_format) && opts.cellDates) { @@ -595,7 +596,7 @@ var SYLK = /*#__PURE__*/(function() { o += (cell.v||0); if(cell.f && !cell.F) o += ";E" + a1_to_rc(cell.f, {r:R, c:C}); break; case 'b': o += cell.v ? "TRUE" : "FALSE"; break; - case 'e': o += cell.w || cell.v; break; + case 'e': o += cell.w || BErr[cell.v] || cell.v; break; case 'd': o += datenum(parseDate(cell.v, date1904), date1904); break; case 's': o += '"' + (cell.v == null ? "" : String(cell.v)).replace(/"/g,"").replace(/;/g, ";;") + '"'; break; } diff --git a/bits/49_theme.js b/bits/49_theme.js index 75a486b..05fcfbf 100644 --- a/bits/49_theme.js +++ b/bits/49_theme.js @@ -18,10 +18,12 @@ function parse_clrScheme(t, themes, opts) { /* 20.1.2.3.32 srgbClr CT_SRgbColor */ case '': break; /* 20.1.2.3.33 sysClr CT_SystemColor */ case '': break; /* 20.1.4.1.1 accent1 (Accent 1) */ /* 20.1.4.1.2 accent2 (Accent 2) */ @@ -35,8 +37,10 @@ function parse_clrScheme(t, themes, opts) { /* 20.1.4.1.19 hlink (Hyperlink) */ /* 20.1.4.1.22 lt1 (Light 1) */ /* 20.1.4.1.23 lt2 (Light 2) */ - case '': case '': - case '': case '': + case '': + case '': + case '': + case '': case '': case '': case '': case '': case '': case '': diff --git a/bits/75_xlml.js b/bits/75_xlml.js index f432d06..c0d9fdd 100644 --- a/bits/75_xlml.js +++ b/bits/75_xlml.js @@ -99,10 +99,11 @@ function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, a if(sid === undefined && row) sid = row.StyleID; if(sid === undefined && csty) sid = csty.StyleID; while(styles[sid] !== undefined) { - if(styles[sid].nf) nf = styles[sid].nf; - if(styles[sid].Interior) interiors.push(styles[sid].Interior); - if(!styles[sid].Parent) break; - sid = styles[sid].Parent; + var ssid = styles[sid]; + if(ssid.nf) nf = ssid.nf; + if(ssid.Interior) interiors.push(ssid.Interior); + if(!ssid.Parent) break; + sid = ssid.Parent; } switch(data.Type) { case 'Boolean': diff --git a/bits/76_xls.js b/bits/76_xls.js index 785a619..eda716e 100644 --- a/bits/76_xls.js +++ b/bits/76_xls.js @@ -125,7 +125,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { if(icv < 64) return palette[icv-8] || XLSIcv[icv]; return XLSIcv[icv]; }; - var process_cell_style = function pcs(cell, line/*:any*/, options) { + var process_cell_style = function pcs(line/*:any*/, options) { var xfd = line.XF.data; if(!xfd || !xfd.patternType || !options || !options.cellStyles) return; line.s = ({}/*:any*/); @@ -137,7 +137,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { var addcell = function addcell(cell/*:any*/, line/*:any*/, options/*:any*/) { if(!biff4w && file_depth > 1) return; if(options.sheetRows && cell.r >= options.sheetRows) return; - if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options); + if(options.cellStyles && line.XF && line.XF.data) process_cell_style(line, options); delete line.ixfe; delete line.XF; lastcell = cell; last_cell = encode_cell(cell); diff --git a/bits/78_writebiff.js b/bits/78_writebiff.js index 47be0d0..6fc87a2 100644 --- a/bits/78_writebiff.js +++ b/bits/78_writebiff.js @@ -31,7 +31,7 @@ function write_biff_continue(ba/*:BufArray*/, type/*:number*/, payload, length/* } } -function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:string*/) { +function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:?string*/) { var out = new_buf(9); write_BIFF2Cell(out, r, c); write_Bes(val, t || 'b', out); @@ -101,7 +101,7 @@ function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:n function write_ws_biff2(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) { var dense = ws["!data"] != null; - var range = safe_decode_range(ws['!ref'] || "A1"), ref/*:string*/, rr = "", cols/*:Array*/ = []; + var range = safe_decode_range(ws['!ref'] || "A1"), rr = "", cols/*:Array*/ = []; if(range.e.c > 0xFF || range.e.r > 0x3FFF) { if(opts.WTF) throw new Error("Range " + (ws['!ref'] || "A1") + " exceeds format limit A1:IV16384"); range.e.c = Math.min(range.e.c, 0xFF); @@ -545,9 +545,9 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) { var date1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904; if(b8) ws['!links'] = []; + for(var C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C); var comments = []; var row = []; - for(var C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C); for(var R = range.s.r; R <= range.e.r; ++R) { if(dense) row = ws["!data"][R] || []; rr = encode_row(R); diff --git a/bits/81_writeods.js b/bits/81_writeods.js index eb8d5e9..9dabdd6 100644 --- a/bits/81_writeods.js +++ b/bits/81_writeods.js @@ -202,11 +202,11 @@ function write_names_ods(Names, SheetNames, idx) { } var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function() { /* 6.1.2 White Space Characters */ - var write_text_p = function(text/*:string*/)/*:string*/ { + var write_text_p = function(text/*:string*/, span)/*:string*/ { return escapexml(text) .replace(/ +/g, function($$){return '';}) .replace(/\t/g, "") - .replace(/\n/g, "") + .replace(/\n/g, span ? "": "") .replace(/^ /, "").replace(/ $/, ""); }; diff --git a/bits/85_parsezip.js b/bits/85_parsezip.js index 41f6a28..eb62436 100644 --- a/bits/85_parsezip.js +++ b/bits/85_parsezip.js @@ -257,7 +257,7 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ { if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,strip_front_slash(dir.vba[0]),true); else if(dir.defaults && dir.defaults.bin === CT_VBA) out.vbaraw = getzipdata(zip, 'xl/vbaProject.bin',true); } - // TODO: pass back content types metdata for xlsm/xlsx resolution + // TODO: pass back content types metadata for xlsm/xlsx resolution out.bookType = xlsb ? "xlsb" : "xlsx"; return out; } diff --git a/packages/dta/README.md b/packages/dta/README.md index 9ac81c7..9ea525b 100644 --- a/packages/dta/README.md +++ b/packages/dta/README.md @@ -52,7 +52,7 @@ const wb = DTA.parse(fs.readFileSync("auto.dta")); DTA.set_utils(XLSX.utils); (async() => { /* fetch file */ - const data = await (await fetch("test.dta")).arrayBuffer(); + const data = await (await fetch("test.dta")).arrayBuffer(); /* parse */ const wb = DTA.parse(new Uint8Array(data)); /* wb is a SheetJS workbook object */ diff --git a/test.js b/test.js index 4b46692..8a0d7ca 100644 --- a/test.js +++ b/test.js @@ -367,13 +367,13 @@ if(!browser) describe('should parse test files', function() { }); function get_cell(ws/*:Worksheet*/, addr/*:string*/) { - if(!Array.isArray(ws)) return ws[addr]; + if(!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, f) { - if(Array.isArray(ws)) ws.forEach(function(row) { if(row) row.forEach(f); }); + if(ws["!data"]) ws["!data"].forEach(function(row) { if(row) row.forEach(f); }); else Object.keys(ws).forEach(function(addr) { if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return; f(ws[addr]); }); } @@ -1023,10 +1023,14 @@ describe('parse features', function() { }); describe('column properties', function() { - var wbs = [], wbs_no_slk = []; + var wbs = [], wbs_no_slk = [], ols = []; + var ol = fs.existsSync(paths.olxls); var bef = (function() { wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(n), {type:TYPE, cellStyles:true}); }); wbs_no_slk = wbs.slice(0, 5); + /* */ + if(!ol) return; + ols = OLPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, cellStyles:true}); }); }); if(typeof before != 'undefined') before(bef); else it('before', bef); @@ -1061,6 +1065,22 @@ describe('parse features', function() { assert.equal(x[7].wpx, 101); }); }); + it('should have correct outline levels', function() { + 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); + }); + }); }); describe('row properties', function() { @@ -2476,10 +2496,12 @@ describe('dbf', function() { it(wbs[1][0], function() { var wsfalse = wbsfalse[1][1].Sheets["Sheet1"]; [ - ["A1", "v", "CHAR10"], ["A2", "v", "test1"], ["B2", "v", 123.45], ["C2", "v", 12.345], ["D2", "v", 1234.1], + ["A1", "v", "CHAR10"], ["A2", "v", "test1"], ["B2", "v", 123.45], + ["C2", "v", 12.345], ["D2", "v", 1234.1], ["E2", "v", 6260], ["E2", "w", "19170219"], ["F2", "v", 6260], ["F2", "w", "19170219"], - ["G2", "v", 1231.4], ["H2", "v", 123234], ["I2", "v", true], ["L2", "v", "SheetJS"] + ["G2", "v", 1231.4], ["H2", "v", 123234], + ["I2", "v", true], ["L2", "v", "SheetJS"] ].forEach(function(r) { assert.equal(get_cell(wsfalse, r[0])[r[1]], r[2]); }); @@ -2703,6 +2725,97 @@ describe('rtf', function() { }); }); +describe('dense mode', function() { + it('sheet_new', function() { + var sp = X.utils.sheet_new(); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({}); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({dense: false}); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({dense: true}); assert.ok(!!sp["!data"]); + }); + + 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]]; + 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"); + }); + 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.ok(ws["A1"]); + 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.ok(ws["!data"][0][0]); + }); + }); + it('aoa_to_sheet', function() { + var aoa = [["SheetJS"],[5433795]]; + 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"]); + }); + 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"]); + }); + 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"]); + }); + it('sheet_add_json', function() { + var aoa = [["SheetJS"]]; + var sp = 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"]); + }); + ofmt.forEach(function(f) { it('write ' + f, function() { + 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); + }); }); + it('sheet_to_formulae', function() { + 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") + ); + }); + }); + it('sheet_to_csv', function() { + 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]) + ); + }); + }); +}); + describe('corner cases', function() { it('output functions', function() { var ws = X.utils.aoa_to_sheet([ diff --git a/tests/core.js b/tests/core.js index 4b46692..74de560 100644 --- a/tests/core.js +++ b/tests/core.js @@ -367,13 +367,13 @@ if(!browser) describe('should parse test files', function() { }); function get_cell(ws/*:Worksheet*/, addr/*:string*/) { - if(!Array.isArray(ws)) return ws[addr]; + if(!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, f) { - if(Array.isArray(ws)) ws.forEach(function(row) { if(row) row.forEach(f); }); + if(ws["!data"]) ws["!data"].forEach(function(row) { if(row) row.forEach(f); }); else Object.keys(ws).forEach(function(addr) { if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return; f(ws[addr]); }); } @@ -1023,10 +1023,14 @@ describe('parse features', function() { }); describe('column properties', function() { - var wbs = [], wbs_no_slk = []; + var wbs = [], wbs_no_slk = [], ols = []; + var ol = fs.existsSync(paths.olxls); var bef = (function() { wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(n), {type:TYPE, cellStyles:true}); }); wbs_no_slk = wbs.slice(0, 5); + /* */ + if(!ol) return; + ols = OLPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, cellStyles:true}); }); }); if(typeof before != 'undefined') before(bef); else it('before', bef); @@ -1061,6 +1065,22 @@ describe('parse features', function() { assert.equal(x[7].wpx, 101); }); }); + it('should have correct outline levels', function() { + 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); + }); + }); }); describe('row properties', function() { @@ -2476,10 +2496,12 @@ describe('dbf', function() { it(wbs[1][0], function() { var wsfalse = wbsfalse[1][1].Sheets["Sheet1"]; [ - ["A1", "v", "CHAR10"], ["A2", "v", "test1"], ["B2", "v", 123.45], ["C2", "v", 12.345], ["D2", "v", 1234.1], + ["A1", "v", "CHAR10"], ["A2", "v", "test1"], ["B2", "v", 123.45], + ["C2", "v", 12.345], ["D2", "v", 1234.1], ["E2", "v", 6260], ["E2", "w", "19170219"], ["F2", "v", 6260], ["F2", "w", "19170219"], - ["G2", "v", 1231.4], ["H2", "v", 123234], ["I2", "v", true], ["L2", "v", "SheetJS"] + ["G2", "v", 1231.4], ["H2", "v", 123234], + ["I2", "v", true], ["L2", "v", "SheetJS"] ].forEach(function(r) { assert.equal(get_cell(wsfalse, r[0])[r[1]], r[2]); }); @@ -2703,6 +2725,75 @@ describe('rtf', function() { }); }); +describe('dense mode', function() { + it('sheet_new', function() { + var sp = X.utils.sheet_new(); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({}); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({dense: false}); assert.ok(!sp["!data"]); + sp = X.utils.sheet_new({dense: true}); assert.ok(!!sp["!data"]); + }); + + 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]]; + 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"); + }); + 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.ok(ws["A1"]); + 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.ok(ws["!data"][0][0]); + }); + }); + it('aoa_to_sheet', function() { + var aoa = [["SheetJS"],[5433795]]; + 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"]); + }); + 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"]); + }); + 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"]); + }); + it('sheet_add_json', function() { + var aoa = [["SheetJS"]]; + var sp = 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"]); + }); + ofmt.forEach(function(f) { it('write ' + f, function() { + 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); + }); }); +;}); + describe('corner cases', function() { it('output functions', function() { var ws = X.utils.aoa_to_sheet([