version bump 0.4.0: very basic XLSB support
subfiles reshuffled (XLSB and XLSX/XLSM content separated)
This commit is contained in:
parent
ad16dc3235
commit
32d835ab81
4
Makefile
4
Makefile
@ -4,8 +4,8 @@ TARGET=xlsx.js
|
||||
$(TARGET): $(DEPS)
|
||||
cat $^ > $@
|
||||
|
||||
bits/51_version.js: package.json
|
||||
echo "XLSX.version = '"`grep version package.json | awk '{gsub(/[^0-9\.]/,"",$$2); print $$2}'`"';" > bits/51_version.js
|
||||
bits/31_version.js: package.json
|
||||
echo "XLSX.version = '"`grep version package.json | awk '{gsub(/[^0-9\.]/,"",$$2); print $$2}'`"';" > $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
@ -1,7 +1,7 @@
|
||||
# xlsx
|
||||
|
||||
Currently a parser for XLSX and XLSM files. Cleanroom implementation from the
|
||||
ISO 29500 Office Open XML specifications and related documents.
|
||||
Currently a parser for XLSX/XLSM/XLSB files. Cleanroom implementation from the
|
||||
ISO 29500 Office Open XML specifications, [MS-XLSB], and related documents.
|
||||
|
||||
## Installation
|
||||
|
||||
@ -17,7 +17,7 @@ In the browser:
|
||||
|
||||
## Usage
|
||||
|
||||
The node version installs a binary `xlsx2csv` which can read XLSX/XLSM files and output the contents in various formats. The source is available at `xlsx2csv.njs` in the bin directory.
|
||||
The node version installs a binary `xlsx2csv` which can read XLSX/XLSM/XLSB files and output the contents in various formats. The source is available at `xlsx2csv.njs` in the bin directory.
|
||||
|
||||
See <http://oss.sheetjs.com/js-xlsx/> for a browser example.
|
||||
|
||||
@ -92,6 +92,7 @@ ISO/IEC 29500:2012(E) "Information technology — Document description and proce
|
||||
OSP-covered specifications:
|
||||
|
||||
- [MS-XLSX]: Excel (.xlsx) Extensions to the Office Open XML SpreadsheetML File Format
|
||||
- [MS-XLSB]: Excel (.xlsb) Binary File Format
|
||||
|
||||
## Badges
|
||||
|
||||
|
1
bits/31_version.js
Normal file
1
bits/31_version.js
Normal file
@ -0,0 +1 @@
|
||||
XLSX.version = '0.4.0';
|
9
bits/32_codepage.js
Normal file
9
bits/32_codepage.js
Normal file
@ -0,0 +1,9 @@
|
||||
if(typeof module !== "undefined" && typeof require !== 'undefined') {
|
||||
if(typeof cptable === 'undefined') var cptable = require('codepage');
|
||||
var current_codepage = 1252, current_cptable = cptable[1252];
|
||||
}
|
||||
function reset_cp() {
|
||||
current_codepage = 1252; if(typeof cptable !== 'undefined') current_cptable = cptable[1252];
|
||||
}
|
||||
function _getchar(x) { return String.fromCharCode(x); }
|
||||
|
26
bits/35_ziputils.js
Normal file
26
bits/35_ziputils.js
Normal file
@ -0,0 +1,26 @@
|
||||
function getdata(data) {
|
||||
if(!data) return null;
|
||||
if(data.data) return data.data;
|
||||
if(data._data && data._data.getContent) {
|
||||
/* TODO: something far more intelligent */
|
||||
if(data.name.substr(-4) === ".bin") return Array.prototype.slice.call(data._data.getContent());
|
||||
return Array.prototype.slice.call(data._data.getContent(),0).map(function(x) { return String.fromCharCode(x); }).join("");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getzipfile(zip, file) {
|
||||
var f = file; if(zip.files[f]) return zip.files[f];
|
||||
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
|
||||
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
|
||||
throw new Error("Cannot find file " + file + " in zip");
|
||||
}
|
||||
|
||||
var _fs, jszip;
|
||||
if(typeof JSZip !== 'undefined') jszip = JSZip;
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
if(typeof jszip === 'undefined') jszip = require('./jszip').JSZip;
|
||||
_fs = require('fs');
|
||||
}
|
||||
}
|
@ -7,6 +7,12 @@ function parsexmltag(tag) {
|
||||
return z;
|
||||
}
|
||||
|
||||
function evert(obj) {
|
||||
var o = {};
|
||||
Object.keys(obj).forEach(function(k) { if(obj.hasOwnProperty(k)) o[obj[k]] = k; });
|
||||
return o;
|
||||
}
|
||||
|
||||
var encodings = {
|
||||
'"': '"',
|
||||
''': "'",
|
||||
@ -14,13 +20,21 @@ var encodings = {
|
||||
'<': '<',
|
||||
'&': '&'
|
||||
};
|
||||
|
||||
var rencoding = evert(encodings);
|
||||
var rencstr = "&<>'\"".split("");
|
||||
|
||||
// TODO: CP remap (need to read file version to determine OS)
|
||||
function unescapexml(text){
|
||||
var s = text + '';
|
||||
for(var y in encodings) s = s.replace(new RegExp(y,'g'), encodings[y]);
|
||||
return s.replace(/_x([0-9a-fA-F]*)_/g,function(m,c) {return _chr(parseInt(c,16));});
|
||||
}
|
||||
function escapexml(text){
|
||||
var s = text + '';
|
||||
rencstr.forEach(function(y){s=s.replace(new RegExp(y,'g'), rencoding[y]);});
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
function parsexmlbool(value, tag) {
|
||||
switch(value) {
|
157
bits/37_xlsbutils.js
Normal file
157
bits/37_xlsbutils.js
Normal file
@ -0,0 +1,157 @@
|
||||
function readIEEE754(buf, idx, isLE, nl, ml) {
|
||||
if(isLE === undefined) isLE = true;
|
||||
if(!nl) nl = 8;
|
||||
if(!ml && nl === 8) ml = 52;
|
||||
var e, m, el = nl * 8 - ml - 1, eMax = (1 << el) - 1, eBias = eMax >> 1;
|
||||
var bits = -7, d = isLE ? -1 : 1, i = isLE ? (nl - 1) : 0, s = buf[idx + i];
|
||||
|
||||
i += d;
|
||||
e = s & ((1 << (-bits)) - 1); s >>>= (-bits); bits += el;
|
||||
for (; bits > 0; e = e * 256 + buf[idx + i], i += d, bits -= 8);
|
||||
m = e & ((1 << (-bits)) - 1); e >>>= (-bits); bits += ml;
|
||||
for (; bits > 0; m = m * 256 + buf[idx + i], i += d, bits -= 8);
|
||||
if (e === eMax) return m ? NaN : ((s ? -1 : 1) * Infinity);
|
||||
else if (e === 0) e = 1 - eBias;
|
||||
else { m = m + Math.pow(2, ml); e = e - eBias; }
|
||||
return (s ? -1 : 1) * m * Math.pow(2, e - ml);
|
||||
}
|
||||
|
||||
function s2a(s) {
|
||||
if(typeof Buffer !== 'undefined') return new Buffer(s, "binary");
|
||||
var w = s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
|
||||
return w;
|
||||
}
|
||||
|
||||
var __toBuffer;
|
||||
if(typeof Buffer !== "undefined") {
|
||||
Buffer.prototype.hexlify= function() { return this.toString('hex'); };
|
||||
Buffer.prototype.utf16le= function(s,e){return this.toString('utf16le',s,e).replace(/\u0000/,'').replace(/[\u0001-\u0006]/,'!');};
|
||||
Buffer.prototype.utf8 = function(s,e) { return this.toString('utf8',s,e); };
|
||||
Buffer.prototype.lpstr = function(i) { var len = this.readUInt32LE(i); return len > 0 ? this.utf8(i+4,i+4+len-1) : "";};
|
||||
Buffer.prototype.lpwstr = function(i) { var len = 2*this.readUInt32LE(i); return this.utf8(i+4,i+4+len-1);};
|
||||
if(typeof cptable !== "undefined") Buffer.prototype.lpstr = function(i) {
|
||||
var len = this.readUInt32LE(i);
|
||||
if(len === 0) return "";
|
||||
if(typeof current_cptable === "undefined") return this.utf8(i+4,i+4+len-1);
|
||||
var t = Array(this.slice(i+4,i+4+len-1));
|
||||
//1console.log("start", this.l, len, t);
|
||||
var c, j = i+4, o = "", cc;
|
||||
for(;j!=i+4+len;++j) {
|
||||
c = this.readUInt8(j);
|
||||
cc = current_cptable.dec[c];
|
||||
if(typeof cc === 'undefined') {
|
||||
c = c*256 + this.readUInt8(++j);
|
||||
cc = current_cptable.dec[c];
|
||||
}
|
||||
if(typeof cc === 'undefined') throw "Unrecognized character " + c.toString(16);
|
||||
if(c === 0) break;
|
||||
o += cc;
|
||||
//1console.log(cc, cc.charCodeAt(0), o, this.l);
|
||||
}
|
||||
return o;
|
||||
};
|
||||
__toBuffer = function(bufs) { return Buffer.concat(bufs[0]); };
|
||||
} else {
|
||||
__toBuffer = function(bufs) {
|
||||
var x = [];
|
||||
for(var i = 0; i != bufs[0].length; ++i) { x = x.concat(bufs[0][i]); }
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
var __readUInt8 = function(b, idx) { return b.readUInt8 ? b.readUInt8(idx) : b[idx]; };
|
||||
var __readUInt16LE = function(b, idx) { return b.readUInt16LE ? b.readUInt16LE(idx) : b[idx+1]*(1<<8)+b[idx]; };
|
||||
var __readInt16LE = function(b, idx) { var u = __readUInt16LE(b,idx); if(!(u & 0x8000)) return u; return (0xffff - u + 1) * -1; };
|
||||
var __readUInt32LE = function(b, idx) { return b.readUInt32LE ? b.readUInt32LE(idx) : b[idx+3]*(1<<24)+b[idx+2]*(1<<16)+b[idx+1]*(1<<8)+b[idx]; };
|
||||
var __readInt32LE = function(b, idx) { if(b.readInt32LE) return b.readInt32LE(idx); var u = __readUInt32LE(b,idx); if(!(u & 0x80000000)) return u; return (0xffffffff - u + 1) * -1; };
|
||||
var __readDoubleLE = function(b, idx) { return b.readDoubleLE ? b.readDoubleLE(idx) : readIEEE754(b, idx||0);};
|
||||
|
||||
var __hexlify = function(b) { return b.map(function(x){return (x<16?"0":"") + x.toString(16);}).join(""); };
|
||||
|
||||
var __utf16le = function(b,s,e) { if(b.utf16le) return b.utf16le(s,e); var str = ""; for(var i=s; i<e; i+=2) str += String.fromCharCode(__readUInt16LE(b,i)); return str.replace(/\u0000/,'').replace(/[\u0001-\u0006]/,'!'); };
|
||||
|
||||
var __utf8 = function(b,s,e) { if(b.utf8) return b.utf8(s,e); var str = ""; for(var i=s; i<e; i++) str += String.fromCharCode(__readUInt8(b,i)); return str; };
|
||||
|
||||
var __lpstr = function(b,i) { if(b.lpstr) return b.lpstr(i); var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
|
||||
var __lpwstr = function(b,i) { if(b.lpwstr) return b.lpwstr(i); var len = 2*__readUInt32LE(b,i); return __utf8(b, i+4,i+4+len-1);};
|
||||
|
||||
function bconcat(bufs) { return (typeof Buffer !== 'undefined') ? Buffer.concat(bufs) : [].concat.apply([], bufs); }
|
||||
|
||||
function ReadShift(size, t) {
|
||||
var o, w, vv, i, loc; t = t || 'u';
|
||||
if(size === 'ieee754') { size = 8; t = 'f'; }
|
||||
switch(size) {
|
||||
case 1: o = __readUInt8(this, this.l); break;
|
||||
case 2: o=(t==='u' ? __readUInt16LE : __readInt16LE)(this, this.l); break;
|
||||
case 4: o = __readUInt32LE(this, this.l); break;
|
||||
case 8: if(t === 'f') { o = __readDoubleLE(this, this.l); break; }
|
||||
/* falls through */
|
||||
case 16: o = this.toString('hex', this.l,this.l+size); break;
|
||||
|
||||
case 'utf8': size = t; o = __utf8(this, this.l, this.l + size); break;
|
||||
case 'utf16le': size=2*t; o = __utf16le(this, this.l, this.l + size); break;
|
||||
|
||||
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
|
||||
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
|
||||
|
||||
case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
|
||||
|
||||
/* sbcs and dbcs support continue records in the SST way TODO codepages */
|
||||
/* TODO: DBCS http://msdn.microsoft.com/en-us/library/cc194788.aspx */
|
||||
case 'dbcs': size = 2*t; o = ""; loc = this.l;
|
||||
for(i = 0; i != t; ++i) {
|
||||
if(this.lens && this.lens.indexOf(loc) !== -1) {
|
||||
w = __readUInt8(this, loc);
|
||||
this.l = loc + 1;
|
||||
vv = ReadShift.call(this, w ? 'dbcs' : 'sbcs', t-i);
|
||||
return o + vv;
|
||||
}
|
||||
o += _getchar(__readUInt16LE(this, loc));
|
||||
loc+=2;
|
||||
} break;
|
||||
|
||||
case 'sbcs': size = t; o = ""; loc = this.l;
|
||||
for(i = 0; i != t; ++i) {
|
||||
if(this.lens && this.lens.indexOf(loc) !== -1) {
|
||||
w = __readUInt8(this, loc);
|
||||
this.l = loc + 1;
|
||||
vv = ReadShift.call(this, w ? 'dbcs' : 'sbcs', t-i);
|
||||
return o + vv;
|
||||
}
|
||||
o += _getchar(__readUInt8(this, loc));
|
||||
loc+=1;
|
||||
} break;
|
||||
|
||||
case 'cstr': size = 0; o = "";
|
||||
while((w=__readUInt8(this, this.l + size++))!==0) o+= _getchar(w);
|
||||
break;
|
||||
case 'wstr': size = 0; o = "";
|
||||
while((w=__readUInt16LE(this,this.l +size))!==0){o+= _getchar(w);size+=2;}
|
||||
size+=2; break;
|
||||
}
|
||||
this.l+=size; return o;
|
||||
}
|
||||
|
||||
function CheckField(hexstr, fld) {
|
||||
var b = this.slice(this.l, this.l+hexstr.length/2);
|
||||
var m = b.hexlify ? b.hexlify() : __hexlify(b);
|
||||
if(m !== hexstr) throw (fld||"") + 'Expected ' + hexstr + ' saw ' + m;
|
||||
this.l += hexstr.length/2;
|
||||
}
|
||||
|
||||
function WarnField(hexstr, fld) {
|
||||
var b = this.slice(this.l, this.l+hexstr.length/2);
|
||||
var m = b.hexlify ? b.hexlify() : __hexlify(b);
|
||||
if(m !== hexstr) console.error((fld||"") + 'Expected ' + hexstr +' saw ' + m);
|
||||
this.l += hexstr.length/2;
|
||||
}
|
||||
|
||||
function prep_blob(blob, pos) {
|
||||
blob.read_shift = ReadShift.bind(blob);
|
||||
blob.chk = CheckField;
|
||||
blob.l = pos || 0;
|
||||
var read = ReadShift.bind(blob), chk = CheckField.bind(blob);
|
||||
return [read, chk];
|
||||
}
|
||||
|
||||
function parsenoop(blob, length) { blob.l += length; }
|
15
bits/38_recordhopper.js
Normal file
15
bits/38_recordhopper.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* [MS-XLSB] 2.1.4 Record */
|
||||
var recordhopper = function(data, cb) {
|
||||
var tmpbyte, cntbyte;
|
||||
prep_blob(data, data.l || 0);
|
||||
while(data.l < data.length) {
|
||||
var RT = data.read_shift(1);
|
||||
if(RT & 0x80) RT = (RT & 0x7F) + ((data.read_shift(1) & 0x7F)<<7);
|
||||
var R = RecordEnum[RT] || RecordEnum[0xFFFF];
|
||||
|
||||
var length = tmpbyte = data.read_shift(1);
|
||||
for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte);
|
||||
var d = R.f(data, length);
|
||||
if(cb(d, R)) return;
|
||||
}
|
||||
};
|
86
bits/39_parsestructs.js
Normal file
86
bits/39_parsestructs.js
Normal file
@ -0,0 +1,86 @@
|
||||
/* [MS-XLSB] 2.1.7.121 */
|
||||
var parse_RichStr = function(data, length) {
|
||||
var start = data.l;
|
||||
var flags = data.read_shift(1);
|
||||
var fRichStr = flags & 1, fExtStr = flags & 2;
|
||||
var str = parse_XLWideString(data);
|
||||
z = {
|
||||
t: str,
|
||||
raw:"<t>" + escapexml(str) + "</t>",
|
||||
r: str
|
||||
}
|
||||
if(fRichStr) {
|
||||
/* TODO: formatted string */
|
||||
var dwSizeStrRun = data.read_shift(4);
|
||||
}
|
||||
if(fExtStr) {
|
||||
/* TODO: phonetic string */
|
||||
}
|
||||
data.l = start + length;
|
||||
return z;
|
||||
};
|
||||
|
||||
/* [MS-XLSB] 2.5.9 */
|
||||
function parse_Cell(data) {
|
||||
var col = data.read_shift(4);
|
||||
var iStyleRef = data.read_shift(2);
|
||||
iStyleRef += data.read_shift(1) <<16;
|
||||
var fPhShow = data.read_shift(1);
|
||||
return { c:col };
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.5.21 */
|
||||
var parse_CodeName = function(data, length) { return parse_XLWideString(data, length); };
|
||||
|
||||
/* [MS-XLSB] 2.5.114 */
|
||||
var parse_RelID = function(data, length) { return parse_XLNullableWideString(data, length); };
|
||||
|
||||
/* [MS-XLSB] 2.5.122 */
|
||||
function parse_RkNumber(data) {
|
||||
var b = data.slice(data.l, data.l+4);
|
||||
var fX100 = b[0] & 1, fInt = b[0] & 2;
|
||||
data.l+=4;
|
||||
b[0] &= ~3;
|
||||
var RK = fInt === 0 ? __readDoubleLE([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
|
||||
return fX100 ? RK/100 : RK;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.5.153 */
|
||||
var parse_UncheckedRfX = function(data) {
|
||||
var cell = {s: {}, e: {}};
|
||||
cell.s.r = data.read_shift(4);
|
||||
cell.e.r = data.read_shift(4);
|
||||
cell.s.c = data.read_shift(4);
|
||||
cell.e.c = data.read_shift(4);
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.5.166 */
|
||||
var parse_XLNullableWideString = function(data) {
|
||||
var cchCharacters = data.read_shift(4);
|
||||
return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift('dbcs', cchCharacters);
|
||||
};
|
||||
|
||||
/* [MS-XLSB] 2.5.168 */
|
||||
var parse_XLWideString = function(data) {
|
||||
var cchCharacters = data.read_shift(4);
|
||||
return cchCharacters === 0 ? "" : data.read_shift('dbcs', cchCharacters);
|
||||
};
|
||||
|
||||
/* [MS-XLSB] 2.5.171 */
|
||||
function parse_Xnum(data, length) { return data.read_shift('ieee754'); }
|
||||
|
||||
/* [MS-XLSB] 2.5.198.2 */
|
||||
var BErr = {
|
||||
0x00: "#NULL!",
|
||||
0x07: "#DIV/0!",
|
||||
0x0F: "#VALUE!",
|
||||
0x17: "#REF!",
|
||||
0x1D: "#NAME?",
|
||||
0x24: "#NUM!",
|
||||
0x2A: "#N/A",
|
||||
0x2B: "#GETTING_DATA",
|
||||
0xFF: "#WTF?"
|
||||
};
|
||||
var RBErr = evert(BErr);
|
||||
|
@ -1 +0,0 @@
|
||||
XLSX.version = '0.3.10';
|
@ -124,15 +124,14 @@ var parse_si = function(x) {
|
||||
};
|
||||
|
||||
/* 18.4 Shared String Table */
|
||||
var parse_sst = (function(){
|
||||
return function(data) {
|
||||
var s = [];
|
||||
/* 18.4.9 sst CT_Sst */
|
||||
var sst = data.match(new RegExp("<sst([^>]*)>([\\s\\S]*)<\/sst>","m"));
|
||||
if(isval(sst)) {
|
||||
s = sst[2].replace(/<si>/g,"").split(/<\/si>/).map(parse_si);
|
||||
sst = parsexmltag(sst[1]); s.Count = sst.count; s.Unique = sst.uniqueCount;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
})();
|
||||
var parse_sst_xml = function(data) {
|
||||
var s = [];
|
||||
/* 18.4.9 sst CT_Sst */
|
||||
var sst = data.match(new RegExp("<sst([^>]*)>([\\s\\S]*)<\/sst>","m"));
|
||||
if(isval(sst)) {
|
||||
s = sst[2].replace(/<si>/g,"").split(/<\/si>/).map(parse_si);
|
||||
sst = parsexmltag(sst[1]); s.Count = sst.count; s.Unique = sst.uniqueCount;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
18
bits/53_sstbin.js
Normal file
18
bits/53_sstbin.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* [MS-XLSB] 2.4.219 BrtBeginSst */
|
||||
var parse_BrtBeginSst = function(data, length) {
|
||||
return [data.read_shift(4), data.read_shift(4)];
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.1.7.45 Shared Strings */
|
||||
var parse_sst_bin = function(data) {
|
||||
var s = [];
|
||||
recordhopper(data, function(val, R) {
|
||||
switch(R.n) {
|
||||
case 'BrtBeginSst': s.Count = val[0]; s.Unique = val[1]; break;
|
||||
case 'BrtSSTItem': s.push(val); break;
|
||||
case 'BrtEndSst': return true;
|
||||
default: throw new Error("Unexpected record " + R.n);
|
||||
}
|
||||
});
|
||||
return s;
|
||||
};
|
4
bits/54_sst.js
Normal file
4
bits/54_sst.js
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
var parse_sst = function(data, name) {
|
||||
return name.substr(-4)===".bin" ? parse_sst_bin(data) : parse_sst_xml(data);
|
||||
};
|
2
bits/56_stycommon.js
Normal file
2
bits/56_stycommon.js
Normal file
@ -0,0 +1,2 @@
|
||||
var styles = {}; // shared styles
|
||||
|
66
bits/57_styxml.js
Normal file
66
bits/57_styxml.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* 18.8.31 numFmts CT_NumFmts */
|
||||
function parseNumFmts(t) {
|
||||
styles.NumberFmt = [];
|
||||
for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<numFmts': case '</numFmts>': case '<numFmts/>': break;
|
||||
case '<numFmt': {
|
||||
var f=unescapexml(y.formatCode), i=parseInt(y.numFmtId,10);
|
||||
styles.NumberFmt[i] = f; SSF.load(f,i);
|
||||
} break;
|
||||
default: throw 'unrecognized ' + y[0] + ' in numFmts';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 18.8.10 cellXfs CT_CellXfs */
|
||||
function parseCXfs(t) {
|
||||
styles.CellXf = [];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<cellXfs': case '<cellXfs/>': case '</cellXfs>': break;
|
||||
|
||||
/* 18.8.45 xf CT_Xf */
|
||||
case '<xf': if(y.numFmtId) y.numFmtId = parseInt(y.numFmtId, 10);
|
||||
styles.CellXf.push(y); break;
|
||||
case '</xf>': break;
|
||||
|
||||
/* 18.8.1 alignment CT_CellAlignment */
|
||||
case '<alignment': break;
|
||||
|
||||
/* 18.8.33 protection CT_CellProtection */
|
||||
case '<protection': case '</protection>': case '<protection/>': break;
|
||||
|
||||
case '<extLst': case '</extLst>': break;
|
||||
case '<ext': break;
|
||||
default: throw 'unrecognized ' + y[0] + ' in cellXfs';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 18.8 Styles CT_Stylesheet*/
|
||||
function parse_styles(data) {
|
||||
/* 18.8.39 styleSheet CT_Stylesheet */
|
||||
var t;
|
||||
|
||||
/* numFmts CT_NumFmts ? */
|
||||
if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parseNumFmts(t);
|
||||
|
||||
/* fonts CT_Fonts ? */
|
||||
/* fills CT_Fills ? */
|
||||
/* borders CT_Borders ? */
|
||||
/* cellStyleXfs CT_CellStyleXfs ? */
|
||||
|
||||
/* cellXfs CT_CellXfs ? */
|
||||
if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parseCXfs(t);
|
||||
|
||||
/* dxfs CT_Dxfs ? */
|
||||
/* tableStyles CT_TableStyles ? */
|
||||
/* colors CT_Colors ? */
|
||||
/* extLst CT_ExtensionList ? */
|
||||
|
||||
return styles;
|
||||
}
|
3
bits/58_stybin.js
Normal file
3
bits/58_stybin.js
Normal file
@ -0,0 +1,3 @@
|
||||
function parse_sty_bin(data) {
|
||||
|
||||
}
|
147
bits/60_xlsx.js
Normal file
147
bits/60_xlsx.js
Normal file
@ -0,0 +1,147 @@
|
||||
|
||||
var ct2type = {
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks", /*XLSX*/
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.main+xml":"workbooks", /*XLSM*/
|
||||
"application/vnd.ms-excel.sheet.binary.macroEnabled.main":"workbooks", /*XLSB*/
|
||||
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":"sheets", /*XLS[XM]*/
|
||||
"application/vnd.ms-excel.worksheet":"sheets", /*XLSB*/
|
||||
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":"styles", /*XLS[XM]*/
|
||||
"application/vnd.ms-excel.styles":"styles", /*XLSB*/
|
||||
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml": "strs", /*XLS[XM]*/
|
||||
"application/vnd.ms-excel.sharedStrings": "strs", /*XLSB*/
|
||||
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml": "calcchains", /*XLS[XM]*/
|
||||
//"application/vnd.ms-excel.calcChain": "calcchains", /*XLSB*/
|
||||
|
||||
"application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
|
||||
"application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops",
|
||||
"application/vnd.openxmlformats-officedocument.theme+xml":"themes",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments",
|
||||
"foo": "bar"
|
||||
};
|
||||
|
||||
var XMLNS_CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
|
||||
function parseProps(data) {
|
||||
var p = { Company:'' }, q = {};
|
||||
var strings = ["Application", "DocSecurity", "Company", "AppVersion"];
|
||||
var bools = ["HyperlinksChanged","SharedDoc","LinksUpToDate","ScaleCrop"];
|
||||
var xtra = ["HeadingPairs", "TitlesOfParts"];
|
||||
var xtracp = ["category", "contentStatus", "lastModifiedBy", "lastPrinted", "revision", "version"];
|
||||
var xtradc = ["creator", "description", "identifier", "language", "subject", "title"];
|
||||
var xtradcterms = ["created", "modified"];
|
||||
xtra = xtra.concat(xtracp.map(function(x) { return "cp:" + x; }));
|
||||
xtra = xtra.concat(xtradc.map(function(x) { return "dc:" + x; }));
|
||||
xtra = xtra.concat(xtradcterms.map(function(x) { return "dcterms:" + x; }));
|
||||
|
||||
|
||||
strings.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1];});
|
||||
bools.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1] == "true";});
|
||||
xtra.forEach(function(f) {
|
||||
var cur = data.match(new RegExp("<" + f + "[^>]*>(.*)<\/" + f + ">"));
|
||||
if(cur && cur.length > 0) q[f] = cur[1];
|
||||
});
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
switch(v[i].v) {
|
||||
case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
}
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); });
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
p.Creator = q["dc:creator"];
|
||||
p.LastModifiedBy = q["cp:lastModifiedBy"];
|
||||
p.CreatedDate = new Date(q["dcterms:created"]);
|
||||
p.ModifiedDate = new Date(q["dcterms:modified"]);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* 18.6 Calculation Chain */
|
||||
function parseDeps(data) {
|
||||
var d = [];
|
||||
var l = 0, i = 1;
|
||||
(data.match(/<[^>]*>/g)||[]).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
/* 18.6.2 calcChain CT_CalcChain 1 */
|
||||
case '<calcChain': case '<calcChain>': case '</calcChain>': break;
|
||||
/* 18.6.1 c CT_CalcCell 1 */
|
||||
case '<c': delete y[0]; if(y.i) i = y.i; else y.i = i; d.push(y); break;
|
||||
}
|
||||
});
|
||||
return d;
|
||||
}
|
||||
|
||||
var ctext = {};
|
||||
function parseCT(data) {
|
||||
if(!data || !data.match) return data;
|
||||
var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], strs:[], comments: [], xmlns: "" };
|
||||
(data.match(/<[^>]*>/g)||[]).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
case '<Types': ct.xmlns = y.xmlns; break;
|
||||
case '<Default': ctext[y.Extension] = y.ContentType; break;
|
||||
case '<Override':
|
||||
if(y.ContentType in ct2type)ct[ct2type[y.ContentType]].push(y.PartName);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if(ct.xmlns !== XMLNS_CT) throw new Error("Unknown Namespace: " + ct.xmlns);
|
||||
ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : "";
|
||||
ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
|
||||
ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
|
||||
delete ct.calcchains;
|
||||
return ct;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
function parseRels(data, currentFilePath) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
currentFilePath = '/'+currentFilePath;
|
||||
}
|
||||
var rels = {};
|
||||
|
||||
var resolveRelativePathIntoAbsolute = function (to) {
|
||||
var toksFrom = currentFilePath.split('/');
|
||||
toksFrom.pop(); // folder path
|
||||
var toksTo = to.split('/');
|
||||
var reversed = [];
|
||||
while (toksTo.length !== 0) {
|
||||
var tokTo = toksTo.shift();
|
||||
if (tokTo === '..') {
|
||||
toksFrom.pop();
|
||||
} else if (tokTo !== '.') {
|
||||
toksFrom.push(tokTo);
|
||||
}
|
||||
}
|
||||
return toksFrom.join('/');
|
||||
}
|
||||
|
||||
data.match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
/* 9.3.2.2 OPC_Relationships */
|
||||
if (y[0] === '<Relationship') {
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
|
||||
var canonictarget = resolveRelativePathIntoAbsolute(y.Target);
|
||||
rels[canonictarget] = rel;
|
||||
}
|
||||
});
|
||||
|
||||
return rels;
|
||||
}
|
||||
|
||||
|
68
bits/65_comments.js
Normal file
68
bits/65_comments.js
Normal file
@ -0,0 +1,68 @@
|
||||
/* 18.7.3 CT_Comment */
|
||||
function parseComments(data) {
|
||||
if(data.match(/<comments *\/>/)) {
|
||||
throw new Error('Not a valid comments xml');
|
||||
}
|
||||
var authors = [];
|
||||
var commentList = [];
|
||||
data.match(/<authors>([^\u2603]*)<\/authors>/m)[1].split('</author>').forEach(function(x) {
|
||||
if(x === "" || x.trim() === "") return;
|
||||
authors.push(x.match(/<author[^>]*>(.*)/)[1]);
|
||||
});
|
||||
data.match(/<commentList>([^\u2603]*)<\/commentList>/m)[1].split('</comment>').forEach(function(x, index) {
|
||||
if(x === "" || x.trim() === "") return;
|
||||
var y = parsexmltag(x.match(/<comment[^>]*>/)[0]);
|
||||
var comment = { author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid };
|
||||
var textMatch = x.match(/<text>([^\u2603]*)<\/text>/m);
|
||||
if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
|
||||
var rt = parse_si(textMatch[1]);
|
||||
comment.raw = rt.raw;
|
||||
comment.t = rt.t;
|
||||
comment.r = rt.r;
|
||||
commentList.push(comment);
|
||||
});
|
||||
return commentList;
|
||||
}
|
||||
|
||||
function parseCommentsAddToSheets(zip, dirComments, sheets, sheetRels) {
|
||||
for(var i = 0; i != dirComments.length; ++i) {
|
||||
var canonicalpath=dirComments[i];
|
||||
var comments=parseComments(getdata(getzipfile(zip, canonicalpath.replace(/^\//,''))));
|
||||
// find the sheets targeted by these comments
|
||||
var sheetNames = Object.keys(sheets);
|
||||
for(var j = 0; j != sheetNames.length; ++j) {
|
||||
var sheetName = sheetNames[j];
|
||||
var rels = sheetRels[sheetName];
|
||||
if (rels) {
|
||||
var rel = rels[canonicalpath];
|
||||
if (rel) {
|
||||
insertCommentsIntoSheet(sheetName, sheets[sheetName], comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertCommentsIntoSheet(sheetName, sheet, comments) {
|
||||
comments.forEach(function(comment) {
|
||||
var cell = sheet[comment.ref];
|
||||
if (!cell) {
|
||||
cell = {};
|
||||
sheet[comment.ref] = cell;
|
||||
var range = decode_range(sheet["!ref"]);
|
||||
var thisCell = decode_cell(comment.ref);
|
||||
if(range.s.r > thisCell.r) range.s.r = thisCell.r;
|
||||
if(range.e.r < thisCell.r) range.e.r = thisCell.r;
|
||||
if(range.s.c > thisCell.c) range.s.c = thisCell.c;
|
||||
if(range.e.c < thisCell.c) range.e.c = thisCell.c;
|
||||
var encoded = encode_range(range);
|
||||
if (encoded !== sheet["!ref"]) sheet["!ref"] = encoded;
|
||||
}
|
||||
|
||||
if (!cell.c) {
|
||||
cell.c = [];
|
||||
}
|
||||
cell.c.push({a: comment.author, t: comment.t, raw: comment.raw, r: comment.r});
|
||||
});
|
||||
}
|
||||
|
660
bits/70_xlsx.js
660
bits/70_xlsx.js
@ -1,660 +0,0 @@
|
||||
|
||||
var ct2type = {
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks",
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.main+xml":"workbooks", /*XLSM*/
|
||||
"application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
|
||||
"application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml": "calcchains",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml":"sheets",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml": "strs",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml":"styles",
|
||||
"application/vnd.openxmlformats-officedocument.theme+xml":"themes",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments",
|
||||
"foo": "bar"
|
||||
};
|
||||
|
||||
/* 18.2.28 (CT_WorkbookProtection) Defaults */
|
||||
var WBPropsDef = {
|
||||
allowRefreshQuery: '0',
|
||||
autoCompressPictures: '1',
|
||||
backupFile: '0',
|
||||
checkCompatibility: '0',
|
||||
codeName: '',
|
||||
date1904: '0',
|
||||
dateCompatibility: '1',
|
||||
//defaultThemeVersion: '0',
|
||||
filterPrivacy: '0',
|
||||
hidePivotFieldList: '0',
|
||||
promptedSolutions: '0',
|
||||
publishItems: '0',
|
||||
refreshAllConnections: false,
|
||||
saveExternalLinkValues: '1',
|
||||
showBorderUnselectedTables: '1',
|
||||
showInkAnnotation: '1',
|
||||
showObjects: 'all',
|
||||
showPivotChartFilter: '0'
|
||||
//updateLinks: 'userSet'
|
||||
};
|
||||
|
||||
/* 18.2.30 (CT_BookView) Defaults */
|
||||
var WBViewDef = {
|
||||
activeTab: '0',
|
||||
autoFilterDateGrouping: '1',
|
||||
firstSheet: '0',
|
||||
minimized: '0',
|
||||
showHorizontalScroll: '1',
|
||||
showSheetTabs: '1',
|
||||
showVerticalScroll: '1',
|
||||
tabRatio: '600',
|
||||
visibility: 'visible'
|
||||
//window{Height,Width}, {x,y}Window
|
||||
};
|
||||
|
||||
/* 18.2.19 (CT_Sheet) Defaults */
|
||||
var SheetDef = {
|
||||
state: 'visible'
|
||||
};
|
||||
|
||||
/* 18.2.2 (CT_CalcPr) Defaults */
|
||||
var CalcPrDef = {
|
||||
calcCompleted: 'true',
|
||||
calcMode: 'auto',
|
||||
calcOnSave: 'true',
|
||||
concurrentCalc: 'true',
|
||||
fullCalcOnLoad: 'false',
|
||||
fullPrecision: 'true',
|
||||
iterate: 'false',
|
||||
iterateCount: '100',
|
||||
iterateDelta: '0.001',
|
||||
refMode: 'A1'
|
||||
};
|
||||
|
||||
/* 18.2.3 (CT_CustomWorkbookView) Defaults */
|
||||
var CustomWBViewDef = {
|
||||
autoUpdate: 'false',
|
||||
changesSavedWin: 'false',
|
||||
includeHiddenRowCol: 'true',
|
||||
includePrintSettings: 'true',
|
||||
maximized: 'false',
|
||||
minimized: 'false',
|
||||
onlySync: 'false',
|
||||
personalView: 'false',
|
||||
showComments: 'commIndicator',
|
||||
showFormulaBar: 'true',
|
||||
showHorizontalScroll: 'true',
|
||||
showObjects: 'all',
|
||||
showSheetTabs: 'true',
|
||||
showStatusbar: 'true',
|
||||
showVerticalScroll: 'true',
|
||||
tabRatio: '600',
|
||||
xWindow: '0',
|
||||
yWindow: '0'
|
||||
};
|
||||
|
||||
var XMLNS_CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
var XMLNS_WB = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
|
||||
|
||||
var strs = {}; // shared strings
|
||||
var styles = {}; // shared styles
|
||||
var _ssfopts = {}; // spreadsheet formatting options
|
||||
|
||||
/* 18.3 Worksheets */
|
||||
function parseSheet(data) {
|
||||
if(!data) return data;
|
||||
/* 18.3.1.99 worksheet CT_Worksheet */
|
||||
var s = {};
|
||||
|
||||
/* 18.3.1.35 dimension CT_SheetDimension ? */
|
||||
var ref = data.match(/<dimension ref="([^"]*)"\s*\/>/);
|
||||
if(ref && ref.length == 2 && ref[1].indexOf(":") !== -1) s["!ref"] = ref[1];
|
||||
|
||||
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
|
||||
var q = ["v","f"];
|
||||
var sidx = 0;
|
||||
/* 18.3.1.80 sheetData CT_SheetData ? */
|
||||
if(!data.match(/<sheetData *\/>/))
|
||||
data.match(/<sheetData>([^\u2603]*)<\/sheetData>/m)[1].split("</row>").forEach(function(x) {
|
||||
if(x === "" || x.trim() === "") return;
|
||||
|
||||
/* 18.3.1.73 row CT_Row */
|
||||
var row = parsexmltag(x.match(/<row[^>]*>/)[0]);
|
||||
if(refguess.s.r > row.r - 1) refguess.s.r = row.r - 1;
|
||||
if(refguess.e.r < row.r - 1) refguess.e.r = row.r - 1;
|
||||
|
||||
/* 18.3.1.4 c CT_Cell */
|
||||
var cells = x.substr(x.indexOf('>')+1).split(/<c/);
|
||||
cells.forEach(function(c, idx) { if(c === "" || c.trim() === "") return;
|
||||
var cref = c.match(/r="([^"]*)"/);
|
||||
c = "<c" + c;
|
||||
if(cref && cref.length == 2) {
|
||||
var cref_cell = decode_cell(cref[1]);
|
||||
idx = cref_cell.c;
|
||||
}
|
||||
if(refguess.s.c > idx) refguess.s.c = idx;
|
||||
if(refguess.e.c < idx) refguess.e.c = idx;
|
||||
var cell = parsexmltag((c.match(/<c[^>]*>/)||[c])[0]); delete cell[0];
|
||||
var d = c.substr(c.indexOf('>')+1);
|
||||
var p = {};
|
||||
q.forEach(function(f){var x=d.match(matchtag(f));if(x)p[f]=unescapexml(x[1]);});
|
||||
|
||||
/* SCHEMA IS ACTUALLY INCORRECT HERE. IF A CELL HAS NO T, EMIT "" */
|
||||
if(cell.t === undefined && p.v === undefined) { p.t = "str"; p.v = undefined; }
|
||||
else p.t = (cell.t ? cell.t : "n"); // default is "n" in schema
|
||||
switch(p.t) {
|
||||
case 'n': p.v = parseFloat(p.v); break;
|
||||
case 's': {
|
||||
sidx = parseInt(p.v, 10);
|
||||
p.v = strs[sidx].t;
|
||||
p.r = strs[sidx].r;
|
||||
} break;
|
||||
case 'str': if(p.v) p.v = utf8read(p.v); break; // normal string
|
||||
case 'inlineStr':
|
||||
p.t = 'str'; p.v = unescapexml((d.match(matchtag('t'))||["",""])[1]);
|
||||
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;
|
||||
/* in case of error, stick value in .raw */
|
||||
case 'e': p.raw = p.v; p.v = undefined; break;
|
||||
default: throw "Unrecognized cell type: " + p.t;
|
||||
}
|
||||
|
||||
/* formatting */
|
||||
var fmtid = 0;
|
||||
if(cell.s && styles.CellXf) {
|
||||
var cf = styles.CellXf[cell.s];
|
||||
if(cf && cf.numFmtId) fmtid = cf.numFmtId;
|
||||
}
|
||||
p.raw = p.v;
|
||||
p.rawt = p.t;
|
||||
try {
|
||||
p.v = SSF.format(fmtid,p.v,_ssfopts);
|
||||
p.t = 'str';
|
||||
} catch(e) { p.v = p.raw; p.t = p.rawt; }
|
||||
|
||||
s[cell.r] = p;
|
||||
});
|
||||
});
|
||||
if(!s["!ref"]) s["!ref"] = encode_range(refguess);
|
||||
return s;
|
||||
}
|
||||
|
||||
function parseProps(data) {
|
||||
var p = { Company:'' }, q = {};
|
||||
var strings = ["Application", "DocSecurity", "Company", "AppVersion"];
|
||||
var bools = ["HyperlinksChanged","SharedDoc","LinksUpToDate","ScaleCrop"];
|
||||
var xtra = ["HeadingPairs", "TitlesOfParts"];
|
||||
var xtracp = ["category", "contentStatus", "lastModifiedBy", "lastPrinted", "revision", "version"];
|
||||
var xtradc = ["creator", "description", "identifier", "language", "subject", "title"];
|
||||
var xtradcterms = ["created", "modified"];
|
||||
xtra = xtra.concat(xtracp.map(function(x) { return "cp:" + x; }));
|
||||
xtra = xtra.concat(xtradc.map(function(x) { return "dc:" + x; }));
|
||||
xtra = xtra.concat(xtradcterms.map(function(x) { return "dcterms:" + x; }));
|
||||
|
||||
|
||||
strings.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1];});
|
||||
bools.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1] == "true";});
|
||||
xtra.forEach(function(f) {
|
||||
var cur = data.match(new RegExp("<" + f + "[^>]*>(.*)<\/" + f + ">"));
|
||||
if(cur && cur.length > 0) q[f] = cur[1];
|
||||
});
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
switch(v[i].v) {
|
||||
case "Worksheets": widx = j; p.Worksheets = +v[++i]; break;
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
}
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(utf8read);
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
p.Creator = q["dc:creator"];
|
||||
p.LastModifiedBy = q["cp:lastModifiedBy"];
|
||||
p.CreatedDate = new Date(q["dcterms:created"]);
|
||||
p.ModifiedDate = new Date(q["dcterms:modified"]);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* 18.6 Calculation Chain */
|
||||
function parseDeps(data) {
|
||||
var d = [];
|
||||
var l = 0, i = 1;
|
||||
(data.match(/<[^>]*>/g)||[]).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
/* 18.6.2 calcChain CT_CalcChain 1 */
|
||||
case '<calcChain': case '<calcChain>': case '</calcChain>': break;
|
||||
/* 18.6.1 c CT_CalcCell 1 */
|
||||
case '<c': delete y[0]; if(y.i) i = y.i; else y.i = i; d.push(y); break;
|
||||
}
|
||||
});
|
||||
return d;
|
||||
}
|
||||
|
||||
var ctext = {};
|
||||
|
||||
function parseCT(data) {
|
||||
if(!data || !data.match) return data;
|
||||
var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], strs:[], comments: [], xmlns: "" };
|
||||
(data.match(/<[^>]*>/g)||[]).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
case '<Types': ct.xmlns = y.xmlns; break;
|
||||
case '<Default': ctext[y.Extension] = y.ContentType; break;
|
||||
case '<Override':
|
||||
if(y.ContentType in ct2type)ct[ct2type[y.ContentType]].push(y.PartName);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if(ct.xmlns !== XMLNS_CT) throw new Error("Unknown Namespace: " + ct.xmlns);
|
||||
ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : "";
|
||||
ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
|
||||
ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
|
||||
delete ct.calcchains;
|
||||
return ct;
|
||||
}
|
||||
|
||||
|
||||
/* 18.2 Workbook */
|
||||
function parseWB(data) {
|
||||
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
|
||||
var pass = false;
|
||||
data.match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
|
||||
/* 18.2.27 workbook CT_Workbook 1 */
|
||||
case '<workbook': wb.xmlns = y.xmlns; break;
|
||||
case '</workbook>': break;
|
||||
|
||||
/* 18.2.13 fileVersion CT_FileVersion ? */
|
||||
case '<fileVersion': delete y[0]; wb.AppVersion = y; break;
|
||||
case '<fileVersion/>': break;
|
||||
|
||||
/* 18.2.12 fileSharing CT_FileSharing ? */
|
||||
case '<fileSharing': case '<fileSharing/>': break;
|
||||
|
||||
/* 18.2.28 workbookPr CT_WorkbookPr ? */
|
||||
case '<workbookPr': delete y[0]; wb.WBProps = y; break;
|
||||
case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
|
||||
|
||||
/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
|
||||
case '<workbookProtection/>': break;
|
||||
|
||||
/* 18.2.1 bookViews CT_BookViews ? */
|
||||
case '<bookViews>': case '</bookViews>': break;
|
||||
/* 18.2.30 workbookView CT_BookView + */
|
||||
case '<workbookView': delete y[0]; wb.WBView.push(y); break;
|
||||
|
||||
/* 18.2.20 sheets CT_Sheets 1 */
|
||||
case '<sheets>': case '</sheets>': break; // aggregate sheet
|
||||
/* 18.2.19 sheet CT_Sheet + */
|
||||
case '<sheet': delete y[0]; y.name = utf8read(y.name); wb.Sheets.push(y); break;
|
||||
|
||||
/* 18.2.15 functionGroups CT_FunctionGroups ? */
|
||||
case '<functionGroups': case '<functionGroups/>': break;
|
||||
/* 18.2.14 functionGroup CT_FunctionGroup + */
|
||||
case '<functionGroup': break;
|
||||
|
||||
/* 18.2.9 externalReferences CT_ExternalReferences ? */
|
||||
case '<externalReferences': case '</externalReferences>': break;
|
||||
/* 18.2.8 externalReference CT_ExternalReference + */
|
||||
case '<externalReference': break;
|
||||
|
||||
/* 18.2.6 definedNames CT_DefinedNames ? */
|
||||
case '<definedNames/>': break;
|
||||
case '<definedNames>': pass=true; break;
|
||||
case '</definedNames>': pass=false; break;
|
||||
/* 18.2.5 definedName CT_DefinedName + */
|
||||
case '<definedName': case '<definedName/>': case '</definedName>': break;
|
||||
|
||||
/* 18.2.2 calcPr CT_CalcPr ? */
|
||||
case '<calcPr': delete y[0]; wb.CalcPr = y; break;
|
||||
case '<calcPr/>': delete y[0]; wb.CalcPr = y; break;
|
||||
|
||||
/* 18.2.16 oleSize CT_OleSize ? (ref required) */
|
||||
case '<oleSize': break;
|
||||
|
||||
/* 18.2.4 customWorkbookViews CT_CustomWorkbookViews ? */
|
||||
case '<customWorkbookViews>': case '</customWorkbookViews>': case '<customWorkbookViews': break;
|
||||
/* 18.2.3 customWorkbookView CT_CustomWorkbookView + */
|
||||
case '<customWorkbookView': case '</customWorkbookView>': break;
|
||||
|
||||
/* 18.2.18 pivotCaches CT_PivotCaches ? */
|
||||
case '<pivotCaches>': case '</pivotCaches>': case '<pivotCaches': break;
|
||||
/* 18.2.17 pivotCache CT_PivotCache ? */
|
||||
case '<pivotCache': break;
|
||||
|
||||
/* 18.2.21 smartTagPr CT_SmartTagPr ? */
|
||||
case '<smartTagPr': case '<smartTagPr/>': break;
|
||||
|
||||
/* 18.2.23 smartTagTypes CT_SmartTagTypes ? */
|
||||
case '<smartTagTypes': case '<smartTagTypes>': case '</smartTagTypes>': break;
|
||||
/* 18.2.22 smartTagType CT_SmartTagType ? */
|
||||
case '<smartTagType': break;
|
||||
|
||||
/* 18.2.24 webPublishing CT_WebPublishing ? */
|
||||
case '<webPublishing': case '<webPublishing/>': break;
|
||||
|
||||
/* 18.2.11 fileRecoveryPr CT_FileRecoveryPr ? */
|
||||
case '<fileRecoveryPr': case '<fileRecoveryPr/>': break;
|
||||
|
||||
/* 18.2.26 webPublishObjects CT_WebPublishObjects ? */
|
||||
case '<webPublishObjects>': case '<webPublishObjects': case '</webPublishObjects>': break;
|
||||
/* 18.2.25 webPublishObject CT_WebPublishObject ? */
|
||||
case '<webPublishObject': break;
|
||||
|
||||
/* 18.2.10 extLst CT_ExtensionList ? */
|
||||
case '<extLst>': case '</extLst>': case '<extLst/>': break;
|
||||
/* 18.2.7 ext CT_Extension + */
|
||||
case '<ext': pass=true; break; //TODO: check with versions of excel
|
||||
case '</ext>': pass=false; break;
|
||||
|
||||
/* Others */
|
||||
case '<mx:ArchID': break;
|
||||
case '<mc:AlternateContent': pass=true; break;
|
||||
case '</mc:AlternateContent>': pass=false; break;
|
||||
}
|
||||
});
|
||||
if(wb.xmlns !== XMLNS_WB) throw new Error("Unknown Namespace: " + wb.xmlns);
|
||||
|
||||
var z;
|
||||
/* defaults */
|
||||
for(z in WBPropsDef) if(typeof wb.WBProps[z] === 'undefined') wb.WBProps[z] = WBPropsDef[z];
|
||||
for(z in CalcPrDef) if(typeof wb.CalcPr[z] === 'undefined') wb.CalcPr[z] = CalcPrDef[z];
|
||||
|
||||
wb.WBView.forEach(function(w){for(var z in WBViewDef) if(typeof w[z] === 'undefined') w[z]=WBViewDef[z]; });
|
||||
wb.Sheets.forEach(function(w){for(var z in SheetDef) if(typeof w[z] === 'undefined') w[z]=SheetDef[z]; });
|
||||
|
||||
_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
|
||||
|
||||
return wb;
|
||||
}
|
||||
|
||||
/* 18.8.31 numFmts CT_NumFmts */
|
||||
function parseNumFmts(t) {
|
||||
styles.NumberFmt = [];
|
||||
for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<numFmts': case '</numFmts>': case '<numFmts/>': break;
|
||||
case '<numFmt': {
|
||||
var f=unescapexml(y.formatCode), i=parseInt(y.numFmtId,10);
|
||||
styles.NumberFmt[i] = f; SSF.load(f,i);
|
||||
} break;
|
||||
default: throw 'unrecognized ' + y[0] + ' in numFmts';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 18.8.10 cellXfs CT_CellXfs */
|
||||
function parseCXfs(t) {
|
||||
styles.CellXf = [];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<cellXfs': case '<cellXfs/>': case '</cellXfs>': break;
|
||||
|
||||
/* 18.8.45 xf CT_Xf */
|
||||
case '<xf': if(y.numFmtId) y.numFmtId = parseInt(y.numFmtId, 10);
|
||||
styles.CellXf.push(y); break;
|
||||
case '</xf>': break;
|
||||
|
||||
/* 18.8.1 alignment CT_CellAlignment */
|
||||
case '<alignment': break;
|
||||
|
||||
/* 18.8.33 protection CT_CellProtection */
|
||||
case '<protection': case '</protection>': case '<protection/>': break;
|
||||
|
||||
case '<extLst': case '</extLst>': break;
|
||||
case '<ext': break;
|
||||
default: throw 'unrecognized ' + y[0] + ' in cellXfs';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 18.8 Styles CT_Stylesheet*/
|
||||
function parseStyles(data) {
|
||||
/* 18.8.39 styleSheet CT_Stylesheet */
|
||||
var t;
|
||||
|
||||
/* numFmts CT_NumFmts ? */
|
||||
if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parseNumFmts(t);
|
||||
|
||||
/* fonts CT_Fonts ? */
|
||||
/* fills CT_Fills ? */
|
||||
/* borders CT_Borders ? */
|
||||
/* cellStyleXfs CT_CellStyleXfs ? */
|
||||
|
||||
/* cellXfs CT_CellXfs ? */
|
||||
if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parseCXfs(t);
|
||||
|
||||
/* dxfs CT_Dxfs ? */
|
||||
/* tableStyles CT_TableStyles ? */
|
||||
/* colors CT_Colors ? */
|
||||
/* extLst CT_ExtensionList ? */
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
function parseRels(data, currentFilePath) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
currentFilePath = '/'+currentFilePath;
|
||||
}
|
||||
var rels = {};
|
||||
|
||||
var resolveRelativePathIntoAbsolute = function (to) {
|
||||
var toksFrom = currentFilePath.split('/');
|
||||
toksFrom.pop(); // folder path
|
||||
var toksTo = to.split('/');
|
||||
var reversed = [];
|
||||
while (toksTo.length !== 0) {
|
||||
var tokTo = toksTo.shift();
|
||||
if (tokTo === '..') {
|
||||
toksFrom.pop();
|
||||
} else if (tokTo !== '.') {
|
||||
toksFrom.push(tokTo);
|
||||
}
|
||||
}
|
||||
return toksFrom.join('/');
|
||||
}
|
||||
|
||||
data.match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
/* 9.3.2.2 OPC_Relationships */
|
||||
if (y[0] === '<Relationship') {
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
|
||||
var canonictarget = resolveRelativePathIntoAbsolute(y.Target);
|
||||
rels[canonictarget] = rel;
|
||||
}
|
||||
});
|
||||
|
||||
return rels;
|
||||
}
|
||||
|
||||
/* 18.7.3 CT_Comment */
|
||||
function parseComments(data) {
|
||||
if(data.match(/<comments *\/>/)) {
|
||||
throw new Error('Not a valid comments xml');
|
||||
}
|
||||
var authors = [];
|
||||
var commentList = [];
|
||||
data.match(/<authors>([^\u2603]*)<\/authors>/m)[1].split('</author>').forEach(function(x) {
|
||||
if(x === "" || x.trim() === "") return;
|
||||
authors.push(x.match(/<author[^>]*>(.*)/)[1]);
|
||||
});
|
||||
data.match(/<commentList>([^\u2603]*)<\/commentList>/m)[1].split('</comment>').forEach(function(x, index) {
|
||||
if(x === "" || x.trim() === "") return;
|
||||
var y = parsexmltag(x.match(/<comment[^>]*>/)[0]);
|
||||
var comment = { author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid };
|
||||
var textMatch = x.match(/<text>([^\u2603]*)<\/text>/m);
|
||||
if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
|
||||
var rt = parse_si(textMatch[1]);
|
||||
comment.raw = rt.raw;
|
||||
comment.t = rt.t;
|
||||
comment.r = rt.r;
|
||||
commentList.push(comment);
|
||||
});
|
||||
return commentList;
|
||||
}
|
||||
|
||||
function parseCommentsAddToSheets(zip, dirComments, sheets, sheetRels) {
|
||||
for(var i = 0; i != dirComments.length; ++i) {
|
||||
var canonicalpath=dirComments[i];
|
||||
var comments=parseComments(getdata(getzipfile(zip, canonicalpath.replace(/^\//,''))));
|
||||
// find the sheets targeted by these comments
|
||||
var sheetNames = Object.keys(sheets);
|
||||
for(var j = 0; j != sheetNames.length; ++j) {
|
||||
var sheetName = sheetNames[j];
|
||||
var rels = sheetRels[sheetName];
|
||||
if (rels) {
|
||||
var rel = rels[canonicalpath];
|
||||
if (rel) {
|
||||
insertCommentsIntoSheet(sheetName, sheets[sheetName], comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertCommentsIntoSheet(sheetName, sheet, comments) {
|
||||
comments.forEach(function(comment) {
|
||||
var cell = sheet[comment.ref];
|
||||
if (!cell) {
|
||||
cell = {};
|
||||
sheet[comment.ref] = cell;
|
||||
var range = decode_range(sheet["!ref"]);
|
||||
var thisCell = decode_cell(comment.ref);
|
||||
if(range.s.r > thisCell.r) range.s.r = thisCell.r;
|
||||
if(range.e.r < thisCell.r) range.e.r = thisCell.r;
|
||||
if(range.s.c > thisCell.c) range.s.c = thisCell.c;
|
||||
if(range.e.c < thisCell.c) range.e.c = thisCell.c;
|
||||
var encoded = encode_range(range);
|
||||
if (encoded !== sheet["!ref"]) sheet["!ref"] = encoded;
|
||||
}
|
||||
|
||||
if (!cell.c) {
|
||||