font-ligatures
Version:
Find ligature replacements for any system font
97 lines (82 loc) • 3.85 kB
text/typescript
import { ChainingContextualSubstitutionTable, Lookup } from '../tables';
import { LookupTree } from '../types';
import mergeTrees from '../merge';
import { listGlyphsByIndex } from './coverage';
import getGlyphClass, { listClassGlyphs } from './classDef';
import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, EntryMeta } from './helper';
/**
* Build lookup tree for GSUB lookup table 6, format 2.
* https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#62-chaining-context-substitution-format-2-class-based-glyph-contexts
*
* @param table JSON representation of the table
* @param lookups List of lookup tables
* @param tableIndex Index of this table in the overall lookup
*/
export default function buildTree(table: ChainingContextualSubstitutionTable.Format2, lookups: Lookup[], tableIndex: number): LookupTree {
const results: LookupTree[] = [];
const firstGlyphs = listGlyphsByIndex(table.coverage);
for (const { glyphId } of firstGlyphs) {
const firstInputClass = getGlyphClass(table.inputClassDef, glyphId);
for (const [glyphId, inputClass] of firstInputClass.entries()) {
// istanbul ignore next - invalid font
if (inputClass === null) {
continue;
}
const classSet = table.chainClassSet[inputClass];
// If the class set is null there's nothing to do with this table.
if (!classSet) {
continue;
}
for (const [subIndex, subTable] of classSet.entries()) {
const result: LookupTree = {
individual: {},
range: []
};
let currentEntries: EntryMeta[] = getInputTree(
result,
subTable.lookupRecords,
lookups,
0,
glyphId
).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] }));
for (const [index, classNum] of subTable.input.entries()) {
currentEntries = processInputPosition(
listClassGlyphs(table.inputClassDef, classNum),
index + 1,
currentEntries,
subTable.lookupRecords,
lookups
);
}
for (const classNum of subTable.lookahead) {
currentEntries = processLookaheadPosition(
listClassGlyphs(table.lookaheadClassDef, classNum),
currentEntries
);
}
for (const classNum of subTable.backtrack) {
currentEntries = processBacktrackPosition(
listClassGlyphs(table.backtrackClassDef, classNum),
currentEntries
);
}
// When we get to the end, all of the entries we've accumulated
// should have a lookup defined
for (const { entry, substitutions } of currentEntries) {
entry.lookup = {
substitutions,
index: tableIndex,
subIndex,
length: subTable.input.length + 1,
contextRange: [
-1 * subTable.backtrack.length,
1 + subTable.input.length + subTable.lookahead.length
]
};
}
results.push(result);
}
}
}
return mergeTrees(results);
}