OpenType layout is the part of a font that tells text software which glyphs to use
(ligatures, contextual forms, the shapes complex scripts need) and where to put them
(kerning, mark placement). The GSUB and GPOS tables do this,
helped by GDEF, BASE and JSTF, and they all build
on the structures on this page. They apply equally to fonts with TrueType and CFF
outlines. All numbers are big-endian, and offsets count from the start of the table
or record named in each case; an offset of 0 (NULL) means the subtable is absent.
How the pieces fit
Text is processed per script and language. A ScriptList
gives each script a set of language systems; each language system lists the
features (such as liga, kern) it
supports; each feature lists lookups; each lookup is a type
of operation and one or more subtables that do it. The application chooses the
features (some always on, some chosen by the user), then runs the lookups in
LookupList order over the glyph sequence.
GSUB and GPOS both start with offsets to their own
ScriptList, FeatureList and LookupList, and in version 1.1 a FeatureVariations
table.
ScriptList and Script
| Structure | Type | Name | Description |
|---|---|---|---|
| ScriptList | uint16 | scriptCount | Number of scripts. |
| ScriptRecord × count | scriptRecords | Sorted by tag. | |
| ScriptRecord | Tag | scriptTag | A registered script tag, such as latn, arab or DFLT. |
Offset16 | scriptOffset | To the Script table, from the start of the ScriptList. | |
| Script | Offset16 | defaultLangSysOffset | To the default LangSys, from the start of the Script table. May be NULL, except in the DFLT script. |
uint16 | langSysCount | Number of language-specific systems (0 if none). | |
| LangSysRecord × count | langSysRecords | Sorted by tag. | |
| LangSysRecord | Tag | langSysTag | A registered language system tag, such as TRK . |
Offset16 | langSysOffset | From the start of the Script table. | |
| LangSys | Offset16 | lookupOrderOffset | Reserved, NULL. |
uint16 | requiredFeatureIndex | A feature that is always applied for this language, or 0xFFFF for none. | |
uint16 | featureIndexCount | Number of features. | |
uint16 × count | featureIndices | Indexes into the FeatureList, in any order. |
The DFLT script is used when the text’s script has no Script table of
its own, or no particular script; it should hold only a default language system.
A language system that is not listed falls back to its script’s default.
FeatureList and Feature
| Structure | Type | Name | Description |
|---|---|---|---|
| FeatureList | uint16 | featureCount | Number of features. |
| FeatureRecord × count | featureRecords | Sorted by tag; records with the same tag may be in any order. | |
| FeatureRecord | Tag | featureTag | A registered feature tag, such as liga. |
Offset16 | featureOffset | From the start of the FeatureList. | |
| Feature | Offset16 | featureParamsOffset | Feature parameters, from the start of the Feature table, or NULL. Defined only for size, ss01–ss20 and cv01–cv99: see feature parameter tables. |
uint16 | lookupIndexCount | Number of lookups. | |
uint16 × count | lookupListIndices | Indexes into the LookupList, in any order. |
The same feature tag can appear several times, once for each script or language that needs different lookups for it.
LookupList and Lookup
| Structure | Type | Name | Description |
|---|---|---|---|
| LookupList | uint16 | lookupCount | Number of lookups. |
Offset16 × count | lookupOffsets | From the start of the LookupList. This order is the order lookups run in. | |
| Lookup | uint16 | lookupType | The kind of lookup: see GSUB and GPOS. |
uint16 | lookupFlag | The flags below. | |
uint16 | subTableCount | Number of subtables. | |
Offset16 × count | subtableOffsets | From the start of the Lookup table; all of the lookup’s type, tried in order. | |
uint16 | markFilteringSet | Only present if USE_MARK_FILTERING_SET is set: an index into GDEF’s mark glyph sets. |
Lookup flags
| Mask | Name | Meaning |
|---|---|---|
0x0001 | RIGHT_TO_LEFT | Only for GPOS cursive attachment (type 3): the last glyph of a matched sequence stays on the baseline. Ignored elsewhere; it does not set text direction. |
0x0002 | IGNORE_BASE_GLYPHS | Skip base glyphs (GDEF class 1). |
0x0004 | IGNORE_LIGATURES | Skip ligature glyphs (GDEF class 2). |
0x0008 | IGNORE_MARKS | Skip all marks (GDEF class 3). |
0x0010 | USE_MARK_FILTERING_SET | Skip marks not in the mark glyph set given by markFilteringSet. |
0x00E0 | reserved | 0. |
0xFF00 | MARK_ATTACHMENT_CLASS_FILTER | If not 0, skip marks whose GDEF mark attachment class is not this value. |
The three ignore flags need GDEF’s glyph class definitions. A mark filtering set
overrides the mark attachment class filter, and IGNORE_MARKS overrides
both. Skipped glyphs are passed over when matching, as if not there.
Coverage
A Coverage table lists the glyphs a subtable applies to and gives each a Coverage Index (0, 1, 2 … in glyph order), which subtables use to find that glyph’s data.
| Format | Type | Name | Description |
|---|---|---|---|
| 1 | uint16 | format | 1: a list of glyphs. |
uint16 | glyphCount | Number of glyphs. | |
uint16 × count | glyphArray | Glyph IDs in increasing order; the position is the Coverage Index. | |
| 2 | uint16 | format | 2: ranges. |
uint16 | rangeCount | Number of ranges. | |
| RangeRecord × count | rangeRecords | Each uint16 startGlyphID, uint16 endGlyphID, uint16 startCoverageIndex; sorted, not overlapping. |
In format 2 the Coverage Index of a glyph is
startCoverageIndex + glyphID − startGlyphID, and each range’s
startCoverageIndex follows on from the previous range.
ClassDef
A ClassDef table puts glyphs into numbered classes, from 1; every glyph it does not mention is in class 0.
| Format | Type | Name | Description |
|---|---|---|---|
| 1 | uint16 | format | 1: a run of glyphs. |
uint16 | startGlyphID | First glyph. | |
uint16 | glyphCount | Number of glyphs. | |
uint16 × count | classValues | The class of each glyph from startGlyphID on. | |
| 2 | uint16 | format | 2: ranges. |
uint16 | classRangeCount | Number of ranges. | |
| ClassRangeRecord × count | classRangeRecords | Each uint16 startGlyphID, uint16 endGlyphID, uint16 class; sorted, not overlapping. |
Contextual subtables
Contextual lookups (GSUB type 5, GPOS type 7) match a sequence of input glyphs and then apply other lookups at chosen positions in it. Chained contextual lookups (GSUB type 6, GPOS type 8) add glyphs that must come before (backtrack) and after (lookahead). Both have three formats: matching by glyph ID, by class or by coverage. The actions are SequenceLookup records:
| Type | Name | Description |
|---|---|---|
uint16 | sequenceIndex | Position in the input sequence, from 0. |
uint16 | lookupListIndex | The lookup to apply there. |
The records are applied in order, each to the result of the one before. Rules are tried in order and the first that matches is used, so longer rules go first. Only the input glyphs are acted on; afterwards processing moves on past the input sequence, not the lookahead.
Format 1: glyph sequences
| Structure | Type | Name | Description |
|---|---|---|---|
| SequenceContextFormat1 | uint16 | format | 1 |
Offset16 | coverageOffset | First glyphs of the rules. | |
uint16 | seqRuleSetCount | One rule set per covered glyph… | |
Offset16 × count | seqRuleSetOffsets | …in Coverage Index order; may be NULL. | |
| SequenceRuleSet | uint16 | seqRuleCount | Number of rules. |
Offset16 × count | seqRuleOffsets | From the start of the rule set, in order of preference. | |
| SequenceRule | uint16 | glyphCount | Input length, including the first glyph. |
uint16 | seqLookupCount | Number of actions. | |
uint16 × (glyphCount − 1) | inputSequence | The input glyphs after the first. | |
| SequenceLookup × count | seqLookupRecords | Actions. |
Format 2: class sequences
| Structure | Type | Name | Description |
|---|---|---|---|
| SequenceContextFormat2 | uint16 | format | 2 |
Offset16 | coverageOffset | First glyphs of the rules. | |
Offset16 | classDefOffset | Classes of the input glyphs. | |
uint16 | classSeqRuleSetCount | One rule set per class, from class 0… | |
Offset16 × count | classSeqRuleSetOffsets | …may be NULL. A ClassSequenceRuleSet has the same layout as a SequenceRuleSet. | |
| ClassSequenceRule | As SequenceRule, but inputSequence holds classes. | ||
Format 3: coverage sequences
| Type | Name | Description |
|---|---|---|
uint16 | format | 3 |
uint16 | glyphCount | Input length. |
uint16 | seqLookupCount | Number of actions. |
Offset16 × glyphCount | coverageOffsets | One Coverage table per input position. |
| SequenceLookup × count | seqLookupRecords | Actions. |
Chained formats 1, 2 and 3
The same three formats with backtrack and lookahead added. Backtrack glyphs are stored in reverse order: the first is the glyph just before the input.
| Structure | Fields, in order |
|---|---|
| ChainedSequenceContextFormat1 | uint16 format (1), Offset16 coverageOffset, uint16 chainedSeqRuleSetCount, Offset16 chainedSeqRuleSetOffsets[] |
| ChainedSequenceRuleSet | uint16 chainedSeqRuleCount, Offset16 chainedSeqRuleOffsets[] |
| ChainedSequenceRule | uint16 backtrackGlyphCount, uint16 backtrackSequence[], uint16 inputGlyphCount, uint16 inputSequence[inputGlyphCount − 1], uint16 lookaheadGlyphCount, uint16 lookaheadSequence[], uint16 seqLookupCount, SequenceLookup seqLookupRecords[] |
| ChainedSequenceContextFormat2 | uint16 format (2), Offset16 coverageOffset, Offset16 backtrackClassDefOffset, Offset16 inputClassDefOffset, Offset16 lookaheadClassDefOffset, uint16 chainedClassSeqRuleSetCount, Offset16 chainedClassSeqRuleSetOffsets[] (per input class) |
| ChainedClassSequenceRule | As ChainedSequenceRule, with classes from the three ClassDefs instead of glyphs. |
| ChainedSequenceContextFormat3 | uint16 format (3), uint16 backtrackGlyphCount, Offset16 backtrackCoverageOffsets[], uint16 inputGlyphCount, Offset16 inputCoverageOffsets[], uint16 lookaheadGlyphCount, Offset16 lookaheadCoverageOffsets[], uint16 seqLookupCount, SequenceLookup seqLookupRecords[] |
Offsets in rule sets count from the rule set; all others from the start of the subtable.
Device and VariationIndex tables
Positioning values can point to a Device table, which adjusts them by whole pixels at particular sizes, or, in a variable font, to a VariationIndex table in the same place. The third field tells them apart.
| Structure | Type | Name | Description |
|---|---|---|---|
| Device | uint16 | startSize | Smallest size, in pixels per em, adjusted. |
uint16 | endSize | Largest size adjusted. | |
uint16 | deltaFormat | 1, 2 or 3, below. | |
uint16 × varies | deltaValue | Packed adjustments, one per size from startSize to endSize. | |
| VariationIndex | uint16 | deltaSetOuterIndex | Which ItemVariationData in GDEF’s item variation store. |
uint16 | deltaSetInnerIndex | Which delta set (row) in it. | |
uint16 | deltaFormat | 0x8000. |
| deltaFormat | Name | Meaning |
|---|---|---|
0x0001 | LOCAL_2_BIT_DELTAS | Signed 2-bit values, −2 to 1, eight per uint16. |
0x0002 | LOCAL_4_BIT_DELTAS | Signed 4-bit values, −8 to 7, four per uint16. |
0x0003 | LOCAL_8_BIT_DELTAS | Signed 8-bit values, −128 to 127, two per uint16. |
0x8000 | VARIATION_INDEX | The table is a VariationIndex table. |
0x7FFC | reserved | 0. |
Values are packed from the most significant bits down, the first for
startSize; unused low bits of the last word are 0. So 4-bit deltas 1, 2,
3 and −1 pack into 0x123F.
FeatureVariations
In variable fonts, version 1.1 of GSUB and GPOS can swap
in different Feature tables for parts of the design space, for example a different
dollar sign at heavy weights. Software that does not support it uses the normal
features.
| Structure | Type | Name | Description |
|---|---|---|---|
| FeatureVariations | uint16 | majorVersion | 1. |
uint16 | minorVersion | 0. | |
uint32 | featureVariationRecordCount | Number of records. | |
| record × count | featureVariationRecords | In order of precedence. | |
| FeatureVariationRecord | Offset32 | conditionSetOffset | From the start of FeatureVariations; 0 means always true. |
Offset32 | featureTableSubstitutionOffset | From the start of FeatureVariations; 0 means no substitutions. | |
| ConditionSet | uint16 | conditionCount | Number of conditions; 0 always matches. |
Offset32 × count | conditionOffsets | From the start of the ConditionSet. All must match. | |
| Condition, format 1 | uint16 | format | 1, an axis range. The only format defined; a set with an unknown format fails. |
uint16 | axisIndex | Axis, in fvar order. If invalid, the record is ignored. | |
F2DOT14 | filterRangeMinValue | Lowest normalised axis value, −1 to 1. | |
F2DOT14 | filterRangeMaxValue | Highest; matches when min ≤ value ≤ max. | |
| FeatureTableSubstitution | uint16 | majorVersion | 1. |
uint16 | minorVersion | 0. | |
uint16 | substitutionCount | Number of records. | |
| record × count | substitutions | Each uint16 featureIndex (strictly increasing) and Offset32 alternateFeatureOffset (from the start of this table) to a replacement Feature table. |
The first record whose conditions all match is used and the rest are ignored. Replacement Feature tables keep the original’s tag and are stored at the end of the FeatureVariations table.
Example
The start of the GSUB table of Noto Sans Javanese Regular
(NotoSansJavanese-Regular.otf, macOS):
0001209C 00 01 00 00 00 0A 00 24 00 62 00 01 6A 61 76 61 .......$.b..java
000120AC 00 08 00 04 00 00 00 00 FF FF 00 04 00 00 00 01 ................
000120BC 00 02 00 03 00 04 61 62 76 73 00 1A 62 6C 77 66 ......abvs..blwf
000120CC 00 20 62 6C 77 73 00 26 70 73 74 73 00 2C 00 00 . blws.&psts.,..
000120DC 00 01 00 1F 00 00 00 01 00 00 00 00 00 01 00 1C ................
000120EC 00 00 00 07 00 01 00 02 00 03 00 12 00 13 00 20 ...............
000120FC 00 22 00 23 00 48 01 E0 02 DE 04 B8 05 4C 05 5A .".#.H.......L.Z
| Offset | Bytes | Meaning |
|---|---|---|
0 | 00 01 00 00 00 0A 00 24 00 62 | GSUB 1.0; ScriptList at 10, FeatureList at 36, LookupList at 98. |
10 | 00 01 6A 61 76 61 00 08 | One script, java (Javanese), its Script table 8 bytes on, at 18. |
18 | 00 04 00 00 | Default LangSys 4 bytes on; no language-specific systems. |
22 | 00 00 FF FF 00 04 00 00 00 01 00 02 00 03 | LangSys: no required feature; features 0, 1, 2 and 3. |
36 | 00 04 + 4 records | FeatureList: abvs at +26, blwf at +32, blws at +38, psts at +44, sorted by tag. |
62 | 00 00 00 01 00 1F | abvs: no parameters, lookup 31. |
68 | 00 00 00 01 00 00 | blwf: lookup 0. |
74 | 00 00 00 01 00 1C | blws: lookup 28. |
80 | 00 00 00 07 … | psts: lookups 1, 2, 3, 18, 19, 32, 34. |
98 | 00 23 00 48 01 E0 … | LookupList: 35 lookups, the first at +72. |
Lookup 0, at 98 + 72, starts 00 04 00 00 00 01 00 08: type 4 (ligature
substitution, decoded on the GSUB page),
no flags, one subtable 8 bytes on. Its Coverage table is 00 01 00 01 00 70:
format 1, one glyph, glyph 112.