forked from sheetjs/sheetjs
NUMBERS write multiple worksheets [ci skip]
This commit is contained in:
parent
1ca49a50bd
commit
4ae4f0fad9
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user