diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 575a049..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index b64031e..18554cd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ tmp *.htm *.html *.sheetjs + +lab/ +test_files +example.js +example2.js +.idea diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml deleted file mode 100644 index 922003b..0000000 --- a/.idea/scopes/scope_settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/bits/67_wsxml.js b/bits/67_wsxml.js index 6ee52cf..61392ab 100644 --- a/bits/67_wsxml.js +++ b/bits/67_wsxml.js @@ -126,7 +126,7 @@ function write_ws_xml_cols(ws, cols) { } function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { - if(cell.v === undefined) return ""; + if(cell.v === undefined && cell.s === undefined) return ""; var vv = ""; var oldt = cell.t, oldv = cell.v; switch(cell.t) { @@ -213,7 +213,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) { if(opts.cellFormula && (cref=d.match(match_f))!== null) p.f=unescapexml(cref[1]); /* SCHEMA IS ACTUALLY INCORRECT HERE. IF A CELL HAS NO T, EMIT "" */ - if(tag.t === undefined && p.v === undefined) { + if(tag.t === undefined && tag.s === undefined && p.v === undefined) { if(!opts.sheetStubs) continue; p.t = "stub"; } @@ -222,7 +222,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess) { if(guess.e.c < idx) guess.e.c = idx; /* 18.18.11 t ST_CellType */ switch(p.t) { - case 'n': p.v = parseFloat(p.v); break; + case 'n': + p.v = parseFloat(p.v); + if(isNaN(p.v)) p.v = "" // we don't want NaN if p.v is null + break; case 's': sstr = strs[parseInt(p.v, 10)]; p.v = sstr.t; diff --git a/example.js b/example.js deleted file mode 100644 index f0b068b..0000000 --- a/example.js +++ /dev/null @@ -1,151 +0,0 @@ -var XLSX = require('./'); -var Workbook = require('../workbook'); - -///http://daveaddey.com/?p=40 -function JSDateToExcelDate(inDate) { - return 25569.0 + ((inDate.getTime() - (inDate.getTimezoneOffset() * 60 * 1000)) / (1000 * 60 * 60 * 24)); -} - -var workbook = new Workbook(XLSX) - .addRowsToSheet("Main", [ - [ - { - v: "This is a submerged cell", - s:{ - border: { - left: {style: 'thick', color: {auto: 1}}, - top: {style: 'thick', color: {auto: 1}}, - bottom: {style: 'thick', color: {auto: 1}} - } - } - }, - { - v: "Pirate ship", - s:{ - border: { - top: {style: 'thick', color: {auto: 1}}, - bottom: {style: 'thick', color: {auto: 1}} - } - } - }, - { - v: "Sunken treasure", - s:{ - border: { - right: {style: 'thick', color: {auto: 1}}, - top: {style: 'thick', color: {auto: 1}}, - bottom: {style: 'thick', color: {auto: 1}} - } - } - }], - [ - {"v": "Blank"}, - {"v": "Red", "s": {fill: { fgColor: { rgb: "FFFF0000"}}}}, - {"v": "Green", "s": {fill: { fgColor: { rgb: "FF00FF00"}}}}, - {"v": "Blue", "s": {fill: { fgColor: { rgb: "FF0000FF"}}}} - ], - [ - {"v": "Default"}, - {"v": "Arial", "s": {font: {name: "Arial", sz: 24, color: {theme: "5"}}}}, - {"v": "Times New Roman", "s": {font: {name: "Times New Roman", sz: 16, color: {rgb: "FF2222FF"}}}}, - {"v": "Courier New", "s": {font: {name: "Courier New", sz: 14}}} - ], - [ - 0.618033989, - {"v": 0.618033989}, - {"v": 0.618033989, "t": "n"}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.00%"}}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.00%"}, fill: { fgColor: { rgb: "FFFFCC00"}}} - ], - [ - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0%"}}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.0%"}}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.00%"}}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.000%"}}, - {"v": 0.618033989, "t": "n", "s": { "numFmt": "0.0000%"}}, - {"v": 0, "t": "n", "s": { numFmt: "0.00%;\\(0.00%\\);\\-;@"}, fill: { fgColor: { rgb: "FFFFCC00"}}} - ], - [ - {v: (new Date()).toLocaleString()}, - {v: JSDateToExcelDate(new Date()), t: 'd'}, - {v: JSDateToExcelDate(new Date()), s: {numFmt: 'd-mmm-yy'}} - ] - , - [ - {v: "left", "s": { alignment: {horizontal: "left"}}}, - {v: "left", "s": { alignment: {horizontal: "center"}}}, - {v: "left", "s": { alignment: {horizontal: "right"}}} - ],[ - {v: "vertical", "s": { alignment: {vertical: "top"}}}, - {v: "vertical", "s": { alignment: {vertical: "center"}}}, - {v: "vertical", "s": { alignment: {vertical: "bottom"}}} - ],[ - {v: "indent", "s": { alignment: {indent: "1"}}}, - {v: "indent", "s": { alignment: {indent: "2"}}}, - {v: "indent", "s": { alignment: {indent: "3"}}} - ], - [{ - v: "In publishing and graphic design, lorem ipsum is a filler text commonly used to demonstrate the graphic elements of a document or visual presentation. ", - s: { alignment: { wrapText: 1, alignment: 'right', vertical: 'center', indent: 1}} - } - ], - [ - {v: 41684.35264774306, s: {numFmt: 'm/d/yy'}}, - {v: 41684.35264774306, s: {numFmt: 'd-mmm-yy'}}, - {v: 41684.35264774306, s: {numFmt: 'h:mm:ss AM/PM'}}, - {v: JSDateToExcelDate(new Date()), s: {numFmt: 'm/d/yy'}}, - {v: 42065.02247239584, s: {numFmt: 'm/d/yy'}}, - {v: JSDateToExcelDate(new Date()), s: {numFmt: 'm/d/yy h:mm:ss AM/PM'}} - ], - [ - {v: "Apple", s: {border: {top: { style: "thin"}, left: { style: "thin"}, right: { style: "thin"}, bottom: { style: "thin"}}}}, - {}, - { - v: "Apple", - s: { - border: { - diagonalUp: 1, diagonalDown: 1, - top: { style: "dashed", color: {auto: 1}}, - right: { style: "medium", color: {theme: "5"}}, - bottom: { style: "hair", color: {theme: 5, tint: "-0.3"}}, - left: { style: "thin", color: {rgb: "FFFFAA00"}}, - diagonal: {style: "dotted", color: {auto: 1}} - } - } - }, - {}, - { - v: "Pear", - s: { - border: { - diagonalUp: 1, diagonalDown: 1, - top: { style: "dashed", color: {auto: 1}}, - right: { style: "dotted", color: {theme: "5"}}, - bottom: { style: "mediumDashed", color: {theme: 5, tint: "-0.3"}}, - left: { style: "double", color: {rgb: "FFFFAA00"}}, - diagonal: {style: "hair", color: {auto: 1}} - } - } - } - ], - [ - {v: "Up 90", s: {alignment: {textRotation: 90}}}, - {v: "Up 45", s: {alignment: {textRotation: 45}}}, - {v: "Horizontal", s: {alignment: {textRotation: 0}}}, - {v: "Down 45", s: {alignment: {textRotation: 135}}}, - {v: "Down 90", s: {alignment: {textRotation: 180}}}, - {v: "Vertical", s: {alignment: {textRotation: 255}}} - ], - [ - {v: "Font color test", s: { font: {fgColor: {rgb: "FFC6EFCE"}}}}, - {v: "right to left", s: {alignment: {readingOrder: 2}}} - ] - ]).mergeCells("Main", { - "s": {"c": 0, "r": 0 }, - "e": {"c": 2, "r": 0 } - }).finalize(); - - -var OUTFILE = '/tmp/wb.xlsx'; -XLSX.writeFile(workbook, OUTFILE, {defaultCellStyle: { font: { name: "Verdana", sz: 11, color: "FF00FF88"}, fill: {fgColor: {rgb: "FFFFAA00"}}}}); -console.log("Results written to " + OUTFILE) diff --git a/lab/wb/xl/styles-1.xml b/lab/wb/xl/styles-1.xml deleted file mode 100644 index e69de29..0000000 diff --git a/test-acid.js b/tests/test-acid.js similarity index 100% rename from test-acid.js rename to tests/test-acid.js diff --git a/test-csv.js b/tests/test-csv.js similarity index 100% rename from test-csv.js rename to tests/test-csv.js diff --git a/tests/test-min.js b/tests/test-min.js new file mode 100644 index 0000000..96ee7f2 --- /dev/null +++ b/tests/test-min.js @@ -0,0 +1,10 @@ +var X = require('./xlsx.js'); +var file = 'test_files/2013/apachepoi_44861.xls.xlsb'; +var file = 'test_files/apachepoi_44861.xls'; +var opts = {cellNF: true}; +describe('from 2013', function() { + it('should parse', function() { + var wb = X.readFile(file, opts); + }); + }); + diff --git a/test-open.js b/tests/test-open.js similarity index 100% rename from test-open.js rename to tests/test-open.js diff --git a/test-style.js b/tests/test-style.js similarity index 91% rename from test-style.js rename to tests/test-style.js index 0f57293..bb62103 100644 --- a/test-style.js +++ b/tests/test-style.js @@ -1,4 +1,4 @@ -var XLSX = require('./'); +var XLSX = require('../.'); var JSZip = require('jszip'); var fs = require('fs'); @@ -43,6 +43,38 @@ function basicallyEquals(left, right) { } } + +describe('styles with blank cells', function () { + it ('retains styles with blank cells', function() { + + + var OUTFILE = '/tmp/ex1.xlsx'; + var OUTFILE2 = '/tmp/ex1a.xlsx'; + + var workbook = { + SheetNames : ["Sheet1"], + Sheets: { + "Sheet1": { + "B2": {v: "Top left", s: { border: { top: { style: 'medium', color: { rgb: "FFFFAA00"}}, left: { style: 'medium', color: { rgb: "FFFFAA00"}} }}}, + "C2": {v: "Top right", s: { border: { top: { style: 'medium', color: { rgb: "FFFFAA00"}}, right: { style: 'medium', color: { rgb: "FFFFAA00"}} }}}, + "B3": {v: "Bottom left", s: { border: { bottom: { style: 'medium', color: { rgb: "FFFFAA00"}}, left: { style: 'medium', color: { rgb: "FFFFAA00"}} }}}, + "C3": {v: "", s: { border: { bottom: { style: 'medium', color: { rgb: "FFFFAA00"}}, right: { style: 'medium', color: { rgb: "FFFFAA00"}} }}}, + "!ref":"B2:C3" + } + } + }; + + // write the file and read it back... + XLSX.writeFile(workbook, OUTFILE, {bookType: 'xlsx', bookSST: false}); + var workbook2 = XLSX.readFile(OUTFILE, {cellStyles: true}); + assert(basicallyEquals(workbook.Sheets, workbook2.Sheets)); + + XLSX.writeFile(workbook2, OUTFILE2, {bookType: 'xlsx', bookSST: false}); + var workbook3 = XLSX.readFile(OUTFILE2, {cellStyles: true}); + assert(basicallyEquals(workbook.Sheets, workbook3.Sheets)) + }); +}); + describe("Export styles", function () { var workbook, wbout, wbin; @@ -658,4 +690,5 @@ describe("Export styles", function () { assert(basicallyEquals(workbook.Sheets.Main,wb2.Sheets.Main)); }); -}); \ No newline at end of file +}); + diff --git a/xlsx.js b/xlsx.js index faaf85f..b44fe15 100644 --- a/xlsx.js +++ b/xlsx.js @@ -7651,7 +7651,7 @@ function write_ws_xml_cols(ws, cols) { } function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { - if(cell.v === undefined) return ""; + if(cell.v === undefined && cell.s === undefined) return ""; var vv = ""; var oldt = cell.t, oldv = cell.v; switch(cell.t) { @@ -7738,7 +7738,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) { if(opts.cellFormula && (cref=d.match(match_f))!== null) p.f=unescapexml(cref[1]); /* SCHEMA IS ACTUALLY INCORRECT HERE. IF A CELL HAS NO T, EMIT "" */ - if(tag.t === undefined && p.v === undefined) { + if(tag.t === undefined && tag.s === undefined && p.v === undefined) { if(!opts.sheetStubs) continue; p.t = "stub"; } @@ -7747,7 +7747,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess) { if(guess.e.c < idx) guess.e.c = idx; /* 18.18.11 t ST_CellType */ switch(p.t) { - case 'n': p.v = parseFloat(p.v); break; + case 'n': + p.v = parseFloat(p.v); + if(isNaN(p.v)) p.v = "" // we don't want NaN if p.v is null + break; case 's': sstr = strs[parseInt(p.v, 10)]; p.v = sstr.t;