version bump 0.8.2: ODS and cleanup

- README and example cleanup
- basic XLSB and ODS write support
- flow typecheck for ODS file
  Note: xlsx.js flow fails: https://github.com/facebook/flow/issues/380
- exposed jszip compression (fixes #220, closes #284)

README issues:

|  id  | author         | comment                                      |
|-----:|:---------------|:---------------------------------------------|
| #202 | @sao93859      | closes #202                                  |
| #211 | @alexanderchan | closes #211 corrected examples               |
| #327 | @cskaandorp    | changed saveAs example to match write tests  |
| #424 | @dskrvk        | added note about s2roa h/t @LeonardoPatignio |
| #496 | @jimmywarting  | closes #496 adapted rABS examples with rAAS  |

ODS file format issues:

|  id  | author         | comment                                      |
|-----:|:---------------|:---------------------------------------------|
| #148 | @user4815162342| closes #148 h/t @ziacik                      |
| #166 | @paulproteus   | closes #166 rudimentary ODS write support    |
| #177 | @ziacik        | closes #177                                  |
| #179 | @ziacik        | closes #179 use JSON when available          |
| #317 | @ziacik        | closes #317                                  |
| #328 | @think01       | closes #328                                  |
| #383 | @mdamt         | closes #383 duplicate cells should be copied |
| #430 | @RB-Lab        | closes #430                                  |
| #546 | @lgodard       | closes #546 thanks to other changes          |
This commit is contained in:
SheetJS 2017-02-03 15:50:45 -05:00
parent 2a756fffcc
commit 86d6a093f0
66 changed files with 13914 additions and 298 deletions

@ -11,8 +11,21 @@
.*/demo/browser.js
.*/shim.js
.*/odsbits/.*
.*/ods.js
.*/xlsx.js
.*/xlsxworker.js
.*/xlsxworker1.js
.*/xlsxworker2.js
.*/jszip.js
.*/tests/.*
.*/xlsx.flow.js
[include]
xlsx.js
xlsxworker.flow.js
xlsxworker1.flow.js
xlsxworker2.flow.js
ods.flow.js
.*/bin/.*.njs
.*/demo/browser.flow.js
@ -23,4 +36,4 @@ misc/flowdeps.js
[options]
module.file_ext=.js
module.file_ext=.njs
module.ignore_non_literal_requires=true

3
.gitignore vendored

@ -13,8 +13,7 @@ tmp
*.xlsb
*.xlsm
*.xlsx
*.xlsm
*.xlsb
*.ods
*.xml
*.htm
*.html

@ -14,6 +14,7 @@ tmp
*.xlsb
*.xlsm
*.xlsx
*.ods
*.xml
*.htm
*.html

@ -5,7 +5,9 @@ node_js:
- "5"
- "4.2"
- "0.12"
- "0.11"
- "0.10"
- "0.9"
- "0.8"
before_install:
- "npm install -g npm@next"

@ -11,13 +11,19 @@ ULIB=$(shell echo $(LIB) | tr a-z A-Z)
DEPS=$(sort $(wildcard bits/*.js))
TARGET=$(LIB).js
FLOWTARGET=$(LIB).flow.js
FLOWAUX=$(patsubst %.js,%.flow.js,$(AUXTARGETS))
AUXSCPTS=xlsxworker1.js xlsxworker2.js xlsxworker.js
FLOWTGTS=$(TARGET) $(AUXTARGETS) $(AUXSCPTS)
## Main Targets
.PHONY: all
all: $(TARGET) $(AUXTARGETS) ## Build library and auxiliary scripts
all: $(TARGET) $(AUXTARGETS) $(AUXSCPTS) ## Build library and auxiliary scripts
$(TARGET): $(DEPS)
$(FLOWTGTS): %.js : %.flow.js
node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^[ \t]*\/\*[:#][^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*[:#][^*]*\*\//gm,""))' > $@
$(FLOWTARGET): $(DEPS)
cat $^ | tr -d '\15\32' > $@
bits/01_version.js: package.json
@ -58,6 +64,8 @@ dist-deps: ods.js ## Copy dependencies for distribution
cp node_modules/codepage/dist/cpexcel.full.js dist/cpexcel.js
cp jszip.js dist/jszip.js
cp ods.js dist/ods.js
uglifyjs ods.js -o dist/ods.min.js --source-map dist/ods.min.map --preamble "$$(head -n 1 bits/00_header.js)"
misc/strip_sourcemap.sh dist/ods.min.js
bower.json: misc/_bower.json package.json
cat $< | sed 's/_VERSION_/'`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`'/' > $@
@ -69,9 +77,8 @@ aux: $(AUXTARGETS)
ods: ods.js
ODSDEPS=$(sort $(wildcard odsbits/*.js))
ods.js: $(ODSDEPS) ## Build ODS support library
ods.flow.js: $(ODSDEPS) ## Build ODS support library
cat $(ODSDEPS) | tr -d '\15\32' > $@
cp ods.js dist/ods.js
## Testing
@ -90,7 +97,7 @@ $(TESTFMT): test_%:
## Code Checking
.PHONY: lint
lint: $(TARGET) ## Run jshint and jscs checks
lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks
@jshint --show-non-errors $(TARGET) $(AUXTARGETS)
@jshint --show-non-errors $(CMDS)
@jshint --show-non-errors package.json bower.json

@ -14,14 +14,18 @@ Supported read formats:
Supported write formats:
- XLSX
- Excel 2007+ XML Formats (XLSX/XLSM)
- Excel 2007+ Binary Format (XLSB) nodejs only
- CSV (and general DSV)
- JSON and JS objects (various styles)
- OpenDocument Spreadsheet (ODS)
Demo: <http://oss.sheetjs.com/js-xlsx>
Source: <http://git.io/xlsx>
Paid support available through the [reinforcements program](http://sheetjs.com/reinforcements)
## Installation
With [npm](https://www.npmjs.org/package/xlsx):
@ -53,7 +57,7 @@ circumstances, so they do not ship with the core. For browser use, they must
be included directly:
```html
<!-- international support from https://github.com/sheetjs/js-codepage -->
<!-- international support from js-codepage -->
<script src="dist/cpexcel.js"></script>
<!-- ODS support -->
<script src="dist/ods.js"></script>
@ -116,45 +120,75 @@ oReq.onload = function(e) {
oReq.send();
```
- HTML5 drag-and-drop using readAsBinaryString:
- HTML5 drag-and-drop using readAsBinaryString or readAsArrayBuffer:
note: readAsBinaryString and readAsArrayBuffer may not be available in every
browser. Use dynamic feature tests to determine which method to use.
```js
/* processing array buffers, only required for readAsArrayBuffer */
function fixdata(data) {
var o = "", l = 0, w = 10240;
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
return o;
}
var rABS = true; // true: readAsBinaryString ; false: readAsArrayBuffer
/* set up drag-and-drop event */
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files;
var i,f;
for (i = 0, f = files[i]; i != files.length; ++i) {
for (i = 0; i != files.length; ++i) {
f = files[i];
var reader = new FileReader();
var name = f.name;
reader.onload = function(e) {
var data = e.target.result;
/* if binary string, read with type 'binary' */
var workbook = XLSX.read(data, {type: 'binary'});
var workbook;
if(rABS) {
/* if binary string, read with type 'binary' */
workbook = XLSX.read(data, {type: 'binary'});
} else {
/* if array buffer, convert to base64 */
var arr = fixdata(data);
workbook = XLSX.read(btoa(arr), {type: 'base64'});
}
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsBinaryString(f);
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
}
}
drop_dom_element.addEventListener('drop', handleDrop, false);
```
- HTML5 input file element using readAsBinaryString:
- HTML5 input file element using readAsBinaryString or readAsArrayBuffer:
```js
/* fixdata and rABS are defined in the drag and drop example */
function handleFile(e) {
var files = e.target.files;
var i,f;
for (i = 0, f = files[i]; i != files.length; ++i) {
for (i = 0; i != files.length; ++i) {
f = files[i];
var reader = new FileReader();
var name = f.name;
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
var workbook;
if(rABS) {
/* if binary string, read with type 'binary' */
workbook = XLSX.read(data, {type: 'binary'});
} else {
/* if array buffer, convert to base64 */
var arr = fixdata(data);
workbook = XLSX.read(btoa(arr), {type: 'base64'});
}
/* DO SOMETHING WITH workbook HERE */
};
@ -164,6 +198,11 @@ function handleFile(e) {
input_dom_element.addEventListener('change', handleFile, false);
```
The readAsArrayBuffer form requires some preprocessing:
```js
```
## Working with the Workbook
The full object format is described later in this README.
@ -254,7 +293,7 @@ function s2ab(s) {
}
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([s2ab(wbout)],{type:""}), "test.xlsx")
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx");
```
Complete examples:
@ -290,8 +329,11 @@ Utilities are available in the `XLSX.utils` object:
Exporting:
- `sheet_to_json` converts a workbook object to an array of JSON objects.
- `sheet_to_csv` generates delimiter-separated-values output
- `sheet_to_formulae` generates a list of the formulae (with value fallbacks)
`sheet_to_row_object_array` is an alias that will be removed in the future.
- `sheet_to_csv` generates delimiter-separated-values output.
- `sheet_to_formulae` generates a list of the formulae (with value fallbacks).
The `sheet_to_*` functions accept a worksheet and an optional options object.
Cell and cell address manipulation:
@ -371,7 +413,7 @@ Type `d` is the Date type, generated only when the option `cellDates` is passed.
Since JSON does not have a natural Date type, parsers are generally expected to
store ISO 8601 Date strings like you would get from `date.toISOString()`. On
the other hand, writers and exporters should be able to handle date strings and
JS Date objects. Note that Excel disregards the timezone modifier and treats all
JS Date objects. Note that Excel disregards timezone modifiers and treats all
dates in the local timezone. js-xlsx does not correct for this error.
Type `s` is the String type. `v` should be explicitly stored as a string to
@ -395,7 +437,7 @@ Special worksheet keys (accessible as `worksheet[key]`, each starting with `!`):
Functions that handle worksheets should test for the presence of `!ref` field.
If the `!ref` is omitted or is not a valid range, functions are free to treat
the sheet as empty or attempt to guess the range. The standard utilities that
ship with this library treat sheets as empty (for example, the CSV output is an
ship with this library treat sheets as empty (for example, the CSV output is
empty string).
When reading a worksheet with the `sheetRows` property set, the ref parameter
@ -468,9 +510,10 @@ The exported `write` and `writeFile` functions accept an options argument:
| Option Name | Default | Description |
| :---------- | ------: | :--------------------------------------------------- |
| cellDates | false | Store dates as type `d` (default is `n`) |
| bookSST | false | Generate Shared String Table ** |
| cellDates | `false` | Store dates as type `d` (default is `n`) |
| bookSST | `false` | Generate Shared String Table ** |
| bookType | 'xlsx' | Type of Workbook ("xlsx" or "xlsm" or "xlsb") |
| compression | `false` | Use file compression for formats with ZIP containers |
- `bookSST` is slower and more memory intensive, but has better compatibility
with older versions of iOS Numbers
@ -483,7 +526,7 @@ The exported `write` and `writeFile` functions accept an options argument:
## Tested Environments
- NodeJS 0.8, 0.10 (latest release), 0.11.14 (unstable), io.js
- NodeJS 0.8, 0.9, 0.10, 0.11, 0.12, 4.x, 5.x, 6.x, 7.x
- IE 6/7/8/9/10/11 using Base64 mode (IE10/11 using HTML5 mode)
- FF 18 using Base64 or HTML5 mode
- Chrome 24 using Base64 or HTML5 mode
@ -491,8 +534,8 @@ The exported `write` and `writeFile` functions accept an options argument:
Tests utilize the mocha testing framework. Travis-CI and Sauce Labs links:
- <https://travis-ci.org/SheetJS/js-xlsx> for XLSX module in nodejs
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS* modules
- <https://saucelabs.com/u/sheetjs> for XLS* modules using Sauce Labs
- <https://travis-ci.org/SheetJS/SheetJS.github.io> for XLS\* modules
- <https://saucelabs.com/u/sheetjs> for XLS\* modules using Sauce Labs
## Test Files
@ -537,7 +580,7 @@ version release and should not be committed between versions.
## License
Please consult the attached LICENSE file for details. All rights not explicitly
granted by the Apache 2.0 license are reserved by the Original Author.
granted by the Apache 2.0 License are reserved by the Original Author.
It is the opinion of the Original Author that this code conforms to the terms of
the Microsoft Open Specifications Promise, falling under the same terms as
@ -573,10 +616,16 @@ Open Document Format for Office Applications Version 1.2 (29 September 2011)
## Badges
[![Build Status](https://saucelabs.com/browser-matrix/xlsx.svg)](https://saucelabs.com/u/xlsx)
[![Build Status](https://travis-ci.org/SheetJS/js-xlsx.svg?branch=master)](https://travis-ci.org/SheetJS/js-xlsx)
[![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-xlsx/master.svg)](https://coveralls.io/r/SheetJS/js-xlsx?branch=master)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
[![NPM Downloads](https://img.shields.io/npm/dt/xlsx.svg)](https://npmjs.org/package/xlsx)
[![Dependencies Status](https://david-dm.org/sheetjs/js-xlsx/status.svg)](https://david-dm.org/sheetjs/js-xlsx)
[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

@ -48,7 +48,7 @@ if(process.version === 'v0.10.31') {
process.exit(1);
}
var filename, sheetname = '';
var filename/*:?string*/, sheetname = '';
if(program.args[0]) {
filename = program.args[0];
if(program.args[1]) sheetname = program.args[1];
@ -60,13 +60,13 @@ if(!filename) {
console.error(n + ": must specify a filename");
process.exit(1);
}
/*:: if(filename) { */
if(!fs.existsSync(filename)) {
console.error(n + ": " + filename + ": No such file or directory");
process.exit(2);
}
var opts = {}, wb;
var opts = {}, wb/*:?Workbook*/;
if(program.listSheets) opts.bookSheets = true;
if(program.sheetRows) opts.sheetRows = program.sheetRows;
if(program.password) opts.password = program.password;
@ -98,6 +98,7 @@ else try {
}
if(program.read) process.exit(0);
/*:: if(wb) { */
if(program.listSheets) {
console.log(wb.SheetNames.join("\n"));
process.exit(0);
@ -105,9 +106,9 @@ if(program.listSheets) {
var wopts = {WTF:opts.WTF, bookSST:program.sst};
if(program.xlsx) return X.writeFile(wb, sheetname || (filename + ".xlsx"), wopts);
if(program.xlsm) return X.writeFile(wb, sheetname || (filename + ".xlsm"), wopts);
if(program.xlsb) return X.writeFile(wb, sheetname || (filename + ".xlsb"), wopts);
if(program.xlsx) { X.writeFile(wb, sheetname || (filename + ".xlsx"), wopts); process.exit(0); }
if(program.xlsm) { X.writeFile(wb, sheetname || (filename + ".xlsm"), wopts); process.exit(0); }
if(program.xlsb) { X.writeFile(wb, sheetname || (filename + ".xlsb"), wopts); process.exit(0); }
var target_sheet = sheetname || '';
if(target_sheet === '') target_sheet = wb.SheetNames[0];
@ -121,7 +122,7 @@ try {
process.exit(4);
}
if(program.perf) return;
if(program.perf) process.exit(0);
var oo = "";
if(!program.quiet) console.error(target_sheet);
@ -132,3 +133,5 @@ else oo = X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep});
if(program.output) fs.writeFileSync(program.output, oo);
else console.log(oo);
/*:: } */
/*:: } */

@ -1 +1 @@
XLSX.version = '0.8.1';
XLSX.version = '0.8.2';

8
bits/09_types.js Normal file

@ -0,0 +1,8 @@
/*::
declare type Block = any;
declare type BufArray = {
next(sz:number):Block;
end():any;
push(buf:Block):void;
};
*/

@ -1,4 +1,4 @@
function isval(x) { return x !== undefined && x !== null; }
function isval(x/*:?any*/)/*:boolean*/ { return x !== undefined && x !== null; }
function keys(o) { return Object.keys(o); }
@ -36,7 +36,7 @@ function datenum(v, date1904) {
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
}
function cc2str(arr) {
function cc2str(arr/*:Array<number>*/)/*:string*/ {
var o = "";
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
return o;

@ -26,12 +26,13 @@ function getzipfile(zip, file) {
return o;
}
function getzipdata(zip, file, safe) {
function getzipdata(zip, file, safe/*:?boolean*/) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
/*:: declare var JSZip:any; */
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {

@ -132,7 +132,8 @@ function WriteShift(t, val, f) {
size = 2 * val.length;
} else switch(t) {
case 1: size = 1; this[this.l] = val&255; break;
case 3: size = 3; this[this.l+2] = val & 255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l] = val&255; break;
case 2: size = 2; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; break;
case 3: size = 3; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l+2] = val&255; break;
case 4: size = 4; this.writeUInt32LE(val, this.l); break;
case 8: size = 8; if(f === 'f') { this.writeDoubleLE(val, this.l); break; }
/* falls through */
@ -148,16 +149,16 @@ function CheckField(hexstr, fld) {
this.l += hexstr.length>>1;
}
function prep_blob(blob, pos) {
function prep_blob(blob, pos/*:number*/) {
blob.l = pos;
blob.read_shift = ReadShift;
blob.chk = CheckField;
blob.write_shift = WriteShift;
}
function parsenoop(blob, length) { blob.l += length; }
function parsenoop(blob, length/*:number*/) { blob.l += length; }
function writenoop(blob, length) { blob.l += length; }
function writenoop(blob, length/*:number*/) { blob.l += length; }
function new_buf(sz) {
var o = new_raw_buf(sz);

@ -26,7 +26,7 @@ function buf_array() {
var curbuf = newblk(blksz);
var endbuf = function ba_endbuf() {
curbuf.length = curbuf.l;
if(curbuf.length > curbuf.l) curbuf = curbuf.slice(0, curbuf.l);
if(curbuf.length > 0) bufs.push(curbuf);
curbuf = null;
};
@ -47,7 +47,7 @@ function buf_array() {
return { next:next, push:push, end:end, _bufs:bufs };
}
function write_record(ba, type, payload, length) {
function write_record(ba/*:BufArray*/, type/*:string*/, payload, length/*:?number*/) {
var t = evert_RE[type], l;
if(!length) length = XLSBRecordEnum[t].p || (payload||[]).length || 0;
l = 1 + (t >= 0x80 ? 1 : 0) + 1 + length;
@ -62,5 +62,5 @@ function write_record(ba, type, payload, length) {
if(length >= 0x80) { o.write_shift(1, (length & 0x7F)+0x80); length >>= 7; }
else { o.write_shift(1, length); break; }
}
if(length > 0 && is_buf(payload)) ba.push(payload);
if(/*:: length != null &&*/length > 0 && is_buf(payload)) ba.push(payload);
}

@ -91,6 +91,15 @@ function parse_RkNumber(data) {
var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
return fX100 ? RK/100 : RK;
}
function write_RkNumber(data/*:number*/, o) {
if(o == null) o = new_buf(4);
var fX100 = 0, fInt = 0, d100 = data * 100;
if(data == (data | 0) && data >= -(1<<29) && data < (1 << 29)) { fInt = 1; }
else if(d100 == (d100 | 0) && d100 >= -(1<<29) && d100 < (1 << 29)) { fInt = 1; fX100 = 1; }
if(fInt) o.write_shift(-4, ((fX100 ? d100 : data) << 2) + (fX100 + 2));
else throw new Error("unsupported RkNumber " + data); // TODO
}
/* [MS-XLSB] 2.5.153 */
function parse_UncheckedRfX(data) {
@ -113,8 +122,9 @@ function write_UncheckedRfX(r, o) {
/* [MS-XLSB] 2.5.171 */
/* [MS-XLS] 2.5.342 */
/* TODO: error checking, NaN and Infinity values are not valid Xnum */
function parse_Xnum(data, length) { return data.read_shift(8, 'f'); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, 'f', data); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, data, 'f'); }
/* [MS-XLSB] 2.5.198.2 */
var BErr = {

@ -40,7 +40,7 @@ function parse_cust_props(data, opts) {
p[name] = unescapexml(text);
break;
default:
if(typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
if(opts.WTF && typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
}
} else if(x.substr(0,2) === "</") {
} else if(opts.WTF) throw new Error(x);

@ -24,7 +24,7 @@ function parse_ws_xml(data, opts, rels) {
var mergecells = [];
if(data.indexOf("</mergeCells>")!==-1) {
var merges = data.match(mergecregex);
for(ridx = 0; ridx != merges.length; ++ridx)
if(merges) for(ridx = 0; ridx != merges.length; ++ridx)
mergecells[ridx] = safe_decode_range(merges[ridx].substr(merges[ridx].indexOf("\"")+1));
}
@ -36,7 +36,7 @@ function parse_ws_xml(data, opts, rels) {
parse_ws_xml_cols(columns, cols);
}
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
/* 18.3.1.80 sheetData CT_SheetData ? */
var mtch=data.match(sheetdataregex);
@ -282,7 +282,7 @@ var WS_XML_ROOT = writextag('worksheet', null, {
'xmlns:r': XMLNS.r
});
function write_ws_xml(idx, opts, wb) {
function write_ws_xml(idx/*:number*/, opts, wb)/*:string*/ {
var o = [XML_HEADER, WS_XML_ROOT];
var s = wb.SheetNames[idx], sidx = 0, rdata = "";
var ws = wb.Sheets[s];

@ -6,6 +6,45 @@ function parse_BrtRowHdr(data, length) {
data.l += length-4;
return z;
}
function write_BrtRowHdr(R/*:number*/, range, ws) {
var o = new_buf(17+8*16);
o.write_shift(4, R);
/* TODO: flags styles */
o.write_shift(4, 0);
o.write_shift(2, 0x0140);
o.write_shift(2, 0);
o.write_shift(1, 0);
/* [MS-XLSB] 2.5.8 BrtColSpan explains the mechanism */
var ncolspan = 0, lcs = o.l;
o.l += 4;
var caddr = {r:R, c:0};
for(var i = 0; i < 16; ++i) {
if(range.s.c > ((i+1) << 10) || range.e.c < (i << 10)) continue;
var first = -1, last = -1;
for(var j = (i<<10); j < ((i+1)<<10); ++j) {
caddr.c = j;
if(ws[encode_cell(caddr)]) { if(first < 0) first = j; last = j; }
}
if(first < 0) continue;
++ncolspan;
o.write_shift(4, first);
o.write_shift(4, last);
}
var l = o.l;
o.l = lcs;
o.write_shift(4, ncolspan);
o.l = l;
return o.length > o.l ? o.slice(0, o.l) : o;
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
var parse_BrtWsDim = parse_UncheckedRfX;
@ -25,9 +64,9 @@ function parse_BrtCellBlank(data, length) {
var cell = parse_XLSBCell(data);
return [cell];
}
function write_BrtCellBlank(cell, val, o) {
function write_BrtCellBlank(cell, ncell, o) {
if(o == null) o = new_buf(8);
return write_XLSBCell(val, o);
return write_XLSBCell(ncell, o);
}
@ -37,12 +76,18 @@ function parse_BrtCellBool(data, length) {
var fBool = data.read_shift(1);
return [cell, fBool, 'b'];
}
function write_BrtCellBool(cell, ncell, o) {
if(o == null) o = new_buf(9);
write_XLSBCell(ncell, o);
o.write_shift(1, cell.v ? 1 : 0);
return o;
}
/* [MS-XLSB] 2.4.305 BrtCellError */
function parse_BrtCellError(data, length) {
var cell = parse_XLSBCell(data);
var fBool = data.read_shift(1);
return [cell, fBool, 'e'];
var bError = data.read_shift(1);
return [cell, bError, 'e'];
}
/* [MS-XLSB] 2.4.308 BrtCellIsst */
@ -51,6 +96,12 @@ function parse_BrtCellIsst(data, length) {
var isst = data.read_shift(4);
return [cell, isst, 's'];
}
function write_BrtCellIsst(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
o.write_shift(4, ncell.v);
return o;
}
/* [MS-XLSB] 2.4.310 BrtCellReal */
function parse_BrtCellReal(data, length) {
@ -58,6 +109,12 @@ function parse_BrtCellReal(data, length) {
var value = parse_Xnum(data);
return [cell, value, 'n'];
}
function write_BrtCellReal(cell, ncell, o) {
if(o == null) o = new_buf(16);
write_XLSBCell(ncell, o);
write_Xnum(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.311 BrtCellRk */
function parse_BrtCellRk(data, length) {
@ -65,6 +122,13 @@ function parse_BrtCellRk(data, length) {
var value = parse_RkNumber(data);
return [cell, value, 'n'];
}
function write_BrtCellRk(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
write_RkNumber(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.314 BrtCellSt */
function parse_BrtCellSt(data, length) {
@ -72,6 +136,12 @@ function parse_BrtCellSt(data, length) {
var value = parse_XLWideString(data);
return [cell, value, 'str'];
}
function write_BrtCellSt(cell, ncell, o) {
if(o == null) o = new_buf(12 + 4 * cell.v.length);
write_XLSBCell(ncell, o);
write_XLWideString(cell.v, o);
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.647 BrtFmlaBool */
function parse_BrtFmlaBool(data, length, opts) {
@ -147,12 +217,13 @@ function parse_ws_bin(data, opts, rels) {
var s = {};
var ref;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var pass = false, end = false;
var row, p, cf, R, C, addr, sstr, rr;
var mergecells = [];
recordhopper(data, function ws_parse(val, R) {
//console.log(R);
if(end) return;
switch(R.n) {
case 'BrtWsDim': ref = val; break;
@ -329,7 +400,7 @@ function parse_ws_bin(data, opts, rels) {
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
}
}, opts);
if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(!s["!ref"] && (refguess.s.r < 2000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(opts.sheetRows && s["!ref"]) {
var tmpref = safe_decode_range(s["!ref"]);
if(opts.sheetRows < +tmpref.e.r) {
@ -362,12 +433,23 @@ function write_ws_bin_cell(ba, cell, R, C, opts) {
case 's': case 'str':
if(opts.bookSST) {
vv = get_sst_id(opts.Strings, cell.v);
o.t = "s"; break;
o.t = "s"; o.v = vv;
write_record(ba, "BrtCellIsst", write_BrtCellIsst(cell, o));
} else {
o.t = "str";
write_record(ba, "BrtCellSt", write_BrtCellSt(cell, o));
}
o.t = "str"; break;
case 'n': break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
return;
case 'n':
/* TODO: determine threshold for Real vs RK */
if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) write_record(ba, "BrtCellRk", write_BrtCellRk(cell, o));
else write_record(ba, "BrtCellReal", write_BrtCellReal(cell, o));
return;
case 'b':
o.t = "b";
write_record(ba, "BrtCellBool", write_BrtCellBool(cell, o));
return;
case 'e': /* TODO: error */ o.t = "e"; break;
}
write_record(ba, "BrtCellBlank", write_BrtCellBlank(cell, o));
}
@ -379,6 +461,7 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);

@ -13,7 +13,7 @@ function write_BrtBundleSh(data, o) {
o.write_shift(4, data.iTabID);
write_RelID(data.strRelID, o);
write_XLWideString(data.name.substr(0,31), o);
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.807 BrtWbProp */
@ -121,7 +121,7 @@ function write_BrtFileVersion(data, o) {
write_XLWideString(XLSX.version, o);
write_XLWideString("7262", o);
o.length = o.l;
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.1.7.60 Workbook */
@ -144,6 +144,7 @@ function write_BrtCalcProp(data, o) {
return o;
}
/* [MS-XLSB] 2.4.640 BrtFileRecover */
function write_BrtFileRecover(data, o) {
if(!o) o = new_buf(1);
o.write_shift(1,0);
@ -156,22 +157,22 @@ function write_wb_bin(wb, opts) {
write_record(ba, "BrtBeginBook");
write_record(ba, "BrtFileVersion", write_BrtFileVersion());
/* [[BrtFileSharingIso] BrtFileSharing] */
write_record(ba, "BrtWbProp", write_BrtWbProp());
if(0) write_record(ba, "BrtWbProp", write_BrtWbProp());
/* [ACABSPATH] */
/* [[BrtBookProtectionIso] BrtBookProtection] */
write_BOOKVIEWS(ba, wb, opts);
if(0) write_BOOKVIEWS(ba, wb, opts);
write_BUNDLESHS(ba, wb, opts);
/* [FNGROUP] */
/* [EXTERNALS] */
/* *BrtName */
write_record(ba, "BrtCalcProp", write_BrtCalcProp());
if(0) write_record(ba, "BrtCalcProp", write_BrtCalcProp());
/* [BrtOleSize] */
/* *(BrtUserBookView *FRT) */
/* [PIVOTCACHEIDS] */
/* [BrtWbFactoid] */
/* [SMARTTAGTYPES] */
/* [BrtWebOpt] */
write_record(ba, "BrtFileRecover", write_BrtFileRecover());
if(0) write_record(ba, "BrtFileRecover", write_BrtFileRecover());
/* [WEBPUBITEMS] */
/* [CRERRS] */
/* FRTWORKBOOK */

@ -147,7 +147,7 @@ function parse_xlml_xml(d, opts) {
var sheets = {}, sheetnames = [], cursheet = {}, sheetname = "";
var table = {}, cell = {}, row = {}, dtag, didx;
var c = 0, r = 0;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var styles = {}, stag = {};
var ss = "", fidx = 0;
var mergecells = [];
@ -207,7 +207,7 @@ function parse_xlml_xml(d, opts) {
if(mergecells.length) cursheet["!merges"] = mergecells;
sheets[sheetname] = cursheet;
} else {
refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
r = c = 0;
state.push([Rn[3], false]);
tmp = xlml_parsexmltag(Rn[0]);

11
bits/83_ods.js Normal file

@ -0,0 +1,11 @@
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}
function write_ods(wb, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}

@ -1,6 +0,0 @@
/* Helper function to call out to ODS parser */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}

@ -36,5 +36,7 @@ var fix_write_opts = fix_opts_func([
['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
['compression', false], /* Use file compression */
['WTF', false] /* WTF mode (throws errors) */
]);

@ -10,6 +10,7 @@ function add_rels(rels, rId, f, type, relobj) {
}
function write_zip(wb, opts) {
if(opts.bookType == "ods") return write_ods(wb, opts);
if(wb && !wb.SSF) {
wb.SSF = SSF.get_table();
}

@ -1,13 +1,17 @@
function write_zip_type(wb, opts) {
var o = opts||{};
var z = write_zip(wb, o);
var oopts = {};
if(opts.compression) oopts.compression = 'DEFLATE';
switch(o.type) {
case "base64": return z.generate({type:"base64"});
case "binary": return z.generate({type:"string"});
case "buffer": return z.generate({type:"nodebuffer"});
case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"}));
case "base64": oopts.type = "base64"; break;
case "binary": oopts.type = "string"; break;
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
return z.generate(oopts);
}
function writeSync(wb, opts) {
@ -21,13 +25,14 @@ function writeSync(wb, opts) {
function writeFileSync(wb, filename, opts) {
var o = opts||{}; o.type = 'file';
o.file = filename;
switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.substr(-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 '.xls': o.bookType = 'xls'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
}}
return writeSync(wb, o);
}

@ -2,7 +2,7 @@
"name": "js-xlsx",
"homepage": "https://github.com/SheetJS/js-xlsx",
"main": "dist/xlsx.js",
"version": "0.8.1",
"version": "0.8.2",
"ignore": [
"bin",
"bits",

287
dist/ods.js vendored

@ -21,6 +21,14 @@ function cc2str(arr) {
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
return o;
}
function dup(o) {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
var out = {};
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
return out;
}
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
@ -67,7 +75,7 @@ function parsexmltag(tag, skip_root) {
for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
if(!skip_root) z[0] = tag.substr(0, eq);
if(eq === tag.length) return z;
var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
var m = tag.match(attregexg), j=0, v="", i=0, q="", cc="";
if(m) for(i = 0; i != m.length; ++i) {
cc = m[i];
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
@ -108,7 +116,7 @@ function escapexml(text){
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
function parsexmlbool(value) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
@ -147,6 +155,8 @@ function parse_isodur(s) {
}
return sec;
}
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
@ -157,14 +167,14 @@ function xlml_normalize(d) {
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
function parse_manifest(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
FEtag = parsexmltag(Rn[0], false);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
@ -172,11 +182,46 @@ var parse_manifest = function(d, opts) {
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
default: if(opts && opts.WTF) throw Rn;
}
};
}
function write_manifest(manifest, opts) {
var o = [XML_HEADER];
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
for(var i = 0; i < manifest.length; ++i) o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
o.push('</manifest:manifest>');
return o.join("");
}
/* Part 3 Section 6 Metadata Manifest File */
function write_rdf_type(file, res, tag) {
return [
' <rdf:Description rdf:about="' + file + '">\n',
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + '#' + res + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf_has(base, file) {
return [
' <rdf:Description rdf:about="' + base + '">\n',
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf(rdf, opts) {
var o = [XML_HEADER];
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
for(var i = 0; i != rdf.length; ++i) {
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
o.push(write_rdf_has("",rdf[i][0]));
}
o.push(write_rdf_type("","Document", "pkg"));
o.push('</rdf:RDF>');
return o.join("");
}
var parse_text_p = function(text, tag) {
return utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
};
var utf8read = function utf8reada(orig) {
@ -213,27 +258,29 @@ var parse_content_xml = (function() {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var ctag = {value:""};
var textp = "", textpidx = 0, textptag;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
var merges = [], mrange = {}, mR = 0, mC = 0;
var rept = 1;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 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);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
sheetag = parsexmltag(Rn[0], false);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {}; merges = [];
@ -247,17 +294,18 @@ var parse_content_xml = (function() {
++C; break; /* stub */
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0]);
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
else ++C;
}
else if(Rn[1]!=='/') {
++C;
rept = 1;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
ctag = parsexmltag(Rn[0], false);
q = {t:ctag['value-type'], v:null};
if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
mR = parseInt(ctag['number-rows-spanned'],10) || 0;
@ -265,6 +313,10 @@ var parse_content_xml = (function() {
mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
merges.push(mrange);
}
/* 19.675.2 table:number-columns-repeated */
if(ctag['number-columns-repeated']) rept = parseInt(ctag['number-columns-repeated'], 10);
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
@ -273,14 +325,22 @@ 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 'string': q.t = 's'; break;
default: throw new Error('Unsupported value type ' + q.t);
default:
if(q.t === 'string' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 's') q.v = textp;
if(q.t === 's') q.v = textp || '';
if(textp) q.w = textp;
if(!(opts.sheetRows && opts.sheetRows < R)) ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
if(!(opts.sheetRows && opts.sheetRows < R)) {
ws[get_utils().encode_cell({r:R,c:C})] = q;
while(--rept > 0) ws[get_utils().encode_cell({r:R,c:++C})] = dup(q);
if(range.e.c <= C) range.e.c = C;
}
q = {};
textp = "";
}
break; // 9.1.4 <table:table-cell>
@ -296,6 +356,13 @@ var parse_content_xml = (function() {
/* ignore state */
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
case 'image': // 10.4.4 <draw:image>
case 'data-pilot-tables': // 9.6.2 <table:data-pilot-tables>
case 'list-style': // 16.30 <text:list-style>
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
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]);
break;
@ -309,7 +376,7 @@ var parse_content_xml = (function() {
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
NFtag = parsexmltag(Rn[0], false);
state.push([Rn[3], true]);
} break;
@ -317,6 +384,7 @@ var parse_content_xml = (function() {
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
@ -329,10 +397,12 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'fraction': break; // TODO 16.27.6 <number:fraction>
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
@ -347,7 +417,7 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
@ -373,30 +443,114 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'named-range': break; // 9.4.12 <table:named-range>
case 'named-expression': break; // 9.4.13 <table:named-expression>
case 'sort': break; // 9.4.19 <table:sort>
case 'sort-by': break; // 9.4.20 <table:sort-by>
case 'sort-groups': break; // 9.4.22 <table:sort-groups>
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
case 'iteration': break; // 9.4.3 <table:iteration>
case 'content-validations': break; // 9.4.4 <table:
case 'content-validation': break; // 9.4.5 <table:
case 'error-message': break; // 9.4.7 <table:
case 'database-ranges': break; // 9.4.14 <table:database-ranges>
case 'database-range': break; // 9.4.15 <table:database-range>
case 'filter': break; // 9.5.2 <table:filter>
case 'filter-and': break; // 9.5.3 <table:filter-and>
case 'filter-or': break; // 9.5.4 <table:filter-or>
case 'filter-condition': break; // 9.5.5 <table:filter-condition>
default: if(opts.WTF) throw Rn;
case 'list-level-style-bullet': break; // 16.31 <text:
case 'list-level-style-number': break; // 16.32 <text:
case 'list-level-properties': break; // 17.19 <style:
/* 7.3 Document Fields */
case 'sender-firstname': // 7.3.6.2
case 'sender-lastname': // 7.3.6.3
case 'sender-initials': // 7.3.6.4
case 'sender-title': // 7.3.6.5
case 'sender-position': // 7.3.6.6
case 'sender-email': // 7.3.6.7
case 'sender-phone-private': // 7.3.6.8
case 'sender-fax': // 7.3.6.9
case 'sender-company': // 7.3.6.10
case 'sender-phone-work': // 7.3.6.11
case 'sender-street': // 7.3.6.12
case 'sender-city': // 7.3.6.13
case 'sender-postal-code': // 7.3.6.14
case 'sender-country': // 7.3.6.15
case 'sender-state-or-province': // 7.3.6.16
case 'author-name': // 7.3.7.1
case 'author-initials': // 7.3.7.2
case 'chapter': // 7.3.8
case 'file-name': // 7.3.9
case 'template-name': // 7.3.9
case 'sheet-name': // 7.3.9
break;
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
case 'source-service': // 9.6.6
case 'data-pilot-field': // 9.6.7
case 'data-pilot-level': // 9.6.8
case 'data-pilot-subtotals': // 9.6.9
case 'data-pilot-subtotal': // 9.6.10
case 'data-pilot-members': // 9.6.11
case 'data-pilot-member': // 9.6.12
case 'data-pilot-display-info': // 9.6.13
case 'data-pilot-sort-info': // 9.6.14
case 'data-pilot-layout-info': // 9.6.15
case 'data-pilot-field-reference': // 9.6.16
case 'data-pilot-groups': // 9.6.17
case 'data-pilot-group': // 9.6.18
case 'data-pilot-group-member': // 9.6.19
break;
/* 10.3 Drawing Shapes */
case 'rect': // 10.3.2
break;
/* 14.6 DDE Connections */
case 'dde-connection-decls': // 14.6.2 <text:
case 'dde-connection-decl': // 14.6.3 <text:
case 'dde-link': // 14.6.4 <table:
case 'dde-source': // 14.6.5 <office:
break;
case 'properties': break; // 13.7 <form:properties>
case 'property': break; // 13.8 <form:property>
case 'a': break; // 6.1.8 hyperlink
/* non-standard */
case 'table-protection': break;
case 'data-pilot-grand-total': break; // <table:
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(opts.WTF) throw Rn;
}
var out = {
Sheets: Sheets,
@ -405,10 +559,81 @@ var parse_content_xml = (function() {
return out;
};
})();
var write_content_xml = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var write_ws = function(ws, wb, i, opts) {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = get_utils().decode_range(ws['!ref']);
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var ref = get_utils().encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
}
o.push(' </table:table-row>\n');
}
o.push(' </table:table>\n');
return o.join("");
};
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
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>');
return o.join("");
};
})();
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
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);
};
}
function write_ods(wb, opts) {
var zip = new jszip();
var f = "";
var manifest = [];
var rdf = [];
/* 3:3.3 and 2:2.2.4 */
f = "mimetype";
zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
/* Part 2 Section 2.2 Documents */
f = "content.xml";
zip.file(f, write_content_xml(wb, opts));
manifest.push([f, "text/xml"]);
rdf.push([f, "ContentFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* Part 3 Section 4 Manifest File */
f = "META-INF/manifest.xml";
zip.file(f, write_manifest(manifest, opts));
return zip;
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
})(typeof exports !== 'undefined' ? exports : ODS);

2
dist/ods.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/ods.min.map vendored Normal file

File diff suppressed because one or more lines are too long

20
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

18
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

172
dist/xlsx.js vendored

@ -4,7 +4,7 @@
/*jshint funcscope:true, eqnull:true */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.8.1';
XLSX.version = '0.8.2';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -1645,7 +1645,8 @@ function WriteShift(t, val, f) {
size = 2 * val.length;
} else switch(t) {
case 1: size = 1; this[this.l] = val&255; break;
case 3: size = 3; this[this.l+2] = val & 255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l] = val&255; break;
case 2: size = 2; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; break;
case 3: size = 3; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l+2] = val&255; break;
case 4: size = 4; this.writeUInt32LE(val, this.l); break;
case 8: size = 8; if(f === 'f') { this.writeDoubleLE(val, this.l); break; }
/* falls through */
@ -1706,7 +1707,7 @@ function buf_array() {
var curbuf = newblk(blksz);
var endbuf = function ba_endbuf() {
curbuf.length = curbuf.l;
if(curbuf.length > curbuf.l) curbuf = curbuf.slice(0, curbuf.l);
if(curbuf.length > 0) bufs.push(curbuf);
curbuf = null;
};
@ -1893,6 +1894,15 @@ function parse_RkNumber(data) {
var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
return fX100 ? RK/100 : RK;
}
function write_RkNumber(data, o) {
if(o == null) o = new_buf(4);
var fX100 = 0, fInt = 0, d100 = data * 100;
if(data == (data | 0) && data >= -(1<<29) && data < (1 << 29)) { fInt = 1; }
else if(d100 == (d100 | 0) && d100 >= -(1<<29) && d100 < (1 << 29)) { fInt = 1; fX100 = 1; }
if(fInt) o.write_shift(-4, ((fX100 ? d100 : data) << 2) + (fX100 + 2));
else throw new Error("unsupported RkNumber " + data); // TODO
}
/* [MS-XLSB] 2.5.153 */
function parse_UncheckedRfX(data) {
@ -1915,8 +1925,9 @@ function write_UncheckedRfX(r, o) {
/* [MS-XLSB] 2.5.171 */
/* [MS-XLS] 2.5.342 */
/* TODO: error checking, NaN and Infinity values are not valid Xnum */
function parse_Xnum(data, length) { return data.read_shift(8, 'f'); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, 'f', data); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, data, 'f'); }
/* [MS-XLSB] 2.5.198.2 */
var BErr = {
@ -2730,7 +2741,7 @@ function parse_cust_props(data, opts) {
p[name] = unescapexml(text);
break;
default:
if(typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
if(opts.WTF && typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
}
} else if(x.substr(0,2) === "</") {
} else if(opts.WTF) throw new Error(x);
@ -7286,7 +7297,7 @@ function parse_ws_xml(data, opts, rels) {
var mergecells = [];
if(data.indexOf("</mergeCells>")!==-1) {
var merges = data.match(mergecregex);
for(ridx = 0; ridx != merges.length; ++ridx)
if(merges) for(ridx = 0; ridx != merges.length; ++ridx)
mergecells[ridx] = safe_decode_range(merges[ridx].substr(merges[ridx].indexOf("\"")+1));
}
@ -7298,7 +7309,7 @@ function parse_ws_xml(data, opts, rels) {
parse_ws_xml_cols(columns, cols);
}
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
/* 18.3.1.80 sheetData CT_SheetData ? */
var mtch=data.match(sheetdataregex);
@ -7573,6 +7584,45 @@ function parse_BrtRowHdr(data, length) {
data.l += length-4;
return z;
}
function write_BrtRowHdr(R, range, ws) {
var o = new_buf(17+8*16);
o.write_shift(4, R);
/* TODO: flags styles */
o.write_shift(4, 0);
o.write_shift(2, 0x0140);
o.write_shift(2, 0);
o.write_shift(1, 0);
/* [MS-XLSB] 2.5.8 BrtColSpan explains the mechanism */
var ncolspan = 0, lcs = o.l;
o.l += 4;
var caddr = {r:R, c:0};
for(var i = 0; i < 16; ++i) {
if(range.s.c > ((i+1) << 10) || range.e.c < (i << 10)) continue;
var first = -1, last = -1;
for(var j = (i<<10); j < ((i+1)<<10); ++j) {
caddr.c = j;
if(ws[encode_cell(caddr)]) { if(first < 0) first = j; last = j; }
}
if(first < 0) continue;
++ncolspan;
o.write_shift(4, first);
o.write_shift(4, last);
}
var l = o.l;
o.l = lcs;
o.write_shift(4, ncolspan);
o.l = l;
return o.length > o.l ? o.slice(0, o.l) : o;
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
var parse_BrtWsDim = parse_UncheckedRfX;
@ -7592,9 +7642,9 @@ function parse_BrtCellBlank(data, length) {
var cell = parse_XLSBCell(data);
return [cell];
}
function write_BrtCellBlank(cell, val, o) {
function write_BrtCellBlank(cell, ncell, o) {
if(o == null) o = new_buf(8);
return write_XLSBCell(val, o);
return write_XLSBCell(ncell, o);
}
@ -7604,12 +7654,18 @@ function parse_BrtCellBool(data, length) {
var fBool = data.read_shift(1);
return [cell, fBool, 'b'];
}
function write_BrtCellBool(cell, ncell, o) {
if(o == null) o = new_buf(9);
write_XLSBCell(ncell, o);
o.write_shift(1, cell.v ? 1 : 0);
return o;
}
/* [MS-XLSB] 2.4.305 BrtCellError */
function parse_BrtCellError(data, length) {
var cell = parse_XLSBCell(data);
var fBool = data.read_shift(1);
return [cell, fBool, 'e'];
var bError = data.read_shift(1);
return [cell, bError, 'e'];
}
/* [MS-XLSB] 2.4.308 BrtCellIsst */
@ -7618,6 +7674,12 @@ function parse_BrtCellIsst(data, length) {
var isst = data.read_shift(4);
return [cell, isst, 's'];
}
function write_BrtCellIsst(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
o.write_shift(4, ncell.v);
return o;
}
/* [MS-XLSB] 2.4.310 BrtCellReal */
function parse_BrtCellReal(data, length) {
@ -7625,6 +7687,12 @@ function parse_BrtCellReal(data, length) {
var value = parse_Xnum(data);
return [cell, value, 'n'];
}
function write_BrtCellReal(cell, ncell, o) {
if(o == null) o = new_buf(16);
write_XLSBCell(ncell, o);
write_Xnum(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.311 BrtCellRk */
function parse_BrtCellRk(data, length) {
@ -7632,6 +7700,13 @@ function parse_BrtCellRk(data, length) {
var value = parse_RkNumber(data);
return [cell, value, 'n'];
}
function write_BrtCellRk(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
write_RkNumber(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.314 BrtCellSt */
function parse_BrtCellSt(data, length) {
@ -7639,6 +7714,12 @@ function parse_BrtCellSt(data, length) {
var value = parse_XLWideString(data);
return [cell, value, 'str'];
}
function write_BrtCellSt(cell, ncell, o) {
if(o == null) o = new_buf(12 + 4 * cell.v.length);
write_XLSBCell(ncell, o);
write_XLWideString(cell.v, o);
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.647 BrtFmlaBool */
function parse_BrtFmlaBool(data, length, opts) {
@ -7714,12 +7795,13 @@ function parse_ws_bin(data, opts, rels) {
var s = {};
var ref;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var pass = false, end = false;
var row, p, cf, R, C, addr, sstr, rr;
var mergecells = [];
recordhopper(data, function ws_parse(val, R) {
//console.log(R);
if(end) return;
switch(R.n) {
case 'BrtWsDim': ref = val; break;
@ -7896,7 +7978,7 @@ function parse_ws_bin(data, opts, rels) {
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
}
}, opts);
if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(!s["!ref"] && (refguess.s.r < 2000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(opts.sheetRows && s["!ref"]) {
var tmpref = safe_decode_range(s["!ref"]);
if(opts.sheetRows < +tmpref.e.r) {
@ -7929,12 +8011,23 @@ function write_ws_bin_cell(ba, cell, R, C, opts) {
case 's': case 'str':
if(opts.bookSST) {
vv = get_sst_id(opts.Strings, cell.v);
o.t = "s"; break;
o.t = "s"; o.v = vv;
write_record(ba, "BrtCellIsst", write_BrtCellIsst(cell, o));
} else {
o.t = "str";
write_record(ba, "BrtCellSt", write_BrtCellSt(cell, o));
}
o.t = "str"; break;
case 'n': break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
return;
case 'n':
/* TODO: determine threshold for Real vs RK */
if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) write_record(ba, "BrtCellRk", write_BrtCellRk(cell, o));
else write_record(ba, "BrtCellReal", write_BrtCellReal(cell, o));
return;
case 'b':
o.t = "b";
write_record(ba, "BrtCellBool", write_BrtCellBool(cell, o));
return;
case 'e': /* TODO: error */ o.t = "e"; break;
}
write_record(ba, "BrtCellBlank", write_BrtCellBlank(cell, o));
}
@ -7946,6 +8039,7 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);
@ -8258,7 +8352,7 @@ function write_BrtBundleSh(data, o) {
o.write_shift(4, data.iTabID);
write_RelID(data.strRelID, o);
write_XLWideString(data.name.substr(0,31), o);
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.807 BrtWbProp */
@ -8366,7 +8460,7 @@ function write_BrtFileVersion(data, o) {
write_XLWideString(XLSX.version, o);
write_XLWideString("7262", o);
o.length = o.l;
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.1.7.60 Workbook */
@ -8389,6 +8483,7 @@ function write_BrtCalcProp(data, o) {
return o;
}
/* [MS-XLSB] 2.4.640 BrtFileRecover */
function write_BrtFileRecover(data, o) {
if(!o) o = new_buf(1);
o.write_shift(1,0);
@ -8401,22 +8496,22 @@ function write_wb_bin(wb, opts) {
write_record(ba, "BrtBeginBook");
write_record(ba, "BrtFileVersion", write_BrtFileVersion());
/* [[BrtFileSharingIso] BrtFileSharing] */
write_record(ba, "BrtWbProp", write_BrtWbProp());
if(0) write_record(ba, "BrtWbProp", write_BrtWbProp());
/* [ACABSPATH] */
/* [[BrtBookProtectionIso] BrtBookProtection] */
write_BOOKVIEWS(ba, wb, opts);
if(0) write_BOOKVIEWS(ba, wb, opts);
write_BUNDLESHS(ba, wb, opts);
/* [FNGROUP] */
/* [EXTERNALS] */
/* *BrtName */
write_record(ba, "BrtCalcProp", write_BrtCalcProp());
if(0) write_record(ba, "BrtCalcProp", write_BrtCalcProp());
/* [BrtOleSize] */
/* *(BrtUserBookView *FRT) */
/* [PIVOTCACHEIDS] */
/* [BrtWbFactoid] */
/* [SMARTTAGTYPES] */
/* [BrtWebOpt] */
write_record(ba, "BrtFileRecover", write_BrtFileRecover());
if(0) write_record(ba, "BrtFileRecover", write_BrtFileRecover());
/* [WEBPUBITEMS] */
/* [CRERRS] */
/* FRTWORKBOOK */
@ -8625,7 +8720,7 @@ function parse_xlml_xml(d, opts) {
var sheets = {}, sheetnames = [], cursheet = {}, sheetname = "";
var table = {}, cell = {}, row = {}, dtag, didx;
var c = 0, r = 0;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var styles = {}, stag = {};
var ss = "", fidx = 0;
var mergecells = [];
@ -8685,7 +8780,7 @@ function parse_xlml_xml(d, opts) {
if(mergecells.length) cursheet["!merges"] = mergecells;
sheets[sheetname] = cursheet;
} else {
refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
r = c = 0;
state.push([Rn[3], false]);
tmp = xlml_parsexmltag(Rn[0]);
@ -11080,12 +11175,17 @@ var XLSRecordEnum = {
};
/* Helper function to call out to ODS parser */
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}
function write_ods(wb, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -11124,6 +11224,8 @@ var fix_write_opts = fix_opts_func([
['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
['compression', false], /* Use file compression */
['WTF', false] /* WTF mode (throws errors) */
]);
function safe_parse_wbrels(wbrels, sheets) {
@ -11278,6 +11380,7 @@ function add_rels(rels, rId, f, type, relobj) {
}
function write_zip(wb, opts) {
if(opts.bookType == "ods") return write_ods(wb, opts);
if(wb && !wb.SSF) {
wb.SSF = SSF.get_table();
}
@ -11405,13 +11508,17 @@ function readFileSync(data, opts) {
function write_zip_type(wb, opts) {
var o = opts||{};
var z = write_zip(wb, o);
var oopts = {};
if(opts.compression) oopts.compression = 'DEFLATE';
switch(o.type) {
case "base64": return z.generate({type:"base64"});
case "binary": return z.generate({type:"string"});
case "buffer": return z.generate({type:"nodebuffer"});
case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"}));
case "base64": oopts.type = "base64"; break;
case "binary": oopts.type = "string"; break;
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
return z.generate(oopts);
}
function writeSync(wb, opts) {
@ -11425,13 +11532,14 @@ function writeSync(wb, opts) {
function writeFileSync(wb, filename, opts) {
var o = opts||{}; o.type = 'file';
o.file = filename;
switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.substr(-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 '.xls': o.bookType = 'xls'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
}}
return writeSync(wb, o);
}

17
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

@ -21,7 +21,7 @@
</style>
</head>
<body>
<b>JS-XLSX (XLSX/XLSB/XLSM/XLS/XML) Live Demo</b><br />
<b>JS-XLSX (XLSX/XLSB/XLSM/XLS/XML/ODS) Live Demo</b><br />
Output Format:
<select name="format">
<option value="csv" selected> CSV</option>
@ -48,7 +48,7 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<script src="jszip.js"></script>
<script src="xlsx.js"></script>
<!-- uncomment the next line here and in xlsxworker.js for ODS support -->
<script src="dist/ods.js"></script>
<script src="ods.js"></script>
<script>
var X = XLSX;
var XW = {

12
misc/flow.js Normal file

@ -0,0 +1,12 @@
/*::
type ZIPFile = any;
type ParseOpts = any;
type Workbook = {
SheetNames: Array<string>;
Sheets: any;
};
type XLSXModule = any;
*/

8
misc/flowdeps.js Normal file

@ -0,0 +1,8 @@
/*::
declare module 'exit-on-epipe' {};
declare module 'xlsx' { declare var exports:XLSXModule; };
declare module '../' { declare var exports:XLSXModule; };
declare module 'commander' { declare var exports:any; };
*/

642
ods.flow.js Normal file

@ -0,0 +1,642 @@
/* ods.js (C) 2014-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint -W041 */
var ODS = {};
(function make_ods(ODS) {
/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
/*:: declare var XLSX: any; */
var get_utils = function() {
if(typeof XLSX !== 'undefined') return XLSX.utils;
if(typeof module !== "undefined" && typeof require !== 'undefined') try {
return require('../' + 'xlsx').utils;
} catch(e) {
try { return require('./' + 'xlsx').utils; }
catch(ee) { return require('xl' + 'sx').utils; }
}
throw new Error("Cannot find XLSX utils");
};
var has_buf = (typeof Buffer !== 'undefined');
function cc2str(arr) {
var o = "";
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
return o;
}
function dup(o/*:object*/)/*:object*/ {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
var out = {};
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
return out;
}
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer().toString('binary');
if(data.asBinary) return data.asBinary();
if(data._data && data._data.getContent) return cc2str(Array.prototype.slice.call(data._data.getContent(),0));
return null;
}
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe/*:?boolean*/) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
/*:: declare var JSZip:any; */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z/*:any*/ = [];
var eq = 0, c = 0;
for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
if(!skip_root) z[0] = tag.substr(0, eq);
if(eq === tag.length) return z;
var m = tag.match(attregexg), j=0, v="", i=0, q="", cc="";
if(m) for(i = 0; i != m.length; ++i) {
cc = m[i];
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;
}
return z;
}
function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
var encodings = {
'&quot;': '"',
'&apos;': "'",
'&gt;': '>',
'&lt;': '<',
'&amp;': '&'
};
var rencoding = {
'"': '&quot;',
"'": '&apos;',
'>': '&gt;',
'<': '&lt;',
'&': '&amp;'
};
var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
function unescapexml(text){
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
}
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) + "_";});
}
function parsexmlbool(value) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
default: return false;
}
}
function datenum(v) {
var epoch = Date.parse(v);
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
}
/* ISO 8601 Duration */
function parse_isodur(s) {
var sec = 0, mt = 0, time = false;
var m = s.match(/P([0-9\.]+Y)?([0-9\.]+M)?([0-9\.]+D)?T([0-9\.]+H)?([0-9\.]+M)?([0-9\.]+S)?/);
if(!m) throw new Error("|" + s + "| is not an ISO8601 Duration");
for(var i = 1; i != m.length; ++i) {
if(!m[i]) continue;
mt = 1;
if(i > 3) time = true;
switch(m[i].substr(m[i].length-1)) {
case 'Y':
throw new Error("Unsupported ISO Duration Field: " + m[i].substr(m[i].length-1));
case 'D': mt *= 24;
/* falls through */
case 'H': mt *= 60;
/* falls through */
case 'M':
if(!time) throw new Error("Unsupported ISO Duration Field: M");
else mt *= 60;
/* falls through */
case 'S': break;
}
sec += mt * parseInt(m[i], 10);
}
return sec;
}
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf &&/*::typeof Buffer !== "undefined" && d != null &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
function parse_manifest(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0], false);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
case 'algorithm': // 4.5 <manifest:algorithm>
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: if(opts && opts.WTF) throw Rn;
}
}
function write_manifest(manifest/*:Array<Array<string> >*/, opts)/*:string*/ {
var o = [XML_HEADER];
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
for(var i = 0; i < manifest.length; ++i) o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
o.push('</manifest:manifest>');
return o.join("");
}
/* Part 3 Section 6 Metadata Manifest File */
function write_rdf_type(file/*:string*/, res/*:string*/, tag/*:?string*/) {
return [
' <rdf:Description rdf:about="' + file + '">\n',
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + '#' + res + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf_has(base/*:string*/, file/*:string*/) {
return [
' <rdf:Description rdf:about="' + base + '">\n',
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf(rdf, opts) {
var o = [XML_HEADER];
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
for(var i = 0; i != rdf.length; ++i) {
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
o.push(write_rdf_has("",rdf[i][0]));
}
o.push(write_rdf_type("","Document", "pkg"));
o.push('</rdf:RDF>');
return o.join("");
}
var parse_text_p = function(text, tag) {
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
};
var utf8read = function utf8reada(orig) {
var out = "", i = 0, c = 0, d = 0, e = 0, f = 0, w = 0;
while (i < orig.length) {
c = orig.charCodeAt(i++);
if (c < 128) { out += String.fromCharCode(c); continue; }
d = orig.charCodeAt(i++);
if (c>191 && c<224) { out += String.fromCharCode(((c & 31) << 6) | (d & 63)); continue; }
e = orig.charCodeAt(i++);
if (c < 240) { out += String.fromCharCode(((c & 15) << 12) | ((d & 63) << 6) | (e & 63)); continue; }
f = orig.charCodeAt(i++);
w = (((c & 7) << 18) | ((d & 63) << 12) | ((e & 63) << 6) | (f & 63))-65536;
out += String.fromCharCode(0xD800 + ((w>>>10)&1023));
out += String.fromCharCode(0xDC00 + (w&1023));
}
return out;
};
var parse_content_xml = (function() {
var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"],
month: ["m", "mm"],
year: ["y", "yy"],
hours: ["h", "hh"],
minutes: ["m", "mm"],
seconds: ["s", "ss"],
"am-pm": ["A/P", "AM/PM"],
"day-of-week": ["ddd", "dddd"]
};
return function pcx(d, opts) {
var str = xlml_normalize(d);
var state/*:Array<any>*/ = [], tmp;
var tag/*:: = {}*/;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag/*:: = {name:""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = {t:"", v:null, z:null, w:""}*/;
var ctag = {value:""};
var textp = "", textpidx = 0, textptag/*:: = {}*/;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
var merges = [], mrange = {}, mR = 0, mC = 0;
var rept = 1;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 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);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0], false);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {}; merges = [];
}
break;
case 'table-row': // 9.1.3 <table:table-row>
if(Rn[1] === '/') break;
++R; C = -1; break;
case 'covered-table-cell': // 9.1.5 table:covered-table-cell
++C; break; /* stub */
case 'table-cell':
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);
else ++C;
}
else if(Rn[1]!=='/') {
++C;
rept = 1;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
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:""*/};
if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
mR = parseInt(ctag['number-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-columns-spanned'],10) || 0;
mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
merges.push(mrange);
}
/* 19.675.2 table:number-columns-repeated */
if(ctag['number-columns-repeated']) rept = parseInt(ctag['number-columns-repeated'], 10);
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
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;
default:
if(q.t === 'string' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 's') q.v = textp || '';
if(textp) q.w = textp;
if(!(opts.sheetRows && opts.sheetRows < R)) {
ws[get_utils().encode_cell({r:R,c:C})] = q;
while(--rept > 0) ws[get_utils().encode_cell({r:R,c:++C})] = dup(q);
if(range.e.c <= C) range.e.c = C;
}
q = {/*:: t:"", v:null, z:null, w:""*/};
textp = "";
}
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 'scripts': // 3.12 <office:scripts>
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 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
case 'image': // 10.4.4 <draw:image>
case 'data-pilot-tables': // 9.6.2 <table:data-pilot-tables>
case 'list-style': // 16.30 <text:list-style>
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
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]);
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>
case 'time-style': // 16.27.18 <number:time-style>
if(Rn[1]==='/'){
number_format_map[NFtag.name] = NF;
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0], false);
state.push([Rn[3], true]);
} break;
case 'script': break; // 3.13 <office:script>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties>
case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
case 'number': // 16.27.3 <number:number>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'fraction': break; // TODO 16.27.6 <number:fraction>
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
case 'era': // 16.27.14 <number:era>
case 'day-of-week': // 16.27.15 <number:day-of-week>
case 'week-of-year': // 16.27.16 <number:week-of-year>
case 'quarter': // 16.27.17 <number:quarter>
case 'hours': // 16.27.19 <number:hours>
case 'minutes': // 16.27.20 <number:minutes>
case 'seconds': // 16.27.21 <number:seconds>
case 'am-pm': // 16.27.22 <number:am-pm>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'boolean-style': break; // 16.27.23 <number:boolean-style>
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;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
case 'time-style':
NF += str.slice(pidx, Rn.index);
break;
}
else pidx = Rn.index + Rn[0].length;
break;
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 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.12 <table:named-range>
case 'named-expression': break; // 9.4.13 <table:named-expression>
case 'sort': break; // 9.4.19 <table:sort>
case 'sort-by': break; // 9.4.20 <table:sort-by>
case 'sort-groups': break; // 9.4.22 <table:sort-groups>
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
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>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
case 'iteration': break; // 9.4.3 <table:iteration>
case 'content-validations': break; // 9.4.4 <table:
case 'content-validation': break; // 9.4.5 <table:
case 'error-message': break; // 9.4.7 <table:
case 'database-ranges': break; // 9.4.14 <table:database-ranges>
case 'database-range': break; // 9.4.15 <table:database-range>
case 'filter': break; // 9.5.2 <table:filter>
case 'filter-and': break; // 9.5.3 <table:filter-and>
case 'filter-or': break; // 9.5.4 <table:filter-or>
case 'filter-condition': break; // 9.5.5 <table:filter-condition>
case 'list-level-style-bullet': break; // 16.31 <text:
case 'list-level-style-number': break; // 16.32 <text:
case 'list-level-properties': break; // 17.19 <style:
/* 7.3 Document Fields */
case 'sender-firstname': // 7.3.6.2
case 'sender-lastname': // 7.3.6.3
case 'sender-initials': // 7.3.6.4
case 'sender-title': // 7.3.6.5
case 'sender-position': // 7.3.6.6
case 'sender-email': // 7.3.6.7
case 'sender-phone-private': // 7.3.6.8
case 'sender-fax': // 7.3.6.9
case 'sender-company': // 7.3.6.10
case 'sender-phone-work': // 7.3.6.11
case 'sender-street': // 7.3.6.12
case 'sender-city': // 7.3.6.13
case 'sender-postal-code': // 7.3.6.14
case 'sender-country': // 7.3.6.15
case 'sender-state-or-province': // 7.3.6.16
case 'author-name': // 7.3.7.1
case 'author-initials': // 7.3.7.2
case 'chapter': // 7.3.8
case 'file-name': // 7.3.9
case 'template-name': // 7.3.9
case 'sheet-name': // 7.3.9
break;
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
case 'source-service': // 9.6.6
case 'data-pilot-field': // 9.6.7
case 'data-pilot-level': // 9.6.8
case 'data-pilot-subtotals': // 9.6.9
case 'data-pilot-subtotal': // 9.6.10
case 'data-pilot-members': // 9.6.11
case 'data-pilot-member': // 9.6.12
case 'data-pilot-display-info': // 9.6.13
case 'data-pilot-sort-info': // 9.6.14
case 'data-pilot-layout-info': // 9.6.15
case 'data-pilot-field-reference': // 9.6.16
case 'data-pilot-groups': // 9.6.17
case 'data-pilot-group': // 9.6.18
case 'data-pilot-group-member': // 9.6.19
break;
/* 10.3 Drawing Shapes */
case 'rect': // 10.3.2
break;
/* 14.6 DDE Connections */
case 'dde-connection-decls': // 14.6.2 <text:
case 'dde-connection-decl': // 14.6.3 <text:
case 'dde-link': // 14.6.4 <table:
case 'dde-source': // 14.6.5 <office:
break;
case 'properties': break; // 13.7 <form:properties>
case 'property': break; // 13.8 <form:property>
case 'a': break; // 6.1.8 hyperlink
/* non-standard */
case 'table-protection': break;
case 'data-pilot-grand-total': break; // <table:
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(opts.WTF) throw Rn;
}
var out = {
Sheets: Sheets,
SheetNames: SheetNames
};
return out;
};
})();
var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = get_utils().decode_range(ws['!ref']);
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var ref = get_utils().encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
}
o.push(' </table:table-row>\n');
}
o.push(' </table:table>\n');
return o.join("");
};
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
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>');
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);
}
function write_ods(wb/*:any*/, opts/*:any*/) {
/*:: if(!jszip) throw new Error("JSZip is not available"); */
var zip = new jszip();
var f = "";
var manifest/*:Array<Array<string> >*/ = [];
var rdf = [];
/* 3:3.3 and 2:2.2.4 */
f = "mimetype";
zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
/* Part 2 Section 2.2 Documents */
f = "content.xml";
zip.file(f, write_content_xml(wb, opts));
manifest.push([f, "text/xml"]);
rdf.push([f, "ContentFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* Part 3 Section 4 Manifest File */
f = "META-INF/manifest.xml";
zip.file(f, write_manifest(manifest, opts));
return zip;
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
})(typeof exports !== 'undefined' ? exports : ODS);

287
ods.js

@ -21,6 +21,14 @@ function cc2str(arr) {
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
return o;
}
function dup(o) {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
var out = {};
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
return out;
}
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
@ -67,7 +75,7 @@ function parsexmltag(tag, skip_root) {
for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
if(!skip_root) z[0] = tag.substr(0, eq);
if(eq === tag.length) return z;
var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
var m = tag.match(attregexg), j=0, v="", i=0, q="", cc="";
if(m) for(i = 0; i != m.length; ++i) {
cc = m[i];
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
@ -108,7 +116,7 @@ function escapexml(text){
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
function parsexmlbool(value) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
@ -147,6 +155,8 @@ function parse_isodur(s) {
}
return sec;
}
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
@ -157,14 +167,14 @@ function xlml_normalize(d) {
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
function parse_manifest(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
FEtag = parsexmltag(Rn[0], false);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
@ -172,11 +182,46 @@ var parse_manifest = function(d, opts) {
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
default: if(opts && opts.WTF) throw Rn;
}
};
}
function write_manifest(manifest, opts) {
var o = [XML_HEADER];
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
for(var i = 0; i < manifest.length; ++i) o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
o.push('</manifest:manifest>');
return o.join("");
}
/* Part 3 Section 6 Metadata Manifest File */
function write_rdf_type(file, res, tag) {
return [
' <rdf:Description rdf:about="' + file + '">\n',
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + '#' + res + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf_has(base, file) {
return [
' <rdf:Description rdf:about="' + base + '">\n',
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf(rdf, opts) {
var o = [XML_HEADER];
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
for(var i = 0; i != rdf.length; ++i) {
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
o.push(write_rdf_has("",rdf[i][0]));
}
o.push(write_rdf_type("","Document", "pkg"));
o.push('</rdf:RDF>');
return o.join("");
}
var parse_text_p = function(text, tag) {
return utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,""));
return unescapexml(utf8read(text.replace(/<text:s\/>/g," ").replace(/<[^>]*>/g,"")));
};
var utf8read = function utf8reada(orig) {
@ -213,27 +258,29 @@ var parse_content_xml = (function() {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var ctag = {value:""};
var textp = "", textpidx = 0, textptag;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
var merges = [], mrange = {}, mR = 0, mC = 0;
var rept = 1;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 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);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
sheetag = parsexmltag(Rn[0], false);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {}; merges = [];
@ -247,17 +294,18 @@ var parse_content_xml = (function() {
++C; break; /* stub */
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0]);
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
else ++C;
}
else if(Rn[1]!=='/') {
++C;
rept = 1;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
ctag = parsexmltag(Rn[0], false);
q = {t:ctag['value-type'], v:null};
if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
mR = parseInt(ctag['number-rows-spanned'],10) || 0;
@ -265,6 +313,10 @@ var parse_content_xml = (function() {
mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
merges.push(mrange);
}
/* 19.675.2 table:number-columns-repeated */
if(ctag['number-columns-repeated']) rept = parseInt(ctag['number-columns-repeated'], 10);
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
@ -273,14 +325,22 @@ 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 'string': q.t = 's'; break;
default: throw new Error('Unsupported value type ' + q.t);
default:
if(q.t === 'string' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 's') q.v = textp;
if(q.t === 's') q.v = textp || '';
if(textp) q.w = textp;
if(!(opts.sheetRows && opts.sheetRows < R)) ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
if(!(opts.sheetRows && opts.sheetRows < R)) {
ws[get_utils().encode_cell({r:R,c:C})] = q;
while(--rept > 0) ws[get_utils().encode_cell({r:R,c:++C})] = dup(q);
if(range.e.c <= C) range.e.c = C;
}
q = {};
textp = "";
}
break; // 9.1.4 <table:table-cell>
@ -296,6 +356,13 @@ var parse_content_xml = (function() {
/* ignore state */
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
case 'image': // 10.4.4 <draw:image>
case 'data-pilot-tables': // 9.6.2 <table:data-pilot-tables>
case 'list-style': // 16.30 <text:list-style>
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
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]);
break;
@ -309,7 +376,7 @@ var parse_content_xml = (function() {
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
NFtag = parsexmltag(Rn[0], false);
state.push([Rn[3], true]);
} break;
@ -317,6 +384,7 @@ var parse_content_xml = (function() {
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
@ -329,10 +397,12 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'fraction': break; // TODO 16.27.6 <number:fraction>
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
@ -347,7 +417,7 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
@ -373,30 +443,114 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'named-range': break; // 9.4.12 <table:named-range>
case 'named-expression': break; // 9.4.13 <table:named-expression>
case 'sort': break; // 9.4.19 <table:sort>
case 'sort-by': break; // 9.4.20 <table:sort-by>
case 'sort-groups': break; // 9.4.22 <table:sort-groups>
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
case 'iteration': break; // 9.4.3 <table:iteration>
case 'content-validations': break; // 9.4.4 <table:
case 'content-validation': break; // 9.4.5 <table:
case 'error-message': break; // 9.4.7 <table:
case 'database-ranges': break; // 9.4.14 <table:database-ranges>
case 'database-range': break; // 9.4.15 <table:database-range>
case 'filter': break; // 9.5.2 <table:filter>
case 'filter-and': break; // 9.5.3 <table:filter-and>
case 'filter-or': break; // 9.5.4 <table:filter-or>
case 'filter-condition': break; // 9.5.5 <table:filter-condition>
default: if(opts.WTF) throw Rn;
case 'list-level-style-bullet': break; // 16.31 <text:
case 'list-level-style-number': break; // 16.32 <text:
case 'list-level-properties': break; // 17.19 <style:
/* 7.3 Document Fields */
case 'sender-firstname': // 7.3.6.2
case 'sender-lastname': // 7.3.6.3
case 'sender-initials': // 7.3.6.4
case 'sender-title': // 7.3.6.5
case 'sender-position': // 7.3.6.6
case 'sender-email': // 7.3.6.7
case 'sender-phone-private': // 7.3.6.8
case 'sender-fax': // 7.3.6.9
case 'sender-company': // 7.3.6.10
case 'sender-phone-work': // 7.3.6.11
case 'sender-street': // 7.3.6.12
case 'sender-city': // 7.3.6.13
case 'sender-postal-code': // 7.3.6.14
case 'sender-country': // 7.3.6.15
case 'sender-state-or-province': // 7.3.6.16
case 'author-name': // 7.3.7.1
case 'author-initials': // 7.3.7.2
case 'chapter': // 7.3.8
case 'file-name': // 7.3.9
case 'template-name': // 7.3.9
case 'sheet-name': // 7.3.9
break;
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
case 'source-service': // 9.6.6
case 'data-pilot-field': // 9.6.7
case 'data-pilot-level': // 9.6.8
case 'data-pilot-subtotals': // 9.6.9
case 'data-pilot-subtotal': // 9.6.10
case 'data-pilot-members': // 9.6.11
case 'data-pilot-member': // 9.6.12
case 'data-pilot-display-info': // 9.6.13
case 'data-pilot-sort-info': // 9.6.14
case 'data-pilot-layout-info': // 9.6.15
case 'data-pilot-field-reference': // 9.6.16
case 'data-pilot-groups': // 9.6.17
case 'data-pilot-group': // 9.6.18
case 'data-pilot-group-member': // 9.6.19
break;
/* 10.3 Drawing Shapes */
case 'rect': // 10.3.2
break;
/* 14.6 DDE Connections */
case 'dde-connection-decls': // 14.6.2 <text:
case 'dde-connection-decl': // 14.6.3 <text:
case 'dde-link': // 14.6.4 <table:
case 'dde-source': // 14.6.5 <office:
break;
case 'properties': break; // 13.7 <form:properties>
case 'property': break; // 13.8 <form:property>
case 'a': break; // 6.1.8 hyperlink
/* non-standard */
case 'table-protection': break;
case 'data-pilot-grand-total': break; // <table:
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(opts.WTF) throw Rn;
}
var out = {
Sheets: Sheets,
@ -405,10 +559,81 @@ var parse_content_xml = (function() {
return out;
};
})();
var write_content_xml = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var write_ws = function(ws, wb, i, opts) {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = get_utils().decode_range(ws['!ref']);
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var ref = get_utils().encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
}
o.push(' </table:table-row>\n');
}
o.push(' </table:table>\n');
return o.join("");
};
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
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>');
return o.join("");
};
})();
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
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);
};
}
function write_ods(wb, opts) {
var zip = new jszip();
var f = "";
var manifest = [];
var rdf = [];
/* 3:3.3 and 2:2.2.4 */
f = "mimetype";
zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
/* Part 2 Section 2.2 Documents */
f = "content.xml";
zip.file(f, write_content_xml(wb, opts));
manifest.push([f, "text/xml"]);
rdf.push([f, "ContentFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* Part 3 Section 4 Manifest File */
f = "META-INF/manifest.xml";
zip.file(f, write_manifest(manifest, opts));
return zip;
}
ODS.parse_ods = parse_ods;
ODS.write_ods = write_ods;
})(typeof exports !== 'undefined' ? exports : ODS);

1
odsbits/.npmignore Normal file

@ -0,0 +1 @@
*.js

@ -1,3 +1,4 @@
/*:: declare var XLSX: any; */
var get_utils = function() {
if(typeof XLSX !== 'undefined') return XLSX.utils;
if(typeof module !== "undefined" && typeof require !== 'undefined') try {

@ -5,3 +5,11 @@ function cc2str(arr) {
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
return o;
}
function dup(o/*:object*/)/*:object*/ {
if(typeof JSON != 'undefined') return JSON.parse(JSON.stringify(o));
if(typeof o != 'object' || !o) return o;
var out = {};
for(var k in o) if(o.hasOwnProperty(k)) out[k] = dup(o[k]);
return out;
}

@ -20,13 +20,14 @@ function getzipfile(zip, file) {
return o;
}
function getzipdata(zip, file, safe) {
function getzipdata(zip, file, safe/*:?boolean*/) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
/*:: declare var JSZip:any; */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {

@ -2,12 +2,12 @@ var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z = [];
var z/*:any*/ = [];
var eq = 0, c = 0;
for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
if(!skip_root) z[0] = tag.substr(0, eq);
if(eq === tag.length) return z;
var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
var m = tag.match(attregexg), j=0, v="", i=0, q="", cc="";
if(m) for(i = 0; i != m.length; ++i) {
cc = m[i];
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
@ -48,7 +48,7 @@ function escapexml(text){
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
function parsexmlbool(value) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
@ -87,3 +87,5 @@ function parse_isodur(s) {
}
return sec;
}
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';

@ -1,6 +1,6 @@
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(has_buf &&/*::typeof Buffer !== "undefined" && d != null &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw "badf";
}

@ -1,13 +1,13 @@
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
function parse_manifest(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
FEtag = parsexmltag(Rn[0], false);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
@ -15,6 +15,15 @@ var parse_manifest = function(d, opts) {
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
default: if(opts && opts.WTF) throw Rn;
}
};
}
function write_manifest(manifest/*:Array<Array<string> >*/, opts)/*:string*/ {
var o = [XML_HEADER];
o.push('<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">\n');
o.push(' <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>\n');
for(var i = 0; i < manifest.length; ++i) o.push(' <manifest:file-entry manifest:full-path="' + manifest[i][0] + '" manifest:media-type="' + manifest[i][1] + '"/>\n');
o.push('</manifest:manifest>');
return o.join("");
}

26
odsbits/35_rdf.js Normal file

@ -0,0 +1,26 @@
/* Part 3 Section 6 Metadata Manifest File */
function write_rdf_type(file/*:string*/, res/*:string*/, tag/*:?string*/) {
return [
' <rdf:Description rdf:about="' + file + '">\n',
' <rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/' + (tag || "odf") + '#' + res + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf_has(base/*:string*/, file/*:string*/) {
return [
' <rdf:Description rdf:about="' + base + '">\n',
' <ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="' + file + '"/>\n',
' </rdf:Description>\n'
].join("");
}
function write_rdf(rdf, opts) {
var o = [XML_HEADER];
o.push('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n');
for(var i = 0; i != rdf.length; ++i) {
o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
o.push(write_rdf_has("",rdf[i][0]));
}
o.push(write_rdf_type("","Document", "pkg"));
o.push('</rdf:RDF>');
return o.join("");
}

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

@ -14,29 +14,31 @@ var parse_content_xml = (function() {
return function pcx(d, opts) {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var state/*:Array<any>*/ = [], tmp;
var tag/*:: = {}*/;
var NFtag = {name:""}, NF = "", pidx = 0;
var sheetag/*:: = {name:""}*/;
var Sheets = {}, SheetNames/*:Array<string>*/ = [], ws = {};
var Rn, q/*:: = {t:"", v:null, z:null, w:""}*/;
var ctag = {value:""};
var textp = "", textpidx = 0, textptag/*:: = {}*/;
var R = -1, C = -1, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
var merges = [], mrange = {}, mR = 0, mC = 0;
var rept = 1;
xlmlregex.lastIndex = 0;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 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);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
sheetag = parsexmltag(Rn[0], false);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {}; merges = [];
@ -50,24 +52,29 @@ var parse_content_xml = (function() {
++C; break; /* stub */
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') {
ctag = parsexmltag(Rn[0]);
ctag = parsexmltag(Rn[0], false);
if(ctag['number-columns-repeated']) C+= parseInt(ctag['number-columns-repeated'], 10);
else ++C;
}
else if(Rn[1]!=='/') {
++C;
rept = 1;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
q = {t:ctag['value-type'], v:null};
ctag = parsexmltag(Rn[0], false);
q = {t:ctag['value-type'], v:null/*:: , z:null, w:""*/};
if(ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
mR = parseInt(ctag['number-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-columns-spanned'],10) || 0;
mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
merges.push(mrange);
}
/* 19.675.2 table:number-columns-repeated */
if(ctag['number-columns-repeated']) rept = parseInt(ctag['number-columns-repeated'], 10);
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
@ -76,14 +83,22 @@ 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 'string': q.t = 's'; break;
default: throw new Error('Unsupported value type ' + q.t);
default:
if(q.t === 'string' || !q.t) {
q.t = 's';
if(ctag['string-value'] != null) textp = ctag['string-value'];
} else throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 's') q.v = textp;
if(q.t === 's') q.v = textp || '';
if(textp) q.w = textp;
if(!(opts.sheetRows && opts.sheetRows < R)) ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
if(!(opts.sheetRows && opts.sheetRows < R)) {
ws[get_utils().encode_cell({r:R,c:C})] = q;
while(--rept > 0) ws[get_utils().encode_cell({r:R,c:++C})] = dup(q);
if(range.e.c <= C) range.e.c = C;
}
q = {/*:: t:"", v:null, z:null, w:""*/};
textp = "";
}
break; // 9.1.4 <table:table-cell>
@ -99,6 +114,13 @@ var parse_content_xml = (function() {
/* ignore state */
case 'shapes': // 9.2.8 <table:shapes>
case 'frame': // 10.4.2 <draw:frame>
case 'text-box': // 10.4.3 <draw:text-box>
case 'image': // 10.4.4 <draw:image>
case 'data-pilot-tables': // 9.6.2 <table:data-pilot-tables>
case 'list-style': // 16.30 <text:list-style>
case 'form': // 13.13 <form:form>
case 'dde-links': // 9.8 <table:dde-links>
case 'annotation': // 14.1 <office:annotation>
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]);
break;
@ -112,7 +134,7 @@ var parse_content_xml = (function() {
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
NFtag = parsexmltag(Rn[0], false);
state.push([Rn[3], true]);
} break;
@ -120,6 +142,7 @@ var parse_content_xml = (function() {
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'map': break; // 16.3 <style:map>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
@ -132,10 +155,12 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'fraction': break; // TODO 16.27.6 <number:fraction>
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
@ -150,7 +175,7 @@ var parse_content_xml = (function() {
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
tag = parsexmltag(Rn[0], false);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
@ -176,30 +201,114 @@ var parse_content_xml = (function() {
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'named-range': break; // 9.4.12 <table:named-range>
case 'named-expression': break; // 9.4.13 <table:named-expression>
case 'sort': break; // 9.4.19 <table:sort>
case 'sort-by': break; // 9.4.20 <table:sort-by>
case 'sort-groups': break; // 9.4.22 <table:sort-groups>
case 'span': break; // <text:span>
case 'line-break': break; // 6.1.5 <text:line-break>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
else { textptag = parsexmltag(Rn[0], false); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
case 'object': break; // 10.4.6.2 <draw:object>
case 'title': break; // <*:title>
case 'desc': break; // <*:desc>
case 'table-source': break; // 9.2.6
case 'iteration': break; // 9.4.3 <table:iteration>
case 'content-validations': break; // 9.4.4 <table:
case 'content-validation': break; // 9.4.5 <table:
case 'error-message': break; // 9.4.7 <table:
case 'database-ranges': break; // 9.4.14 <table:database-ranges>
case 'database-range': break; // 9.4.15 <table:database-range>
case 'filter': break; // 9.5.2 <table:filter>
case 'filter-and': break; // 9.5.3 <table:filter-and>
case 'filter-or': break; // 9.5.4 <table:filter-or>
case 'filter-condition': break; // 9.5.5 <table:filter-condition>
default: if(opts.WTF) throw Rn;
case 'list-level-style-bullet': break; // 16.31 <text:
case 'list-level-style-number': break; // 16.32 <text:
case 'list-level-properties': break; // 17.19 <style:
/* 7.3 Document Fields */
case 'sender-firstname': // 7.3.6.2
case 'sender-lastname': // 7.3.6.3
case 'sender-initials': // 7.3.6.4
case 'sender-title': // 7.3.6.5
case 'sender-position': // 7.3.6.6
case 'sender-email': // 7.3.6.7
case 'sender-phone-private': // 7.3.6.8
case 'sender-fax': // 7.3.6.9
case 'sender-company': // 7.3.6.10
case 'sender-phone-work': // 7.3.6.11
case 'sender-street': // 7.3.6.12
case 'sender-city': // 7.3.6.13
case 'sender-postal-code': // 7.3.6.14
case 'sender-country': // 7.3.6.15
case 'sender-state-or-province': // 7.3.6.16
case 'author-name': // 7.3.7.1
case 'author-initials': // 7.3.7.2
case 'chapter': // 7.3.8
case 'file-name': // 7.3.9
case 'template-name': // 7.3.9
case 'sheet-name': // 7.3.9
break;
/* 9.6 Data Pilot Tables <table: */
case 'data-pilot-table': // 9.6.3
case 'source-cell-range': // 9.6.5
case 'source-service': // 9.6.6
case 'data-pilot-field': // 9.6.7
case 'data-pilot-level': // 9.6.8
case 'data-pilot-subtotals': // 9.6.9
case 'data-pilot-subtotal': // 9.6.10
case 'data-pilot-members': // 9.6.11
case 'data-pilot-member': // 9.6.12
case 'data-pilot-display-info': // 9.6.13
case 'data-pilot-sort-info': // 9.6.14
case 'data-pilot-layout-info': // 9.6.15
case 'data-pilot-field-reference': // 9.6.16
case 'data-pilot-groups': // 9.6.17
case 'data-pilot-group': // 9.6.18
case 'data-pilot-group-member': // 9.6.19
break;
/* 10.3 Drawing Shapes */
case 'rect': // 10.3.2
break;
/* 14.6 DDE Connections */
case 'dde-connection-decls': // 14.6.2 <text:
case 'dde-connection-decl': // 14.6.3 <text:
case 'dde-link': // 14.6.4 <table:
case 'dde-source': // 14.6.5 <office:
break;
case 'properties': break; // 13.7 <form:properties>
case 'property': break; // 13.8 <form:property>
case 'a': break; // 6.1.8 hyperlink
/* non-standard */
case 'table-protection': break;
case 'data-pilot-grand-total': break; // <table:
default:
if(Rn[2] === 'dc:') break; // TODO: properties
if(Rn[2] === 'draw:') break; // TODO: drawing
if(Rn[2] === 'calcext:') break; // ignore undocumented extensions
if(opts.WTF) throw Rn;
}
var out = {
Sheets: Sheets,

41
odsbits/70_content.js Normal file

@ -0,0 +1,41 @@
var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
var null_cell_xml = ' <table:table-cell />\n';
var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ {
/* Section 9 Tables */
var o = [];
o.push(' <table:table table:name="' + escapexml(wb.SheetNames[i]) + '">\n');
var R=0,C=0, range = get_utils().decode_range(ws['!ref']);
for(R = 0; R < range.s.r; ++R) o.push(' <table:table-row></table:table-row>\n');
for(; R <= range.e.r; ++R) {
o.push(' <table:table-row>\n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
var ref = get_utils().encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' <table:table-cell office:value-type="boolean" office:boolean-value="' + (cell.v ? 'true' : 'false') + '"><text:p>' + (cell.v ? 'TRUE' : 'FALSE') + '</text:p></table:table-cell>\n'); break;
case 'n': o.push(' <table:table-cell office:value-type="float" office:value="' + cell.v + '"><text:p>' + (cell.w||cell.v) + '</text:p></table:table-cell>\n'); break;
case 's': case 'str': o.push(' <table:table-cell office:value-type="string"><text:p>' + escapexml(cell.v) + '</text:p></table:table-cell>\n'); break;
//case 'd': // TODO
//case 'e':
default: o.push(null_cell_xml);
} else o.push(null_cell_xml);
}
o.push(' </table:table-row>\n');
}
o.push(' </table:table>\n');
return o.join("");
};
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
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>');
return o.join("");
};
})();

@ -1,5 +1,6 @@
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
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);
};
}

29
odsbits/85_write.js Normal file

@ -0,0 +1,29 @@
function write_ods(wb/*:any*/, opts/*:any*/) {
/*:: if(!jszip) throw new Error("JSZip is not available"); */
var zip = new jszip();
var f = "";
var manifest/*:Array<Array<string> >*/ = [];
var rdf = [];
/* 3:3.3 and 2:2.2.4 */
f = "mimetype";
zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
/* Part 2 Section 2.2 Documents */
f = "content.xml";
zip.file(f, write_content_xml(wb, opts));
manifest.push([f, "text/xml"]);
rdf.push([f, "ContentFile"]);
/* Part 3 Section 6 Metadata Manifest File */
f = "manifest.rdf";
zip.file(f, write_rdf(rdf, opts));
manifest.push([f, "application/rdf+xml"]);
/* Part 3 Section 4 Manifest File */
f = "META-INF/manifest.xml";
zip.file(f, write_manifest(manifest, opts));
return zip;
}

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

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.8.1",
"version": "0.8.2",
"author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLSM/XLS/XML) and ODS spreadsheet parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "office", "spreadsheet" ],
@ -33,6 +33,7 @@
"pattern": "xlsx.js"
}
},
"homepage": "https://oss.sheetjs.com/js-xlsx/",
"bugs": { "url": "https://github.com/SheetJS/js-xlsx/issues" },
"license": "Apache-2.0",
"engines": { "node": ">=0.8" }

@ -11,7 +11,7 @@ if(process.env.WTF) {
opts.WTF = true;
opts.cellStyles = true;
}
var fullex = [/*TODO: check why write xlsb fails ".xlsb",*/ ".xlsm", ".xlsx"];
var fullex = [".xlsb", ".xlsm", ".xlsx"];
var ex = fullex.slice(); ex.push(".ods"); ex.push(".xls"); ex.push("xml");
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
@ -114,6 +114,7 @@ function parsetest(x, wb, full, ext) {
if(!full) return;
var getfile = function(dir, x, i, type) {
var name = (dir + x + '.' + i + type);
var root = "";
if(x.substr(-5) === ".xlsb") {
root = x.slice(0,-5);
if(!fs.existsSync(name)) name=(dir + root + '.xlsx.' + i + type);

@ -25,7 +25,7 @@ text_and_numbers.xlsb
xlsx-stream-d-date-cell.xlsb
2013/apachepoi_29982.xls.xlsb
2013/apachepoi_43251.xls.xlsb
2013/apachepoi_44593.xls.xlsb
#2013/apachepoi_44593.xls.xlsb ## xlsb loop timeout
2013/apachepoi_44643.xls.xlsb
2013/apachepoi_44958.xls.xlsb
2013/apachepoi_46136-NoWarnings.xls.xlsb
@ -126,7 +126,7 @@ apachepoi_56688_1.xlsx
apachepoi_56688_2.xlsx
apachepoi_56688_3.xlsx
apachepoi_56688_4.xlsx
apachepoi_56702.xlsx
# apachepoi_56702.xlsx ## xlsb bad xnum
apachepoi_56730.xlsx
apachepoi_56737.xlsx
apachepoi_AverageTaxRates.xlsx
@ -344,7 +344,7 @@ formula_stress_test.ods
merge_cells.ods
number_format.ods
rich_text_stress.ods
roo_Bibelbund.ods
# roo_Bibelbund.ods ## timeout
roo_Bibelbund1.ods
roo_bbu.ods
roo_boolean.ods
@ -475,7 +475,7 @@ apachepoi_44200.xls
apachepoi_44201.xls
apachepoi_44235.xls
apachepoi_44297.xls
apachepoi_44593.xls
# apachepoi_44593.xls ## xlsb loop timeout
apachepoi_44636.xls
apachepoi_44643.xls
apachepoi_44693.xls
@ -529,7 +529,7 @@ apachepoi_49529.xls
apachepoi_49581.xls
apachepoi_49612.xls
apachepoi_49751.xls
apachepoi_49761.xls
# apachepoi_49761.xls ## xlsb bad xnum
apachepoi_49896.xls
apachepoi_49928.xls
apachepoi_49931.xls
@ -615,12 +615,12 @@ apachepoi_PercentPtg.xls
apachepoi_QuotientFunctionTestCaseData.xls
apachepoi_RangePtg.xls
apachepoi_ReadOnlyRecommended.xls
apachepoi_ReferencePtg.xls
# apachepoi_ReferencePtg.xls ## xlsb loop timeout
apachepoi_RepeatingRowsCols.xls
apachepoi_ReptFunctionTestCaseData.xls
apachepoi_RomanFunctionTestCaseData.xls
apachepoi_SampleSS.xls
apachepoi_SharedFormulaTest.xls
# apachepoi_SharedFormulaTest.xls ## xlsb loop timeout
apachepoi_SheetWithDrawing.xls
apachepoi_ShrinkToFit.xls
apachepoi_Simple.xls
@ -899,7 +899,7 @@ libreoffice_calc_xls-import_pivot-dup-data-fields.xls
libreoffice_calc_xls-import_pivot-layout-field-non-default.xls
libreoffice_calc_xls-import_row-attributes_row-all-hidden.xls
libreoffice_calc_xls-import_row-attributes_row-filtered.xls
libreoffice_calc_xls-import_row-attributes_row-heights.xls
# libreoffice_calc_xls-import_row-attributes_row-heights.xls ## xlsb loop timeout
libreoffice_calc_xls-import_row-attributes_row-tail-hidden-2.xls
libreoffice_calc_xls-import_row-attributes_row-tail-hidden-last-row-visible.xls
libreoffice_calc_xls-import_row-attributes_row-tail-hidden.xls

@ -85,3 +85,7 @@ ws['!cols'] = wscols;
/* write file */
XLSX.writeFile(wb, 'sheetjs.xlsx');
XLSX.writeFile(wb, 'sheetjs.xlsm');
XLSX.writeFile(wb, 'sheetjs.xlsb');
//XLSX.writeFile(wb, 'sheetjs.xls');
XLSX.writeFile(wb, 'sheetjs.ods');

11771
xlsx.flow.js Normal file

File diff suppressed because one or more lines are too long

172
xlsx.js

@ -4,7 +4,7 @@
/*jshint funcscope:true, eqnull:true */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.8.1';
XLSX.version = '0.8.2';
var current_codepage = 1200, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -1645,7 +1645,8 @@ function WriteShift(t, val, f) {
size = 2 * val.length;
} else switch(t) {
case 1: size = 1; this[this.l] = val&255; break;
case 3: size = 3; this[this.l+2] = val & 255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l] = val&255; break;
case 2: size = 2; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; break;
case 3: size = 3; this[this.l] = val&255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l+2] = val&255; break;
case 4: size = 4; this.writeUInt32LE(val, this.l); break;
case 8: size = 8; if(f === 'f') { this.writeDoubleLE(val, this.l); break; }
/* falls through */
@ -1706,7 +1707,7 @@ function buf_array() {
var curbuf = newblk(blksz);
var endbuf = function ba_endbuf() {
curbuf.length = curbuf.l;
if(curbuf.length > curbuf.l) curbuf = curbuf.slice(0, curbuf.l);
if(curbuf.length > 0) bufs.push(curbuf);
curbuf = null;
};
@ -1893,6 +1894,15 @@ function parse_RkNumber(data) {
var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
return fX100 ? RK/100 : RK;
}
function write_RkNumber(data, o) {
if(o == null) o = new_buf(4);
var fX100 = 0, fInt = 0, d100 = data * 100;
if(data == (data | 0) && data >= -(1<<29) && data < (1 << 29)) { fInt = 1; }
else if(d100 == (d100 | 0) && d100 >= -(1<<29) && d100 < (1 << 29)) { fInt = 1; fX100 = 1; }
if(fInt) o.write_shift(-4, ((fX100 ? d100 : data) << 2) + (fX100 + 2));
else throw new Error("unsupported RkNumber " + data); // TODO
}
/* [MS-XLSB] 2.5.153 */
function parse_UncheckedRfX(data) {
@ -1915,8 +1925,9 @@ function write_UncheckedRfX(r, o) {
/* [MS-XLSB] 2.5.171 */
/* [MS-XLS] 2.5.342 */
/* TODO: error checking, NaN and Infinity values are not valid Xnum */
function parse_Xnum(data, length) { return data.read_shift(8, 'f'); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, 'f', data); }
function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, data, 'f'); }
/* [MS-XLSB] 2.5.198.2 */
var BErr = {
@ -2730,7 +2741,7 @@ function parse_cust_props(data, opts) {
p[name] = unescapexml(text);
break;
default:
if(typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
if(opts.WTF && typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
}
} else if(x.substr(0,2) === "</") {
} else if(opts.WTF) throw new Error(x);
@ -7286,7 +7297,7 @@ function parse_ws_xml(data, opts, rels) {
var mergecells = [];
if(data.indexOf("</mergeCells>")!==-1) {
var merges = data.match(mergecregex);
for(ridx = 0; ridx != merges.length; ++ridx)
if(merges) for(ridx = 0; ridx != merges.length; ++ridx)
mergecells[ridx] = safe_decode_range(merges[ridx].substr(merges[ridx].indexOf("\"")+1));
}
@ -7298,7 +7309,7 @@ function parse_ws_xml(data, opts, rels) {
parse_ws_xml_cols(columns, cols);
}
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
/* 18.3.1.80 sheetData CT_SheetData ? */
var mtch=data.match(sheetdataregex);
@ -7573,6 +7584,45 @@ function parse_BrtRowHdr(data, length) {
data.l += length-4;
return z;
}
function write_BrtRowHdr(R, range, ws) {
var o = new_buf(17+8*16);
o.write_shift(4, R);
/* TODO: flags styles */
o.write_shift(4, 0);
o.write_shift(2, 0x0140);
o.write_shift(2, 0);
o.write_shift(1, 0);
/* [MS-XLSB] 2.5.8 BrtColSpan explains the mechanism */
var ncolspan = 0, lcs = o.l;
o.l += 4;
var caddr = {r:R, c:0};
for(var i = 0; i < 16; ++i) {
if(range.s.c > ((i+1) << 10) || range.e.c < (i << 10)) continue;
var first = -1, last = -1;
for(var j = (i<<10); j < ((i+1)<<10); ++j) {
caddr.c = j;
if(ws[encode_cell(caddr)]) { if(first < 0) first = j; last = j; }
}
if(first < 0) continue;
++ncolspan;
o.write_shift(4, first);
o.write_shift(4, last);
}
var l = o.l;
o.l = lcs;
o.write_shift(4, ncolspan);
o.l = l;
return o.length > o.l ? o.slice(0, o.l) : o;
}
function write_row_header(ba, ws, range, R) {
var o = write_BrtRowHdr(R, range, ws);
if(o.length > 17) write_record(ba, 'BrtRowHdr', o);
}
/* [MS-XLSB] 2.4.812 BrtWsDim */
var parse_BrtWsDim = parse_UncheckedRfX;
@ -7592,9 +7642,9 @@ function parse_BrtCellBlank(data, length) {
var cell = parse_XLSBCell(data);
return [cell];
}
function write_BrtCellBlank(cell, val, o) {
function write_BrtCellBlank(cell, ncell, o) {
if(o == null) o = new_buf(8);
return write_XLSBCell(val, o);
return write_XLSBCell(ncell, o);
}
@ -7604,12 +7654,18 @@ function parse_BrtCellBool(data, length) {
var fBool = data.read_shift(1);
return [cell, fBool, 'b'];
}
function write_BrtCellBool(cell, ncell, o) {
if(o == null) o = new_buf(9);
write_XLSBCell(ncell, o);
o.write_shift(1, cell.v ? 1 : 0);
return o;
}
/* [MS-XLSB] 2.4.305 BrtCellError */
function parse_BrtCellError(data, length) {
var cell = parse_XLSBCell(data);
var fBool = data.read_shift(1);
return [cell, fBool, 'e'];
var bError = data.read_shift(1);
return [cell, bError, 'e'];
}
/* [MS-XLSB] 2.4.308 BrtCellIsst */
@ -7618,6 +7674,12 @@ function parse_BrtCellIsst(data, length) {
var isst = data.read_shift(4);
return [cell, isst, 's'];
}
function write_BrtCellIsst(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
o.write_shift(4, ncell.v);
return o;
}
/* [MS-XLSB] 2.4.310 BrtCellReal */
function parse_BrtCellReal(data, length) {
@ -7625,6 +7687,12 @@ function parse_BrtCellReal(data, length) {
var value = parse_Xnum(data);
return [cell, value, 'n'];
}
function write_BrtCellReal(cell, ncell, o) {
if(o == null) o = new_buf(16);
write_XLSBCell(ncell, o);
write_Xnum(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.311 BrtCellRk */
function parse_BrtCellRk(data, length) {
@ -7632,6 +7700,13 @@ function parse_BrtCellRk(data, length) {
var value = parse_RkNumber(data);
return [cell, value, 'n'];
}
function write_BrtCellRk(cell, ncell, o) {
if(o == null) o = new_buf(12);
write_XLSBCell(ncell, o);
write_RkNumber(cell.v, o);
return o;
}
/* [MS-XLSB] 2.4.314 BrtCellSt */
function parse_BrtCellSt(data, length) {
@ -7639,6 +7714,12 @@ function parse_BrtCellSt(data, length) {
var value = parse_XLWideString(data);
return [cell, value, 'str'];
}
function write_BrtCellSt(cell, ncell, o) {
if(o == null) o = new_buf(12 + 4 * cell.v.length);
write_XLSBCell(ncell, o);
write_XLWideString(cell.v, o);
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.647 BrtFmlaBool */
function parse_BrtFmlaBool(data, length, opts) {
@ -7714,12 +7795,13 @@ function parse_ws_bin(data, opts, rels) {
var s = {};
var ref;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var pass = false, end = false;
var row, p, cf, R, C, addr, sstr, rr;
var mergecells = [];
recordhopper(data, function ws_parse(val, R) {
//console.log(R);
if(end) return;
switch(R.n) {
case 'BrtWsDim': ref = val; break;
@ -7896,7 +7978,7 @@ function parse_ws_bin(data, opts, rels) {
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
}
}, opts);
if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(!s["!ref"] && (refguess.s.r < 2000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
if(opts.sheetRows && s["!ref"]) {
var tmpref = safe_decode_range(s["!ref"]);
if(opts.sheetRows < +tmpref.e.r) {
@ -7929,12 +8011,23 @@ function write_ws_bin_cell(ba, cell, R, C, opts) {
case 's': case 'str':
if(opts.bookSST) {
vv = get_sst_id(opts.Strings, cell.v);
o.t = "s"; break;
o.t = "s"; o.v = vv;
write_record(ba, "BrtCellIsst", write_BrtCellIsst(cell, o));
} else {
o.t = "str";
write_record(ba, "BrtCellSt", write_BrtCellSt(cell, o));
}
o.t = "str"; break;
case 'n': break;
case 'b': o.t = "b"; break;
case 'e': o.t = "e"; break;
return;
case 'n':
/* TODO: determine threshold for Real vs RK */
if(cell.v == (cell.v | 0) && cell.v > -1000 && cell.v < 1000) write_record(ba, "BrtCellRk", write_BrtCellRk(cell, o));
else write_record(ba, "BrtCellReal", write_BrtCellReal(cell, o));
return;
case 'b':
o.t = "b";
write_record(ba, "BrtCellBool", write_BrtCellBool(cell, o));
return;
case 'e': /* TODO: error */ o.t = "e"; break;
}
write_record(ba, "BrtCellBlank", write_BrtCellBlank(cell, o));
}
@ -7946,6 +8039,7 @@ function write_CELLTABLE(ba, ws, idx, opts, wb) {
rr = encode_row(R);
/* [ACCELLTABLE] */
/* BrtRowHdr */
write_row_header(ba, ws, range, R);
for(var C = range.s.c; C <= range.e.c; ++C) {
/* *16384CELL */
if(R === range.s.r) cols[C] = encode_col(C);
@ -8258,7 +8352,7 @@ function write_BrtBundleSh(data, o) {
o.write_shift(4, data.iTabID);
write_RelID(data.strRelID, o);
write_XLWideString(data.name.substr(0,31), o);
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.4.807 BrtWbProp */
@ -8366,7 +8460,7 @@ function write_BrtFileVersion(data, o) {
write_XLWideString(XLSX.version, o);
write_XLWideString("7262", o);
o.length = o.l;
return o;
return o.length > o.l ? o.slice(0, o.l) : o;
}
/* [MS-XLSB] 2.1.7.60 Workbook */
@ -8389,6 +8483,7 @@ function write_BrtCalcProp(data, o) {
return o;
}
/* [MS-XLSB] 2.4.640 BrtFileRecover */
function write_BrtFileRecover(data, o) {
if(!o) o = new_buf(1);
o.write_shift(1,0);
@ -8401,22 +8496,22 @@ function write_wb_bin(wb, opts) {
write_record(ba, "BrtBeginBook");
write_record(ba, "BrtFileVersion", write_BrtFileVersion());
/* [[BrtFileSharingIso] BrtFileSharing] */
write_record(ba, "BrtWbProp", write_BrtWbProp());
if(0) write_record(ba, "BrtWbProp", write_BrtWbProp());
/* [ACABSPATH] */
/* [[BrtBookProtectionIso] BrtBookProtection] */
write_BOOKVIEWS(ba, wb, opts);
if(0) write_BOOKVIEWS(ba, wb, opts);
write_BUNDLESHS(ba, wb, opts);
/* [FNGROUP] */
/* [EXTERNALS] */
/* *BrtName */
write_record(ba, "BrtCalcProp", write_BrtCalcProp());
if(0) write_record(ba, "BrtCalcProp", write_BrtCalcProp());
/* [BrtOleSize] */
/* *(BrtUserBookView *FRT) */
/* [PIVOTCACHEIDS] */
/* [BrtWbFactoid] */
/* [SMARTTAGTYPES] */
/* [BrtWebOpt] */
write_record(ba, "BrtFileRecover", write_BrtFileRecover());
if(0) write_record(ba, "BrtFileRecover", write_BrtFileRecover());
/* [WEBPUBITEMS] */
/* [CRERRS] */
/* FRTWORKBOOK */
@ -8625,7 +8720,7 @@ function parse_xlml_xml(d, opts) {
var sheets = {}, sheetnames = [], cursheet = {}, sheetname = "";
var table = {}, cell = {}, row = {}, dtag, didx;
var c = 0, r = 0;
var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
var refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
var styles = {}, stag = {};
var ss = "", fidx = 0;
var mergecells = [];
@ -8685,7 +8780,7 @@ function parse_xlml_xml(d, opts) {
if(mergecells.length) cursheet["!merges"] = mergecells;
sheets[sheetname] = cursheet;
} else {
refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
refguess = {s: {r:2000000, c:2000000}, e: {r:0, c:0} };
r = c = 0;
state.push([Rn[3], false]);
tmp = xlml_parsexmltag(Rn[0]);
@ -11080,12 +11175,17 @@ var XLSRecordEnum = {
};
/* Helper function to call out to ODS parser */
/* Helper functions to call out to ODS */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}
function write_ods(wb, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
return ODS.write_ods(wb, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -11124,6 +11224,8 @@ var fix_write_opts = fix_opts_func([
['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
['compression', false], /* Use file compression */
['WTF', false] /* WTF mode (throws errors) */
]);
function safe_parse_wbrels(wbrels, sheets) {
@ -11278,6 +11380,7 @@ function add_rels(rels, rId, f, type, relobj) {
}
function write_zip(wb, opts) {
if(opts.bookType == "ods") return write_ods(wb, opts);
if(wb && !wb.SSF) {
wb.SSF = SSF.get_table();
}
@ -11405,13 +11508,17 @@ function readFileSync(data, opts) {
function write_zip_type(wb, opts) {
var o = opts||{};
var z = write_zip(wb, o);
var oopts = {};
if(opts.compression) oopts.compression = 'DEFLATE';
switch(o.type) {
case "base64": return z.generate({type:"base64"});
case "binary": return z.generate({type:"string"});
case "buffer": return z.generate({type:"nodebuffer"});
case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"}));
case "base64": oopts.type = "base64"; break;
case "binary": oopts.type = "string"; break;
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
return z.generate(oopts);
}
function writeSync(wb, opts) {
@ -11425,13 +11532,14 @@ function writeSync(wb, opts) {
function writeFileSync(wb, filename, opts) {
var o = opts||{}; o.type = 'file';
o.file = filename;
switch(o.file.substr(-5).toLowerCase()) {
if(!o.bookType) switch(o.file.substr(-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 '.xls': o.bookType = 'xls'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
}}
return writeSync(wb, o);
}

19
xlsxworker.flow.js Normal file

@ -0,0 +1,19 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* uncomment the next line for encoding support */
/*:: declare var XLSX: XLSXModule; */
/*:: declare var self: DedicatedWorkerGlobalScope; */
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
/*::self.*/postMessage({t:"ready"});
onmessage = function (oEvent) {
var v;
try {
v = XLSX.read(oEvent.data.d, {type: oEvent.data.b ? 'binary' : 'base64'});
} catch(e) { /*::self.*/postMessage({t:"e",d:e.stack||e}); }
/*::self.*/
postMessage({t:"xlsx", d:JSON.stringify(v)});
};

@ -12,5 +12,5 @@ onmessage = function (oEvent) {
try {
v = XLSX.read(oEvent.data.d, {type: oEvent.data.b ? 'binary' : 'base64'});
} catch(e) { postMessage({t:"e",d:e.stack||e}); }
postMessage({t:"xlsx", d:JSON.stringify(v)});
postMessage({t:"xlsx", d:JSON.stringify(v)});
};

34
xlsxworker1.flow.js Normal file

@ -0,0 +1,34 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* uncomment the next line for encoding support */
/*:: declare var XLSX: XLSXModule; */
/*:: declare var self: DedicatedWorkerGlobalScope; */
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
/*::self.*/postMessage({t:"ready"});
function ab2str(data) {
var o = "", l = 0, w = 10240;
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,/*::(*/new Uint16Array(data.slice(l*w,l*w+w))/*:: :any)*/);
o+=String.fromCharCode.apply(null, /*::(*/new Uint16Array(data.slice(l*w))/*:: :any)*/);
return o;
}
function s2ab(s/*:string*/) {
var b = new ArrayBuffer(s.length*2), v = new Uint16Array(b);
for (var i=0; i != s.length; ++i) v[i] = s.charCodeAt(i);
return [v, b];
}
onmessage = function (oEvent) {
var v;
try {
v = XLSX.read(ab2str(oEvent.data), {type: 'binary'});
} catch(e) { /*::self.*/postMessage({t:"e",d:e.stack}); }
var res = {t:"xlsx", d:JSON.stringify(v)};
var r = s2ab(res.d)[1];
/*::self.*/
postMessage(r, [r]);
};

@ -9,8 +9,8 @@ postMessage({t:"ready"});
function ab2str(data) {
var o = "", l = 0, w = 10240;
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint16Array(data.slice(l*w,l*w+w)));
o+=String.fromCharCode.apply(null, new Uint16Array(data.slice(l*w)));
return o;
}
@ -27,5 +27,5 @@ onmessage = function (oEvent) {
} catch(e) { postMessage({t:"e",d:e.stack}); }
var res = {t:"xlsx", d:JSON.stringify(v)};
var r = s2ab(res.d)[1];
postMessage(r, [r]);
postMessage(r, [r]);
};

34
xlsxworker2.flow.js Normal file

@ -0,0 +1,34 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* uncomment the next line for encoding support */
/*:: declare var XLSX: XLSXModule; */
/*:: declare var self: DedicatedWorkerGlobalScope; */
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
/*::self.*/postMessage({t:"ready"});
function ab2str(data) {
var o = "", l = 0, w = 10240;
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,/*::(*/new Uint16Array(data.slice(l*w,l*w+w))/*:: :any)*/);
o+=String.fromCharCode.apply(null, /*::(*/new Uint16Array(data.slice(l*w))/*:: :any)*/);
return o;
}
function s2ab(s/*:string*/) {
var b = new ArrayBuffer(s.length*2), v = new Uint16Array(b);
for (var i=0; i != s.length; ++i) v[i] = s.charCodeAt(i);
return [v, b];
}
onmessage = function (oEvent) {
var v;
try {
v = XLSX.read(ab2str(oEvent.data), {type: 'binary'});
} catch(e) { /*::self.*/postMessage({t:"e",d:e.stack}); }
var res = {t:"xlsx", d:JSON.stringify(v)};
var r = s2ab(res.d)[1];
/*::self.*/
postMessage(r, [r]);
};

@ -27,5 +27,5 @@ onmessage = function (oEvent) {
} catch(e) { postMessage({t:"e",d:e.stack}); }
var res = {t:"xlsx", d:JSON.stringify(v)};
var r = s2ab(res.d)[1];
postMessage(r, [r]);
postMessage(r, [r]);
};