XLML write support

- XLML write (fixes #173 h/t @SheetJSDev)
- removed old iteration style from README (see #592)
- CellXF & StyleXF fields (fixes #414 h/t @ronnywang)
This commit is contained in:
SheetJS 2017-03-14 04:19:51 -04:00
parent 456ab63dc4
commit 7cb978b846
17 changed files with 668 additions and 96 deletions

@ -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

@ -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:

@ -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:**
- <http://oss.sheetjs.com/js-xlsx/> 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:**
- <http://sheetjs.com/demos/writexlsx.html> generates a simple file
- <http://git.io/WEK88Q> 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: | |

@ -20,6 +20,7 @@ program
.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx')
.option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods')
.option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.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];

@ -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*/);

@ -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 + '>';
}

@ -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);}

@ -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));

@ -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 "<Cell></Cell>";
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 = '<Data ss:Type="' + t + '">' + p + '</Data>';
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 = ["<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("</Row>");
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("<Table>" + t + "</Table>");
/* 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
});
}

@ -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':

@ -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;

@ -32,6 +32,7 @@ digraph G {
xlsx -> csf
csf -> xlsb
xlsb -> csf
csf -> xlml
xlml -> csf
xls2 -> csf
csf -> xls2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 78 KiB

@ -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;});

@ -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

@ -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 "<Cell></Cell>";
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 = '<Data ss:Type="' + t + '">' + p + '</Data>';
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 = ["<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("</Row>");
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("<Table>" + t + "</Table>");
/* 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;

206
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 "<Cell></Cell>";
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 = '<Data ss:Type="' + t + '">' + p + '</Data>';
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 = ["<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("</Row>");
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("<Table>" + t + "</Table>");
/* 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;