1
Fork 0

version bump 0.8.5: FODS/UOS and IE6+ support

- read and write support for Flat ODS files
- read support for Uniform Office Spreadsheet (UOS)
- IE6-8 cell regex split fix (fixes #350 #140 #268 h/t @Aymkdn @C0d3ine)
- replace substr negative index with slices (fixes #351 h/t @Aymkdn)
- ODS parsexmltag ignores ext overrides (fixes #548 h/t @lgodard)
- csv can be written using write/writeFile with csv type
- added `type` to README (fixes #432 h/t @tomkel)
This commit is contained in:
SheetJS 2017-02-21 22:57:59 -08:00
parent d7ecca0e8b
commit 7408679252
43 changed files with 919 additions and 348 deletions

2
.gitignore vendored
View File

@ -12,7 +12,9 @@ tmp
*.[xX][lL][sSwW]
*.[xX][lL][sS][xXmMbB]
*.[oO][dD][sS]
*.[fF][oO][dD][sS]
*.[xX][mM][lL]
*.[uU][oO][sS]
*.htm
*.html
*.sheetjs

View File

@ -13,7 +13,9 @@ tmp
*.[xX][lL][sSwW]
*.[xX][lL][sS][xXmMbB]
*.[oO][dD][sS]
*.[fF][oO][dD][sS]
*.[xX][mM][lL]
*.[uU][oO][sS]
*.htm
*.html
*.sheetjs

178
README.md
View File

@ -1,26 +1,29 @@
# xlsx
Parser and writer for various spreadsheet formats. Pure-JS cleanroom
implementation from official specifications and related documents.
implementation from official specifications, related documents, and test files.
Emphasis on parsing and writing robustness, cross-format feature compatibility
with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
Supported read formats:
File format support for known spreadsheet data formats:
- Excel 2007+ XML Formats (XLSX/XLSM)
- Excel 2007+ Binary Format (XLSB BIFF12)
- Excel 2003-2004 XML Format (XML "SpreadsheetML")
- Excel 97-2004 (XLS BIFF8)
- Excel 5.0/95 (XLS BIFF5)
- Excel 2.0/3.0/4.0 (XLS BIFF2/BIFF3/BIFF4)
- OpenDocument Spreadsheet (ODS)
Supported write formats:
- Excel 2.0 (XLS BIFF2, compatible with *every version* of Excel)
- Excel 2007+ XML Formats (XLSX/XLSM, compatible with Excel 2007+)
- Excel 2007+ Binary Format (XLSB, compatible with Excel 2007+)
- CSV (and general DSV)
- JSON and JS objects (various styles)
- OpenDocument Spreadsheet (ODS)
| Format | Read | Write |
|:-------------------------------------------------------------|:-----:|:-----:|
| **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 97-2004 (XLS BIFF8) | :o: | |
| Excel 5.0/95 (XLS BIFF5) | :o: | |
| Excel 4.0 (XLS/XLW BIFF4) | :o: | |
| Excel 3.0 (XLS BIFF3) | :o: | |
| Excel 2.0/2.1 (XLS BIFF2) | :o: | :o: |
| **Excel Supported Text Formats** |:-----:|:-----:|
| Delimiter-Separated Values (CSV/TSV/DSV) | | :o: |
| **Other Workbook/Worksheet Formats** |:-----:|:-----:|
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | |
Demo: <http://oss.sheetjs.com/js-xlsx>
@ -52,7 +55,7 @@ $ bower install js-xlsx
CDNjs automatically pulls the latest version and makes all versions available at
<http://cdnjs.com/libraries/xlsx>
## Optional Modules
### Optional Modules
The node version automatically requires modules for additional features. Some
of these modules are rather large in size and are only needed in special
@ -70,7 +73,7 @@ An appropriate version for each dependency is included in the dist/ directory.
The complete single-file version is generated at `dist/xlsx.full.min.js`
## ECMAScript 5 Compatibility
### ECMAScript 5 Compatibility
Since xlsx.js uses ES5 functions like `Array#forEach`, older browsers require
[Polyfills](http://git.io/QVh77g). This repo and the gh-pages branch include
@ -239,13 +242,21 @@ Complete examples:
- <http://oss.sheetjs.com/js-xlsx/> HTML5 File API / Base64 Text / Web Workers
Note that older versions of IE does not support HTML5 File API, so the base64
mode is provided for testing. On OSX you can get the base64 encoding with:
Note that older versions of IE do not support HTML5 File API, so the base64 mode
is used for testing. On OSX you can get the base64 encoding with:
```bash
$ <target_file.xlsx base64 | pbcopy
$ <target_file base64 | pbcopy
```
On Windows XP and up you can get the base64 encoding using `certutil`:
```cmd
> certutil -encode target_file target_file.b64
```
(note: You have to open the file and remove the header and footer lines)
- <http://oss.sheetjs.com/js-xlsx/ajax.html> XMLHttpRequest
- <https://github.com/SheetJS/js-xlsx/blob/master/bin/xlsx.njs> node
@ -488,6 +499,7 @@ The exported `read` and `readFile` functions accept an options argument:
| Option Name | Default | Description |
| :---------- | ------: | :--------------------------------------------------- |
| type | | Input data encoding (see Input Type below) |
| cellFormula | true | Save formulae to the .f field ** |
| cellHTML | true | Parse rich text and save HTML to the .h field |
| cellNF | false | Save number format string to the .z field |
@ -521,12 +533,40 @@ The exported `read` and `readFile` functions accept an options argument:
The defaults are enumerated in bits/84\_defaults.js
### Input Type
Strings can be interpreted in multiple ways. The `type` parameter for `read`
tells the library how to parse the data argument:
| `type` | expected input |
|------------|-----------------------------------------------------------------|
| `"base64"` | string: base64 encoding of the file |
| `"binary"` | string: binary string (`n`-th byte is `data.charCodeAt(n)`) |
| `"buffer"` | nodejs Buffer |
| `"array"` | array: array of 8-bit unsigned int (`n`-th byte is `data[n]`) |
| `"file"` | string: filename that will be read and processed (nodejs only) |
### Guessing File Type
Excel and other spreadsheet tools read the first few bytes and apply other
heuristics to determine a file type. This enables file type punning: renaming
files with the `.xls` extension will tell your computer to use Excel to open the
file but Excel will know how to handle it. This library applies similar logic:
| Byte 0 | Raw File Type | Spreadsheet Types |
|:-------|:--------------------------------------------------------------------|
| `0xD0` | CFB Container | BIFF 5/8 or password-protected XLSX/XLSB |
| `0x09` | BIFF Stream | BIFF 2/3/4/5 |
| `0x3C` | XML | SpreadsheetML or Flat ODS or UOS1 |
| `0x50` | ZIP Archive | XLSB or XLSX/M or ODS or UOS2 |
## Writing Options
The exported `write` and `writeFile` functions accept an options argument:
| Option Name | Default | Description |
| :---------- | -------: | :-------------------------------------------------- |
| type | | Output data encoding (see Output Type below) |
| cellDates | `false` | Store dates as type `d` (default is `n`) |
| bookSST | `false` | Generate Shared String Table ** |
| bookType | `"xlsx"` | Type of Workbook (see below for supported formats) |
@ -541,20 +581,96 @@ The exported `write` and `writeFile` functions accept an options argument:
third-party readers. Excel itself does not usually write cells with type `d`
so non-Excel tools may ignore the data or blow up in the presence of dates.
Supported output formats (`bookType`):
### Supported Output Formats
| bookType | file ext | container | sheets | Description |
| :------- | -------: | :-------: | :----- |:---------------------------- |
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet format |
For broad compatibility with third-party tools, this library supports many
output formats. The specific file type is controlled with `bookType` option:
| bookType | file ext | container | sheets | Description |
| :------- | -------: | :-------: | :----- |:--------------------------------- |
| `xlsx` | `.xlsx` | ZIP | multi | Excel 2007+ XML Format |
| `xlsm` | `.xlsm` | ZIP | multi | Excel 2007+ Macro XML Format |
| `xlsb` | `.xlsb` | ZIP | multi | Excel 2007+ Binary Format |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `biff2` | `.xls` | none | single | Excel 2.0 Worksheet format |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `csv` | `.csv` | none | single | Comma Separated Values |
- `compression` only applies to formats with ZIP containers.
- Formats that only support a single sheet require a `sheet` option specifying
the worksheet. If the string is empty, the first worksheet is used.
### Output Type
The `type` argument for `write` mirrors the `type` argument for `read`:
| `type` | output |
|------------|-----------------------------------------------------------------|
| `"base64"` | string: base64 encoding of the file |
| `"binary"` | string: binary string (`n`-th byte is `data.charCodeAt(n)`) |
| `"buffer"` | nodejs Buffer |
| `"file"` | string: name of file to be written (nodejs only) |
## File Formats
Despite the fact that the name of the library is `xlsx`, it supports numerous
non-XLSX file formats:
### Excel 2.0-95 (BIFF2/BIFF3/BIFF4/BIFF5)
BIFF 2/3 XLS are single-sheet streams of binary records. Excel 4 introduced
the concept of a workbook (`XLW` files) but also had single-sheet `XLS` format.
The structure is largely similar to the Lotus 1-2-3 file formats. BIFF5/8/12
extended the format in various ways but largely stuck to the same record format.
There is no official specification for any of these formats. Excel 95 can write
files in these formats, so record lengths and fields were backsolved by writing
in all of the supported formats and comparing files. Excel 2016 can generate
BIFF5 files, enabling a full suite of file tests starting from XLSX or BIFF2.
### Excel 97-2004 Binary (BIFF8)
BIFF8 exclusively uses the Compound File Binary container format, splitting some
content into streams within the file. At its core, it still uses an extended
version of the binary record format from older versions of BIFF.
The `MS-XLS` specification covers the basics of the file format, and other
specifications expand on serialization of features like properties.
### Excel 2003-2004 (SpreadsheetML)
Predating XLSX, SpreadsheetML files are simple XML files. There is no official
and comprehensive specification, although MS has released whitepapers on the
format. Since Excel 2016 can generate SpreadsheetML files, backsolving is
pretty straightforward.
### Excel 2007+ Binary (XLSB, BIFF12)
Introduced in parallel with XLSX, the XLSB filetype combines BIFF architecture
with the content separation and ZIP container of XLSX. For the most part nodes
in an XLSX sub-file can be mapped to XLSB records in a corresponding sub-file.
The `MS-XLSB` specification covers the basics of the file format, and other
specifications expand on serialization of features like properties.
### OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)
ODS is an XML-in-ZIP format akin to XLSX while FODS is an XML format akin to
SpreadsheetML. Both are detailed in the OASIS standard, but tools like LO/OO
add undocumented extensions.
UOS is a very similar format, and it comes in 2 varieties corresponding to ODS
and FODS respectively. For the most part, the difference between the formats
lies in the names of tags and attributes.
### Comma-Separated Values
Excel CSV deviates from RFC4180 in a number of important ways. The generated
CSV files should generally work in Excel although they may not work in RFC4180
compatible readers.
## Tested Environments
- NodeJS 0.8, 0.9, 0.10, 0.11, 0.12, 4.x, 5.x, 6.x, 7.x

View File

@ -13,11 +13,14 @@ program
.option('-p, --password <pw>', 'if file is encrypted, try with specified pw')
.option('-l, --list-sheets', 'list sheet names and exit')
.option('-o, --output <file>', 'output to specified file')
.option('-B, --xlsb', 'emit XLSB to <sheetname> or <file>.xlsb')
.option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm')
.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('-T, --fods', 'emit FODS to <sheetname> or <file>.xls (Flat ODS)')
.option('-S, --formulae', 'print formulae')
.option('-j, --json', 'emit formatted JSON (all fields text)')
.option('-J, --raw-js', 'emit raw JS object (raw numbers)')
@ -39,6 +42,8 @@ program.on('--help', function() {
console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/');
});
/* output formats, update list with full option name */
var workbook_formats = ['xlsx', 'xlsm', 'xlsb', 'ods', 'fods'];
program.parse(process.argv);
/* see https://github.com/SheetJS/j/issues/4 */
@ -112,7 +117,7 @@ var wopts = ({WTF:opts.WTF, bookSST:program.sst}/*:any*/);
if(program.compress) wopts.compression = true;
/* full workbook formats */
['xlsx', 'xlsm', 'xlsb', 'ods'].forEach(function(m) { if(program[m]) {
workbook_formats.forEach(function(m) { if(program[m]) {
X.writeFile(wb, sheetname || ((filename || "") + "." + m), wopts);
process.exit(0);
} });

View File

@ -1 +1 @@
XLSX.version = '0.8.4';
XLSX.version = '0.8.5';

View File

@ -564,7 +564,7 @@ function eval_fmt(fmt, v, opts, flen) {
case '[':
o = c;
while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};

View File

@ -42,6 +42,12 @@ function cc2str(arr/*:Array<number>*/)/*:string*/ {
return o;
}
function str2cc(str) {
var o = [];
for(var i = 0; i != str.length; ++i) o.push(str.charCodeAt(i));
return o;
}
function dup(o/*:any*/)/*:any*/ {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;

View File

@ -11,11 +11,15 @@ function getdatabin(data) {
if(!data) return null;
if(data.data) return char_codes(data.data);
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
if(data._data && data._data.getContent) {
var o = data._data.getContent();
if(typeof o == "string") return str2cc(o);
return Array.prototype.slice.call(o);
}
return null;
}
function getdata(data) { return (data && data.name.substr(data.name.length-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function getdata(data) { return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function safegetzipfile(zip, file/*:string*/) {
var f = file; if(zip.files[f]) return zip.files[f];

View File

@ -42,7 +42,7 @@ var unescapexml/*:StringConv*/ = (function() {
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
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)).substr(-4) + "_";});
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
/* TODO: handle codepages */

View File

@ -82,7 +82,10 @@ function ReadShift(size, t) {
case 'utf8': o = __utf8(this, this.l, this.l + size); break;
case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break;
case 'wstr': o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size)); size = 2 * size; break;
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
o = size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;

View File

@ -192,7 +192,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
for(ri = 0; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;

View File

@ -504,7 +504,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
}
var o/*:CellAddress*/ = ({r:R, c:C}/*:any*/);
/* TODO: cell style */
o.s = get_cell_style(opts.cellXfs, cell, opts);
//o.s = get_cell_style(opts.cellXfs, cell, opts);
switch(cell.t) {
case 's': case 'str':
if(opts.bookSST) {

View File

@ -1,15 +1,15 @@
function parse_wb(data, name/*:string*/, opts)/*:Workbook*/ {
if(name.substr(name.length-4)===".bin") return parse_wb_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_wb_bin((data/*:any*/), opts);
return parse_wb_xml((data/*:any*/), opts);
}
function parse_ws(data, name/*:string*/, opts, rels, wb)/*:Worksheet*/ {
if(name.substr(name.length-4)===".bin") return parse_ws_bin((data/*:any*/), opts, rels, wb);
if(name.slice(-4)===".bin") return parse_ws_bin((data/*:any*/), opts, rels, wb);
return parse_ws_xml((data/*:any*/), opts, rels, wb);
}
function parse_sty(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_sty_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), opts);
return parse_sty_xml((data/*:any*/), opts);
}
@ -18,41 +18,41 @@ function parse_theme(data, name/*:string*/, opts) {
}
function parse_sst(data, name/*:string*/, opts)/*:SST*/ {
if(name.substr(name.length-4)===".bin") return parse_sst_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_sst_bin((data/*:any*/), opts);
return parse_sst_xml((data/*:any*/), opts);
}
function parse_cmnt(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_comments_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_comments_bin((data/*:any*/), opts);
return parse_comments_xml((data/*:any*/), opts);
}
function parse_cc(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_cc_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_cc_bin((data/*:any*/), opts);
return parse_cc_xml((data/*:any*/), opts);
}
function write_wb(wb, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
}
function write_ws(data/*:Worksheet*/, name/*:string*/, opts, wb/*:Workbook*/) {
return (name.substr(name.length-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
return (name.slice(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
}
function write_sty(data, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
}
function write_sst(data/*:SST*/, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
}
/*
function write_cmnt(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
}
function write_cc(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
}
*/

View File

@ -200,15 +200,15 @@ function parse_xlml_xml(d, opts) {
if(cell.Index) c = +cell.Index - 1;
if(c < refguess.s.c) refguess.s.c = c;
if(c > refguess.e.c) refguess.e.c = c;
if(Rn[0].substr(-2) === "/>") ++c;
if(Rn[0].slice(-2) === "/>") ++c;
comments = [];
}
break;
case 'Row':
if(Rn[1]==='/' || Rn[0].substr(-2) === "/>") {
if(Rn[1]==='/' || Rn[0].slice(-2) === "/>") {
if(r < refguess.s.r) refguess.s.r = r;
if(r > refguess.e.r) refguess.e.r = r;
if(Rn[0].substr(-2) === "/>") {
if(Rn[0].slice(-2) === "/>") {
row = xlml_parsexmltag(Rn[0]);
if(row.Index) r = +row.Index - 1;
}
@ -274,7 +274,7 @@ function parse_xlml_xml(d, opts) {
case 'Alignment': break;
case 'Borders': break;
case 'Font':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") ss += str.slice(fidx, Rn.index);
else fidx = Rn.index + Rn[0].length;
break;
@ -300,7 +300,7 @@ function parse_xlml_xml(d, opts) {
case 'TotalTime':
case 'HyperlinkBase':
case 'Manager':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_prop(Props, Rn[3], str.slice(pidx, Rn.index));
else pidx = Rn.index + Rn[0].length;
break;
@ -352,6 +352,11 @@ function parse_xlml_xml(d, opts) {
break;
default:
/* FODS file root is <office:document> */
if(state.length == 0 && Rn[3] == "document") return parse_fods(str, opts);
/* UOS file root is <uof:UOF> */
if(state.length == 0 && Rn[3] == "UOF") return parse_fods(str, opts);
var seen = true;
switch(state[state.length-1][0]) {
/* OfficeDocumentSettings */
@ -689,7 +694,7 @@ function parse_xlml_xml(d, opts) {
/* CustomDocumentProperties */
if(!state[state.length-1][1]) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
if(state[state.length-1][0]==='CustomDocumentProperties') {
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_custprop(Custprops, Rn, cp, str.slice(pidx, Rn.index));
else { cp = Rn; pidx = Rn.index + Rn[0].length; }
break;
@ -714,6 +719,7 @@ function parse_xlml(data, opts) {
}
}
/* TODO */
function write_xlml(wb, opts)/*:string*/ {
var o = [XML_HEADER];
return o.join("");

View File

@ -101,18 +101,3 @@ function write_biff_buf(wb/*:Workbook*/, o/*:WriteOpts*/) {
// TODO
return ba.end();
}
function write_biff(wb/*:Workbook*/, o/*:WriteOpts*/) {
var out = write_biff_buf(wb, o);
switch(o.type) {
case "base64": break; // TODO
case "binary": {
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
}
case "file": return _fs.writeFileSync(o.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + o.type);
}
}

7
bits/82_sheeter.js Normal file
View File

@ -0,0 +1,7 @@
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}

View File

@ -9,3 +9,8 @@ function write_ods(wb, opts) {
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function parse_fods(data, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_fods) throw new Error("Unsupported ODS");
return ODS.parse_fods(data, opts);
}

View File

@ -13,7 +13,7 @@ function safe_parse_ws(zip, path/*:string*/, relsPath/*:string*/, sheet, sheetRe
} catch(e) { if(opts.WTF) throw e; }
}
var nodirs = function nodirs(x/*:string*/)/*:boolean*/{return x.substr(-1) != '/';};
var nodirs = function nodirs(x/*:string*/)/*:boolean*/{return x.slice(-1) != '/';};
function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
make_ssf(SSF);
opts = opts || {};
@ -22,6 +22,8 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
/* UOC */
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')/*:?any*/), opts);
@ -37,7 +39,7 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
dir.workbooks.push(binname);
xlsb = true;
}
if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
if(dir.workbooks[0].slice(-3) == "bin") xlsb = true;
if(xlsb) set_cp(1200);
if(!opts.bookSheets && !opts.bookProps) {

View File

@ -14,11 +14,36 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
return z.generate(oopts);
}
function write_string_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": break; // TODO
case "binary": break; // TODO
case "file": return _fs.writeFileSync(opts.file, out, {encoding:'utf8'});
case "buffer": break; // TODO
default: return out;
}
}
function write_binary_type(out, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": break; // TODO
case "binary":
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
case "file": return _fs.writeFileSync(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
}
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
var o = opts||{};
switch(o.bookType) {
case 'xml': return write_xlml(wb, o);
case 'biff2': return write_biff(wb, o);
case 'xml': 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);
default: return write_zip_type(wb, o);
}
}
@ -26,14 +51,16 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) {
var o = opts||{}; o.type = 'file';
o.file = filename;
if(!o.bookType) switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
default: switch(o.file.substr(-4).toLowerCase()) {
case '.fods': o.bookType = 'fods'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
}}
return writeSync(wb, o);
}

112
dist/ods.js vendored
View File

@ -65,7 +65,7 @@ if (typeof exports !== 'undefined') {
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var attregexg=/[^\s?>\/]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
@ -80,8 +80,15 @@ function parsexmltag(tag, skip_root) {
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
if(j===q.length) z[q] = v;
else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
if(j===q.length) {
if(q.indexOf("_") > 0) q = q.substr(0, q.indexOf("_"));
z[q] = v;
}
else {
var k = (j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1);
if(z[k] && q.substr(j-3,3) == "ext") continue;
z[k] = v;
}
}
return z;
}
@ -163,7 +170,8 @@ function xlml_normalize(d) {
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* UOS uses CJK in tags, original regex /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/ */
var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
function parse_manifest(d, opts) {
@ -220,7 +228,7 @@ function write_rdf(rdf, opts) {
return o.join("");
}
var parse_text_p = function(text, tag) {
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
return unescapexml(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
};
var utf8read = function utf8reada(orig) {
@ -270,6 +278,7 @@ var parse_content_xml = (function() {
var tag;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag;
var rowtag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag = {value:""};
@ -281,13 +290,13 @@ var parse_content_xml = (function() {
var rept = 1, isstub = false;
var i = 0;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
while((Rn = xlmlregex.exec(str))) switch(Rn[3]=Rn[3].replace(/_.*$/,"")) {
case 'table': // 9.1.2 <table:table>
case 'table': case '工作表': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
if(merges.length) ws['!merges'] = merges;
sheetag.name = utf8read(sheetag.name);
sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
@ -299,12 +308,14 @@ var parse_content_xml = (function() {
}
break;
case 'table-row': // 9.1.3 <table:table-row>
case 'table-row': case '行': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break;
++R; C = -1; break;
rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R;
C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'table-cell':
case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
@ -318,7 +329,7 @@ var parse_content_xml = (function() {
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['value-type'], v:null});
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null});
if(opts.cellFormula) {
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
@ -351,8 +362,9 @@ var parse_content_xml = (function() {
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || !q.t) {
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
@ -377,15 +389,23 @@ var parse_content_xml = (function() {
break; // 9.1.4 <table:table-cell>
/* pure state */
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'document': // TODO: <office:document> is the root for FODS
case 'document-content': case '电子表格文档': // 3.1.3.2 <office:document-content>
case 'spreadsheet': case '主体': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'styles': // TODO <office:styles>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
/* ignore state */
case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
case 'settings': // TODO: <office:settings>
case 'config-item-set': // TODO: <office:config-item-set>
case 'config-item-map-indexed': // TODO: <office:config-item-map-indexed>
case 'config-item-map-entry': // TODO: <office:config-item-map-entry>
case 'config-item-map-named': // TODO: <office:config-item-map-entry>
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
@ -395,11 +415,18 @@ var parse_content_xml = (function() {
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
case 'event-listeners': // TODO
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
textp = ""; textpidx = 0;
break;
case 'scientific-number': // TODO: <number:scientific-number>
break;
case 'currency-symbol': // TODO: <number:currency-symbol>
break;
case 'currency-style': // TODO: <number:currency-style>
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
@ -414,8 +441,12 @@ var parse_content_xml = (function() {
} break;
case 'script': break; // 3.13 <office:script>
case 'libraries': break; // TODO: <ooo:libraries>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'master-styles': break; // TODO: <office:automatic-styles>
case 'default-style': // TODO: <style:default-style>
case 'page-layout': break; // TODO: <style:page-layout>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
@ -458,7 +489,7 @@ var parse_content_xml = (function() {
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
@ -471,7 +502,7 @@ var parse_content_xml = (function() {
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
@ -489,7 +520,7 @@ var parse_content_xml = (function() {
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
case 'p': case '文本串':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
@ -497,7 +528,7 @@ var parse_content_xml = (function() {
case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'title': case '标题': break; // <*:title> OR <uof:标题>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
@ -541,6 +572,25 @@ var parse_content_xml = (function() {
case 'sheet-name': // 7.3.9
break;
case 'event-listener': // TODO
/* TODO: FODS Properties */
case 'initial-creator':
case 'creator':
case 'creation-date':
case 'generator':
case 'document-statistic':
case 'user-defined':
break;
/* TODO: FODS Config */
case 'config-item':
break;
/* TODO: style tokens */
case 'page-number': break; // TODO <text:page-number>
case 'page-count': break; // TODO <text:page-count>
case 'time': break; // TODO <text:time>
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
@ -582,7 +632,12 @@ var parse_content_xml = (function() {
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'style:') break; // TODO: styles
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(Rn[2] === 'loext:') break; // ignore undocumented extensions
if(Rn[2] === 'uof:') break; // TODO: uof
if(Rn[2] === '表:') break; // TODO: uof
if(Rn[2] === '字:') break; // TODO: uof
if(opts.WTF) throw Rn;
}
var out = {
@ -623,23 +678,33 @@ var write_content_xml = (function() {
return function wcx(wb, opts) {
var o = [XML_HEADER];
/* 3.1.3.2 */
o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
if(opts.bookType == "fods") o.push('<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">');
else o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
o.push(' </office:spreadsheet>\n');
o.push(' </office:body>\n');
o.push('</office:document-content>');
if(opts.bookType == "fods") o.push('</office:document>');
else o.push('</office:document-content>');
return o.join("");
};
})();
/* Part 3: Packages */
function parse_ods(zip, opts) {
opts = opts || ({});
var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
return parse_content_xml(getzipdata(zip, 'content.xml'), opts);
var ods = !!safegetzipfile(zip, 'objectdata');
if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
var content = getzipdata(zip, 'content.xml');
return parse_content_xml(ods ? content : utf8read(content), opts);
}
function parse_fods(data, opts) {
return parse_content_xml(data, opts);
}
function write_ods(wb, opts) {
if(opts.bookType == "fods") return write_content_xml(wb, opts);
var zip = new jszip();
var f = "";
@ -669,4 +734,5 @@ var zip = new jszip();
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
ODS.parse_fods = parse_fods;
})(typeof exports !== 'undefined' ? exports : ODS);

2
dist/ods.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/ods.min.map vendored

File diff suppressed because one or more lines are too long

22
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

22
dist/xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

137
dist/xlsx.js vendored
View File

@ -4,7 +4,7 @@
/*jshint funcscope:true, eqnull:true */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.8.4';
XLSX.version = '0.8.5';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -651,7 +651,7 @@ function eval_fmt(fmt, v, opts, flen) {
case '[':
o = c;
while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};
@ -1297,6 +1297,12 @@ function cc2str(arr) {
return o;
}
function str2cc(str) {
var o = [];
for(var i = 0; i != str.length; ++i) o.push(str.charCodeAt(i));
return o;
}
function dup(o) {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
@ -1319,11 +1325,15 @@ function getdatabin(data) {
if(!data) return null;
if(data.data) return char_codes(data.data);
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
if(data._data && data._data.getContent) {
var o = data._data.getContent();
if(typeof o == "string") return str2cc(o);
return Array.prototype.slice.call(o);
}
return null;
}
function getdata(data) { return (data && data.name.substr(data.name.length-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function getdata(data) { return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
@ -1402,7 +1412,7 @@ var unescapexml = (function() {
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
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)).substr(-4) + "_";});
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
/* TODO: handle codepages */
@ -1613,7 +1623,10 @@ function ReadShift(size, t) {
case 'utf8': o = __utf8(this, this.l, this.l + size); break;
case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break;
case 'wstr': o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size)); size = 2 * size; break;
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
o = size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
@ -8050,7 +8063,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
for(ri = 0; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
@ -8691,7 +8704,7 @@ function write_ws_bin_cell(ba, cell, R, C, opts) {
}
var o = ({r:R, c:C});
/* TODO: cell style */
o.s = get_cell_style(opts.cellXfs, cell, opts);
//o.s = get_cell_style(opts.cellXfs, cell, opts);
switch(cell.t) {
case 's': case 'str':
if(opts.bookSST) {
@ -9236,17 +9249,17 @@ function write_wb_bin(wb, opts) {
return ba.end();
}
function parse_wb(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_wb_bin((data), opts);
if(name.slice(-4)===".bin") return parse_wb_bin((data), opts);
return parse_wb_xml((data), opts);
}
function parse_ws(data, name, opts, rels, wb) {
if(name.substr(name.length-4)===".bin") return parse_ws_bin((data), opts, rels, wb);
if(name.slice(-4)===".bin") return parse_ws_bin((data), opts, rels, wb);
return parse_ws_xml((data), opts, rels, wb);
}
function parse_sty(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_sty_bin((data), opts);
if(name.slice(-4)===".bin") return parse_sty_bin((data), opts);
return parse_sty_xml((data), opts);
}
@ -9255,42 +9268,42 @@ function parse_theme(data, name, opts) {
}
function parse_sst(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_sst_bin((data), opts);
if(name.slice(-4)===".bin") return parse_sst_bin((data), opts);
return parse_sst_xml((data), opts);
}
function parse_cmnt(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_comments_bin((data), opts);
if(name.slice(-4)===".bin") return parse_comments_bin((data), opts);
return parse_comments_xml((data), opts);
}
function parse_cc(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_cc_bin((data), opts);
if(name.slice(-4)===".bin") return parse_cc_bin((data), opts);
return parse_cc_xml((data), opts);
}
function write_wb(wb, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
}
function write_ws(data, name, opts, wb) {
return (name.substr(name.length-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
return (name.slice(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
}
function write_sty(data, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
}
function write_sst(data, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
}
/*
function write_cmnt(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
}
function write_cc(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
}
*/
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
@ -9493,15 +9506,15 @@ function parse_xlml_xml(d, opts) {
if(cell.Index) c = +cell.Index - 1;
if(c < refguess.s.c) refguess.s.c = c;
if(c > refguess.e.c) refguess.e.c = c;
if(Rn[0].substr(-2) === "/>") ++c;
if(Rn[0].slice(-2) === "/>") ++c;
comments = [];
}
break;
case 'Row':
if(Rn[1]==='/' || Rn[0].substr(-2) === "/>") {
if(Rn[1]==='/' || Rn[0].slice(-2) === "/>") {
if(r < refguess.s.r) refguess.s.r = r;
if(r > refguess.e.r) refguess.e.r = r;
if(Rn[0].substr(-2) === "/>") {
if(Rn[0].slice(-2) === "/>") {
row = xlml_parsexmltag(Rn[0]);
if(row.Index) r = +row.Index - 1;
}
@ -9567,7 +9580,7 @@ function parse_xlml_xml(d, opts) {
case 'Alignment': break;
case 'Borders': break;
case 'Font':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") ss += str.slice(fidx, Rn.index);
else fidx = Rn.index + Rn[0].length;
break;
@ -9593,7 +9606,7 @@ function parse_xlml_xml(d, opts) {
case 'TotalTime':
case 'HyperlinkBase':
case 'Manager':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_prop(Props, Rn[3], str.slice(pidx, Rn.index));
else pidx = Rn.index + Rn[0].length;
break;
@ -9645,6 +9658,11 @@ function parse_xlml_xml(d, opts) {
break;
default:
/* FODS file root is <office:document> */
if(state.length == 0 && Rn[3] == "document") return parse_fods(str, opts);
/* UOS file root is <uof:UOF> */
if(state.length == 0 && Rn[3] == "UOF") return parse_fods(str, opts);
var seen = true;
switch(state[state.length-1][0]) {
/* OfficeDocumentSettings */
@ -9982,7 +10000,7 @@ function parse_xlml_xml(d, opts) {
/* CustomDocumentProperties */
if(!state[state.length-1][1]) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
if(state[state.length-1][0]==='CustomDocumentProperties') {
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_custprop(Custprops, Rn, cp, str.slice(pidx, Rn.index));
else { cp = Rn; pidx = Rn.index + Rn[0].length; }
break;
@ -10007,6 +10025,7 @@ function parse_xlml(data, opts) {
}
}
/* TODO */
function write_xlml(wb, opts) {
var o = [XML_HEADER];
return o.join("");
@ -12067,20 +12086,12 @@ function write_biff_buf(wb, o) {
// TODO
return ba.end();
}
function write_biff(wb, o) {
var out = write_biff_buf(wb, o);
switch(o.type) {
case "base64": break; // TODO
case "binary": {
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
}
case "file": return _fs.writeFileSync(o.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + o.type);
}
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb, o) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
@ -12093,6 +12104,11 @@ function write_ods(wb, opts) {
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function parse_fods(data, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_fods) throw new Error("Unsupported ODS");
return ODS.parse_fods(data, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -12150,7 +12166,7 @@ function safe_parse_ws(zip, path, relsPath, sheet, sheetRels, sheets, opts, wb)
} catch(e) { if(opts.WTF) throw e; }
}
var nodirs = function nodirs(x){return x.substr(-1) != '/';};
var nodirs = function nodirs(x){return x.slice(-1) != '/';};
function parse_zip(zip, opts) {
make_ssf(SSF);
opts = opts || {};
@ -12159,6 +12175,8 @@ function parse_zip(zip, opts) {
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
/* UOC */
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')), opts);
@ -12174,7 +12192,7 @@ function parse_zip(zip, opts) {
dir.workbooks.push(binname);
xlsb = true;
}
if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
if(dir.workbooks[0].slice(-3) == "bin") xlsb = true;
if(xlsb) set_cp(1200);
if(!opts.bookSheets && !opts.bookProps) {
@ -12424,11 +12442,36 @@ function write_zip_type(wb, opts) {
return z.generate(oopts);
}
function write_string_type(out, opts) {
switch(opts.type) {
case "base64": break; // TODO
case "binary": break; // TODO
case "file": return _fs.writeFileSync(opts.file, out, {encoding:'utf8'});
case "buffer": break; // TODO
default: return out;
}
}
function write_binary_type(out, opts) {
switch(opts.type) {
case "base64": break; // TODO
case "binary":
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
case "file": return _fs.writeFileSync(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
}
function writeSync(wb, opts) {
var o = opts||{};
switch(o.bookType) {
case 'xml': return write_xlml(wb, o);
case 'biff2': return write_biff(wb, o);
case 'xml': 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);
default: return write_zip_type(wb, o);
}
}
@ -12436,14 +12479,16 @@ function writeSync(wb, opts) {
function writeFileSync(wb, filename, opts) {
var o = opts||{}; o.type = 'file';
o.file = filename;
if(!o.bookType) switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
default: switch(o.file.substr(-4).toLowerCase()) {
case '.fods': o.bookType = 'fods'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
}}
return writeSync(wb, o);
}

20
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

View File

@ -67,7 +67,7 @@ if (typeof exports !== 'undefined') {
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var attregexg=/[^\s?>\/]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
@ -82,8 +82,15 @@ function parsexmltag(tag, skip_root) {
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
if(j===q.length) z[q] = v;
else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
if(j===q.length) {
if(q.indexOf("_") > 0) q = q.substr(0, q.indexOf("_"));
z[q] = v;
}
else {
var k = (j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1);
if(z[k] && q.substr(j-3,3) == "ext") continue;
z[k] = v;
}
}
return z;
}
@ -165,7 +172,8 @@ function xlml_normalize(d) {
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* UOS uses CJK in tags, original regex /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/ */
var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
function parse_manifest(d, opts) {
@ -222,7 +230,7 @@ function write_rdf(rdf, opts) {
return o.join("");
}
var parse_text_p = function(text, tag) {
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
return unescapexml(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
};
var utf8read = function utf8reada(orig) {
@ -272,6 +280,7 @@ var parse_content_xml = (function() {
var tag/*:: = {}*/;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag/*:: = {name:""}*/;
var rowtag/*:: = {'行号':""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = ({t:"", v:null, z:null, w:""}:any)*/;
var ctag = {value:""};
@ -283,13 +292,13 @@ var parse_content_xml = (function() {
var rept = 1, isstub = false;
var i = 0;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
while((Rn = xlmlregex.exec(str))) switch(Rn[3]=Rn[3].replace(/_.*$/,"")) {
case 'table': // 9.1.2 <table:table>
case 'table': case '工作表': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
if(merges.length) ws['!merges'] = merges;
sheetag.name = utf8read(sheetag.name);
sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
@ -301,12 +310,14 @@ var parse_content_xml = (function() {
}
break;
case 'table-row': // 9.1.3 <table:table-row>
case 'table-row': case '行': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break;
++R; C = -1; break;
rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R;
C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'table-cell':
case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
@ -320,7 +331,7 @@ var parse_content_xml = (function() {
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
if(opts.cellFormula) {
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
@ -353,8 +364,9 @@ var parse_content_xml = (function() {
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || !q.t) {
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
@ -379,15 +391,23 @@ var parse_content_xml = (function() {
break; // 9.1.4 <table:table-cell>
/* pure state */
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'document': // TODO: <office:document> is the root for FODS
case 'document-content': case '电子表格文档': // 3.1.3.2 <office:document-content>
case 'spreadsheet': case '主体': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'styles': // TODO <office:styles>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
/* ignore state */
case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
case 'settings': // TODO: <office:settings>
case 'config-item-set': // TODO: <office:config-item-set>
case 'config-item-map-indexed': // TODO: <office:config-item-map-indexed>
case 'config-item-map-entry': // TODO: <office:config-item-map-entry>
case 'config-item-map-named': // TODO: <office:config-item-map-entry>
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
@ -397,11 +417,18 @@ var parse_content_xml = (function() {
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
case 'event-listeners': // TODO
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
textp = ""; textpidx = 0;
break;
case 'scientific-number': // TODO: <number:scientific-number>
break;
case 'currency-symbol': // TODO: <number:currency-symbol>
break;
case 'currency-style': // TODO: <number:currency-style>
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
@ -416,8 +443,12 @@ var parse_content_xml = (function() {
} break;
case 'script': break; // 3.13 <office:script>
case 'libraries': break; // TODO: <ooo:libraries>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'master-styles': break; // TODO: <office:automatic-styles>
case 'default-style': // TODO: <style:default-style>
case 'page-layout': break; // TODO: <style:page-layout>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
@ -460,7 +491,7 @@ var parse_content_xml = (function() {
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
@ -473,7 +504,7 @@ var parse_content_xml = (function() {
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
@ -491,7 +522,7 @@ var parse_content_xml = (function() {
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
case 'p': case '文本串':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
@ -499,7 +530,7 @@ var parse_content_xml = (function() {
case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'title': case '标题': break; // <*:title> OR <uof:标题>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
@ -543,6 +574,25 @@ var parse_content_xml = (function() {
case 'sheet-name': // 7.3.9
break;
case 'event-listener': // TODO
/* TODO: FODS Properties */
case 'initial-creator':
case 'creator':
case 'creation-date':
case 'generator':
case 'document-statistic':
case 'user-defined':
break;
/* TODO: FODS Config */
case 'config-item':
break;
/* TODO: style tokens */
case 'page-number': break; // TODO <text:page-number>
case 'page-count': break; // TODO <text:page-count>
case 'time': break; // TODO <text:time>
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
@ -584,7 +634,12 @@ var parse_content_xml = (function() {
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'style:') break; // TODO: styles
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(Rn[2] === 'loext:') break; // ignore undocumented extensions
if(Rn[2] === 'uof:') break; // TODO: uof
if(Rn[2] === '表:') break; // TODO: uof
if(Rn[2] === '字:') break; // TODO: uof
if(opts.WTF) throw Rn;
}
var out = {
@ -625,23 +680,33 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
return function wcx(wb, opts) {
var o = [XML_HEADER];
/* 3.1.3.2 */
o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
if(opts.bookType == "fods") o.push('<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">');
else o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
o.push(' </office:spreadsheet>\n');
o.push(' </office:body>\n');
o.push('</office:document-content>');
if(opts.bookType == "fods") o.push('</office:document>');
else o.push('</office:document-content>');
return o.join("");
};
})();
/* Part 3: Packages */
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
opts = opts || ({}/*:any*/);
var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
return parse_content_xml(getzipdata(zip, 'content.xml'), opts);
var ods = !!safegetzipfile(zip, 'objectdata');
if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
var content = getzipdata(zip, 'content.xml');
return parse_content_xml(ods ? content : utf8read(content), opts);
}
function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) {
return parse_content_xml(data, opts);
}
function write_ods(wb/*:any*/, opts/*:any*/) {
if(opts.bookType == "fods") return write_content_xml(wb, opts);
/*:: if(!jszip) throw new Error("JSZip is not available"); */
var zip = new jszip();
var f = "";
@ -672,4 +737,5 @@ function write_ods(wb/*:any*/, opts/*:any*/) {
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
ODS.parse_fods = parse_fods;
})(typeof exports !== 'undefined' ? exports : ODS);

112
ods.js
View File

@ -65,7 +65,7 @@ if (typeof exports !== 'undefined') {
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var attregexg=/[^\s?>\/]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
@ -80,8 +80,15 @@ function parsexmltag(tag, skip_root) {
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
if(j===q.length) z[q] = v;
else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
if(j===q.length) {
if(q.indexOf("_") > 0) q = q.substr(0, q.indexOf("_"));
z[q] = v;
}
else {
var k = (j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1);
if(z[k] && q.substr(j-3,3) == "ext") continue;
z[k] = v;
}
}
return z;
}
@ -163,7 +170,8 @@ function xlml_normalize(d) {
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* UOS uses CJK in tags, original regex /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/ */
var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
function parse_manifest(d, opts) {
@ -220,7 +228,7 @@ function write_rdf(rdf, opts) {
return o.join("");
}
var parse_text_p = function(text, tag) {
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
return unescapexml(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
};
var utf8read = function utf8reada(orig) {
@ -270,6 +278,7 @@ var parse_content_xml = (function() {
var tag;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag;
var rowtag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag = {value:""};
@ -281,13 +290,13 @@ var parse_content_xml = (function() {
var rept = 1, isstub = false;
var i = 0;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
while((Rn = xlmlregex.exec(str))) switch(Rn[3]=Rn[3].replace(/_.*$/,"")) {
case 'table': // 9.1.2 <table:table>
case 'table': case '工作表': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
if(merges.length) ws['!merges'] = merges;
sheetag.name = utf8read(sheetag.name);
sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
@ -299,12 +308,14 @@ var parse_content_xml = (function() {
}
break;
case 'table-row': // 9.1.3 <table:table-row>
case 'table-row': case '行': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break;
++R; C = -1; break;
rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R;
C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'table-cell':
case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
@ -318,7 +329,7 @@ var parse_content_xml = (function() {
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['value-type'], v:null});
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null});
if(opts.cellFormula) {
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
@ -351,8 +362,9 @@ var parse_content_xml = (function() {
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || !q.t) {
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
@ -377,15 +389,23 @@ var parse_content_xml = (function() {
break; // 9.1.4 <table:table-cell>
/* pure state */
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'document': // TODO: <office:document> is the root for FODS
case 'document-content': case '电子表格文档': // 3.1.3.2 <office:document-content>
case 'spreadsheet': case '主体': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'styles': // TODO <office:styles>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
/* ignore state */
case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
case 'settings': // TODO: <office:settings>
case 'config-item-set': // TODO: <office:config-item-set>
case 'config-item-map-indexed': // TODO: <office:config-item-map-indexed>
case 'config-item-map-entry': // TODO: <office:config-item-map-entry>
case 'config-item-map-named': // TODO: <office:config-item-map-entry>
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
@ -395,11 +415,18 @@ var parse_content_xml = (function() {
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
case 'event-listeners': // TODO
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
textp = ""; textpidx = 0;
break;
case 'scientific-number': // TODO: <number:scientific-number>
break;
case 'currency-symbol': // TODO: <number:currency-symbol>
break;
case 'currency-style': // TODO: <number:currency-style>
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
@ -414,8 +441,12 @@ var parse_content_xml = (function() {
} break;
case 'script': break; // 3.13 <office:script>
case 'libraries': break; // TODO: <ooo:libraries>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'master-styles': break; // TODO: <office:automatic-styles>
case 'default-style': // TODO: <style:default-style>
case 'page-layout': break; // TODO: <style:page-layout>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
@ -458,7 +489,7 @@ var parse_content_xml = (function() {
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
@ -471,7 +502,7 @@ var parse_content_xml = (function() {
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
@ -489,7 +520,7 @@ var parse_content_xml = (function() {
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
case 'p': case '文本串':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
@ -497,7 +528,7 @@ var parse_content_xml = (function() {
case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'title': case '标题': break; // <*:title> OR <uof:标题>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
@ -541,6 +572,25 @@ var parse_content_xml = (function() {
case 'sheet-name': // 7.3.9
break;
case 'event-listener': // TODO
/* TODO: FODS Properties */
case 'initial-creator':
case 'creator':
case 'creation-date':
case 'generator':
case 'document-statistic':
case 'user-defined':
break;
/* TODO: FODS Config */
case 'config-item':
break;
/* TODO: style tokens */
case 'page-number': break; // TODO <text:page-number>
case 'page-count': break; // TODO <text:page-count>
case 'time': break; // TODO <text:time>
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
@ -582,7 +632,12 @@ var parse_content_xml = (function() {
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'style:') break; // TODO: styles
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(Rn[2] === 'loext:') break; // ignore undocumented extensions
if(Rn[2] === 'uof:') break; // TODO: uof
if(Rn[2] === '表:') break; // TODO: uof
if(Rn[2] === '字:') break; // TODO: uof
if(opts.WTF) throw Rn;
}
var out = {
@ -623,23 +678,33 @@ var write_content_xml = (function() {
return function wcx(wb, opts) {
var o = [XML_HEADER];
/* 3.1.3.2 */
o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
if(opts.bookType == "fods") o.push('<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">');
else o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
o.push(' </office:spreadsheet>\n');
o.push(' </office:body>\n');
o.push('</office:document-content>');
if(opts.bookType == "fods") o.push('</office:document>');
else o.push('</office:document-content>');
return o.join("");
};
})();
/* Part 3: Packages */
function parse_ods(zip, opts) {
opts = opts || ({});
var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
return parse_content_xml(getzipdata(zip, 'content.xml'), opts);
var ods = !!safegetzipfile(zip, 'objectdata');
if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
var content = getzipdata(zip, 'content.xml');
return parse_content_xml(ods ? content : utf8read(content), opts);
}
function parse_fods(data, opts) {
return parse_content_xml(data, opts);
}
function write_ods(wb, opts) {
if(opts.bookType == "fods") return write_content_xml(wb, opts);
var zip = new jszip();
var f = "";
@ -669,4 +734,5 @@ var zip = new jszip();
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
ODS.parse_fods = parse_fods;
})(typeof exports !== 'undefined' ? exports : ODS);

View File

@ -1,4 +1,4 @@
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var attregexg=/[^\s?>\/]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
@ -13,8 +13,15 @@ function parsexmltag(tag, skip_root) {
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
if(j===q.length) z[q] = v;
else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
if(j===q.length) {
if(q.indexOf("_") > 0) q = q.substr(0, q.indexOf("_"));
z[q] = v;
}
else {
var k = (j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1);
if(z[k] && q.substr(j-3,3) == "ext") continue;
z[k] = v;
}
}
return z;
}

View File

@ -5,4 +5,5 @@ function xlml_normalize(d) {
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* UOS uses CJK in tags, original regex /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/ */
var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;

View File

@ -1,5 +1,5 @@
var parse_text_p = function(text, tag) {
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
return unescapexml(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
};
var utf8read = function utf8reada(orig) {

View File

@ -18,6 +18,7 @@ var parse_content_xml = (function() {
var tag/*:: = {}*/;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag/*:: = {name:""}*/;
var rowtag/*:: = {'行号':""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = ({t:"", v:null, z:null, w:""}:any)*/;
var ctag = {value:""};
@ -29,13 +30,13 @@ var parse_content_xml = (function() {
var rept = 1, isstub = false;
var i = 0;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
while((Rn = xlmlregex.exec(str))) switch(Rn[3]=Rn[3].replace(/_.*$/,"")) {
case 'table': // 9.1.2 <table:table>
case 'table': case '工作表': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
if(merges.length) ws['!merges'] = merges;
sheetag.name = utf8read(sheetag.name);
sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
@ -47,12 +48,14 @@ var parse_content_xml = (function() {
}
break;
case 'table-row': // 9.1.3 <table:table-row>
case 'table-row': case '行': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break;
++R; C = -1; break;
rowtag = parsexmltag(Rn[0], false);
if(rowtag['行号']) R = rowtag['行号'] - 1; else ++R;
C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'table-cell':
case 'table-cell': case '数据':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
@ -66,7 +69,7 @@ var parse_content_xml = (function() {
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0], false);
q = ({t:ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
q = ({t:ctag['数据类型'] || ctag['value-type'], v:null/*:: , z:null, w:""*/}/*:any*/);
if(opts.cellFormula) {
if(ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
@ -99,8 +102,9 @@ var parse_content_xml = (function() {
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'number': q.t = 'n'; q.v = parseFloat(ctag['数据数值']); break;
default:
if(q.t === 'string' || !q.t) {
if(q.t === 'string' || q.t === 'text' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
@ -125,15 +129,23 @@ var parse_content_xml = (function() {
break; // 9.1.4 <table:table-cell>
/* pure state */
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'document': // TODO: <office:document> is the root for FODS
case 'document-content': case '电子表格文档': // 3.1.3.2 <office:document-content>
case 'spreadsheet': case '主体': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'styles': // TODO <office:styles>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
/* ignore state */
case 'meta': case '元数据': // TODO: <office:meta> <uof:元数据> FODS/UOF
case 'settings': // TODO: <office:settings>
case 'config-item-set': // TODO: <office:config-item-set>
case 'config-item-map-indexed': // TODO: <office:config-item-map-indexed>
case 'config-item-map-entry': // TODO: <office:config-item-map-entry>
case 'config-item-map-named': // TODO: <office:config-item-map-entry>
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
@ -143,11 +155,18 @@ var parse_content_xml = (function() {
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
case 'event-listeners': // TODO
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], false]);
textp = ""; textpidx = 0;
break;
case 'scientific-number': // TODO: <number:scientific-number>
break;
case 'currency-symbol': // TODO: <number:currency-symbol>
break;
case 'currency-style': // TODO: <number:currency-style>
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
@ -162,8 +181,12 @@ var parse_content_xml = (function() {
} break;
case 'script': break; // 3.13 <office:script>
case 'libraries': break; // TODO: <ooo:libraries>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'master-styles': break; // TODO: <office:automatic-styles>
case 'default-style': // TODO: <style:default-style>
case 'page-layout': break; // TODO: <style:page-layout>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
@ -206,7 +229,7 @@ var parse_content_xml = (function() {
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
@ -219,7 +242,7 @@ var parse_content_xml = (function() {
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'body': case '电子表格': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
@ -237,7 +260,7 @@ var parse_content_xml = (function() {
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
case 'p': case '文本串':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
@ -245,7 +268,7 @@ var parse_content_xml = (function() {
case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'title': case '标题': break; // <*:title> OR <uof:标题>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
@ -289,6 +312,25 @@ var parse_content_xml = (function() {
case 'sheet-name': // 7.3.9
break;
case 'event-listener': // TODO
/* TODO: FODS Properties */
case 'initial-creator':
case 'creator':
case 'creation-date':
case 'generator':
case 'document-statistic':
case 'user-defined':
break;
/* TODO: FODS Config */
case 'config-item':
break;
/* TODO: style tokens */
case 'page-number': break; // TODO <text:page-number>
case 'page-count': break; // TODO <text:page-count>
case 'time': break; // TODO <text:time>
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
@ -330,7 +372,12 @@ var parse_content_xml = (function() {
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'style:') break; // TODO: styles
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(Rn[2] === 'loext:') break; // ignore undocumented extensions
if(Rn[2] === 'uof:') break; // TODO: uof
if(Rn[2] === '表:') break; // TODO: uof
if(Rn[2] === '字:') break; // TODO: uof
if(opts.WTF) throw Rn;
}
var out = {

View File

@ -29,13 +29,15 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
return function wcx(wb, opts) {
var o = [XML_HEADER];
/* 3.1.3.2 */
o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
if(opts.bookType == "fods") o.push('<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">');
else o.push('<office:document-content office:version="1.2" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">\n'); // TODO
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
o.push(' </office:spreadsheet>\n');
o.push(' </office:body>\n');
o.push('</office:document-content>');
if(opts.bookType == "fods") o.push('</office:document>');
else o.push('</office:document-content>');
return o.join("");
};
})();

View File

@ -1,6 +1,12 @@
/* Part 3: Packages */
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
opts = opts || ({}/*:any*/);
var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
return parse_content_xml(getzipdata(zip, 'content.xml'), opts);
var ods = !!safegetzipfile(zip, 'objectdata');
if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
var content = getzipdata(zip, 'content.xml');
return parse_content_xml(ods ? content : utf8read(content), opts);
}
function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) {
return parse_content_xml(data, opts);
}

View File

@ -1,4 +1,6 @@
function write_ods(wb/*:any*/, opts/*:any*/) {
if(opts.bookType == "fods") return write_content_xml(wb, opts);
/*:: if(!jszip) throw new Error("JSZip is not available"); */
var zip = new jszip();
var f = "";

View File

@ -1,2 +1,3 @@
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
ODS.parse_fods = parse_fods;

View File

@ -1,8 +1,8 @@
{
"name": "xlsx",
"version": "0.8.4",
"version": "0.8.5",
"author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLSM/XLS/XML) and ODS spreadsheet parser and writer",
"description": "Excel (XLSB/XLSX/XLSM/XLS/XML) and ODS (ODS/FODS/UOS) spreadsheet parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "office", "spreadsheet" ],
"bin": {
"xlsx": "./bin/xlsx.njs"

View File

@ -89,3 +89,5 @@ XLSX.writeFile(wb, 'sheetjs.xlsm');
XLSX.writeFile(wb, 'sheetjs.xlsb');
XLSX.writeFile(wb, 'sheetjs.xls', {bookType:'biff2'});
XLSX.writeFile(wb, 'sheetjs.ods');
XLSX.writeFile(wb, 'sheetjs.fods');
XLSX.writeFile(wb, 'sheetjs.csv');

View File

@ -4,7 +4,7 @@
/*jshint funcscope:true, eqnull:true */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.8.4';
XLSX.version = '0.8.5';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -668,7 +668,7 @@ function eval_fmt(fmt, v, opts, flen) {
case '[':
o = c;
while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};
@ -1314,6 +1314,12 @@ function cc2str(arr/*:Array<number>*/)/*:string*/ {
return o;
}
function str2cc(str) {
var o = [];
for(var i = 0; i != str.length; ++i) o.push(str.charCodeAt(i));
return o;
}
function dup(o/*:any*/)/*:any*/ {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
@ -1336,11 +1342,15 @@ function getdatabin(data) {
if(!data) return null;
if(data.data) return char_codes(data.data);
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
if(data._data && data._data.getContent) {
var o = data._data.getContent();
if(typeof o == "string") return str2cc(o);
return Array.prototype.slice.call(o);
}
return null;
}
function getdata(data) { return (data && data.name.substr(data.name.length-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function getdata(data) { return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function safegetzipfile(zip, file/*:string*/) {
var f = file; if(zip.files[f]) return zip.files[f];
@ -1420,7 +1430,7 @@ var unescapexml/*:StringConv*/ = (function() {
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
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)).substr(-4) + "_";});
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
/* TODO: handle codepages */
@ -1631,7 +1641,10 @@ function ReadShift(size, t) {
case 'utf8': o = __utf8(this, this.l, this.l + size); break;
case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break;
case 'wstr': o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size)); size = 2 * size; break;
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
o = size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
@ -8068,7 +8081,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
for(ri = 0; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
@ -8709,7 +8722,7 @@ function write_ws_bin_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:num
}
var o/*:CellAddress*/ = ({r:R, c:C}/*:any*/);
/* TODO: cell style */
o.s = get_cell_style(opts.cellXfs, cell, opts);
//o.s = get_cell_style(opts.cellXfs, cell, opts);
switch(cell.t) {
case 's': case 'str':
if(opts.bookSST) {
@ -9254,17 +9267,17 @@ function write_wb_bin(wb, opts) {
return ba.end();
}
function parse_wb(data, name/*:string*/, opts)/*:Workbook*/ {
if(name.substr(name.length-4)===".bin") return parse_wb_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_wb_bin((data/*:any*/), opts);
return parse_wb_xml((data/*:any*/), opts);
}
function parse_ws(data, name/*:string*/, opts, rels, wb)/*:Worksheet*/ {
if(name.substr(name.length-4)===".bin") return parse_ws_bin((data/*:any*/), opts, rels, wb);
if(name.slice(-4)===".bin") return parse_ws_bin((data/*:any*/), opts, rels, wb);
return parse_ws_xml((data/*:any*/), opts, rels, wb);
}
function parse_sty(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_sty_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_sty_bin((data/*:any*/), opts);
return parse_sty_xml((data/*:any*/), opts);
}
@ -9273,42 +9286,42 @@ function parse_theme(data, name/*:string*/, opts) {
}
function parse_sst(data, name/*:string*/, opts)/*:SST*/ {
if(name.substr(name.length-4)===".bin") return parse_sst_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_sst_bin((data/*:any*/), opts);
return parse_sst_xml((data/*:any*/), opts);
}
function parse_cmnt(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_comments_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_comments_bin((data/*:any*/), opts);
return parse_comments_xml((data/*:any*/), opts);
}
function parse_cc(data, name/*:string*/, opts) {
if(name.substr(name.length-4)===".bin") return parse_cc_bin((data/*:any*/), opts);
if(name.slice(-4)===".bin") return parse_cc_bin((data/*:any*/), opts);
return parse_cc_xml((data/*:any*/), opts);
}
function write_wb(wb, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
}
function write_ws(data/*:Worksheet*/, name/*:string*/, opts, wb/*:Workbook*/) {
return (name.substr(name.length-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
return (name.slice(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
}
function write_sty(data, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
}
function write_sst(data/*:SST*/, name/*:string*/, opts) {
return (name.substr(name.length-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
}
/*
function write_cmnt(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
}
function write_cc(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
}
*/
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
@ -9513,15 +9526,15 @@ function parse_xlml_xml(d, opts) {
if(cell.Index) c = +cell.Index - 1;
if(c < refguess.s.c) refguess.s.c = c;
if(c > refguess.e.c) refguess.e.c = c;
if(Rn[0].substr(-2) === "/>") ++c;
if(Rn[0].slice(-2) === "/>") ++c;
comments = [];
}
break;
case 'Row':
if(Rn[1]==='/' || Rn[0].substr(-2) === "/>") {
if(Rn[1]==='/' || Rn[0].slice(-2) === "/>") {
if(r < refguess.s.r) refguess.s.r = r;
if(r > refguess.e.r) refguess.e.r = r;
if(Rn[0].substr(-2) === "/>") {
if(Rn[0].slice(-2) === "/>") {
row = xlml_parsexmltag(Rn[0]);
if(row.Index) r = +row.Index - 1;
}
@ -9587,7 +9600,7 @@ function parse_xlml_xml(d, opts) {
case 'Alignment': break;
case 'Borders': break;
case 'Font':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") ss += str.slice(fidx, Rn.index);
else fidx = Rn.index + Rn[0].length;
break;
@ -9613,7 +9626,7 @@ function parse_xlml_xml(d, opts) {
case 'TotalTime':
case 'HyperlinkBase':
case 'Manager':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_prop(Props, Rn[3], str.slice(pidx, Rn.index));
else pidx = Rn.index + Rn[0].length;
break;
@ -9665,6 +9678,11 @@ function parse_xlml_xml(d, opts) {
break;
default:
/* FODS file root is <office:document> */
if(state.length == 0 && Rn[3] == "document") return parse_fods(str, opts);
/* UOS file root is <uof:UOF> */
if(state.length == 0 && Rn[3] == "UOF") return parse_fods(str, opts);
var seen = true;
switch(state[state.length-1][0]) {
/* OfficeDocumentSettings */
@ -10002,7 +10020,7 @@ function parse_xlml_xml(d, opts) {
/* CustomDocumentProperties */
if(!state[state.length-1][1]) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
if(state[state.length-1][0]==='CustomDocumentProperties') {
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_custprop(Custprops, Rn, cp, str.slice(pidx, Rn.index));
else { cp = Rn; pidx = Rn.index + Rn[0].length; }
break;
@ -10027,6 +10045,7 @@ function parse_xlml(data, opts) {
}
}
/* TODO */
function write_xlml(wb, opts)/*:string*/ {
var o = [XML_HEADER];
return o.join("");
@ -12087,20 +12106,12 @@ function write_biff_buf(wb/*:Workbook*/, o/*:WriteOpts*/) {
// TODO
return ba.end();
}
function write_biff(wb/*:Workbook*/, o/*:WriteOpts*/) {
var out = write_biff_buf(wb, o);
switch(o.type) {
case "base64": break; // TODO
case "binary": {
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
}
case "file": return _fs.writeFileSync(o.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + o.type);
}
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
@ -12113,6 +12124,11 @@ function write_ods(wb, opts) {
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function parse_fods(data, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_fods) throw new Error("Unsupported ODS");
return ODS.parse_fods(data, opts);
}
function fix_opts_func(defaults/*:Array<Array<any> >*/)/*:{(o:any):void}*/ {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -12170,7 +12186,7 @@ function safe_parse_ws(zip, path/*:string*/, relsPath/*:string*/, sheet, sheetRe
} catch(e) { if(opts.WTF) throw e; }
}
var nodirs = function nodirs(x/*:string*/)/*:boolean*/{return x.substr(-1) != '/';};
var nodirs = function nodirs(x/*:string*/)/*:boolean*/{return x.slice(-1) != '/';};
function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
make_ssf(SSF);
opts = opts || {};
@ -12179,6 +12195,8 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
/* UOC */
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')/*:?any*/), opts);
@ -12194,7 +12212,7 @@ function parse_zip(zip/*:ZIP*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
dir.workbooks.push(binname);
xlsb = true;
}
if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
if(dir.workbooks[0].slice(-3) == "bin") xlsb = true;
if(xlsb) set_cp(1200);
if(!opts.bookSheets && !opts.bookProps) {
@ -12446,11 +12464,36 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
return z.generate(oopts);
}
function write_string_type(out/*:string*/, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": break; // TODO
case "binary": break; // TODO
case "file": return _fs.writeFileSync(opts.file, out, {encoding:'utf8'});
case "buffer": break; // TODO
default: return out;
}
}
function write_binary_type(out, opts/*:WriteOpts*/) {
switch(opts.type) {
case "base64": break; // TODO
case "binary":
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
case "file": return _fs.writeFileSync(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
}
function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
var o = opts||{};
switch(o.bookType) {
case 'xml': return write_xlml(wb, o);
case 'biff2': return write_biff(wb, o);
case 'xml': 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);
default: return write_zip_type(wb, o);
}
}
@ -12458,14 +12501,16 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) {
var o = opts||{}; o.type = 'file';
o.file = filename;
if(!o.bookType) switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
default: switch(o.file.substr(-4).toLowerCase()) {
case '.fods': o.bookType = 'fods'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
}}
return writeSync(wb, o);
}

137
xlsx.js
View File

@ -4,7 +4,7 @@
/*jshint funcscope:true, eqnull:true */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.8.4';
XLSX.version = '0.8.5';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -651,7 +651,7 @@ function eval_fmt(fmt, v, opts, flen) {
case '[':
o = c;
while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
if(o.match(abstime)) {
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
out[out.length] = {t:'Z', v:o.toLowerCase()};
@ -1297,6 +1297,12 @@ function cc2str(arr) {
return o;
}
function str2cc(str) {
var o = [];
for(var i = 0; i != str.length; ++i) o.push(str.charCodeAt(i));
return o;
}
function dup(o) {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
@ -1319,11 +1325,15 @@ function getdatabin(data) {
if(!data) return null;
if(data.data) return char_codes(data.data);
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
if(data._data && data._data.getContent) {
var o = data._data.getContent();
if(typeof o == "string") return str2cc(o);
return Array.prototype.slice.call(o);
}
return null;
}
function getdata(data) { return (data && data.name.substr(data.name.length-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function getdata(data) { return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
@ -1402,7 +1412,7 @@ var unescapexml = (function() {
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
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)).substr(-4) + "_";});
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).slice(-4) + "_";});
}
/* TODO: handle codepages */
@ -1613,7 +1623,10 @@ function ReadShift(size, t) {
case 'utf8': o = __utf8(this, this.l, this.l + size); break;
case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break;
case 'wstr': o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size)); size = 2 * size; break;
case 'wstr':
if(typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l+2*size));
else return ReadShift.call(this, size, 'dbcs');
o = size = 2 * size; break;
/* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
@ -8050,7 +8063,7 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
for(ri = 0; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
@ -8691,7 +8704,7 @@ function write_ws_bin_cell(ba, cell, R, C, opts) {
}
var o = ({r:R, c:C});
/* TODO: cell style */
o.s = get_cell_style(opts.cellXfs, cell, opts);
//o.s = get_cell_style(opts.cellXfs, cell, opts);
switch(cell.t) {
case 's': case 'str':
if(opts.bookSST) {
@ -9236,17 +9249,17 @@ function write_wb_bin(wb, opts) {
return ba.end();
}
function parse_wb(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_wb_bin((data), opts);
if(name.slice(-4)===".bin") return parse_wb_bin((data), opts);
return parse_wb_xml((data), opts);
}
function parse_ws(data, name, opts, rels, wb) {
if(name.substr(name.length-4)===".bin") return parse_ws_bin((data), opts, rels, wb);
if(name.slice(-4)===".bin") return parse_ws_bin((data), opts, rels, wb);
return parse_ws_xml((data), opts, rels, wb);
}
function parse_sty(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_sty_bin((data), opts);
if(name.slice(-4)===".bin") return parse_sty_bin((data), opts);
return parse_sty_xml((data), opts);
}
@ -9255,42 +9268,42 @@ function parse_theme(data, name, opts) {
}
function parse_sst(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_sst_bin((data), opts);
if(name.slice(-4)===".bin") return parse_sst_bin((data), opts);
return parse_sst_xml((data), opts);
}
function parse_cmnt(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_comments_bin((data), opts);
if(name.slice(-4)===".bin") return parse_comments_bin((data), opts);
return parse_comments_xml((data), opts);
}
function parse_cc(data, name, opts) {
if(name.substr(name.length-4)===".bin") return parse_cc_bin((data), opts);
if(name.slice(-4)===".bin") return parse_cc_bin((data), opts);
return parse_cc_xml((data), opts);
}
function write_wb(wb, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
return (name.slice(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
}
function write_ws(data, name, opts, wb) {
return (name.substr(name.length-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
return (name.slice(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
}
function write_sty(data, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
}
function write_sst(data, name, opts) {
return (name.substr(name.length-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
}
/*
function write_cmnt(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
}
function write_cc(data, name:string, opts) {
return (name.substr(name.length-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
return (name.slice(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
}
*/
var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
@ -9493,15 +9506,15 @@ function parse_xlml_xml(d, opts) {
if(cell.Index) c = +cell.Index - 1;
if(c < refguess.s.c) refguess.s.c = c;
if(c > refguess.e.c) refguess.e.c = c;
if(Rn[0].substr(-2) === "/>") ++c;
if(Rn[0].slice(-2) === "/>") ++c;
comments = [];
}
break;
case 'Row':
if(Rn[1]==='/' || Rn[0].substr(-2) === "/>") {
if(Rn[1]==='/' || Rn[0].slice(-2) === "/>") {
if(r < refguess.s.r) refguess.s.r = r;
if(r > refguess.e.r) refguess.e.r = r;
if(Rn[0].substr(-2) === "/>") {
if(Rn[0].slice(-2) === "/>") {
row = xlml_parsexmltag(Rn[0]);
if(row.Index) r = +row.Index - 1;
}
@ -9567,7 +9580,7 @@ function parse_xlml_xml(d, opts) {
case 'Alignment': break;
case 'Borders': break;
case 'Font':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") ss += str.slice(fidx, Rn.index);
else fidx = Rn.index + Rn[0].length;
break;
@ -9593,7 +9606,7 @@ function parse_xlml_xml(d, opts) {
case 'TotalTime':
case 'HyperlinkBase':
case 'Manager':
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_prop(Props, Rn[3], str.slice(pidx, Rn.index));
else pidx = Rn.index + Rn[0].length;
break;
@ -9645,6 +9658,11 @@ function parse_xlml_xml(d, opts) {
break;
default:
/* FODS file root is <office:document> */
if(state.length == 0 && Rn[3] == "document") return parse_fods(str, opts);
/* UOS file root is <uof:UOF> */
if(state.length == 0 && Rn[3] == "UOF") return parse_fods(str, opts);
var seen = true;
switch(state[state.length-1][0]) {
/* OfficeDocumentSettings */
@ -9982,7 +10000,7 @@ function parse_xlml_xml(d, opts) {
/* CustomDocumentProperties */
if(!state[state.length-1][1]) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
if(state[state.length-1][0]==='CustomDocumentProperties') {
if(Rn[0].substr(-2) === "/>") break;
if(Rn[0].slice(-2) === "/>") break;
else if(Rn[1]==="/") xlml_set_custprop(Custprops, Rn, cp, str.slice(pidx, Rn.index));
else { cp = Rn; pidx = Rn.index + Rn[0].length; }
break;
@ -10007,6 +10025,7 @@ function parse_xlml(data, opts) {
}
}
/* TODO */
function write_xlml(wb, opts) {
var o = [XML_HEADER];
return o.join("");
@ -12067,20 +12086,12 @@ function write_biff_buf(wb, o) {
// TODO
return ba.end();
}
function write_biff(wb, o) {
var out = write_biff_buf(wb, o);
switch(o.type) {
case "base64": break; // TODO
case "binary": {
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
}
case "file": return _fs.writeFileSync(o.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + o.type);
}
/* actual implementation in utils, wrappers are for read/write */
function write_csv_str(wb, o) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
}
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
@ -12093,6 +12104,11 @@ function write_ods(wb, opts) {
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function parse_fods(data, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_fods) throw new Error("Unsupported ODS");
return ODS.parse_fods(data, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -12150,7 +12166,7 @@ function safe_parse_ws(zip, path, relsPath, sheet, sheetRels, sheets, opts, wb)
} catch(e) { if(opts.WTF) throw e; }
}
var nodirs = function nodirs(x){return x.substr(-1) != '/';};
var nodirs = function nodirs(x){return x.slice(-1) != '/';};
function parse_zip(zip, opts) {
make_ssf(SSF);
opts = opts || {};
@ -12159,6 +12175,8 @@ function parse_zip(zip, opts) {
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
/* UOC */
if(safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')), opts);
@ -12174,7 +12192,7 @@ function parse_zip(zip, opts) {
dir.workbooks.push(binname);
xlsb = true;
}
if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
if(dir.workbooks[0].slice(-3) == "bin") xlsb = true;
if(xlsb) set_cp(1200);
if(!opts.bookSheets && !opts.bookProps) {
@ -12424,11 +12442,36 @@ function write_zip_type(wb, opts) {
return z.generate(oopts);
}
function write_string_type(out, opts) {
switch(opts.type) {
case "base64": break; // TODO
case "binary": break; // TODO
case "file": return _fs.writeFileSync(opts.file, out, {encoding:'utf8'});
case "buffer": break; // TODO
default: return out;
}
}
function write_binary_type(out, opts) {
switch(opts.type) {
case "base64": break; // TODO
case "binary":
var bstr = "";
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
return bstr;
case "file": return _fs.writeFileSync(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
}
function writeSync(wb, opts) {
var o = opts||{};
switch(o.bookType) {
case 'xml': return write_xlml(wb, o);
case 'biff2': return write_biff(wb, o);
case 'xml': 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);
default: return write_zip_type(wb, o);
}
}
@ -12436,14 +12479,16 @@ function writeSync(wb, opts) {
function writeFileSync(wb, filename, opts) {
var o = opts||{}; o.type = 'file';
o.file = filename;
if(!o.bookType) switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.slice(-5).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
default: switch(o.file.substr(-4).toLowerCase()) {
case '.fods': o.bookType = 'fods'; break;
default: switch(o.file.slice(-4).toLowerCase()) {
case '.xls': o.bookType = 'biff2'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
}}
return writeSync(wb, o);
}