version bump 0.11.7: VFP DBF write

- DBF writer (dBASE II + VFP extensions)
- updated CFB to 0.13.2
- BIFF8 XLS write VBA
This commit is contained in:
SheetJS 2017-10-27 12:25:54 -04:00
parent ed4348a6b6
commit 825830d1cd
34 changed files with 754 additions and 232 deletions

View File

@ -53,6 +53,7 @@ init: ## Initial setup for development
DISTHDR=misc/suppress_export.js
.PHONY: dist
dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
mkdir -p dist
<$(TARGET) sed "s/require('stream')/{}/g;s/require('....*')/undefined/g" > dist/$(TARGET)
cp LICENSE dist/
uglifyjs $(DISTHDR) dist/$(TARGET) $(UGLIFYOPTS) -o dist/$(LIB).min.js --source-map dist/$(LIB).min.map --preamble "$$(head -n 1 bits/00_header.js)"
@ -64,15 +65,17 @@ dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
.PHONY: dist-deps
dist-deps: ## Copy dependencies for distribution
mkdir -p dist
cp node_modules/codepage/dist/cpexcel.full.js dist/cpexcel.js
cp jszip.js dist/jszip.js
.PHONY: aux
aux: $(AUXTARGETS)
BYTEFILE=dist/xlsx.min.js dist/xlsx.{core,full}.min.js
.PHONY: bytes
bytes: ## Display minified and gzipped file sizes
for i in dist/xlsx.min.js dist/xlsx.{core,full}.min.js; do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
for i in $(BYTEFILE); do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
.PHONY: graph
graph: formats.png legend.png ## Rebuild format conversion graph
@ -176,7 +179,8 @@ old-lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks
.PHONY: tslint
tslint: $(TARGET) ## Run typescript checks
#@npm install dtslint typescript
@npm run-script dtslint
#@npm run-script dtslint
dtslint types
.PHONY: flow
flow: lint ## Run flow checker

View File

@ -1439,8 +1439,9 @@ if a sheet is visible is to check if the `Hidden` property is logical truth:
VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
property of the workbook object when the `bookVBA` option is `true`. They are
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
writers automatically insert the data blobs if it is present in the workbook.
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The supported format
writers automatically insert the data blobs if it is present in the workbook and
associate with the worksheet names.
<details>
<summary><b>Macrosheets</b> (click to show)</summary>
@ -1640,6 +1641,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | none | single | dBASE II + VFP Extensions (DBF) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
@ -1973,7 +1975,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | :o: |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | :o: | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
@ -2142,10 +2144,12 @@ Many older formats supported only one worksheet:
DBF is really a typed table format: each column can only hold one data type and
each record omits type information. The parser generates a header row and
inserts records starting at the second row of the worksheet.
inserts records starting at the second row of the worksheet. The writer makes
files compatible with Visual FoxPro extensions.
Multi-file extensions like external memos and tables are currently unsupported,
limited by the general ability to read arbitrary files in the web browser.
limited by the general ability to read arbitrary files in the web browser. The
reader understands DBF Level 7 extensions like DATETIME.
</details>
@ -2479,4 +2483,3 @@ granted by the Apache 2.0 License are reserved by the Original Author.
- ISO/IEC 29500:2012(E) "Information technology — Document description and processing languages — Office Open XML File Formats"
- Open Document Format for Office Applications Version 1.2 (29 September 2011)
- Worksheet File Format (From Lotus) December 1984

View File

@ -34,6 +34,7 @@ program
.option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
.option('-H, --html', 'emit HTML to <sheetname> or <file>.html')
.option('-D, --dif', 'emit DIF to <sheetname> or <file>.dif (Lotus DIF)')
.option('-U, --dbf', 'emit DBF to <sheetname> or <file>.dbf (MSVFP DBF)')
.option('-K, --sylk', 'emit SYLK to <sheetname> or <file>.slk (Excel SYLK)')
.option('-P, --prn', 'emit PRN to <sheetname> or <file>.prn (Lotus PRN)')
.option('-t, --txt', 'emit TXT to <sheetname> or <file>.txt (UTF-8 TSV)')
@ -186,6 +187,7 @@ try {
['prn', '.prn'],
['rtf', '.rtf'],
['txt', '.txt'],
['dbf', '.dbf'],
['dif', '.dif']
].forEach(function(m) { if(program[m[0]] || isfmt(m[1])) {
wopts.bookType = m[0];

View File

@ -1 +1 @@
XLSX.version = '0.11.6';
XLSX.version = '0.11.7';

View File

@ -38,7 +38,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.13.1';
exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@ -59,6 +59,8 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3;
var ssz = 512;
@ -356,9 +358,8 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
}
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs');
get_fs();
return parse(fs.readFileSync(filename), options);
}
@ -638,6 +639,7 @@ var consts = {
};
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o);
@ -652,7 +654,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options);
switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o));
}
@ -668,13 +670,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb);
var file = CFB.find(cfb, name);
if(!file) {
var fpath = cfb.FullPaths[0];
var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name;
else {
if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/");
}
file = ({name: filename(name)}/*:any*/);
file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb);

View File

@ -362,6 +362,14 @@ function parse_XLUnicodeString2(blob, length, opts) {
if(cch === 0) { blob.l++; return ""; }
return blob.read_shift(cch, 'sbcs-cont');
}
/* TODO: BIFF5 and lower, codepage awareness */
function write_XLUnicodeString(str, opts, o) {
if(!o) o = new_buf(3 + 2 * str.length);
o.write_shift(2, str.length);
o.write_shift(1, 1);
o.write_shift(31, str, 'utf16le');
return o;
}
/* [MS-XLS] 2.5.61 ControlInfo */
function parse_ControlInfo(blob, length, opts) {

View File

@ -193,9 +193,105 @@ function dbf_to_workbook(buf, opts)/*:Workbook*/ {
catch(e) { if(opts && opts.WTF) throw e; }
return ({SheetNames:[],Sheets:{}});
}
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
var ba = buf_array();
var aoa/*:AOA*/ = sheet_to_json(ws, {header:1, raw:true, cellDates:true});
var headers = aoa[0], data = aoa.slice(1);
var i = 0, j = 0, hcnt = 0, rlen = 1;
for(i = 0; i < headers.length; ++i) {
if(i == null) continue;
++hcnt;
if(typeof headers[i] !== 'string') throw new Error("DBF Invalid column name");
if(headers.indexOf(headers[i]) !== i) for(j=0; j<1024;++j)
if(headers.indexOf(headers[i] + "_" + j) == -1) { headers[i] += "_" + j; break; }
}
var range = safe_decode_range(ws['!ref']);
var coltypes = [];
for(i = 0; i <= range.e.c - range.s.c; ++i) {
var col/*:Array<any>*/ = [];
for(j=0; j < data.length; ++j) {
if(data[j][i] != null) col.push(data[j][i]);
}
if(col.length == 0 || headers[i] == null) { coltypes[i] = '?'; continue; }
var guess = '', _guess = '';
for(j = 0; j < col.length; ++j) {
switch(typeof col[j]) {
/* TODO: check if L2 compat is desired */
case 'number': _guess = 'B'; break;
case 'string': _guess = 'C'; break;
case 'boolean': _guess = 'L'; break;
case 'object': _guess = col[j] instanceof Date ? 'D' : 'C'; break;
default: _guess = 'C';
}
guess = guess && guess != _guess ? 'C' : _guess;
if(guess == 'C') break;
}
rlen += _RLEN[guess] || 0;
coltypes[i] = guess;
}
var h = ba.next(32);
h.write_shift(4, 0x13021130);
h.write_shift(4, data.length);
h.write_shift(2, 296 + 32 * hcnt);
h.write_shift(2, rlen);
for(i=0; i < 4; ++i) h.write_shift(4, 0);
h.write_shift(4, 0x00000300); // TODO: CP
for(i = 0, j = 0; i < headers.length; ++i) {
if(headers[i] == null) continue;
var hf = ba.next(32);
var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
hf.write_shift(1, _f, "sbcs");
hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
hf.write_shift(4, j);
hf.write_shift(1, _RLEN[coltypes[i]] || 0);
hf.write_shift(1, 0);
hf.write_shift(1, 0x02);
hf.write_shift(4, 0);
hf.write_shift(1, 0);
hf.write_shift(4, 0);
hf.write_shift(4, 0);
j += _RLEN[coltypes[i]] || 0;
}
var hb = ba.next(264);
hb.write_shift(4, 0x0000000D);
for(i=0; i < 65;++i) hb.write_shift(4, 0x00000000);
for(i=0; i < data.length; ++i) {
var rout = ba.next(rlen);
rout.write_shift(1, 0);
for(j=0; j<headers.length; ++j) {
if(headers[j] == null) continue;
switch(coltypes[j]) {
case 'L': rout.write_shift(1, data[i][j] == null ? 0x3F : data[i][j] ? 0x54 : 0x46); break;
case 'B': rout.write_shift(8, data[i][j]||0, 'f'); break;
case 'D':
if(!data[i][j]) rout.write_shift(8, "00000000", "sbcs");
else {
rout.write_shift(4, ("0000"+data[i][j].getFullYear()).slice(-4), "sbcs");
rout.write_shift(2, ("00"+(data[i][j].getMonth()+1)).slice(-2), "sbcs");
rout.write_shift(2, ("00"+data[i][j].getDate()).slice(-2), "sbcs");
} break;
case 'C':
var _s = String(data[i][j]||"");
rout.write_shift(1, _s, "sbcs");
for(hcnt=0; hcnt < 250-_s.length; ++hcnt) rout.write_shift(1, 0x20); break;
}
}
// data
}
ba.next(1).write_shift(1, 0x1A);
return ba.end();
}
return {
to_workbook: dbf_to_workbook,
to_sheet: dbf_to_sheet
to_sheet: dbf_to_sheet,
from_sheet: sheet_to_dbf
};
})();

