Permalink
Browse files

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`)
  • Loading branch information...
1 parent b645f6e commit d15b81e0e9ee61ed720d16f4e25b34d951275ff4 @SheetJSDev SheetJSDev committed May 16, 2014
View
@@ -1,2 +1,3 @@
node_modules
misc/coverage.html
+tmp
View
@@ -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"
View
@@ -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
View
@@ -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.
View
@@ -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 > $@
View
@@ -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);
View
@@ -1 +1 @@
-XLSX.version = '0.6.2';
+XLSX.version = '0.7.0';
View
@@ -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 = {
'&quot;': '"',
'&apos;': "'",
@@ -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'
+];
File renamed without changes.
@@ -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 = [];
+};
Oops, something went wrong.

0 comments on commit d15b81e

Please sign in to comment.