|
|
import r from 'restructure';
|
|
|
import {ScriptList, FeatureList, LookupList, Coverage, ClassDef, Device, Context, ChainingContext} from './opentype';
|
|
|
import {FeatureVariations} from './variations';
|
|
|
|
|
|
let ValueFormat = new r.Bitfield(r.uint16, [
|
|
|
'xPlacement', 'yPlacement',
|
|
|
'xAdvance', 'yAdvance',
|
|
|
'xPlaDevice', 'yPlaDevice',
|
|
|
'xAdvDevice', 'yAdvDevice'
|
|
|
]);
|
|
|
|
|
|
let types = {
|
|
|
xPlacement: r.int16,
|
|
|
yPlacement: r.int16,
|
|
|
xAdvance: r.int16,
|
|
|
yAdvance: r.int16,
|
|
|
xPlaDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: 'rel' }),
|
|
|
yPlaDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: 'rel' }),
|
|
|
xAdvDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: 'rel' }),
|
|
|
yAdvDevice: new r.Pointer(r.uint16, Device, { type: 'global', relativeTo: 'rel' })
|
|
|
};
|
|
|
|
|
|
class ValueRecord {
|
|
|
constructor(key = 'valueFormat') {
|
|
|
this.key = key;
|
|
|
}
|
|
|
|
|
|
buildStruct(parent) {
|
|
|
let struct = parent;
|
|
|
while (!struct[this.key] && struct.parent) {
|
|
|
struct = struct.parent;
|
|
|
}
|
|
|
|
|
|
if (!struct[this.key]) return;
|
|
|
|
|
|
let fields = {};
|
|
|
fields.rel = () => struct._startOffset;
|
|
|
|
|
|
let format = struct[this.key];
|
|
|
for (let key in format) {
|
|
|
if (format[key]) {
|
|
|
fields[key] = types[key];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return new r.Struct(fields);
|
|
|
}
|
|
|
|
|
|
size(val, ctx) {
|
|
|
return this.buildStruct(ctx).size(val, ctx);
|
|
|
}
|
|
|
|
|
|
decode(stream, parent) {
|
|
|
let res = this.buildStruct(parent).decode(stream, parent);
|
|
|
delete res.rel;
|
|
|
return res;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let PairValueRecord = new r.Struct({
|
|
|
secondGlyph: r.uint16,
|
|
|
value1: new ValueRecord('valueFormat1'),
|
|
|
value2: new ValueRecord('valueFormat2')
|
|
|
});
|
|
|
|
|
|
let PairSet = new r.Array(PairValueRecord, r.uint16);
|
|
|
|
|
|
let Class2Record = new r.Struct({
|
|
|
value1: new ValueRecord('valueFormat1'),
|
|
|
value2: new ValueRecord('valueFormat2')
|
|
|
});
|
|
|
|
|
|
let Anchor = new r.VersionedStruct(r.uint16, {
|
|
|
1: { // Design units only
|
|
|
xCoordinate: r.int16,
|
|
|
yCoordinate: r.int16
|
|
|
},
|
|
|
|
|
|
2: { // Design units plus contour point
|
|
|
xCoordinate: r.int16,
|
|
|
yCoordinate: r.int16,
|
|
|
anchorPoint: r.uint16
|
|
|
},
|
|
|
|
|
|
3: { // Design units plus Device tables
|
|
|
xCoordinate: r.int16,
|
|
|
yCoordinate: r.int16,
|
|
|
xDeviceTable: new r.Pointer(r.uint16, Device),
|
|
|
yDeviceTable: new r.Pointer(r.uint16, Device)
|
|
|
}
|
|
|
});
|
|
|
|
|
|
let EntryExitRecord = new r.Struct({
|
|
|
entryAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'}),
|
|
|
exitAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'})
|
|
|
});
|
|
|
|
|
|
let MarkRecord = new r.Struct({
|
|
|
class: r.uint16,
|
|
|
markAnchor: new r.Pointer(r.uint16, Anchor, {type: 'parent'})
|
|
|
});
|
|
|
|
|
|
let MarkArray = new r.Array(MarkRecord, r.uint16);
|
|
|
|
|
|
let BaseRecord = new r.Array(new r.Pointer(r.uint16, Anchor), t => t.parent.classCount);
|
|
|
let BaseArray = new r.Array(BaseRecord, r.uint16);
|
|
|
|
|
|
let ComponentRecord = new r.Array(new r.Pointer(r.uint16, Anchor), t => t.parent.parent.classCount);
|
|
|
let LigatureAttach = new r.Array(ComponentRecord, r.uint16);
|
|
|
let LigatureArray = new r.Array(new r.Pointer(r.uint16, LigatureAttach), r.uint16);
|
|
|
|
|
|
let GPOSLookup = new r.VersionedStruct('lookupType', {
|
|
|
1: new r.VersionedStruct(r.uint16, { // Single Adjustment
|
|
|
1: { // Single positioning value
|
|
|
coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
valueFormat: ValueFormat,
|
|
|
value: new ValueRecord()
|
|
|
},
|
|
|
2: {
|
|
|
coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
valueFormat: ValueFormat,
|
|
|
valueCount: r.uint16,
|
|
|
values: new r.LazyArray(new ValueRecord(), 'valueCount')
|
|
|
}
|
|
|
}),
|
|
|
|
|
|
2: new r.VersionedStruct(r.uint16, { // Pair Adjustment Positioning
|
|
|
1: { // Adjustments for glyph pairs
|
|
|
coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
valueFormat1: ValueFormat,
|
|
|
valueFormat2: ValueFormat,
|
|
|
pairSetCount: r.uint16,
|
|
|
pairSets: new r.LazyArray(new r.Pointer(r.uint16, PairSet), 'pairSetCount')
|
|
|
},
|
|
|
|
|
|
2: { // Class pair adjustment
|
|
|
coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
valueFormat1: ValueFormat,
|
|
|
valueFormat2: ValueFormat,
|
|
|
classDef1: new r.Pointer(r.uint16, ClassDef),
|
|
|
classDef2: new r.Pointer(r.uint16, ClassDef),
|
|
|
class1Count: r.uint16,
|
|
|
class2Count: r.uint16,
|
|
|
classRecords: new r.LazyArray(new r.LazyArray(Class2Record, 'class2Count'), 'class1Count')
|
|
|
}
|
|
|
}),
|
|
|
|
|
|
3: { // Cursive Attachment Positioning
|
|
|
format: r.uint16,
|
|
|
coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
entryExitCount: r.uint16,
|
|
|
entryExitRecords: new r.Array(EntryExitRecord, 'entryExitCount')
|
|
|
},
|
|
|
|
|
|
4: { // MarkToBase Attachment Positioning
|
|
|
format: r.uint16,
|
|
|
markCoverage: new r.Pointer(r.uint16, Coverage),
|
|
|
baseCoverage: new r.Pointer(r.uint16, Coverage),
|
|
|
classCount: r.uint16,
|
|
|
markArray: new r.Pointer(r.uint16, MarkArray),
|
|
|
baseArray: new r.Pointer(r.uint16, BaseArray)
|
|
|
},
|
|
|
|
|
|
5: { // MarkToLigature Attachment Positioning
|
|
|
format: r.uint16,
|
|
|
markCoverage: new r.Pointer(r.uint16, Coverage),
|
|
|
ligatureCoverage: new r.Pointer(r.uint16, Coverage),
|
|
|
classCount: r.uint16,
|
|
|
markArray: new r.Pointer(r.uint16, MarkArray),
|
|
|
ligatureArray: new r.Pointer(r.uint16, LigatureArray)
|
|
|
},
|
|
|
|
|
|
6: { // MarkToMark Attachment Positioning
|
|
|
format: r.uint16,
|
|
|
mark1Coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
mark2Coverage: new r.Pointer(r.uint16, Coverage),
|
|
|
classCount: r.uint16,
|
|
|
mark1Array: new r.Pointer(r.uint16, MarkArray),
|
|
|
mark2Array: new r.Pointer(r.uint16, BaseArray)
|
|
|
},
|
|
|
|
|
|
7: Context, // Contextual positioning
|
|
|
8: ChainingContext, // Chaining contextual positioning
|
|
|
|
|
|
9: { // Extension Positioning
|
|
|
posFormat: r.uint16,
|
|
|
lookupType: r.uint16, // cannot also be 9
|
|
|
extension: new r.Pointer(r.uint32, GPOSLookup)
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// Fix circular reference
|
|
|
GPOSLookup.versions[9].extension.type = GPOSLookup;
|
|
|
|
|
|
export default new r.VersionedStruct(r.uint32, {
|
|
|
header: {
|
|
|
scriptList: new r.Pointer(r.uint16, ScriptList),
|
|
|
featureList: new r.Pointer(r.uint16, FeatureList),
|
|
|
lookupList: new r.Pointer(r.uint16, new LookupList(GPOSLookup))
|
|
|
},
|
|
|
|
|
|
0x00010000: {},
|
|
|
0x00010001: {
|
|
|
featureVariations: new r.Pointer(r.uint32, FeatureVariations)
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// export GPOSLookup for JSTF table
|
|
|
export { GPOSLookup };
|
|
|
|