diff --git a/README.md b/README.md
index e6e3cca..bd35219 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ The exported `read` and `readFile` functions accept an options argument:
| cellHTML | true | Parse rich text and save HTML to the .h field |
| cellNF | false | Save number format string to the .z field |
| sheetStubs | false | Create cell objects for stub cells |
+| bookDeps | false | If true, parse calculation chains |
| bookProps | false | If true, only parse enough to get book metadata ** |
| bookSheets | false | If true, only parse enough to get the sheet names |
@@ -87,6 +88,7 @@ The exported `read` and `readFile` functions accept an options argument:
- Even if `cellNF` is false, formatted text (.w) will be generated
- In some cases, sheets may be parsed even if `bookSheets` is false.
- `bookSheets` and `bookProps` combine to give both sets of information
+- `Deps` will be an empty object if `bookDeps` is falsy
The defaults are enumerated in bits/84_defaults.js
diff --git a/bin/xlsx2csv.njs b/bin/xlsx2csv.njs
index c1949d7..de34e91 100755
--- a/bin/xlsx2csv.njs
+++ b/bin/xlsx2csv.njs
@@ -44,13 +44,14 @@ if(!fs.existsSync(filename)) {
process.exit(2);
}
-if(program.dev) X.verbose = 2;
-
-var opts = {};
+var opts = {}, wb;
if(program.listSheets) opts.bookSheets = true;
-var wb;
-if(program.dev) wb = X.readFile(filename, opts);
+if(program.dev) {
+ X.verbose = 2;
+ opts.WTF = true;
+ wb = X.readFile(filename, opts);
+}
else try {
wb = X.readFile(filename, opts);
} catch(e) {
diff --git a/bits/31_version.js b/bits/31_version.js
index 70a5ece..4eda6dc 100644
--- a/bits/31_version.js
+++ b/bits/31_version.js
@@ -1 +1 @@
-XLSX.version = '0.5.6';
+XLSX.version = '0.5.7';
diff --git a/bits/35_ziputils.js b/bits/35_ziputils.js
index f514a57..328a112 100644
--- a/bits/35_ziputils.js
+++ b/bits/35_ziputils.js
@@ -18,6 +18,12 @@ function getzipfile(zip, file) {
throw new Error("Cannot find file " + file + " in zip");
}
+function getzipdata(zip, file, safe) {
+ if(!safe) return getdata(getzipfile(zip, file));
+ if(!file) return null;
+ try { return getzipdata(zip, file); } catch(e) { return null; }
+}
+
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
diff --git a/bits/65_comments.js b/bits/65_comments.js
index ef4d01a..83df24e 100644
--- a/bits/65_comments.js
+++ b/bits/65_comments.js
@@ -25,7 +25,8 @@ function parse_comments_xml(data, opts) {
function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
for(var i = 0; i != dirComments.length; ++i) {
var canonicalpath=dirComments[i];
- var comments=parse_comments_xml(getdata(getzipfile(zip, canonicalpath.replace(/^\//,''))), opts);
+ var comments=parse_comments_xml(getzipdata(zip, canonicalpath.replace(/^\//,''), true), opts);
+ if(!comments || !comments.length) return;
// find the sheets targeted by these comments
var sheetNames = Object.keys(sheets);
for(var j = 0; j != sheetNames.length; ++j) {
diff --git a/bits/72_wsxml.js b/bits/72_wsxml.js
index 18e8343..dcc342b 100644
--- a/bits/72_wsxml.js
+++ b/bits/72_wsxml.js
@@ -43,6 +43,7 @@ function parse_ws_xml(data, opts) {
else p.t = (cell.t ? cell.t : "n"); // default is "n" in schema
if(refguess.s.c > idx) refguess.s.c = idx;
if(refguess.e.c < idx) refguess.e.c = idx;
+ /* 18.18.11 t ST_CellType */
switch(p.t) {
case 'n': p.v = parseFloat(p.v); break;
case 's': {
@@ -57,20 +58,14 @@ function parse_ws_xml(data, opts) {
is = is ? parse_si(is[1]) : {t:"",r:""};
p.t = 'str'; p.v = is.t;
break; // inline string
- case 'b':
- switch(p.v) {
- case '0': case 'FALSE': case "false": case false: p.v=false; break;
- case '1': case 'TRUE': case "true": case true: p.v=true; break;
- default: throw "Unrecognized boolean: " + p.v;
- } break;
- case 'd':
+ case 'b': if(typeof p.v !== 'boolean') p.v = parsexmlbool(p.v); break;
+ case 'd': /* TODO: date1904 logic */
var epoch = Date.parse(p.v);
p.v = (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
p.t = 'n';
break;
/* in case of error, stick value in .raw */
case 'e': p.raw = RBErr[p.v]; break;
- default: throw "Unrecognized cell type: " + p.t;
}
/* formatting */
@@ -82,7 +77,7 @@ function parse_ws_xml(data, opts) {
try {
p.w = SSF.format(fmtid,p.v,_ssfopts);
if(opts.cellNF) p.z = SSF._table[fmtid];
- } catch(e) { }
+ } catch(e) { if(opts.WTF) throw e; }
s[cell.r] = p;
});
});
diff --git a/bits/73_wsbin.js b/bits/73_wsbin.js
index b3ee0f9..17bdf92 100644
--- a/bits/73_wsbin.js
+++ b/bits/73_wsbin.js
@@ -152,7 +152,7 @@ var parse_ws_bin = function(data, opts) {
if((cf = styles.CellXf[val[0].iStyleRef])) try {
p.w = SSF.format(cf.ifmt,p.v,_ssfopts);
if(opts.cellNF) p.z = SSF._table[cf.ifmt];
- } catch(e) { }
+ } catch(e) { if(opts.WTF) throw e; }
s[encode_cell({c:val[0].c,r:row.r})] = p;
break; // TODO
diff --git a/bits/84_defaults.js b/bits/84_defaults.js
index ff9c5ea..66945f4 100644
--- a/bits/84_defaults.js
+++ b/bits/84_defaults.js
@@ -6,10 +6,11 @@ function fixopts(opts) {
['sheetStubs', false], /* emit empty cells */
+ ['bookDeps', false], /* parse calculation chains */
['bookSheets', false], /* only try to get sheet names (no Sheets) */
['bookProps', false], /* only try to get properties (no Sheets) */
- ['WTF', false] /* WTF mode (do not use) */
+ ['WTF', false] /* WTF mode (throws errors) */
];
defaults.forEach(function(d) { if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1]; });
}
diff --git a/bits/85_parsezip.js b/bits/85_parsezip.js
index bb57abd..fe5e16f 100644
--- a/bits/85_parsezip.js
+++ b/bits/85_parsezip.js
@@ -4,8 +4,9 @@ function parseZip(zip, opts) {
reset_cp();
var entries = Object.keys(zip.files);
var keys = entries.filter(function(x){return x.substr(-1) != '/';}).sort();
- var dir = parseCT(getdata(getzipfile(zip, '[Content_Types].xml')));
+ var dir = parseCT(getzipdata(zip, '[Content_Types].xml'));
var xlsb = false;
+ var sheets;
if(dir.workbooks.length === 0) {
var binname = "xl/workbook.bin";
if(!getzipfile(zip,binname)) throw new Error("Could not find workbook entry");
@@ -15,42 +16,43 @@ function parseZip(zip, opts) {
if(!opts.bookSheets && !opts.bookProps) {
strs = {};
- if(dir.sst) strs=parse_sst(getdata(getzipfile(zip, dir.sst.replace(/^\//,''))), dir.sst, opts);
+ if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts);
styles = {};
- if(dir.style) styles = parse_sty(getdata(getzipfile(zip, dir.style.replace(/^\//,''))),dir.style);
+ if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style);
}
- var wb = parse_wb(getdata(getzipfile(zip, dir.workbooks[0].replace(/^\//,''))), dir.workbooks[0], opts);
+ var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);
var props = {}, propdata = "";
try {
- propdata = dir.coreprops.length !== 0 ? getdata(getzipfile(zip, dir.coreprops[0].replace(/^\//,''))) : "";
- propdata += dir.extprops.length !== 0 ? getdata(getzipfile(zip, dir.extprops[0].replace(/^\//,''))) : "";
+ propdata = dir.coreprops.length !== 0 ? getzipdata(zip, dir.coreprops[0].replace(/^\//,'')) : "";
+ propdata += dir.extprops.length !== 0 ? getzipdata(zip, dir.extprops[0].replace(/^\//,'')) : "";
props = propdata !== "" ? parseProps(propdata) : {};
} catch(e) { }
var custprops = {};
if(!opts.bookSheets || opts.bookProps) {
- if (dir.custprops.length !== 0) try {
- propdata = getdata(getzipfile(zip, dir.custprops[0].replace(/^\//,'')));
- custprops = parseCustomProps(propdata);
- } catch(e) {/*console.error(e);*/}
+ if (dir.custprops.length !== 0) {
+ propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true);
+ if(propdata) custprops = parseCustomProps(propdata);
+ }
}
var out = {};
if(opts.bookSheets || opts.bookProps) {
- var sheets;
if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
else if(wb.Sheets) sheets = wb.Sheets.map(function(x){ return x.name; });
if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
if(typeof sheets !== 'undefined') out.SheetNames = sheets;
if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
}
+ sheets = {};
var deps = {};
- if(dir.calcchain) deps=parseDeps(getdata(getzipfile(zip, dir.calcchain.replace(/^\//,''))));
- var sheets = {}, i=0;
+ if(opts.bookDeps && dir.calcchain) deps=parseDeps(getzipdata(zip, dir.calcchain.replace(/^\//,'')));
+
+ var i=0;
var sheetRels = {};
var path, relsPath;
if(!props.Worksheets) {
@@ -62,12 +64,12 @@ function parseZip(zip, opts) {
props.SheetNames[j] = wbsheets[j].name;
}
for(i = 0; i != props.Worksheets; ++i) {
- try { /* TODO: remove these guards */
+ try {
path = 'xl/worksheets/sheet' + (i+1) + (xlsb?'.bin':'.xml');
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
- sheets[props.SheetNames[i]]=parse_ws(getdata(getzipfile(zip, path)),path,opts);
- sheetRels[props.SheetNames[i]]=parseRels(getdata(getzipfile(zip, relsPath)), path);
- } catch(e) {}
+ sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts);
+ sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path);
+ } catch(e) { if(opts.WTF) throw e; }
}
} else {
for(i = 0; i != props.Worksheets; ++i) {
@@ -75,9 +77,9 @@ function parseZip(zip, opts) {
//var path = dir.sheets[i].replace(/^\//,'');
path = 'xl/worksheets/sheet' + (i+1) + (xlsb?'.bin':'.xml');
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
- sheets[props.SheetNames[i]]=parse_ws(getdata(getzipfile(zip, path)),path,opts);
- sheetRels[props.SheetNames[i]]=parseRels(getdata(getzipfile(zip, relsPath)), path);
- } catch(e) {/*console.error(e);*/}
+ sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts);
+ sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path);
+ } catch(e) { if(opts.WTF) throw e; }
}
}
diff --git a/package.json b/package.json
index 3c4d11b..197621e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "xlsx",
- "version": "0.5.6",
+ "version": "0.5.7",
"author": "sheetjs",
"description": "XLSB / XLSX / XLSM parser",
"keywords": [ "xlsx", "xlsb", "xlsm", "office", "excel", "spreadsheet" ],
@@ -11,6 +11,7 @@
"dependencies": {
"ssf":"~0.5.6",
"codepage":"",
+ "cfb":"",
"jszip":"~2.1.0",
"commander":""
},
diff --git a/test.js b/test.js
index 87d5da7..e43ec86 100644
--- a/test.js
+++ b/test.js
@@ -1,7 +1,10 @@
/* vim: set ts=2: */
var XLSX;
var fs = require('fs'), assert = require('assert');
-describe('source',function(){ it('should load', function(){ XLSX = require('./'); });});
+describe('source',function(){it('should load',function(){XLSX=require('./');});});
+
+var opts = {};
+if(process.env.WTF) opts.WTF = true;
var ex = [".xlsb", ".xlsm", ".xlsx"];
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
@@ -15,9 +18,11 @@ function fixsheetname(x) { return x.substr(0,31); }
function normalizecsv(x) { return x.replace(/\t/g,",").replace(/#{255}/g,"").replace(/"/g,"").replace(/[\n\r]+/g,"\n").replace(/\n*$/,""); }
+var dir = "./test_files/";
+
function parsetest(x, wb) {
describe(x + ' should have all bits', function() {
- var sname = './test_files/2011/' + x + '.sheetnames';
+ var sname = dir + '2011/' + x + '.sheetnames';
it('should have all sheets', function() {
wb.SheetNames.forEach(function(y) { assert(wb.Sheets[y], 'bad sheet ' + y); });
});
@@ -50,7 +55,7 @@ function parsetest(x, wb) {
});
describe(x + ' should generate correct output', function() {
wb.SheetNames.forEach(function(ws, i) {
- var name = ('./test_files/' + x + '.' + i + '.csv');
+ var name = (dir + x + '.' + i + '.csv');
it('#' + i + ' (' + ws + ')', fs.existsSync(name) ? function() {
var file = fs.readFileSync(name, 'utf-8');
var csv = XLSX.utils.make_csv(wb.Sheets[ws]);
@@ -63,156 +68,169 @@ function parsetest(x, wb) {
describe('should parse test files', function() {
files.forEach(function(x) {
it(x, x.substr(-8) == ".pending" ? null : function() {
- var wb = XLSX.readFile('./test_files/' + x);
+ var wb = XLSX.readFile(dir + x, opts);
parsetest(x, wb);
});
});
});
-describe('should have comment as part of cell\'s properties', function(){
- var ws;
- before(function() {
- XLSX = require('./');
- var wb = XLSX.readFile('./test_files/apachepoi_SimpleWithComments.xlsx');
- var sheetName = 'Sheet1';
- ws = wb.Sheets[sheetName];
- });
- it('Parse comments.xml and insert into cell',function(){
- assert.equal(ws.B1.c.length, 1,"must have 1 comment");
- assert.equal(ws.B1.c[0].t, "Yegor Kozlov:\r\nfirst cell", "must have the concatenated texts");
- assert.equal(ws.B1.c[0].h, 'Yegor Kozlov:
first cell', "must have the html representation");
- assert.equal(ws.B1.c[0].r, 'Yegor Kozlov:\r\nfirst cell', "must have the rich text representation");
- assert.equal(ws.B1.c[0].a, "Yegor Kozlov","must have the same author");
- });
-});
-
describe('options', function() {
var html_cell_types = ['s'];
before(function() {
XLSX = require('./');
});
- it('should generate HTML by default', function() {
- var wb = XLSX.readFile('./test_files/comments_stress_test.xlsx');
- var ws = wb.Sheets.Sheet1;
- Object.keys(ws).forEach(function(addr) {
- if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- assert(html_cell_types.indexOf(ws[addr].t) === -1 || ws[addr].h);
- });
- });
- it('should not generate HTML when requested', function() {
- var wb = XLSX.readFile('./test_files/comments_stress_test.xlsx', {cellHTML: false});
- var ws = wb.Sheets.Sheet1;
- Object.keys(ws).forEach(function(addr) {
- if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- assert(typeof ws[addr].h === 'undefined');
- });
- });
- it('should generate formulae by default', function() {
- var wb = XLSX.readFile('./test_files/formula_stress_test.xlsb');
- var found = false;
- wb.SheetNames.forEach(function(s) {
- var ws = wb.Sheets[s];
+ describe('cell', function() {
+ it('should generate HTML by default', function() {
+ var wb = XLSX.readFile(dir + 'comments_stress_test.xlsx');
+ var ws = wb.Sheets.Sheet1;
Object.keys(ws).forEach(function(addr) {
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- if(typeof ws[addr].f !== 'undefined') return found = true;
+ assert(html_cell_types.indexOf(ws[addr].t) === -1 || ws[addr].h);
});
});
- assert(found);
- });
- it('should not generate formulae when requested', function() {
- var wb = XLSX.readFile('./test_files/formula_stress_test.xlsb', {cellFormula: false});
- wb.SheetNames.forEach(function(s) {
- var ws = wb.Sheets[s];
+ it('should not generate HTML when requested', function() {
+ var wb = XLSX.readFile(dir+'comments_stress_test.xlsx', {cellHTML:false});
+ var ws = wb.Sheets.Sheet1;
Object.keys(ws).forEach(function(addr) {
if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- assert(typeof ws[addr].f === 'undefined');
+ assert(typeof ws[addr].h === 'undefined');
+ });
+ });
+ it('should generate formulae by default', function() {
+ var wb = XLSX.readFile(dir + 'formula_stress_test.xlsb');
+ var found = false;
+ wb.SheetNames.forEach(function(s) {
+ var ws = wb.Sheets[s];
+ Object.keys(ws).forEach(function(addr) {
+ if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
+ if(typeof ws[addr].f !== 'undefined') return found = true;
+ });
+ });
+ assert(found);
+ });
+ it('should not generate formulae when requested', function() {
+ var wb =XLSX.readFile(dir+'formula_stress_test.xlsb',{cellFormula:false});
+ wb.SheetNames.forEach(function(s) {
+ var ws = wb.Sheets[s];
+ Object.keys(ws).forEach(function(addr) {
+ if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
+ assert(typeof ws[addr].f === 'undefined');
+ });
+ });
+ });
+ it('should not generate number formats by default', function() {
+ var wb = XLSX.readFile(dir+'number_format.xlsm');
+ wb.SheetNames.forEach(function(s) {
+ var ws = wb.Sheets[s];
+ Object.keys(ws).forEach(function(addr) {
+ if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
+ assert(typeof ws[addr].z === 'undefined');
+ });
+ });
+ });
+ it('should generate number formats when requested', function() {
+ var wb = XLSX.readFile(dir+'number_format.xlsm', {cellNF: true});
+ wb.SheetNames.forEach(function(s) {
+ var ws = wb.Sheets[s];
+ Object.keys(ws).forEach(function(addr) {
+ if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
+ assert(typeof ws[addr].t!== 'n' || typeof ws[addr].z !== 'undefined');
+ });
});
});
});
- it('should not generate sheet stubs by default', function() {
- var wb = XLSX.readFile('./test_files/merge_cells.xlsx');
- assert.throws(function() { wb.Sheets.Merge.A2.v; });
- });
- it('should generate sheet stubs when requested', function() {
- var wb = XLSX.readFile('./test_files/merge_cells.xlsx', {sheetStubs:true});
- assert(typeof wb.Sheets.Merge.A2.t !== 'undefined');
- });
- it('should not generate number formats by default', function() {
- var wb = XLSX.readFile('./test_files/number_format.xlsm');
- wb.SheetNames.forEach(function(s) {
- var ws = wb.Sheets[s];
- Object.keys(ws).forEach(function(addr) {
- if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- assert(typeof ws[addr].z === 'undefined');
- });
+ describe('sheet', function() {
+ it('should not generate sheet stubs by default', function() {
+ var wb = XLSX.readFile(dir+'merge_cells.xlsx');
+ assert.throws(function() { wb.Sheets.Merge.A2.v; });
});
- });
- it('should generate number formats when requested', function() {
- var wb = XLSX.readFile('./test_files/number_format.xlsm', {cellNF: true});
- wb.SheetNames.forEach(function(s) {
- var ws = wb.Sheets[s];
- Object.keys(ws).forEach(function(addr) {
- if(addr[0] === "!" || !ws.hasOwnProperty(addr)) return;
- assert(typeof ws[addr].t !== 'n' || typeof ws[addr].z !== 'undefined');
- });
+ it('should generate sheet stubs when requested', function() {
+ var wb = XLSX.readFile(dir+'merge_cells.xlsx', {sheetStubs:true});
+ assert(typeof wb.Sheets.Merge.A2.t !== 'undefined');
});
});
describe('book', function() {
it('bookSheets should not generate sheets', function() {
- var wb = XLSX.readFile('./test_files/merge_cells.xlsx', {bookSheets:true});
+ var wb = XLSX.readFile(dir+'merge_cells.xlsx', {bookSheets:true});
assert(typeof wb.Sheets === 'undefined');
});
it('bookProps should not generate sheets', function() {
- var wb = XLSX.readFile('./test_files/number_format.xlsb', {bookProps:true});
+ var wb = XLSX.readFile(dir+'number_format.xlsb', {bookProps:true});
assert(typeof wb.Sheets === 'undefined');
});
it('bookProps && bookSheets should not generate sheets', function() {
- var wb = XLSX.readFile('./test_files/LONumbers.xlsx', {bookProps:true, bookSheets:true});
+ var wb = XLSX.readFile(dir+'LONumbers.xlsx', {bookProps:true, bookSheets:true});
assert(typeof wb.Sheets === 'undefined');
});
+ it('should not generate deps by default', function() {
+ var wb = XLSX.readFile(dir+'formula_stress_test.xlsx', {bookDeps:true});
+ assert(typeof wb.Deps !== 'undefined' && wb.Deps.length > 0);
+ });
+ it('bookDeps should generate deps', function() {
+ var wb = XLSX.readFile(dir+'formula_stress_test.xlsx');
+ assert(typeof wb.Deps === 'undefined' || !(wb.Deps.length>0));
+ });
});
});
describe('input formats', function() {
it('should read binary strings', function() {
- XLSX.read(fs.readFileSync('./test_files/comments_stress_test.xlsb', 'binary'), {type: 'binary'});
- XLSX.read(fs.readFileSync('./test_files/comments_stress_test.xlsx', 'binary'), {type: 'binary'});
+ XLSX.read(fs.readFileSync(dir+'comments_stress_test.xlsb', 'binary'), {type: 'binary'});
+ XLSX.read(fs.readFileSync(dir+'comments_stress_test.xlsx', 'binary'), {type: 'binary'});
});
it('should read base64 strings', function() {
- XLSX.read(fs.readFileSync('./test_files/comments_stress_test.xlsb', 'base64'), {type: 'base64'});
- XLSX.read(fs.readFileSync('./test_files/comments_stress_test.xlsx', 'base64'), {type: 'base64'});
+ XLSX.read(fs.readFileSync(dir+'comments_stress_test.xlsb', 'base64'), {type: 'base64'});
+ XLSX.read(fs.readFileSync(dir+'comments_stress_test.xlsx', 'base64'), {type: 'base64'});
});
});
-describe('should have core properties and custom properties parsed', function() {
- var wb;
- before(function() {
- XLSX = require('./');
- wb = XLSX.readFile('./test_files/custom_properties.xlsx');
+describe('features', function() {
+ describe('should have comment as part of cell properties', function(){
+ var ws;
+ before(function() {
+ XLSX = require('./');
+ var wb = XLSX.readFile(dir+'apachepoi_SimpleWithComments.xlsx');
+ var sheetName = 'Sheet1';
+ ws = wb.Sheets[sheetName];
+ });
+ it('Parse comments.xml and insert into cell',function(){
+ assert.equal(ws.B1.c.length, 1,"must have 1 comment");
+ assert.equal(ws.B1.c[0].t, "Yegor Kozlov:\r\nfirst cell", "must have the concatenated texts");
+ assert.equal(ws.B1.c[0].h, 'Yegor Kozlov:
first cell', "must have the html representation");
+ assert.equal(ws.B1.c[0].r, 'Yegor Kozlov:\r\nfirst cell', "must have the rich text representation");
+ assert.equal(ws.B1.c[0].a, "Yegor Kozlov","must have the same author");
+ });
});
- it('Must have read the core properties', function() {
- assert.equal(wb.Props.Company, 'Vector Inc');
- assert.equal(wb.Props.Creator, 'Pony Foo');
- });
- it('Must have read the custom properties', function() {
- assert.equal(wb.Custprops['I am a boolean'], true);
- assert.equal(wb.Custprops['Date completed'], '1967-03-09T16:30:00Z');
- assert.equal(wb.Custprops.Status, 2);
- assert.equal(wb.Custprops.Counter, -3.14);
- });
-});
-describe.skip('should parse a sheet with a d date cell', function() {
- var wb, ws;
- before(function() {
- XLSX = require('./');
- wb = XLSX.readFile('./test_files/xlsx-stream-d-date-cell.xlsx');
- // wb = XLSX.readFile('./test_files/xlsx-stream-array.xlsx');
- var sheetName = 'Sheet1';
- ws = wb.Sheets[sheetName];
+ describe('should have core properties and custom properties parsed', function() {
+ var wb;
+ before(function() {
+ XLSX = require('./');
+ wb = XLSX.readFile(dir+'custom_properties.xlsx');
+ });
+ it('Must have read the core properties', function() {
+ assert.equal(wb.Props.Company, 'Vector Inc');
+ assert.equal(wb.Props.Creator, 'Pony Foo');
+ });
+ it('Must have read the custom properties', function() {
+ assert.equal(wb.Custprops['I am a boolean'], true);
+ assert.equal(wb.Custprops['Date completed'], '1967-03-09T16:30:00Z');
+ assert.equal(wb.Custprops.Status, 2);
+ assert.equal(wb.Custprops.Counter, -3.14);
+ });
});
- it('Must have read the date', function() {
- var sheet = XLSX.utils.sheet_to_row_object_array(ws);
- assert.equal(sheet[3]['てすと'], '2/14/14');
+
+ describe('should parse cells with date type', function() {
+ var wb, ws;
+ before(function() {
+ XLSX = require('./');
+ wb = XLSX.readFile(dir+'xlsx-stream-d-date-cell.xlsx');
+ var sheetName = 'Sheet1';
+ ws = wb.Sheets[sheetName];
+ });
+ it('Must have read the date', function() {
+ var sheet = XLSX.utils.sheet_to_row_object_array(ws);
+ assert.equal(sheet[3]['てすと'], '2/14/14');
+ });
});
});
diff --git a/tests/files b/tests/files
index 255a962..09abbe1 160000
--- a/tests/files
+++ b/tests/files
@@ -1 +1 @@
-Subproject commit 255a962b8f9a0f07ac75281bded02fea93e00b16
+Subproject commit 09abbe1b28237722d93a8510f42c71094c1244fd
diff --git a/xlsx.js b/xlsx.js
index db44b8f..93808f9 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -424,7 +424,7 @@ SSF.load_table = function(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i]) SSF.loa
make_ssf(SSF);
var XLSX = {};
(function(XLSX){
-XLSX.version = '0.5.6';
+XLSX.version = '0.5.7';
var current_codepage, current_cptable, cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('codepage');
@@ -455,6 +455,12 @@ function getzipfile(zip, file) {
throw new Error("Cannot find file " + file + " in zip");
}
+function getzipdata(zip, file, safe) {
+ if(!safe) return getdata(getzipfile(zip, file));
+ if(!file) return null;
+ try { return getzipdata(zip, file); } catch(e) { return null; }
+}
+
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
@@ -1239,7 +1245,8 @@ function parse_comments_xml(data, opts) {
function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
for(var i = 0; i != dirComments.length; ++i) {
var canonicalpath=dirComments[i];
- var comments=parse_comments_xml(getdata(getzipfile(zip, canonicalpath.replace(/^\//,''))), opts);
+ var comments=parse_comments_xml(getzipdata(zip, canonicalpath.replace(/^\//,''), true), opts);
+ if(!comments || !comments.length) return;
// find the sheets targeted by these comments
var sheetNames = Object.keys(sheets);
for(var j = 0; j != sheetNames.length; ++j) {
@@ -1329,6 +1336,7 @@ function parse_ws_xml(data, opts) {
else p.t = (cell.t ? cell.t : "n"); // default is "n" in schema
if(refguess.s.c > idx) refguess.s.c = idx;
if(refguess.e.c < idx) refguess.e.c = idx;
+ /* 18.18.11 t ST_CellType */
switch(p.t) {
case 'n': p.v = parseFloat(p.v); break;
case 's': {
@@ -1343,20 +1351,14 @@ function parse_ws_xml(data, opts) {
is = is ? parse_si(is[1]) : {t:"",r:""};
p.t = 'str'; p.v = is.t;
break; // inline string
- case 'b':
- switch(p.v) {
- case '0': case 'FALSE': case "false": case false: p.v=false; break;
- case '1': case 'TRUE': case "true": case true: p.v=true; break;
- default: throw "Unrecognized boolean: " + p.v;
- } break;
- case 'd':
+ case 'b': if(typeof p.v !== 'boolean') p.v = parsexmlbool(p.v); break;
+ case 'd': /* TODO: date1904 logic */
var epoch = Date.parse(p.v);
p.v = (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
p.t = 'n';
break;
/* in case of error, stick value in .raw */
case 'e': p.raw = RBErr[p.v]; break;
- default: throw "Unrecognized cell type: " + p.t;
}
/* formatting */
@@ -1368,7 +1370,7 @@ function parse_ws_xml(data, opts) {
try {
p.w = SSF.format(fmtid,p.v,_ssfopts);
if(opts.cellNF) p.z = SSF._table[fmtid];
- } catch(e) { }
+ } catch(e) { if(opts.WTF) throw e; }
s[cell.r] = p;
});
});
@@ -1530,7 +1532,7 @@ var parse_ws_bin = function(data, opts) {
if((cf = styles.CellXf[val[0].iStyleRef])) try {
p.w = SSF.format(cf.ifmt,p.v,_ssfopts);
if(opts.cellNF) p.z = SSF._table[cf.ifmt];
- } catch(e) { }
+ } catch(e) { if(opts.WTF) throw e; }
s[encode_cell({c:val[0].c,r:row.r})] = p;
break; // TODO
@@ -2674,10 +2676,11 @@ function fixopts(opts) {
['sheetStubs', false], /* emit empty cells */
+ ['bookDeps', false], /* parse calculation chains */
['bookSheets', false], /* only try to get sheet names (no Sheets) */
['bookProps', false], /* only try to get properties (no Sheets) */
- ['WTF', false] /* WTF mode (do not use) */
+ ['WTF', false] /* WTF mode (throws errors) */
];
defaults.forEach(function(d) { if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1]; });
}
@@ -2687,8 +2690,9 @@ function parseZip(zip, opts) {
reset_cp();
var entries = Object.keys(zip.files);
var keys = entries.filter(function(x){return x.substr(-1) != '/';}).sort();
- var dir = parseCT(getdata(getzipfile(zip, '[Content_Types].xml')));
+ var dir = parseCT(getzipdata(zip, '[Content_Types].xml'));
var xlsb = false;
+ var sheets;
if(dir.workbooks.length === 0) {
var binname = "xl/workbook.bin";
if(!getzipfile(zip,binname)) throw new Error("Could not find workbook entry");
@@ -2698,42 +2702,43 @@ function parseZip(zip, opts) {
if(!opts.bookSheets && !opts.bookProps) {
strs = {};
- if(dir.sst) strs=parse_sst(getdata(getzipfile(zip, dir.sst.replace(/^\//,''))), dir.sst, opts);
+ if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts);
styles = {};
- if(dir.style) styles = parse_sty(getdata(getzipfile(zip, dir.style.replace(/^\//,''))),dir.style);
+ if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style);
}
- var wb = parse_wb(getdata(getzipfile(zip, dir.workbooks[0].replace(/^\//,''))), dir.workbooks[0], opts);
+ var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);
var props = {}, propdata = "";
try {
- propdata = dir.coreprops.length !== 0 ? getdata(getzipfile(zip, dir.coreprops[0].replace(/^\//,''))) : "";
- propdata += dir.extprops.length !== 0 ? getdata(getzipfile(zip, dir.extprops[0].replace(/^\//,''))) : "";
+ propdata = dir.coreprops.length !== 0 ? getzipdata(zip, dir.coreprops[0].replace(/^\//,'')) : "";
+ propdata += dir.extprops.length !== 0 ? getzipdata(zip, dir.extprops[0].replace(/^\//,'')) : "";
props = propdata !== "" ? parseProps(propdata) : {};
} catch(e) { }
var custprops = {};
if(!opts.bookSheets || opts.bookProps) {
- if (dir.custprops.length !== 0) try {
- propdata = getdata(getzipfile(zip, dir.custprops[0].replace(/^\//,'')));
- custprops = parseCustomProps(propdata);
- } catch(e) {/*console.error(e);*/}
+ if (dir.custprops.length !== 0) {
+ propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true);
+ if(propdata) custprops = parseCustomProps(propdata);
+ }
}
var out = {};
if(opts.bookSheets || opts.bookProps) {
- var sheets;
if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
else if(wb.Sheets) sheets = wb.Sheets.map(function(x){ return x.name; });
if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
if(typeof sheets !== 'undefined') out.SheetNames = sheets;
if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
}
+ sheets = {};
var deps = {};
- if(dir.calcchain) deps=parseDeps(getdata(getzipfile(zip, dir.calcchain.replace(/^\//,''))));
- var sheets = {}, i=0;
+ if(opts.bookDeps && dir.calcchain) deps=parseDeps(getzipdata(zip, dir.calcchain.replace(/^\//,'')));
+
+ var i=0;
var sheetRels = {};
var path, relsPath;
if(!props.Worksheets) {
@@ -2745,12 +2750,12 @@ function parseZip(zip, opts) {
props.SheetNames[j] = wbsheets[j].name;
}
for(i = 0; i != props.Worksheets; ++i) {
- try { /* TODO: remove these guards */
+ try {
path = 'xl/worksheets/sheet' + (i+1) + (xlsb?'.bin':'.xml');
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
- sheets[props.SheetNames[i]]=parse_ws(getdata(getzipfile(zip, path)),path,opts);
- sheetRels[props.SheetNames[i]]=parseRels(getdata(getzipfile(zip, relsPath)), path);
- } catch(e) {}
+ sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts);
+ sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path);
+ } catch(e) { if(opts.WTF) throw e; }
}
} else {
for(i = 0; i != props.Worksheets; ++i) {
@@ -2758,9 +2763,9 @@ function parseZip(zip, opts) {
//var path = dir.sheets[i].replace(/^\//,'');
path = 'xl/worksheets/sheet' + (i+1) + (xlsb?'.bin':'.xml');
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
- sheets[props.SheetNames[i]]=parse_ws(getdata(getzipfile(zip, path)),path,opts);
- sheetRels[props.SheetNames[i]]=parseRels(getdata(getzipfile(zip, relsPath)), path);
- } catch(e) {/*console.error(e);*/}
+ sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts);
+ sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path);
+ } catch(e) { if(opts.WTF) throw e; }
}
}