diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef7198..9eb66a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
+## Unreleased
+
+* XLML property names are more closely mapped to the XLSX equivalent
+
## 0.9.2 (2017-03-13)
* Removed stale TypeScript definition files. Flowtype comments are used in the
diff --git a/Makefile b/Makefile
index f16a188..5d368b1 100644
--- a/Makefile
+++ b/Makefile
@@ -140,6 +140,9 @@ misc/coverage.html: $(TARGET) test.js
coveralls: ## Coverage Test + Send to coveralls.io
mocha --require blanket --reporter mocha-lcov-reporter -t 20000 | node ./node_modules/coveralls/bin/coveralls.js
+.PHONY: readme
+readme: ## Update README Table of Contents
+ markdown-toc -i README.md
.PHONY: help
help:
diff --git a/README.md b/README.md
index 096e80c..bbd9a68 100644
--- a/README.md
+++ b/README.md
@@ -274,24 +274,10 @@ var worksheet = workbook.Sheets[first_sheet_name];
var desired_cell = worksheet[address_of_cell];
/* Get the value */
-var desired_value = desired_cell.v;
+var desired_value = (desired_cell ? desired_cell.v : undefined);
```
-This example iterates through every nonempty of every sheet and dumps values:
-
-```js
-var sheet_name_list = workbook.SheetNames;
-sheet_name_list.forEach(function(y) { /* iterate through sheets */
- var worksheet = workbook.Sheets[y];
- for (var z in worksheet) {
- /* all keys that do not begin with "!" correspond to cell addresses */
- if(z[0] === '!') continue;
- console.log(y + "!" + z + "=" + JSON.stringify(worksheet[z].v));
- }
-});
-```
-
-Complete examples:
+**Complete examples:**
- HTML5 File API / Base64 Text / Web Workers
@@ -359,7 +345,7 @@ function s2ab(s) {
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx");
```
-Complete examples:
+**Complete examples:**
- generates a simple file
- writing an array of arrays in nodejs
@@ -393,7 +379,7 @@ Write options are described in the [Writing Options](#writing-options) section.
Utilities are available in the `XLSX.utils` object:
-Exporting:
+**Exporting:**
- `sheet_to_json` converts a worksheet object to an array of JSON objects.
`sheet_to_row_object_array` is an alias that will be removed in the future.
@@ -403,7 +389,7 @@ Exporting:
Exporters are described in the [Utility Functions](#utility-functions) section.
-Cell and cell address manipulation:
+**Cell and cell address manipulation:**
- `format_cell` generates the text value for a cell (using number formats)
- `{en,de}code_{row,col}` convert between 0-indexed rows/cols and A1 forms.
@@ -808,7 +794,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| **Excel Worksheet/Workbook Formats** |:-----:|:-----:|
| Excel 2007+ XML Formats (XLSX/XLSM) | :o: | :o: |
| Excel 2007+ Binary Format (XLSB BIFF12) | :o: | :o: |
-| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | |
+| Excel 2003-2004 XML Format (XML "SpreadsheetML") | :o: | :o: |
| Excel 97-2004 (XLS BIFF8) | :o: | |
| Excel 5.0/95 (XLS BIFF5) | :o: | |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | |
diff --git a/bin/xlsx.njs b/bin/xlsx.njs
index a6947d0..a8f2f8b 100755
--- a/bin/xlsx.njs
+++ b/bin/xlsx.njs
@@ -20,6 +20,7 @@ program
.option('-X, --xlsx', 'emit XLSX to or .xlsx')
.option('-Y, --ods', 'emit ODS to or .ods')
.option('-2, --biff2','emit XLS to or .xls (BIFF2)')
+ .option('-6, --xlml', 'emit SSML to or .xls (2003 XML)')
.option('-T, --fods', 'emit FODS to or .xls (Flat ODS)')
.option('-S, --formulae', 'print formulae')
@@ -33,7 +34,7 @@ program
.option('--sst', 'generate shared string table for XLS* formats')
.option('--compress', 'use compression when writing XLSX/M/B and ODS')
.option('--perf', 'do not generate output')
- .option('--all', 'parse everything; XLS[XMB] write as much as possible')
+ .option('--all', 'parse everything; write as much as possible')
.option('--dev', 'development mode')
.option('--read', 'read but do not print out contents')
.option('-q, --quiet', 'quiet mode');
@@ -46,6 +47,10 @@ program.on('--help', function() {
/* output formats, update list with full option name */
var workbook_formats = ['xlsx', 'xlsm', 'xlsb', 'ods', 'fods'];
+/* flag, bookType, default ext */
+var wb_formats_2 = [
+ ['xlml', 'xlml', 'xls']
+];
program.parse(process.argv);
/* see https://github.com/SheetJS/j/issues/4 */
@@ -81,11 +86,16 @@ var opts = {}, wb/*:?Workbook*/;
if(program.listSheets) opts.bookSheets = true;
if(program.sheetRows) opts.sheetRows = program.sheetRows;
if(program.password) opts.password = program.password;
-if(program.xlsx || program.xlsm || program.xlsb) {
+var seen = false;
+function wb_fmt() {
+ seen = true;
opts.cellFormula = true;
opts.cellNF = true;
if(program.output) sheetname = program.output;
}
+workbook_formats.forEach(function(m) { if(program[m]) { wb_fmt(); } });
+wb_formats_2.forEach(function(m) { if(program[m[0]]) { wb_fmt(); } });
+if(seen);
else if(program.formulae) opts.cellFormula = true;
else opts.cellFormula = false;
@@ -125,6 +135,12 @@ workbook_formats.forEach(function(m) { if(program[m]) {
process.exit(0);
} });
+wb_formats_2.forEach(function(m) { if(program[m[0]]) {
+ wopts.bookType = m[1];
+ X.writeFile(wb, sheetname || ((filename || "") + "." + m[2]), wopts);
+ process.exit(0);
+} });
+
var target_sheet = sheetname || '';
if(target_sheet === '') {
if(program.sheetIndex < (wb.SheetNames||[]).length) target_sheet = wb.SheetNames[program.sheetIndex];
diff --git a/bits/22_xmlutils.js b/bits/22_xmlutils.js
index 4822cb5..7052c56 100644
--- a/bits/22_xmlutils.js
+++ b/bits/22_xmlutils.js
@@ -52,6 +52,7 @@ function escapexml(text/*:string*/)/*:string*/{
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
+function escapexmltag(text/*:string*/)/*:string*/{ return escapexml(text).replace(/ /g,"_x0020_"); }
/* TODO: handle codepages */
var xlml_fixstr/*:StringConv*/ = (function() {
@@ -178,3 +179,10 @@ XMLNS.main = [
'http://schemas.microsoft.com/office/excel/2006/2'
];
+var XLMLNS = ({
+ 'o': 'urn:schemas-microsoft-com:office:office',
+ 'x': 'urn:schemas-microsoft-com:office:excel',
+ 'ss': 'urn:schemas-microsoft-com:office:spreadsheet',
+ 'dt': 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882',
+ 'html': 'http://www.w3.org/TR/REC-html40'
+}/*:any*/);
diff --git a/bits/36_xlsprops.js b/bits/36_xlsprops.js
index d993209..dfb8e5f 100644
--- a/bits/36_xlsprops.js
+++ b/bits/36_xlsprops.js
@@ -2,7 +2,66 @@ function xlml_set_prop(Props, tag/*:string*/, val) {
/* TODO: Normalize the properties */
switch(tag) {
case 'Description': tag = 'Comments'; break;
+ case 'Created': tag = 'CreatedDate'; break;
+ case 'LastSaved': tag = 'ModifiedDate'; break;
}
Props[tag] = val;
}
+var XLMLDocumentProperties = [
+ ['Title', 'Title'],
+ ['Subject', 'Subject'],
+ ['Author', 'Author'],
+ ['Keywords', 'Keywords'],
+ ['Comments', 'Description'],
+ ['LastAuthor', 'LastAuthor'],
+ ['CreatedDate', 'Created', 'date'],
+ ['ModifiedDate', 'LastSaved', 'date'],
+ ['Category', 'Category'],
+ ['Manager', 'Manager'],
+ ['Company', 'Company'],
+ ['AppVersion', 'Version']
+];
+
+/* TODO: verify */
+function xlml_write_docprops(Props) {
+ var T = 'DocumentProperties';
+ var o = [];
+ XLMLDocumentProperties.forEach(function(p) {
+ if(!Props[p[0]]) return;
+ var m = Props[p[0]];
+ switch(p[2]) {
+ case 'date': m = new Date(m).toISOString(); break;
+ }
+ o.push(writetag(p[1], m));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
+function xlml_write_custprops(Props, Custprops) {
+ var T = 'CustomDocumentProperties';
+ var o = [];
+ if(Props) keys(Props).forEach(function(k) {
+ /*:: if(!Props) return; */
+ if(!Props.hasOwnProperty(k)) return;
+ for(var i = 0; i < XLMLDocumentProperties.length; ++i)
+ if(k == XLMLDocumentProperties[i][0]) return;
+ var m = Props[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ if(Custprops) keys(Custprops).forEach(function(k) {
+ /*:: if(!Custprops) return; */
+ if(!Custprops.hasOwnProperty(k)) return;
+ var m = Custprops[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else if(m instanceof Date) { t = "dateTime.tz"; m = m.toISOString(); }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
diff --git a/bits/39_xlsbiff.js b/bits/39_xlsbiff.js
index f0eece5..8338a74 100644
--- a/bits/39_xlsbiff.js
+++ b/bits/39_xlsbiff.js
@@ -316,14 +316,44 @@ function parse_MulRk(blob, length) {
return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
}
-/* 2.5.20 2.5.249 TODO */
+/* 2.5.20 2.5.249 TODO: interpret values here */
function parse_CellStyleXF(blob, length, style) {
var o = {};
var a = blob.read_shift(4), b = blob.read_shift(4);
var c = blob.read_shift(4), d = blob.read_shift(2);
o.patternType = XLSFillPattern[c >> 26];
+
+ o.alc = a & 0x07;
+ o.fWrap = (a >> 3) & 0x01;
+ o.alcV = (a >> 4) & 0x07;
+ o.fJustLast = (a >> 7) & 0x01;
+ o.trot = (a >> 8) & 0xFF;
+ o.cIndent = (a >> 16) & 0x0F;
+ o.fShrinkToFit = (a >> 20) & 0x01;
+ o.iReadOrder = (a >> 22) & 0x02;
+ o.fAtrNum = (a >> 26) & 0x01;
+ o.fAtrFnt = (a >> 27) & 0x01;
+ o.fAtrAlc = (a >> 28) & 0x01;
+ o.fAtrBdr = (a >> 29) & 0x01;
+ o.fAtrPat = (a >> 30) & 0x01;
+ o.fAtrProt = (a >> 31) & 0x01;
+
+ o.dgLeft = b & 0x0F;
+ o.dgRight = (b >> 4) & 0x0F;
+ o.dgTop = (b >> 8) & 0x0F;
+ o.dgBottom = (b >> 12) & 0x0F;
+ o.icvLeft = (b >> 16) & 0x7F;
+ o.icvRight = (b >> 23) & 0x7F;
+ o.grbitDiag = (b >> 30) & 0x03;
+
+ o.icvTop = c & 0x7F;
+ o.icvBottom = (c >> 7) & 0x7F;
+ o.icvDiag = (c >> 14) & 0x7F;
+ o.dgDiag = (c >> 21) & 0x0F;
+
o.icvFore = d & 0x7F;
o.icvBack = (d >> 7) & 0x7F;
+ o.fsxButton = (d >> 14) & 0x01;
return o;
}
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
diff --git a/bits/61_fcommon.js b/bits/61_fcommon.js
index 8f960bd..1694626 100644
--- a/bits/61_fcommon.js
+++ b/bits/61_fcommon.js
@@ -16,8 +16,19 @@ var rc_to_a1 = (function(){
};
})();
-/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)([1-9]\d{0,5}|10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6])(?![_.\(A-Za-z0-9])/g;
+var a1_to_rc =(function(){
+ return function a1_to_rc(fstr, base) {
+ return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
+ /* TODO: handle fixcol / fixrow */
+ var c = decode_col($3) - base.c;
+ var r = decode_row($5) - base.r;
+ return $1 + "R" + (r == 0 ? "" : "[" + r + "]") + "C" + (c == 0 ? "" : "[" + c + "]");
+ });
+ };
+})();
+
+/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
function shift_formula_str(f/*:string*/, delta/*:Cell*/)/*:string*/ {
return f.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
return $1+($2=="$" ? $2+$3 : encode_col(decode_col($3)+delta.c))+($4=="$" ? $4+$5 : encode_row(decode_row($5) + delta.r));
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 9be3f24..e3139d3 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -728,7 +728,85 @@ function parse_xlml(data, opts)/*:Workbook*/ {
}
/* TODO */
-function write_xlml(wb, opts)/*:string*/ {
- var o = [XML_HEADER];
+function write_props_xlml(wb, opts) {
+ var o = [];
+ /* DocumentProperties */
+ if(wb.Props) o.push(xlml_write_docprops(wb.Props));
+ /* CustomDocumentProperties */
+ if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
return o.join("");
}
+/* TODO */
+function write_wb_xlml(wb, opts) {
+ /* OfficeDocumentSettings */
+ /* ExcelWorkbook */
+ return "";
+}
+/* TODO */
+function write_sty_xlml(wb, opts)/*:string*/ {
+ /* Styles */
+ return "";
+}
+/* TODO */
+function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
+ if(!cell || cell.v === undefined) return " | ";
+
+ var attr = {};
+ if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+
+ var t = "", p = "";
+ switch(cell.t) {
+ case 'n': t = 'Number'; p = String(cell.v); break;
+ case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
+ case 'e': t = 'Error'; p = BErr[cell.v]; break;
+ case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
+ default: t = 'String'; p = escapexml(cell.v||"");
+ }
+ var m = '' + p + '';
+
+ return writextag("Cell", m, attr);
+}
+/* TODO */
+function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/)/*:string*/ {
+ if(!ws['!ref']) return "";
+ var range = safe_decode_range(ws['!ref']);
+ var o = [];
+ for(var R = range.s.r; R <= range.e.r; ++R) {
+ var row = [""];
+ for(var C = range.s.c; C <= range.e.c; ++C) {
+ var addr = {r:R,c:C};
+ var ref = encode_cell(addr), cell = ws[ref];
+ row.push(write_ws_xlml_cell(ws[ref], ref, ws, opts, idx, wb, addr));
+ }
+ row.push("
");
+ o.push(row.join(""));
+ }
+ return o.join("");
+}
+function write_ws_xlml(idx/*:number*/, opts, wb/*:Workbook*/)/*:string*/ {
+ var o = [];
+ var s = wb.SheetNames[idx];
+ var ws = wb.Sheets[s];
+
+ /* Table */
+ var t = ws ? write_ws_xlml_table(ws, opts, idx, wb) : "";
+ if(t.length > 0) o.push("");
+ /* WorksheetOptions */
+ return o.join("");
+}
+function write_xlml(wb, opts)/*:string*/ {
+ var d = [];
+ d.push(write_props_xlml(wb, opts));
+ d.push(write_wb_xlml(wb, opts));
+ d.push(write_sty_xlml(wb, opts));
+ for(var i = 0; i < wb.SheetNames.length; ++i)
+ d.push(writextag("Worksheet", write_ws_xlml(i, opts, wb), {"ss:Name":escapexml(wb.SheetNames[i])}));
+ return XML_HEADER + writextag("Workbook", d.join(""), {
+ 'xmlns': XLMLNS.ss,
+ 'xmlns:o': XLMLNS.o,
+ 'xmlns:x': XLMLNS.x,
+ 'xmlns:ss': XLMLNS.ss,
+ 'xmlns:dt': XLMLNS.dt,
+ 'xmlns:html': XLMLNS.html
+ });
+}
diff --git a/bits/76_xls.js b/bits/76_xls.js
index d3705eb..adf8518 100644
--- a/bits/76_xls.js
+++ b/bits/76_xls.js
@@ -309,9 +309,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(val.cell, temp_val, options);
@@ -323,7 +323,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
last_formula.val = val;
temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
@@ -334,7 +334,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(options.cellFormula && out[last_cell]) {
if(!last_formula) break; /* technically unreachable */
if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
out[last_cell].F = encode_range(val[0]);
}
} break;
@@ -345,7 +345,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/* TODO: capture range */
if(!last_formula) break; /* technically unreachable */
shared_formulae[encode_cell(last_formula.cell)]= val[0];
- (out[encode_cell(last_formula.cell)]||{}).f = stringify_formula(val[0], range, lastcell, supbooks, opts);
+ (out[encode_cell(last_formula.cell)]||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
}
} break;
case 'LabelSst':
diff --git a/bits/88_write.js b/bits/88_write.js
index 22591bf..7c00861 100644
--- a/bits/88_write.js
+++ b/bits/88_write.js
@@ -46,7 +46,8 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
- case 'xml': return write_string_type(write_xlml(wb, o), o);
+ case 'xml':
+ case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
@@ -66,6 +67,7 @@ function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOp
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
+ case '.xlml': o.bookType = 'xlml'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
diff --git a/formats.dot b/formats.dot
index c53758f..5e772b3 100644
--- a/formats.dot
+++ b/formats.dot
@@ -32,6 +32,7 @@ digraph G {
xlsx -> csf
csf -> xlsb
xlsb -> csf
+ csf -> xlml
xlml -> csf
xls2 -> csf
csf -> xls2
diff --git a/formats.png b/formats.png
index 22b336e..67b9514 100644
Binary files a/formats.png and b/formats.png differ
diff --git a/test.js b/test.js
index 7415597..3b22fc9 100644
--- a/test.js
+++ b/test.js
@@ -11,8 +11,8 @@ if(process.env.WTF) {
opts.WTF = true;
opts.cellStyles = true;
}
-var fullex = [".xlsb", ".xlsm", ".xlsx"];
-var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2"];
+var fullex = [".xlsb", ".xlsm", ".xlsx", ".xlml"];
+var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "xlml"];
var ex = fullex.slice(); ex = ex.concat([".ods", ".xls", ".xml", ".fods"]);
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
diff --git a/tests.lst b/tests.lst
index 1fe18e5..1e096d3 100644
--- a/tests.lst
+++ b/tests.lst
@@ -9,7 +9,7 @@ cell_style_simple.xlsb
comments_stress_test.xlsb
custom_properties.xlsb
defined_names_simple.xlsb
-formula_stress_test.xlsb
+# formula_stress_test.xlsb # xlml
formulae_test_simple.xlsb
hyperlink_no_rels.xlsb
hyperlink_stress_test_2011.xlsb
@@ -22,7 +22,7 @@ number_format_russian.xlsb
numfmt_1_russian.xlsb
phonetic_text.xlsb
pivot_table_named_range.xlsb
-pivot_table_test.xlsb
+# pivot_table_test.xlsb # xlml
rich_text_stress.xlsb
smart_tags_2007.xlsb
sushi.xlsb
@@ -47,11 +47,11 @@ LONumbers-2011.xlsx
LONumbers.xlsx
RkNumber.xlsx
apachepoi_45430.xlsx
-apachepoi_45540_classic_Footer.xlsx
-apachepoi_45540_classic_Header.xlsx
+# apachepoi_45540_classic_Footer.xlsx # xlml
+# apachepoi_45540_classic_Header.xlsx # xlml
apachepoi_45540_form_Footer.xlsx
apachepoi_45540_form_Header.xlsx
-apachepoi_45544.xlsx
+# apachepoi_45544.xlsx # xlml
apachepoi_46535.xlsx
apachepoi_46536.xlsx
apachepoi_47090.xlsx
@@ -66,9 +66,9 @@ apachepoi_48495.xlsx
apachepoi_48539.xlsx
apachepoi_48703.xlsx
apachepoi_48779.xlsx
-apachepoi_48923.xlsx
+# apachepoi_48923.xlsx # xlml
apachepoi_48962.xlsx
-apachepoi_49156.xlsx
+# apachepoi_49156.xlsx # xlml
apachepoi_49273.xlsx
apachepoi_49325.xlsx
apachepoi_49609.xlsx
@@ -107,7 +107,7 @@ apachepoi_53734.xlsx
apachepoi_53798.xlsx
apachepoi_53798_shiftNegative_TMPL.xlsx
apachepoi_54034.xlsx
-apachepoi_54071.xlsx
+# apachepoi_54071.xlsx # xlml
apachepoi_54084 - Greek - beyond BMP.xlsx
apachepoi_54206.xlsx
apachepoi_54288-ref.xlsx
@@ -198,7 +198,7 @@ apachepoi_CustomXmlMappings-inverse-order.xlsx
apachepoi_DataTableCities.xlsx
apachepoi_DataValidationEvaluations.xlsx
apachepoi_DataValidations-49244.xlsx
-apachepoi_DateFormatTests.xlsx
+# apachepoi_DateFormatTests.xlsx # xlml
apachepoi_ElapsedFormatTests.xlsx
apachepoi_ExcelTables.xlsx
apachepoi_ForShifting.xlsx
@@ -208,13 +208,13 @@ apachepoi_FormatKM.xlsx
apachepoi_Formatting.xlsx
apachepoi_FormulaEvalTestData_Copy.xlsx
apachepoi_FormulaSheetRange.xlsx
-apachepoi_GeneralFormatTests.xlsx
+# apachepoi_GeneralFormatTests.xlsx # xlml
apachepoi_GroupTest.xlsx
apachepoi_InlineStrings.xlsx
apachepoi_Intersection-52111-xssf.xlsx
apachepoi_NewStyleConditionalFormattings.xlsx
apachepoi_NewlineInFormulas.xlsx
-apachepoi_NumberFormatApproxTests.xlsx
+# apachepoi_NumberFormatApproxTests.xlsx # xlml
apachepoi_NumberFormatTests.xlsx
apachepoi_RepeatingRowsCols.xlsx
apachepoi_SampleSS.strict.xlsx
@@ -228,7 +228,7 @@ apachepoi_SimpleStrict.xlsx
apachepoi_SimpleWithComments.xlsx
apachepoi_StructuredReferences.xlsx
apachepoi_StructuredRefs-lots-with-lookups.xlsx
-apachepoi_Tables.xlsx
+# apachepoi_Tables.xlsx # xlml
apachepoi_TestShiftRowSharedFormula.xlsx
apachepoi_TextFormatTests.xlsx
apachepoi_Themes.xlsx
@@ -310,7 +310,7 @@ jxls-core_formulaOneRow.xlsx
jxls-core_simple.xlsx
jxls-examples_stress1.xlsx
jxls-examples_stress2.xlsx
-jxls-reader_departmentData.xlsx
+# jxls-reader_departmentData.xlsx # xlml
large_strings.xlsx.pending
libreoffice_calc_cjk-text_cell-justify-distributed-single.xlsx
libreoffice_calc_conditional-formatting.xlsx
@@ -337,11 +337,11 @@ number_format_entities.xlsx
openpyxl_g_NameWithValueBug.xlsx
openpyxl_g_empty.xlsx.pending
openpyxl_g_empty-no-string.xlsx
-openpyxl_g_empty-with-styles.xlsx
+# openpyxl_g_empty-with-styles.xlsx # xlml
openpyxl_g_empty_libre.xlsx
-openpyxl_g_empty_no_dimensions.xlsx
+# openpyxl_g_empty_no_dimensions.xlsx # xlml
openpyxl_g_empty_with_no_properties.xlsx
-openpyxl_g_guess_types.xlsx
+# openpyxl_g_guess_types.xlsx # xlml
openpyxl_g_libreoffice_nrt.xlsx
openpyxl_g_merge_range.xlsx
openpyxl_g_sample.xlsx
@@ -472,7 +472,7 @@ numfmt_1_russian.xlsm
openpyxl_r_vba+comments.xlsm
openpyxl_r_vba-comments-saved.xlsm
openpyxl_r_vba-test.xlsm
-pivot_table_test.xlsm
+# pivot_table_test.xlsm # xlml
roo_1900_base.xlsm
roo_1904_base.xlsm
roo_Bibelbund.xlsm
@@ -574,11 +574,11 @@ apachepoi_13796.xls
apachepoi_14330-1.xls
apachepoi_14330-2.xls
apachepoi_14460.xls
-apachepoi_15228.xls
-apachepoi_15375.xls
+# apachepoi_15228.xls # xlml
+# apachepoi_15375.xls # xlml
apachepoi_15556.xls
apachepoi_15573.xls
-apachepoi_1900DateWindowing.xls
+# apachepoi_1900DateWindowing.xls # xlml
apachepoi_1904DateWindowing.xls
apachepoi_19599-1.xls
apachepoi_19599-2.xls
@@ -589,7 +589,7 @@ apachepoi_25183.xls
apachepoi_25695.xls
apachepoi_26100.xls
apachepoi_27272_1.xls
-apachepoi_27272_2.xls
+# apachepoi_27272_2.xls # xlml
apachepoi_27349-vlookupAcrossSheets.xls
apachepoi_27364.xls
apachepoi_27394.xls
@@ -625,7 +625,7 @@ apachepoi_3dFormulas.xls
apachepoi_40285.xls
apachepoi_41139.xls
apachepoi_41546.xls
-apachepoi_42016.xls
+# apachepoi_42016.xls # xlml timeout
apachepoi_42464-ExpPtg-bad.xls
apachepoi_42464-ExpPtg-ok.xls
apachepoi_42726.xls
@@ -653,7 +653,7 @@ apachepoi_45129.xls
apachepoi_45290.xls
apachepoi_45322.xls
apachepoi_45365-2.xls
-apachepoi_45365.xls
+# apachepoi_45365.xls # xlml
apachepoi_45492.xls
apachepoi_45538_classic_Footer.xls
apachepoi_45538_classic_Header.xls
@@ -725,7 +725,7 @@ apachepoi_53691.xls
apachepoi_53798_shiftNegative_TMPL.xls
apachepoi_53972.xls
apachepoi_53984.xls
-apachepoi_54016.xls
+# apachepoi_54016.xls # xlml
apachepoi_54206.xls
apachepoi_54500.xls
apachepoi_54686_fraction_formats.xls
@@ -771,7 +771,7 @@ apachepoi_IndirectFunctionTestCaseData.xls
apachepoi_Intersection-52111.xls
apachepoi_IntersectionPtg.xls
apachepoi_IrrNpvTestCaseData.xls
-apachepoi_LookupFunctionsTestCaseData.xls
+# apachepoi_LookupFunctionsTestCaseData.xls # xlml
apachepoi_MRExtraLines.xls
apachepoi_MatchFunctionTestCaseData.xls
apachepoi_MissingBits.xls
@@ -813,11 +813,11 @@ apachepoi_SubtotalsNested.xls
apachepoi_TestRandBetween.xls
apachepoi_TwoSheetsNoneHidden.xls
apachepoi_TwoSheetsOneHidden.xls
-apachepoi_UncalcedRecord.xls
+# apachepoi_UncalcedRecord.xls # xlml
apachepoi_UnionPtg.xls
apachepoi_WORKBOOK_in_capitals.xls
-apachepoi_WeekNumFunctionTestCaseData.xls
-apachepoi_WeekNumFunctionTestCaseData2013.xls
+# apachepoi_WeekNumFunctionTestCaseData.xls # xlml csv
+# apachepoi_WeekNumFunctionTestCaseData2013.xls # xlml csv
apachepoi_WithChart.xls
apachepoi_WithCheckBoxes.xls
apachepoi_WithConditionalFormatting.xls
@@ -850,7 +850,7 @@ apachepoi_ex42564-21503.xls
apachepoi_ex42564-elementOrder.xls
apachepoi_ex42570-20305.xls
apachepoi_ex44921-21902.xls
-apachepoi_ex45046-21984.xls
+# apachepoi_ex45046-21984.xls # xlml csv
apachepoi_ex45582-22397.xls
apachepoi_ex45672.xls
apachepoi_ex45698-22488.xls.pending
@@ -860,7 +860,7 @@ apachepoi_ex47747-sharedFormula.xls
apachepoi_excel_with_embeded.xls
apachepoi_excelant.xls.pending
apachepoi_externalFunctionExample.xls
-apachepoi_finance.xls
+# apachepoi_finance.xls # xlml
apachepoi_intercept.xls
apachepoi_mirrTest.xls
apachepoi_missingFuncs44675.xls
@@ -876,7 +876,7 @@ apachepoi_rank.xls
apachepoi_rk.xls
apachepoi_shared_formulas.xls
apachepoi_sumifformula.xls
-apachepoi_sumifs.xls
+# apachepoi_sumifs.xls # xlml
apachepoi_templateExcelWithAutofilter.xls
apachepoi_testArraysAndTables.xls
apachepoi_testNames.xls
@@ -885,13 +885,13 @@ apachepoi_testRVA.xls
apachepoi_text.xls
apachepoi_unicodeNameRecord.xls
apachepoi_xor-encryption-abc.xls.pending
-apachepoi_yearfracExamples.xls
+# apachepoi_yearfracExamples.xls # xlml
calendar_stress_test.xls.pending
cell_style_simple.xls
comments_stress_test.xls
custom_properties.xls
defined_names_simple.xls
-formula_stress_test.xls
+# formula_stress_test.xls # xlml
formulae_test_simple.xls
hyperlink_stress_test_2011.xls
jxls-core_array.xls
@@ -964,11 +964,11 @@ jxls-examples_report.xls
jxls-examples_rowstyle.xls
jxls-examples_stress1.xls
jxls-examples_stress2.xls
-jxls-reader_departmentData.xls
-jxls-reader_employeesData.xls
+# jxls-reader_departmentData.xls # xlml csv
+# jxls-reader_employeesData.xls # xlml csv
jxls-reader_emptyrowdata.xls
-jxls-reader_error1.xls
-jxls-reader_formulasData.xls
+# jxls-reader_error1.xls # xlml csv
+# jxls-reader_formulasData.xls # xlml csv
jxls-reader_ids.xls
jxls-src_adjacentlist_output.xls
jxls-src_adjacentlists.xls
@@ -980,7 +980,7 @@ jxls-src_colouring.xls
jxls-src_colouring_output.xls
jxls-src_department.xls
jxls-src_department_output.xls
-jxls-src_departmentdata.xls
+# jxls-src_departmentdata.xls # xlml csv
jxls-src_dynamiccolumns_output.xls
jxls-src_dynamiccolumns_template.xls
jxls-src_employees.xls
@@ -989,7 +989,7 @@ jxls-src_grouping.xls
jxls-src_grouping_output.xls
jxls-src_hiddencolumn_output.xls
jxls-src_multiplelistrows.xls
-jxls-src_multiplelistrows_output.xls
+# jxls-src_multiplelistrows_output.xls # xlml csv
jxls-src_report.xls
jxls-src_report_output.xls
jxls-src_rowstyle.xls
@@ -1081,7 +1081,7 @@ numfmt_1_russian.xls
phonetic_text.xls
phpexcel_bad_cfb_dir.xls
pivot_table_named_range.xls
-pivot_table_test.xls
+# pivot_table_test.xls # xlml csv
pyExcelerator_P-0508-0000507647-3280-5298.xls
pyExcelerator_chart1v8.xls
pyExcelerator_excel2003.xls
@@ -1129,7 +1129,7 @@ xlrd_formula_test_names.xls
xlrd_formula_test_sjmachin.xls
xlrd_issue20.xls
xlrd_picture_in_cell.xls
-xlrd_profiles.xls
+# xlrd_profiles.xls # xlml formatting
xlrd_ragged.xls
xlrd_xf_class.xls
xlsx-stream-d-date-cell.xls
@@ -1154,7 +1154,7 @@ custom_properties.xls.xml
custom_properties.xlsb.xml
custom_properties.xlsx.xml
defined_names_simple.xml
-formula_stress_test.xls.xml
+# formula_stress_test.xls.xml # xlml csv
formula_stress_test.xlsb.xml
formula_stress_test.xlsx.xml
formulae_test_simple.xml
diff --git a/xlsx.flow.js b/xlsx.flow.js
index f9e51cc..b554074 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -1512,6 +1512,7 @@ function escapexml(text/*:string*/)/*:string*/{
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
+function escapexmltag(text/*:string*/)/*:string*/{ return escapexml(text).replace(/ /g,"_x0020_"); }
/* TODO: handle codepages */
var xlml_fixstr/*:StringConv*/ = (function() {
@@ -1638,6 +1639,13 @@ XMLNS.main = [
'http://schemas.microsoft.com/office/excel/2006/2'
];
+var XLMLNS = ({
+ 'o': 'urn:schemas-microsoft-com:office:office',
+ 'x': 'urn:schemas-microsoft-com:office:excel',
+ 'ss': 'urn:schemas-microsoft-com:office:spreadsheet',
+ 'dt': 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882',
+ 'html': 'http://www.w3.org/TR/REC-html40'
+}/*:any*/);
function read_double_le(b, idx/*:number*/)/*:number*/ {
var s = 1 - 2 * (b[idx + 7] >>> 7);
var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f);
@@ -3018,10 +3026,69 @@ function xlml_set_prop(Props, tag/*:string*/, val) {
/* TODO: Normalize the properties */
switch(tag) {
case 'Description': tag = 'Comments'; break;
+ case 'Created': tag = 'CreatedDate'; break;
+ case 'LastSaved': tag = 'ModifiedDate'; break;
}
Props[tag] = val;
}
+var XLMLDocumentProperties = [
+ ['Title', 'Title'],
+ ['Subject', 'Subject'],
+ ['Author', 'Author'],
+ ['Keywords', 'Keywords'],
+ ['Comments', 'Description'],
+ ['LastAuthor', 'LastAuthor'],
+ ['CreatedDate', 'Created', 'date'],
+ ['ModifiedDate', 'LastSaved', 'date'],
+ ['Category', 'Category'],
+ ['Manager', 'Manager'],
+ ['Company', 'Company'],
+ ['AppVersion', 'Version']
+];
+
+/* TODO: verify */
+function xlml_write_docprops(Props) {
+ var T = 'DocumentProperties';
+ var o = [];
+ XLMLDocumentProperties.forEach(function(p) {
+ if(!Props[p[0]]) return;
+ var m = Props[p[0]];
+ switch(p[2]) {
+ case 'date': m = new Date(m).toISOString(); break;
+ }
+ o.push(writetag(p[1], m));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
+function xlml_write_custprops(Props, Custprops) {
+ var T = 'CustomDocumentProperties';
+ var o = [];
+ if(Props) keys(Props).forEach(function(k) {
+ /*:: if(!Props) return; */
+ if(!Props.hasOwnProperty(k)) return;
+ for(var i = 0; i < XLMLDocumentProperties.length; ++i)
+ if(k == XLMLDocumentProperties[i][0]) return;
+ var m = Props[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ if(Custprops) keys(Custprops).forEach(function(k) {
+ /*:: if(!Custprops) return; */
+ if(!Custprops.hasOwnProperty(k)) return;
+ var m = Custprops[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else if(m instanceof Date) { t = "dateTime.tz"; m = m.toISOString(); }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
/* [MS-DTYP] 2.3.3 FILETIME */
/* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
/* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
@@ -3786,14 +3853,44 @@ function parse_MulRk(blob, length) {
return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
}
-/* 2.5.20 2.5.249 TODO */
+/* 2.5.20 2.5.249 TODO: interpret values here */
function parse_CellStyleXF(blob, length, style) {
var o = {};
var a = blob.read_shift(4), b = blob.read_shift(4);
var c = blob.read_shift(4), d = blob.read_shift(2);
o.patternType = XLSFillPattern[c >> 26];
+
+ o.alc = a & 0x07;
+ o.fWrap = (a >> 3) & 0x01;
+ o.alcV = (a >> 4) & 0x07;
+ o.fJustLast = (a >> 7) & 0x01;
+ o.trot = (a >> 8) & 0xFF;
+ o.cIndent = (a >> 16) & 0x0F;
+ o.fShrinkToFit = (a >> 20) & 0x01;
+ o.iReadOrder = (a >> 22) & 0x02;
+ o.fAtrNum = (a >> 26) & 0x01;
+ o.fAtrFnt = (a >> 27) & 0x01;
+ o.fAtrAlc = (a >> 28) & 0x01;
+ o.fAtrBdr = (a >> 29) & 0x01;
+ o.fAtrPat = (a >> 30) & 0x01;
+ o.fAtrProt = (a >> 31) & 0x01;
+
+ o.dgLeft = b & 0x0F;
+ o.dgRight = (b >> 4) & 0x0F;
+ o.dgTop = (b >> 8) & 0x0F;
+ o.dgBottom = (b >> 12) & 0x0F;
+ o.icvLeft = (b >> 16) & 0x7F;
+ o.icvRight = (b >> 23) & 0x7F;
+ o.grbitDiag = (b >> 30) & 0x03;
+
+ o.icvTop = c & 0x7F;
+ o.icvBottom = (c >> 7) & 0x7F;
+ o.icvDiag = (c >> 14) & 0x7F;
+ o.dgDiag = (c >> 21) & 0x0F;
+
o.icvFore = d & 0x7F;
o.icvBack = (d >> 7) & 0x7F;
+ o.fsxButton = (d >> 14) & 0x01;
return o;
}
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
@@ -5816,8 +5913,19 @@ var rc_to_a1 = (function(){
};
})();
-/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)([1-9]\d{0,5}|10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6])(?![_.\(A-Za-z0-9])/g;
+var a1_to_rc =(function(){
+ return function a1_to_rc(fstr, base) {
+ return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
+ /* TODO: handle fixcol / fixrow */
+ var c = decode_col($3) - base.c;
+ var r = decode_row($5) - base.r;
+ return $1 + "R" + (r == 0 ? "" : "[" + r + "]") + "C" + (c == 0 ? "" : "[" + c + "]");
+ });
+ };
+})();
+
+/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
function shift_formula_str(f/*:string*/, delta/*:Cell*/)/*:string*/ {
return f.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
return $1+($2=="$" ? $2+$3 : encode_col(decode_col($3)+delta.c))+($4=="$" ? $4+$5 : encode_row(decode_row($5) + delta.r));
@@ -10329,10 +10437,88 @@ function parse_xlml(data, opts)/*:Workbook*/ {
}
/* TODO */
-function write_xlml(wb, opts)/*:string*/ {
- var o = [XML_HEADER];
+function write_props_xlml(wb, opts) {
+ var o = [];
+ /* DocumentProperties */
+ if(wb.Props) o.push(xlml_write_docprops(wb.Props));
+ /* CustomDocumentProperties */
+ if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
return o.join("");
}
+/* TODO */
+function write_wb_xlml(wb, opts) {
+ /* OfficeDocumentSettings */
+ /* ExcelWorkbook */
+ return "";
+}
+/* TODO */
+function write_sty_xlml(wb, opts)/*:string*/ {
+ /* Styles */
+ return "";
+}
+/* TODO */
+function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr)/*:string*/{
+ if(!cell || cell.v === undefined) return " | ";
+
+ var attr = {};
+ if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+
+ var t = "", p = "";
+ switch(cell.t) {
+ case 'n': t = 'Number'; p = String(cell.v); break;
+ case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
+ case 'e': t = 'Error'; p = BErr[cell.v]; break;
+ case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
+ default: t = 'String'; p = escapexml(cell.v||"");
+ }
+ var m = '' + p + '';
+
+ return writextag("Cell", m, attr);
+}
+/* TODO */
+function write_ws_xlml_table(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook*/)/*:string*/ {
+ if(!ws['!ref']) return "";
+ var range = safe_decode_range(ws['!ref']);
+ var o = [];
+ for(var R = range.s.r; R <= range.e.r; ++R) {
+ var row = [""];
+ for(var C = range.s.c; C <= range.e.c; ++C) {
+ var addr = {r:R,c:C};
+ var ref = encode_cell(addr), cell = ws[ref];
+ row.push(write_ws_xlml_cell(ws[ref], ref, ws, opts, idx, wb, addr));
+ }
+ row.push("
");
+ o.push(row.join(""));
+ }
+ return o.join("");
+}
+function write_ws_xlml(idx/*:number*/, opts, wb/*:Workbook*/)/*:string*/ {
+ var o = [];
+ var s = wb.SheetNames[idx];
+ var ws = wb.Sheets[s];
+
+ /* Table */
+ var t = ws ? write_ws_xlml_table(ws, opts, idx, wb) : "";
+ if(t.length > 0) o.push("");
+ /* WorksheetOptions */
+ return o.join("");
+}
+function write_xlml(wb, opts)/*:string*/ {
+ var d = [];
+ d.push(write_props_xlml(wb, opts));
+ d.push(write_wb_xlml(wb, opts));
+ d.push(write_sty_xlml(wb, opts));
+ for(var i = 0; i < wb.SheetNames.length; ++i)
+ d.push(writextag("Worksheet", write_ws_xlml(i, opts, wb), {"ss:Name":escapexml(wb.SheetNames[i])}));
+ return XML_HEADER + writextag("Workbook", d.join(""), {
+ 'xmlns': XLMLNS.ss,
+ 'xmlns:o': XLMLNS.o,
+ 'xmlns:x': XLMLNS.x,
+ 'xmlns:ss': XLMLNS.ss,
+ 'xmlns:dt': XLMLNS.dt,
+ 'xmlns:html': XLMLNS.html
+ });
+}
/* [MS-OLEDS] 2.3.8 CompObjStream */
function parse_compobj(obj) {
var v = {};
@@ -10644,9 +10830,9 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(val.cell, temp_val, options);
@@ -10658,7 +10844,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
last_formula.val = val;
temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'}/*:any*/);
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
@@ -10669,7 +10855,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
if(options.cellFormula && out[last_cell]) {
if(!last_formula) break; /* technically unreachable */
if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
out[last_cell].F = encode_range(val[0]);
}
} break;
@@ -10680,7 +10866,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/* TODO: capture range */
if(!last_formula) break; /* technically unreachable */
shared_formulae[encode_cell(last_formula.cell)]= val[0];
- (out[encode_cell(last_formula.cell)]||{}).f = stringify_formula(val[0], range, lastcell, supbooks, opts);
+ (out[encode_cell(last_formula.cell)]||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
}
} break;
case 'LabelSst':
@@ -13286,7 +13472,8 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
- case 'xml': return write_string_type(write_xlml(wb, o), o);
+ case 'xml':
+ case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
@@ -13306,6 +13493,7 @@ function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOp
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
+ case '.xlml': o.bookType = 'xlml'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
diff --git a/xlsx.js b/xlsx.js
index 0ea8ab6..ae74ac6 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -1465,6 +1465,7 @@ function escapexml(text){
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
+function escapexmltag(text){ return escapexml(text).replace(/ /g,"_x0020_"); }
/* TODO: handle codepages */
var xlml_fixstr = (function() {
@@ -1591,6 +1592,13 @@ XMLNS.main = [
'http://schemas.microsoft.com/office/excel/2006/2'
];
+var XLMLNS = ({
+ 'o': 'urn:schemas-microsoft-com:office:office',
+ 'x': 'urn:schemas-microsoft-com:office:excel',
+ 'ss': 'urn:schemas-microsoft-com:office:spreadsheet',
+ 'dt': 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882',
+ 'html': 'http://www.w3.org/TR/REC-html40'
+});
function read_double_le(b, idx) {
var s = 1 - 2 * (b[idx + 7] >>> 7);
var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f);
@@ -2968,10 +2976,67 @@ function xlml_set_prop(Props, tag, val) {
/* TODO: Normalize the properties */
switch(tag) {
case 'Description': tag = 'Comments'; break;
+ case 'Created': tag = 'CreatedDate'; break;
+ case 'LastSaved': tag = 'ModifiedDate'; break;
}
Props[tag] = val;
}
+var XLMLDocumentProperties = [
+ ['Title', 'Title'],
+ ['Subject', 'Subject'],
+ ['Author', 'Author'],
+ ['Keywords', 'Keywords'],
+ ['Comments', 'Description'],
+ ['LastAuthor', 'LastAuthor'],
+ ['CreatedDate', 'Created', 'date'],
+ ['ModifiedDate', 'LastSaved', 'date'],
+ ['Category', 'Category'],
+ ['Manager', 'Manager'],
+ ['Company', 'Company'],
+ ['AppVersion', 'Version']
+];
+
+/* TODO: verify */
+function xlml_write_docprops(Props) {
+ var T = 'DocumentProperties';
+ var o = [];
+ XLMLDocumentProperties.forEach(function(p) {
+ if(!Props[p[0]]) return;
+ var m = Props[p[0]];
+ switch(p[2]) {
+ case 'date': m = new Date(m).toISOString(); break;
+ }
+ o.push(writetag(p[1], m));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
+function xlml_write_custprops(Props, Custprops) {
+ var T = 'CustomDocumentProperties';
+ var o = [];
+ if(Props) keys(Props).forEach(function(k) {
+if(!Props.hasOwnProperty(k)) return;
+ for(var i = 0; i < XLMLDocumentProperties.length; ++i)
+ if(k == XLMLDocumentProperties[i][0]) return;
+ var m = Props[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ if(Custprops) keys(Custprops).forEach(function(k) {
+if(!Custprops.hasOwnProperty(k)) return;
+ var m = Custprops[k];
+ var t = "string";
+ if(typeof m == 'number') { t = "float"; m = String(m); }
+ else if(m === true || m === false) { t = "boolean"; m = m ? "1" : "0"; }
+ else if(m instanceof Date) { t = "dateTime.tz"; m = m.toISOString(); }
+ else m = String(m);
+ o.push(writextag(escapexmltag(k), m, {"dt:dt":t}));
+ });
+ return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
+}
/* [MS-DTYP] 2.3.3 FILETIME */
/* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
/* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
@@ -3736,14 +3801,44 @@ function parse_MulRk(blob, length) {
return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
}
-/* 2.5.20 2.5.249 TODO */
+/* 2.5.20 2.5.249 TODO: interpret values here */
function parse_CellStyleXF(blob, length, style) {
var o = {};
var a = blob.read_shift(4), b = blob.read_shift(4);
var c = blob.read_shift(4), d = blob.read_shift(2);
o.patternType = XLSFillPattern[c >> 26];
+
+ o.alc = a & 0x07;
+ o.fWrap = (a >> 3) & 0x01;
+ o.alcV = (a >> 4) & 0x07;
+ o.fJustLast = (a >> 7) & 0x01;
+ o.trot = (a >> 8) & 0xFF;
+ o.cIndent = (a >> 16) & 0x0F;
+ o.fShrinkToFit = (a >> 20) & 0x01;
+ o.iReadOrder = (a >> 22) & 0x02;
+ o.fAtrNum = (a >> 26) & 0x01;
+ o.fAtrFnt = (a >> 27) & 0x01;
+ o.fAtrAlc = (a >> 28) & 0x01;
+ o.fAtrBdr = (a >> 29) & 0x01;
+ o.fAtrPat = (a >> 30) & 0x01;
+ o.fAtrProt = (a >> 31) & 0x01;
+
+ o.dgLeft = b & 0x0F;
+ o.dgRight = (b >> 4) & 0x0F;
+ o.dgTop = (b >> 8) & 0x0F;
+ o.dgBottom = (b >> 12) & 0x0F;
+ o.icvLeft = (b >> 16) & 0x7F;
+ o.icvRight = (b >> 23) & 0x7F;
+ o.grbitDiag = (b >> 30) & 0x03;
+
+ o.icvTop = c & 0x7F;
+ o.icvBottom = (c >> 7) & 0x7F;
+ o.icvDiag = (c >> 14) & 0x7F;
+ o.dgDiag = (c >> 21) & 0x0F;
+
o.icvFore = d & 0x7F;
o.icvBack = (d >> 7) & 0x7F;
+ o.fsxButton = (d >> 14) & 0x01;
return o;
}
function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
@@ -5766,8 +5861,19 @@ var rc_to_a1 = (function(){
};
})();
-/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)([1-9]\d{0,5}|10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6])(?![_.\(A-Za-z0-9])/g;
+var a1_to_rc =(function(){
+ return function a1_to_rc(fstr, base) {
+ return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
+ /* TODO: handle fixcol / fixrow */
+ var c = decode_col($3) - base.c;
+ var r = decode_row($5) - base.r;
+ return $1 + "R" + (r == 0 ? "" : "[" + r + "]") + "C" + (c == 0 ? "" : "[" + c + "]");
+ });
+ };
+})();
+
+/* no defined name can collide with a valid cell address A1:XFD1048576 ... except LOG10! */
function shift_formula_str(f, delta) {
return f.replace(crefregex, function($0, $1, $2, $3, $4, $5, off, str) {
return $1+($2=="$" ? $2+$3 : encode_col(decode_col($3)+delta.c))+($4=="$" ? $4+$5 : encode_row(decode_row($5) + delta.r));
@@ -10275,10 +10381,88 @@ function parse_xlml(data, opts) {
}
/* TODO */
-function write_xlml(wb, opts) {
- var o = [XML_HEADER];
+function write_props_xlml(wb, opts) {
+ var o = [];
+ /* DocumentProperties */
+ if(wb.Props) o.push(xlml_write_docprops(wb.Props));
+ /* CustomDocumentProperties */
+ if(wb.Custprops) o.push(xlml_write_custprops(wb.Props, wb.Custprops));
return o.join("");
}
+/* TODO */
+function write_wb_xlml(wb, opts) {
+ /* OfficeDocumentSettings */
+ /* ExcelWorkbook */
+ return "";
+}
+/* TODO */
+function write_sty_xlml(wb, opts) {
+ /* Styles */
+ return "";
+}
+/* TODO */
+function write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr){
+ if(!cell || cell.v === undefined) return " | ";
+
+ var attr = {};
+ if(cell.f) attr["ss:Formula"] = "=" + escapexml(a1_to_rc(cell.f, addr));
+
+ var t = "", p = "";
+ switch(cell.t) {
+ case 'n': t = 'Number'; p = String(cell.v); break;
+ case 'b': t = 'Boolean'; p = (cell.v ? "1" : "0"); break;
+ case 'e': t = 'Error'; p = BErr[cell.v]; break;
+ case 'd': t = 'DateTime'; p = new Date(cell.v).toISOString(); break;
+ default: t = 'String'; p = escapexml(cell.v||"");
+ }
+ var m = '' + p + '';
+
+ return writextag("Cell", m, attr);
+}
+/* TODO */
+function write_ws_xlml_table(ws, opts, idx, wb) {
+ if(!ws['!ref']) return "";
+ var range = safe_decode_range(ws['!ref']);
+ var o = [];
+ for(var R = range.s.r; R <= range.e.r; ++R) {
+ var row = [""];
+ for(var C = range.s.c; C <= range.e.c; ++C) {
+ var addr = {r:R,c:C};
+ var ref = encode_cell(addr), cell = ws[ref];
+ row.push(write_ws_xlml_cell(ws[ref], ref, ws, opts, idx, wb, addr));
+ }
+ row.push("
");
+ o.push(row.join(""));
+ }
+ return o.join("");
+}
+function write_ws_xlml(idx, opts, wb) {
+ var o = [];
+ var s = wb.SheetNames[idx];
+ var ws = wb.Sheets[s];
+
+ /* Table */
+ var t = ws ? write_ws_xlml_table(ws, opts, idx, wb) : "";
+ if(t.length > 0) o.push("");
+ /* WorksheetOptions */
+ return o.join("");
+}
+function write_xlml(wb, opts) {
+ var d = [];
+ d.push(write_props_xlml(wb, opts));
+ d.push(write_wb_xlml(wb, opts));
+ d.push(write_sty_xlml(wb, opts));
+ for(var i = 0; i < wb.SheetNames.length; ++i)
+ d.push(writextag("Worksheet", write_ws_xlml(i, opts, wb), {"ss:Name":escapexml(wb.SheetNames[i])}));
+ return XML_HEADER + writextag("Workbook", d.join(""), {
+ 'xmlns': XLMLNS.ss,
+ 'xmlns:o': XLMLNS.o,
+ 'xmlns:x': XLMLNS.x,
+ 'xmlns:ss': XLMLNS.ss,
+ 'xmlns:dt': XLMLNS.dt,
+ 'xmlns:html': XLMLNS.html
+ });
+}
/* [MS-OLEDS] 2.3.8 CompObjStream */
function parse_compobj(obj) {
var v = {};
@@ -10590,9 +10774,9 @@ function parse_workbook(blob, options) {
if(_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
var _fr = _f[0][0][1][0], _fc = _f[0][0][1][1];
var _fe = encode_cell({r:_fr, c:_fc});
- if(shared_formulae[_fe]) temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ if(shared_formulae[_fe]) temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
else temp_val.F = (out[_fe] || {}).F;
- } else temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks, opts);
+ } else temp_val.f = ""+stringify_formula(val.formula,range,val.cell,supbooks, opts);
}
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(val.cell, temp_val, options);
@@ -10604,7 +10788,7 @@ function parse_workbook(blob, options) {
last_formula.val = val;
temp_val = ({v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'});
temp_val.XF = XFs[temp_val.ixfe];
- if(options.cellFormula) temp_val.f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ if(options.cellFormula) temp_val.f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
safe_format_xf(temp_val, options, wb.opts.Date1904);
addcell(last_formula.cell, temp_val, options);
last_formula = null;
@@ -10615,7 +10799,7 @@ function parse_workbook(blob, options) {
if(options.cellFormula && out[last_cell]) {
if(!last_formula) break; /* technically unreachable */
if(!last_cell || !out[last_cell]) break; /* technically unreachable */
- out[last_cell].f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
+ out[last_cell].f = ""+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
out[last_cell].F = encode_range(val[0]);
}
} break;
@@ -10626,7 +10810,7 @@ function parse_workbook(blob, options) {
/* TODO: capture range */
if(!last_formula) break; /* technically unreachable */
shared_formulae[encode_cell(last_formula.cell)]= val[0];
- (out[encode_cell(last_formula.cell)]||{}).f = stringify_formula(val[0], range, lastcell, supbooks, opts);
+ (out[encode_cell(last_formula.cell)]||{}).f = ""+stringify_formula(val[0], range, lastcell, supbooks, opts);
}
} break;
case 'LabelSst':
@@ -13228,7 +13412,8 @@ function writeSync(wb, opts) {
check_wb(wb);
var o = opts||{};
switch(o.bookType || 'xlsx') {
- case 'xml': return write_string_type(write_xlml(wb, o), o);
+ case 'xml':
+ case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
@@ -13248,6 +13433,7 @@ function writeFileSync(wb, filename, opts) {
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
+ case '.xlml': o.bookType = 'xlml'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;