forked from sheetjs/sheetjs
version bump 0.7.0: Basic write support
- very basic XLSX / XLSM write support with roundtrip tests (XLSB stubs) - reorganized source tree - new XLSB range check ensures that A1 is not emitted for empty sheets - SSF table emitted in output (consistent with js-xls) - CLI supports writing Backwards-incompatible changes: o new Property aliases (see CORE_PROPS and EXT_PROPS) o FILETIME custom properties parsed as JS Dates o `xlsx2csv` -> `xlsx` (and `bin/xlsx{2csv,}.njs`)
This commit is contained in:
parent
b645f6ef98
commit
d15b81e0e9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
misc/coverage.html
|
||||
tmp
|
||||
|
@ -5,6 +5,7 @@ node_js:
|
||||
before_install:
|
||||
- "npm install -g mocha"
|
||||
- "npm install blanket"
|
||||
- "npm install xlsjs"
|
||||
- "npm install coveralls mocha-lcov-reporter"
|
||||
before_script:
|
||||
- "make init"
|
||||
|
@ -5,7 +5,7 @@ order to maintain that, every contributor must be vigilant.
|
||||
|
||||
There have been many projects in the past that have been very lax regarding
|
||||
licensing, and we are of the opinion that those are ticking timebombs and that
|
||||
no corporate product should depend on them.
|
||||
no corporate product should depend on them.
|
||||
|
||||
|
||||
# Required Reading
|
||||
@ -23,7 +23,7 @@ Before thinking about contributing, make sure that:
|
||||
|
||||
- You are not, nor have ever been, an employee of Microsoft Corporation.
|
||||
|
||||
- You have not signed any NDAs or Shared Source Agreements with Microsoft
|
||||
- You have not signed any NDAs or Shared Source Agreements with Microsoft
|
||||
Corporation or a subsidiary
|
||||
|
||||
- You have not consulted any existing relevant codebase (if you have, please
|
||||
@ -42,11 +42,11 @@ Keep these in mind as you work:
|
||||
consult in the process (and be extra careful not to use unlicensed code on
|
||||
the internet.
|
||||
|
||||
- You are working on your own time. Unless they explicitly grant permission,
|
||||
- You are working on your own time. Unless they explicitly grant permission,
|
||||
your employer may be the ultimate owner of your IP
|
||||
|
||||
|
||||
# Post-Contribution
|
||||
# Post-Contribution
|
||||
|
||||
Before contributions are merged, you will receive an email (at the address
|
||||
associated with the git commit) and will be asked to confirm the aforementioned
|
||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (C) 2012-2014 SheetJS
|
||||
Copyright (C) 2012-2014 SheetJS
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
8
Makefile
8
Makefile
@ -1,7 +1,7 @@
|
||||
LIB=xlsx
|
||||
DEPS=$(wildcard bits/*.js)
|
||||
TARGET=$(LIB).js
|
||||
FMT=xlsx xlsm xlsb misc
|
||||
FMT=xlsx xlsm xlsb misc full
|
||||
REQS=jszip.js
|
||||
ADDONS=dist/cpexcel.js
|
||||
|
||||
@ -25,6 +25,7 @@ init:
|
||||
|
||||
.PHONY: test mocha
|
||||
test mocha: test.js
|
||||
mkdir -p tmp
|
||||
mocha -R spec
|
||||
|
||||
TESTFMT=$(patsubst %,test_%,$(FMT))
|
||||
@ -42,6 +43,11 @@ cov: misc/coverage.html
|
||||
cov-spin:
|
||||
make cov & bash misc/spin.sh $$!
|
||||
|
||||
COVFMT=$(patsubst %,cov_%,$(FMT))
|
||||
.PHONY: $(COVFMT)
|
||||
$(COVFMT): cov_%:
|
||||
FMTS=$* make cov
|
||||
|
||||
misc/coverage.html: $(TARGET) test.js
|
||||
mocha --require blanket -R html-cov > $@
|
||||
|
||||
|
44
README.md
44
README.md
@ -1,6 +1,6 @@
|
||||
# xlsx
|
||||
|
||||
Currently a parser for XLSX/XLSM/XLSB files. Cleanroom implementation from the
|
||||
Parser and writer for XLSX/XLSM/XLSB files. Cleanroom implementation from the
|
||||
ISO 29500 Office Open XML specifications, [MS-XLSB], and related documents.
|
||||
|
||||
## Installation
|
||||
@ -45,7 +45,7 @@ The complete single-file version is generated at `dist/xlsx.full.min.js`
|
||||
|
||||
Simple usage (walks through every cell of every sheet and dumps the values):
|
||||
|
||||
var XLSX = require('xlsx');
|
||||
if(typeof require !== 'undefined') XLSX = require('xlsx');
|
||||
var workbook = XLSX.readFile('test.xlsx');
|
||||
var sheet_name_list = workbook.SheetNames;
|
||||
sheet_name_list.forEach(function(y) {
|
||||
@ -56,9 +56,9 @@ Simple usage (walks through every cell of every sheet and dumps the values):
|
||||
}
|
||||
});
|
||||
|
||||
The node version installs a binary `xlsx2csv` which can read XLSX/XLSM/XLSB
|
||||
The node version installs a binary `xlsx` which can read XLSX/XLSM/XLSB
|
||||
files and output the contents in various formats. The source is available at
|
||||
`xlsx2csv.njs` in the bin directory.
|
||||
`xlsx.njs` in the bin directory.
|
||||
|
||||
See <http://oss.sheetjs.com/js-xlsx/> for a browser example.
|
||||
|
||||
@ -76,12 +76,27 @@ Some helper functions in `XLSX.utils` generate different views of the sheets:
|
||||
|
||||
For more details:
|
||||
|
||||
- `bin/xlsx2csv.njs` is a tool for node
|
||||
- `bin/xlsx.njs` is a tool for node
|
||||
- `index.html` is the live demo
|
||||
- `bits/90_utils.js` contains the logic for generating CSV and JSON from sheets
|
||||
|
||||
## Interface
|
||||
|
||||
`XLSX` is the exposed variable in the browser and the exported variable in node
|
||||
|
||||
|
||||
`XLSX.read(data, read_opts)` attempts to parse `data`.
|
||||
|
||||
`XLSX.readFile(filename, read_opts)` attempts to read `filename` and parse.
|
||||
|
||||
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
|
||||
|
||||
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
|
||||
|
||||
## Cell Object Description
|
||||
|
||||
js-xlsx conforms to the Common Spreadsheet Format (CSF):
|
||||
|
||||
`.SheetNames` is an ordered list of the sheets in the workbook
|
||||
|
||||
`.Sheets[sheetname]` returns a data structure representing the sheet. Each key
|
||||
@ -102,7 +117,7 @@ that does not start with `!` corresponds to a cell (using `A-1` notation).
|
||||
|
||||
For dates, `.v` holds the raw date code from the sheet and `.w` holds the text
|
||||
|
||||
## Options
|
||||
## Parsing Options
|
||||
|
||||
The exported `read` and `readFile` functions accept an options argument:
|
||||
|
||||
@ -133,6 +148,21 @@ The exported `read` and `readFile` functions accept an options argument:
|
||||
|
||||
The defaults are enumerated in bits/84_defaults.js
|
||||
|
||||
## Writing Options
|
||||
|
||||
The exported `write` and `writeFile` functions accept an options argument:
|
||||
|
||||
| Option Name | Default | Description |
|
||||
| :---------- | ------: | :---------- |
|
||||
| bookSST | false | Generate Shared String Table ** |
|
||||
| bookType | 'xlsx' | Type of Workbook ("xlsx" or "xlsm" or "xlsb") |
|
||||
|
||||
- `bookSST` is slower and more memory intensive, but has better compatibility
|
||||
with iOS Numbers
|
||||
- `bookType = 'xlsb'` is stubbed and far from complete
|
||||
- The raw data is the only thing guaranteed to be saved. Formulae, formatting,
|
||||
and other niceties are not serialized (pending CSF standardization)
|
||||
|
||||
## Tested Environments
|
||||
|
||||
- Node 0.8, 0.10 (latest release)
|
||||
@ -165,6 +195,8 @@ $ simplehttpserver # or "python -mSimpleHTTPServer" or "serve"
|
||||
$ open -a Chromium.app http://localhost:8000/stress.html
|
||||
```
|
||||
|
||||
For a much smaller test, run `make test_misc`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Due to the precarious nature of the Open Specifications Promise, it is very
|
||||
|
@ -10,9 +10,13 @@ program
|
||||
.option('-f, --file <file>', 'use specified workbook')
|
||||
.option('-s, --sheet <sheet>', 'print specified sheet (default first sheet)')
|
||||
.option('-l, --list-sheets', 'list sheet names and exit')
|
||||
.option('-o, --output <file>', 'output to specified file')
|
||||
/*.option('-B, --xlsb', 'emit XLSB to <sheetname> or <file>.xlsb') */
|
||||
.option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm')
|
||||
.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx')
|
||||
.option('-S, --formulae', 'print formulae')
|
||||
.option('-j, --json', 'emit formatted JSON rather than CSV (all fields text)')
|
||||
.option('-J, --raw-js', 'emit raw JS object rather than CSV (raw numbers)')
|
||||
.option('-j, --json', 'emit formatted JSON (all fields text)')
|
||||
.option('-J, --raw-js', 'emit raw JS object (raw numbers)')
|
||||
.option('-F, --field-sep <sep>', 'CSV field separator', ",")
|
||||
.option('-R, --row-sep <sep>', 'CSV row separator', "\n")
|
||||
.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)')
|
||||
@ -21,6 +25,7 @@ program
|
||||
.option('-q, --quiet', 'quiet mode');
|
||||
|
||||
program.on('--help', function() {
|
||||
console.log(' Default output format is CSV');
|
||||
console.log(' Support email: dev@sheetjs.com');
|
||||
console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/');
|
||||
});
|
||||
@ -36,19 +41,22 @@ if(program.sheet) sheetname = program.sheet;
|
||||
if(program.file) filename = program.file;
|
||||
|
||||
if(!filename) {
|
||||
console.error(n + "2csv: must specify a filename");
|
||||
console.error(n + ": must specify a filename");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if(!fs.existsSync(filename)) {
|
||||
console.error(n + "2csv: " + filename + ": No such file or directory");
|
||||
console.error(n + ": " + filename + ": No such file or directory");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
var opts = {}, wb;
|
||||
if(program.listSheets) opts.bookSheets = true;
|
||||
if(program.sheetRows) opts.sheetRows = program.sheetRows;
|
||||
|
||||
if(program.xlsx || program.xlsm || program.xlsb) {
|
||||
opts.cellNF = true;
|
||||
if(program.output) sheetname = program.output;
|
||||
}
|
||||
if(program.dev) {
|
||||
X.verbose = 2;
|
||||
opts.WTF = true;
|
||||
@ -57,7 +65,7 @@ if(program.dev) {
|
||||
else try {
|
||||
wb = X.readFile(filename, opts);
|
||||
} catch(e) {
|
||||
var msg = (program.quiet) ? "" : n + "2csv: error parsing ";
|
||||
var msg = (program.quiet) ? "" : n + ": error parsing ";
|
||||
msg += filename + ": " + e;
|
||||
console.error(msg);
|
||||
process.exit(3);
|
||||
@ -69,6 +77,12 @@ if(program.listSheets) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var wopts = {WTF:opts.WTF};
|
||||
|
||||
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);
|
||||
|
||||
var target_sheet = sheetname || '';
|
||||
if(target_sheet === '') target_sheet = wb.SheetNames[0];
|
||||
|
||||
@ -77,12 +91,15 @@ try {
|
||||
ws = wb.Sheets[target_sheet];
|
||||
if(!ws) throw "Sheet " + target_sheet + " cannot be found";
|
||||
} catch(e) {
|
||||
console.error(n + "2csv: error parsing "+filename+" "+target_sheet+": " + e);
|
||||
console.error(n + ": error parsing "+filename+" "+target_sheet+": " + e);
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
if(!program.quiet) console.error(target_sheet);
|
||||
if(program.formulae) console.log(X.utils.get_formulae(ws).join("\n"));
|
||||
else if(program.json) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws)));
|
||||
else if(program.rawJs) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws,{raw:true})));
|
||||
else console.log(X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep}));
|
||||
var oo = "";
|
||||
if(program.formulae) oo = X.utils.get_formulae(ws).join("\n");
|
||||
else if(program.json) oo = JSON.stringify(X.utils.sheet_to_row_object_array(ws));
|
||||
else if(program.rawJs) oo = JSON.stringify(X.utils.sheet_to_row_object_array(ws,{raw:true}));
|
||||
else oo = X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep});
|
||||
|
||||
if(program.output) fs.writeFileSync(program.output, oo);
|
@ -1 +1 @@
|
||||
XLSX.version = '0.6.2';
|
||||
XLSX.version = '0.7.0';
|
||||
|
13
bits/30_jsutils.js
Normal file
13
bits/30_jsutils.js
Normal file
@ -0,0 +1,13 @@
|
||||
function isval(x) { return typeof x !== "undefined" && x !== null; }
|
||||
|
||||
function keys(o) { return Object.keys(o).filter(function(x) { return o.hasOwnProperty(x); }); }
|
||||
|
||||
function evert(obj, arr) {
|
||||
var o = {};
|
||||
keys(obj).forEach(function(k) {
|
||||
if(!obj.hasOwnProperty(k)) return;
|
||||
if(!arr) o[obj[k]] = k;
|
||||
else (o[obj[k]]=o[obj[k]]||[]).push(k);
|
||||
});
|
||||
return o;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
var _chr = function(c) { return String.fromCharCode(c); };
|
||||
var _ord = function(c) { return c.charCodeAt(0); };
|
||||
var attregexg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
|
||||
var attregex=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
|
||||
function parsexmltag(tag) {
|
||||
@ -13,12 +14,6 @@ function parsexmltag(tag) {
|
||||
return z;
|
||||
}
|
||||
|
||||
function evert(obj) {
|
||||
var o = {};
|
||||
Object.keys(obj).forEach(function(k) { if(obj.hasOwnProperty(k)) o[obj[k]] = k; });
|
||||
return o;
|
||||
}
|
||||
|
||||
var encodings = {
|
||||
'"': '"',
|
||||
''': "'",
|
||||
@ -38,6 +33,7 @@ function unescapexml(text){
|
||||
function escapexml(text){
|
||||
var s = text + '';
|
||||
rencstr.forEach(function(y){s=s.replace(new RegExp(y,'g'), rencoding[y]);});
|
||||
s = s.replace(/[\u0000-\u0007]/g,function(s) { return "_x" + ("0000"+_ord(s).toString(16)).substr(-4) + "_";}); /* TODO: verify range */
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -83,4 +79,37 @@ function parseVector(data) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function isval(x) { return typeof x !== "undefined" && x !== null; }
|
||||
function writetag(f,g) {return '<' + f + (g.match(/(^\s|\s$|\n)/)?' xml:space="preserve"' : "") + '>' + g + '</' + f + '>';}
|
||||
|
||||
/*jshint -W041 */
|
||||
function writextag(f,g,h) { return '<' + f + (h != null ? keys(h).map(function(k) { return " " + k + '="' + h[k] + '"';}).join("") : "") + (g == null ? "/" : (g.match(/(^\s|\s$|\n)/)?' xml:space="preserve"' : "") + '>' + g + '</' + f) + '>';}
|
||||
|
||||
function write_w3cdtf(d, t) { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } }
|
||||
|
||||
function write_vt(s) {
|
||||
if(typeof s == 'string') return writextag('vt:lpwstr', s);
|
||||
if(typeof s == 'number') return writextag((s|0)==s?'vt:i4':'vt:r8', String(s));
|
||||
if(typeof s == 'boolean') return writextag('vt:bool', s?'true':'false');
|
||||
if(s instanceof Date) return writextag('vt:filetime', write_w3cdtf(s));
|
||||
throw new Error("Unable to serialize " + s);
|
||||
}
|
||||
|
||||
var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
|
||||
var XMLNS = {
|
||||
'dc': 'http://purl.org/dc/elements/1.1/',
|
||||
'dcterms': 'http://purl.org/dc/terms/',
|
||||
'dcmitype': 'http://purl.org/dc/dcmitype/',
|
||||
'mx': 'http://schemas.microsoft.com/office/mac/excel/2008/main',
|
||||
'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
|
||||
'sjs': 'http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties',
|
||||
'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes',
|
||||
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'xsd': 'http://www.w3.org/2001/XMLSchema'
|
||||
};
|
||||
|
||||
XMLNS.main = [
|
||||
'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
|
||||
'http://purl.oclc.org/ooxml/spreadsheetml/main',
|
||||
'http://schemas.microsoft.com/office/excel/2006/main',
|
||||
'http://schemas.microsoft.com/office/excel/2006/2'
|
||||
];
|
@ -13,3 +13,8 @@ var recordhopper = function(data, cb, opts) {
|
||||
if(cb(d, R, RT)) return;
|
||||
}
|
||||
};
|
||||
|
||||
/* control buffer usage for fixed-length buffers */
|
||||
var blobhopper = function() {
|
||||
var bufs = [];
|
||||
};
|
@ -5,15 +5,9 @@
|
||||
/* [MS-XLSB] 2.1.7 Part Enumeration */
|
||||
var ct2type = {
|
||||
/* Workbook */
|
||||
"application/vnd.ms-excel.main": "workbooks",
|
||||
"application/vnd.ms-excel.sheet.macroEnabled.main+xml": "workbooks",
|
||||
"application/vnd.ms-excel.sheet.binary.macroEnabled.main": "workbooks",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": "TODO", /* Template */
|
||||
|
||||
/* Worksheet */
|
||||
"application/vnd.ms-excel.worksheet": "sheets",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml": "sheets",
|
||||
"application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */
|
||||
|
||||
/* Chartsheet */
|
||||
@ -30,14 +24,6 @@ var ct2type = {
|
||||
"application/vnd.ms-excel.intlmacrosheet": "TODO",
|
||||
"application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */
|
||||
|
||||
/* Shared Strings */
|
||||
"application/vnd.ms-excel.sharedStrings": "strs",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml": "strs",
|
||||
|
||||
/* Styles */
|
||||
"application/vnd.ms-excel.styles": "styles",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml": "styles",
|
||||
|
||||
/* File Properties */
|
||||
"application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
|
||||
"application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops",
|
||||
@ -151,99 +137,48 @@ var ct2type = {
|
||||
/* VML */
|
||||
"application/vnd.openxmlformats-officedocument.vmlDrawing": "TODO",
|
||||
|
||||
"application/vnd.openxmlformats-package.relationships+xml": "TODO",
|
||||
"application/vnd.openxmlformats-package.relationships+xml": "rels",
|
||||
"application/vnd.openxmlformats-officedocument.oleObject": "TODO",
|
||||
|
||||
"foo": "bar"
|
||||
"sheet": "js"
|
||||
};
|
||||
|
||||
var XMLNS_CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
|
||||
function parseProps(data) {
|
||||
var p = { Company:'' }, q = {};
|
||||
var strings = ["Application", "DocSecurity", "Company", "AppVersion"];
|
||||
var bools = ["HyperlinksChanged","SharedDoc","LinksUpToDate","ScaleCrop"];
|
||||
var xtra = ["HeadingPairs", "TitlesOfParts"];
|
||||
var xtracp = ["category", "contentStatus", "lastModifiedBy", "lastPrinted", "revision", "version"];
|
||||
var xtradc = ["creator", "description", "identifier", "language", "subject", "title"];
|
||||
var xtradcterms = ["created", "modified"];
|
||||
xtra = xtra.concat(xtracp.map(function(x) { return "cp:" + x; }));
|
||||
xtra = xtra.concat(xtradc.map(function(x) { return "dc:" + x; }));
|
||||
xtra = xtra.concat(xtradcterms.map(function(x) { return "dcterms:" + x; }));
|
||||
|
||||
|
||||
strings.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1];});
|
||||
bools.forEach(function(f){p[f] = (data.match(matchtag(f))||[])[1] == "true";});
|
||||
xtra.forEach(function(f) {
|
||||
var cur = data.match(new RegExp("<" + f + "[^>]*>(.*)<\/" + f + ">"));
|
||||
if(cur && cur.length > 0) q[f] = cur[1];
|
||||
});
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
switch(v[i].v) {
|
||||
case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
}
|
||||
var CT_LIST = (function(){
|
||||
var o = {
|
||||
workbooks: {
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
||||
xlsm: "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
|
||||
xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.main",
|
||||
xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"
|
||||
},
|
||||
strs: { /* Shared Strings */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
||||
xlsb: "application/vnd.ms-excel.sharedStrings"
|
||||
},
|
||||
sheets: {
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
||||
xlsb: "application/vnd.ms-excel.worksheet"
|
||||
},
|
||||
styles: {/* Styles */
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
||||
xlsb: "application/vnd.ms-excel.styles"
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); });
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
p.Creator = q["dc:creator"];
|
||||
p.LastModifiedBy = q["cp:lastModifiedBy"];
|
||||
p.CreatedDate = new Date(q["dcterms:created"]);
|
||||
p.ModifiedDate = new Date(q["dcterms:modified"]);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
keys(o).forEach(function(k) { if(!o[k].xlsm) o[k].xlsm = o[k].xlsx; });
|
||||
keys(o).forEach(function(k){ keys(o[k]).forEach(function(v) { ct2type[o[k][v]] = k; }); });
|
||||
return o;
|
||||
})();
|
||||
|
||||
/* 15.2.12.2 Custom File Properties Part */
|
||||
function parseCustomProps(data) {
|
||||
var p = {}, name;
|
||||
data.match(/<[^>]+>([^<]*)/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<property': name = y.name; break;
|
||||
case '</property>': name = null; break;
|
||||
default: if (x.indexOf('<vt:') === 0) {
|
||||
var toks = x.split('>');
|
||||
var type = toks[0].substring(4), text = toks[1];
|
||||
/* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */
|
||||
switch(type) {
|
||||
case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr':
|
||||
p[name] = unescapexml(text);
|
||||
break;
|
||||
case 'bool':
|
||||
p[name] = parsexmlbool(text, '<vt:bool>');
|
||||
break;
|
||||
case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint':
|
||||
p[name] = parseInt(text, 10);
|
||||
break;
|
||||
case 'r4': case 'r8': case 'decimal':
|
||||
p[name] = parseFloat(text);
|
||||
break;
|
||||
case 'filetime': case 'date':
|
||||
p[name] = text; // should we make this into a date?
|
||||
break;
|
||||
case 'cy': case 'error':
|
||||
p[name] = unescapexml(text);
|
||||
break;
|
||||
default:
|
||||
console.warn('Unexpected', x, type, toks);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return p;
|
||||
}
|
||||
var type2ct = evert(ct2type, true);
|
||||
|
||||
var ctext = {};
|
||||
function parseCT(data, opts) {
|
||||
XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
||||
|
||||
function parse_ct(data, opts) {
|
||||
var ctext = {};
|
||||
if(!data || !data.match) return data;
|
||||
var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
|
||||
TODO:[], xmlns: "" };
|
||||
TODO:[], rels:[], xmlns: "" };
|
||||
(data.match(/<[^>]*>/g)||[]).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
@ -252,11 +187,11 @@ function parseCT(data, opts) {
|
||||
case '<Default': ctext[y.Extension] = y.ContentType; break;
|
||||
case '<Override':
|
||||
if(y.ContentType in ct2type)ct[ct2type[y.ContentType]].push(y.PartName);
|
||||
else if(opts.WTF) console.error(y.ContentType);
|
||||
else if(opts.WTF) console.error(y);
|
||||
break;
|
||||
}
|
||||
});
|
||||
if(ct.xmlns !== XMLNS_CT) throw new Error("Unknown Namespace: " + ct.xmlns);
|
||||
if(ct.xmlns !== XMLNS.CT) throw new Error("Unknown Namespace: " + ct.xmlns);
|
||||
ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : "";
|
||||
ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
|
||||
ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
|
||||
@ -265,44 +200,54 @@ function parseCT(data, opts) {
|
||||
return ct;
|
||||
}
|
||||
|
||||
var CTYPE_XML_ROOT = writextag('Types', null, {
|
||||
'xmlns': XMLNS.CT,
|
||||
'xmlns:xsd': XMLNS.xsd,
|
||||
'xmlns:xsi': XMLNS.xsi
|
||||
});
|
||||
|
||||
var CTYPE_DEFAULTS = [
|
||||
['xml', 'application/xml'],
|
||||
['rels', type2ct.rels[0]]
|
||||
].map(function(x) {
|
||||
return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]});
|
||||
});
|
||||
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
function parseRels(data, currentFilePath) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
currentFilePath = '/'+currentFilePath;
|
||||
}
|
||||
var rels = {};
|
||||
var hash = {};
|
||||
var resolveRelativePathIntoAbsolute = function (to) {
|
||||
var toksFrom = currentFilePath.split('/');
|
||||
toksFrom.pop(); // folder path
|
||||
var toksTo = to.split('/');
|
||||
var reversed = [];
|
||||
while (toksTo.length !== 0) {
|
||||
var tokTo = toksTo.shift();
|
||||
if (tokTo === '..') {
|
||||
toksFrom.pop();
|
||||
} else if (tokTo !== '.') {
|
||||
toksFrom.push(tokTo);
|
||||
}
|
||||
function write_ct(ct, opts) {
|
||||
var o = [], v;
|
||||
o.push(XML_HEADER);
|
||||
o.push(CTYPE_XML_ROOT);
|
||||
o = o.concat(CTYPE_DEFAULTS);
|
||||
var f1 = function(w) {
|
||||
if(ct[w] && ct[w].length > 0) {
|
||||
v = ct[w][0];
|
||||
o.push(writextag('Override', null, {
|
||||
'PartName': (v[0] == '/' ? "":"/") + v,
|
||||
'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
|
||||
}));
|
||||
}
|
||||
return toksFrom.join('/');
|
||||
};
|
||||
|
||||
data.match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
/* 9.3.2.2 OPC_Relationships */
|
||||
if (y[0] === '<Relationship') {
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
|
||||
var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
|
||||
rels[canonictarget] = rel;
|
||||
hash[y.Id] = rel;
|
||||
}
|
||||
});
|
||||
rels["!id"] = hash;
|
||||
return rels;
|
||||
var f2 = function(w) {
|
||||
ct[w].forEach(function(v) {
|
||||
o.push(writextag('Override', null, {
|
||||
'PartName': (v[0] == '/' ? "":"/") + v,
|
||||
'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
|
||||
}));
|
||||
});
|
||||
};
|
||||
var f3 = function(t) {
|
||||
(ct[t]||[]).forEach(function(v) {
|
||||
o.push(writextag('Override', null, {
|
||||
'PartName': (v[0] == '/' ? "":"/") + v,
|
||||
'ContentType': type2ct[t][0]
|
||||
}));
|
||||
});
|
||||
};
|
||||
f1('workbooks');
|
||||
f2('sheets');
|
||||
f3('themes');
|
||||
['strs', 'styles'].forEach(f1);
|
||||
['coreprops', 'extprops', 'custprops'].forEach(f3);
|
||||
if(o.length>2){ o.push('</Types>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
|
61
bits/41_rels.js
Normal file
61
bits/41_rels.js
Normal file
@ -0,0 +1,61 @@
|
||||
/* 9.3.2 OPC Relationships Markup */
|
||||
var RELS = {
|
||||
WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
||||
SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||
};
|
||||
|
||||
function parse_rels(data, currentFilePath) {
|
||||
if (!data) return data;
|
||||
if (currentFilePath.charAt(0) !== '/') {
|
||||
currentFilePath = '/'+currentFilePath;
|
||||
}
|
||||
var rels = {};
|
||||
var hash = {};
|
||||
var resolveRelativePathIntoAbsolute = function (to) {
|
||||
var toksFrom = currentFilePath.split('/');
|
||||
toksFrom.pop(); // folder path
|
||||
var toksTo = to.split('/');
|
||||
var reversed = [];
|
||||
while (toksTo.length !== 0) {
|
||||
var tokTo = toksTo.shift();
|
||||
if (tokTo === '..') {
|
||||
toksFrom.pop();
|
||||
} else if (tokTo !== '.') {
|
||||
toksFrom.push(tokTo);
|
||||
}
|
||||
}
|
||||
return toksFrom.join('/');
|
||||
};
|
||||
|
||||
data.match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
/* 9.3.2.2 OPC_Relationships */
|
||||
if (y[0] === '<Relationship') {
|
||||
var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
|
||||
var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
|
||||
rels[canonictarget] = rel;
|
||||
hash[y.Id] = rel;
|
||||
}
|
||||
});
|
||||
rels["!id"] = hash;
|
||||
return rels;
|
||||
}
|
||||
|
||||
XMLNS.RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
|
||||
|
||||
var RELS_ROOT = writextag('Relationships', null, {
|
||||
//'xmlns:ns0': XMLNS.RELS,
|
||||
'xmlns': XMLNS.RELS
|
||||
});
|
||||
|
||||
/* TODO */
|
||||
function write_rels(rels) {
|
||||
var o = [];
|
||||
o.push(XML_HEADER);
|
||||
o.push(RELS_ROOT);
|
||||
keys(rels['!id']).forEach(function(rid) { var rel = rels['!id'][rid];
|
||||
o.push(writextag('Relationship', null, rel));
|
||||
});
|
||||
if(o.length>2){ o.push('</Relationships>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
66
bits/43_coreprops.js
Normal file
66
bits/43_coreprops.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* ECMA-376 Part II 11.1 Core Properties Part */
|
||||
/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
|
||||
var CORE_PROPS = [
|
||||
["cp:category", "Category"],
|
||||
["cp:contentStatus", "ContentStatus"],
|
||||
["cp:keywords", "Keywords"],
|
||||
["cp:lastModifiedBy", "LastAuthor"],
|
||||
["cp:lastPrinted", "LastPrinted"],
|
||||
["cp:revision", "RevNumber"],
|
||||
["cp:version", "Version"],
|
||||
["dc:creator", "Author"],
|
||||
["dc:description", "Comments"],
|
||||
["dc:identifier", "Identifier"],
|
||||
["dc:language", "Language"],
|
||||
["dc:subject", "Subject"],
|
||||
["dc:title", "Title"],
|
||||
["dcterms:created", "CreatedDate", 'date'],
|
||||
["dcterms:modified", "ModifiedDate", 'date']
|
||||
];
|
||||
|
||||
XMLNS.CORE_PROPS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
|
||||
RELS.CORE_PROPS = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
|
||||
|
||||
|
||||
function parse_core_props(data) {
|
||||
var p = {};
|
||||
|
||||
CORE_PROPS.forEach(function(f) {
|
||||
var g = "(?:"+ f[0].substr(0,f[0].indexOf(":")) +":)"+ f[0].substr(f[0].indexOf(":")+1);
|
||||
var cur = data.match(new RegExp("<" + g + "[^>]*>(.*)<\/" + g + ">"));
|
||||
if(cur && cur.length > 0) p[f[1]] = cur[1];
|
||||
if(f[2] === 'date' && p[f[1]]) p[f[1]] = new Date(p[f[1]]);
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, {
|
||||
//'xmlns': XMLNS.CORE_PROPS,
|
||||
'xmlns:cp': XMLNS.CORE_PROPS,
|
||||
'xmlns:dc': XMLNS.dc,
|
||||
'xmlns:dcterms': XMLNS.dcterms,
|
||||
'xmlns:dcmitype': XMLNS.dcmitype,
|
||||
'xmlns:xsi': XMLNS.xsi
|
||||
});
|
||||
|
||||
function write_core_props(cp, opts) {
|
||||
var o = [], p = {};
|
||||
o.push(XML_HEADER);
|
||||
o.push(CORE_PROPS_XML_ROOT);
|
||||
if(!cp) return o.join("");
|
||||
|
||||
var doit = function(f, g, h) {
|
||||
if(p[f] || typeof g === 'undefined' || g === "") return;
|
||||
if(typeof g !== 'string') g = String(g); /* TODO: remove */
|
||||
p[f] = g;
|
||||
o.push(h ? writextag(f,g,h) : writetag(f,g));
|
||||
};
|
||||
|
||||
if(typeof cp.CreatedDate !== 'undefined') doit("dcterms:created", write_w3cdtf(cp.CreatedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"});
|
||||
if(typeof cp.ModifiedDate !== 'undefined') doit("dcterms:modified", write_w3cdtf(cp.ModifiedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"});
|
||||
|
||||
CORE_PROPS.forEach(function(f) { doit(f[0], cp[f[1]]); });
|
||||
if(o.length>2){ o.push('</cp:coreProperties>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
75
bits/44_extprops.js
Normal file
75
bits/44_extprops.js
Normal file
@ -0,0 +1,75 @@
|
||||
/* 15.2.12.3 Extended File Properties Part */
|
||||
/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
|
||||
var EXT_PROPS = [
|
||||
["Application", "Application", "string"],
|
||||
["AppVersion", "AppVersion", "string"],
|
||||
["Company", "Company", "string"],
|
||||
["DocSecurity", "DocSecurity", "string"],
|
||||
["Manager", "Manager", "string"],
|
||||
["HyperlinksChanged", "HyperlinksChanged", "bool"],
|
||||
["SharedDoc", "SharedDoc", "bool"],
|
||||
["LinksUpToDate", "LinksUpToDate", "bool"],
|
||||
["ScaleCrop", "ScaleCrop", "bool"],
|
||||
["HeadingPairs", "HeadingPairs", "raw"],
|
||||
["TitlesOfParts", "TitlesOfParts", "raw"],
|
||||
];
|
||||
|
||||
XMLNS.EXT_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
|
||||
RELS.EXT_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
|
||||
|
||||
function parse_ext_props(data, p) {
|
||||
var q = {}; if(!p) p = {};
|
||||
|
||||
EXT_PROPS.forEach(function(f) {
|
||||
switch(f[2]) {
|
||||
case "string": p[f[1]] = (data.match(matchtag(f[0]))||[])[1]; break;
|
||||
case "bool": p[f[1]] = (data.match(matchtag(f[0]))||[])[1] === "true"; break;
|
||||
case "raw":
|
||||
var cur = data.match(new RegExp("<" + f[0] + "[^>]*>(.*)<\/" + f[0] + ">"));
|
||||
if(cur && cur.length > 0) q[f[1]] = cur[1];
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if(q.HeadingPairs && q.TitlesOfParts) {
|
||||
var v = parseVector(q.HeadingPairs);
|
||||
var j = 0, widx = 0;
|
||||
for(var i = 0; i !== v.length; ++i) {
|
||||
switch(v[i].v) {
|
||||
case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
|
||||
case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
|
||||
}
|
||||
}
|
||||
var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); });
|
||||
p.SheetNames = parts.slice(widx, widx + p.Worksheets);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
var EXT_PROPS_XML_ROOT = writextag('Properties', null, {
|
||||
'xmlns': XMLNS.EXT_PROPS,
|
||||
'xmlns:vt': XMLNS.vt
|
||||
});
|
||||
|
||||
function write_ext_props(cp, opts) {
|
||||
var o = [], p = {}, W = writextag;
|
||||
o.push(XML_HEADER);
|
||||
o.push(EXT_PROPS_XML_ROOT);
|
||||
if(!cp) return o.join("");
|
||||
|
||||
EXT_PROPS.forEach(function(f) {
|
||||
if(typeof cp[f[1]] === 'undefined') return;
|
||||
var v;
|
||||
switch(f[2]) {
|
||||
case 'string': v = cp[f[1]]; break;
|
||||
case 'bool': v = cp[f[1]] ? 'true' : 'false'; break;
|
||||
}
|
||||
if(typeof v !== 'undefined') o.push(W(f[0], v));
|
||||
});
|
||||
|
||||
/* TODO: HeadingPairs, TitlesOfParts */
|
||||
o.push(W('HeadingPairs', W('vt:vector', W('vt:variant', '<vt:lpstr>Worksheets</vt:lpstr>')+W('vt:variant', W('vt:i4', String(cp.Worksheets))), {size:2, baseType:"variant"})));
|
||||
o.push(W('TitlesOfParts', W('vt:vector', cp.SheetNames.map(function(s) { return "<vt:lpstr>" + s + "</vt:lpstr>"; }).join(""), {size: cp.Worksheets, baseType:"lpstr"})));
|
||||
if(o.length>2){ o.push('</Properties>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
70
bits/45_custprops.js
Normal file
70
bits/45_custprops.js
Normal file
@ -0,0 +1,70 @@
|
||||
/* 15.2.12.2 Custom File Properties Part */
|
||||
XMLNS.CUST_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
|
||||
RELS.CUST_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
|
||||
|
||||
function parse_cust_props(data, opts) {
|
||||
var p = {}, name;
|
||||
data.match(/<[^>]+>([^<]*)/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
switch(y[0]) {
|
||||
case '<?xml': break;
|
||||
case '<Properties':
|
||||
if(y.xmlns !== XMLNS.CUST_PROPS) throw "unrecognized xmlns " + y.xmlns;
|
||||
if(y.xmlnsvt && y.xmlnsvt !== XMLNS.vt) throw "unrecognized vt " + y.xmlnsvt;
|
||||
break;
|
||||
case '<property': name = y.name; break;
|
||||
case '</property>': name = null; break;
|
||||
default: if (x.indexOf('<vt:') === 0) {
|
||||
var toks = x.split('>');
|
||||
var type = toks[0].substring(4), text = toks[1];
|
||||
/* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */
|
||||
switch(type) {
|
||||
case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr':
|
||||
p[name] = unescapexml(text);
|
||||
break;
|
||||
case 'bool':
|
||||
p[name] = parsexmlbool(text, '<vt:bool>');
|
||||
break;
|
||||
case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint':
|
||||
p[name] = parseInt(text, 10);
|
||||
break;
|
||||
case 'r4': case 'r8': case 'decimal':
|
||||
p[name] = parseFloat(text);
|
||||
break;
|
||||
case 'filetime': case 'date':
|
||||
p[name] = new Date(text);
|
||||
break;
|
||||
case 'cy': case 'error':
|
||||
p[name] = unescapexml(text);
|
||||
break;
|
||||
default:
|
||||
console.warn('Unexpected', x, type, toks);
|
||||
}
|
||||
} else if(x.substr(0,2) === "</") {
|
||||
} else if(opts.WTF) throw new Error(x);
|
||||
}
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
var CUST_PROPS_XML_ROOT = writextag('Properties', null, {
|
||||
'xmlns': XMLNS.CUST_PROPS,
|
||||
'xmlns:vt': XMLNS.vt
|
||||
});
|
||||
|
||||
function write_cust_props(cp, opts) {
|
||||
var o = [], p = {};
|
||||
o.push(XML_HEADER);
|
||||
o.push(CUST_PROPS_XML_ROOT);
|
||||
if(!cp) return o.join("");
|
||||
var pid = 1;
|
||||
keys(cp).forEach(function(k) { ++pid;
|
||||
o.push(writextag('property', write_vt(cp[k]), {
|
||||
'fmtid': '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}',
|
||||
'pid': pid,
|
||||
'name': k
|
||||
}));
|
||||
});
|
||||
if(o.length>2){ o.push('</Properties>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
@ -19,7 +19,7 @@ var CS2CP = {
|
||||
222: 874, /* THAI */
|
||||
238: 1250, /* EASTEUROPE */
|
||||
255: 1252, /* OEM */
|
||||
69: 6969 /* MISC */
|
||||
69: 6969 /* MISC */
|
||||
};
|
||||
|
||||
/* Parse a list of <r> tags */
|
||||
@ -169,3 +169,18 @@ var parse_sst_xml = function(data, opts) {
|
||||
return s;
|
||||
};
|
||||
|
||||
RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
|
||||
|
||||
var write_sst_xml = function(sst, opts) {
|
||||
if(!opts.bookSST) return "";
|
||||
var o = [];
|
||||
o.push(XML_HEADER);
|
||||
o.push(writextag('sst', null, {
|
||||
xmlns: XMLNS.main[0],
|
||||
count: sst.Count,
|
||||
uniqueCount: sst.Unique
|
||||
}));
|
||||
sst.forEach(function(s) { o.push("<si>" + (s.r ? s.r : "<t>" + escapexml(s.t) + "</t>") + "</si>"); });
|
||||
if(o.length>2){ o.push('</sst>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
};
|
||||
|
@ -20,3 +20,5 @@ var parse_sst_bin = function(data, opts) {
|
||||
});
|
||||
return s;
|
||||
};
|
||||
|
||||
var write_sst_bin = function(sst, opts) { };
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 18.8.31 numFmts CT_NumFmts */
|
||||
function parseNumFmts(t, opts) {
|
||||
function parse_numFmts(t, opts) {
|
||||
styles.NumberFmt = [];
|
||||
for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
@ -15,8 +15,21 @@ function parseNumFmts(t, opts) {
|
||||
});
|
||||
}
|
||||
|
||||
function write_numFmts(NF, opts) {
|
||||
var o = [];
|
||||
o.push("<numFmts>");
|
||||
[[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) {
|
||||
for(var i = r[0]; i <= r[1]; ++i) if(NF[i])
|
||||
o.push(writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])}));
|
||||
});
|
||||
o.push("</numFmts>");
|
||||
if(o.length === 2) return "";
|
||||
o[0] = writextag('numFmts', null, { count:o.length-2 }).replace("/>", ">");
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
/* 18.8.10 cellXfs CT_CellXfs */
|
||||
function parseCXfs(t, opts) {
|
||||
function parse_cellXfs(t, opts) {
|
||||
styles.CellXf = [];
|
||||
t[0].match(/<[^>]*>/g).forEach(function(x) {
|
||||
var y = parsexmltag(x);
|
||||
@ -42,13 +55,23 @@ function parseCXfs(t, opts) {
|
||||
});
|
||||
}
|
||||
|
||||
function write_cellXfs(cellXfs) {
|
||||
var o = [];
|
||||
o.push(writextag('cellXfs',null));
|
||||
cellXfs.forEach(function(c) { o.push(writextag('xf', null, c)); });
|
||||
o.push("</cellXfs>");
|
||||
if(o.length === 2) return "";
|
||||
o[0] = writextag('cellXfs',null, {count:o.length-2}).replace("/>",">");
|
||||
return o.join("");
|
||||
}
|
||||
|
||||
/* 18.8 Styles CT_Stylesheet*/
|
||||
function parse_sty_xml(data, opts) {
|
||||
/* 18.8.39 styleSheet CT_Stylesheet */
|
||||
var t;
|
||||
|
||||
/* numFmts CT_NumFmts ? */
|
||||
if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parseNumFmts(t, opts);
|
||||
if((t=data.match(/<numFmts([^>]*)>.*<\/numFmts>/))) parse_numFmts(t, opts);
|
||||
|
||||
/* fonts CT_Fonts ? */
|
||||
/* fills CT_Fills ? */
|
||||
@ -56,7 +79,7 @@ function parse_sty_xml(data, opts) {
|
||||
/* cellStyleXfs CT_CellStyleXfs ? */
|
||||
|
||||
/* cellXfs CT_CellXfs ? */
|
||||
if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parseCXfs(t, opts);
|
||||
if((t=data.match(/<cellXfs([^>]*)>.*<\/cellXfs>/))) parse_cellXfs(t, opts);
|
||||
|
||||
/* dxfs CT_Dxfs ? */
|
||||
/* tableStyles CT_TableStyles ? */
|
||||
@ -65,3 +88,28 @@ function parse_sty_xml(data, opts) {
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
var STYLES_XML_ROOT = writextag('styleSheet', null, {
|
||||
'xmlns': XMLNS.main[0],
|
||||
'xmlns:vt': XMLNS.vt
|
||||
});
|
||||
|
||||
RELS.STY = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
||||
|
||||
function write_sty_xml(wb, opts) {
|
||||
var o = [], p = {}, W = writextag, w;
|
||||
o.push(XML_HEADER);
|
||||
o.push(STYLES_XML_ROOT);
|
||||
if((w = write_numFmts(wb.SSF))) o.push(w);
|
||||
o.push('<fonts count="1"><font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts>');
|
||||
o.push('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
|
||||
o.push('<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>');
|
||||
o.push('<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>');
|
||||
if((w = write_cellXfs(opts.cellXfs))) o.push(w);
|
||||
o.push('<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>');
|
||||
o.push('<dxfs count="0"/>');
|
||||
o.push('<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4"/>');
|
||||
|
||||
if(o.length>2){ o.push('</styleSheet>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
}
|
||||
|
3
bits/59_theme.js
Normal file
3
bits/59_theme.js
Normal file
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@ function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
|
||||
var comments=parse_cmnt(getzipdata(zip, canonicalpath.replace(/^\//,''), true), canonicalpath, opts);
|
||||
if(!comments || !comments.length) continue;
|
||||
// find the sheets targeted by these comments
|
||||
var sheetNames = Object.keys(sheets);
|
||||
var sheetNames = keys(sheets);
|
||||
for(var j = 0; j != sheetNames.length; ++j) {
|
||||
var sheetName = sheetNames[j];
|
||||
var rels = sheetRels[sheetName];
|
||||
|
@ -1,3 +1,23 @@
|
||||
var strs = {}; // shared strings
|
||||
var _ssfopts = {}; // spreadsheet formatting options
|
||||
|
||||
RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
|
||||
|
||||
function get_sst_id(sst, str) {
|
||||
for(var i = 0; i != sst.length; ++i) if(sst[i].t === str) { sst.Count ++; return i; }
|
||||
sst[sst.length] = {t:str}; sst.Count ++; sst.Unique ++; return sst.length-1;
|
||||
}
|
||||
|
||||
function get_cell_style(styles, cell, opts) {
|
||||
var z = opts.revssf[cell.z];
|
||||
for(var i = 0; i != styles.length; ++i) if(styles[i].numFmtId === z) return i;
|
||||
styles[styles.length] = {
|
||||
numFmtId:z,
|
||||
fontId:0,
|
||||
fillId:0,
|
||||
borderId:0,
|
||||
xfId:0,
|
||||
applyNumberFormat:1
|
||||
};
|
||||
return styles.length-1;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ function parse_ws_xml(data, opts, rels) {
|
||||
var mergecells = [];
|
||||
if(data.match(/<\/mergeCells>/)) {
|
||||
var merges = data.match(/<mergeCell ref="([A-Z0-9:]+)"\s*\/>/g);
|
||||
mergecells = merges.map(function(range) {
|
||||
mergecells = merges.map(function(range) {
|
||||
return decode_range(/<mergeCell ref="([A-Z0-9:]+)"\s*\/>/.exec(range)[1]);
|
||||
});
|
||||
}
|
||||
@ -123,3 +123,55 @@ function parse_ws_xml(data, opts, rels) {
|
||||
return s;
|
||||
}
|
||||
|
||||
var WS_XML_ROOT = writextag('worksheet', null, {
|
||||
'xmlns': XMLNS.main[0],
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
var write_ws_xml_cell = function(cell, ref, ws, opts, idx, wb) {
|
||||
var v = writextag('v', escapexml(String(cell.v))), o = {r:ref};
|
||||
if(cell.z) o.s = get_cell_style(opts.cellXfs, cell, opts);
|
||||
/* TODO: cell style */
|
||||
if(typeof cell.v === 'undefined') return "";
|
||||
switch(cell.t) {
|
||||
case 's': case 'str': {
|
||||
if(opts.bookSST) {
|
||||
v = writextag('v', String(get_sst_id(opts.Strings, cell.v)));
|
||||
o.t = "s"; return writextag('c', v, o);
|
||||
} else { o.t = "str"; return writextag('c', v, o); }
|
||||
} break;
|
||||
case 'n': o.t = "n"; return writextag('c', v, o);
|
||||
case 'b': o.t = "b"; return writextag('c', v, o);
|
||||
case 'e': o.t = "e"; return writextag('c', v, o);
|
||||
}
|
||||
};
|
||||
|
||||
var write_ws_xml_data = function(ws, opts, idx, wb) {
|
||||
var o = [], r = [], range = utils.decode_range(ws['!ref']), cell, ref;
|
||||
for(var R = range.s.r; R <= range.e.r; ++R) {
|
||||
r = [];
|
||||
for(var C = range.s.c; C <= range.e.c; ++C) {
|
||||
ref = utils.encode_cell({c:C, r:R});
|
||||
if(!ws[ref]) continue;
|
||||
if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb))) r.push(cell);
|
||||
}
|
||||
if(r.length) o.push(writextag('row', r.join(""), {r:encode_row(R)}));
|
||||
}
|
||||
return o.join("");
|
||||
};
|
||||
|
||||
var write_ws_xml = function(idx, opts, wb) {
|
||||
var o = [], s = wb.SheetNames[idx], ws = wb.Sheets[s] || {}, sidx = 0, rdata = "";
|
||||
o.push(XML_HEADER);
|
||||
o.push(WS_XML_ROOT);
|
||||
o.push(writextag('dimension', null, {'ref': ws['!ref'] || 'A1'}));
|
||||
|
||||
sidx = o.length;
|
||||
o.push(writextag('sheetData', null));
|
||||
if(ws['!ref']) rdata = write_ws_xml_data(ws, opts, idx, wb);
|
||||
if(rdata.length) o.push(rdata);
|
||||
if(o.length>sidx+1){ o.push('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
|
||||
|
||||
if(o.length>2){ o.push('</worksheet>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
};
|
||||
|
@ -317,7 +317,7 @@ var parse_ws_bin = function(data, opts, rels) {
|
||||
default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
|
||||
}
|
||||
}, opts);
|
||||
if(!s["!ref"] && ref) s["!ref"] = encode_range(ref);
|
||||
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(opts.sheetRows && s["!ref"]) {
|
||||
var tmpref = decode_range(s["!ref"]);
|
||||
if(opts.sheetRows < +tmpref.e.r) {
|
||||
@ -334,3 +334,4 @@ var parse_ws_bin = function(data, opts, rels) {
|
||||
return s;
|
||||
};
|
||||
|
||||
var write_ws_bin = function(wb, opts, rels) {};
|
||||
|
@ -1,10 +1,3 @@
|
||||
var XMLNS_WB = [
|
||||
'http://purl.oclc.org/ooxml/spreadsheetml/main',
|
||||
'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
|
||||
'http://schemas.microsoft.com/office/excel/2006/main',
|
||||
'http://schemas.microsoft.com/office/excel/2006/2'
|
||||
];
|
||||
|
||||
/* 18.2 Workbook */
|
||||
function parse_wb_xml(data) {
|
||||
var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
|
||||
@ -110,7 +103,7 @@ function parse_wb_xml(data) {
|
||||
case '</mc:AlternateContent>': pass=false; break;
|
||||
}
|
||||
});
|
||||
if(XMLNS_WB.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
|
||||
if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
|
||||
|
||||
var z;
|
||||
/* defaults */
|
||||
@ -125,3 +118,28 @@ function parse_wb_xml(data) {
|
||||
return wb;
|
||||
}
|
||||
|
||||
var WB_XML_ROOT = writextag('workbook', null, {
|
||||
'xmlns': XMLNS.main[0],
|
||||
//'xmlns:mx': XMLNS.mx,
|
||||
//'xmlns:s': XMLNS.main[0],
|
||||
'xmlns:r': XMLNS.r
|
||||
});
|
||||
|
||||
var write_wb_xml = function(wb, opts) {
|
||||
var o = [];
|
||||
o.push(XML_HEADER);
|
||||
o.push(WB_XML_ROOT);
|
||||
/* TODO: put this somewhere else */
|
||||
var date1904 = "false";
|
||||
try { date1904 = parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false"; } catch(e) { date1904 = "false"; }
|
||||
o.push(writextag('workbookPr', null, {date1904:date1904}));
|
||||
o.push("<sheets>");
|
||||
var i = 1;
|
||||
wb.SheetNames.forEach(function(s) {
|
||||
o.push(writextag('sheet',null,{name:s, sheetId:String(i), "r:id":"rId"+i}));
|
||||
++i;
|
||||
});
|
||||
o.push("</sheets>");
|
||||
if(o.length>2){ o.push('</workbook>'); o[1]=o[1].replace("/>",">"); }
|
||||
return o.join("");
|
||||
};
|
||||
|
@ -69,3 +69,7 @@ var parse_wb_bin = function(data, opts) {
|
||||
|
||||
return wb;
|
||||
};
|
||||
|
||||
var write_wb_bin = function(wb, opts) {
|
||||
|
||||
};
|
||||
|
@ -1,23 +1,48 @@
|
||||
function parse_wb(data, name, opts) {
|
||||
return name.substr(-4)===".bin" ? parse_wb_bin(data, opts) : parse_wb_xml(data, opts);
|
||||
return (name.substr(-4)===".bin" ? parse_wb_bin : parse_wb_xml)(data, opts);
|
||||
}
|
||||
|
||||
function parse_ws(data, name, opts, rels) {
|
||||
return name.substr(-4)===".bin" ? parse_ws_bin(data, opts, rels) : parse_ws_xml(data, opts, rels);
|
||||
return (name.substr(-4)===".bin" ? parse_ws_bin : parse_ws_xml)(data, opts, rels);
|
||||
}
|
||||
|
||||
function parse_sty(data, name, opts) {
|
||||
return name.substr(-4)===".bin" ? parse_sty_bin(data, opts) : parse_sty_xml(data, opts);
|
||||
return (name.substr(-4)===".bin" ? parse_sty_bin : parse_sty_xml)(data, opts);
|
||||
}
|
||||
|
||||
function parse_sst(data, name, opts) {
|
||||
return name.substr(-4)===".bin" ? parse_sst_bin(data, opts) : parse_sst_xml(data, opts);
|
||||
return (name.substr(-4)===".bin" ? parse_sst_bin : parse_sst_xml)(data, opts);
|
||||
}
|
||||
|
||||
function parse_cmnt(data, name, opts) {
|
||||
return name.substr(-4)===".bin" ? parse_comments_bin(data, opts) : parse_comments_xml(data, opts);
|
||||
return (name.substr(-4)===".bin" ? parse_comments_bin : parse_comments_xml)(data, opts);
|
||||
}
|
||||
|
||||
function parse_cc(data, name, opts) {
|
||||
return name.substr(-4)===".bin" ? parse_cc_bin(data, opts) : parse_cc_xml(data, opts);
|
||||
return (name.substr(-4)===".bin" ? parse_cc_bin : parse_cc_xml)(data, opts);
|
||||
}
|
||||
|
||||
function write_wb(wb, name, opts) {
|
||||
return (name.substr(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
|
||||
}
|
||||
|
||||
function write_ws(data, name, opts, wb) {
|
||||
return (name.substr(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
|
||||
}
|
||||
|
||||
function write_sty(data, name, opts) {
|
||||
return (name.substr(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
|
||||
}
|
||||
|
||||
function write_sst(data, name, opts) {
|
||||
return (name.substr(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
|
||||
}
|
||||
/*
|
||||
function write_cmnt(data, name, opts) {
|
||||
return (name.substr(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
|
||||
}
|
||||
|
||||
function write_cc(data, name, opts) {
|
||||
return (name.substr(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
|
||||
}
|
||||
*/
|
||||
|
@ -1,22 +1,34 @@
|
||||
function fixopts(opts) {
|
||||
var defaults = [
|
||||
['cellNF', false], /* emit cell number format string as .z */
|
||||
['cellHTML', true], /* emit html string as .h */
|
||||
['cellFormula', true], /* emit formulae as .f */
|
||||
|
||||
['sheetStubs', false], /* emit empty cells */
|
||||
['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
|
||||
|
||||
['bookDeps', false], /* parse calculation chains */
|
||||
['bookSheets', false], /* only try to get sheet names (no Sheets) */
|
||||
['bookProps', false], /* only try to get properties (no Sheets) */
|
||||
['bookFiles', false], /* include raw file structure (keys, files) */
|
||||
['bookVBA', false], /* include vba raw data (vbaraw) */
|
||||
|
||||
['WTF', false] /* WTF mode (throws errors) */
|
||||
];
|
||||
defaults.forEach(function(d) {
|
||||
if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1];
|
||||
if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
|
||||
});
|
||||
function fix_opts(defaults) {
|
||||
return function(opts) {
|
||||
defaults.forEach(function(d) {
|
||||
if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1];
|
||||
if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var fix_read_opts = fix_opts([
|
||||
['cellNF', false], /* emit cell number format string as .z */
|
||||
['cellHTML', true], /* emit html string as .h */
|
||||
['cellFormula', true], /* emit formulae as .f */
|
||||
|
||||
['sheetStubs', false], /* emit empty cells */
|
||||
['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
|
||||
|
||||
['bookDeps', false], /* parse calculation chains */
|
||||
['bookSheets', false], /* only try to get sheet names (no Sheets) */
|
||||
['bookProps', false], /* only try to get properties (no Sheets) */
|
||||
['bookFiles', false], /* include raw file structure (keys, files) */
|
||||
['bookVBA', false], /* include vba raw data (vbaraw) */
|
||||
|
||||
['WTF', false] /* WTF mode (throws errors) */
|
||||
]);
|
||||
|
||||
|
||||
var fix_write_opts = fix_opts([
|
||||
['bookSST', false], /* Generate Shared String Table */
|
||||
|
||||
['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
|
||||
|
||||
['WTF', false] /* WTF mode (throws errors) */
|
||||
]);
|
||||
|
@ -1,10 +1,10 @@
|
||||
function parseZip(zip, opts) {
|
||||
function parse_zip(zip, opts) {
|
||||
make_ssf(SSF);
|
||||
opts = opts || {};
|
||||
fixopts(opts);
|
||||
fix_read_opts(opts);
|
||||
reset_cp();
|
||||
var entries = Object.keys(zip.files);
|
||||
var keys = entries.filter(function(x){return x.substr(-1) != '/';}).sort();
|
||||
var dir = parseCT(getzipdata(zip, '[Content_Types].xml'), opts);
|
||||
var entries = keys(zip.files).filter(function(x){return x.substr(-1) != '/';}).sort();
|
||||
var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
|
||||
var xlsb = false;
|
||||
var sheets, binname;
|
||||
if(dir.workbooks.length === 0) {
|
||||
@ -19,7 +19,7 @@ function parseZip(zip, opts) {
|
||||
}
|
||||
|
||||
if(!opts.bookSheets && !opts.bookProps) {
|
||||
strs = {};
|
||||
strs = [];
|
||||
if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts);
|
||||
|
||||
styles = {};
|
||||
@ -29,17 +29,21 @@ function parseZip(zip, opts) {
|
||||
var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);
|
||||
|
||||
var props = {}, propdata = "";
|
||||
try {
|
||||
propdata = dir.coreprops.length !== 0 ? getzipdata(zip, dir.coreprops[0].replace(/^\//,'')) : "";
|
||||
propdata += dir.extprops.length !== 0 ? getzipdata(zip, dir.extprops[0].replace(/^\//,'')) : "";
|
||||
props = propdata !== "" ? parseProps(propdata) : {};
|
||||
} catch(e) { }
|
||||
|
||||
if(dir.coreprops.length !== 0) {
|
||||
propdata = getzipdata(zip, dir.coreprops[0].replace(/^\//,''), true);
|
||||
if(propdata) props = parse_core_props(propdata);
|
||||
if(dir.extprops.length !== 0) {
|
||||
propdata = getzipdata(zip, dir.extprops[0].replace(/^\//,''), true);
|
||||
if(propdata) parse_ext_props(propdata, props);
|
||||
}
|
||||
}
|
||||
|
||||
var custprops = {};
|
||||
if(!opts.bookSheets || opts.bookProps) {
|
||||
if (dir.custprops.length !== 0) {
|
||||
propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true);
|
||||
if(propdata) custprops = parseCustomProps(propdata);
|
||||
if(propdata) custprops = parse_cust_props(propdata, opts);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +64,6 @@ function parseZip(zip, opts) {
|
||||
var sheetRels = {};
|
||||
var path, relsPath;
|
||||
if(!props.Worksheets) {
|
||||
/* Google Docs doesn't generate the appropriate metadata, so we impute: */
|
||||
var wbsheets = wb.Sheets;
|
||||
props.Worksheets = wbsheets.length;
|
||||
props.SheetNames = [];
|
||||
@ -76,7 +79,7 @@ function parseZip(zip, opts) {
|
||||
path = 'xl/worksheets/sheet'+(i+1-nmode)+(xlsb?'.bin':'.xml');
|
||||
path = path.replace(/sheet0\./,"sheet.");
|
||||
relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
|
||||
sheetRels[props.SheetNames[i]]=parseRels(getzipdata(zip, relsPath, true), path);
|
||||
sheetRels[props.SheetNames[i]]=parse_rels(getzipdata(zip, relsPath, true), path);
|
||||
sheets[props.SheetNames[i]]=parse_ws(getzipdata(zip, path),path,opts,sheetRels[props.SheetNames[i]]);
|
||||
} catch(e) { if(opts.WTF) throw e; }
|
||||
}
|
||||
@ -93,9 +96,10 @@ function parseZip(zip, opts) {
|
||||
SheetNames: props.SheetNames,
|
||||
Strings: strs,
|
||||
Styles: styles,
|
||||
SSF: SSF.get_table()
|
||||
};
|
||||
if(opts.bookFiles) {
|
||||
out.keys = keys;
|
||||
out.keys = entries;
|
||||
out.files = zip.files;
|
||||
}
|
||||
if(opts.bookVBA) {
|
||||
|
85
bits/86_writezip.js
Normal file
85
bits/86_writezip.js
Normal file
@ -0,0 +1,85 @@
|
||||
function add_rels(rels, rId, f, type, relobj) {
|
||||
if(!relobj) relobj = {};
|
||||
if(!rels['!id']) rels['!id'] = {};
|
||||
relobj.Id = 'rId' + rId;
|
||||
relobj.Type = type;
|
||||
relobj.Target = f;
|
||||
if(rels['!id'][relobj.Id]) throw new Error("Cannot rewrite rId " + rId);
|
||||
rels['!id'][relobj.Id] = relobj;
|
||||
rels[('/' + relobj.Target).replace("//","/")] = relobj;
|
||||
}
|
||||
|
||||
function write_zip(wb, opts) {
|
||||
if(wb && wb.SSF) {
|
||||
make_ssf(SSF); SSF.load_table(wb.SSF);
|
||||
opts.revssf = evert(wb.SSF); opts.revssf[wb.SSF[65535]] = 0;
|
||||
}
|
||||
opts.rels = {}; opts.wbrels = {};
|
||||
opts.Strings = []; opts.Strings.Count = 0; opts.Strings.Unique = 0;
|
||||
var wbext = opts.bookType == "xlsb" ? "bin" : "xml";
|
||||
var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
|
||||
coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
|
||||
TODO:[], rels:[], xmlns: "" };
|
||||
fix_write_opts(opts = opts || {});
|
||||
var zip = new jszip();
|
||||
var f = "", rId = 0;
|
||||
|
||||
opts.cellXfs = [];
|
||||
|
||||
f = "docProps/core.xml";
|
||||
zip.file(f, write_core_props(wb.Props, opts));
|
||||
ct.coreprops.push(f);
|
||||
add_rels(opts.rels, 3, f, RELS.CORE_PROPS);
|
||||
|
||||
f = "docProps/app.xml";
|
||||
wb.Props.SheetNames = wb.SheetNames;
|
||||
wb.Props.Worksheets = wb.SheetNames.length;
|
||||
zip.file(f, write_ext_props(wb.Props, opts));
|
||||
ct.extprops.push(f);
|
||||
add_rels(opts.rels, 4, f, RELS.EXT_PROPS);
|
||||
|
||||
if(wb.Custprops !== wb.Props) { /* TODO: fix xlsjs */
|
||||
f = "docProps/custom.xml";
|
||||
zip.file(f, write_cust_props(wb.Custprops, opts));
|
||||
ct.custprops.push(f);
|
||||
add_rels(opts.rels, 5, f, RELS.CUST_PROPS);
|
||||
}
|
||||
|
||||
f = "xl/workbook." + wbext;
|
||||
zip.file(f, write_wb(wb, f, opts));
|
||||
ct.workbooks.push(f);
|
||||
add_rels(opts.rels, 1, f, RELS.WB);
|
||||
|
||||
wb.SheetNames.forEach(function(s, i) {
|
||||
rId = i+1; f = "xl/worksheets/sheet" + rId + "." + wbext;
|
||||
zip.file(f, write_ws(i, f, opts, wb));
|
||||
ct.sheets.push(f);
|
||||
add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS);
|
||||
});
|
||||
|
||||
if((opts.Strings||[]).length > 0) {
|
||||
f = "xl/sharedStrings." + wbext;
|
||||
zip.file(f, write_sst(opts.Strings, f, opts));
|
||||
ct.strs.push(f);
|
||||
add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST);
|
||||
}
|
||||
|
||||
/* TODO: something more intelligent with themes */
|
||||
|
||||
/* f = "xl/theme/theme1.xml"
|
||||
zip.file(f, write_theme());
|
||||
ct.themes.push(f);
|
||||
add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME);*/
|
||||
|
||||
/* TODO: something more intelligent with styles */
|
||||
|
||||
f = "xl/styles.xml";
|
||||
zip.file(f, write_sty(wb, f, opts));
|
||||
ct.styles.push(f);
|
||||
add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY);
|
||||
|
||||
zip.file("[Content_Types].xml", write_ct(ct, opts));
|
||||
zip.file('_rels/.rels', write_rels(opts.rels));
|
||||
zip.file('xl/_rels/workbook.xml.rels', write_rels(opts.wbrels));
|
||||
return zip;
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
function readSync(data, options) {
|
||||
function readSync(data, opts) {
|
||||
var zip, d = data;
|
||||
var o = options||{};
|
||||
switch((o.type||"base64")){
|
||||
case "file":
|
||||
if(typeof Buffer !== 'undefined') { zip=new jszip(d=_fs.readFileSync(data)); break; }
|
||||
d = _fs.readFileSync(data).toString('base64');
|
||||
/* falls through */
|
||||
var o = opts||{};
|
||||
if(!o.type) o.type = (typeof Buffer !== 'undefined' && data instanceof Buffer) ? "buffer" : "base64";
|
||||
switch(o.type) {
|
||||
case "base64": zip = new jszip(d, { base64:true }); break;
|
||||
case "binary": zip = new jszip(d, { base64:false }); break;
|
||||
case "buffer": zip = new jszip(d); break;
|
||||
case "file": zip=new jszip(d=_fs.readFileSync(data)); break;
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
return parseZip(zip, o);
|
||||
return parse_zip(zip, o);
|
||||
}
|
||||
|
||||
function readFileSync(data, options) {
|
||||
var o = options||{}; o.type = 'file';
|
||||
function readFileSync(data, opts) {
|
||||
var o = opts||{}; o.type = 'file';
|
||||
return readSync(data, o);
|
||||
}
|
||||
|
||||
|
22
bits/89_write.js
Normal file
22
bits/89_write.js
Normal file
@ -0,0 +1,22 @@
|
||||
function writeSync(wb, opts) {
|
||||
var o = opts||{};
|
||||
var z = write_zip(wb, o);
|
||||
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"}));
|
||||
default: throw new Error("Unrecognized type " + o.type);
|
||||
}
|
||||
}
|
||||
|
||||
function writeFileSync(wb, filename, opts) {
|
||||
var o = opts||{}; o.type = 'file';
|
||||
o.file = filename;
|
||||
switch(o.file.substr(-5).toLowerCase()) {
|
||||
case '.xlsm': o.bookType = 'xlsm'; break;
|
||||
case '.xlsb': o.bookType = 'xlsb'; break;
|
||||
}
|
||||
return writeSync(wb, o);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
XLSX.parseZip = parseZip;
|
||||
XLSX.parseZip = parse_zip;
|
||||
XLSX.read = readSync;
|
||||
XLSX.readFile = readFileSync;
|
||||
XLSX.write = writeSync;
|
||||
XLSX.writeFile = writeFileSync;
|
||||
XLSX.utils = utils;
|
||||
XLSX.SSF = SSF;
|
||||
|
2
dist/LICENSE
vendored
2
dist/LICENSE
vendored
@ -1,4 +1,4 @@
|
||||
Copyright (C) 2012-2014 SheetJS
|
||||
Copyright (C) 2012-2014 SheetJS
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
9
dist/xlsx.core.min.js
vendored
9
dist/xlsx.core.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.core.min.map
vendored
2
dist/xlsx.core.min.map
vendored
File diff suppressed because one or more lines are too long
9
dist/xlsx.full.min.js
vendored
9
dist/xlsx.full.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.full.min.map
vendored
2
dist/xlsx.full.min.map
vendored
File diff suppressed because one or more lines are too long
1353
dist/xlsx.js
vendored
1353
dist/xlsx.js
vendored
File diff suppressed because one or more lines are too long
7
dist/xlsx.min.js
vendored
7
dist/xlsx.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.min.map
vendored
2
dist/xlsx.min.map
vendored
File diff suppressed because one or more lines are too long
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "xlsx",
|
||||
"version": "0.6.2",
|
||||
"version": "0.7.0",
|
||||
"author": "sheetjs",
|
||||
"description": "XLSB / XLSX / XLSM (Excel 2007+ Spreadsheet) parser",
|
||||
"keywords": [ "xlsx", "xlsb", "xlsm", "office", "excel", "spreadsheet" ],
|
||||
"bin": {
|
||||
"xlsx2csv": "./bin/xlsx2csv.njs"
|
||||
"xlsx": "./bin/xlsx.njs"
|
||||
},
|
||||
"main": "./xlsx",
|
||||
"dependencies": {
|
||||
|
186
test.js
186
test.js
@ -3,9 +3,11 @@ var X;
|
||||
var fs = require('fs'), assert = require('assert');
|
||||
describe('source',function(){it('should load',function(){X=require('./');});});
|
||||
|
||||
var opts = {};
|
||||
var opts = {cellNF: true};
|
||||
if(process.env.WTF) opts.WTF = true;
|
||||
var ex = [".xlsb", ".xlsm", ".xlsx"];
|
||||
var fullex = [".xlsb", ".xlsm", ".xlsx"];
|
||||
var ex = fullex;
|
||||
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;});
|
||||
var exp = ex.map(function(x){ return x + ".pending"; });
|
||||
function test_file(x){return ex.indexOf(x.substr(-5))>=0||exp.indexOf(x.substr(-13))>=0;}
|
||||
@ -43,8 +45,9 @@ var paths = {
|
||||
var N1 = 'XLSX';
|
||||
var N2 = 'XLSB';
|
||||
|
||||
function parsetest(x, wb, full) {
|
||||
describe(x + ' should have all bits', function() {
|
||||
function parsetest(x, wb, full, ext) {
|
||||
ext = (ext ? " [" + ext + "]": "");
|
||||
describe(x + ext + ' should have all bits', function() {
|
||||
var sname = dir + '2011/' + x + '.sheetnames';
|
||||
it('should have all sheets', function() {
|
||||
wb.SheetNames.forEach(function(y) { assert(wb.Sheets[y], 'bad sheet ' + y); });
|
||||
@ -55,21 +58,21 @@ function parsetest(x, wb, full) {
|
||||
assert.equal(names, file);
|
||||
} : null);
|
||||
});
|
||||
describe(x + ' should generate CSV', function() {
|
||||
describe(x + ext + ' should generate CSV', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
it('#' + i + ' (' + ws + ')', function() {
|
||||
X.utils.make_csv(wb.Sheets[ws]);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(x + ' should generate JSON', function() {
|
||||
describe(x + ext + ' should generate JSON', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
it('#' + i + ' (' + ws + ')', function() {
|
||||
X.utils.sheet_to_row_object_array(wb.Sheets[ws]);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe(x + ' should generate formulae', function() {
|
||||
describe(x + ext + ' should generate formulae', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
it('#' + i + ' (' + ws + ')', function() {
|
||||
X.utils.get_formulae(wb.Sheets[ws]);
|
||||
@ -87,7 +90,7 @@ function parsetest(x, wb, full) {
|
||||
}
|
||||
return name;
|
||||
};
|
||||
describe(x + ' should generate correct CSV output', function() {
|
||||
describe(x + ext + ' should generate correct CSV output', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
var name = getfile(dir, x, i, ".csv");
|
||||
it('#' + i + ' (' + ws + ')', fs.existsSync(name) ? function() {
|
||||
@ -97,7 +100,7 @@ function parsetest(x, wb, full) {
|
||||
} : null);
|
||||
});
|
||||
});
|
||||
describe(x + ' should generate correct JSON output', function() {
|
||||
describe(x + ext + ' should generate correct JSON output', function() {
|
||||
wb.SheetNames.forEach(function(ws, i) {
|
||||
var rawjson = getfile(dir, x, i, ".rawjson");
|
||||
if(fs.existsSync(rawjson)) it('#' + i + ' (' + ws + ')', function() {
|
||||
@ -115,7 +118,7 @@ function parsetest(x, wb, full) {
|
||||
});
|
||||
});
|
||||
if(!fs.existsSync(dir + '2013/' + x + '.xlsb')) return;
|
||||
describe(x + '.xlsb from 2013', function() {
|
||||
describe(x + ext + '.xlsb from 2013', function() {
|
||||
it('should parse', function() {
|
||||
var wb = X.readFile(dir + '2013/' + x + '.xlsb', opts);
|
||||
});
|
||||
@ -127,6 +130,9 @@ describe('should parse test files', function() {
|
||||
it(x, x.substr(-8) == ".pending" ? null : function() {
|
||||
var wb = X.readFile(dir + x, opts);
|
||||
parsetest(x, wb, true);
|
||||
['.xlsx', '.xlsm'].forEach(function(ext, idx) {
|
||||
parsetest(x, X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,""), bookSST: idx != 1})), true, ext);
|
||||
});
|
||||
});
|
||||
});
|
||||
fileA.forEach(function(x) {
|
||||
@ -137,7 +143,7 @@ describe('should parse test files', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('options', function() {
|
||||
describe('parse options', function() {
|
||||
var html_cell_types = ['s'];
|
||||
before(function() {
|
||||
X = require('./');
|
||||
@ -308,9 +314,76 @@ describe('input formats', function() {
|
||||
X.read(fs.readFileSync(paths.cst1, 'base64'), {type: 'base64'});
|
||||
X.read(fs.readFileSync(paths.cst2, 'base64'), {type: 'base64'});
|
||||
});
|
||||
it('should read buffers', function() {
|
||||
X.read(fs.readFileSync(paths.cst1), {type: 'buffer'});
|
||||
X.read(fs.readFileSync(paths.cst2), {type: 'buffer'});
|
||||
});
|
||||
it('should throw if format is unknown', function() {
|
||||
assert.throws(function() { X.read(fs.readFileSync(paths.cst1), {type: 'dafuq'}); });
|
||||
assert.throws(function() { X.read(fs.readFileSync(paths.cst2), {type: 'dafuq'}); });
|
||||
});
|
||||
it('should infer buffer type', function() {
|
||||
X.read(fs.readFileSync(paths.cst1));
|
||||
X.read(fs.readFileSync(paths.cst2));
|
||||
});
|
||||
it('should default to base64 type', function() {
|
||||
assert.throws(function() { X.read(fs.readFileSync(paths.cst1, 'binary')); });
|
||||
assert.throws(function() { X.read(fs.readFileSync(paths.cst2, 'binary')); });
|
||||
X.read(fs.readFileSync(paths.cst1, 'base64'));
|
||||
X.read(fs.readFileSync(paths.cst2, 'base64'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('features', function() {
|
||||
describe('output formats', function() {
|
||||
var wb1, wb2;
|
||||
before(function() {
|
||||
X = require('./');
|
||||
wb1 = X.readFile(paths.cp1);
|
||||
wb2 = X.readFile(paths.cp2);
|
||||
});
|
||||
it('should write binary strings', function() {
|
||||
X.write(wb1, {type: 'binary'});
|
||||
X.write(wb2, {type: 'binary'});
|
||||
X.read(X.write(wb1, {type: 'binary'}), {type: 'binary'});
|
||||
X.read(X.write(wb2, {type: 'binary'}), {type: 'binary'});
|
||||
});
|
||||
it('should write base64 strings', function() {
|
||||
X.write(wb1, {type: 'base64'});
|
||||
X.write(wb2, {type: 'base64'});
|
||||
X.read(X.write(wb1, {type: 'base64'}), {type: 'base64'});
|
||||
X.read(X.write(wb2, {type: 'base64'}), {type: 'base64'});
|
||||
});
|
||||
it('should write buffers', function() {
|
||||
X.write(wb1, {type: 'buffer'});
|
||||
X.write(wb2, {type: 'buffer'});
|
||||
X.read(X.write(wb1, {type: 'buffer'}), {type: 'buffer'});
|
||||
X.read(X.write(wb2, {type: 'buffer'}), {type: 'buffer'});
|
||||
});
|
||||
it('should throw if format is unknown', function() {
|
||||
assert.throws(function() { X.write(wb1, {type: 'dafuq'}); });
|
||||
assert.throws(function() { X.write(wb2, {type: 'dafuq'}); });
|
||||
});
|
||||
});
|
||||
|
||||
function coreprop(wb) {
|
||||
assert.equal(wb.Props.Title, 'Example with properties');
|
||||
assert.equal(wb.Props.Subject, 'Test it before you code it');
|
||||
assert.equal(wb.Props.Author, 'Pony Foo');
|
||||
assert.equal(wb.Props.Manager, 'Despicable Drew');
|
||||
assert.equal(wb.Props.Company, 'Vector Inc');
|
||||
assert.equal(wb.Props.Category, 'Quirky');
|
||||
assert.equal(wb.Props.Keywords, 'example humor');
|
||||
assert.equal(wb.Props.Comments, 'some comments');
|
||||
assert.equal(wb.Props.LastAuthor, 'Hugues');
|
||||
}
|
||||
function custprop(wb) {
|
||||
assert.equal(wb.Custprops['I am a boolean'], true);
|
||||
assert.equal(wb.Custprops['Date completed'].toISOString(), '1967-03-09T16:30:00.000Z');
|
||||
assert.equal(wb.Custprops.Status, 2);
|
||||
assert.equal(wb.Custprops.Counter, -3.14);
|
||||
}
|
||||
|
||||
describe('parse features', function() {
|
||||
it('should have comment as part of cell properties', function(){
|
||||
var X = require('./');
|
||||
var sheet = 'Sheet1';
|
||||
@ -335,17 +408,6 @@ describe('features', function() {
|
||||
wb2 = X.readFile(paths.cp2);
|
||||
});
|
||||
|
||||
function coreprop(wb) {
|
||||
assert.equal(wb.Props.Company, 'Vector Inc');
|
||||
assert.equal(wb.Props.Creator, 'Pony Foo');
|
||||
}
|
||||
function custprop(wb) {
|
||||
assert.equal(wb.Custprops['I am a boolean'], true);
|
||||
assert.equal(wb.Custprops['Date completed'], '1967-03-09T16:30:00Z');
|
||||
assert.equal(wb.Custprops.Status, 2);
|
||||
assert.equal(wb.Custprops.Counter, -3.14);
|
||||
}
|
||||
|
||||
it(N1 + ' should parse core properties', function() { coreprop(wb1); });
|
||||
it(N2 + ' should parse core properties', function() { coreprop(wb2); });
|
||||
it(N1 + ' should parse custom properties', function() { custprop(wb1); });
|
||||
@ -434,17 +496,75 @@ describe('features', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid files', function() {
|
||||
it('should fail on passwords', function() {
|
||||
assert.throws(function() { X.readFile(dir + 'excel-reader-xlsx_error03.xlsx'); });
|
||||
describe('roundtrip features', function() {
|
||||
before(function() {
|
||||
X = require('./');
|
||||
});
|
||||
it('should fail on XLS files', function() {
|
||||
assert.throws(function() { X.readFile(dir + 'roo_type_excel.xlsx'); });
|
||||
describe('should parse core properties and custom properties', function() {
|
||||
var wb1, wb2, base = './tmp/cp';
|
||||
before(function() {
|
||||
wb1 = X.readFile(paths.cp1);
|
||||
wb2 = X.readFile(paths.cp2);
|
||||
fullex.forEach(function(p) {
|
||||
X.writeFile(wb1, base + '.xlsm' + p);
|
||||
X.writeFile(wb2, base + '.xlsb' + p);
|
||||
});
|
||||
});
|
||||
fullex.forEach(function(p) { ['.xlsm','.xlsb'].forEach(function(q) {
|
||||
it(q + p + ' should roundtrip core and custom properties', function() {
|
||||
var wb = X.readFile(base + q + p);
|
||||
coreprop(wb);
|
||||
custprop(wb);
|
||||
}); });
|
||||
});
|
||||
});
|
||||
it('should fail on ODS files', function() {
|
||||
assert.throws(function() { X.readFile(dir + 'roo_type_openoffice.xlsx');});
|
||||
});
|
||||
it('should fail on DOC files', function() {
|
||||
assert.throws(function() { X.readFile(dir + 'word_doc.doc');});
|
||||
/* the XLSJS require should not cause the test suite to fail */
|
||||
var XLSJS;
|
||||
try {
|
||||
XLSJS = require('xlsjs');
|
||||
var xls = XLSJS.readFile('./test_files/formula_stress_test.xls');
|
||||
var xml = XLSJS.readFile('./test_files/formula_stress_test.xls.xml');
|
||||
} catch(e) { return; }
|
||||
describe('xlsjs conversions', function() { [
|
||||
['XLS', 'formula_stress_test.xls'],
|
||||
['XML', 'formula_stress_test.xls.xml']
|
||||
].forEach(function(w) {
|
||||
it('should be able to write ' + w[0] + ' files from xlsjs', function() {
|
||||
var xls = XLSJS.readFile('./test_files/' + w[1]);
|
||||
X.writeFile(xls, './tmp/' + w[1] + '.xlsx');
|
||||
X.writeFile(xls, './tmp/' + w[1] + '.xlsb');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid files', function() {
|
||||
describe('parse', function() { [
|
||||
['passwords', 'excel-reader-xlsx_error03.xlsx'],
|
||||
['XLS files', 'roo_type_excel.xlsx'],
|
||||
['ODS files', 'roo_type_openoffice.xlsx'],
|
||||
['DOC files', 'word_doc.doc']
|
||||
].forEach(function(w) { it('should fail on ' + w[0], function() { assert.throws(function() { X.readFile(dir + w[1]); }); }); });
|
||||
});
|
||||
describe('write', function() {
|
||||
it('should pass', function() { X.write(X.readFile(paths.fst1), {type:'binary'}); });
|
||||
it('should pass if a sheet is missing', function() {
|
||||
var wb = X.readFile(paths.fst1); delete wb.Sheets[wb.SheetNames[0]];
|
||||
X.read(X.write(wb, {type:'binary'}), {type:'binary'});
|
||||
});
|
||||
it('should fail if SheetNames is missing', function() {
|
||||
var wb = X.readFile(paths.fst1);
|
||||
assert.throws(function() {
|
||||
delete wb.SheetNames;
|
||||
X.write(wb, {type:'binary'});
|
||||
});
|
||||
});
|
||||
it('should fail if Sheets is missing', function() {
|
||||
var wb = X.readFile(paths.fst1);
|
||||
assert.throws(function() {
|
||||
delete wb.Sheets;
|
||||
X.write(wb, {type:'binary'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1 +1,3 @@
|
||||
calendar_stress_test.xlsx
|
||||
large_strings.xlsb
|
||||
time_stress_test_1.xlsb
|
||||
|
@ -7,7 +7,6 @@ comments_stress_test.xlsb
|
||||
custom_properties.xlsb
|
||||
formula_stress_test.xlsb
|
||||
hyperlink_stress_test_2011.xlsb
|
||||
large_strings.xlsb
|
||||
merge_cells.xlsb
|
||||
named_ranges_2011.xlsb
|
||||
number_format.xlsb
|
||||
@ -15,7 +14,6 @@ pivot_table_named_range.xlsb
|
||||
pivot_table_test.xlsb
|
||||
rich_text_stress.xlsb
|
||||
text_and_numbers.xlsb
|
||||
time_stress_test_1.xlsb
|
||||
xlsx-stream-d-date-cell.xlsb
|
||||
2013/apachepoi_29982.xls.xlsb
|
||||
2013/apachepoi_43251.xls.xlsb
|
||||
|
1353
xlsx.js
1353
xlsx.js
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user