Skip to content

Commit

Permalink
version bump 0.12.0: extendscript fixes
Browse files Browse the repository at this point in the history
- ExtendScript write quirks (fixes SheetJS#986 h/t @grefel)
- BIFF8 write number formats (fixes SheetJS#987 h/t @scwood)
- xlsx.extendscript.js library script
- readFile / writeFile support ExtendScript
- flow update
  • Loading branch information
SheetJSDev committed Feb 8, 2018
1 parent fb97bf1 commit f002afa
Show file tree
Hide file tree
Showing 44 changed files with 29,922 additions and 260 deletions.
3 changes: 3 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ module.file_ext=.js
module.file_ext=.njs
module.ignore_non_literal_requires=true
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore

[lints]
deprecated-declare-exports=off
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.

## 0.11.19
## 0.12.0 (2018-02-08)

* Extendscript target script in NPM package

## 0.11.19 (2018-02-03)

* Error on empty workbook

Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
misc/strip_sourcemap.sh dist/$(LIB).core.min.js
uglifyjs $(DISTHDR) $(REQS) $(ADDONS) dist/$(TARGET) $(AUXTARGETS) $(UGLIFYOPTS) -o dist/$(LIB).full.min.js --source-map dist/$(LIB).full.min.map --preamble "$$(head -n 1 bits/00_header.js)"
misc/strip_sourcemap.sh dist/$(LIB).full.min.js
cat <(head -n 1 bits/00_header.js) shim.js $(DISTHDR) $(REQS) dist/$(TARGET) > dist/$(LIB).extendscript.js

.PHONY: dist-deps
dist-deps: ## Copy dependencies for distribution
Expand All @@ -73,7 +74,7 @@ dist-deps: ## Copy dependencies for distribution
.PHONY: aux
aux: $(AUXTARGETS)

BYTEFILE=dist/xlsx.min.js dist/xlsx.{core,full}.min.js
BYTEFILE=dist/xlsx.min.js dist/xlsx.{core,full}.min.js dist/xlsx.extendscript.js
.PHONY: bytes
bytes: ## Display minified and gzipped file sizes
for i in $(BYTEFILE); do printj "%-30s %7d %10d" $$i $$(wc -c < $$i) $$(gzip --best --stdout $$i | wc -c); done
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,23 @@ var workbook = XLSX.readFile('test.xlsx');

</details>

<details>
<summary><b>Photoshop ExtendScript read a file</b> (click to show)</summary>

`readFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
The specified path should be an absolute path:

```js
#include "xlsx.extendscript.js"
/* Read test.xlsx from the Documents folder */
var workbook = XLSX.readFile(Folder.myDocuments + '/' + 'test.xlsx');
/* DO SOMETHING WITH workbook HERE */
```

The [`extendscript` demo](demos/extendscript/) includes a more complex example.

</details>

<details>
<summary><b>Browser read TABLE element from page</b> (click to show)</summary>

Expand Down Expand Up @@ -604,6 +621,23 @@ XLSX.writeFile(workbook, 'out.xlsb');

</details>

<details>
<summary><b>Photoshop ExtendScript write a file</b> (click to show)</summary>

`writeFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
The specified path should be an absolute path:

```js
#include "xlsx.extendscript.js"
/* output format determined by filename */
XLSX.writeFile(workbook, 'out.xlsx');
/* at this point, out.xlsx is a file that you can distribute */
```

The [`extendscript` demo](demos/extendscript/) includes a more complex example.

</details>

<details>
<summary><b>Browser add TABLE element to page</b> (click to show)</summary>

Expand Down
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.11.19';
XLSX.version = '0.12.0';
23 changes: 21 additions & 2 deletions bits/19_fsutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function blobify(data) {
}
/* write or download file */
function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
/*global IE_SaveFile, Blob, navigator, saveAs, URL, document */
/*global IE_SaveFile, Blob, navigator, saveAs, URL, document, File */
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
var data = (enc == "utf8") ? utf8write(payload) : payload;
/*:: declare var IE_SaveFile: any; */
Expand All @@ -32,6 +32,25 @@ function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
}
}
}
throw new Error("cannot initiate download");
// $FlowIgnore
if(typeof $ !== 'undefined' && typeof File !== 'undefined' && typeof Folder !== 'undefined') try { // extendscript
// $FlowIgnore
var out = File(fname); out.open("w"); out.encoding = "binary";
if(Array.isArray(payload)) payload = a2s(payload);
out.write(payload); out.close(); return payload;
} catch(e) { if(!e.message || !e.message.match(/onstruct/)) throw e; }
throw new Error("cannot save file " + fname);
}

