import r from 'restructure'; class UnboundedArrayAccessor { constructor(type, stream, parent) { this.type = type; this.stream = stream; this.parent = parent; this.base = this.stream.pos; this._items = []; } getItem(index) { if (this._items[index] == null) { let pos = this.stream.pos; this.stream.pos = this.base + this.type.size(null, this.parent) * index; this._items[index] = this.type.decode(this.stream, this.parent); this.stream.pos = pos; } return this._items[index]; } inspect() { return `[UnboundedArray ${this.type.constructor.name}]`; } } export class UnboundedArray extends r.Array { constructor(type) { super(type, 0); } decode(stream, parent) { return new UnboundedArrayAccessor(this.type, stream, parent); } } export let LookupTable = function(ValueType = r.uint16) { // Helper class that makes internal structures invisible to pointers class Shadow { constructor(type) { this.type = type; } decode(stream, ctx) { ctx = ctx.parent.parent; return this.type.decode(stream, ctx); } size(val, ctx) { ctx = ctx.parent.parent; return this.type.size(val, ctx); } encode(stream, val, ctx) { ctx = ctx.parent.parent; return this.type.encode(stream, val, ctx); } } ValueType = new Shadow(ValueType); let BinarySearchHeader = new r.Struct({ unitSize: r.uint16, nUnits: r.uint16, searchRange: r.uint16, entrySelector: r.uint16, rangeShift: r.uint16 }); let LookupSegmentSingle = new r.Struct({ lastGlyph: r.uint16, firstGlyph: r.uint16, value: ValueType }); let LookupSegmentArray = new r.Struct({ lastGlyph: r.uint16, firstGlyph: r.uint16, values: new r.Pointer(r.uint16, new r.Array(ValueType, t => t.lastGlyph - t.firstGlyph + 1), {type: 'parent'}) }); let LookupSingle = new r.Struct({ glyph: r.uint16, value: ValueType }); return new r.VersionedStruct(r.uint16, { 0: { values: new UnboundedArray(ValueType) // length == number of glyphs maybe? }, 2: { binarySearchHeader: BinarySearchHeader, segments: new r.Array(LookupSegmentSingle, t => t.binarySearchHeader.nUnits) }, 4: { binarySearchHeader: BinarySearchHeader, segments: new r.Array(LookupSegmentArray, t => t.binarySearchHeader.nUnits) }, 6: { binarySearchHeader: BinarySearchHeader, segments: new r.Array(LookupSingle, t => t.binarySearchHeader.nUnits) }, 8: { firstGlyph: r.uint16, count: r.uint16, values: new r.Array(ValueType, 'count') } }); }; export function StateTable(entryData = {}, lookupType = r.uint16) { let entry = Object.assign({ newState: r.uint16, flags: r.uint16 }, entryData); let Entry = new r.Struct(entry); let StateArray = new UnboundedArray(new r.Array(r.uint16, t => t.nClasses)); let StateHeader = new r.Struct({ nClasses: r.uint32, classTable: new r.Pointer(r.uint32, new LookupTable(lookupType)), stateArray: new r.Pointer(r.uint32, StateArray), entryTable: new r.Pointer(r.uint32, new UnboundedArray(Entry)) }); return StateHeader; } // This is the old version of the StateTable structure export function StateTable1(entryData = {}, lookupType = r.uint16) { let ClassLookupTable = new r.Struct({ version() { return 8; }, // simulate LookupTable firstGlyph: r.uint16, values: new r.Array(r.uint8, r.uint16) }); let entry = Object.assign({ newStateOffset: r.uint16, // convert offset to stateArray index newState: t => (t.newStateOffset - (t.parent.stateArray.base - t.parent._startOffset)) / t.parent.nClasses, flags: r.uint16 }, entryData); let Entry = new r.Struct(entry); let StateArray = new UnboundedArray(new r.Array(r.uint8, t => t.nClasses)); let StateHeader1 = new r.Struct({ nClasses: r.uint16, classTable: new r.Pointer(r.uint16, ClassLookupTable), stateArray: new r.Pointer(r.uint16, StateArray), entryTable: new r.Pointer(r.uint16, new UnboundedArray(Entry)) }); return StateHeader1; }