NUMBERS write multiple worksheets [ci skip]

This commit is contained in:
SheetJS 2022-09-26 13:51:47 -04:00
parent 1ca49a50bd
commit 4ae4f0fad9

View File

@ -148,6 +148,22 @@ function varint_to_i32(buf: Uint8Array): number {
}
return i32;
}
/** Parse a 64-bit unsigned integer as a pair */
function varint_to_u64(buf: Uint8Array): [number, number] {
var l = 0, lo = buf[l] & 0x7F, hi = 0;
varint: if(buf[l++] >= 0x80) {
lo |= (buf[l] & 0x7F) << 7; if(buf[l++] < 0x80) break varint;
lo |= (buf[l] & 0x7F) << 14; if(buf[l++] < 0x80) break varint;
lo |= (buf[l] & 0x7F) << 21; if(buf[l++] < 0x80) break varint;
lo |= (buf[l] & 0x7F) << 28; hi = (buf[l] >> 4) & 0x07; if(buf[l++] < 0x80) break varint;
hi |= (buf[l] & 0x7F) << 3; if(buf[l++] < 0x80) break varint;
hi |= (buf[l] & 0x7F) << 10; if(buf[l++] < 0x80) break varint;
hi |= (buf[l] & 0x7F) << 17; if(buf[l++] < 0x80) break varint;
hi |= (buf[l] & 0x7F) << 24; if(buf[l++] < 0x80) break varint;
hi |= (buf[l] & 0x7F) << 31;
}
return [lo >>> 0, hi >>> 0];
}
//<<export { varint_to_i32 };
interface ProtoItem {
@ -608,6 +624,21 @@ function write_TSP_Reference(idx: number): Uint8Array {
}
//<<export { parse_TSP_Reference, write_TSP_Reference };
/** Insert Object Reference */
function numbers_add_oref(iwa: IWAArchiveInfo, ref: number): void {
var orefs: number[] = iwa.messages[0].meta[5]?.[0] ? parse_packed_varints(iwa.messages[0].meta[5][0].data) : [];
var orefidx = orefs.indexOf(ref);
if(orefidx == -1) {
orefs.push(ref);
iwa.messages[0].meta[5] =[ {type: 2, data: write_packed_varints(orefs) }];
}
}
/** Delete Object Reference */
function numbers_del_oref(iwa: IWAArchiveInfo, ref: number): void {
var orefs: number[] = iwa.messages[0].meta[5]?.[0] ? parse_packed_varints(iwa.messages[0].meta[5][0].data) : [];
iwa.messages[0].meta[5] =[ {type: 2, data: write_packed_varints(orefs.filter(r => r != ref)) }];
}
type MessageSpace = {[id: number]: IWAMessage[]};
/** Parse .TST.TableDataList */
@ -621,6 +652,7 @@ function parse_TST_TableDataList(M: MessageSpace, root: IWAMessage): any[] {
(entries||[]).forEach(entry => {
// .TST.TableDataList.ListEntry
var le = parse_shallow(entry.data);
if(!le[1]) return; // sometimes the list has a spurious entry at index 0
var key = varint_to_i32(le[1][0].data)>>>0;
switch(type) {
case 1: data[key] = u8str(le[3][0].data); break;
@ -853,7 +885,7 @@ function parse_numbers_iwa(cfb: CFB$Container, opts?: ParsingOptions ): WorkBook
if(!indices.length) throw new Error("File has no messages");
/* find document root */
if(M?.[1]?.[0]?.meta?.[1]?.[0].data && varint_to_i32(M[1][0].meta[1][0].data) == 10000) throw new Error("Pages documents are not supported");
if(M?.[1]?.[0].meta?.[1]?.[0].data && varint_to_i32(M[1][0].meta[1][0].data) == 10000) throw new Error("Pages documents are not supported");
var docroot: IWAMessage | false = M?.[1]?.[0]?.meta?.[1]?.[0].data && varint_to_i32(M[1][0].meta[1][0].data) == 1 && M[1][0];
if(!docroot) indices.forEach((idx) => {
M[idx].forEach((iwam) => {
@ -876,7 +908,18 @@ interface DependentInfo {
type: number;
}
/** Write .TST.TileRowInfo */
function write_tile_row(tri: ProtoMessage, data: any[], SST: string[], wide: boolean): number {
function write_TST_TileRowInfo(data: any[], SST: string[], wide: boolean): ProtoMessage {
var tri: ProtoMessage = [
[],
[ { type: 0, data: write_varint49(0) }],
[ { type: 0, data: write_varint49(0) }],
[ { type: 2, data: new Uint8Array([]) }],
[ { type: 2, data: new Uint8Array(Array.from({length:510}, () => 255)) }],
[ { type: 0, data: write_varint49(5) }],
[ { type: 2, data: new Uint8Array([]) }],
[ { type: 2, data: new Uint8Array(Array.from({length:510}, () => 255)) }],
[ { type: 0, data: write_varint49(1) }],
] as ProtoMessage;
if(!tri[6]?.[0] || !tri[7]?.[0]) throw "Mutation only works on post-BNC storages!";
//var wide_offsets = tri[8]?.[0]?.data && varint_to_i32(tri[8][0].data) > 0 || false;
var cnt = 0;
@ -930,7 +973,7 @@ function write_tile_row(tri: ProtoMessage, data: any[], SST: string[], wide: boo
tri[6][0].data = u8concat(cell_storage);
/*if(!wide)*/ tri[3][0].data = u8concat(_cell_storage);
tri[8] = [{type: 0, data: write_varint49(wide ? 1 : 0)}];
return cnt;
return tri;
}
/** Write IWA Message */
@ -1000,16 +1043,21 @@ function write_numbers_iwa(wb: WorkBook, opts?: WritingOptions): CFB$Container {
/* read template and build packet metadata */
var cfb: CFB$Container = CFB.read(opts.numbers, { type: "base64" });
var dependents: Dependents = build_numbers_deps(cfb);
var deps: Dependents = build_numbers_deps(cfb);
/* .TN.DocumentArchive */
var cfb_DA = CFB.find(cfb, dependents[1].location);
if(!cfb_DA) throw `Could not find ${dependents[1].location} in Numbers template`;
var iwa_DA = parse_iwa_file(decompress_iwa_file(cfb_DA.content as Uint8Array));
var docroot: IWAArchiveInfo = iwa_DA.find(packet => packet.id == 1) as IWAArchiveInfo;
var docroot: IWAArchiveInfo = numbers_iwa_find(cfb, deps, 1);
if(docroot == null) throw `Could not find message ${1} in Numbers template`;
var sheetrefs = mappa(parse_shallow(docroot.messages[0].data)[1], parse_TSP_Reference);
wb.SheetNames.forEach((name, idx) => write_numbers_ws(cfb, dependents, wb.Sheets[name], name, idx, sheetrefs[idx]));
if(sheetrefs.length > 1) throw new Error("Template NUMBERS file must have exactly one sheet")
wb.SheetNames.forEach((name, idx) => {
if(idx >= 1) {
numbers_add_ws(cfb, deps, idx + 1);
docroot = numbers_iwa_find(cfb, deps, 1);
sheetrefs = mappa(parse_shallow(docroot.messages[0].data)[1], parse_TSP_Reference);
}
write_numbers_ws(cfb, deps, wb.Sheets[name], name, idx, sheetrefs[idx])
});
return cfb;
}
@ -1034,10 +1082,377 @@ function numbers_iwa_find(cfb: CFB$Container, deps: Dependents, id: number) {
return ainfo;
}
/** Deep copy of the essential parts of a worksheet */
function numbers_add_ws(cfb: CFB$Container, deps: Dependents, wsidx: number) {
var sheetref = -1, newsheetref = -1;
var remap: {[x: number]: number} = {};
/* .TN.DocumentArchive -> new .TN.SheetArchive */
numbers_iwa_doit(cfb, deps, 1, (docroot: IWAArchiveInfo, arch: IWAArchiveInfo[]) => {
var doc = parse_shallow(docroot.messages[0].data);
/* new sheet reference */
sheetref = parse_TSP_Reference(parse_shallow(docroot.messages[0].data)[1][0].data);
newsheetref = get_unique_msgid({ deps: [1], location: deps[sheetref].location, type: 2 }, deps);
remap[sheetref] = newsheetref;
/* connect root -> sheet */
numbers_add_oref(docroot, newsheetref);
doc[1].push({ type: 2, data: write_TSP_Reference(newsheetref) });
/* copy sheet */
var sheet = numbers_iwa_find(cfb, deps, sheetref);
sheet.id = newsheetref;
if(deps[1].location == deps[newsheetref].location) arch.push(sheet);
else numbers_iwa_doit(cfb, deps, newsheetref, (_, x) => x.push(sheet));
docroot.messages[0].data = write_shallow(doc);
});
/* .TN.SheetArchive -> new .TST.TableInfoArchive */
var tiaref = -1;
numbers_iwa_doit(cfb, deps, newsheetref, (sheetroot: IWAArchiveInfo, arch: IWAArchiveInfo[]) => {
var sa = parse_shallow(sheetroot.messages[0].data);
/* remove everything except for name and drawables */
for(var i = 3; i <= 69; ++i) delete sa[i];
/* remove all references to drawables */
var drawables = mappa(sa[2], parse_TSP_Reference);
drawables.forEach(n => numbers_del_oref(sheetroot, n));
/* add new tia reference */
tiaref = get_unique_msgid({ deps: [newsheetref], location: deps[drawables[0]].location, type: deps[drawables[0]].type }, deps);
numbers_add_oref(sheetroot, tiaref);
remap[drawables[0]] = tiaref;
sa[2] = [ { type: 2, data: write_TSP_Reference(tiaref) } ];
/* copy tia */
var tia = numbers_iwa_find(cfb, deps, drawables[0]);
tia.id = tiaref;
if(deps[drawables[0]].location == deps[newsheetref].location) arch.push(tia);
else {
var loc = deps[newsheetref].location;
loc = loc.replace(/^Root Entry\//,""); // NOTE: the Root Entry prefix is an artifact of the CFB container library
loc = loc.replace(/^Index\//, "").replace(/\.iwa$/,"");
numbers_iwa_doit(cfb, deps, 2, (ai => {
var mlist = parse_shallow(ai.messages[0].data);
/* add reference from SheetArchive file to TIA */
var parentidx = mlist[3].findIndex(m => {
var mm = parse_shallow(m.data);
if(mm[3]?.[0]) return u8str(mm[3][0].data) == loc;
if(mm[2]?.[0] && u8str(mm[2][0].data) == loc) return true;
return false;
});
var parent = parse_shallow(mlist[3][parentidx].data);
if(!parent[6]) parent[6] = [];
parent[6].push({
type: 2,
data: write_shallow([
[],
[{type: 0, data: write_varint49(tiaref) }]
])
});
mlist[3][parentidx].data = write_shallow(parent);
ai.messages[0].data = write_shallow(mlist);
}));
numbers_iwa_doit(cfb, deps, tiaref, (_, x) => x.push(tia));
}
sheetroot.messages[0].data = write_shallow(sa);
});
/* .TST.TableInfoArchive -> new .TST.TableModelArchive */
var tmaref = -1;
numbers_iwa_doit(cfb, deps, tiaref, (tiaroot: IWAArchiveInfo, arch: IWAArchiveInfo[]) => {
var tia = parse_shallow(tiaroot.messages[0].data);
/* update parent reference */
var da = parse_shallow(tia[1][0].data);
for(var i = 3; i <= 69; ++i) delete da[i];
var dap = parse_TSP_Reference(da[2][0].data);
da[2][0].data = write_TSP_Reference(remap[dap]);
tia[1][0].data = write_shallow(da);
/* remove old tma reference */
var oldtmaref = parse_TSP_Reference(tia[2][0].data);
numbers_del_oref(tiaroot, oldtmaref);
/* add new tma reference */
tmaref = get_unique_msgid({ deps: [tiaref], location: deps[oldtmaref].location, type: deps[oldtmaref].type }, deps);
numbers_add_oref(tiaroot, tmaref);
remap[oldtmaref] = tmaref;
tia[2][0].data = write_TSP_Reference(tmaref);
/* copy tma */
var tma = numbers_iwa_find(cfb, deps, oldtmaref);
tma.id = tmaref;
if(deps[tiaref].location == deps[tmaref].location) arch.push(tma);
else numbers_iwa_doit(cfb, deps, tmaref, (_, x) => x.push(tma));
tiaroot.messages[0].data = write_shallow(tia);
});
/* identifier for finding the TableModelArchive in the archive */
var loc = deps[tmaref].location;
loc = loc.replace(/^Root Entry\//,""); // NOTE: the Root Entry prefix is an artifact of the CFB container library
loc = loc.replace(/^Index\//, "").replace(/\.iwa$/,"");
/* .TST.TableModelArchive */
numbers_iwa_doit(cfb, deps, tmaref, (tmaroot: IWAArchiveInfo, arch: IWAArchiveInfo[]) => {
/* TODO: formulae currently break due to missing CE details */
var tma = parse_shallow(tmaroot.messages[0].data);
var uuid = u8str(tma[1][0].data), new_uuid = uuid.replace(/-[A-Z0-9]*/, `-${wsidx.toString(16).padStart(4, "0")}`);
tma[1][0].data = stru8(new_uuid);
/* NOTE: These lists should be revisited every time the template is changed */
/* bare fields */
[ 12, 13, 29, 31, 32, 33, 39, 44, 47, 81, 82, 84 ].forEach(n => delete tma[n]);
if(tma[45]) {
// .TST.SortRuleReferenceTrackerArchive
var srrta = parse_shallow(tma[45][0].data);
var ref = parse_TSP_Reference(srrta[1][0].data);
numbers_del_oref(tmaroot, ref);
delete tma[45];
}
if(tma[70]) {
// .TST.HiddenStatesOwnerArchive
var hsoa = parse_shallow(tma[70][0].data);
hsoa[2]?.forEach(item => {
// .TST.HiddenStatesArchive
var hsa = parse_shallow(item.data);
[2,3].map(n => hsa[n][0]).forEach(hseadata => {
// .TST.HiddenStateExtentArchive
var hsea = parse_shallow(hseadata.data);
if(!hsea[8]) return;
var ref = parse_TSP_Reference(hsea[8][0].data);
numbers_del_oref(tmaroot, ref);
});
});
delete tma[70];
}
[ 46, // deleting field 46 (base_column_row_uids) forces Numbers to refresh cell table
30, 34, 35, 36, 38, 48, 49, 60, 61, 62, 63, 64, 71, 72, 73, 74, 75, 85, 86, 87, 88, 89
].forEach(n => {
if(!tma[n]) return;
var ref = parse_TSP_Reference(tma[n][0].data);
delete tma[n];
numbers_del_oref(tmaroot, ref);
});
/* update .TST.DataStore */
var store = parse_shallow(tma[4][0].data);
{
/* TODO: actually scan through dep tree and update */
/* blind copy of single references */
[ 2, 4, 5, 6, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22 ].forEach(n => {
if(!store[n]?.[0]) return;
var oldref = parse_TSP_Reference(store[n][0].data);
var newref = get_unique_msgid({ deps: [tmaref], location: deps[oldref].location, type: deps[oldref].type }, deps);
numbers_del_oref(tmaroot, oldref);
numbers_add_oref(tmaroot, newref);
remap[oldref] = newref;
var msg = numbers_iwa_find(cfb, deps, oldref);
msg.id = newref;
if(deps[oldref].location == deps[tmaref].location) arch.push(msg);
else {
deps[newref].location = deps[oldref].location.replace(oldref.toString(), newref.toString());
if(deps[newref].location == deps[oldref].location) deps[newref].location = deps[newref].location.replace(/\.iwa/, `-${newref}.iwa`);
CFB.utils.cfb_add(cfb, deps[newref].location, compress_iwa_file(write_iwa_file([ msg ])));
var newloc = deps[newref].location;
newloc = newloc.replace(/^Root Entry\//,""); // NOTE: the Root Entry prefix is an artifact of the CFB container library
newloc = newloc.replace(/^Index\//, "").replace(/\.iwa$/,"");
numbers_iwa_doit(cfb, deps, 2, (ai => {
var mlist = parse_shallow(ai.messages[0].data);
mlist[3].push({type: 2, data: write_shallow([
[],
[{type: 0, data: write_varint49(newref)}],
[{type: 2, data: stru8(newloc.replace(/-.*$/, "")) }],
[{type: 2, data: stru8(newloc)}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[],
[],
[],
[],
[{type: 0, data: write_varint49(0)}],
[],
[{type: 0, data: write_varint49(0 /* TODO: save_token */)}],
])});
mlist[1] = [{type: 0, data: write_varint49(Math.max(newref + 1, parse_varint49(mlist[1][0].data) ))}];
/* add reference from TableModelArchive file to Tile */
var parentidx = mlist[3].findIndex(m => {
var mm = parse_shallow(m.data);
if(mm[3]?.[0]) return u8str(mm[3][0].data) == loc;
if(mm[2]?.[0] && u8str(mm[2][0].data) == loc) return true;
return false;
});
var parent = parse_shallow(mlist[3][parentidx].data);
if(!parent[6]) parent[6] = [];
parent[6].push({
type: 2,
data: write_shallow([
[],
[{type: 0, data: write_varint49(newref) }]
])
});
mlist[3][parentidx].data = write_shallow(parent);
ai.messages[0].data = write_shallow(mlist);
}));
}
store[n][0].data = write_TSP_Reference(newref);
});
/* copy row header storage */
var row_headers = parse_shallow(store[1][0].data);
{
row_headers[2]?.forEach(tspref => {
var oldref = parse_TSP_Reference(tspref.data);
var newref = get_unique_msgid({ deps: [tmaref], location: deps[oldref].location, type: deps[oldref].type }, deps);
numbers_del_oref(tmaroot, oldref);
numbers_add_oref(tmaroot, newref);
remap[oldref] = newref;
var msg = numbers_iwa_find(cfb, deps, oldref);
msg.id = newref;
if(deps[oldref].location == deps[tmaref].location) {
arch.push(msg);
} else {
deps[newref].location = deps[oldref].location.replace(oldref.toString(), newref.toString());
if(deps[newref].location == deps[oldref].location) deps[newref].location = deps[newref].location.replace(/\.iwa/, `-${newref}.iwa`);
CFB.utils.cfb_add(cfb, deps[newref].location, compress_iwa_file(write_iwa_file([ msg ])));
var newloc = deps[newref].location;
newloc = newloc.replace(/^Root Entry\//,""); // NOTE: the Root Entry prefix is an artifact of the CFB container library
newloc = newloc.replace(/^Index\//, "").replace(/\.iwa$/,"");
numbers_iwa_doit(cfb, deps, 2, (ai => {
var mlist = parse_shallow(ai.messages[0].data);
mlist[3].push({type: 2, data: write_shallow([
[],
[{type: 0, data: write_varint49(newref)}],
[{type: 2, data: stru8(newloc.replace(/-.*$/, "")) }],
[{type: 2, data: stru8(newloc)}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[],
[],
[],
[],
[{type: 0, data: write_varint49(0)}],
[],
[{type: 0, data: write_varint49(0 /* TODO: save_token */)}],
])});
mlist[1] = [{type: 0, data: write_varint49(Math.max(newref + 1, parse_varint49(mlist[1][0].data) ))}];
/* add reference from TableModelArchive file to Tile */
var parentidx = mlist[3].findIndex(m => {
var mm = parse_shallow(m.data);
if(mm[3]?.[0]) return u8str(mm[3][0].data) == loc;
if(mm[2]?.[0] && u8str(mm[2][0].data) == loc) return true;
return false;
});
var parent = parse_shallow(mlist[3][parentidx].data);
if(!parent[6]) parent[6] = [];
parent[6].push({
type: 2,
data: write_shallow([
[],
[{type: 0, data: write_varint49(newref) }]
])
});
mlist[3][parentidx].data = write_shallow(parent);
ai.messages[0].data = write_shallow(mlist);
}));
}
tspref.data = write_TSP_Reference(newref);
})
}
store[1][0].data = write_shallow(row_headers);
/* copy tiles */
var tiles = parse_shallow(store[3][0].data);
{
tiles[1].forEach(t => {
var tst = parse_shallow(t.data);
var oldtileref = parse_TSP_Reference(tst[2][0].data);
var newtileref = remap[oldtileref];
if(!remap[oldtileref]) {
newtileref = get_unique_msgid({ deps: [tmaref], location: "", type: deps[oldtileref].type }, deps);
deps[newtileref].location = `Root Entry/Index/Tables/Tile-${newtileref}.iwa`;
remap[oldtileref] = newtileref;
var oldtile = numbers_iwa_find(cfb, deps, oldtileref);
oldtile.id = newtileref;
numbers_del_oref(tmaroot, oldtileref);
numbers_add_oref(tmaroot, newtileref);
CFB.utils.cfb_add(cfb, `/Index/Tables/Tile-${newtileref}.iwa`, compress_iwa_file(write_iwa_file([ oldtile ])));
numbers_iwa_doit(cfb, deps, 2, (ai => {
var mlist = parse_shallow(ai.messages[0].data);
mlist[3].push({type: 2, data: write_shallow([
[],
[{type: 0, data: write_varint49(newtileref)}],
[{type: 2, data: stru8("Tables/Tile") }],
[{type: 2, data: stru8(`Tables/Tile-${newtileref}`)}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[{type: 2, data: new Uint8Array([2, 0, 0])}],
[],
[],
[],
[],
[{type: 0, data: write_varint49(0)}],
[],
[{type: 0, data: write_varint49(0 /* TODO: save_token */)}],
])});
mlist[1] = [{type: 0, data: write_varint49(Math.max(newtileref + 1, parse_varint49(mlist[1][0].data) ))}];
/* add reference from TableModelArchive file to Tile */
var parentidx = mlist[3].findIndex(m => {
var mm = parse_shallow(m.data);
if(mm[3]?.[0]) return u8str(mm[3][0].data) == loc;
if(mm[2]?.[0] && u8str(mm[2][0].data) == loc) return true;
return false;
});
var parent = parse_shallow(mlist[3][parentidx].data);
if(!parent[6]) parent[6] = [];
parent[6].push({
type: 2,
data: write_shallow([
[],
[{type: 0, data: write_varint49(newtileref) }]
])
});
mlist[3][parentidx].data = write_shallow(parent);
ai.messages[0].data = write_shallow(mlist);
}));
}
tst[2][0].data = write_TSP_Reference(newtileref);
t.data = write_shallow(tst);
})
}
store[3][0].data = write_shallow(tiles);
}
tma[4][0].data = write_shallow(store);
tmaroot.messages[0].data = write_shallow(tma);
});
}
/** Write NUMBERS worksheet */
function write_numbers_ws(cfb: CFB$Container, deps: Dependents, ws: WorkSheet, wsname: string, sheetidx: number, rootref: number): void {
/* TODO: support multiple worksheets, larger ranges, more data types, etc */
if(sheetidx >= 1) return console.error("The Numbers writer currently writes only the first table");
/* TODO: support more data types, etc */
/* .TN.SheetArchive */
var drawables: number[] = [];
@ -1064,7 +1479,7 @@ function write_numbers_ws(cfb: CFB$Container, deps: Dependents, ws: WorkSheet, w
var USE_WIDE_ROWS = true;
/** Write .TST.TableModelArchive */
function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IWAArchiveInfo, tmafile: IWAArchiveInfo[], tmaref: number) {
function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws: WorkSheet, tmaroot: IWAArchiveInfo, tmafile: IWAArchiveInfo[], tmaref: number) {
var range = decode_range(ws["!ref"] as string);
range.s.r = range.s.c = 0;
@ -1128,6 +1543,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
{
sstdata[3] = [];
SST.forEach((str, i) => {
if(i == 0) return; // Numbers will assert if index zero
sstdata[3].push({type: 2, data: write_shallow([ [],
[ { type: 0, data: write_varint49(i) } ],
[ { type: 0, data: write_varint49(1) } ],
@ -1145,12 +1561,21 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
var tilestore = parse_shallow(store[3][0].data);
{
/* number of rows per tile */
var tstride = 256; // NOTE: if this is not 256, Numbers will recalculate
var tstride = 256; // NOTE: if this is not 256, Numbers will assert and recalculate
tilestore[2] = [{type: 0, data: write_varint49(tstride)}];
//tilestore[3] = [{type: 0, data: write_varint49(USE_WIDE_ROWS ? 1 : 0)}]; // elicits a modification message
var tileref = parse_TSP_Reference(parse_shallow(tilestore[1][0].data)[2][0].data);
var save_token = 0;
/* save the save_token from package metadata */
var save_token = ((): number => {
/* .TSP.PackageMetadata */
var metadata = numbers_iwa_find(cfb, deps, 2);
var mlist = parse_shallow(metadata.messages[0].data);
/* .TSP.ComponentInfo field 1 is the id, field 12 is the save token */
var mlst = mlist[3].filter(m => parse_varint49(parse_shallow(m.data)[1][0].data) == tileref);
return (mlst?.length) ? parse_varint49(parse_shallow(mlst[0].data)[12][0].data) : 0;
})();
/* remove existing tile */
{
@ -1160,8 +1585,6 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
numbers_iwa_doit(cfb, deps, 2, (ai => {
var mlist = parse_shallow(ai.messages[0].data);
var lst = mlist[3].filter(m => parse_varint49(parse_shallow(m.data)[1][0].data) == tileref);
if(lst && lst.length > 0) save_token = parse_varint49(parse_shallow(lst[0].data)[12][0].data);
mlist[3] = mlist[3].filter(m => parse_varint49(parse_shallow(m.data)[1][0].data) != tileref);
/* remove reference from TableModelArchive file to Tile */
@ -1178,6 +1601,8 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
ai.messages[0].data = write_shallow(mlist);
}));
numbers_del_oref(tmaroot, tileref);
}
/* rewrite entire tile storage */
@ -1205,18 +1630,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
[{type: 0, data: write_varint49(USE_WIDE_ROWS ? 1 : 0)}]
];
for(var R = tidx * tstride; R <= Math.min(range.e.r, (tidx + 1) * tstride - 1); ++R) {
var tilerow: ProtoMessage = [
[],
[ { type: 0, data: write_varint49(0) }],
[ { type: 0, data: write_varint49(0) }],
[ { type: 2, data: new Uint8Array([]) }],
[ { type: 2, data: new Uint8Array(Array.from({length:510}, () => 255)) }],
[ { type: 0, data: write_varint49(5) }],
[ { type: 2, data: new Uint8Array([]) }],
[ { type: 2, data: new Uint8Array(Array.from({length:510}, () => 255)) }],
[ { type: 0, data: write_varint49(1) }],
] as ProtoMessage;
write_tile_row(tilerow, data[R], SST, USE_WIDE_ROWS);
var tilerow = write_TST_TileRowInfo(data[R], SST, USE_WIDE_ROWS);
tilerow[1][0].data = write_varint49(R - tidx * tstride);
tiledata[5].push({data: write_shallow(tilerow), type: 2});
}
@ -1278,12 +1692,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
}));
/* add to TableModelArchive object references */
var orefs: number[] = tmaroot.messages[0].meta[5]?.[0] ? parse_packed_varints(tmaroot.messages[0].meta[5][0].data) : [];
var orefidx = orefs.indexOf(newtileid);
if(orefidx == -1) {
orefs[orefidx = orefs.length] = newtileid;
tmaroot.messages[0].meta[5] =[ {type: 2, data: write_packed_varints(orefs) }];
}
numbers_add_oref(tmaroot, newtileid);
/* add to row rbtree */
rbtree[1].push({type: 2, data: write_shallow([
@ -1345,12 +1754,7 @@ function write_numbers_tma(cfb: CFB$Container, deps: Dependents, ws, tmaroot: IW
}));
/* add object reference from TableModelArchive */
/* var*/ orefs /*: number[]*/ = tmaroot.messages[0].meta[5]?.[0] ? parse_packed_varints(tmaroot.messages[0].meta[5][0].data) : [];
/* var*/ orefidx = orefs.indexOf(mergeid);
if(orefidx == -1) {
orefs[orefidx = orefs.length] = mergeid;
tmaroot.messages[0].meta[5] =[ {type: 2, data: write_packed_varints(orefs) }];
}
numbers_add_oref(tmaroot, mergeid);
} else delete store[13]; // TODO: delete references to merge if not needed