XLS/XLSX/XLSB Interpret Chartsheets as Data Tables
- chartsheets are now stored as sheets with "!type" set to "chart" - fixed Strict XML Workbook Relationships - stubbed support for dialog and macro sheets - removed legacy XLS `!range` field
This commit is contained in:
parent
471275b761
commit
663270b762
@ -7,6 +7,10 @@ changes may not be included if they are not expected to break existing code.
|
||||
|
||||
## Unreleased
|
||||
|
||||
* XLS legacy `!range` field removed
|
||||
|
||||
## 0.9.6 (2017-03-25)
|
||||
|
||||
* `sheet_to_json` now passes `null` values when `raw` is set to `true`
|
||||
* `sheet_to_json` treats `null` stub cells as values in conjunction with `raw`
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -69,6 +69,10 @@ dist-deps: ## Copy dependencies for distribution
|
||||
.PHONY: aux
|
||||
aux: $(AUXTARGETS)
|
||||
|
||||
.PHONY: bytes
|
||||
bytes: ## display minified and gzipped file sizes
|
||||
for i in dist/xlsx.min.js dist/xlsx.{core,full}.min.js; do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
|
||||
|
||||
.PHONY: graph
|
||||
graph: formats.png ## Rebuild format conversion graph
|
||||
formats.png: formats.dot
|
||||
|
12
README.md
12
README.md
@ -38,6 +38,7 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
|
||||
+ [Data Types](#data-types)
|
||||
+ [Dates](#dates)
|
||||
* [Worksheet Object](#worksheet-object)
|
||||
* [Chartsheet Object](#chartsheet-object)
|
||||
* [Workbook Object](#workbook-object)
|
||||
* [Document Features](#document-features)
|
||||
+ [Formulae](#formulae)
|
||||
@ -543,6 +544,13 @@ Special worksheet keys (accessible as `worksheet[key]`, each starting with `!`):
|
||||
will write all cells in the merge range if they exist, so be sure that only
|
||||
the first cell (upper-left) in the range is set.
|
||||
|
||||
### Chartsheet Object
|
||||
|
||||
Chartsheets are represented as standard worksheets. They are distinguished with
|
||||
the `!type` property set to `"chart"`.
|
||||
|
||||
The underlying data and `!ref` refer to the cached data in the chartsheet.
|
||||
|
||||
### Workbook Object
|
||||
|
||||
`workbook.SheetNames` is an ordered list of the sheets in the workbook
|
||||
@ -762,8 +770,8 @@ The exported `write` and `writeFile` functions accept an options argument:
|
||||
|
||||
- `bookSST` is slower and more memory intensive, but has better compatibility
|
||||
with older versions of iOS Numbers
|
||||
- The raw data is the only thing guaranteed to be saved. Formulae, formatting,
|
||||
and other niceties may not be serialized (pending CSF standardization)
|
||||
- The raw data is the only thing guaranteed to be saved. Features not described
|
||||
in this README may not be serialized.
|
||||
- `cellDates` only applies to XLSX output and is not guaranteed to work with
|
||||
third-party readers. Excel itself does not usually write cells with type `d`
|
||||
so non-Excel tools may ignore the data or blow up in the presence of dates.
|
||||
|
@ -60,3 +60,15 @@ if (typeof exports !== 'undefined') {
|
||||
_fs = require('fs');
|
||||
}
|
||||
}
|
||||
|
||||
function resolve_path(path/*:string*/, base/*:string*/)/*:string*/ {
|
||||
var result = base.split('/');
|
||||
if(base.slice(-1) != "/") result.pop(); // folder path
|
||||
var target = path.split('/');
|
||||
while (target.length !== 0) {
|
||||
var step = target.shift();
|
||||
if (step === '..') result.pop();
|
||||
else if (step !== '.') result.push(step);
|
||||
}
|
||||
return result.join('/');
|
||||
}
|
||||
|
@ -10,17 +10,7 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
/* Worksheet */
|
||||
"application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */
|
||||
|
||||
/* Chartsheet */
|
||||
"application/vnd.ms-excel.chartsheet": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": "TODO",
|
||||
|
||||
/* Dialogsheet */
|
||||
"application/vnd.ms-excel.dialogsheet": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": "TODO",
|
||||
|
||||
/* Macrosheet */
|
||||
"application/vnd.ms-excel.macrosheet": "TODO",
|
||||
"application/vnd.ms-excel.macrosheet+xml": "TODO",
|
||||
"application/vnd.ms-excel.intlmacrosheet": "TODO",
|
||||
"application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */
|
||||
|
||||
@ -31,6 +21,7 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
|
||||
/* Custom Data Properties */
|
||||
"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO",
|
||||
|
||||
/* Comments */
|
||||
"application/vnd.ms-excel.comments": "comments",
|
||||
@ -110,6 +101,9 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
/* Themes */
|
||||
"application/vnd.openxmlformats-officedocument.theme+xml": "themes",
|
||||
|
||||
/* Theme Override */
|
||||
"application/vnd.openxmlformats-officedocument.themeOverride+xml": "TODO",
|
||||
|
||||
/* Timeline */
|
||||
"application/vnd.ms-excel.Timeline+xml": "TODO", /* verify */
|
||||
"application/vnd.ms-excel.TimelineCache+xml": "TODO", /* verify */
|
||||
@ -146,6 +140,9 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
"application/vnd.openxmlformats-package.relationships+xml": "rels",
|
||||
"application/vnd.openxmlformats-officedocument.oleObject": "TODO",
|
||||
|
||||
/* Image */
|
||||
"image/png": "TODO",
|
||||
|
||||
"sheet": "js"
|
||||
}/*:any*/);
|
||||
|
||||
@ -161,11 +158,23 @@ var CT_LIST = (function(){
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
||||
xlsb: "application/vnd.ms-excel.sharedStrings"
|
||||
},
|
||||
sheets: {
|
||||
sheets: { /* Worksheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.worksheet"
|
||||
},
|
||||
styles: {/* Styles */
|
||||
charts: { /* Chartsheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.chartsheet"
|
||||
},
|
||||
dialogs: { /* Dialogsheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.dialogsheet"
|
||||
},
|
||||
macros: { /* Macrosheet (Excel 4.0 Macros) */
|
||||
xlsx: "application/vnd.ms-excel.macrosheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.macrosheet"
|
||||
},
|
||||
styles: { /* Styles */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
||||
xlsb: "application/vnd.ms-excel.styles"
|
||||
}
|
||||
@ -180,9 +189,12 @@ var type2ct/*{[string]:Array<string>}*/ = evert_arr(ct2type);
|
||||
XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
|
||||
function parse_ct(data/*:?string*/, opts) {
|
||||
var ct = ({ workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
|
||||
TODO:[], rels:[], xmlns: "" }/*:any*/);
|
||||
var ct = ({
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[],
|
||||
rels:[], strs:[], comments:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [],
|
||||
TODO:[], xmlns: "" }/*:any*/);
|
||||
if(!data || !data.match) return ct;
|
||||
var ctext = {};
|
||||
(data.match(tagregex)||[]).forEach(function(x) {
|
||||
|
@ -1,9 +1,15 @@
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
/* 9.3 Relationships */
|
||||
var RELS = ({
|
||||
WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||
SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||
}/*:any*/);
|
||||
|
||||
/* 9.3.3 Representing Relationships */
|
||||
function get_rels_path(file/*:string*/)/*:string*/ {
|
||||
var n = file.lastIndexOf("/");
|
||||
return file.substr(0,n) + '/_rels' + file.substr(n) + ".rels";
|
||||
}
|
||||
|
||||
function parse_rels(data/*:?string*/, currentFilePath/*:string*/) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
@ -11,28 +17,13 @@ function parse_rels(data/*:?string*/, currentFilePath/*:string*/) {
|
||||
}
|
||||
var rels = {};
|
||||
var hash = {};
|
||||
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(tagregex)||[]).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 = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
|
||||
var canonictarget = y.TargetMode === 'External' ? y.Target : resolve_path(y.Target, currentFilePath);
|
||||
rels[canonictarget] = rel;
|
||||
hash[y.Id] = rel;
|
||||
}
|
||||
|
@ -33,9 +33,12 @@ function parse_ext_props(data, p) {
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0, cidx = -1;
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return x.v; });
|
||||
var idx = 0, len = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
len = +(v[++i].v);
|
||||
switch(v[i].v) {
|
||||
case "Worksheets":
|
||||
case "工作表":
|
||||
case "Листы":
|
||||
case "ワークシート":
|
||||
@ -47,17 +50,24 @@ function parse_ext_props(data, p) {
|
||||
case "Folhas de cálculo":
|
||||
case "Planilhas":
|
||||
case "Werkbladen":
|
||||
case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
|
||||
p.Worksheets = len;
|
||||
p.SheetNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
|
||||
case "Named Ranges":
|
||||
case "Benannte Bereiche":
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
p.NamedRanges = len;
|
||||
p.DefinedNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
|
||||
case "Charts": cidx = j; p.Charts = +(v[++i].v); break;
|
||||
default: break; //throw new Error(v[i].v);
|
||||
case "Charts":
|
||||
case "Diagramme":
|
||||
p.Chartsheets = len;
|
||||
p.ChartNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
}
|
||||
idx += len;
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return x.v; });
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
@ -247,10 +247,10 @@ function parse_PropertySetStream(file, PIDSI) {
|
||||
var SystemIdentifier = blob.read_shift(4);
|
||||
blob.chk(CFB.utils.consts.HEADER_CLSID, 'CLSID: ');
|
||||
NumSets = blob.read_shift(4);
|
||||
if(NumSets !== 1 && NumSets !== 2) throw "Unrecognized #Sets: " + NumSets;
|
||||
if(NumSets !== 1 && NumSets !== 2) throw new Error("Unrecognized #Sets: " + NumSets);
|
||||
FMTID0 = blob.read_shift(16); Offset0 = blob.read_shift(4);
|
||||
|
||||
if(NumSets === 1 && Offset0 !== blob.l) throw "Length mismatch";
|
||||
if(NumSets === 1 && Offset0 !== blob.l) throw new Error("Length mismatch: " + Offset0 + " !== " + blob.l);
|
||||
else if(NumSets === 2) { FMTID1 = blob.read_shift(16); Offset1 = blob.read_shift(4); }
|
||||
var PSet0 = parse_PropertySet(blob, PIDSI);
|
||||
|
||||
@ -405,7 +405,7 @@ var parse_HyperlinkMoniker = function(blob, length) {
|
||||
switch(clsid) {
|
||||
case "e0c9ea79f9bace118c8200aa004ba90b": return parse_URLMoniker(blob, length);
|
||||
case "0303000000000000c000000000000046": return parse_FileMoniker(blob, length);
|
||||
default: throw "unsupported moniker " + clsid;
|
||||
default: throw new Error("Unsupported Moniker " + clsid);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,7 @@ function parse_AddinUdf(blob, length, opts) {
|
||||
var udfName = parse_ShortXLUnicodeString(blob, length, opts);
|
||||
var cb = blob.read_shift(2);
|
||||
l -= blob.l;
|
||||
if(cb !== l) throw "Malformed AddinUdf: padding = " + l + " != " + cb;
|
||||
if(cb !== l) throw new Error("Malformed AddinUdf: padding = " + l + " != " + cb);
|
||||
blob.l += cb;
|
||||
return udfName;
|
||||
}
|
||||
@ -127,7 +127,7 @@ function parse_FtArray(blob, length, ot) {
|
||||
fts.push(FtTab[ft](blob, s + length - blob.l));
|
||||
} catch(e) { blob.l = s + length; return fts; }
|
||||
}
|
||||
if(blob.l != s + length) blob.l = s + length; //throw "bad Object Ft-sequence";
|
||||
if(blob.l != s + length) blob.l = s + length; //throw new Error("bad Object Ft-sequence");
|
||||
return fts;
|
||||
}
|
||||
|
||||
@ -138,8 +138,9 @@ var parse_FontIndex = parseuint16;
|
||||
|
||||
/* 2.4.21 */
|
||||
function parse_BOF(blob, length) {
|
||||
var o = {};
|
||||
var o = {BIFFVer:0, dt:0};
|
||||
o.BIFFVer = blob.read_shift(2); length -= 2;
|
||||
if(length >= 2) { o.dt = blob.read_shift(2); blob.l -= 2; }
|
||||
switch(o.BIFFVer) {
|
||||
case 0x0600: /* BIFF8 */
|
||||
case 0x0500: /* BIFF5 */
|
||||
@ -147,6 +148,7 @@ function parse_BOF(blob, length) {
|
||||
break;
|
||||
default: if(length > 6) throw new Error("Unexpected BIFF Ver " + o.BIFFVer);
|
||||
}
|
||||
|
||||
blob.read_shift(length);
|
||||
return o;
|
||||
}
|
||||
@ -156,7 +158,7 @@ function parse_BOF(blob, length) {
|
||||
function parse_InterfaceHdr(blob, length) {
|
||||
if(length === 0) return 0x04b0;
|
||||
var q;
|
||||
if((q=blob.read_shift(2))!==0x04b0) throw 'InterfaceHdr codePage ' + q;
|
||||
if((q=blob.read_shift(2))!==0x04b0) throw new Error("InterfaceHdr codePage " + q);
|
||||
return 0x04b0;
|
||||
}
|
||||
|
||||
@ -222,7 +224,7 @@ function parse_Row(blob, length) {
|
||||
/* 2.4.125 */
|
||||
function parse_ForceFullCalculation(blob, length) {
|
||||
var header = parse_frtHeader(blob);
|
||||
if(header.type != 0x08A3) throw "Invalid Future Record " + header.type;
|
||||
if(header.type != 0x08A3) throw new Error("Invalid Future Record " + header.type);
|
||||
var fullcalc = blob.read_shift(4);
|
||||
return fullcalc !== 0x0;
|
||||
}
|
||||
@ -311,9 +313,9 @@ function parse_MulRk(blob, length) {
|
||||
var rw = blob.read_shift(2), col = blob.read_shift(2);
|
||||
var rkrecs = [];
|
||||
while(blob.l < target) rkrecs.push(parse_RkRec(blob));
|
||||
if(blob.l !== target) throw "MulRK read error";
|
||||
if(blob.l !== target) throw new Error("MulRK read error");
|
||||
var lastcol = blob.read_shift(2);
|
||||
if(rkrecs.length != lastcol - col + 1) throw "MulRK length mismatch";
|
||||
if(rkrecs.length != lastcol - col + 1) throw new Error("MulRK length mismatch");
|
||||
return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
|
||||
}
|
||||
/* 2.4.174 */
|
||||
@ -322,9 +324,9 @@ function parse_MulBlank(blob, length) {
|
||||
var rw = blob.read_shift(2), col = blob.read_shift(2);
|
||||
var ixfes = [];
|
||||
while(blob.l < target) ixfes.push(blob.read_shift(2));
|
||||
if(blob.l !== target) throw "MulBlank read error";
|
||||
if(blob.l !== target) throw new Error("MulBlank read error");
|
||||
var lastcol = blob.read_shift(2);
|
||||
if(ixfes.length != lastcol - col + 1) throw "MulBlank length mismatch";
|
||||
if(ixfes.length != lastcol - col + 1) throw new Error("MulBlank length mismatch");
|
||||
return {r:rw, c:col, C:lastcol, ixfe:ixfes};
|
||||
}
|
||||
|
||||
@ -388,7 +390,7 @@ function parse_Guts(blob, length) {
|
||||
var out = [blob.read_shift(2), blob.read_shift(2)];
|
||||
if(out[0] !== 0) out[0]--;
|
||||
if(out[1] !== 0) out[1]--;
|
||||
if(out[0] > 7 || out[1] > 7) throw "Bad Gutters: " + out.join("|");
|
||||
if(out[0] > 7 || out[1] > 7) throw new Error("Bad Gutters: " + out.join("|"));
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -555,14 +557,14 @@ try {
|
||||
//var fmla = parse_ObjFmla(blob, s + length - blob.l);
|
||||
|
||||
for(var i = 1; i < blob.lens.length-1; ++i) {
|
||||
if(blob.l-s != blob.lens[i]) throw "TxO: bad continue record";
|
||||
if(blob.l-s != blob.lens[i]) throw new Error("TxO: bad continue record");
|
||||
var hdr = blob[blob.l];
|
||||
var t = parse_XLUnicodeStringNoCch(blob, blob.lens[i+1]-blob.lens[i]-1);
|
||||
texts += t;
|
||||
if(texts.length >= (hdr ? cchText : 2*cchText)) break;
|
||||
}
|
||||
if(texts.length !== cchText && texts.length !== cchText*2) {
|
||||
throw "cchText: " + cchText + " != " + texts.length;
|
||||
throw new Error("cchText: " + cchText + " != " + texts.length);
|
||||
}
|
||||
|
||||
blob.l = s + length;
|
||||
@ -570,9 +572,9 @@ try {
|
||||
// var rgTxoRuns = [];
|
||||
// for(var j = 0; j != cbRuns/8-1; ++j) blob.l += 8;
|
||||
// var cchText2 = blob.read_shift(2);
|
||||
// if(cchText2 !== cchText) throw "TxOLastRun mismatch: " + cchText2 + " " + cchText;
|
||||
// if(cchText2 !== cchText) throw new Error("TxOLastRun mismatch: " + cchText2 + " " + cchText);
|
||||
// blob.l += 6;
|
||||
// if(s + length != blob.l) throw "TxO " + (s + length) + ", at " + blob.l;
|
||||
// if(s + length != blob.l) throw new Error("TxO " + (s + length) + ", at " + blob.l);
|
||||
return { t: texts };
|
||||
} catch(e) { blob.l = s + length; return { t: texts }; }
|
||||
}
|
||||
@ -642,6 +644,15 @@ function parse_ColInfo(blob, length, opts) {
|
||||
return {s:colFirst, e:colLast, w:coldx, ixfe:ixfe, flags:flags};
|
||||
}
|
||||
|
||||
/* 2.4.261 */
|
||||
function parse_ShtProps(blob, length, opts) {
|
||||
var def = {area:false};
|
||||
if(opts.biff != 5) { blob.l += length; return def; }
|
||||
var d = blob.read_shift(1); blob.l += 3;
|
||||
if((d & 0x10)) def.area = true;
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
var parse_Style = parsenoop;
|
||||
var parse_StyleExt = parsenoop;
|
||||
@ -934,7 +945,6 @@ var parse_Surf = parsenoop;
|
||||
var parse_RadarArea = parsenoop;
|
||||
var parse_AxisParent = parsenoop;
|
||||
var parse_LegendException = parsenoop;
|
||||
var parse_ShtProps = parsenoop;
|
||||
var parse_SerToCrt = parsenoop;
|
||||
var parse_AxesUsed = parsenoop;
|
||||
var parse_SBaseRef = parsenoop;
|
||||
|
@ -267,7 +267,7 @@ function parse_cellXfs(t, styles, opts) {
|
||||
/* 18.2.10 extLst CT_ExtensionList ? */
|
||||
case '<extLst': case '</extLst>': break;
|
||||
case '<ext': break;
|
||||
default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in cellXfs';
|
||||
default: if(opts.WTF) throw new Error('unrecognized ' + y[0] + ' in cellXfs');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ function parse_theme_xml(data/*:string*/, opts) {
|
||||
var themes = {};
|
||||
|
||||
/* themeElements CT_BaseStyles */
|
||||
if(!(t=data.match(themeltregex))) throw 'themeElements not found in theme';
|
||||
if(!(t=data.match(themeltregex))) throw new Error('themeElements not found in theme');
|
||||
parse_themeElements(t[0], themes, opts);
|
||||
|
||||
return themes;
|
||||
|
18
bits/54_drawing.js
Normal file
18
bits/54_drawing.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* 20.5 DrawingML - SpreadsheetML Drawing */
|
||||
function parse_drawing(data, rels/*:any*/) {
|
||||
if(!data) return "??";
|
||||
/*
|
||||
Chartsheet Drawing:
|
||||
- 20.5.2.35 wsDr CT_Drawing
|
||||
- 20.5.2.1 absoluteAnchor CT_AbsoluteAnchor
|
||||
- 20.5.2.16 graphicFrame CT_GraphicalObjectFrame
|
||||
- 20.1.2.2.16 graphic CT_GraphicalObject
|
||||
- 20.1.2.2.17 graphicData CT_GraphicalObjectData
|
||||
- chart reference
|
||||
the actual type is based on the URI of the graphicData
|
||||
TODO: handle embedded charts and other types of graphics
|
||||
*/
|
||||
var id = (data.match(/<c:chart [^>]*r:id="([^"]*)"/)||["",""])[1];
|
||||
|
||||
return rels['!id'][id].Target;
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
var strs = {}; // shared strings
|
||||
var _ssfopts = {}; // spreadsheet formatting options
|
||||
|
||||
RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
|
||||
RELS.WS = [
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
|
||||
"http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet"
|
||||
];
|
||||
|
||||
function get_sst_id(sst/*:SST*/, str/*:string*/)/*:number*/ {
|
||||
for(var i = 0, len = sst.length; i < len; ++i) if(sst[i].t === str) { sst.Count ++; return i; }
|
||||
|
41
bits/69_chartxml.js
Normal file
41
bits/69_chartxml.js
Normal file
@ -0,0 +1,41 @@
|
||||
function parse_numCache(data) {
|
||||
var col = [];
|
||||
|
||||
/* 21.2.2.150 pt CT_NumVal */
|
||||
(data.match(/<c:pt idx="(\d*)">(.*?)<\/c:pt>/mg)||[]).forEach(function(pt) {
|
||||
var q = pt.match(/<c:pt idx="(.*?)"><c:v>(.*)<\/c:v><\/c:pt>/);
|
||||
if(!q) return;
|
||||
col[+q[1]] = +q[2];
|
||||
});
|
||||
|
||||
/* 21.2.2.71 formatCode CT_Xstring */
|
||||
var nf = unescapexml((data.match(/<c:formatCode>(.*?)<\/c:formatCode>/) || ["","General"])[1]);
|
||||
|
||||
return [col, nf];
|
||||
}
|
||||
|
||||
/* 21.2 DrawingML - Charts */
|
||||
function parse_chart(data, name/*:string*/, opts, rels, wb, csheet) {
|
||||
var cs = ((csheet || {"!type":"chart"})/*:any*/);
|
||||
if(!data) return csheet;
|
||||
/* 21.2.2.27 chart CT_Chart */
|
||||
|
||||
var C = 0, R = 0, col = "A";
|
||||
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
|
||||
|
||||
/* 21.2.2.120 numCache CT_NumData */
|
||||
(data.match(/<c:numCache>.*?<\/c:numCache>/gm)||[]).forEach(function(nc) {
|
||||
var cache = parse_numCache(nc);
|
||||
refguess.s.r = refguess.s.c = 0;
|
||||
refguess.e.c = C;
|
||||
col = encode_col(C);
|
||||
cache[0].forEach(function(n,i) {
|
||||
cs[col + encode_row(i)] = {t:'n', v:n, z:cache[1] };
|
||||
R = i;
|
||||
});
|
||||
if(refguess.e.r < R) refguess.e.r = R;
|
||||
++C;
|
||||
});
|
||||
if(C > 0) cs["!ref"] = encode_range(refguess);
|
||||
return cs;
|
||||
}
|
47
bits/70_csheet.js
Normal file
47
bits/70_csheet.js
Normal file
@ -0,0 +1,47 @@
|
||||
RELS.CS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet";
|
||||
|
||||
/* 18.3 Worksheets also covers Chartsheets */
|
||||
function parse_cs_xml(data/*:?string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
/* 18.3.1.12 chartsheet CT_ChartSheet */
|
||||
if(!rels) rels = {'!id':{}};
|
||||
var s = {'!type':"chart", '!chart':null, '!rel':""};
|
||||
var m;
|
||||
|
||||
/* 18.3.1.36 drawing CT_Drawing */
|
||||
if((m = data.match(/drawing r:id="(.*?)"/))) s['!rel'] = m[1];
|
||||
|
||||
if(rels['!id'][s['!rel']]) s['!chart'] = rels['!id'][s['!rel']];
|
||||
return s;
|
||||
}
|
||||
|
||||
/* [MS-XLSB] 2.1.7.7 Chart Sheet */
|
||||
function parse_cs_bin(data, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(!data) return data;
|
||||
if(!rels) rels = {'!id':{}};
|
||||
var s = {'!type':"chart", '!chart':null, '!rel':""};
|
||||
var pass = false;
|
||||
recordhopper(data, function cs_parse(val, Record) {
|
||||
switch(Record.n) {
|
||||
|
||||
case 'BrtDrawing': s['!rel'] = val; break;
|
||||
|
||||
case 'BrtBeginSheet': break;
|
||||
case 'BrtCsProp': break; // TODO
|
||||
case 'BrtBeginCsViews': break; // TODO
|
||||
case 'BrtBeginCsView': break; // TODO
|
||||
case 'BrtEndCsView': break; // TODO
|
||||
case 'BrtEndCsViews': break; // TODO
|
||||
case 'BrtCsProtection': break; // TODO
|
||||
case 'BrtMargins': break; // TODO
|
||||
case 'BrtCsPageSetup': break; // TODO
|
||||
case 'BrtEndSheet': break; // TODO
|
||||
case 'BrtBeginHeaderFooter': break; // TODO
|
||||
case 'BrtEndHeaderFooter': break; // TODO
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + Record.n);
|
||||
}
|
||||
}, opts);
|
||||
|
||||
if(rels['!id'][s['!rel']]) s['!chart'] = rels['!id'][s['!rel']];
|
||||
return s;
|
||||
}
|
@ -8,6 +8,11 @@ function parse_ws(data, name/*:string*/, opts, rels, wb, themes, styles)/*:Works
|
||||
return parse_ws_xml((data/*:any*/), opts, rels, wb, themes, styles);
|
||||
}
|
||||
|
||||
function parse_cs(data, name/*:string*/, opts, rels, wb, themes, styles)/*:Worksheet*/ {
|
||||
if(name.slice(-4)===".bin") return parse_cs_bin((data/*:any*/), opts, rels, wb, themes, styles);
|
||||
return parse_cs_xml((data/*:any*/), opts, rels, wb, themes, styles);
|
||||
}
|
||||
|
||||
function parse_sty(data, name/*:string*/, themes, opts) {
|
||||
if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), themes, opts);
|
||||
return parse_sty_xml((data/*:any*/), themes, opts);
|
||||
|
@ -260,7 +260,6 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
case 'EOF': {
|
||||
if(--file_depth) break;
|
||||
if(range.e) {
|
||||
out["!range"] = range;
|
||||
if(range.e.r > 0 && range.e.c > 0) {
|
||||
range.e.r--; range.e.c--;
|
||||
out["!ref"] = encode_range(range);
|
||||
@ -295,6 +294,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
opts.snames.push(cur_sheet);
|
||||
}
|
||||
else cur_sheet = (Directory[s] || {name:""}).name;
|
||||
if(val.dt == 0x20) out["!type"] = "chart";
|
||||
mergecells = [];
|
||||
objects = [];
|
||||
array_formulae = []; opts.arrayf = array_formulae;
|
||||
@ -304,6 +304,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
|
||||
} break;
|
||||
|
||||
case 'Number': case 'BIFF2NUM': case 'BIFF2INT': {
|
||||
if(out["!type"] == "chart" && out[encode_cell({c:val.c, r:val.r})]) ++val.c;
|
||||
temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
|
||||
safe_format_xf(temp_val, options, wb.opts.Date1904);
|
||||
addcell({c:val.c, r:val.r}, temp_val, options);
|
||||
|
@ -475,7 +475,7 @@ var XLSBRecordEnum = {
|
||||
/*::[*/0x021E/*::]*/: { n:"BrtBeginECTwFldInfo", f:parsenoop },
|
||||
/*::[*/0x0224/*::]*/: { n:"BrtFileSharing", f:parsenoop },
|
||||
/*::[*/0x0225/*::]*/: { n:"BrtOleSize", f:parsenoop },
|
||||
/*::[*/0x0226/*::]*/: { n:"BrtDrawing", f:parsenoop },
|
||||
/*::[*/0x0226/*::]*/: { n:"BrtDrawing", f:parse_RelID },
|
||||
/*::[*/0x0227/*::]*/: { n:"BrtLegacyDrawing", f:parsenoop },
|
||||
/*::[*/0x0228/*::]*/: { n:"BrtLegacyDrawingHF", f:parsenoop },
|
||||
/*::[*/0x0229/*::]*/: { n:"BrtWebOpt", f:parsenoop },
|
||||
|
@ -1,15 +1,37 @@
|
||||
function safe_parse_wbrels(wbrels, sheets) {
|
||||
if(!wbrels) return 0;
|
||||
function get_type(n) {
|
||||
if(RELS.WS.indexOf(n) > -1) return "sheet";
|
||||
if(RELS.CS && n == RELS.CS) return "chart";
|
||||
if(RELS.DS && n == RELS.DS) return "dialog";
|
||||
if(RELS.MS && n == RELS.MS) return "macro";
|
||||
if(!n || !n.length) return "sheet";
|
||||
return n;
|
||||
}
|
||||
try {
|
||||
wbrels = sheets.map(function pwbr(w) { return [w.name, wbrels['!id'][w.id].Target]; });
|
||||
wbrels = sheets.map(function pwbr(w) { if(!w.id) w.id = w.strRelID; return [w.name, wbrels['!id'][w.id].Target, get_type(wbrels['!id'][w.id].Type)]; });
|
||||
} catch(e) { return null; }
|
||||
return !wbrels || wbrels.length === 0 ? null : wbrels;
|
||||
}
|
||||
|
||||
function safe_parse_ws(zip, path/*:string*/, relsPath/*:string*/, sheet, sheetRels, sheets, opts, wb, themes, styles) {
|
||||
function safe_parse_sheet(zip, path/*:string*/, relsPath/*:string*/, sheet, sheetRels, sheets, stype/*:string*/, opts, wb, themes, styles) {
|
||||
try {
|
||||
sheetRels[sheet]=parse_rels(getzipstr(zip, relsPath, true), path);
|
||||
sheets[sheet]=parse_ws(getzipdata(zip, path),path,opts,sheetRels[sheet], wb, themes, styles);
|
||||
var data = getzipdata(zip, path);
|
||||
switch(stype) {
|
||||
case 'sheet': sheets[sheet]=parse_ws(data, path, opts,sheetRels[sheet], wb, themes, styles); break;
|
||||
case 'chart':
|
||||
var cs = parse_cs(data, path, opts,sheetRels[sheet], wb, themes, styles);
|
||||
sheets[sheet] = cs;
|
||||
if(!cs || !cs['!chart']) break;
|
||||
var dfile = resolve_path(cs['!chart'].Target, path);
|
||||
var drelsp = get_rels_path(dfile);
|
||||
var draw = parse_drawing(getzipstr(zip, dfile, true), parse_rels(getzipstr(zip,drelsp,true), dfile));
|
||||
var chartp = resolve_path(draw, dfile);
|
||||
var crelsp = get_rels_path(chartp);
|
||||
cs = parse_chart(getzipstr(zip, chartp, true), chartp, opts, parse_rels(getzipstr(zip, crelsp,true), chartp), wb, cs);
|
||||
break;
|
||||
}
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
|
||||
@ -76,8 +98,8 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
|
||||
var out = ({}/*:any*/);
|
||||
if(opts.bookSheets || opts.bookProps) {
|
||||
if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
|
||||
else if(wb.Sheets) sheets = wb.Sheets.map(function pluck(x){ return x.name; });
|
||||
if(wb.Sheets) sheets = wb.Sheets.map(function pluck(x){ return x.name; });
|
||||
else if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
|
||||
if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
|
||||
if(opts.bookSheets && typeof sheets !== 'undefined') out.SheetNames = sheets;
|
||||
if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
|
||||
@ -106,13 +128,16 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
|
||||
/* Numbers iOS hack */
|
||||
var nmode = (getzipdata(zip,"xl/worksheets/sheet.xml",true))?1:0;
|
||||
for(i = 0; i != props.Worksheets; ++i) {
|
||||
if(wbrels && wbrels[i]) path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
|
||||
else {
|
||||
var stype = "sheet";
|
||||
if(wbrels && wbrels[i]) {
|
||||
path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
|
||||
stype = wbrels[i][2];
|
||||
} else {
|
||||
path = 'xl/worksheets/sheet'+(i+1-nmode)+"." + wbext;
|
||||
path = path.replace(/sheet0\./,"sheet.");
|
||||
}
|
||||
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
|
||||
safe_parse_ws(zip, path, relsPath, props.SheetNames[i], sheetRels, sheets, opts, wb, themes, styles);
|
||||
safe_parse_sheet(zip, path, relsPath, props.SheetNames[i], sheetRels, sheets, stype, opts, wb, themes, styles);
|
||||
}
|
||||
|
||||
if(dir.comments) parse_comments(zip, dir.comments, sheets, sheetRels, opts);
|
||||
|
@ -65,7 +65,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
|
||||
f = "xl/worksheets/sheet" + rId + "." + wbext;
|
||||
zip.file(f, write_ws(rId-1, f, opts, wb));
|
||||
ct.sheets.push(f);
|
||||
add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS);
|
||||
add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]);
|
||||
}
|
||||
|
||||
if(opts.Strings != null && opts.Strings.length > 0) {
|
||||
|
7
docbits/54_shobject.md
Normal file
7
docbits/54_shobject.md
Normal file
@ -0,0 +1,7 @@
|
||||
### Chartsheet Object
|
||||
|
||||
Chartsheets are represented as standard worksheets. They are distinguished with
|
||||
the `!type` property set to `"chart"`.
|
||||
|
||||
The underlying data and `!ref` refer to the cached data in the chartsheet.
|
||||
|
@ -13,8 +13,8 @@ The exported `write` and `writeFile` functions accept an options argument:
|
||||
|
||||
- `bookSST` is slower and more memory intensive, but has better compatibility
|
||||
with older versions of iOS Numbers
|
||||
- The raw data is the only thing guaranteed to be saved. Formulae, formatting,
|
||||
and other niceties may not be serialized (pending CSF standardization)
|
||||
- The raw data is the only thing guaranteed to be saved. Features not described
|
||||
in this README may not be serialized.
|
||||
- `cellDates` only applies to XLSX output and is not guaranteed to work with
|
||||
third-party readers. Excel itself does not usually write cells with type `d`
|
||||
so non-Excel tools may ignore the data or blow up in the presence of dates.
|
||||
|
22
test.js
22
test.js
@ -88,15 +88,17 @@ function parsetest(x, wb, full, ext) {
|
||||
ext = (ext ? " [" + ext + "]": "");
|
||||
if(!full && ext) return;
|
||||
describe(x + ext + ' should have all bits', function() {
|
||||
var sname = dir + '2011/' + x.substr(x.lastIndexOf('/')+1) + '.sheetnames';
|
||||
var sname = dir + '2016/' + x.substr(x.lastIndexOf('/')+1) + '.sheetnames';
|
||||
if(!fs.existsSync(sname)) sname = dir + '2011/' + x.substr(x.lastIndexOf('/')+1) + '.sheetnames';
|
||||
if(!fs.existsSync(sname)) sname = dir + '2013/' + x.substr(x.lastIndexOf('/')+1) + '.sheetnames';
|
||||
it('should have all sheets', function() {
|
||||
wb.SheetNames.forEach(function(y) { assert(wb.Sheets[y], 'bad sheet ' + y); });
|
||||
});
|
||||
it('should have the right sheet names', fs.existsSync(sname) ? function() {
|
||||
if(fs.existsSync(sname)) it('should have the right sheet names', function() {
|
||||
var file = fs.readFileSync(sname, 'utf-8').replace(/\r/g,"");
|
||||
var names = wb.SheetNames.map(fixsheetname).join("\n") + "\n";
|
||||
assert.equal(names, file);
|
||||
} : null);
|
||||
if(file.length) assert.equal(names, file);
|
||||
});
|
||||
});
|
||||
describe(x + ext + ' should generate CSV', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
@ -140,11 +142,11 @@ function parsetest(x, wb, full, ext) {
|
||||
describe(x + ext + ' should generate correct CSV output', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
var name = getfile(dir, x, i, ".csv");
|
||||
it('#' + i + ' (' + ws + ')', fs.existsSync(name) ? function() {
|
||||
if(fs.existsSync(name)) it('#' + i + ' (' + ws + ')', function() {
|
||||
var file = fs.readFileSync(name, 'utf-8');
|
||||
var csv = X.utils.make_csv(wb.Sheets[ws]);
|
||||
assert.equal(fixcsv(csv), fixcsv(file), "CSV badness");
|
||||
} : null);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(x + ext + ' should generate correct JSON output', function() {
|
||||
@ -188,8 +190,8 @@ var wbtable = {};
|
||||
|
||||
describe('should parse test files', function() {
|
||||
files.forEach(function(x) {
|
||||
if(!fs.existsSync(dir + x)) return;
|
||||
it(x, x.substr(-8) == ".pending" ? null : function() {
|
||||
if(x.slice(-8) == ".pending" || !fs.existsSync(dir + x)) return;
|
||||
it(x, function() {
|
||||
var wb = X.readFile(dir + x, opts);
|
||||
wbtable[dir + x] = wb;
|
||||
parsetest(x, wb, true);
|
||||
@ -206,8 +208,8 @@ describe('should parse test files', function() {
|
||||
});
|
||||
});
|
||||
fileA.forEach(function(x) {
|
||||
if(!fs.existsSync(dir + x)) return;
|
||||
it(x, x.substr(-8) == ".pending" ? null : function() {
|
||||
if(x.slice(-8) == ".pending" || !fs.existsSync(dir + x)) return;
|
||||
it(x, function() {
|
||||
var wb = X.readFile(dir + x, {WTF:opts.wtf, sheetRows:10});
|
||||
parsetest(x, wb, false);
|
||||
});
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 7fe887ff7b26b233db24f3d4df513319c493efc8
|
||||
Subproject commit cbafd3a184d0c668aaae1f9f8420ccaed74ad8e5
|
25
tests.lst
25
tests.lst
@ -1,15 +1,24 @@
|
||||
AutoFilter.xlsb
|
||||
BlankSheetTypes.xlsb.pending
|
||||
BlankSheetTypes.xlsb.pending # macrosheets
|
||||
ErrorTypes.xlsb
|
||||
NumberFormatCondition.xlsb
|
||||
RkNumber.xlsb
|
||||
apachepoi_Simple.xlsb.pending
|
||||
apachepoi_51519.xlsb
|
||||
# apachepoi_Simple.xlsb # Beta file
|
||||
apachepoi_WithTextBox.xlsb
|
||||
apachepoi_comments.xlsb
|
||||
apachepoi_date.xlsb
|
||||
apachepoi_hyperlink.xlsb
|
||||
apachepoi_protected_passtika.xlsb.pending
|
||||
apachepoi_sample.xlsb
|
||||
apachepoi_testVarious.xlsb
|
||||
calendar_stress_test.xlsb.pending
|
||||
cell_style_simple.xlsb
|
||||
column_width.xlsb
|
||||
comments_stress_test.xlsb
|
||||
custom_properties.xlsb
|
||||
defined_names_simple.xlsb
|
||||
# formula_stress_test.xlsb # xlml
|
||||
formula_stress_test.xlsb
|
||||
formulae_test_simple.xlsb
|
||||
hyperlink_no_rels.xlsb
|
||||
hyperlink_stress_test_2011.xlsb
|
||||
@ -22,8 +31,10 @@ number_format_russian.xlsb
|
||||
numfmt_1_russian.xlsb
|
||||
phonetic_text.xlsb
|
||||
pivot_table_named_range.xlsb
|
||||
# pivot_table_test.xlsb # xlml
|
||||
pivot_table_test.xlsb
|
||||
rich_text_stress.xlsb
|
||||
row_height.xlsb
|
||||
sized_boxen.xlsb
|
||||
smart_tags_2007.xlsb
|
||||
sushi.xlsb
|
||||
text_and_numbers.xlsb
|
||||
@ -226,7 +237,7 @@ apachepoi_Themes2.xlsx
|
||||
apachepoi_TwoSheetsNoneHidden.xlsx
|
||||
apachepoi_TwoSheetsOneHidden.xlsx
|
||||
apachepoi_WithChart.xlsx
|
||||
apachepoi_WithChartSheet.xlsx.pending
|
||||
apachepoi_WithChartSheet.xlsx
|
||||
apachepoi_WithConditionalFormatting.xlsx
|
||||
apachepoi_WithDrawing.xlsx
|
||||
apachepoi_WithEmbeded.xlsx
|
||||
@ -242,7 +253,7 @@ apachepoi_atp.xlsx
|
||||
apachepoi_bug60858.xlsx
|
||||
apachepoi_chartTitle_noTitle.xlsx
|
||||
apachepoi_chartTitle_withTitle.xlsx
|
||||
apachepoi_chart_sheet.xlsx.pending
|
||||
apachepoi_chart_sheet.xlsx
|
||||
apachepoi_commentTest.xlsx
|
||||
apachepoi_comments.xlsx
|
||||
apachepoi_craftonhills.edu_programreview_report.aspx_goalpriorityreport_0011d159-1eeb-4b63-8833-867b0926e5f3.xlsx
|
||||
@ -343,7 +354,7 @@ openpyxl_r_bug304.xlsx.pending
|
||||
openpyxl_r_comments.xlsx
|
||||
openpyxl_r_complex-styles.xlsx
|
||||
openpyxl_r_conditional-formatting.xlsx
|
||||
openpyxl_r_contains_chartsheets.xlsx.pending
|
||||
openpyxl_r_contains_chartsheets.xlsx
|
||||
openpyxl_r_date_1900.xlsx
|
||||
openpyxl_r_date_1904.xlsx
|
||||
openpyxl_r_formulae.xlsx
|
||||
|
317
xlsx.flow.js
317
xlsx.flow.js
@ -1533,6 +1533,18 @@ if (typeof exports !== 'undefined') {
|
||||
_fs = require('fs');
|
||||
}
|
||||
}
|
||||
|
||||
function resolve_path(path/*:string*/, base/*:string*/)/*:string*/ {
|
||||
var result = base.split('/');
|
||||
if(base.slice(-1) != "/") result.pop(); // folder path
|
||||
var target = path.split('/');
|
||||
while (target.length !== 0) {
|
||||
var step = target.shift();
|
||||
if (step === '..') result.pop();
|
||||
else if (step !== '.') result.push(step);
|
||||
}
|
||||
return result.join('/');
|
||||
}
|
||||
var attregexg=/([^\s?>\/]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var tagregex=/<[^>]*>/g;
|
||||
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
|
||||
@ -2552,17 +2564,7 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
/* Worksheet */
|
||||
"application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */
|
||||
|
||||
/* Chartsheet */
|
||||
"application/vnd.ms-excel.chartsheet": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": "TODO",
|
||||
|
||||
/* Dialogsheet */
|
||||
"application/vnd.ms-excel.dialogsheet": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": "TODO",
|
||||
|
||||
/* Macrosheet */
|
||||
"application/vnd.ms-excel.macrosheet": "TODO",
|
||||
"application/vnd.ms-excel.macrosheet+xml": "TODO",
|
||||
"application/vnd.ms-excel.intlmacrosheet": "TODO",
|
||||
"application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */
|
||||
|
||||
@ -2573,6 +2575,7 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
|
||||
/* Custom Data Properties */
|
||||
"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO",
|
||||
|
||||
/* Comments */
|
||||
"application/vnd.ms-excel.comments": "comments",
|
||||
@ -2652,6 +2655,9 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
/* Themes */
|
||||
"application/vnd.openxmlformats-officedocument.theme+xml": "themes",
|
||||
|
||||
/* Theme Override */
|
||||
"application/vnd.openxmlformats-officedocument.themeOverride+xml": "TODO",
|
||||
|
||||
/* Timeline */
|
||||
"application/vnd.ms-excel.Timeline+xml": "TODO", /* verify */
|
||||
"application/vnd.ms-excel.TimelineCache+xml": "TODO", /* verify */
|
||||
@ -2688,6 +2694,9 @@ var ct2type/*{[string]:string}*/ = ({
|
||||
"application/vnd.openxmlformats-package.relationships+xml": "rels",
|
||||
"application/vnd.openxmlformats-officedocument.oleObject": "TODO",
|
||||
|
||||
/* Image */
|
||||
"image/png": "TODO",
|
||||
|
||||
"sheet": "js"
|
||||
}/*:any*/);
|
||||
|
||||
@ -2703,11 +2712,23 @@ var CT_LIST = (function(){
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
||||
xlsb: "application/vnd.ms-excel.sharedStrings"
|
||||
},
|
||||
sheets: {
|
||||
sheets: { /* Worksheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.worksheet"
|
||||
},
|
||||
styles: {/* Styles */
|
||||
charts: { /* Chartsheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.chartsheet"
|
||||
},
|
||||
dialogs: { /* Dialogsheet */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.dialogsheet"
|
||||
},
|
||||
macros: { /* Macrosheet (Excel 4.0 Macros) */
|
||||
xlsx: "application/vnd.ms-excel.macrosheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.macrosheet"
|
||||
},
|
||||
styles: { /* Styles */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
||||
xlsb: "application/vnd.ms-excel.styles"
|
||||
}
|
||||
@ -2722,9 +2743,12 @@ var type2ct/*{[string]:Array<string>}*/ = evert_arr(ct2type);
|
||||
XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
|
||||
function parse_ct(data/*:?string*/, opts) {
|
||||
var ct = ({ workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
|
||||
TODO:[], rels:[], xmlns: "" }/*:any*/);
|
||||
var ct = ({
|
||||
workbooks:[], sheets:[], charts:[], dialogs:[],
|
||||
rels:[], strs:[], comments:[],
|
||||
coreprops:[], extprops:[], custprops:[], themes:[], styles:[],
|
||||
calcchains:[], vba: [],
|
||||
TODO:[], xmlns: "" }/*:any*/);
|
||||
if(!data || !data.match) return ct;
|
||||
var ctext = {};
|
||||
(data.match(tagregex)||[]).forEach(function(x) {
|
||||
@ -2799,12 +2823,18 @@ function write_ct(ct, opts)/*:string*/ {
|
||||
if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
/* 9.3 Relationships */
|
||||
var RELS = ({
|
||||
WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||
SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||
}/*:any*/);
|
||||
|
||||
/* 9.3.3 Representing Relationships */
|
||||
function get_rels_path(file/*:string*/)/*:string*/ {
|
||||
var n = file.lastIndexOf("/");
|
||||
return file.substr(0,n) + '/_rels' + file.substr(n) + ".rels";
|
||||
}
|
||||
|
||||
function parse_rels(data/*:?string*/, currentFilePath/*:string*/) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
@ -2812,28 +2842,13 @@ function parse_rels(data/*:?string*/, currentFilePath/*:string*/) {
|
||||
}
|
||||
var rels = {};
|
||||
var hash = {};
|
||||
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(tagregex)||[]).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 = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
|
||||
var canonictarget = y.TargetMode === 'External' ? y.Target : resolve_path(y.Target, currentFilePath);
|
||||
rels[canonictarget] = rel;
|
||||
hash[y.Id] = rel;
|
||||
}
|
||||
@ -3024,9 +3039,12 @@ function parse_ext_props(data, p) {
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0, cidx = -1;
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return x.v; });
|
||||
var idx = 0, len = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
len = +(v[++i].v);
|
||||
switch(v[i].v) {
|
||||
case "Worksheets":
|
||||
case "工作表":
|
||||
case "Листы":
|
||||
case "ワークシート":
|
||||
@ -3038,17 +3056,24 @@ function parse_ext_props(data, p) {
|
||||
case "Folhas de cálculo":
|
||||
case "Planilhas":
|
||||
case "Werkbladen":
|
||||
case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
|
||||
p.Worksheets = len;
|
||||
p.SheetNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
|
||||
case "Named Ranges":
|
||||
case "Benannte Bereiche":
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
p.NamedRanges = len;
|
||||
p.DefinedNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
|
||||
case "Charts": cidx = j; p.Charts = +(v[++i].v); break;
|
||||
default: break; //throw new Error(v[i].v);
|
||||
case "Charts":
|
||||
case "Diagramme":
|
||||
p.Chartsheets = len;
|
||||
p.ChartNames = parts.slice(idx, idx + len);
|
||||
break;
|
||||
}
|
||||
idx += len;
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return x.v; });
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -3465,10 +3490,10 @@ function parse_PropertySetStream(file, PIDSI) {
|
||||
var SystemIdentifier = blob.read_shift(4);
|
||||
blob.chk(CFB.utils.consts.HEADER_CLSID, 'CLSID: ');
|
||||
NumSets = blob.read_shift(4);
|
||||
if(NumSets !== 1 && NumSets !== 2) throw "Unrecognized #Sets: " + NumSets;
|
||||
if(NumSets !== 1 && NumSets !== 2) throw new Error("Unrecognized #Sets: " + NumSets);
|
||||
FMTID0 = blob.read_shift(16); Offset0 = blob.read_shift(4);
|
||||
|
||||
if(NumSets === 1 && Offset0 !== blob.l) throw "Length mismatch";
|
||||
if(NumSets === 1 && Offset0 !== blob.l) throw new Error("Length mismatch: " + Offset0 + " !== " + blob.l);
|
||||
else if(NumSets === 2) { FMTID1 = blob.read_shift(16); Offset1 = blob.read_shift(4); }
|
||||
var PSet0 = parse_PropertySet(blob, PIDSI);
|
||||
|
||||
@ -3623,7 +3648,7 @@ var parse_HyperlinkMoniker = function(blob, length) {
|
||||
switch(clsid) {
|
||||
case "e0c9ea79f9bace118c8200aa004ba90b": return parse_URLMoniker(blob, length);
|
||||
case "0303000000000000c000000000000046": return parse_FileMoniker(blob, length);
|
||||
default: throw "unsupported moniker " + clsid;
|
||||
default: throw new Error("Unsupported Moniker " + clsid);
|
||||
}
|
||||
};
|
||||
|
||||
@ -3708,7 +3733,7 @@ function parse_AddinUdf(blob, length, opts) {
|
||||
var udfName = parse_ShortXLUnicodeString(blob, length, opts);
|
||||
var cb = blob.read_shift(2);
|
||||
l -= blob.l;
|
||||
if(cb !== l) throw "Malformed AddinUdf: padding = " + l + " != " + cb;
|
||||
if(cb !== l) throw new Error("Malformed AddinUdf: padding = " + l + " != " + cb);
|
||||
blob.l += cb;
|
||||
return udfName;
|
||||
}
|
||||
@ -3791,7 +3816,7 @@ function parse_FtArray(blob, length, ot) {
|
||||
fts.push(FtTab[ft](blob, s + length - blob.l));
|
||||
} catch(e) { blob.l = s + length; return fts; }
|
||||
}
|
||||
if(blob.l != s + length) blob.l = s + length; //throw "bad Object Ft-sequence";
|
||||
if(blob.l != s + length) blob.l = s + length; //throw new Error("bad Object Ft-sequence");
|
||||
return fts;
|
||||
}
|
||||
|
||||
@ -3802,8 +3827,9 @@ var parse_FontIndex = parseuint16;
|
||||
|
||||
/* 2.4.21 */
|
||||
function parse_BOF(blob, length) {
|
||||
var o = {};
|
||||
var o = {BIFFVer:0, dt:0};
|
||||
o.BIFFVer = blob.read_shift(2); length -= 2;
|
||||
if(length >= 2) { o.dt = blob.read_shift(2); blob.l -= 2; }
|
||||
switch(o.BIFFVer) {
|
||||
case 0x0600: /* BIFF8 */
|
||||
case 0x0500: /* BIFF5 */
|
||||
@ -3811,6 +3837,7 @@ function parse_BOF(blob, length) {
|
||||
break;
|
||||
default: if(length > 6) throw new Error("Unexpected BIFF Ver " + o.BIFFVer);
|
||||
}
|
||||
|
||||
blob.read_shift(length);
|
||||
return o;
|
||||
}
|
||||
@ -3820,7 +3847,7 @@ function parse_BOF(blob, length) {
|
||||
function parse_InterfaceHdr(blob, length) {
|
||||
if(length === 0) return 0x04b0;
|
||||
var q;
|
||||
if |