2017-12-30 05:40:35 +00:00
|
|
|
function write_biff_rec(ba/*:BufArray*/, type/*:number|string*/, payload, length/*:?number*/)/*:void*/ {
|
2017-09-30 06:18:11 +00:00
|
|
|
var t/*:number*/ = +type || +XLSRE[/*::String(*/type/*::)*/];
|
2017-09-22 22:18:51 +00:00
|
|
|
if(isNaN(t)) return;
|
|
|
|
var len = length || (payload||[]).length || 0;
|
2017-12-30 05:40:35 +00:00
|
|
|
var o = ba.next(4);
|
2017-02-10 19:23:01 +00:00
|
|
|
o.write_shift(2, t);
|
|
|
|
o.write_shift(2, len);
|
|
|
|
if(/*:: len != null &&*/len > 0 && is_buf(payload)) ba.push(payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
function write_BIFF2Cell(out, r/*:number*/, c/*:number*/) {
|
|
|
|
if(!out) out = new_buf(7);
|
|
|
|
out.write_shift(2, r);
|
|
|
|
out.write_shift(2, c);
|
2017-12-30 05:40:35 +00:00
|
|
|
out.write_shift(2, 0);
|
2017-02-10 19:23:01 +00:00
|
|
|
out.write_shift(1, 0);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2017-07-27 20:07:51 +00:00
|
|
|
function write_BIFF2BERR(r/*:number*/, c/*:number*/, val, t/*:?string*/) {
|
2017-02-10 19:23:01 +00:00
|
|
|
var out = new_buf(9);
|
|
|
|
write_BIFF2Cell(out, r, c);
|
|
|
|
if(t == 'e') { out.write_shift(1, val); out.write_shift(1, 1); }
|
|
|
|
else { out.write_shift(1, val?1:0); out.write_shift(1, 0); }
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: codepage, large strings */
|
2017-07-27 20:07:51 +00:00
|
|
|
function write_BIFF2LABEL(r/*:number*/, c/*:number*/, val) {
|
2017-02-10 19:23:01 +00:00
|
|
|
var out = new_buf(8 + 2*val.length);
|
|
|
|
write_BIFF2Cell(out, r, c);
|
|
|
|
out.write_shift(1, val.length);
|
|
|
|
out.write_shift(val.length, val, 'sbcs');
|
|
|
|
return out.l < out.length ? out.slice(0, out.l) : out;
|
|
|
|
}
|
|
|
|
|
2017-09-22 22:18:51 +00:00
|
|
|
function write_ws_biff2_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
|
2017-03-18 00:45:06 +00:00
|
|
|
if(cell.v != null) switch(cell.t) {
|
2017-04-02 06:47:25 +00:00
|
|
|
case 'd': case 'n':
|
2017-10-17 00:14:32 +00:00
|
|
|
var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
|
2017-04-02 06:47:25 +00:00
|
|
|
if((v == (v|0)) && (v >= 0) && (v < 65536))
|
|
|
|
write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v));
|
2017-02-10 19:23:01 +00:00
|
|
|
else
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(ba, 0x0003, write_BIFF2NUM(R,C, v));
|
2017-03-18 00:45:06 +00:00
|
|
|
return;
|
|
|
|
case 'b': case 'e': write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t)); return;
|
2017-02-10 19:23:01 +00:00
|
|
|
/* TODO: codepage, sst */
|
|
|
|
case 's': case 'str':
|
|
|
|
write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v));
|
2017-03-18 00:45:06 +00:00
|
|
|
return;
|
2017-02-10 19:23:01 +00:00
|
|
|
}
|
2017-03-18 00:45:06 +00:00
|
|
|
write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
|
2017-02-10 19:23:01 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 22:18:51 +00:00
|
|
|
function write_ws_biff2(ba/*:BufArray*/, ws/*:Worksheet*/, idx/*:number*/, opts, wb/*:Workbook*/) {
|
2017-04-08 06:55:35 +00:00
|
|
|
var dense = Array.isArray(ws);
|
2017-07-27 20:07:51 +00:00
|
|
|
var range = safe_decode_range(ws['!ref'] || "A1"), ref/*:string*/, rr = "", cols/*:Array<string>*/ = [];
|
2017-02-10 19:23:01 +00:00
|
|
|
for(var R = range.s.r; R <= range.e.r; ++R) {
|
|
|
|
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);
|
|
|
|
ref = cols[C] + rr;
|
2017-06-10 01:47:42 +00:00
|
|
|
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
2017-04-08 06:55:35 +00:00
|
|
|
if(!cell) continue;
|
2017-02-10 19:23:01 +00:00
|
|
|
/* write cell */
|
2017-09-22 22:18:51 +00:00
|
|
|
write_ws_biff2_cell(ba, cell, R, C, opts);
|
2017-02-10 19:23:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Based on test files */
|
2017-09-22 22:18:51 +00:00
|
|
|
function write_biff2_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
2017-04-08 06:55:35 +00:00
|
|
|
var o = opts || {};
|
2017-04-09 04:03:19 +00:00
|
|
|
if(DENSE != null && o.dense == null) o.dense = DENSE;
|
2017-02-10 19:23:01 +00:00
|
|
|
var ba = buf_array();
|
|
|
|
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);
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(ba, 0x0009, write_BOF(wb, 0x10, o));
|
2017-02-10 19:23:01 +00:00
|
|
|
/* ... */
|
2017-09-22 22:18:51 +00:00
|
|
|
write_ws_biff2(ba, wb.Sheets[wb.SheetNames[idx]], idx, o, wb);
|
2017-02-10 19:23:01 +00:00
|
|
|
/* ... */
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(ba, 0x000A);
|
2017-02-10 19:23:01 +00:00
|
|
|
return ba.end();
|
|
|
|
}
|
2017-09-22 22:18:51 +00:00
|
|
|
|
2017-12-15 01:18:40 +00:00
|
|
|
function write_ws_biff8_hlinks(ba/*:BufArray*/, ws) {
|
|
|
|
for(var R=0; R<ws['!links'].length; ++R) {
|
|
|
|
var HL = ws['!links'][R];
|
|
|
|
write_biff_rec(ba, "HLink", write_HLink(HL));
|
|
|
|
if(HL[1].Tooltip) write_biff_rec(ba, "HLinkTooltip", write_HLinkTooltip(HL));
|
|
|
|
}
|
|
|
|
delete ws['!links'];
|
|
|
|
}
|
|
|
|
|
2017-09-22 22:18:51 +00:00
|
|
|
function write_ws_biff8_cell(ba/*:BufArray*/, cell/*:Cell*/, R/*:number*/, C/*:number*/, opts) {
|
|
|
|
if(cell.v != null) switch(cell.t) {
|
|
|
|
case 'd': case 'n':
|
2017-10-17 00:14:32 +00:00
|
|
|
var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
|
2017-09-22 22:18:51 +00:00
|
|
|
/* TODO: emit RK as appropriate */
|
|
|
|
write_biff_rec(ba, "Number", write_Number(R, C, v, opts));
|
|
|
|
return;
|
|
|
|
case 'b': case 'e': write_biff_rec(ba, "BoolErr", write_BoolErr(R, C, cell.v, opts, cell.t)); return;
|
|
|
|
/* TODO: codepage, sst */
|
|
|
|
case 's': case 'str':
|
|
|
|
write_biff_rec(ba, "Label", write_Label(R, C, cell.v, opts));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
write_biff_rec(ba, "Blank", write_XLSCell(R, C));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [MS-XLS] 2.1.7.20.5 */
|
|
|
|
function write_ws_biff8(idx/*:number*/, opts, wb/*:Workbook*/) {
|
|
|
|
var ba = buf_array();
|
|
|
|
var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
|
2017-12-30 05:40:35 +00:00
|
|
|
var _WB/*:WBWBProps*/ = ((wb||{}).Workbook||{}/*:any*/);
|
|
|
|
var _sheet/*:WBWSProp*/ = ((_WB.Sheets||[])[idx]||{}/*:any*/);
|
2017-09-22 22:18:51 +00:00
|
|
|
var dense = Array.isArray(ws);
|
|
|
|
var ref/*:string*/, rr = "", cols/*:Array<string>*/ = [];
|
|
|
|
var range = safe_decode_range(ws['!ref'] || "A1");
|
2017-12-15 01:18:40 +00:00
|
|
|
var b8 = opts.biff == 8, b5 = opts.biff == 5;
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(ba, 0x0809, write_BOF(wb, 0x10, opts));
|
|
|
|
/* ... */
|
|
|
|
write_biff_rec(ba, "CalcMode", writeuint16(1));
|
|
|
|
write_biff_rec(ba, "CalcCount", writeuint16(100));
|
|
|
|
write_biff_rec(ba, "CalcRefMode", writebool(true));
|
|
|
|
write_biff_rec(ba, "CalcIter", writebool(false));
|
|
|
|
write_biff_rec(ba, "CalcDelta", write_Xnum(0.001));
|
|
|
|
write_biff_rec(ba, "CalcSaveRecalc", writebool(true));
|
|
|
|
write_biff_rec(ba, "PrintRowCol", writebool(false));
|
|
|
|
write_biff_rec(ba, "PrintGrid", writebool(false));
|
|
|
|
write_biff_rec(ba, "GridSet", writeuint16(1));
|
|
|
|
write_biff_rec(ba, "Guts", write_Guts([0,0]));
|
|
|
|
/* ... */
|
|
|
|
write_biff_rec(ba, "HCenter", writebool(false));
|
|
|
|
write_biff_rec(ba, "VCenter", writebool(false));
|
|
|
|
/* ... */
|
|
|
|
write_biff_rec(ba, "Dimensions", write_Dimensions(range, opts));
|
|
|
|
/* ... */
|
|
|
|
|
2017-12-15 01:18:40 +00:00
|
|
|
if(b8) ws['!links'] = [];
|
2017-09-22 22:18:51 +00:00
|
|
|
for(var R = range.s.r; R <= range.e.r; ++R) {
|
|
|
|
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);
|
|
|
|
ref = cols[C] + rr;
|
|
|
|
var cell = dense ? (ws[R]||[])[C] : ws[ref];
|
|
|
|
if(!cell) continue;
|
|
|
|
/* write cell */
|
|
|
|
write_ws_biff8_cell(ba, cell, R, C, opts);
|
2017-12-15 01:18:40 +00:00
|
|
|
if(b8 && cell.l) ws['!links'].push([ref, cell.l]);
|
2017-09-22 22:18:51 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-20 01:51:14 +00:00
|
|
|
var cname/*:string*/ = _sheet.CodeName || _sheet.name || s;
|
2017-12-15 01:18:40 +00:00
|
|
|
/* ... */
|
2017-12-30 05:40:35 +00:00
|
|
|
if(b8 && _WB.Views) write_biff_rec(ba, "Window2", write_Window2(_WB.Views[0]));
|
|
|
|
/* ... */
|
|
|
|
if(b8) write_biff_rec(ba, "MergeCells", write_MergeCells(ws['!merges']||[]));
|
|
|
|
/* ... */
|
2017-12-15 01:18:40 +00:00
|
|
|
if(b8) write_ws_biff8_hlinks(ba, ws);
|
|
|
|
/* ... */
|
2017-10-27 16:25:54 +00:00
|
|
|
write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
|
2017-09-22 22:18:51 +00:00
|
|
|
/* ... */
|
|
|
|
write_biff_rec(ba, "EOF");
|
|
|
|
return ba.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [MS-XLS] 2.1.7.20.3 */
|
|
|
|
function write_biff8_global(wb/*:Workbook*/, bufs, opts/*:WriteOpts*/) {
|
|
|
|
var A = buf_array();
|
2017-11-20 01:51:14 +00:00
|
|
|
var _wb/*:WBWBProps*/ = /*::((*/(wb.Workbook||{}).WBProps||{/*::CodeName:"ThisWorkbook"*/}/*:: ):any)*/;
|
2017-09-22 22:18:51 +00:00
|
|
|
var b8 = opts.biff == 8, b5 = opts.biff == 5;
|
|
|
|
write_biff_rec(A, 0x0809, write_BOF(wb, 0x05, opts));
|
2017-12-30 05:40:35 +00:00
|
|
|
if(opts.bookType == "xla") write_biff_rec(A, "Addin");
|
2017-10-17 00:14:32 +00:00
|
|
|
write_biff_rec(A, "InterfaceHdr", b8 ? writeuint16(0x04b0) : null);
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(A, "Mms", writezeroes(2));
|
|
|
|
if(b5) write_biff_rec(A, "ToolbarHdr");
|
|
|
|
if(b5) write_biff_rec(A, "ToolbarEnd");
|
|
|
|
write_biff_rec(A, "InterfaceEnd");
|
|
|
|
write_biff_rec(A, "WriteAccess", write_WriteAccess("SheetJS", opts));
|
2017-10-17 00:14:32 +00:00
|
|
|
write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
|
2017-09-22 22:18:51 +00:00
|
|
|
if(b8) write_biff_rec(A, "DSF", writeuint16(0));
|
2017-10-17 00:14:32 +00:00
|
|
|
write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
|
2017-10-27 16:25:54 +00:00
|
|
|
if(b8 && wb.vbaraw) {
|
|
|
|
write_biff_rec(A, "ObProj");
|
2017-11-20 01:51:14 +00:00
|
|
|
// $FlowIgnore
|
|
|
|
var cname/*:string*/ = _wb.CodeName || "ThisWorkbook";
|
2017-10-27 16:25:54 +00:00
|
|
|
write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
|
|
|
|
}
|
2017-10-17 00:14:32 +00:00
|
|
|
write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(A, "WinProtect", writebool(false));
|
|
|
|
write_biff_rec(A, "Protect", writebool(false));
|
|
|
|
write_biff_rec(A, "Password", writeuint16(0));
|
|
|
|
if(b8) write_biff_rec(A, "Prot4Rev", writebool(false));
|
|
|
|
if(b8) write_biff_rec(A, "Prot4RevPass", writeuint16(0));
|
|
|
|
write_biff_rec(A, "Window1", write_Window1(opts));
|
|
|
|
write_biff_rec(A, "Backup", writebool(false));
|
|
|
|
write_biff_rec(A, "HideObj", writeuint16(0));
|
|
|
|
write_biff_rec(A, "Date1904", writebool(safe1904(wb)=="true"));
|
|
|
|
write_biff_rec(A, "CalcPrecision", writebool(true));
|
2017-10-17 00:14:32 +00:00
|
|
|
if(b8) write_biff_rec(A, "RefreshAll", writebool(false));
|
2017-09-22 22:18:51 +00:00
|
|
|
write_biff_rec(A, "BookBool", writeuint16(0));
|
|
|
|
/* ... */
|
2017-10-17 00:14:32 +00:00
|
|
|
if(b8) write_biff_rec(A, "UsesELFs", writebool(false));
|
2017-09-22 22:18:51 +00:00
|
|
|
var a = A.end();
|
|
|
|
|
|
|
|
var C = buf_array();
|
2017-10-17 00:14:32 +00:00
|
|
|
if(b8) write_biff_rec(C, "Country", write_Country());
|
2017-09-22 22:18:51 +00:00
|
|
|
/* BIFF8: [SST *Continue] ExtSST */
|
|
|
|
write_biff_rec(C, "EOF");
|
|
|
|
var c = C.end();
|
|
|
|
|
|
|
|
var B = buf_array();
|
|
|
|
var blen = 0, j = 0;
|
2017-10-17 00:14:32 +00:00
|
|
|
for(j = 0; j < wb.SheetNames.length; ++j) blen += (b8 ? 12 : 11) + (b8 ? 2 : 1) * wb.SheetNames[j].length;
|
2017-09-22 22:18:51 +00:00
|
|
|
var start = a.length + blen + c.length;
|
|
|
|
for(j = 0; j < wb.SheetNames.length; ++j) {
|
|
|
|
write_biff_rec(B, "BoundSheet8", write_BoundSheet8({pos:start, hs:0, dt:0, name:wb.SheetNames[j]}, opts));
|
|
|
|
start += bufs[j].length;
|
|
|
|
}
|
|
|
|
/* 1*BoundSheet8 */
|
|
|
|
var b = B.end();
|
|
|
|
if(blen != b.length) throw new Error("BS8 " + blen + " != " + b.length);
|
|
|
|
|
|
|
|
var out = [];
|
|
|
|
if(a.length) out.push(a);
|
|
|
|
if(b.length) out.push(b);
|
|
|
|
if(c.length) out.push(c);
|
|
|
|
return __toBuffer([out]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [MS-XLS] 2.1.7.20 Workbook Stream */
|
|
|
|
function write_biff8_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
|
|
|
var o = opts || {};
|
|
|
|
var bufs = [];
|
|
|
|
for(var i = 0; i < wb.SheetNames.length; ++i) bufs[bufs.length] = write_ws_biff8(i, o, wb);
|
|
|
|
bufs.unshift(write_biff8_global(wb, bufs, o));
|
|
|
|
return __toBuffer([bufs]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function write_biff_buf(wb/*:Workbook*/, opts/*:WriteOpts*/) {
|
|
|
|
var o = opts || {};
|
|
|
|
switch(o.biff || 2) {
|
|
|
|
case 8: case 5: return write_biff8_buf(wb, opts);
|
|
|
|
case 4: case 3: case 2: return write_biff2_buf(wb, opts);
|
|
|
|
}
|
|
|
|
throw new Error("invalid type " + o.bookType + " for BIFF");
|
|
|
|
}
|