The GSUB table replaces glyphs with other glyphs: Arabic initial, medial
and final forms, ligatures such as fi, small capitals, vertical forms, and the
reordering-free substitutions complex scripts need. cmap maps each
character to one default glyph; GSUB then changes the glyph sequence. Its
scripts, features and lookups use the layout
common tables. All numbers are big-endian, and offsets in a subtable count from the
start of that subtable unless stated.
Header
| Offset | Type | Name | Description |
|---|---|---|---|
0 | uint16 | majorVersion | 1. |
2 | uint16 | minorVersion | 0 or 1. |
4 | Offset16 | scriptListOffset | To the ScriptList, from the start of GSUB. |
6 | Offset16 | featureListOffset | To the FeatureList. |
8 | Offset16 | lookupListOffset | To the LookupList. |
10 | Offset32 | featureVariationsOffset | Version 1.1 only: to the FeatureVariations table, or NULL. |
To use the table, software finds the script in the ScriptList, then the language system (or the script’s default), then the features it wants among that language’s features (swapping in FeatureVariations alternatives if they apply), gathers their lookups and runs them in LookupList order. Each lookup is applied across the whole glyph string before the next one starts, so earlier lookups can create or destroy matches for later ones. After a lookup matches at a glyph, processing moves past all the input glyphs it used (not past a chained lookahead).
The registered feature tags, with the lookup types each is usually built from, and the script and language system tags are listed on their own pages.
Lookup types
| Type | Name | Action | Formats |
|---|---|---|---|
| 1 | Single | One glyph by one glyph | 1, 2 |
| 2 | Multiple | One glyph by several | 1 |
| 3 | Alternate | One glyph by one of several the user can choose | 1 |
| 4 | Ligature | Several glyphs by one | 1 |
| 5 | Contextual | Substitutions within a matched sequence | 1, 2, 3 |
| 6 | Chained contextual | The same, with glyphs before and after | 1, 2, 3 |
| 7 | Extension | A 32-bit offset to a subtable of another type | 1 |
| 8 | Reverse chaining contextual single | Single substitution in context, from the end of the string backwards | 1 |
All subtables of a lookup are of the lookup’s type, but may mix formats.
Type 1: single substitution
| Format | Type | Name | Description |
|---|---|---|---|
| 1 | uint16 | format | 1. |
Offset16 | coverageOffset | The input glyphs. | |
int16 | deltaGlyphID | Added to each input glyph ID to get its replacement, modulo 65536 (a negative result has 65536 added). | |
| 2 | uint16 | format | 2. |
Offset16 | coverageOffset | The input glyphs. | |
uint16 | glyphCount | Same as the number of covered glyphs. | |
uint16 × count | substituteGlyphIDs | Replacements, by Coverage Index. |
Type 2: multiple substitution
| Structure | Type | Name | Description |
|---|---|---|---|
| MultipleSubstFormat1 | uint16 | format | 1. |
Offset16 | coverageOffset | The input glyphs. | |
uint16 | sequenceCount | One Sequence per covered glyph… | |
Offset16 × count | sequenceOffsets | …by Coverage Index. | |
| Sequence | uint16 | glyphCount | Number of output glyphs; must be more than 0 (deleting a glyph this way is not allowed). |
uint16 × count | substituteGlyphIDs | The output glyphs in logical (writing) order. |
Type 3: alternate substitution
| Structure | Type | Name | Description |
|---|---|---|---|
| AlternateSubstFormat1 | uint16 | format | 1. |
Offset16 | coverageOffset | Glyphs that have alternatives. | |
uint16 | alternateSetCount | One AlternateSet per covered glyph… | |
Offset16 × count | alternateSetOffsets | …by Coverage Index. | |
| AlternateSet | uint16 | glyphCount | Number of alternatives. |
uint16 × count | alternateGlyphIDs | The alternatives, in any order; the application or user picks one. |
Type 4: ligature substitution
| Structure | Type | Name | Description |
|---|---|---|---|
| LigatureSubstFormat1 | uint16 | format | 1. |
Offset16 | coverageOffset | First glyphs of the ligatures. | |
uint16 | ligatureSetCount | One LigatureSet per covered glyph… | |
Offset16 × count | ligatureSetOffsets | …by Coverage Index. | |
| LigatureSet | uint16 | ligatureCount | Ligatures starting with this glyph… |
Offset16 × count | ligatureOffsets | …from the start of the LigatureSet, most preferred first (so ffl before ff). | |
| Ligature | uint16 | ligatureGlyph | The ligature glyph. |
uint16 | componentCount | Number of components, the first included. | |
uint16 × (count − 1) | componentGlyphIDs | The second and later components, in logical order. |
The first glyph is replaced by the ligature and the other components are deleted; glyphs skipped because of the lookup flags (such as marks) are kept.
Types 5 and 6: contextual and chained contextual
These use the shared sequence
context formats 1, 2 and 3 and their chained
forms. In GSUB the nested lookups are GSUB lookups, so the
actions can only be substitutions. The whole input sequence is matched first; then the
actions run in order, each position counted in the sequence as already changed by the
previous actions. For example, with four input glyphs, a ligature at position 1
(merging positions 1 and 2) followed by a single substitution of the old fourth glyph
gives that substitution position 2, not 3.
Type 7: extension
| Type | Name | Description |
|---|---|---|
uint16 | format | 1. |
uint16 | extensionLookupType | The real lookup type; any but 7, and the same for all subtables of the lookup. |
Offset32 | extensionOffset | To the real subtable, from the start of this one. |
Used when subtables lie beyond the reach of 16-bit offsets. Software treats the
lookup as if its type were extensionLookupType and each extension subtable
were the real subtable.
Type 8: reverse chaining contextual single substitution
| Type | Name | Description |
|---|---|---|
uint16 | format | 1. |
Offset16 | coverageOffset | The one input glyph position. |
uint16 | backtrackGlyphCount | Glyphs before the input. |
Offset16 × count | backtrackCoverageOffsets | The first is for the glyph just before the input, then further back. |
uint16 | lookaheadGlyphCount | Glyphs after the input. |
Offset16 × count | lookaheadCoverageOffsets | The first is for the glyph just after the input. |
uint16 | glyphCount | Same as the number of covered input glyphs. |
uint16 × count | substituteGlyphIDs | Replacements, by Coverage Index. |
The lookup walks the string from its logical end to its start, so each glyph can depend on the already substituted glyph after it. It was designed for Arabic Nastaliq, where a glyph’s shape depends on the following glyph.
Variable fonts
Version 1.1 adds FeatureVariations, so that substitutions can differ across the
design space, for example simpler glyphs for heavy or narrow instances. The
rvrn feature is meant for such required variation alternates.
Example
Lookup 0 of Noto Sans Javanese Regular, used by its blwf (below-base
forms) feature, as found on the layout
common tables page:
00012146 00 04 00 00 00 01 00 08 00 01 01 8A 00 01 00 08
00012156 00 30 00 62 00 68 00 6E …
| Bytes | Meaning |
|---|---|
00 04 00 00 00 01 00 08 | Lookup: type 4, ligature substitution; no flags; one subtable at +8. |
00 01 01 8A 00 01 00 08 | LigatureSubstFormat1: Coverage at +394, one LigatureSet at +8. |
00 01 00 01 00 70 (at +394) | Coverage format 1: one glyph, 112. |
00 30 00 62 00 68 … | LigatureSet: 48 ligatures starting with glyph 112, the first at +98. |
00 FF 00 02 00 16 | Ligature: glyph 112 followed by glyph 22 becomes glyph 255. |
00 F6 00 02 00 0D | Next: glyph 112 + glyph 13 becomes glyph 246. |
The font’s CFF charset names the glyphs: 112 is pangkon (the Javanese
virama), 22 is aikara and 255 is aikara.pas, its
pasangan (below-base) form. Each of the 48 ligatures turns pangkon followed by a
letter into that letter’s below-base glyph.