version bump 0.17.4: Lotus WK3 workbook writer

This commit is contained in:
SheetJS 2021-11-13 23:38:00 -05:00
parent 9e9e4bcb45
commit fcf9182fa6
34 changed files with 3804 additions and 639 deletions

View File

@ -49,6 +49,7 @@ SQLite
SystemJS
VueJS
WebSQL
WK_
iOS
nodejs
node.js

View File

@ -4,6 +4,10 @@ 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.
## v0.17.4
* CLI script moved to `xlsx-cli` package
## v0.17.3
* `window.XLSX` explicit assignment to satiate LWC

View File

@ -1381,11 +1381,12 @@ prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
| RC-style strings | XLML and plain text | โœ” | โœ” |
| BIFF Parsed formulae | XLSB and all XLS formats | โœ” | |
| OpenFormula formulae | ODS/FODS/UOS | โœ” | โœ” |
| Lotus Parsed formulae | All Lotus WK_ formats | โœ” | |
Since Excel prohibits named cells from colliding with names of A1 or RC style
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
formulae have to be explicitly unwound. OpenFormula formulae can be converted
with regular expressions.
formulae and Lotus Parsed formulae have to be explicitly unwound. OpenFormula
formulae can be converted with regular expressions.
</details>
#### Column Properties
@ -1776,6 +1777,7 @@ The exported `read` and `readFile` functions accept an options argument:
|`sheets` | | If specified, only parse specified sheets ** |
|`PRN` | false | If true, allow parsing of PRN files ** |
|`xlfn` | false | If true, preserve `_xlfn.` prefixes in formulae ** |
|`FS` | | DSV Field Separator override |
- Even if `cellNF` is false, formatted text will be generated and saved to `.w`
- In some cases, sheets may be parsed even if `bookSheets` is false.
@ -1939,12 +1941,14 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | none | single | Lotus Workbook (WK3) |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `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) |
| `wk1` | `.wk1` | none | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
| `eth` | `.eth` | none | single | Ethercalc Record Format (ETH) |
@ -2481,7 +2485,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Flat XML ODF Spreadsheet (FODS) | โœ” | โœ” |
| Uniform Office Format Spreadsheet (ๆ ‡ๆ–‡้€š UOS1/UOS2) | โœ” | |
| dBASE II/III/IV / Visual FoxPro (DBF) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | โœ” | |
| Lotus 1-2-3 (WK1/WK3) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK2/WK4/123) | โœ” | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | โœ” | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | โœ” | โœ” |
@ -2619,6 +2624,10 @@ The Lotus formats consist of binary records similar to the BIFF structure. Lotus
did release a specification decades ago covering the original WK1 format. Other
features were deduced by producing files and comparing to Excel support.
Generated WK1 worksheets are compatible with Lotus 1-2-3 R2 and Excel 5.0.
Generated WK3 workbooks are compatible with Lotus 1-2-3 R9 and Excel 5.0.
</details>
#### Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW)

View File

@ -43,6 +43,7 @@ program
.option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)')
.option('--wk3', 'emit WK3 to <sheetname> or <file>.txt (Lotus WK3)')
.option('-S, --formulae', 'emit list of values and formulae')
.option('-j, --json', 'emit formatted JSON (all fields text)')
@ -90,7 +91,8 @@ var workbook_formats = [
['xla', 'xla', 'xla'],
['biff5', 'biff5', 'xls'],
['ods', 'ods', 'ods'],
['fods', 'fods', 'fods']
['fods', 'fods', 'fods'],
['wk3', 'wk3', 'wk3']
];
var wb_formats_2 = [
['xlml', 'xlml', 'xls']

View File

@ -1 +1 @@
XLSX.version = '0.17.3';
XLSX.version = '0.17.4';

View File

@ -66,7 +66,11 @@ var DocSummaryPIDDSI = {
/*::[*/0x1B/*::]*/: { n: 'ContentStatus', t: VT_STRING },
/*::[*/0x1C/*::]*/: { n: 'Language', t: VT_STRING },
/*::[*/0x1D/*::]*/: { n: 'Version', t: VT_STRING },
/*::[*/0xFF/*::]*/: {}
/*::[*/0xFF/*::]*/: {},
/* [MS-OLEPS] 2.18 */
/*::[*/0x80000000/*::]*/: { n: 'Locale', t: VT_UI4 },
/*::[*/0x80000003/*::]*/: { n: 'Behavior', t: VT_UI4 },
/*::[*/0x72627262/*::]*/: {}
};
/* [MS-OSHARED] 2.3.3.2.1.1 Summary Information Property Set PIDSI */
@ -90,21 +94,13 @@ var SummaryPIDSI = {
/*::[*/0x11/*::]*/: { n: 'Thumbnail', t: VT_CF },
/*::[*/0x12/*::]*/: { n: 'Application', t: VT_STRING },
/*::[*/0x13/*::]*/: { n: 'DocSecurity', t: VT_I4 },
/*::[*/0xFF/*::]*/: {}
};
/* [MS-OLEPS] 2.18 */
var SpecialProperties = {
/*::[*/0xFF/*::]*/: {},
/* [MS-OLEPS] 2.18 */
/*::[*/0x80000000/*::]*/: { n: 'Locale', t: VT_UI4 },
/*::[*/0x80000003/*::]*/: { n: 'Behavior', t: VT_UI4 },
/*::[*/0x72627262/*::]*/: {}
};
(function() {
for(var y in SpecialProperties) if(Object.prototype.hasOwnProperty.call(SpecialProperties, y))
DocSummaryPIDDSI[y] = SummaryPIDSI[y] = SpecialProperties[y];
})();
var DocSummaryRE/*:{[key:string]:string}*/ = evert_key(DocSummaryPIDDSI, "n");
var SummaryRE/*:{[key:string]:string}*/ = evert_key(SummaryPIDSI, "n");

View File

@ -29,7 +29,7 @@ var WK_ = /*#__PURE__*/ (function() {
var o = opts || {};
if(DENSE != null && o.dense == null) o.dense = DENSE;
var s/*:Worksheet*/ = ((o.dense ? [] : {})/*:any*/), n = "Sheet1", sidx = 0;
var sheets = {}, snames = [n];
var sheets = {}, snames = [n], realnames = [];
var refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
var sheetRows = o.sheetRows || 0;
@ -37,28 +37,36 @@ var WK_ = /*#__PURE__*/ (function() {
if(d[2] == 0x02) {
o.Enum = WK1Enum;
lotushopper(d, function(val, R, RT) { switch(RT) {
case 0x00:
case 0x00: /* BOF */
o.vers = val;
if(val >= 0x1000) o.qpro = true;
break;
case 0x06: refguess = val; break; /* RANGE */
case 0x0F: /* LABEL */
case 0x33: /* STRING */
if(!o.qpro) val[1].v = val[1].v.slice(1);
/* falls through */
case 0x0D: /* INTEGER */
case 0x0E: /* NUMBER */
case 0x10: /* FORMULA */
case 0x33: /* STRING */
/* TODO: actual translation of the format code */
if(RT == 0x0E && (val[2] & 0x70) == 0x70 && (val[2] & 0x0F) > 1 && (val[2] & 0x0F) < 15) {
val[1].z = o.dateNF || SSF._table[14];
if(o.cellDates) { val[1].t = 'd'; val[1].v = numdate(val[1].v); }
}
var tmpcell = o.dense ? (s[val[0].r]||[])[val[0].c] : s[encode_cell(val[0])];
if(tmpcell) {
tmpcell.t = val[1].t; tmpcell.v = val[1].v;
if(val[1].z != null) tmpcell.z = val[1].z;
if(val[1].f != null) tmpcell.f = val[1].f;
break;
}
if(o.dense) {
if(!s[val[0].r]) s[val[0].r] = [];
s[val[0].r][val[0].c] = val[1];
} else s[encode_cell(val[0])] = val[1];
break;
default:
}}, o);
} else if(d[2] == 0x1A || d[2] == 0x0E) {
o.Enum = WK3Enum;
@ -89,19 +97,28 @@ var WK_ = /*#__PURE__*/ (function() {
if(refguess.e.c < val[0].c) refguess.e.c = val[0].c;
if(refguess.e.r < val[0].r) refguess.e.r = val[0].r;
break;
case 0x1B: /* XFORMAT */
if(val[0x36b0]) realnames[val[0x36b0][0]] = val[0x36b0][1];
break;
default: break;
}}, o);
} else throw new Error("Unrecognized LOTUS BOF " + d[2]);
s["!ref"] = encode_range(refguess);
sheets[n] = s;
return { SheetNames: snames, Sheets:sheets };
if(!realnames.length) return { SheetNames: snames, Sheets: sheets };
var osheets = {}, rnames = [];
for(var i = 0; i < realnames.length; ++i) if(sheets[snames[i]]) {
rnames.push(realnames[i]);
osheets[realnames[i]] = sheets[snames[i]];
}
return { SheetNames: rnames, Sheets: osheets };
}
function sheet_to_wk1(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(+o.codepage >= 0) set_cp(+o.codepage);
if(o.type == "string") throw new Error("Cannot write DBF to JS string");
if(o.type == "string") throw new Error("Cannot write WK1 to JS string");
var ba = buf_array();
var range = safe_decode_range(ws["!ref"]);
var dense = Array.isArray(ws);
@ -116,7 +133,7 @@ var WK_ = /*#__PURE__*/ (function() {
var ref = cols[C] + rr;
var cell = dense ? (ws[R]||[])[C] : ws[ref];
if(!cell || cell.t == "z") continue;
/* write cell */
/* TODO: formula records */
if(cell.t == "n") {
if((cell.v|0)==cell.v && cell.v >= -32768 && cell.v <= 32767) write_biff_rec(ba, 0x0d, write_INTEGER(R, C, cell.v));
else write_biff_rec(ba, 0x0e, write_NUMBER(R, C, cell.v));
@ -131,12 +148,81 @@ var WK_ = /*#__PURE__*/ (function() {
return ba.end();
}
function book_to_wk3(wb/*:Workbook*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(+o.codepage >= 0) set_cp(+o.codepage);
if(o.type == "string") throw new Error("Cannot write WK3 to JS string");
var ba = buf_array();
write_biff_rec(ba, 0x00, write_BOF_WK3(wb));
for(var i = 0, cnt = 0; i < wb.SheetNames.length; ++i) if((wb.Sheets[wb.SheetNames[i]] || {})["!ref"]) write_biff_rec(ba, 0x1b, write_XFORMAT_SHEETNAME(wb.SheetNames[i], cnt++));
var wsidx = 0;
for(i = 0; i < wb.SheetNames.length; ++i) {
var ws = wb.Sheets[wb.SheetNames[i]];
if(!ws || !ws["!ref"]) continue;
var range = safe_decode_range(ws["!ref"]);
var dense = Array.isArray(ws);
var cols = [];
for(var R = range.s.r; R <= range.e.r; ++R) {
var rr = encode_row(R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R === range.s.r) cols[C] = encode_col(C);
var ref = cols[C] + rr;
var cell = dense ? (ws[R]||[])[C] : ws[ref];
if(!cell || cell.t == "z") continue;
/* TODO: FORMULA19 NUMBER18 records */
if(cell.t == "n") {
write_biff_rec(ba, 0x17, write_NUMBER_17(R, C, wsidx, cell.v));
} else {
var str = format_cell(cell);
/* TODO: max len? */
write_biff_rec(ba, 0x16, write_LABEL_16(R, C, wsidx, str.slice(0, 239)));
}
}
}
++wsidx;
}
write_biff_rec(ba, 0x01);
return ba.end();
}
function write_BOF_WK1(v/*:number*/) {
var out = new_buf(2);
out.write_shift(2, v);
return out;
}
function write_BOF_WK3(wb/*:Workbook*/) {
var out = new_buf(26);
out.write_shift(2, 0x1000);
out.write_shift(2, 0x0004);
out.write_shift(4, 0x0000);
var rows = 0, cols = 0, wscnt = 0;
for(var i = 0; i < wb.SheetNames.length; ++i) {
var name = wb.SheetNames[i];
var ws = wb.Sheets[name];
if(!ws || !ws["!ref"]) continue;
++wscnt;
var range = decode_range(ws["!ref"]);
if(rows < range.e.r) rows = range.e.r;
if(cols < range.e.c) cols = range.e.c;
}
out.write_shift(2, rows);
out.write_shift(1, wscnt);
out.write_shift(1, cols);
out.write_shift(2, 0x00);
out.write_shift(2, 0x00);
out.write_shift(1, 0x01);
out.write_shift(1, 0x02);
out.write_shift(4, 0);
out.write_shift(4, 0);
return out;
}
function parse_RANGE(blob) {
var o = {s:{c:0,r:0},e:{c:0,r:0}};
o.s.c = blob.read_shift(2);
@ -234,11 +320,105 @@ var WK_ = /*#__PURE__*/ (function() {
if(opts.qpro) blob.l = tgt;
else {
var flen = blob.read_shift(2);
wk1_fmla_to_csf(blob.slice(blob.l, blob.l + flen), o);
blob.l += flen;
}
return o;
}
function wk1_parse_rc(B, V, col) {
var rel = V & 0x8000;
V &= ~0x8000;
V = (rel ? B : 0) + ((V >= 0x2000) ? V - 0x4000 : V);
return (rel ? "" : "$") + (col ? encode_col(V) : encode_row(V));
}
var oprec = [
8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 4, 5, 5, 7, 3, 3,
3, 3, 3, 3, 1, 1, 2, 6, 8, 8, 8, 8, 8, 8, 8, 8
];
/* TODO: flesh out */
var FuncTab = {
0x33: ["FALSE", 0],
0x34: ["TRUE", 0],
0x46: ["LEN", 1],
0x50: ["SUM", 69],
0x51: ["AVERAGEA", 69],
0x52: ["COUNTA", 69],
0x53: ["MINA", 69],
0x54: ["MAXA", 69],
0x6F: ["T", 1]
};
var BinOpTab = [
"", "", "", "", "", "", "", "",
"", "+", "-", "*", "/", "^", "=", "<>",
"<=", ">=", "<", ">", "", "", "", "",
"&", "", "", "", "", "", "", ""
];
function wk1_fmla_to_csf(blob, o) {
prep_blob(blob, 0);
var out = [], argc = 0, R = "", C = "";
while(blob.l < blob.length) {
var cc = blob[blob.l++];
switch(cc) {
case 0x00: out.push(blob.read_shift(8, 'f')); break;
case 0x01: {
C = wk1_parse_rc(o[0].c, blob.read_shift(2), true);
R = wk1_parse_rc(o[0].r, blob.read_shift(2), false);
out.push(C + R);
} break;
case 0x02: {
var c = wk1_parse_rc(o[0].c, blob.read_shift(2), true);
var r = wk1_parse_rc(o[0].r, blob.read_shift(2), false);
C = wk1_parse_rc(o[0].c, blob.read_shift(2), true);
R = wk1_parse_rc(o[0].r, blob.read_shift(2), false);
out.push(c + r + ":" + C + R);
} break;
case 0x03:
if(blob.l < blob.length) { console.error("WK1 premature formula end"); return; }
break;
case 0x04: out.push("(" + out.pop() + ")"); break;
case 0x05: out.push(blob.read_shift(2)); break;
case 0x06: {
/* TODO: text encoding */
var Z = ""; while((cc = blob[blob.l++])) Z += String.fromCharCode(cc);
out.push('"' + Z.replace(/"/g, '""') + '"'); break;
} break;
case 0x08: out.push("-" + out.pop()); break;
case 0x17: out.push("+" + out.pop()); break;
case 0x16: out.push("NOT(" + out.pop() + ")"); break;
case 0x14: case 0x15: {
var argR = out.pop(), argL = out.pop();
out.push(["AND", "OR"][cc - 0x14] + "(" + argL + "," + argR + ")");
} break;
default:
if(cc < 0x20 && BinOpTab[cc]) {
argR = out.pop(); argL = out.pop();
out.push(argL + BinOpTab[cc] + argR);
} else if(FuncTab[cc]) {
argc = FuncTab[cc][1];
if(argc == 69) argc = blob[blob.l++];
if(argc > out.length) { console.error("WK1 bad formula parse 0x" + cc.toString(16) + ":|" + out.join("|") + "|"); return; }
var args = out.slice(-argc);
out.length -= argc;
out.push(FuncTab[cc][0] + "(" + args.join(",") + ")");
}
else if(cc <= 0x07) return console.error("WK1 invalid opcode " + cc.toString(16));
else if(cc <= 0x18) return console.error("WK1 unsupported op " + cc.toString(16));
else if(cc <= 0x1E) return console.error("WK1 invalid opcode " + cc.toString(16));
else if(cc <= 0x73) return console.error("WK1 unsupported function opcode " + cc.toString(16));
// possible future functions ??
else return console.error("WK1 unrecognized opcode " + cc.toString(16));
}
}
if(out.length == 1) o[1].f = "" + out[0];
else console.error("WK1 bad formula parse |" + out.join("|") + "|");
}
function parse_cell_3(blob/*::, length*/) {
var o = [{c:0,r:0}, {t:'n',v:0}, 0];
o[0].r = blob.read_shift(2); o[3] = blob[blob.l++]; o[0].c = blob[blob.l++];
@ -251,6 +431,20 @@ var WK_ = /*#__PURE__*/ (function() {
o[1].v = blob.read_shift(length - 4, 'cstr');
return o;
}
function write_LABEL_16(R, C, wsidx, s) {
/* TODO: encoding */
var o = new_buf(6 + s.length);
o.write_shift(2, R);
o.write_shift(1, wsidx);
o.write_shift(1, C);
o.write_shift(1, 0x27);
for(var i = 0; i < s.length; ++i) {
var cc = s.charCodeAt(i);
o.write_shift(1, cc >= 0x80 ? 0x5F : cc);
}
o.write_shift(1, 0);
return o;
}
function parse_NUMBER_18(blob, length) {
var o = parse_cell_3(blob, length);
@ -284,13 +478,41 @@ var WK_ = /*#__PURE__*/ (function() {
return o;
}
var s = e & 0x8000; e = (e&0x7FFF) - 16446;
o[1].v = (1 - s*2) * ( /*(e > 0 ? (v2 << e) : (v2 >>> -e))*/ v2 * Math.pow(2, e+32) + v1 * Math.pow(2, e));
o[1].v = (1 - s*2) * (v2 * Math.pow(2, e+32) + v1 * Math.pow(2, e));
return o;
}
function write_NUMBER_17(R, C, wsidx, v) {
var o = new_buf(14);
o.write_shift(2, R);
o.write_shift(1, wsidx);
o.write_shift(1, C);
if(v == 0) {
o.write_shift(4, 0);
o.write_shift(4, 0);
o.write_shift(2, 0xFFFF);
return o;
}
var s = 0, e = 0, v1 = 0, v2 = 0;
if(v < 0) { s = 1; v = -v; }
e = Math.log2(v) | 0;
v /= Math.pow(2, e-31);
v2 = (v)>>>0;
if((v2&0x80000000) == 0) { v/=2; ++e; v2 = v >>> 0; }
v -= v2;
v2 |= 0x80000000;
v2 >>>= 0;
v *= Math.pow(2, 32);
v1 = v>>>0;
o.write_shift(4, v1);
o.write_shift(4, v2);
e += 0x3FFF + (s ? 0x8000 : 0);
o.write_shift(2, e);
return o;
}
function parse_FORMULA_19(blob, length) {
var o = parse_NUMBER_17(blob, 14);
blob.l += length - 14; /* TODO: formula */
blob.l += length - 14; /* TODO: WK3 formula */
return o;
}
@ -314,6 +536,31 @@ var WK_ = /*#__PURE__*/ (function() {
return o;
}
function parse_XFORMAT(blob, length) {
var o = {}, tgt = blob.l + length;
while(blob.l < tgt) {
var dt = blob.read_shift(2);
if(dt == 0x36b0) {
o[dt] = [0, ""];
o[dt][0] = blob.read_shift(2);
while(blob[blob.l]) { o[dt][1] += String.fromCharCode(blob[blob.l]); blob.l++; } blob.l++;
}
// TODO: 0x3a99 ??
}
return o;
}
function write_XFORMAT_SHEETNAME(name, wsidx) {
var out = new_buf(5 + name.length);
out.write_shift(2, 0x36b0);
out.write_shift(2, wsidx);
for(var i = 0; i < name.length; ++i) {
var cc = name.charCodeAt(i);
out[out.l++] = cc > 0x7F ? 0x5F : cc;
}
out[out.l++] = 0;
return out;
}
var WK1Enum = {
/*::[*/0x0000/*::]*/: { n:"BOF", f:parseuint16 },
/*::[*/0x0001/*::]*/: { n:"EOF" },
@ -407,7 +654,7 @@ var WK_ = /*#__PURE__*/ (function() {
/*::[*/0x0018/*::]*/: { n:"NUMBER18", f:parse_NUMBER_18 },
/*::[*/0x0019/*::]*/: { n:"FORMULA19", f:parse_FORMULA_19},
/*::[*/0x001A/*::]*/: { n:"FORMULA1A" },
/*::[*/0x001B/*::]*/: { n:"XFORMAT" },
/*::[*/0x001B/*::]*/: { n:"XFORMAT", f:parse_XFORMAT },
/*::[*/0x001C/*::]*/: { n:"DTLABELMISC" },
/*::[*/0x001D/*::]*/: { n:"DTLABELCELL" },
/*::[*/0x001E/*::]*/: { n:"GRAPHWINDOW" },
@ -504,6 +751,7 @@ var WK_ = /*#__PURE__*/ (function() {
};
return {
sheet_to_wk1: sheet_to_wk1,
book_to_wk3: book_to_wk3,
to_workbook: lotus_to_workbook
};
})();

View File

@ -106,6 +106,7 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'eth': return write_string_type(write_eth_str(wb, o), o);
case 'fods': return write_string_type(write_ods(wb, o), o);
case 'wk1': return write_binary_type(write_wk1_buf(wb, o), o);
case 'wk3': return write_binary_type(WK_.book_to_wk3(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);

33
dist/xlsx.core.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.core.min.map generated vendored

File diff suppressed because one or more lines are too long

640
dist/xlsx.extendscript.js generated vendored

File diff suppressed because it is too large Load Diff

28
dist/xlsx.full.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.full.min.map generated vendored

File diff suppressed because one or more lines are too long

640
dist/xlsx.js generated vendored

File diff suppressed because it is too large Load Diff

28
dist/xlsx.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map generated vendored

File diff suppressed because one or more lines are too long

16
dist/xlsx.mini.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.mini.min.map generated vendored

File diff suppressed because one or more lines are too long

View File

@ -86,10 +86,11 @@ prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
| RC-style strings | XLML and plain text | โœ” | โœ” |
| BIFF Parsed formulae | XLSB and all XLS formats | โœ” | |
| OpenFormula formulae | ODS/FODS/UOS | โœ” | โœ” |
| Lotus Parsed formulae | All Lotus WK_ formats | โœ” | |
Since Excel prohibits named cells from colliding with names of A1 or RC style
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
formulae have to be explicitly unwound. OpenFormula formulae can be converted
with regular expressions.
formulae and Lotus Parsed formulae have to be explicitly unwound. OpenFormula
formulae can be converted with regular expressions.
</details>

View File

@ -47,6 +47,7 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | none | single | Lotus Workbook (WK3) |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `sylk` | `.sylk` | none | single | Symbolic Link (SYLK) |

View File

@ -24,8 +24,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Flat XML ODF Spreadsheet (FODS) | โœ” | โœ” |
| Uniform Office Format Spreadsheet (ๆ ‡ๆ–‡้€š UOS1/UOS2) | โœ” | |
| dBASE II/III/IV / Visual FoxPro (DBF) | โœ” | โœ” |
| Lotus 1-2-3 (WK1) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK2/WK3/WK4/123) | โœ” | |
| Lotus 1-2-3 (WK1/WK3) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK2/WK4/123) | โœ” | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | โœ” | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | โœ” | โœ” |
@ -163,7 +163,9 @@ The Lotus formats consist of binary records similar to the BIFF structure. Lotus
did release a specification decades ago covering the original WK1 format. Other
features were deduced by producing files and comparing to Excel support.
Generated WK1 files are compatible with Lotus 1-2-3 v2 and Excel 5.0.
Generated WK1 worksheets are compatible with Lotus 1-2-3 R2 and Excel 5.0.
Generated WK3 workbooks are compatible with Lotus 1-2-3 R9 and Excel 5.0.
</details>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

After

Width:  |  Height:  |  Size: 443 KiB

View File

@ -1266,11 +1266,12 @@ prefixed with an apostrophe `'`, consistent with Excel's formula bar display.
| RC-style strings | XLML and plain text | โœ” | โœ” |
| BIFF Parsed formulae | XLSB and all XLS formats | โœ” | |
| OpenFormula formulae | ODS/FODS/UOS | โœ” | โœ” |
| Lotus Parsed formulae | All Lotus WK_ formats | โœ” | |
Since Excel prohibits named cells from colliding with names of A1 or RC style
cell references, a (not-so-simple) regex conversion is possible. BIFF Parsed
formulae have to be explicitly unwound. OpenFormula formulae can be converted
with regular expressions.
formulae and Lotus Parsed formulae have to be explicitly unwound. OpenFormula
formulae can be converted with regular expressions.
#### Column Properties
@ -1630,6 +1631,7 @@ The exported `read` and `readFile` functions accept an options argument:
|`sheets` | | If specified, only parse specified sheets ** |
|`PRN` | false | If true, allow parsing of PRN files ** |
|`xlfn` | false | If true, preserve `_xlfn.` prefixes in formulae ** |
|`FS` | | DSV Field Separator override |
- Even if `cellNF` is false, formatted text will be generated and saved to `.w`
- In some cases, sheets may be parsed even if `bookSheets` is false.
@ -1787,12 +1789,14 @@ output formats. The specific file type is controlled with `bookType` option:
| `xlml` | `.xls` | none | multi | Excel 2003-2004 (SpreadsheetML) |
| `ods` | `.ods` | ZIP | multi | OpenDocument Spreadsheet |
| `fods` | `.fods` | none | multi | Flat OpenDocument Spreadsheet |
| `wk3` | `.wk3` | none | single | Lotus Workbook (WK3) |
| `csv` | `.csv` | none | single | Comma Separated Values |
| `txt` | `.txt` | none | single | UTF-16 Unicode Text (TXT) |
| `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) |
| `wk1` | `.wk1` | none | single | Lotus Worksheet (WK1) |
| `rtf` | `.rtf` | none | single | Rich Text Format (RTF) |
| `prn` | `.prn` | none | single | Lotus Formatted Text |
| `eth` | `.eth` | none | single | Ethercalc Record Format (ETH) |
@ -2299,7 +2303,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
| Flat XML ODF Spreadsheet (FODS) | โœ” | โœ” |
| Uniform Office Format Spreadsheet (ๆ ‡ๆ–‡้€š UOS1/UOS2) | โœ” | |
| dBASE II/III/IV / Visual FoxPro (DBF) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK1/WK2/WK3/WK4/123) | โœ” | |
| Lotus 1-2-3 (WK1/WK3) | โœ” | โœ” |
| Lotus 1-2-3 (WKS/WK2/WK4/123) | โœ” | |
| Quattro Pro Spreadsheet (WQ1/WQ2/WB1/WB2/WB3/QPW) | โœ” | |
| **Other Common Spreadsheet Output Formats** |:-----:|:-----:|
| HTML Tables | โœ” | โœ” |
@ -2414,6 +2419,10 @@ The Lotus formats consist of binary records similar to the BIFF structure. Lotus
did release a specification decades ago covering the original WK1 format. Other
features were deduced by producing files and comparing to Excel support.
Generated WK1 worksheets are compatible with Lotus 1-2-3 R2 and Excel 5.0.
Generated WK3 workbooks are compatible with Lotus 1-2-3 R9 and Excel 5.0.
#### Quattro Pro (WQ1/WQ2/WB1/WB2/WB3/QPW)

View File

@ -52,6 +52,7 @@ digraph G {
csf -> fods
uos -> csf
wk3 -> csf
csf -> wk3
qpw -> csf
}
subgraph WORKSHEET {

View File

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.17.3",
"version": "0.17.4",
"author": "sheetjs",
"description": "SheetJS Spreadsheet data parser and writer",
"keywords": [

View File

@ -31,6 +31,7 @@ function run() {
.option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla')
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)')
.option('--wk3', 'emit WK3 to <sheetname> or <file>.txt (Lotus WK3)')
.option('-S, --formulae', 'emit list of values and formulae')
.option('-j, --json', 'emit formatted JSON (all fields text)')
@ -44,7 +45,7 @@ function run() {
.option('-E, --eth', 'emit ETH to <sheetname> or <file>.eth (Ethercalc)')
.option('-t, --txt', 'emit TXT to <sheetname> or <file>.txt (UTF-8 TSV)')
.option('-r, --rtf', 'emit RTF to <sheetname> or <file>.txt (Table RTF)')
.option('--wk1', 'emit WK1 to <sheetname> or <file>.txt (Lotus WK1)')
.option('--wk1', 'emit WK1 to <sheetname> or <file>.txt (Lotus WK1)')
.option('-z, --dump', 'dump internal representation as JSON')
.option('--props', 'dump workbook properties as CSV')
@ -63,9 +64,6 @@ function run() {
.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 + '/');
});
/* flag, bookType, default ext */
@ -78,7 +76,8 @@ function run() {
['xla', 'xla', 'xla'],
['biff5', 'biff5', 'xls'],
['ods', 'ods', 'ods'],
['fods', 'fods', 'fods']
['fods', 'fods', 'fods'],
['wk3', 'wk3', 'wk3']
];
var wb_formats_2 = [
['xlml', 'xlml', 'xls']

View File

@ -2160,16 +2160,16 @@ describe('HTML', function() {
var wb = X.read(table, {type:"string"});
assert.equal(get_cell(wb.Sheets.Sheet1, "A1").v, "foo\nbar");
});
it.skip('should generate multi-sheet workbooks', function() {
it('should generate multi-sheet workbooks', function() {
var table = "";
for(var i = 0; i < 4; ++i) table += "<table><tr><td>" + X.utils.encode_col(i) + "</td><td>" + i + "</td></tr></table>";
table += table; table += table;
var wb = X.read(table, {type: "string"});
assert.equal(wb.SheetNames.length, 4);
assert.equal(wb.SheetNames.length, 16);
assert.equal(wb.SheetNames[1], "Sheet2");
for(var j = 0; j < 4; ++j) {
assert.equal(get_cell(wb.Sheets.Sheet + (j+1), "A1"), X.utils.encode_col(j));
assert.equal(get_cell(wb.Sheets.Sheet + (j+1), "B1"), j);
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "A1").v, X.utils.encode_col(j));
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "B1").v, j);
}
});
});

12
tests/core.js generated
View File

@ -2160,6 +2160,18 @@ describe('HTML', function() {
var wb = X.read(table, {type:"string"});
assert.equal(get_cell(wb.Sheets.Sheet1, "A1").v, "foo\nbar");
});
it('should generate multi-sheet workbooks', function() {
var table = "";
for(var i = 0; i < 4; ++i) table += "<table><tr><td>" + X.utils.encode_col(i) + "</td><td>" + i + "</td></tr></table>";
table += table; table += table;
var wb = X.read(table, {type: "string"});
assert.equal(wb.SheetNames.length, 16);
assert.equal(wb.SheetNames[1], "Sheet2");
for(var j = 0; j < 4; ++j) {
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "A1").v, X.utils.encode_col(j));
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "B1").v, j);
}
});
});
(domtest ? describe : describe.skip)('input DOM', function() {
it('should interpret values by default', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str)), false); });

636
xlsx.esm.mjs generated
View File

@ -3,7 +3,7 @@
/*exported XLSX */
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
XLSX.version = '0.17.3';
XLSX.version = '0.17.4';
var current_codepage = 1200, current_ansi = 1252;
var VALID_ANSI = [ 874, 932, 936, 949, 950 ];
@ -3561,7 +3561,7 @@ function recordhopper(data, cb/*:RecordHopperCB*/, opts/*:?any*/) {
length = tmpbyte & 0x7F;
for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte);
tgt = data.l + length;
var d = (R.f||parsenoop)(data, length, opts);
var d = R.f && R.f(data, length, opts);
data.l = tgt;
if(cb(d, R.n, RT)) return;
}
@ -4210,7 +4210,11 @@ var DocSummaryPIDDSI = {
/*::[*/0x1B/*::]*/: { n: 'ContentStatus', t: VT_STRING },
/*::[*/0x1C/*::]*/: { n: 'Language', t: VT_STRING },
/*::[*/0x1D/*::]*/: { n: 'Version', t: VT_STRING },
/*::[*/0xFF/*::]*/: {}
/*::[*/0xFF/*::]*/: {},
/* [MS-OLEPS] 2.18 */
/*::[*/0x80000000/*::]*/: { n: 'Locale', t: VT_UI4 },
/*::[*/0x80000003/*::]*/: { n: 'Behavior', t: VT_UI4 },
/*::[*/0x72627262/*::]*/: {}
};
/* [MS-OSHARED] 2.3.3.2.1.1 Summary Information Property Set PIDSI */
@ -4234,21 +4238,13 @@ var SummaryPIDSI = {
/*::[*/0x11/*::]*/: { n: 'Thumbnail', t: VT_CF },
/*::[*/0x12/*::]*/: { n: 'Application', t: VT_STRING },
/*::[*/0x13/*::]*/: { n: 'DocSecurity', t: VT_I4 },
/*::[*/0xFF/*::]*/: {}
};
/* [MS-OLEPS] 2.18 */
var SpecialProperties = {
/*::[*/0xFF/*::]*/: {},
/* [MS-OLEPS] 2.18 */
/*::[*/0x80000000/*::]*/: { n: 'Locale', t: VT_UI4 },
/*::[*/0x80000003/*::]*/: { n: 'Behavior', t: VT_UI4 },
/*::[*/0x72627262/*::]*/: {}
};
(function() {
for(var y in SpecialProperties) if(Object.prototype.hasOwnProperty.call(SpecialProperties, y))
DocSummaryPIDDSI[y] = SummaryPIDSI[y] = SpecialProperties[y];
})();
var DocSummaryRE/*:{[key:string]:string}*/ = evert_key(DocSummaryPIDDSI, "n");
var SummaryRE/*:{[key:string]:string}*/ = evert_key(SummaryPIDSI, "n");
@ -7852,6 +7848,7 @@ var PRN = (function() {
}
else sep = guess_sep(str.slice(0,1024));
}
else if(o && o.FS) sep = o.FS;
else sep = guess_sep(str.slice(0,1024));
var R = 0, C = 0, v = 0;
var start = 0, end = 0, sepcc = sep.charCodeAt(0), instr = false, cc=0, startcc=str.charCodeAt(0);
@ -7904,6 +7901,7 @@ var PRN = (function() {
function prn_to_sheet_str(str/*:string*/, opts)/*:Worksheet*/ {
if(!(opts && opts.PRN)) return dsv_to_sheet_str(str, opts);
if(opts.FS) return dsv_to_sheet_str(str, opts);
if(str.slice(0,4) == "sep=") return dsv_to_sheet_str(str, opts);
if(str.indexOf("\t") >= 0 || str.indexOf(",") >= 0 || str.indexOf(";") >= 0) return dsv_to_sheet_str(str, opts);
return aoa_to_sheet(prn_to_aoa_str(str, opts), opts);
@ -7972,19 +7970,19 @@ function read_wb_ID(d, opts) {
}
}
var WK_ = (function() {
var WK_ = /*#__PURE__*/ (function() {
function lotushopper(data, cb/*:RecordHopperCB*/, opts/*:any*/) {
if(!data) return;
prep_blob(data, data.l || 0);
var Enum = opts.Enum || WK1Enum;
while(data.l < data.length) {
var RT = data.read_shift(2);
var R = Enum[RT] || Enum[0xFF];
var R = Enum[RT] || Enum[0xFFFF];
var length = data.read_shift(2);
var tgt = data.l + length;
var d = (R.f||parsenoop)(data, length, opts);
var d = R.f && R.f(data, length, opts);
data.l = tgt;
if(cb(d, R.n, RT)) return;
if(cb(d, R, RT)) return;
}
}
@ -8003,40 +8001,49 @@ var WK_ = (function() {
var o = opts || {};
if(DENSE != null && o.dense == null) o.dense = DENSE;
var s/*:Worksheet*/ = ((o.dense ? [] : {})/*:any*/), n = "Sheet1", sidx = 0;
var sheets = {}, snames = [n];
var sheets = {}, snames = [n], realnames = [];
var refguess = {s: {r:0, c:0}, e: {r:0, c:0} };
var sheetRows = o.sheetRows || 0;
if(d[2] == 0x02) o.Enum = WK1Enum;
else if(d[2] == 0x1a) o.Enum = WK3Enum;
else if(d[2] == 0x0e) { o.Enum = WK3Enum; o.qpro = true; d.l = 0; }
else throw new Error("Unrecognized LOTUS BOF " + d[2]);
lotushopper(d, function(val, Rn, RT) {
if(d[2] == 0x02) switch(RT) {
case 0x00:
if(d[2] == 0x02) {
o.Enum = WK1Enum;
lotushopper(d, function(val, R, RT) { switch(RT) {
case 0x00: /* BOF */
o.vers = val;
if(val >= 0x1000) o.qpro = true;
break;
case 0x06: refguess = val; break; /* RANGE */
case 0x0F: /* LABEL */
case 0x33: /* STRING */
if(!o.qpro) val[1].v = val[1].v.slice(1);
/* falls through */
case 0x0D: /* INTEGER */
case 0x0E: /* NUMBER */
case 0x10: /* FORMULA */
case 0x33: /* STRING */
/* TODO: actual translation of the format code */
if(RT == 0x0E && (val[2] & 0x70) == 0x70 && (val[2] & 0x0F) > 1 && (val[2] & 0x0F) < 15) {
val[1].z = o.dateNF || SSF._table[14];
if(o.cellDates) { val[1].t = 'd'; val[1].v = numdate(val[1].v); }
}
var tmpcell = o.dense ? (s[val[0].r]||[])[val[0].c] : s[encode_cell(val[0])];
if(tmpcell) {
tmpcell.t = val[1].t; tmpcell.v = val[1].v;
if(val[1].z != null) tmpcell.z = val[1].z;
if(val[1].f != null) tmpcell.f = val[1].f;
break;
}
if(o.dense) {
if(!s[val[0].r]) s[val[0].r] = [];
s[val[0].r][val[0].c] = val[1];
} else s[encode_cell(val[0])] = val[1];
break;
} else switch(RT) {
default:
}}, o);
} else if(d[2] == 0x1A || d[2] == 0x0E) {
o.Enum = WK3Enum;
if(d[2] == 0x0E) { o.qpro = true; d.l = 0; }
lotushopper(d, function(val, R, RT) { switch(RT) {
case 0x16: /* LABEL16 */
val[1].v = val[1].v.slice(1);
/* falls through */
@ -8062,13 +8069,130 @@ var WK_ = (function() {
if(refguess.e.c < val[0].c) refguess.e.c = val[0].c;
if(refguess.e.r < val[0].r) refguess.e.r = val[0].r;
break;
case 0x1B: /* XFORMAT */
if(val[0x36b0]) realnames[val[0x36b0][0]] = val[0x36b0][1];
break;
default: break;
}
}, o);
}}, o);
} else throw new Error("Unrecognized LOTUS BOF " + d[2]);
s["!ref"] = encode_range(refguess);
sheets[n] = s;
return { SheetNames: snames, Sheets:sheets };
if(!realnames.length) return { SheetNames: snames, Sheets: sheets };
var osheets = {}, rnames = [];
for(var i = 0; i < realnames.length; ++i) if(sheets[snames[i]]) {
rnames.push(realnames[i]);
osheets[realnames[i]] = sheets[snames[i]];
}
return { SheetNames: rnames, Sheets: osheets };
}
function sheet_to_wk1(ws/*:Worksheet*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(+o.codepage >= 0) set_cp(+o.codepage);
if(o.type == "string") throw new Error("Cannot write WK1 to JS string");
var ba = buf_array();
var range = safe_decode_range(ws["!ref"]);
var dense = Array.isArray(ws);
var cols = [];
write_biff_rec(ba, 0x00, write_BOF_WK1(0x0406));
write_biff_rec(ba, 0x06, write_RANGE(range));
for(var R = range.s.r; R <= range.e.r; ++R) {
var rr = encode_row(R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R === range.s.r) cols[C] = encode_col(C);
var ref = cols[C] + rr;
var cell = dense ? (ws[R]||[])[C] : ws[ref];
if(!cell || cell.t == "z") continue;
/* TODO: formula records */
if(cell.t == "n") {
if((cell.v|0)==cell.v && cell.v >= -32768 && cell.v <= 32767) write_biff_rec(ba, 0x0d, write_INTEGER(R, C, cell.v));
else write_biff_rec(ba, 0x0e, write_NUMBER(R, C, cell.v));
} else {
var str = format_cell(cell);
write_biff_rec(ba, 0x0F, write_LABEL(R, C, str.slice(0, 239)));
}
}
}
write_biff_rec(ba, 0x01);
return ba.end();
}
function book_to_wk3(wb/*:Workbook*/, opts/*:WriteOpts*/) {
var o = opts || {};
if(+o.codepage >= 0) set_cp(+o.codepage);
if(o.type == "string") throw new Error("Cannot write WK3 to JS string");
var ba = buf_array();
write_biff_rec(ba, 0x00, write_BOF_WK3(wb));
for(var i = 0, cnt = 0; i < wb.SheetNames.length; ++i) if((wb.Sheets[wb.SheetNames[i]] || {})["!ref"]) write_biff_rec(ba, 0x1b, write_XFORMAT_SHEETNAME(wb.SheetNames[i], cnt++));
var wsidx = 0;
for(i = 0; i < wb.SheetNames.length; ++i) {
var ws = wb.Sheets[wb.SheetNames[i]];
if(!ws || !ws["!ref"]) continue;
var range = safe_decode_range(ws["!ref"]);
var dense = Array.isArray(ws);
var cols = [];
for(var R = range.s.r; R <= range.e.r; ++R) {
var rr = encode_row(R);
for(var C = range.s.c; C <= range.e.c; ++C) {
if(R === range.s.r) cols[C] = encode_col(C);
var ref = cols[C] + rr;
var cell = dense ? (ws[R]||[])[C] : ws[ref];
if(!cell || cell.t == "z") continue;
/* TODO: FORMULA19 NUMBER18 records */
if(cell.t == "n") {
write_biff_rec(ba, 0x17, write_NUMBER_17(R, C, wsidx, cell.v));
} else {
var str = format_cell(cell);
/* TODO: max len? */
write_biff_rec(ba, 0x16, write_LABEL_16(R, C, wsidx, str.slice(0, 239)));
}
}
}
++wsidx;
}
write_biff_rec(ba, 0x01);
return ba.end();
}
function write_BOF_WK1(v/*:number*/) {
var out = new_buf(2);
out.write_shift(2, v);
return out;
}
function write_BOF_WK3(wb/*:Workbook*/) {
var out = new_buf(26);
out.write_shift(2, 0x1000);
out.write_shift(2, 0x0004);
out.write_shift(4, 0x0000);
var rows = 0, cols = 0, wscnt = 0;
for(var i = 0; i < wb.SheetNames.length; ++i) {
var name = wb.SheetNames[i];
var ws = wb.Sheets[name];
if(!ws || !ws["!ref"]) continue;
++wscnt;
var range = decode_range(ws["!ref"]);
if(rows < range.e.r) rows = range.e.r;
if(cols < range.e.c) cols = range.e.c;
}
out.write_shift(2, rows);
out.write_shift(1, wscnt);
out.write_shift(1, cols);
out.write_shift(2, 0x00);
out.write_shift(2, 0x00);
out.write_shift(1, 0x01);
out.write_shift(1, 0x02);
out.write_shift(4, 0);
out.write_shift(4, 0);
return out;
}
function parse_RANGE(blob) {
@ -8080,6 +8204,14 @@ var WK_ = (function() {
if(o.s.c == 0xFFFF) o.s.c = o.e.c = o.s.r = o.e.r = 0;
return o;
}
function write_RANGE(range) {
var out = new_buf(8);
out.write_shift(2, range.s.c);
out.write_shift(2, range.s.r);
out.write_shift(2, range.e.c);
out.write_shift(2, range.e.r);
return out;
}
function parse_cell(blob, length, opts) {
var o = [{c:0,r:0}, {t:'n',v:0}, 0];
@ -8109,18 +8241,48 @@ var WK_ = (function() {
o[1].v = blob.read_shift(tgt - blob.l, 'cstr');
return o;
}
function write_LABEL(R, C, s) {
/* TODO: encoding */
var o = new_buf(7 + s.length);
o.write_shift(1, 0xFF);
o.write_shift(2, C);
o.write_shift(2, R);
o.write_shift(1, 0x27); // ??
for(var i = 0; i < o.length; ++i) {
var cc = s.charCodeAt(i);
o.write_shift(1, cc >= 0x80 ? 0x5F : cc);
}
o.write_shift(1, 0);
return o;
}
function parse_INTEGER(blob, length, opts) {
var o = parse_cell(blob, length, opts);
o[1].v = blob.read_shift(2, 'i');
return o;
}
function write_INTEGER(R, C, v) {
var o = new_buf(7);
o.write_shift(1, 0xFF);
o.write_shift(2, C);
o.write_shift(2, R);
o.write_shift(2, v, 'i');
return o;
}
function parse_NUMBER(blob, length, opts) {
var o = parse_cell(blob, length, opts);
o[1].v = blob.read_shift(8, 'f');
return o;
}
function write_NUMBER(R, C, v) {
var o = new_buf(13);
o.write_shift(1, 0xFF);
o.write_shift(2, C);
o.write_shift(2, R);
o.write_shift(8, v, 'f');
return o;
}
function parse_FORMULA(blob, length, opts) {
var tgt = blob.l + length;
@ -8130,11 +8292,105 @@ var WK_ = (function() {
if(opts.qpro) blob.l = tgt;
else {
var flen = blob.read_shift(2);
wk1_fmla_to_csf(blob.slice(blob.l, blob.l + flen), o);
blob.l += flen;
}
return o;
}
function wk1_parse_rc(B, V, col) {
var rel = V & 0x8000;
V &= ~0x8000;
V = (rel ? B : 0) + ((V >= 0x2000) ? V - 0x4000 : V);
return (rel ? "" : "$") + (col ? encode_col(V) : encode_row(V));
}
var oprec = [
8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 4, 5, 5, 7, 3, 3,
3, 3, 3, 3, 1, 1, 2, 6, 8, 8, 8, 8, 8, 8, 8, 8
];
/* TODO: flesh out */
var FuncTab = {
0x33: ["FALSE", 0],
0x34: ["TRUE", 0],
0x46: ["LEN", 1],
0x50: ["SUM", 69],
0x51: ["AVERAGEA", 69],