/* read binary data from file */
function read_binary(path/*:string*/) {
if(typeof _fs !== 'undefined') return _fs.readFileSync(path);
// $FlowIgnore
if(typeof $ !== 'undefined' && typeof File !== 'undefined' && typeof Folder !== 'undefined') try { // extendscript
// $FlowIgnore
var infile = File(path); infile.open("r"); infile.encoding = "binary";
var data = infile.read(); infile.close();
return data;
} catch(e) { if(!e.message || !e.message.match(/onstruct/)) throw e; }
throw new Error("Cannot access file " + path);
}
28 changes: 14 additions & 14 deletions bits/23_binutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ function read_double_le(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ {
var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f);
var m = (b[idx+6]&0x0f);
for(var i = 5; i >= 0; --i) m = m * 256 + b[idx + i];
if(e == 0x7ff) return m == 0 ? s * Infinity : NaN;
if(e == 0x7ff) return m == 0 ? (s * Infinity) : NaN;
if(e == 0) e = -1022;
else { e -= 1023; m += Math.pow(2,52); }
return s * Math.pow(2, e - 52) * m;
}

function write_double_le(b/*:RawBytes|CFBlob*/, v/*:number*/, idx/*:number*/) {
var bs = ((v < 0 || 1/v == -Infinity) ? 1 : 0) << 7, e = 0, m = 0;
var av = bs ? -v : v;
var bs = ((((v < 0) || (1/v == -Infinity)) ? 1 : 0) << 7), e = 0, m = 0;
var av = bs ? (-v) : v;
if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; }
else if(av == 0) e = m = 0;
else {
e = Math.floor(Math.log(av) / Math.LN2);
m = av * Math.pow(2, 52 - e);
if(e <= -1023 && (!isFinite(m) || m < Math.pow(2,52))) { e = -1022; }
if((e <= -1023) && (!isFinite(m) || (m < Math.pow(2,52)))) { e = -1022; }
else { m -= Math.pow(2,52); e+=1023; }
}
for(var i = 0; i <= 5; ++i, m/=256) b[idx + i] = m & 0xff;
b[idx + 6] = ((e & 0x0f) << 4) | m & 0xf;
b[idx + 6] = ((e & 0x0f) << 4) | (m & 0xf);
b[idx + 7] = (e >> 4) | bs;
}

Expand Down Expand Up @@ -73,8 +73,8 @@ if(typeof cptable !== 'undefined') {
}