View File

@ -7,3 +7,12 @@ function make_vba_xls(cfb/*:CFBContainer*/) {
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb/*:CFBContainer*/, vba/*:CFBContainer*/)/*:void*/ {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^/]*[/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}

View File

@ -660,7 +660,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/* empty */
} break;
case 'CodeName': {
/* empty */
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
if(!cur_sheet) Workbook.WBProps.codeName = val;
} break;
case 'GUIDTypeLib': {
/* empty */
@ -894,5 +895,6 @@ function write_xlscfb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:CFBContainer*/ {
}
CFB.utils.cfb_add(cfb, wbpath, write_biff_buf(wb, o));
// TODO: SI, DSI, CO
if(o.biff == 8 && wb.vbaraw) fill_vba_xls(cfb, CFB.read(wb.vbaraw, {type: typeof wb.vbaraw == "string" ? "binary" : "buffer"}));
return cfb;
}

View File

@ -138,6 +138,8 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
write_ws_biff8_cell(ba, cell, R, C, opts);
}
}
var cname = ((((wb||{}).Workbook||{}).Sheets||[])[idx]||{}).name||s;
write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
/* ... */
write_biff_rec(ba, "EOF");
return ba.end();
@ -157,6 +159,11 @@ function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
if(b8) write_biff_rec(A, "DSF", writeuint16(0));
write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
if(b8 && wb.vbaraw) {
write_biff_rec(A, "ObProj");
var cname = ((wb.Workbook||{}).WBProps||{}).codeName || "ThisWorkbook";
write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
}
write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
write_biff_rec(A, "WinProtect", writebool(false));
write_biff_rec(A, "Protect", writebool(false));

View File

@ -1,9 +1,13 @@
/* actual implementation elsewhere, wrappers are for read/write */
function write_sheet_index(wb/*:Workbook*/, sheet/*:?string*/)/*:number*/ {
if(!sheet) return 0;
var idx = wb.SheetNames.indexOf(sheet);
if(idx == -1) throw new Error("Sheet not found: " + sheet);
return idx;
}
function write_obj_str(factory/*:WriteObjStrFactory*/) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/)/*:string*/ {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
var idx = write_sheet_index(wb, o.sheet);
return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb);
};
}
@ -15,3 +19,6 @@ var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_rtf_str = write_obj_str(RTF);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
// $FlowIgnore
var write_dbf_buf = write_obj_str(DBF);

View File

