Skip to content

Commit

Permalink
version bump 0.7.0: Basic write support
Browse files Browse the repository at this point in the history
- 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
SheetJSDev committed May 16, 2014
1 parent b645f6e commit d15b81e
Show file tree
Hide file tree
Showing 47 changed files with 3,455 additions and 1,544 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
misc/coverage.html
tmp
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -25,6 +25,7 @@ init:

.PHONY: test mocha
test mocha: test.js
mkdir -p tmp
mocha -R spec

TESTFMT=$(patsubst %,test_%,$(FMT))
Expand All @@ -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 > $@

Expand Down
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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.

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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
39 changes: 28 additions & 11 deletions bin/xlsx2csv.njs → bin/xlsx.njs
Original file line number Diff line number Diff line change
Expand Up @@ -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)')
Expand All @@ -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+'/');
});
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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];

Expand All @@ -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);
2 changes: 1 addition & 1 deletion bits/01_version.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
XLSX.version = '0.6.2';
XLSX.version = '0.7.0';
13 changes: 13 additions & 0 deletions bits/30_jsutils.js
Original file line number Diff line number Diff line change
@@ -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;
}
43 changes: 36 additions & 7 deletions bits/36_xlsxutils.js → bits/36_xmlutils.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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;': "'",
Expand All @@ -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;
}

Expand Down Expand Up @@ -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.
5 changes: 5 additions & 0 deletions bits/38_recordhopper.js → bits/38_hoppers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
};
Loading

0 comments on commit d15b81e

Please sign in to comment.