Parse OTF GSUB table to find ligatures
Core text is slow. With ligature fonts and ASCII text we only need it when a ligature is present. We could look at the OTF tables to find what ligatures substitutions are in use for the current font. From looking at FreeType, it's not too bad. The structure of the GSUB table is like this:
GSUB {
4 bytes of version which must equal 0x00010000
2 byte offset of scripts
2 byte offset of features
2 byte offset of lookups
data
}
Features table: {
2 byte count
`count` entries {
4 byte feature tag
2 byte offset to feature
}
}
Feature:
2 byte feature params
2 byte lookup count
`lookup count` entries {
2 byte index into lookup table
}
Lookup table: {
2 byte count
`count` entries {
2 byte offset to subtable
}
}
Lookup Subtable {
2 byte lookup type [1, typecount)
2 byte lookup flag
2 byte subtable count
`subtable count` entries {
2 byte entry offset
}
}
Ligature sub entry:
2 byte sub format (must be 1)
If type is 1: G 2 byte coverage offset
2 byte coverage count
`count` entries {
2 byte LigatureSet offset
}
}
LigatureSet {
2 byte count
`count` entries {
2 byte Ligature offset
}
}
Ligature {
2 byte ligature glyph [0, glyphcount]
2 byte component count > 0
`component count` entries {
2 byte glyph index
}
}
Coverage: {
2 byte coverage format
If coverage fromat is 1 {
2 byte glyph count
`glyph count` entries {
2 byte glyph id < glyph count
}
} else if coverage format is 2 {
2 byte range count
`range count` entries {
2 byte start
2 byte end < start
2 byte start cov index
}
}
}
Lookup types:
single-sub = 1
multi-sub = 2
alt-sub = 3
ligature sub = 4 * this is the one we care about *
context sub = 5
chain context sub = 6
extension sub = 7
reverse chain single sub = 8
The relevant freetype files to infer the structure are are otvgsub.c, otvcommn.h, and otvcommn.c
We can get the GSUB table with core graphics APIs like this:
CTFontRef ctfont = (CTFontRef) [NSFont fontWithName:@"FiraCode-Regular" size:12];
CGFontRef cgfont = CTFontCopyGraphicsFont(ctfont, nil);
CFArrayRef *tags = CGFontCopyTableTags(cgfont);
int numTags = CFArrayGetCount(tags);
for (int t = 0; t < numTags; t++) {
uint32_t tag = CFArrayGetValueAtIndex(tags, t);
if (tag == TRUETYPE_TAG('G', 'S', 'U', 'B')) {
CFDataRef table = CGFontCopyTableForTag(cgfont, tag);
char *p = CFDataGetBytePtr(table);
int len = CFDataGetLength(table);
NSLog(@"%p %d",p, len);
}
}