@ -87,13 +87,15 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'txt': return write_stxt_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o, "\ufeff");
case 'dif': return write_string_type(write_dif_str(wb, o), o);
// $FlowIgnore
case 'dbf': return write_binary_type(write_dbf_buf(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'rtf': return write_string_type(write_rtf_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': if(!o.biff) o.biff = 2; /* falls through */
case 'biff3': if(!o.biff) o.biff = 3; /* falls through */
case 'biff4': if(!o.biff) o.biff = 4; return write_binary_type(write_biff_buf(wb, o), o);
case 'biff5': if(!o.biff) o.biff = 5; return write_cfb_type(wb, o);
case 'biff5': if(!o.biff) o.biff = 5; /* falls through */
case 'biff8':
case 'xls': if(!o.biff) o.biff = 8; return write_cfb_type(wb, o);
case 'xlsx':
@ -104,26 +106,17 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
}
}
function resolve_book_type(o/*?WriteFileOpts*/) {
if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
case '.xlml': o.bookType = 'xlml'; break;
case '.sylk': o.bookType = 'sylk'; break;
case '.html': o.bookType = 'html'; break;
case '.xls': o.bookType = 'biff8'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.rtf': o.bookType = 'rtf'; break;
case '.slk': o.bookType = 'sylk'; break;
case '.htm': o.bookType = 'html'; break;
}
function resolve_book_type(o/*:WriteFileOpts*/) {
if(o.bookType) return;
var _BT = {
"xls": "biff8",
"htm": "html",
"slk": "sylk",
"Sh33tJS": "WTF"
};
var ext = o.file.slice(o.file.lastIndexOf(".")).toLowerCase();
if(ext.match(/^\.[a-z]+$/)) o.bookType = ext.slice(1);
o.bookType = _BT[o.bookType] || o.bookType;
}
function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) {

View File

@ -3,15 +3,19 @@
if [ ! -e SheetJS ]; then
react-native init SheetJS
cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs
npm i -S xlsx react-native-table-component react-native-fs
cd -
fi
if [ ! -e SheetJS/logo.png ]; then
curl -O http://oss.sheetjs.com/assets/img/logo.png
mv logo.png SheetJS/logo.png
fi
cp react-native.js SheetJS/index.ios.js
cp react-native.js SheetJS/index.android.js
if [ -e SheetJS/index.ios.js ]; then
cp react-native.js SheetJS/index.ios.js
cp react-native.js SheetJS/index.android.js
else
cp react-native.js SheetJS/index.js
fi
cd SheetJS;
RNFB_ANDROID_PERMISSIONS=true react-native link
cd -;

24
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
dist/xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

195
dist/xlsx.js vendored
View File

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.6';
XLSX.version = '0.11.7';
var current_codepage = 1200;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -1026,7 +1026,7 @@ var DO_NOT_EXPORT_CFB = true;
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '0.13.1';
exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@ -1047,6 +1047,8 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
var mver = 3;
var ssz = 512;
@ -1344,9 +1346,8 @@ function read_date(blob, offset) {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
}
var fs;
function read_file(filename, options) {
if(fs == null) fs = require('fs');
get_fs();
return parse(fs.readFileSync(filename), options);
}
@ -1621,6 +1622,7 @@ var consts = {
};
function write_file(cfb, filename, options) {
get_fs();
var o = _write(cfb, options);
fs.writeFileSync(filename, o);
}
@ -1634,7 +1636,7 @@ function a2s(o) {
function write(cfb, options) {
var o = _write(cfb, options);
switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o)); return o;
case "file": get_fs(); fs.writeFileSync(options.filename, (o)); return o;
case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o));
}
@ -1656,7 +1658,7 @@ function cfb_add(cfb, name, content, opts) {
if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/");
}
file = ({name: filename(name)});
file = ({name: filename(name), type: 2});
cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb);
@ -4339,6 +4341,14 @@ function parse_XLUnicodeString2(blob, length, opts) {
if(cch === 0) { blob.l++; return ""; }
return blob.read_shift(cch, 'sbcs-cont');
}
/* TODO: BIFF5 and lower, codepage awareness */
function write_XLUnicodeString(str, opts, o) {
if(!o) o = new_buf(3 + 2 * str.length);
o.write_shift(2, str.length);
o.write_shift(1, 1);
o.write_shift(31, str, 'utf16le');
return o;
}
/* [MS-XLS] 2.5.61 ControlInfo */
function parse_ControlInfo(blob, length, opts) {
@ -5579,9 +5589,105 @@ function dbf_to_workbook(buf, opts) {
catch(e) { if(opts && opts.WTF) throw e; }
return ({SheetNames:[],Sheets:{}});
}
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
function sheet_to_dbf(ws, opts) {
var o = opts || {};
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
var ba = buf_array();
var aoa = sheet_to_json(ws, {header:1, raw:true, cellDates:true});
var headers = aoa[0], data = aoa.slice(1);
var i = 0, j = 0, hcnt = 0, rlen = 1;
for(i = 0; i < headers.length; ++i) {
if(i == null) continue;
++hcnt;
if(typeof headers[i] !== 'string') throw new Error("DBF Invalid column name");
if(headers.indexOf(headers[i]) !== i) for(j=0; j<1024;++j)
if(headers.indexOf(headers[i] + "_" + j) == -1) { headers[i] += "_" + j; break; }
}
var range = safe_decode_range(ws['!ref']);
var coltypes = [];
for(i = 0; i <= range.e.c - range.s.c; ++i) {
var col = [];
for(j=0; j < data.length; ++j) {
if(data[j][i] != null) col.push(data[j][i]);
}
if(col.length == 0 || headers[i] == null) { coltypes[i] = '?'; continue; }
var guess = '', _guess = '';
for(j = 0; j < col.length; ++j) {
switch(typeof col[j]) {
/* TODO: check if L2 compat is desired */
case 'number': _guess = 'B'; break;
case 'string': _guess = 'C'; break;
case 'boolean': _guess = 'L'; break;
case 'object': _guess = col[j] instanceof Date ? 'D' : 'C'; break;
default: _guess = 'C';
}
guess = guess && guess != _guess ? 'C' : _guess;
if(guess == 'C') break;
}
rlen += _RLEN[guess] || 0;
coltypes[i] = guess;
}
var h = ba.next(32);
h.write_shift(4, 0x13021130);
h.write_shift(4, data.length);
h.write_shift(2, 296 + 32 * hcnt);
h.write_shift(2, rlen);
for(i=0; i < 4; ++i) h.write_shift(4, 0);
h.write_shift(4, 0x00000300); // TODO: CP
for(i = 0, j = 0; i < headers.length; ++i) {
if(headers[i] == null) continue;
var hf = ba.next(32);
var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
hf.write_shift(1, _f, "sbcs");
hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
hf.write_shift(4, j);
hf.write_shift(1, _RLEN[coltypes[i]] || 0);
hf.write_shift(1, 0);
hf.write_shift(1, 0x02);
hf.write_shift(4, 0);
hf.write_shift(1, 0);
hf.write_shift(4, 0);
hf.write_shift(4, 0);
j += _RLEN[coltypes[i]] || 0;
}
var hb = ba.next(264);
hb.write_shift(4, 0x0000000D);
for(i=0; i < 65;++i) hb.write_shift(4, 0x00000000);
for(i=0; i < data.length; ++i) {
var rout = ba.next(rlen);
rout.write_shift(1, 0);
for(j=0; j<headers.length; ++j) {
if(headers[j] == null) continue;
switch(coltypes[j]) {
case 'L': rout.write_shift(1, data[i][j] == null ? 0x3F : data[i][j] ? 0x54 : 0x46); break;
case 'B': rout.write_shift(8, data[i][j]||0, 'f'); break;
case 'D':
if(!data[i][j]) rout.write_shift(8, "00000000", "sbcs");
else {
rout.write_shift(4, ("0000"+data[i][j].getFullYear()).slice(-4), "sbcs");
rout.write_shift(2, ("00"+(data[i][j].getMonth()+1)).slice(-2), "sbcs");
rout.write_shift(2, ("00"+data[i][j].getDate()).slice(-2), "sbcs");
} break;
case 'C':
var _s = String(data[i][j]||"");
rout.write_shift(1, _s, "sbcs");
for(hcnt=0; hcnt < 250-_s.length; ++hcnt) rout.write_shift(1, 0x20); break;
}
}
// data
}
ba.next(1).write_shift(1, 0x1A);
return ba.end();
}
return {
to_workbook: dbf_to_workbook,
to_sheet: dbf_to_sheet
to_sheet: dbf_to_sheet,
from_sheet: sheet_to_dbf
};
})();
@ -8670,6 +8776,15 @@ function make_vba_xls(cfb) {
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb, vba) {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^/]*[/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
@ -14570,10 +14685,10 @@ function safe_format_xf(p, opts, date1904) {
else p.w = SSF._general(p.v);
}
else p.w = SSF.format(fmtid,p.v, {date1904:!!date1904});
if(opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid] || String(fmtid))) {
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
}
} catch(e) { if(opts.WTF) throw e; }
if(opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid] || String(fmtid))) {
var _d = SSF.parse_date_code(p.v); if(_d) { p.t = 'd'; p.v = new Date(_d.y, _d.m-1,_d.d,_d.H,_d.M,_d.S,_d.u); }
}
}
function make_cell(val, ixfe, t) {
@ -15147,7 +15262,7 @@ wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
/* empty */
} break;
case 'CodeName': {
/* empty */
if(!cur_sheet) Workbook.WBProps.codeName = val;
} break;
case 'GUIDTypeLib': {
/* empty */
@ -15381,6 +15496,7 @@ function write_xlscfb(wb, opts) {
}
CFB.utils.cfb_add(cfb, wbpath, write_biff_buf(wb, o));
// TODO: SI, DSI, CO
if(o.biff == 8 && wb.vbaraw) fill_vba_xls(cfb, CFB.read(wb.vbaraw, {type: typeof wb.vbaraw == "string" ? "binary" : "buffer"}));
return cfb;
}
/* [MS-XLSB] 2.3 Record Enumeration */
@ -16777,6 +16893,8 @@ function write_ws_biff8(idx, opts, wb) {
write_ws_biff8_cell(ba, cell, R, C, opts);
}
}
var cname = ((((wb||{}).Workbook||{}).Sheets||[])[idx]||{}).name||s;
write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
/* ... */
write_biff_rec(ba, "EOF");
return ba.end();
@ -16796,6 +16914,11 @@ function write_biff8_global(wb, bufs, opts) {
write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
if(b8) write_biff_rec(A, "DSF", writeuint16(0));
write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
if(b8 && wb.vbaraw) {
write_biff_rec(A, "ObProj");
var cname = ((wb.Workbook||{}).WBProps||{}).codeName || "ThisWorkbook";
write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
}
write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
write_biff_rec(A, "WinProtect", writebool(false));
write_biff_rec(A, "Protect", writebool(false));
@ -17768,12 +17891,16 @@ var zip = new jszip();
return zip;
}
/* actual implementation elsewhere, wrappers are for read/write */
function write_sheet_index(wb, sheet) {
if(!sheet) return 0;
var idx = wb.SheetNames.indexOf(sheet);
if(idx == -1) throw new Error("Sheet not found: " + sheet);
return idx;
}
function write_obj_str(factory) {
return function write_str(wb, o) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
var idx = write_sheet_index(wb, o.sheet);
return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb);
};
}
@ -17785,6 +17912,9 @@ var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_rtf_str = write_obj_str(RTF);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
// $FlowIgnore
var write_dbf_buf = write_obj_str(DBF);
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -18358,13 +18488,15 @@ function writeSync(wb, opts) {
case 'txt': return write_stxt_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o, "\ufeff");
case 'dif': return write_string_type(write_dif_str(wb, o), o);
// $FlowIgnore
case 'dbf': return write_binary_type(write_dbf_buf(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'rtf': return write_string_type(write_rtf_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': if(!o.biff) o.biff = 2; /* falls through */
case 'biff3': if(!o.biff) o.biff = 3; /* falls through */
case 'biff4': if(!o.biff) o.biff = 4; return write_binary_type(write_biff_buf(wb, o), o);
case 'biff5': if(!o.biff) o.biff = 5; return write_cfb_type(wb, o);
case 'biff5': if(!o.biff) o.biff = 5; /* falls through */
case 'biff8':
case 'xls': if(!o.biff) o.biff = 8; return write_cfb_type(wb, o);
case 'xlsx':
@ -18375,26 +18507,17 @@ function writeSync(wb, opts) {
}
}
function resolve_book_type(o/*?WriteFileOpts*/) {
if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
case '.xlml': o.bookType = 'xlml'; break;
case '.sylk': o.bookType = 'sylk'; break;
case '.html': o.bookType = 'html'; break;
case '.xls': o.bookType = 'biff8'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.rtf': o.bookType = 'rtf'; break;
case '.slk': o.bookType = 'sylk'; break;
case '.htm': o.bookType = 'html'; break;
}
function resolve_book_type(o) {
if(o.bookType) return;
var _BT = {
"xls": "biff8",
"htm": "html",
"slk": "sylk",
"Sh33tJS": "WTF"
};
var ext = o.file.slice(o.file.lastIndexOf(".")).toLowerCase();
if(ext.match(/^\.[a-z]+$/)) o.bookType = ext.slice(1);
o.bookType = _BT[o.bookType] || o.bookType;
}
function writeFileSync(wb, filename, opts) {

24
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

View File

@ -2,8 +2,9 @@
VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
property of the workbook object when the `bookVBA` option is `true`. They are
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
writers automatically insert the data blobs if it is present in the workbook.
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The supported format
writers automatically insert the data blobs if it is present in the workbook and
associate with the worksheet names.
<details>
<summary><b>Macrosheets</b> (click to show)</summary>

View File

@ -46,6 +46,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | none | single | dBASE II + VFP Extensions (DBF) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |

View File

@ -23,7 +23,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | :o: |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | :o: | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
@ -192,10 +192,12 @@ Many older formats supported only one worksheet:
DBF is really a typed table format: each column can only hold one data type and
each record omits type information. The parser generates a header row and
inserts records starting at the second row of the worksheet.
inserts records starting at the second row of the worksheet. The writer makes
files compatible with Visual FoxPro extensions.
Multi-file extensions like external memos and tables are currently unsupported,
limited by the general ability to read arbitrary files in the web browser.
limited by the general ability to read arbitrary files in the web browser. The
reader understands DBF Level 7 extensions like DATETIME.
</details>

View File

@ -72,6 +72,7 @@ digraph G {
txt -> csf
csf -> txt
dbf -> csf
csf -> dbf
html -> csf
csf -> html
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 184 KiB

View File

@ -1319,8 +1319,9 @@ if a sheet is visible is to check if the `Hidden` property is logical truth:
VBA Macros are stored in a special data blob that is exposed in the `vbaraw`
property of the workbook object when the `bookVBA` option is `true`. They are
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The `XLSM` and `XLSB`
writers automatically insert the data blobs if it is present in the workbook.
supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The supported format
writers automatically insert the data blobs if it is present in the workbook and
associate with the worksheet names.
Older versions of Excel also supported a non-VBA "macrosheet" sheet type that
@ -1508,6 +1509,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |
| `html` | `.html` | none | single | HTML Document |
| `dif` | `.dif` | none | single | Data Interchange Format (DIF) |
| `dbf` | `.dbf` | none | single | dBASE II + VFP Extensions (DBF) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
@ -1820,7 +1822,7 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| OpenDocument Spreadsheet (ODS) | :o: | :o: |
| Flat XML ODF Spreadsheet (FODS) | :o: | :o: |
| Uniform Office Format Spreadsheet (标文通 UOS1/UOS2) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | |
| dBASE II/III/IV / Visual FoxPro (DBF) | :o: | :o: |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | :o: | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | :o: | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
@ -1954,10 +1956,12 @@ Many older formats supported only one worksheet:
DBF is really a typed table format: each column can only hold one data type and
each record omits type information. The parser generates a header row and
inserts records starting at the second row of the worksheet.
inserts records starting at the second row of the worksheet. The writer makes
files compatible with Visual FoxPro extensions.
Multi-file extensions like external memos and tables are currently unsupported,
limited by the general ability to read arbitrary files in the web browser.
limited by the general ability to read arbitrary files in the web browser. The
reader understands DBF Level 7 extensions like DATETIME.
#### Symbolic Link (SYLK)
@ -2248,4 +2252,3 @@ granted by the Apache 2.0 License are reserved by the Original Author.
- ISO/IEC 29500:2012(E) "Information technology — Document description and processing languages — Office Open XML File Formats"
- Open Document Format for Office Applications Version 1.2 (29 September 2011)
- Worksheet File Format (From Lotus) December 1984

View File

@ -86,4 +86,4 @@
* [Windows](README.md#windows)
* [Tests](README.md#tests)
- [License](README.md#license)
- [References](README.md#references)
- [References](README.md#references)

View File

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.11.6",
"version": "0.11.7",
"author": "sheetjs",
"description": "SheetJS Spreadsheet data parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "csv", "dbf", "dif", "sylk", "office", "spreadsheet" ],
@ -20,7 +20,7 @@
"exit-on-epipe": "~1.0.1",
"ssf": "~0.10.1",
"codepage": "~1.11.0",
"cfb": "~0.13.1",
"cfb": "~0.13.2",
"crc-32": "~1.1.1",
"adler-32": "~1.1.0",
"commander": "~2.11.0"

30
test.js
View File

@ -22,7 +22,7 @@ var opts = ({cellNF: true}/*:any*/);
var TYPE = browser ? "binary" : "buffer";
opts.type = TYPE;
var fullex = [".xlsb", /*".xlsm",*/ ".xlsx"/*, ".xlml", ".xls"*/];
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "biff5", "biff8", "xlml", "sylk", "dif"];
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "biff5", "biff8", "xlml", "sylk", "dif", "dbf"];
var ex = fullex.slice(); ex = ex.concat([".ods", ".xls", ".xml", ".fods"]);
if(typeof process != 'undefined' && ((process||{}).env)) {
opts.WTF = true;
@ -505,7 +505,7 @@ describe('parse options', function() {
X.utils.sheet_to_csv(wb.Sheets.Merge);
X.utils.sheet_to_json(wb.Sheets.Merge);
X.utils.sheet_to_formulae(wb.Sheets.Merge);
ofmt.forEach(function(f) { X.write(wb, {type:TYPE, bookType:f}); });
ofmt.forEach(function(f) { if(f != "dbf") X.write(wb, {type:TYPE, bookType:f}); });
});
});
function checkcells(wb, A46, B26, C16, D2) {
@ -625,6 +625,7 @@ describe('output formats', function() {
["sylk", false, true],
["html", true, true],
["dif", false, true],
["dbf", false, false],
["prn", false, true]
];
function RT(T) {
@ -764,7 +765,7 @@ describe('parse features', function() {
describe('comments', function() {
if(fs.existsSync(paths.swcxlsx)) it('should have comment as part of cell properties', function(){
var X = require(modp);
X = require(modp);
var sheet = 'Sheet1';
var wb1=X.read(fs.readFileSync(paths.swcxlsx), {type:TYPE});
var wb2=X.read(fs.readFileSync(paths.swcxlsb), {type:TYPE});
@ -1867,10 +1868,11 @@ describe('js -> file -> js', function() {
var wb, BIN="binary";
var bef = (function() {
var ws = X.utils.aoa_to_sheet([
[1,2,3],
[true, false, null, "sheetjs"],
["foo", "bar", fixdate, "0.3"],
["baz", 6.9, "qux"]
["number", "bool", "string", "date"],
[1, true, "sheet"],
[2, false, "dot"],
[6.9, false, "JS", fixdate],
[72.62, true, "0.3"]
]);
wb = { SheetNames: ['Sheet1'], Sheets: {Sheet1: ws} };
});
@ -1884,13 +1886,13 @@ describe('js -> file -> js', function() {
it(f, function() {
var newwb = X.read(X.write(wb, {type:BIN, bookType: f}), {type:BIN});
var cb = function(cell) { eqcell(wb, newwb, 'Sheet1', cell); };
['A1', 'B1', 'C1'].forEach(cb); /* int */
['B4'].forEach(cb); /* double */
['A2', 'B2'].forEach(cb); /* bool */
['D2', 'A3', 'B3', 'A4', 'C4'].forEach(cb); /* string */
if(!DIF_XL) cb('C3'); /* date */
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets.Sheet1, 'D3').v, '=""0.3""');// dif forces string formula
else eqcell(wb, newwb, 'Sheet1', 'D3');
['A2', 'A3'].forEach(cb); /* int */
['A4', 'A5'].forEach(cb); /* double */
['B2', 'B3'].forEach(cb); /* bool */
['C2', 'C3'].forEach(cb); /* string */
if(!DIF_XL) cb('D4'); /* date */
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets.Sheet1, 'C5').v, '=""0.3""');// dif forces string formula
else eqcell(wb, newwb, 'Sheet1', 'C5');
});
});
});

View File

@ -22,7 +22,7 @@ var opts = ({cellNF: true}/*:any*/);
var TYPE = browser ? "binary" : "buffer";
opts.type = TYPE;
var fullex = [".xlsb", /*".xlsm",*/ ".xlsx"/*, ".xlml", ".xls"*/];
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "biff5", "biff8", "xlml", "sylk", "dif"];
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "biff5", "biff8", "xlml", "sylk", "dif", "dbf"];
var ex = fullex.slice(); ex = ex.concat([".ods", ".xls", ".xml", ".fods"]);
if(typeof process != 'undefined' && ((process||{}).env)) {
opts.WTF = true;
@ -505,7 +505,7 @@ describe('parse options', function() {
X.utils.sheet_to_csv(wb.Sheets.Merge);
X.utils.sheet_to_json(wb.Sheets.Merge);
X.utils.sheet_to_formulae(wb.Sheets.Merge);
ofmt.forEach(function(f) { X.write(wb, {type:TYPE, bookType:f}); });
ofmt.forEach(function(f) { if(f != "dbf") X.write(wb, {type:TYPE, bookType:f}); });
});
});
function checkcells(wb, A46, B26, C16, D2) {
@ -541,6 +541,7 @@ describe('parse options', function() {
});
it('bookProps && bookSheets should not generate sheets', function() {
PMPaths.forEach(function(p) {
if(!fs.existsSync(p)) return;
var wb = X.read(fs.readFileSync(p), {type:TYPE, bookProps:true, bookSheets:true});
assert(typeof wb.Sheets === 'undefined');
});
@ -624,6 +625,7 @@ describe('output formats', function() {
["sylk", false, true],
["html", true, true],
["dif", false, true],
["dbf", false, false],
["prn", false, true]
];
function RT(T) {
@ -763,7 +765,7 @@ describe('parse features', function() {
describe('comments', function() {
if(fs.existsSync(paths.swcxlsx)) it('should have comment as part of cell properties', function(){
var X = require(modp);
X = require(modp);
var sheet = 'Sheet1';
var wb1=X.read(fs.readFileSync(paths.swcxlsx), {type:TYPE});
var wb2=X.read(fs.readFileSync(paths.swcxlsb), {type:TYPE});
@ -1866,10 +1868,11 @@ describe('js -> file -> js', function() {
var wb, BIN="binary";
var bef = (function() {
var ws = X.utils.aoa_to_sheet([
[1,2,3],
[true, false, null, "sheetjs"],
["foo", "bar", fixdate, "0.3"],
["baz", 6.9, "qux"]
["number", "bool", "string", "date"],
[1, true, "sheet"],
[2, false, "dot"],
[6.9, false, "JS", fixdate],
[72.62, true, "0.3"]
]);
wb = { SheetNames: ['Sheet1'], Sheets: {Sheet1: ws} };
});
@ -1883,13 +1886,13 @@ describe('js -> file -> js', function() {
it(f, function() {
var newwb = X.read(X.write(wb, {type:BIN, bookType: f}), {type:BIN});
var cb = function(cell) { eqcell(wb, newwb, 'Sheet1', cell); };
['A1', 'B1', 'C1'].forEach(cb); /* int */
['B4'].forEach(cb); /* double */
['A2', 'B2'].forEach(cb); /* bool */
['D2', 'A3', 'B3', 'A4', 'C4'].forEach(cb); /* string */
if(!DIF_XL) cb('C3'); /* date */
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets.Sheet1, 'D3').v, '=""0.3""');// dif forces string formula
else eqcell(wb, newwb, 'Sheet1', 'D3');
['A2', 'A3'].forEach(cb); /* int */
['A4', 'A5'].forEach(cb); /* double */
['B2', 'B3'].forEach(cb); /* bool */
['C2', 'C3'].forEach(cb); /* string */
if(!DIF_XL) cb('D4'); /* date */
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets.Sheet1, 'C5').v, '=""0.3""');// dif forces string formula
else eqcell(wb, newwb, 'Sheet1', 'C5');
});
});
});

View File

@ -114,7 +114,7 @@ wb.Sheets["Hidden"] = XLSX.utils.aoa_to_sheet(["Hidden".split(""), [1,2,3]]);
wb.Workbook = {Sheets:[]};
wb.Workbook.Sheets[1] = {Hidden:1};
*/
var data_2 = ["Hidden".split(""), [1,2,3]];
var data_2 = ["Hidden".split(""), [1,true,3,'a',,'c'], [2,false,true,'sh33t',,'j5']];
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(data_2), "Hidden");
XLSX.utils.book_set_sheet_visibility(wb, "Hidden", XLSX.utils.consts.SHEET_HIDDEN);
@ -173,6 +173,7 @@ var filenames = [
['sheetjs.slk'],
['sheetjs.htm'],
['sheetjs.dif'],
['sheetjs.dbf', {sheet:"Hidden"}],
['sheetjs.rtf'],
['sheetjs.prn']
];

View File

@ -102,7 +102,7 @@ wb.SheetNames.push("Hidden");
wb.Sheets["Hidden"] = XLSX.utils.aoa_to_sheet(["Hidden".split(""), [1,2,3]]);
wb.Workbook = {Sheets:[]};
wb.Workbook.Sheets[1] = {Hidden:1};
const data_2 = ["Hidden".split(""), [1,2,3]];
const data_2 = ["Hidden".split(""), [1,true,3,'a',,'c'], [2,false,true,'sh33t',,'j5']];
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(data_2), "Hidden");
XLSX.utils.book_set_sheet_visibility(wb, "Hidden", XLSX.utils.consts.SHEET_HIDDEN);
@ -161,6 +161,7 @@ const filenames: Array<[string]|[string, XLSX.WritingOptions]> = [
['sheetjs.slk'],
['sheetjs.htm'],
['sheetjs.dif'],
['sheetjs.dbf', {sheet:"Hidden"}],
['sheetjs.rtf'],
['sheetjs.prn']
];

View File

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.6';
XLSX.version = '0.11.7';
var current_codepage = 1200;
/*:: declare var cptable:any; */
/*global cptable:true */
@ -1089,7 +1089,7 @@ type CFBFiles = {[n:string]:CFBEntry};
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
exports.version = '0.13.1';
exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */
function namecmp(l/*:string*/, r/*:string*/)/*:number*/ {
var L = l.split("/"), R = r.split("/");
@ -1110,6 +1110,8 @@ function filename(p/*:string*/)/*:string*/ {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
var fs/*:: = require('fs'); */;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
var mver = 3;
var ssz = 512;
@ -1407,9 +1409,8 @@ function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
}
var fs/*:: = require('fs'); */;
function read_file(filename/*:string*/, options/*:CFBReadOpts*/) {
if(fs == null) fs = require('fs');
get_fs();
return parse(fs.readFileSync(filename), options);
}
@ -1689,6 +1690,7 @@ var consts = {
};
function write_file(cfb/*:CFBContainer*/, filename/*:string*/, options/*:CFBWriteOpts*/)/*:void*/ {
get_fs();
var o = _write(cfb, options);
/*:: if(typeof Buffer == 'undefined' || !Buffer.isBuffer(o) || !(o instanceof Buffer)) throw new Error("unreachable"); */
fs.writeFileSync(filename, o);
@ -1703,7 +1705,7 @@ function a2s(o/*:RawBytes*/)/*:string*/ {
function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string*/ {
var o = _write(cfb, options);
switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "file": get_fs(); fs.writeFileSync(options.filename, (o/*:any*/)); return o;
case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o));
}
@ -1719,13 +1721,13 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o
init_cfb(cfb);
var file = CFB.find(cfb, name);
if(!file) {
var fpath = cfb.FullPaths[0];
var fpath/*:string*/ = cfb.FullPaths[0];
if(name.slice(0, fpath.length) == fpath) fpath = name;
else {
if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/");
}
file = ({name: filename(name)}/*:any*/);
file = ({name: filename(name), type: 2}/*:any*/);
cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb);
@ -4424,6 +4426,14 @@ function parse_XLUnicodeString2(blob, length, opts) {
if(cch === 0) { blob.l++; return ""; }
return blob.read_shift(cch, 'sbcs-cont');
}
/* TODO: BIFF5 and lower, codepage awareness */
function write_XLUnicodeString(str, opts, o) {
if(!o) o = new_buf(3 + 2 * str.length);
o.write_shift(2, str.length);
o.write_shift(1, 1);
o.write_shift(31, str, 'utf16le');
return o;
}
/* [MS-XLS] 2.5.61 ControlInfo */
function parse_ControlInfo(blob, length, opts) {
@ -5664,9 +5674,105 @@ function dbf_to_workbook(buf, opts)/*:Workbook*/ {
catch(e) { if(opts && opts.WTF) throw e; }
return ({SheetNames:[],Sheets:{}});
}
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
function sheet_to_dbf(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
var ba = buf_array();
var aoa/*:AOA*/ = sheet_to_json(ws, {header:1, raw:true, cellDates:true});
var headers = aoa[0], data = aoa.slice(1);
var i = 0, j = 0, hcnt = 0, rlen = 1;
for(i = 0; i < headers.length; ++i) {
if(i == null) continue;
++hcnt;
if(typeof headers[i] !== 'string') throw new Error("DBF Invalid column name");
if(headers.indexOf(headers[i]) !== i) for(j=0; j<1024;++j)
if(headers.indexOf(headers[i] + "_" + j) == -1) { headers[i] += "_" + j; break; }
}
var range = safe_decode_range(ws['!ref']);
var coltypes = [];
for(i = 0; i <= range.e.c - range.s.c; ++i) {
var col/*:Array<any>*/ = [];
for(j=0; j < data.length; ++j) {
if(data[j][i] != null) col.push(data[j][i]);
}
if(col.length == 0 || headers[i] == null) { coltypes[i] = '?'; continue; }
var guess = '', _guess = '';
for(j = 0; j < col.length; ++j) {
switch(typeof col[j]) {
/* TODO: check if L2 compat is desired */
case 'number': _guess = 'B'; break;
case 'string': _guess = 'C'; break;
case 'boolean': _guess = 'L'; break;
case 'object': _guess = col[j] instanceof Date ? 'D' : 'C'; break;
default: _guess = 'C';
}
guess = guess && guess != _guess ? 'C' : _guess;
if(guess == 'C') break;
}
rlen += _RLEN[guess] || 0;
coltypes[i] = guess;
}
var h = ba.next(32);
h.write_shift(4, 0x13021130);
h.write_shift(4, data.length);
h.write_shift(2, 296 + 32 * hcnt);
h.write_shift(2, rlen);
for(i=0; i < 4; ++i) h.write_shift(4, 0);
h.write_shift(4, 0x00000300); // TODO: CP
for(i = 0, j = 0; i < headers.length; ++i) {
if(headers[i] == null) continue;
var hf = ba.next(32);
var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
hf.write_shift(1, _f, "sbcs");
hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
hf.write_shift(4, j);
hf.write_shift(1, _RLEN[coltypes[i]] || 0);
hf.write_shift(1, 0);
hf.write_shift(1, 0x02);
hf.write_shift(4, 0);
hf.write_shift(1, 0);
hf.write_shift(4, 0);
hf.write_shift(4, 0);
j += _RLEN[coltypes[i]] || 0;
}
var hb = ba.next(264);
hb.write_shift(4, 0x0000000D);
for(i=0; i < 65;++i) hb.write_shift(4, 0x00000000);
for(i=0; i < data.length; ++i) {
var rout = ba.next(rlen);
rout.write_shift(1, 0);
for(j=0; j<headers.length; ++j) {
if(headers[j] == null) continue;
switch(coltypes[j]) {
case 'L': rout.write_shift(1, data[i][j] == null ? 0x3F : data[i][j] ? 0x54 : 0x46); break;
case 'B': rout.write_shift(8, data[i][j]||0, 'f'); break;
case 'D':
if(!data[i][j]) rout.write_shift(8, "00000000", "sbcs");
else {
rout.write_shift(4, ("0000"+data[i][j].getFullYear()).slice(-4), "sbcs");
rout.write_shift(2, ("00"+(data[i][j].getMonth()+1)).slice(-2), "sbcs");
rout.write_shift(2, ("00"+data[i][j].getDate()).slice(-2), "sbcs");
} break;
case 'C':
var _s = String(data[i][j]||"");
rout.write_shift(1, _s, "sbcs");
for(hcnt=0; hcnt < 250-_s.length; ++hcnt) rout.write_shift(1, 0x20); break;
}
}
// data
}
ba.next(1).write_shift(1, 0x1A);
return ba.end();
}
return {
to_workbook: dbf_to_workbook,
to_sheet: dbf_to_sheet
to_sheet: dbf_to_sheet,
from_sheet: sheet_to_dbf
};
})();
@ -8758,6 +8864,15 @@ function make_vba_xls(cfb/*:CFBContainer*/) {
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb/*:CFBContainer*/, vba/*:CFBContainer*/)/*:void*/ {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^/]*[/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
@ -15246,7 +15361,8 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ {
/* empty */
} break;
case 'CodeName': {
/* empty */
/*:: if(!Workbook.WBProps) Workbook.WBProps = {}; */
if(!cur_sheet) Workbook.WBProps.codeName = val;
} break;
case 'GUIDTypeLib': {
/* empty */
@ -15480,6 +15596,7 @@ function write_xlscfb(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:CFBContainer*/ {
}
CFB.utils.cfb_add(cfb, wbpath, write_biff_buf(wb, o));
// TODO: SI, DSI, CO
if(o.biff == 8 && wb.vbaraw) fill_vba_xls(cfb, CFB.read(wb.vbaraw, {type: typeof wb.vbaraw == "string" ? "binary" : "buffer"}));
return cfb;
}
/* [MS-XLSB] 2.3 Record Enumeration */
@ -16876,6 +16993,8 @@ function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
write_ws_biff8_cell(ba, cell, R, C, opts);
}
}
var cname = ((((wb||{}).Workbook||{}).Sheets||[])[idx]||{}).name||s;
write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
/* ... */
write_biff_rec(ba, "EOF");
return ba.end();
@ -16895,6 +17014,11 @@ function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
if(b8) write_biff_rec(A, "DSF", writeuint16(0));
write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
if(b8 && wb.vbaraw) {
write_biff_rec(A, "ObProj");
var cname = ((wb.Workbook||{}).WBProps||{}).codeName || "ThisWorkbook";
write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
}
write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
write_biff_rec(A, "WinProtect", writebool(false));
write_biff_rec(A, "Protect", writebool(false));
@ -17868,12 +17992,16 @@ function write_ods(wb/*:any*/, opts/*:any*/) {
return zip;
}
/* actual implementation elsewhere, wrappers are for read/write */
function write_sheet_index(wb/*:Workbook*/, sheet/*:?string*/)/*:number*/ {
if(!sheet) return 0;
var idx = wb.SheetNames.indexOf(sheet);
if(idx == -1) throw new Error("Sheet not found: " + sheet);
return idx;
}
function write_obj_str(factory/*:WriteObjStrFactory*/) {
return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/)/*:string*/ {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
var idx = write_sheet_index(wb, o.sheet);
return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb);
};
}
@ -17885,6 +18013,9 @@ var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_rtf_str = write_obj_str(RTF);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
// $FlowIgnore
var write_dbf_buf = write_obj_str(DBF);
function fix_opts_func(defaults/*:Array<Array<any> >*/)/*:{(o:any):void}*/ {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -18461,13 +18592,15 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'txt': return write_stxt_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o, "\ufeff");
case 'dif': return write_string_type(write_dif_str(wb, o), o);
// $FlowIgnore
case 'dbf': return write_binary_type(write_dbf_buf(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'rtf': return write_string_type(write_rtf_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': if(!o.biff) o.biff = 2; /* falls through */
case 'biff3': if(!o.biff) o.biff = 3; /* falls through */
case 'biff4': if(!o.biff) o.biff = 4; return write_binary_type(write_biff_buf(wb, o), o);
case 'biff5': if(!o.biff) o.biff = 5; return write_cfb_type(wb, o);
case 'biff5': if(!o.biff) o.biff = 5; /* falls through */
case 'biff8':
case 'xls': if(!o.biff) o.biff = 8; return write_cfb_type(wb, o);
case 'xlsx':
@ -18478,26 +18611,17 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
}
}
function resolve_book_type(o/*?WriteFileOpts*/) {
if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
case '.xlml': o.bookType = 'xlml'; break;
case '.sylk': o.bookType = 'sylk'; break;
case '.html': o.bookType = 'html'; break;
case '.xls': o.bookType = 'biff8'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.rtf': o.bookType = 'rtf'; break;
case '.slk': o.bookType = 'sylk'; break;
case '.htm': o.bookType = 'html'; break;
}
function resolve_book_type(o/*:WriteFileOpts*/) {
if(o.bookType) return;
var _BT = {
"xls": "biff8",
"htm": "html",
"slk": "sylk",
"Sh33tJS": "WTF"
};
var ext = o.file.slice(o.file.lastIndexOf(".")).toLowerCase();
if(ext.match(/^\.[a-z]+$/)) o.bookType = ext.slice(1);
o.bookType = _BT[o.bookType] || o.bookType;
}
function writeFileSync(wb/*:Workbook*/, filename/*:string*/, opts/*:?WriteFileOpts*/) {

189
xlsx.js
View File

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.6';
XLSX.version = '0.11.7';
var current_codepage = 1200;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -1026,7 +1026,7 @@ var DO_NOT_EXPORT_CFB = true;
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '0.13.1';
exports.version = '0.13.2';
/* [MS-CFB] 2.6.4 */
function namecmp(l, r) {
var L = l.split("/"), R = r.split("/");
@ -1047,6 +1047,8 @@ function filename(p) {
var c = p.lastIndexOf("/");
return (c === -1) ? p : p.slice(c+1);
}
var fs;
function get_fs() { return fs || (fs = require('fs')); }
function parse(file, options) {
var mver = 3;
var ssz = 512;
@ -1344,9 +1346,8 @@ function read_date(blob, offset) {
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
}
var fs;
function read_file(filename, options) {
if(fs == null) fs = require('fs');
get_fs();
return parse(fs.readFileSync(filename), options);
}
@ -1621,6 +1622,7 @@ var consts = {
};
function write_file(cfb, filename, options) {
get_fs();
var o = _write(cfb, options);
fs.writeFileSync(filename, o);
}
@ -1634,7 +1636,7 @@ function a2s(o) {
function write(cfb, options) {
var o = _write(cfb, options);
switch(options && options.type) {
case "file": fs.writeFileSync(options.filename, (o)); return o;
case "file": get_fs(); fs.writeFileSync(options.filename, (o)); return o;
case "binary": return a2s(o);
case "base64": return Base64.encode(a2s(o));
}
@ -1656,7 +1658,7 @@ function cfb_add(cfb, name, content, opts) {
if(fpath.slice(-1) != "/") fpath += "/";
fpath = (fpath + name).replace("//","/");
}
file = ({name: filename(name)});
file = ({name: filename(name), type: 2});
cfb.FileIndex.push(file);
cfb.FullPaths.push(fpath);
CFB.utils.cfb_gc(cfb);
@ -4339,6 +4341,14 @@ function parse_XLUnicodeString2(blob, length, opts) {
if(cch === 0) { blob.l++; return ""; }
return blob.read_shift(cch, 'sbcs-cont');
}
/* TODO: BIFF5 and lower, codepage awareness */
function write_XLUnicodeString(str, opts, o) {
if(!o) o = new_buf(3 + 2 * str.length);
o.write_shift(2, str.length);
o.write_shift(1, 1);
o.write_shift(31, str, 'utf16le');
return o;
}
/* [MS-XLS] 2.5.61 ControlInfo */
function parse_ControlInfo(blob, length, opts) {
@ -5579,9 +5589,105 @@ function dbf_to_workbook(buf, opts) {
catch(e) { if(opts && opts.WTF) throw e; }
return ({SheetNames:[],Sheets:{}});
}
var _RLEN = { 'B': 8, 'C': 250, 'L': 1, 'D': 8, '?': 0, '': 0 };
function sheet_to_dbf(ws, opts) {
var o = opts || {};
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
var ba = buf_array();
var aoa = sheet_to_json(ws, {header:1, raw:true, cellDates:true});
var headers = aoa[0], data = aoa.slice(1);
var i = 0, j = 0, hcnt = 0, rlen = 1;
for(i = 0; i < headers.length; ++i) {
if(i == null) continue;
++hcnt;
if(typeof headers[i] !== 'string') throw new Error("DBF Invalid column name");
if(headers.indexOf(headers[i]) !== i) for(j=0; j<1024;++j)
if(headers.indexOf(headers[i] + "_" + j) == -1) { headers[i] += "_" + j; break; }
}
var range = safe_decode_range(ws['!ref']);
var coltypes = [];
for(i = 0; i <= range.e.c - range.s.c; ++i) {
var col = [];
for(j=0; j < data.length; ++j) {
if(data[j][i] != null) col.push(data[j][i]);
}
if(col.length == 0 || headers[i] == null) { coltypes[i] = '?'; continue; }
var guess = '', _guess = '';
for(j = 0; j < col.length; ++j) {
switch(typeof col[j]) {
/* TODO: check if L2 compat is desired */
case 'number': _guess = 'B'; break;
case 'string': _guess = 'C'; break;
case 'boolean': _guess = 'L'; break;
case 'object': _guess = col[j] instanceof Date ? 'D' : 'C'; break;
default: _guess = 'C';
}
guess = guess && guess != _guess ? 'C' : _guess;
if(guess == 'C') break;
}
rlen += _RLEN[guess] || 0;
coltypes[i] = guess;
}
var h = ba.next(32);
h.write_shift(4, 0x13021130);
h.write_shift(4, data.length);
h.write_shift(2, 296 + 32 * hcnt);
h.write_shift(2, rlen);
for(i=0; i < 4; ++i) h.write_shift(4, 0);
h.write_shift(4, 0x00000300); // TODO: CP
for(i = 0, j = 0; i < headers.length; ++i) {
if(headers[i] == null) continue;
var hf = ba.next(32);
var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
hf.write_shift(1, _f, "sbcs");
hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
hf.write_shift(4, j);
hf.write_shift(1, _RLEN[coltypes[i]] || 0);
hf.write_shift(1, 0);
hf.write_shift(1, 0x02);
hf.write_shift(4, 0);
hf.write_shift(1, 0);
hf.write_shift(4, 0);
hf.write_shift(4, 0);
j += _RLEN[coltypes[i]] || 0;
}
var hb = ba.next(264);
hb.write_shift(4, 0x0000000D);
for(i=0; i < 65;++i) hb.write_shift(4, 0x00000000);
for(i=0; i < data.length; ++i) {
var rout = ba.next(rlen);
rout.write_shift(1, 0);
for(j=0; j<headers.length; ++j) {
if(headers[j] == null) continue;
switch(coltypes[j]) {
case 'L': rout.write_shift(1, data[i][j] == null ? 0x3F : data[i][j] ? 0x54 : 0x46); break;
case 'B': rout.write_shift(8, data[i][j]||0, 'f'); break;
case 'D':
if(!data[i][j]) rout.write_shift(8, "00000000", "sbcs");
else {
rout.write_shift(4, ("0000"+data[i][j].getFullYear()).slice(-4), "sbcs");
rout.write_shift(2, ("00"+(data[i][j].getMonth()+1)).slice(-2), "sbcs");
rout.write_shift(2, ("00"+data[i][j].getDate()).slice(-2), "sbcs");
} break;
case 'C':
var _s = String(data[i][j]||"");
rout.write_shift(1, _s, "sbcs");
for(hcnt=0; hcnt < 250-_s.length; ++hcnt) rout.write_shift(1, 0x20); break;
}
}
// data
}
ba.next(1).write_shift(1, 0x1A);
return ba.end();
}
return {
to_workbook: dbf_to_workbook,
to_sheet: dbf_to_sheet
to_sheet: dbf_to_sheet,
from_sheet: sheet_to_dbf
};
})();
@ -8670,6 +8776,15 @@ function make_vba_xls(cfb) {
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb, vba) {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^/]*[/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
RELS.DS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet";
RELS.MS = "http://schemas.microsoft.com/office/2006/relationships/xlMacrosheet";
@ -15147,7 +15262,7 @@ wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
/* empty */
} break;
case 'CodeName': {
/* empty */
if(!cur_sheet) Workbook.WBProps.codeName = val;
} break;
case 'GUIDTypeLib': {
/* empty */
@ -15381,6 +15496,7 @@ function write_xlscfb(wb, opts) {
}
CFB.utils.cfb_add(cfb, wbpath, write_biff_buf(wb, o));
// TODO: SI, DSI, CO
if(o.biff == 8 && wb.vbaraw) fill_vba_xls(cfb, CFB.read(wb.vbaraw, {type: typeof wb.vbaraw == "string" ? "binary" : "buffer"}));
return cfb;
}
/* [MS-XLSB] 2.3 Record Enumeration */
@ -16777,6 +16893,8 @@ function write_ws_biff8(idx, opts, wb) {
write_ws_biff8_cell(ba, cell, R, C, opts);
}
}
var cname = ((((wb||{}).Workbook||{}).Sheets||[])[idx]||{}).name||s;
write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
/* ... */
write_biff_rec(ba, "EOF");
return ba.end();
@ -16796,6 +16914,11 @@ function write_biff8_global(wb, bufs, opts) {
write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
if(b8) write_biff_rec(A, "DSF", writeuint16(0));
write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
if(b8 && wb.vbaraw) {
write_biff_rec(A, "ObProj");
var cname = ((wb.Workbook||{}).WBProps||{}).codeName || "ThisWorkbook";
write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
}
write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
write_biff_rec(A, "WinProtect", writebool(false));
write_biff_rec(A, "Protect", writebool(false));
@ -17768,12 +17891,16 @@ var zip = new jszip();
return zip;
}
/* actual implementation elsewhere, wrappers are for read/write */
function write_sheet_index(wb, sheet) {
if(!sheet) return 0;
var idx = wb.SheetNames.indexOf(sheet);
if(idx == -1) throw new Error("Sheet not found: " + sheet);
return idx;
}
function write_obj_str(factory) {
return function write_str(wb, o) {
var idx = 0;
for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
var idx = write_sheet_index(wb, o.sheet);
return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb);
};
}
@ -17785,6 +17912,9 @@ var write_dif_str = write_obj_str(DIF);
var write_prn_str = write_obj_str(PRN);
var write_rtf_str = write_obj_str(RTF);
var write_txt_str = write_obj_str({from_sheet:sheet_to_txt});
// $FlowIgnore
var write_dbf_buf = write_obj_str(DBF);
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -18358,13 +18488,15 @@ function writeSync(wb, opts) {
case 'txt': return write_stxt_type(write_txt_str(wb, o), o);
case 'csv': return write_string_type(write_csv_str(wb, o), o, "\ufeff");
case 'dif': return write_string_type(write_dif_str(wb, o), o);
// $FlowIgnore
case 'dbf': return write_binary_type(write_dbf_buf(wb, o), o);
case 'prn': return write_string_type(write_prn_str(wb, o), o);
case 'rtf': return write_string_type(write_rtf_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'biff2': if(!o.biff) o.biff = 2; /* falls through */
case 'biff3': if(!o.biff) o.biff = 3; /* falls through */
case 'biff4': if(!o.biff) o.biff = 4; return write_binary_type(write_biff_buf(wb, o), o);
case 'biff5': if(!o.biff) o.biff = 5; return write_cfb_type(wb, o);
case 'biff5': if(!o.biff) o.biff = 5; /* falls through */
case 'biff8':
case 'xls': if(!o.biff) o.biff = 8; return write_cfb_type(wb, o);
case 'xlsx':
@ -18375,26 +18507,17 @@ function writeSync(wb, opts) {
}
}
function resolve_book_type(o/*?WriteFileOpts*/) {
if(!o.bookType) switch(o.file.slice(o.file.lastIndexOf(".")).toLowerCase()) {
case '.xlsx': o.bookType = 'xlsx'; break;
case '.xlsm': o.bookType = 'xlsm'; break;
case '.xlsb': o.bookType = 'xlsb'; break;
case '.fods': o.bookType = 'fods'; break;
case '.xlml': o.bookType = 'xlml'; break;
case '.sylk': o.bookType = 'sylk'; break;
case '.html': o.bookType = 'html'; break;
case '.xls': o.bookType = 'biff8'; break;
case '.xml': o.bookType = 'xml'; break;
case '.ods': o.bookType = 'ods'; break;
case '.csv': o.bookType = 'csv'; break;
case '.txt': o.bookType = 'txt'; break;
case '.dif': o.bookType = 'dif'; break;
case '.prn': o.bookType = 'prn'; break;
case '.rtf': o.bookType = 'rtf'; break;
case '.slk': o.bookType = 'sylk'; break;
case '.htm': o.bookType = 'html'; break;
}
function resolve_book_type(o) {
if(o.bookType) return;
var _BT = {
"xls": "biff8",
"htm": "html",
"slk": "sylk",
"Sh33tJS": "WTF"
};
var ext = o.file.slice(o.file.lastIndexOf(".")).toLowerCase();
if(ext.match(/^\.[a-z]+$/)) o.bookType = ext.slice(1);
o.bookType = _BT[o.bookType] || o.bookType;
}
function writeFileSync(wb, filename, opts) {