var __readUInt8 = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return b[idx]; };
var __readUInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return b[idx+1]*(1<<8)+b[idx]; };
var __readInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { var u = b[idx+1]*(1<<8)+b[idx]; return (u < 0x8000) ? u : (0xffff - u + 1) * -1; };
var __readUInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx+1]*(1<<8))+b[idx]; };
var __readInt16LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { var u = (b[idx+1]*(1<<8))+b[idx]; return (u < 0x8000) ? u : ((0xffff - u + 1) * -1); };
var __readUInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; };
var __readInt32LE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; };
var __readInt32BE = function(b/*:RawBytes|CFBlob*/, idx/*:number*/)/*:number*/ { return (b[idx]<<24)|(b[idx+1]<<16)|(b[idx+2]<<8)|b[idx+3]; };
Expand All @@ -85,7 +85,7 @@ function ReadShift(size/*:number*/, t/*:?string*/)/*:number|string*/ {
case 'dbcs':
loc = this.l;
if(has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l+2*size).toString("utf16le");
else for(i = 0; i != size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; }
else for(i = 0; i < size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; }
size *= 2;
break;

Expand Down Expand Up @@ -116,7 +116,7 @@ function ReadShift(size/*:number*/, t/*:?string*/)/*:number|string*/ {

/* sbcs and dbcs support continue records in the SST way TODO codepages */
case 'dbcs-cont': o = ""; loc = this.l;
for(i = 0; i != size; ++i) {
for(i = 0; i < size; ++i) {
if(this.lens && this.lens.indexOf(loc) !== -1) {
w = __readUInt8(this, loc);
this.l = loc + 1;
Expand Down Expand Up @@ -150,7 +150,7 @@ function ReadShift(size/*:number*/, t/*:?string*/)/*:number|string*/ {
case 1: oI = __readUInt8(this, this.l); this.l++; return oI;
case 2: oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); this.l += 2; return oI;
case 4: case -4:
if(t === 'i' || (this[this.l+3] & 0x80)===0) { oI = (size > 0 ? __readInt32LE : __readInt32BE)(this, this.l); this.l += 4; return oI; }
if(t === 'i' || ((this[this.l+3] & 0x80)===0)) { oI = ((size > 0) ? __readInt32LE : __readInt32BE)(this, this.l); this.l += 4; return oI; }
else { oR = __readUInt32LE(this, this.l); this.l += 4; } return oR;
case 8: case -8:
if(t === 'f') {
Expand Down Expand Up @@ -179,20 +179,20 @@ function WriteShift(t/*:number*/, val/*:string|number*/, f/*:?string*/)/*:any*/
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
val = val.replace(/[^\x00-\x7F]/g, "_");
/*:: if(typeof val !== 'string') throw new Error("unreachable"); */
for(i = 0; i != val.length; ++i) this[this.l + i] = val.charCodeAt(i) & 0xFF;
for(i = 0; i != val.length; ++i) this[this.l + i] = (val.charCodeAt(i) & 0xFF);
size = val.length;
} else if(f === 'hex') {
for(; i < t; ++i) {
/*:: if(typeof val !== "string") throw new Error("unreachable"); */
this[this.l++] = parseInt(val.slice(2*i, 2*i+2), 16)||0;
this[this.l++] = (parseInt(val.slice(2*i, 2*i+2), 16)||0);
} return this;
} else if(f === 'utf16le') {
/*:: if(typeof val !== "string") throw new Error("unreachable"); */
var end/*:number*/ = this.l + t;
for(i = 0; i < Math.min(val.length, t); ++i) {
var cc = val.charCodeAt(i);
this[this.l++] = cc & 0xff;
this[this.l++] = cc >> 8;
this[this.l++] = (cc & 0xff);
this[this.l++] = (cc >> 8);
}
while(this.l < end) this[this.l++] = 0;
return this;
Expand Down
6 changes: 3 additions & 3 deletions bits/24_hoppers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ function buf_array()/*:BufArray*/ {

var endbuf = function ba_endbuf() {
if(!curbuf) return;
if(curbuf.length > curbuf.l) curbuf = curbuf.slice(0, curbuf.l);
if(curbuf.length > curbuf.l) { curbuf = curbuf.slice(0, curbuf.l); curbuf.l = curbuf.length; }
if(curbuf.length > 0) bufs.push(curbuf);
curbuf = null;
};

var next = function ba_next(sz/*:number*/)/*:Block*/ {
if(curbuf && sz < curbuf.length - curbuf.l) return curbuf;
if(curbuf && (sz < (curbuf.length - curbuf.l))) return curbuf;
endbuf();
return (curbuf = newblk(Math.max(sz+1, blksz)));
};
Expand All @@ -47,7 +47,7 @@ function buf_array()/*:BufArray*/ {
return __toBuffer([bufs]);
};

var push = function ba_push(buf) { endbuf(); curbuf = buf; next(blksz); };
var push = function ba_push(buf) { endbuf(); curbuf = buf; if(curbuf.l == null) curbuf.l = curbuf.length; next(blksz); };

return ({ next:next, push:push, end:end, _bufs:bufs }/*:any*/);
}
Expand Down
8 changes: 4 additions & 4 deletions bits/28_binstructs.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@ var write_RelID = write_XLNullableWideString;
/* [MS-XLS] 2.5.217 */
function parse_RkNumber(data)/*:number*/ {
var b = data.slice(data.l, data.l+4);
var fX100 = b[0] & 1, fInt = b[0] & 2;
var fX100 = (b[0] & 1), fInt = (b[0] & 2);
data.l+=4;
b[0] &= 0xFC; // b[0] &= ~3;
var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
return fX100 ? RK/100 : RK;
return fX100 ? (RK/100) : RK;
}
function write_RkNumber(data/*:number*/, o) {
if(o == null) o = new_buf(4);
var fX100 = 0, fInt = 0, d100 = data * 100;
if(data == (data | 0) && data >= -(1<<29) && data < (1 << 29)) { fInt = 1; }
else if(d100 == (d100 | 0) && d100 >= -(1<<29) && d100 < (1 << 29)) { fInt = 1; fX100 = 1; }
if((data == (data | 0)) && (data >= -(1<<29)) && (data < (1 << 29))) { fInt = 1; }
else if((d100 == (d100 | 0)) && (d100 >= -(1<<29)) && (d100 < (1 << 29))) { fInt = 1; fX100 = 1; }
if(fInt) o.write_shift(-4, ((fX100 ? d100 : data) << 2) + (fX100 + 2));
else throw new Error("unsupported RkNumber " + data); // TODO
}
Expand Down
34 changes: 27 additions & 7 deletions bits/39_xlsbiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ function write_BoundSheet8(data, opts) {
o.write_shift(1, data.name.length);
if(opts.biff >= 8) o.write_shift(1, 1);
o.write_shift(w * data.name.length, data.name, opts.biff < 8 ? 'sbcs' : 'utf16le');
return o.slice(0, o.l);
var out = o.slice(0, o.l);
out.l = o.l; return out;
}

/* 2.4.265 TODO */
Expand Down Expand Up @@ -393,10 +394,10 @@ function parse_Label(blob, length, opts) {
cell.val = str;
return cell;
}
function write_Label(R/*:number*/, C/*:number*/, v/*:string*/, opts) {
function write_Label(R/*:number*/, C/*:number*/, v/*:string*/, os/*:number*/, opts) {
var b8 = !opts || opts.biff == 8;
var o = new_buf(6 + 2 + (+b8) + (1 + b8) * v.length);
write_XLSCell(R, C, 0, o);
write_XLSCell(R, C, os, o);
o.write_shift(2, v.length);
if(b8) o.write_shift(1, 1);
o.write_shift((1 + b8) * v.length, v, b8 ? 'utf16le' : 'sbcs');
Expand All @@ -410,6 +411,14 @@ function parse_Format(blob, length, opts) {
var fmtstr = parse_XLUnicodeString2(blob, 0, opts);
return [numFmtId, fmtstr];
}
function write_Format(i/*:number*/, f/*:string*/, o) {
if(!o) o = new_buf(6 + 4 * f.length);
o.write_shift(2, i);
write_XLUnicodeString(f, null, o);
var out = (o.length > o.l) ? o.slice(0, o.l) : o;
if(o.l == null) o.l = o.length;
return out;
}
var parse_BIFF2Format = parse_XLUnicodeString2;

/* 2.4.90 */
Expand Down Expand Up @@ -515,6 +524,17 @@ function parse_XF(blob, length, opts) {
o.data = parse_CellStyleXF(blob, length, o.fStyle, opts);
return o;
}
function write_XF(data, ixfeP, o) {
if(!o) o = new_buf(20);
o.write_shift(2, 0);
o.write_shift(2, data.numFmtId||0);
o.write_shift(2, 0);
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(2, 0);
return o;
}

/* 2.4.134 */
function parse_Guts(blob) {
Expand Down Expand Up @@ -542,9 +562,9 @@ function parse_BoolErr(blob, length, opts) {
cell.t = (val === true || val === false) ? 'b' : 'e';
return cell;
}
function write_BoolErr(R/*:number*/, C/*:number*/, v, opts, t/*:string*/) {
function write_BoolErr(R/*:number*/, C/*:number*/, v, os/*:number*/, opts, t/*:string*/) {
var o = new_buf(8);
write_XLSCell(R, C, 0, o);
write_XLSCell(R, C, os, o);
write_Bes(v, t, o);
return o;
}
Expand All @@ -556,9 +576,9 @@ function parse_Number(blob) {
cell.val = xnum;
return cell;
}
function write_Number(R/*:number*/, C/*:number*/, v/*::, opts*/) {
function write_Number(R/*:number*/, C/*:number*/, v, os/*:: :number, opts*/) {
var o = new_buf(14);
write_XLSCell(R, C, 0, o);
write_XLSCell(R, C, os, o);
write_Xnum(v, o);
return o;
}
Expand Down
2 changes: 1 addition & 1 deletion bits/40_harb.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ var SYLK = (function() {
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
cell = dense ? (ws[R]||[])[C]: ws[coord];
if(!cell || cell.v == null && (!cell.f || cell.F)) continue;
if(!cell || (cell.v == null && (!cell.f || cell.F))) continue;
o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
}
}
Expand Down
4 changes: 3 additions & 1 deletion bits/48_stybin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ function write_BrtFmt(i/*:number*/, f/*:string*/, o) {
if(!o) o = new_buf(6 + 4 * f.length);
o.write_shift(2, i);
write_XLWideString(f, o);
return o.length > o.l ? o.slice(0, o.l) : o;
var out = (o.length > o.l) ? o.slice(0, o.l) : o;
if(o.l == null) o.l = o.length;
return out;
}

/* [MS-XLSB] 2.4.653 BrtFont TODO */
Expand Down
4 changes: 2 additions & 2 deletions bits/59_vba.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ function make_vba_xls(cfb/*:CFBContainer*/) {
var newcfb = CFB.utils.cfb_new({root:"R"});
cfb.FullPaths.forEach(function(p, i) {
if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
var newpath = p.replace(/^[^/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
var newpath = p.replace(/^[^\/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
});
return CFB.write(newcfb);
Expand All @@ -12,7 +12,7 @@ function make_vba_xls(cfb/*:CFBContainer*/) {
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/");
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
Expand Down
2 changes: 1 addition & 1 deletion bits/67_wsxml.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ function write_ws_xml_data(ws/*:Worksheet*/, opts, idx/*:number*/, wb/*:Workbook
if(_cell === undefined) continue;
if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0 || rows && rows[R]) {
if(r.length > 0 || (rows && rows[R])) {
params = ({r:rr}/*:any*/);
if(rows && rows[R]) {
row = rows[R];
Expand Down
Loading

0 comments on commit f002afa

Please sign in to comment.