The GPOS table moves glyphs: kerning between pairs, placing accents and
vowel marks on letters, stacking marks, joining cursive scripts such as Urdu on a
sloping line. Unlike the old kern
table it can move glyphs both horizontally and vertically, attach them by anchor points,
and depend on context. Scripts, features and lookups use the
layout common tables. All numbers are
big-endian; values are in font design units.
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 GPOS. |
6 | Offset16 | featureListOffset | To the FeatureList. |
8 | Offset16 | lookupListOffset | To the LookupList. |
10 | Offset32 | featureVariationsOffset | Version 1.1 only: FeatureVariations, or NULL. |
Lookup types
| Type | Name | Use | Formats |
|---|---|---|---|
| 1 | Single adjustment | Move one glyph, e.g. superscripts | 1, 2 |
| 2 | Pair adjustment | Move two glyphs relative to each other: kerning | 1, 2 |
| 3 | Cursive attachment | Join each glyph’s exit point to the next glyph’s entry point | 1 |
| 4 | Mark-to-base attachment | Place a mark on a base glyph | 1 |
| 5 | Mark-to-ligature attachment | Place a mark on one component of a ligature | 1 |
| 6 | Mark-to-mark attachment | Place a mark on another mark | 1 |
| 7 | Contextual positioning | Positioning within a matched sequence | 1, 2, 3 |
| 8 | Chained contextual positioning | The same with backtrack and lookahead | 1, 2, 3 |
| 9 | Extension | 32-bit offset to another type’s subtable | 1 |
ValueRecord and ValueFormat
A ValueRecord holds only the fields its ValueFormat bits select, in this order, each 2 bytes; a ValueFormat of 0 means an empty record (no change).
| Bit | Mask | Name | Field | Meaning |
|---|---|---|---|---|
| 0 | 0x0001 | X_PLACEMENT | int16 xPlacement | Move the glyph horizontally. |
| 1 | 0x0002 | Y_PLACEMENT | int16 yPlacement | Move it vertically. |
| 2 | 0x0004 | X_ADVANCE | int16 xAdvance | Change its horizontal advance (horizontal text). |
| 3 | 0x0008 | Y_ADVANCE | int16 yAdvance | Change its vertical advance (vertical text). |
| 4 | 0x0010 | X_PLACEMENT_DEVICE | Offset16 xPlaDeviceOffset | Device or VariationIndex table for xPlacement, or NULL. |
| 5 | 0x0020 | Y_PLACEMENT_DEVICE | Offset16 yPlaDeviceOffset | The same for yPlacement. |
| 6 | 0x0040 | X_ADVANCE_DEVICE | Offset16 xAdvDeviceOffset | The same for xAdvance. |
| 7 | 0x0080 | Y_ADVANCE_DEVICE | Offset16 yAdvDeviceOffset | The same for yAdvance. |
| 8–15 | 0xFF00 | reserved | – | 0. |
Device offsets in a ValueRecord count from the start of the subtable that holds it (the SinglePos or PairPosFormat2 subtable, or the PairSet in PairPosFormat1).
Anchor tables
An anchor is a point on a glyph where another glyph attaches.
| Format | Fields after uint16 format | Meaning |
|---|---|---|
| 1 | int16 xCoordinate, int16 yCoordinate | A point in design units. |
| 2 | int16 xCoordinate, int16 yCoordinate, uint16 anchorPoint | Also the index of a contour point in the glyph, whose hinted position can be used instead. Not suited to variable fonts, where it would make layout depend on outline variations. |
| 3 | int16 xCoordinate, int16 yCoordinate, Offset16 xDeviceOffset, Offset16 yDeviceOffset | Plus Device or VariationIndex tables (from the start of the anchor, may be NULL). Variable fonts use this form. |
MarkArray
| Structure | Type | Name | Description |
|---|---|---|---|
| MarkArray | uint16 | markCount | Number of marks… |
| MarkRecord × count | markRecords | …by mark Coverage Index. | |
| MarkRecord | uint16 | markClass | The mark’s class, from 0. Marks are grouped into classes by where they attach (above, below…); every mark has an explicit class. |
Offset16 | markAnchorOffset | The mark’s own anchor, from the start of the MarkArray. |
markClassCount in the attachment subtables is the number of classes
used, class 0 included; base, ligature and mark records hold one anchor per class.
Type 1: single adjustment
| Format | Type | Name | Description |
|---|---|---|---|
| 1 | uint16 | format | 1. |
Offset16 | coverageOffset | Glyphs to move. | |
uint16 | valueFormat | Fields in the record. | |
| ValueRecord | valueRecord | The same adjustment for every covered glyph. | |
| 2 | uint16 | format | 2. |
Offset16 | coverageOffset | Glyphs to move. | |
uint16 | valueFormat | Fields in each record. | |
uint16 | valueCount | Same as the number of covered glyphs. | |
| ValueRecord × count | valueRecords | One per glyph, by Coverage Index. |
Type 2: pair adjustment
Format 1 lists individual pairs; format 2 gives values for classes of first and second glyphs. A lookup may hold subtables of both formats; they are tried in order, so a format 1 subtable placed first can hold exceptions to the class values.
| Structure | Type | Name | Description |
|---|---|---|---|
| PairPosFormat1 | uint16 | format | 1. |
Offset16 | coverageOffset | First glyphs. | |
uint16 | valueFormat1 | Fields for the first glyph; 0 leaves it unmoved. | |
uint16 | valueFormat2 | Fields for the second glyph. If 0, the second glyph is not consumed: processing continues from it. | |
uint16 | pairSetCount | One PairSet per first glyph… | |
Offset16 × count | pairSetOffsets | …by Coverage Index. | |
| PairSet | uint16 | pairValueCount | Number of pairs. |
| PairValueRecord × count | pairValueRecords | Sorted by second glyph. | |
| PairValueRecord | uint16 | secondGlyph | The second glyph. |
| ValueRecord | valueRecord1 | For the first glyph. | |
| ValueRecord | valueRecord2 | For the second. | |
| PairPosFormat2 | uint16 | format | 2. |
Offset16 | coverageOffset | First glyphs. | |
uint16 | valueFormat1 | As format 1. | |
uint16 | valueFormat2 | As format 1. | |
Offset16 | classDef1Offset | Classes of first glyphs. | |
Offset16 | classDef2Offset | Classes of second glyphs. | |
uint16 | class1Count | Number of first classes, class 0 included. | |
uint16 | class2Count | Number of second classes, class 0 included. | |
| Class1Record × class1Count | class1Records | By first class; each is class2Count Class2Records, by second class, each a valueRecord1 and a valueRecord2. |
Record sizes follow from the two ValueFormats, which a reader must use to step through the arrays.
Type 3: cursive attachment
| Structure | Type | Name | Description |
|---|---|---|---|
| CursivePosFormat1 | uint16 | format | 1. |
Offset16 | coverageOffset | Glyphs that connect. | |
uint16 | entryExitCount | One record per glyph… | |
| EntryExitRecord × count | entryExitRecords | …by Coverage Index. | |
| EntryExitRecord | Offset16 | entryAnchorOffset | Entry anchor, from the start of the subtable, or NULL. |
Offset16 | exitAnchorOffset | Exit anchor, or NULL. |
The exit anchor of one glyph is joined to the entry anchor of the next; if either is
NULL nothing happens. Along the line, the first glyph’s advance is changed so the
anchors meet. Across the line, the second glyph is normally moved to the first; with the
lookup’s RIGHT_TO_LEFT flag set, the first is moved to the second, so the
last glyph of a joined run stays on the baseline and the run rises towards its start, as
in Nastaliq.
Type 4: mark-to-base attachment
| Structure | Type | Name | Description |
|---|---|---|---|
| MarkBasePosFormat1 | uint16 | format | 1. |
Offset16 | markCoverageOffset | Mark glyphs. | |
Offset16 | baseCoverageOffset | Base glyphs. | |
uint16 | markClassCount | Number of mark classes. | |
Offset16 | markArrayOffset | The MarkArray. | |
Offset16 | baseArrayOffset | The BaseArray. | |
| BaseArray | uint16 | baseCount | Number of bases… |
| BaseRecord × count | baseRecords | …by base Coverage Index. | |
| BaseRecord | Offset16 × markClassCount | baseAnchorOffsets | One anchor per mark class, from the start of the BaseArray; NULL if that class does not attach here. |
The mark is moved so its anchor sits on the base’s anchor for the mark’s class. The base is the nearest preceding glyph that is not a mark (by GDEF class).
Type 5: mark-to-ligature attachment
| Structure | Type | Name | Description |
|---|---|---|---|
| MarkLigPosFormat1 | uint16 | format | 1. |
Offset16 | markCoverageOffset | Mark glyphs. | |
Offset16 | ligatureCoverageOffset | Ligature glyphs. | |
uint16 | markClassCount | Number of mark classes. | |
Offset16 | markArrayOffset | The MarkArray. | |
Offset16 | ligatureArrayOffset | The LigatureArray. | |
| LigatureArray | uint16 | ligatureCount | Number of ligatures… |
Offset16 × count | ligatureAttachOffsets | …by ligature Coverage Index, from the start of the LigatureArray. | |
| LigatureAttach | uint16 | componentCount | Number of components… |
| ComponentRecord × count | componentRecords | …in writing order. | |
| ComponentRecord | Offset16 × markClassCount | ligatureAnchorOffsets | One anchor per class for this component, from the start of the LigatureAttach, or NULL. |
Which component a mark belongs to is known from the ligature substitution that made the ligature.
Type 6: mark-to-mark attachment
| Structure | Type | Name | Description |
|---|---|---|---|
| MarkMarkPosFormat1 | uint16 | format | 1. |
Offset16 | mark1CoverageOffset | The marks being attached. | |
Offset16 | mark2CoverageOffset | The marks they attach to. | |
uint16 | markClassCount | Number of mark1 classes. | |
Offset16 | mark1ArrayOffset | MarkArray for mark1. | |
Offset16 | mark2ArrayOffset | The Mark2Array. | |
| Mark2Array | uint16 | mark2Count | Number of mark2 glyphs… |
| Mark2Record × count | mark2Records | …by mark2 Coverage Index. | |
| Mark2Record | Offset16 × markClassCount | mark2AnchorOffsets | One anchor per mark1 class, from the start of the Mark2Array, or NULL. |
Used for stacked marks, such as a Vietnamese tone mark on a circumflex. The mark2 glyph must come straight before mark1, apart from glyphs skipped by the lookup flags.
Types 7 and 8: contextual and chained contextual
As in GSUB, these use the shared
sequence context formats, and
their nested lookups are GPOS lookups. Positions in the
SequenceLookup records refer to the input sequence; since positioning does not change
the number of glyphs, they do not shift.
Type 9: extension
| Type | Name | Description |
|---|---|---|
uint16 | format | 1. |
uint16 | extensionLookupType | The real lookup type; any but 9, the same for all of the lookup’s subtables. |
Offset32 | extensionOffset | To the real subtable, from the start of this one. |
Variable fonts
Positions that change across the design space are given as values with
VariationIndex tables (in ValueRecords or format 3 anchors), whose deltas are stored in
the item variation store in GDEF.
Version 1.1 adds FeatureVariations.
Example
In Noto Sans Javanese Regular, GPOS starts with the same ScriptList as
its GSUB (one script, java) and the features
kern (lookups 0, 1, 2), mark and mkmk. Lookup 0:
0000C5B6 00 02 01 00 00 01 00 08 00 01 00 0C 00 00 00 05
0000C5C6 00 01 00 12 00 01 00 01 01 1F 00 03 01 91 00 62
0000C5D6 00 62 01 92 00 62 00 62 01 93 00 62 00 62
| Bytes | Meaning |
|---|---|
00 02 01 00 00 01 00 08 | Lookup type 2 (pair adjustment); flags 0x0100: mark attachment class filter 1, so marks of other attachment classes are skipped; one subtable at +8. |
00 01 00 0C | PairPosFormat1, Coverage at +12. |
00 00 00 05 | valueFormat1 0 (first glyph unchanged); valueFormat2 5 = X_PLACEMENT + X_ADVANCE. |
00 01 00 12 | One PairSet, at +18. |
00 01 00 01 01 1F | Coverage: one first glyph, 287. |
00 03 | PairSet: three pairs. |
01 91 00 62 00 62 | Second glyph 401: move it right by 98 and add 98 to its advance. |
01 92 …, 01 93 … | The same for glyphs 402 and 403. |