From 4cc980975b32ce59d860539c02f0e66b21f4f1b3 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Sun, 15 Oct 2023 23:03:39 -0400 Subject: [PATCH] QPW string formula results - mujs-compatible regices - NUMBERS use row offsets (fixes #3009 h/t @relaxsnow) --- bits/40_harb.js | 5 ++++- bits/41_lotus.js | 41 ++++++++++++++++++++++++++++++++++------- bits/61_fcommon.js | 5 ++++- bits/83_numbers.js | 18 ++++++++++++++---- modules/83_numbers.js | 18 ++++++++++++++---- modules/83_numbers.ts | 14 ++++++++++++-- 6 files changed, 82 insertions(+), 19 deletions(-) diff --git a/bits/40_harb.js b/bits/40_harb.js index 52e8862..8e55348 100644 --- a/bits/40_harb.js +++ b/bits/40_harb.js @@ -409,7 +409,10 @@ var SYLK = /*#__PURE__*/(function() { "!":161, '"':162, "#":163, "(":164, "%":165, "'":167, "H ":168, "+":171, ";":187, "<":188, "=":189, ">":190, "?":191, "{":223 }/*:any*/); - var sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1") + "|\\|)", "gm"); + var sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1").replace("{", "\\{") + "|\\|)", "gm"); + try { + sylk_char_regex = new RegExp("\u001BN(" + keys(sylk_escapes).join("|").replace(/\|\|\|/, "|\\||").replace(/([?()+])/g,"\\$1") + "|\\|)", "gm"); + } catch(e) {} var sylk_char_fn = function(_, $1){ var o = sylk_escapes[$1]; return typeof o == "number" ? _getansi(o) : o; }; var decode_sylk_char = function($$, $1, $2) { var newcc = (($1.charCodeAt(0) - 0x20)<<4) | ($2.charCodeAt(0) - 0x30); return newcc == 59 ? $$ : _getansi(newcc); }; sylk_escapes["|"] = 254; diff --git a/bits/41_lotus.js b/bits/41_lotus.js index 0996676..f39c464 100644 --- a/bits/41_lotus.js +++ b/bits/41_lotus.js @@ -977,13 +977,25 @@ var WK_ = /*#__PURE__*/(function() { /*::[*/0x0E/*::*/: "dd-mmm-yyyy", /*::[*/0x0F/*::*/: "mmm-yyyy", - /*::[*/0x22/*::*/: "0.00", - /*::[*/0x32/*::*/: "0.00;[Red]0.00", - /*::[*/0x42/*::*/: "0.00;\(0.00\)", - /*::[*/0x52/*::*/: "0.00;[Red]\(0.00\)", - - /*::[*/162/*::*/: '"$"#,##0;\\("$"#,##0\\)' // slightly different from SSF 5 + /* It is suspected that the the low nybble specifies decimal places + /*::[*/0x0022/*::*/: "0.00", + /*::[*/0x0032/*::*/: "0.00;[Red]0.00", + /*::[*/0x0042/*::*/: "0.00;\(0.00\)", + /*::[*/0x0052/*::*/: "0.00;[Red]\(0.00\)", + /*::[*/0x00A2/*::*/: '"$"#,##0.00;\\("$"#,##0.00\\)', + /*::[*/0x0120/*::*/: '0%', + /*::[*/0x0130/*::*/: '0E+00', + /*::[*/0x0140/*::*/: '# ?/?' }; + + function parse_qpw_str(p) { + var cch = p.read_shift(2); + var flags = p.read_shift(1); + /* TODO: find examples with nonzero flags */ + if(flags != 0) throw "unsupported QPW string type " + flags.toString(16); + return p.read_shift(cch, "sbcs-cont"); + } + /* QPW uses a different set of record types */ function qpw_to_workbook_buf(d, opts)/*:Workbook*/ { prep_blob(d, 0); @@ -1094,7 +1106,11 @@ var WK_ = /*#__PURE__*/(function() { case 4: cell = { t: "n", v: parse_RkNumber(p) }; break; case 5: cell = { t: "n", v: p.read_shift(8, 'f') }; break; case 7: cell = { t: "s", v: SST[type = p.read_shift(4) - 1] }; break; - case 8: cell = { t: "n", v: p.read_shift(8, 'f') }; p.l += 2; /* cell.f = formulae[p.read_shift(4)]; */ p.l += 4; break; + case 8: + cell = { t: "n", v: p.read_shift(8, 'f') }; + p.l += 2; /* cell.f = formulae[p.read_shift(4)]; */ p.l += 4; + if(isNaN(cell.v)) cell = { t: "e", v: 0x0F }; // #VALUE! + break; default: throw "Unrecognized QPW cell type " + (flags & 0x1F); } if(fmtidx != -1 && (FMTS[fmtidx - 1]||{}).z) cell.z = FMTS[fmtidx-1].z; @@ -1140,6 +1156,17 @@ var WK_ = /*#__PURE__*/(function() { } } break; + case 0x0C02: { /* String (result of string formula expression) */ + C = p.read_shift(2); + R = p.read_shift(4); + var str = parse_qpw_str(p); + /* TODO: QP10 record has an additional unknown character after the string */ + if(s["!data"] != null) { + if(!s["!data"][R]) s["!data"][R] = []; + s["!data"][R][C] = { t:"s", v:str }; + } else s[encode_col(C) + encode_row(R)] = { t:"s", v:str }; + } break; + default: break; } d.l += length; diff --git a/bits/61_fcommon.js b/bits/61_fcommon.js index 3171974..165b096 100644 --- a/bits/61_fcommon.js +++ b/bits/61_fcommon.js @@ -23,7 +23,10 @@ var rc_to_a1 = /*#__PURE__*/(function(){ }; })(); -var crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g; +var crefregex = /(^|[^._A-Z0-9])(\$?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])(\$?)(\d{1,7})(?![_.\(A-Za-z0-9])/g; +try { + crefregex = /(^|[^._A-Z0-9])([$]?)([A-Z]{1,2}|[A-W][A-Z]{2}|X[A-E][A-Z]|XF[A-D])([$]?)(10[0-3]\d{4}|104[0-7]\d{3}|1048[0-4]\d{2}|10485[0-6]\d|104857[0-6]|[1-9]\d{0,5})(?![_.\(A-Za-z0-9])/g; +}catch(e){} var a1_to_rc = /*#__PURE__*/(function(){ return function a1_to_rc(fstr/*:string*/, base/*:CellAddress*/) { return fstr.replace(crefregex, function($0, $1, $2, $3, $4, $5) { diff --git a/bits/83_numbers.js b/bits/83_numbers.js index d281a9e..7d02754 100644 --- a/bits/83_numbers.js +++ b/bits/83_numbers.js @@ -1117,7 +1117,7 @@ function s5s_to_iwa_comment(s5s) { return out; } function parse_TST_TableModelArchive(M, root, ws, opts) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; var pb = parse_shallow(root.data); var range = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } }; range.e.r = (varint_to_i32(pb[6][0].data) >>> 0) - 1; @@ -1146,7 +1146,17 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { lut.nfmt = parse_TST_TableDataList(M, M[parse_TSP_Reference(store[22][0].data)][0]); var tile = parse_shallow(store[3][0].data); var _R = 0; - tile[1].forEach(function(t) { + if (!((_h = store[9]) == null ? void 0 : _h[0])) + throw "NUMBERS file missing row tree"; + var rtt = parse_shallow(store[9][0].data)[1].map(function(p) { + return parse_shallow(p.data); + }); + rtt.forEach(function(kv) { + _R = varint_to_i32(kv[1][0].data); + var tidx = varint_to_i32(kv[2][0].data); + var t = tile[1][tidx]; + if (!t) + throw "NUMBERS missing tile " + tidx; var tl = parse_shallow(t.data); var ref2 = M[parse_TSP_Reference(tl[2][0].data)][0]; var mtype2 = varint_to_i32(ref2.meta[1][0].data); @@ -1169,12 +1179,12 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { }); _R += _tile.nrows; }); - if ((_h = store[13]) == null ? void 0 : _h[0]) { + if ((_i = store[13]) == null ? void 0 : _i[0]) { var ref = M[parse_TSP_Reference(store[13][0].data)][0]; var mtype = varint_to_i32(ref.meta[1][0].data); if (mtype != 6144) throw new Error("Expected merge type 6144, found ".concat(mtype)); - ws["!merges"] = (_i = parse_shallow(ref.data)) == null ? void 0 : _i[1].map(function(pi) { + ws["!merges"] = (_j = parse_shallow(ref.data)) == null ? void 0 : _j[1].map(function(pi) { var merge = parse_shallow(pi.data); var origin = u8_to_dataview(parse_shallow(merge[1][0].data)[1][0].data), size = u8_to_dataview(parse_shallow(merge[2][0].data)[1][0].data); return { diff --git a/modules/83_numbers.js b/modules/83_numbers.js index d281a9e..7d02754 100644 --- a/modules/83_numbers.js +++ b/modules/83_numbers.js @@ -1117,7 +1117,7 @@ function s5s_to_iwa_comment(s5s) { return out; } function parse_TST_TableModelArchive(M, root, ws, opts) { - var _a, _b, _c, _d, _e, _f, _g, _h, _i; + var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j; var pb = parse_shallow(root.data); var range = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } }; range.e.r = (varint_to_i32(pb[6][0].data) >>> 0) - 1; @@ -1146,7 +1146,17 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { lut.nfmt = parse_TST_TableDataList(M, M[parse_TSP_Reference(store[22][0].data)][0]); var tile = parse_shallow(store[3][0].data); var _R = 0; - tile[1].forEach(function(t) { + if (!((_h = store[9]) == null ? void 0 : _h[0])) + throw "NUMBERS file missing row tree"; + var rtt = parse_shallow(store[9][0].data)[1].map(function(p) { + return parse_shallow(p.data); + }); + rtt.forEach(function(kv) { + _R = varint_to_i32(kv[1][0].data); + var tidx = varint_to_i32(kv[2][0].data); + var t = tile[1][tidx]; + if (!t) + throw "NUMBERS missing tile " + tidx; var tl = parse_shallow(t.data); var ref2 = M[parse_TSP_Reference(tl[2][0].data)][0]; var mtype2 = varint_to_i32(ref2.meta[1][0].data); @@ -1169,12 +1179,12 @@ function parse_TST_TableModelArchive(M, root, ws, opts) { }); _R += _tile.nrows; }); - if ((_h = store[13]) == null ? void 0 : _h[0]) { + if ((_i = store[13]) == null ? void 0 : _i[0]) { var ref = M[parse_TSP_Reference(store[13][0].data)][0]; var mtype = varint_to_i32(ref.meta[1][0].data); if (mtype != 6144) throw new Error("Expected merge type 6144, found ".concat(mtype)); - ws["!merges"] = (_i = parse_shallow(ref.data)) == null ? void 0 : _i[1].map(function(pi) { + ws["!merges"] = (_j = parse_shallow(ref.data)) == null ? void 0 : _j[1].map(function(pi) { var merge = parse_shallow(pi.data); var origin = u8_to_dataview(parse_shallow(merge[1][0].data)[1][0].data), size = u8_to_dataview(parse_shallow(merge[2][0].data)[1][0].data); return { diff --git a/modules/83_numbers.ts b/modules/83_numbers.ts index d0271ac..fe1b18d 100644 --- a/modules/83_numbers.ts +++ b/modules/83_numbers.ts @@ -926,8 +926,18 @@ function parse_TST_TableModelArchive(M: MessageSpace, root: IWAMessage, ws: Work // .TST.TileStorage var tile = parse_shallow(store[3][0].data); var _R = 0; - /* TODO: should this list be sorted by id ? */ - tile[1].forEach(t => { + + // .TST.TableRBTree + if(!store[9]?.[0]) throw "NUMBERS file missing row tree"; + var rtt = parse_shallow(store[9][0].data)[1].map(p => parse_shallow(p.data)); + + /* TODO: check examples with ctt */ + rtt.forEach(kv => { + // .TST.TableRBTree.Node + _R = varint_to_i32(kv[1][0].data); + var tidx = varint_to_i32(kv[2][0].data); + var t = tile[1][tidx]; + if(!t) throw "NUMBERS missing tile " + tidx; var tl = (parse_shallow(t.data)); // var id = varint_to_i32(tl[1][0].data); var ref = M[parse_TSP_Reference(tl[2][0].data)][0];