252 lines
8.5 KiB
Plaintext
252 lines
8.5 KiB
Plaintext
// The `GSUB` table contains ligatures, among other things.
|
|
// https://www.microsoft.com/typography/OTSPEC/gsub.htm
|
|
|
|
import check from '../check';
|
|
import { Parser } from '../parse';
|
|
|
|
const subtableParsers = new Array(9); // subtableParsers[0] is unused
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#SS
|
|
subtableParsers[1] = function parseLookup1() {
|
|
const start = this.offset + this.relativeOffset;
|
|
const substFormat = this.parseUShort();
|
|
if (substFormat === 1) {
|
|
return {
|
|
substFormat: 1,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
deltaGlyphId: this.parseUShort(),
|
|
};
|
|
} else if (substFormat === 2) {
|
|
return {
|
|
substFormat: 2,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
substitute: this.parseOffset16List(),
|
|
};
|
|
}
|
|
check.assert(
|
|
false,
|
|
'0x' + start.toString(16) + ': lookup type 1 format must be 1 or 2.'
|
|
);
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#MS
|
|
subtableParsers[2] = function parseLookup2() {
|
|
const substFormat = this.parseUShort();
|
|
check.argument(
|
|
substFormat === 1,
|
|
'GSUB Multiple Substitution Subtable identifier-format must be 1'
|
|
);
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
sequences: this.parseListOfLists(),
|
|
};
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#AS
|
|
subtableParsers[3] = function parseLookup3() {
|
|
const substFormat = this.parseUShort();
|
|
check.argument(
|
|
substFormat === 1,
|
|
'GSUB Alternate Substitution Subtable identifier-format must be 1'
|
|
);
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
alternateSets: this.parseListOfLists(),
|
|
};
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#LS
|
|
subtableParsers[4] = function parseLookup4() {
|
|
const substFormat = this.parseUShort();
|
|
check.argument(
|
|
substFormat === 1,
|
|
'GSUB ligature table identifier-format must be 1'
|
|
);
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
ligatureSets: this.parseListOfLists(function () {
|
|
return {
|
|
ligGlyph: this.parseUShort(),
|
|
components: this.parseUShortList(this.parseUShort() - 1),
|
|
};
|
|
}),
|
|
};
|
|
};
|
|
|
|
const lookupRecordDesc = {
|
|
sequenceIndex: Parser.uShort,
|
|
lookupListIndex: Parser.uShort,
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#CSF
|
|
subtableParsers[5] = function parseLookup5() {
|
|
const start = this.offset + this.relativeOffset;
|
|
const substFormat = this.parseUShort();
|
|
|
|
if (substFormat === 1) {
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
ruleSets: this.parseListOfLists(function () {
|
|
const glyphCount = this.parseUShort();
|
|
const substCount = this.parseUShort();
|
|
return {
|
|
input: this.parseUShortList(glyphCount - 1),
|
|
lookupRecords: this.parseRecordList(
|
|
substCount,
|
|
lookupRecordDesc
|
|
),
|
|
};
|
|
}),
|
|
};
|
|
} else if (substFormat === 2) {
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
classDef: this.parsePointer(Parser.classDef),
|
|
classSets: this.parseListOfLists(function () {
|
|
const glyphCount = this.parseUShort();
|
|
const substCount = this.parseUShort();
|
|
return {
|
|
classes: this.parseUShortList(glyphCount - 1),
|
|
lookupRecords: this.parseRecordList(
|
|
substCount,
|
|
lookupRecordDesc
|
|
),
|
|
};
|
|
}),
|
|
};
|
|
} else if (substFormat === 3) {
|
|
const glyphCount = this.parseUShort();
|
|
const substCount = this.parseUShort();
|
|
return {
|
|
substFormat: substFormat,
|
|
coverages: this.parseList(
|
|
glyphCount,
|
|
Parser.pointer(Parser.coverage)
|
|
),
|
|
lookupRecords: this.parseRecordList(substCount, lookupRecordDesc),
|
|
};
|
|
}
|
|
check.assert(
|
|
false,
|
|
'0x' + start.toString(16) + ': lookup type 5 format must be 1, 2 or 3.'
|
|
);
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#CC
|
|
subtableParsers[6] = function parseLookup6() {
|
|
const start = this.offset + this.relativeOffset;
|
|
const substFormat = this.parseUShort();
|
|
if (substFormat === 1) {
|
|
return {
|
|
substFormat: 1,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
chainRuleSets: this.parseListOfLists(function () {
|
|
return {
|
|
backtrack: this.parseUShortList(),
|
|
input: this.parseUShortList(this.parseShort() - 1),
|
|
lookahead: this.parseUShortList(),
|
|
lookupRecords: this.parseRecordList(lookupRecordDesc),
|
|
};
|
|
}),
|
|
};
|
|
} else if (substFormat === 2) {
|
|
return {
|
|
substFormat: 2,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
backtrackClassDef: this.parsePointer(Parser.classDef),
|
|
inputClassDef: this.parsePointer(Parser.classDef),
|
|
lookaheadClassDef: this.parsePointer(Parser.classDef),
|
|
chainClassSet: this.parseListOfLists(function () {
|
|
return {
|
|
backtrack: this.parseUShortList(),
|
|
input: this.parseUShortList(this.parseShort() - 1),
|
|
lookahead: this.parseUShortList(),
|
|
lookupRecords: this.parseRecordList(lookupRecordDesc),
|
|
};
|
|
}),
|
|
};
|
|
} else if (substFormat === 3) {
|
|
return {
|
|
substFormat: 3,
|
|
backtrackCoverage: this.parseList(Parser.pointer(Parser.coverage)),
|
|
inputCoverage: this.parseList(Parser.pointer(Parser.coverage)),
|
|
lookaheadCoverage: this.parseList(Parser.pointer(Parser.coverage)),
|
|
lookupRecords: this.parseRecordList(lookupRecordDesc),
|
|
};
|
|
}
|
|
check.assert(
|
|
false,
|
|
'0x' + start.toString(16) + ': lookup type 6 format must be 1, 2 or 3.'
|
|
);
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#ES
|
|
subtableParsers[7] = function parseLookup7() {
|
|
// Extension Substitution subtable
|
|
const substFormat = this.parseUShort();
|
|
check.argument(
|
|
substFormat === 1,
|
|
'GSUB Extension Substitution subtable identifier-format must be 1'
|
|
);
|
|
const extensionLookupType = this.parseUShort();
|
|
const extensionParser = new Parser(
|
|
this.data,
|
|
this.offset + this.parseULong()
|
|
);
|
|
return {
|
|
substFormat: 1,
|
|
lookupType: extensionLookupType,
|
|
extension: subtableParsers[extensionLookupType].call(extensionParser),
|
|
};
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/GSUB.htm#RCCS
|
|
subtableParsers[8] = function parseLookup8() {
|
|
const substFormat = this.parseUShort();
|
|
check.argument(
|
|
substFormat === 1,
|
|
'GSUB Reverse Chaining Contextual Single Substitution Subtable identifier-format must be 1'
|
|
);
|
|
return {
|
|
substFormat: substFormat,
|
|
coverage: this.parsePointer(Parser.coverage),
|
|
backtrackCoverage: this.parseList(Parser.pointer(Parser.coverage)),
|
|
lookaheadCoverage: this.parseList(Parser.pointer(Parser.coverage)),
|
|
substitutes: this.parseUShortList(),
|
|
};
|
|
};
|
|
|
|
// https://www.microsoft.com/typography/OTSPEC/gsub.htm
|
|
function parseGsubTable(data, start) {
|
|
start = start || 0;
|
|
const p = new Parser(data, start);
|
|
const tableVersion = p.parseVersion(1);
|
|
check.argument(
|
|
tableVersion === 1 || tableVersion === 1.1,
|
|
'Unsupported GSUB table version.'
|
|
);
|
|
if (tableVersion === 1) {
|
|
return {
|
|
version: tableVersion,
|
|
scripts: p.parseScriptList(),
|
|
features: p.parseFeatureList(),
|
|
lookups: p.parseLookupList(subtableParsers),
|
|
};
|
|
} else {
|
|
return {
|
|
version: tableVersion,
|
|
scripts: p.parseScriptList(),
|
|
features: p.parseFeatureList(),
|
|
lookups: p.parseLookupList(subtableParsers),
|
|
variations: p.parseFeatureVariationsList(),
|
|
};
|
|
}
|
|
}
|
|
|
|
export default { parse: parseGsubTable };
|