import r from 'restructure'; export default class CFFIndex { constructor(type) { this.type = type; } getCFFVersion(ctx) { while (ctx && !ctx.hdrSize) { ctx = ctx.parent; } return ctx ? ctx.version : -1; } decode(stream, parent) { let version = this.getCFFVersion(parent); let count = version >= 2 ? stream.readUInt32BE() : stream.readUInt16BE(); if (count === 0) { return []; } let offSize = stream.readUInt8(); let offsetType; if (offSize === 1) { offsetType = r.uint8; } else if (offSize === 2) { offsetType = r.uint16; } else if (offSize === 3) { offsetType = r.uint24; } else if (offSize === 4) { offsetType = r.uint32; } else { throw new Error(`Bad offset size in CFFIndex: ${offSize} ${stream.pos}`); } let ret = []; let startPos = stream.pos + ((count + 1) * offSize) - 1; let start = offsetType.decode(stream); for (let i = 0; i < count; i++) { let end = offsetType.decode(stream); if (this.type != null) { let pos = stream.pos; stream.pos = startPos + start; parent.length = end - start; ret.push(this.type.decode(stream, parent)); stream.pos = pos; } else { ret.push({ offset: startPos + start, length: end - start }); } start = end; } stream.pos = startPos + start; return ret; } size(arr, parent) { let size = 2; if (arr.length === 0) { return size; } let type = this.type || new r.Buffer; // find maximum offset to detminine offset type let offset = 1; for (let i = 0; i < arr.length; i++) { let item = arr[i]; offset += type.size(item, parent); } let offsetType; if (offset <= 0xff) { offsetType = r.uint8; } else if (offset <= 0xffff) { offsetType = r.uint16; } else if (offset <= 0xffffff) { offsetType = r.uint24; } else if (offset <= 0xffffffff) { offsetType = r.uint32; } else { throw new Error("Bad offset in CFFIndex"); } size += 1 + offsetType.size() * (arr.length + 1); size += offset - 1; return size; } encode(stream, arr, parent) { stream.writeUInt16BE(arr.length); if (arr.length === 0) { return; } let type = this.type || new r.Buffer; // find maximum offset to detminine offset type let sizes = []; let offset = 1; for (let item of arr) { let s = type.size(item, parent); sizes.push(s); offset += s; } let offsetType; if (offset <= 0xff) { offsetType = r.uint8; } else if (offset <= 0xffff) { offsetType = r.uint16; } else if (offset <= 0xffffff) { offsetType = r.uint24; } else if (offset <= 0xffffffff) { offsetType = r.uint32; } else { throw new Error("Bad offset in CFFIndex"); } // write offset size stream.writeUInt8(offsetType.size()); // write elements offset = 1; offsetType.encode(stream, offset); for (let size of sizes) { offset += size; offsetType.encode(stream, offset); } for (let item of arr) { type.encode(stream, item, parent); } return; } }