From f5f9229ab200dbe24ed1fea7776ec6607d04d766 Mon Sep 17 00:00:00 2001 From: Hugues Malphettes Date: Fri, 14 Feb 2014 11:39:03 +0800 Subject: [PATCH] Support custom properties and return them as the hash workbook.Custprops --- bits/60_xlsx.js | 43 +++++++++++++++++++++++++++++++++++++- bits/85_parsezip.js | 8 +++++++ test.js | 18 ++++++++++++++++ xlsx.js | 51 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/bits/60_xlsx.js b/bits/60_xlsx.js index 4f8d969..d0e2926 100644 --- a/bits/60_xlsx.js +++ b/bits/60_xlsx.js @@ -18,6 +18,7 @@ var ct2type = { "application/vnd.openxmlformats-package.core-properties+xml": "coreprops", "application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops", + "application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops", "application/vnd.openxmlformats-officedocument.theme+xml":"themes", "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments", "foo": "bar" @@ -64,6 +65,46 @@ function parseProps(data) { return p; } +/* 15.2.12.2 Custom File Properties Part */ +function parseCustomProps(data) { + var p = {}, name; + data.match(/<[^>]+>([^<]*)/g).forEach(function(x) { + var y = parsexmltag(x); + switch(y[0]) { + case '': name = null; break; + default: if (x.indexOf(''); + var type = toks[0].substring(4), text = toks[1]; + /* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */ + switch(type) { + case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr': + p[name] = unescapexml(text); + break; + case 'bool': + p[name] = parsexmlbool(text, ''); + break; + case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint': + p[name] = parseInt(text, 10); + break; + case 'r4': case 'r8': case 'decimal': + p[name] = parseFloat(text); + break; + case 'filetime': case 'date': + p[name] = text; // should we make this into a date? + break; + case 'cy': case 'error': + p[name] = unescapexml(text); + break; + default: + console.warn('Unexpected', x, type, toks); + } + } + } + }); + return p; +} + /* 18.6 Calculation Chain */ function parseDeps(data) { var d = []; @@ -85,7 +126,7 @@ var ctext = {}; function parseCT(data) { if(!data || !data.match) return data; var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [], - coreprops: [], extprops: [], strs:[], comments: [], xmlns: "" }; + coreprops: [], extprops: [], custprops: [], strs:[], comments: [], xmlns: "" }; (data.match(/<[^>]*>/g)||[]).forEach(function(x) { var y = parsexmltag(x); switch(y[0]) { diff --git a/bits/85_parsezip.js b/bits/85_parsezip.js index 6fdd753..d2989de 100644 --- a/bits/85_parsezip.js +++ b/bits/85_parsezip.js @@ -29,6 +29,13 @@ function parseZip(zip, opts) { propdata += dir.extprops.length !== 0 ? getdata(getzipfile(zip, dir.extprops[0].replace(/^\//,''))) : ""; props = propdata !== "" ? parseProps(propdata) : {}; } catch(e) { } + var custprops = {}; + if (dir.custprops.length !== 0) { + try { + propdata = getdata(getzipfile(zip, dir.custprops[0].replace(/^\//,''))); + custprops = parseCustomProps(propdata); + } catch(e) {/*console.error(e);*/} + } if(opts.bookSheets) { if(props.Worksheets && props.SheetNames.length > 0) return { SheetNames:props.SheetNames }; @@ -74,6 +81,7 @@ function parseZip(zip, opts) { Directory: dir, Workbook: wb, Props: props, + Custprops: custprops, Deps: deps, Sheets: sheets, SheetNames: props.SheetNames, diff --git a/test.js b/test.js index ad8e5d5..ce5cbe1 100644 --- a/test.js +++ b/test.js @@ -158,3 +158,21 @@ describe('options', function() { }); }); }); + +describe.skip('should have core properties and custom properties parsed', function() { + var wb; + before(function() { + XLSX = require('./'); + wb = XLSX.readFile('./test_files/excel-properties.xlsx', { sheetStubs: false }); + }); + 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); + }); +}); diff --git a/xlsx.js b/xlsx.js index f2bf3d8..af2884d 100644 --- a/xlsx.js +++ b/xlsx.js @@ -1044,6 +1044,7 @@ var ct2type = { "application/vnd.openxmlformats-package.core-properties+xml": "coreprops", "application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops", + "application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops", "application/vnd.openxmlformats-officedocument.theme+xml":"themes", "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments", "foo": "bar" @@ -1090,6 +1091,46 @@ function parseProps(data) { return p; } +/* 15.2.12.2 Custom File Properties Part */ +function parseCustomProps(data) { + var p = {}, name; + data.match(/<[^>]+>([^<]*)/g).forEach(function(x) { + var y = parsexmltag(x); + switch(y[0]) { + case '': name = null; break; + default: if (x.indexOf(''); + var type = toks[0].substring(4), text = toks[1]; + /* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */ + switch(type) { + case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr': + p[name] = unescapexml(text); + break; + case 'bool': + p[name] = parsexmlbool(text, ''); + break; + case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint': + p[name] = parseInt(text, 10); + break; + case 'r4': case 'r8': case 'decimal': + p[name] = parseFloat(text); + break; + case 'filetime': case 'date': + p[name] = text; // should we make this into a date? + break; + case 'cy': case 'error': + p[name] = unescapexml(text); + break; + default: + console.warn('Unexpected', x, type, toks); + } + } + } + }); + return p; +} + /* 18.6 Calculation Chain */ function parseDeps(data) { var d = []; @@ -1111,7 +1152,7 @@ var ctext = {}; function parseCT(data) { if(!data || !data.match) return data; var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [], - coreprops: [], extprops: [], strs:[], comments: [], xmlns: "" }; + coreprops: [], extprops: [], custprops: [], strs:[], comments: [], xmlns: "" }; (data.match(/<[^>]*>/g)||[]).forEach(function(x) { var y = parsexmltag(x); switch(y[0]) { @@ -2660,6 +2701,13 @@ function parseZip(zip, opts) { propdata += dir.extprops.length !== 0 ? getdata(getzipfile(zip, dir.extprops[0].replace(/^\//,''))) : ""; props = propdata !== "" ? parseProps(propdata) : {}; } catch(e) { } + var custprops = {}; + if (dir.custprops.length !== 0) { + try { + propdata = getdata(getzipfile(zip, dir.custprops[0].replace(/^\//,''))); + custprops = parseCustomProps(propdata); + } catch(e) {/*console.error(e);*/} + } if(opts.bookSheets) { if(props.Worksheets && props.SheetNames.length > 0) return { SheetNames:props.SheetNames }; @@ -2705,6 +2753,7 @@ function parseZip(zip, opts) { Directory: dir, Workbook: wb, Props: props, + Custprops: custprops, Deps: deps, Sheets: sheets, SheetNames: props.SheetNames,