avar — Axis Variations Table (OpenType 1.8.3)
The axis variations table ('avar') is an optional table used in variable fonts that use OpenType Font Variations mechanisms. It can be used to modify aspects of how a design varies for different instances along a particular design-variation axis. Specifically, it allows modification of the coordinate normalization that is used when processing variation data for a particular variation instance.
For a general overview of OpenType Font Variations and of coordinates and normalization, see the chapter, OpenType Font Variations Overview.
The 'avar' table must be used in combination with a font variations ('fvar') table and other required or optional tables used in variable fonts. See Variation Data Tables and Miscellaneous Requirements in the Overview chapter for additional details.
Overview
The 'fvar' table defines a range of design variations supported by a font — the font’s variation space. It specifies certain axes of variation that are used, and a range of supported values for each axis using scales appropriate to the nature of each axis. When processing a font’s variation data to derive glyph outlines or other values for a particular variation instance, the coordinates selected by the user must be mapped from the scales that are used to define the axes in the 'fvar' table to a normalized scale that is the same for every axis.
A default normalization mapping is defined that maps the minimum, default and maximum values specified for each axis in the 'fvar' table to -1, 0, and 1, respectively, and that maps other values in between by linear interpolation between those values. The default mapping can be represented by the following pseudo-code.
if (userValue < axisDefaultValue)
{
defaultNormalizedValue = -(axisDefault - userValue) / (axisDefault - axisMin);
}
else if (userValue > axisDefaultValue)
{
defaultNormalizedValue = (userValue - axisDefault) / (axisMax - axisDefault);
}
else
{
defaultNormalizedValue = 0;
}
Take notice that, if the user selects an axis value that is outside the minimum/maximum range specified in the font, then the value used must be clamped to the minimum or maximum value.
The default normalization uses a predefined mapping of three positions along each axis to particular values, dividing the overall range of each axis into two segments. If an 'avar' table is present, then the output of the default normalization can be further modified by allowing mappings to be defined for additional positions along each scale, creating multiple segments, with other values within each segment interpolated. The following figure illustrates an example of such a modification.
The conceptual effect of these additional scale mappings is to make the variation along an axis less linear. Values change linearly within each segment, but additional segments make the way that values change across the entire axis range less linear overall. The effect might also be described as compressing some portions of the scale while making other portions less compressed.
The visual effects of additional axis-value mappings in the 'avar' table is seen in how glyph outlines change as the user-scale values for an axis change. The same amount of change in the user-scale value may correspond to a subtle change in glyph outlines in one portion of the axis range, but more dramatic changes in the glyph outlines in another portion of the axis range.
Note that it may be possible to achieve the same or similar effects by adding glyph variation data for additional regions of the variation space. That approach requires more work and more font data, however, and tedious design iteration may be needed to obtain the desired results. The 'avar' table may provide a simple and light-weight way to achieve a particular effect.
Note also that the variation data created by the font designer will also have a significant effect on whether user-scale values and glyph outlines change uniformly. When an 'avar' table is used, the 'avar' table and the glyph variation data (for TrueType or CFF 2) are both factors in the variation behavior that the user will see.
Table formats
The 'avar' table is comprised of a small header plus segment maps for each axis.
Axis variation table:
Type | Name | Description |
---|---|---|
uint16 | majorVersion | Major version number of the axis variations table — set to 1. |
uint16 | minorVersion | Minor version number of the axis variations table — set to 0. |
uint16 | <reserved> | Permanently reserved; set to zero. |
uint16 | axisCount | The number of variation axes for this font. This must be the same number as axisCount in the 'fvar' table. |
SegmentMaps | axisSegmentMaps[axisCount] | The segment maps array — one segment map for each axis, in the order of axes specified in the 'fvar' table. |
There must be one segment map for each axis defined in the 'fvar' table, and the segment maps for the different axes must be given in the order of axes specified in the 'fvar' table. The segment map for each axis is comprised of a list of axis-value mapping records.
SegmentMaps record:
Type | Name | Description |
---|---|---|
uint16 | positionMapCount | The number of correspondence pairs for this axis. |
AxisValueMap | axisValueMaps[positionMapCount] | The array of axis value map records for this axis. |
Each axis value map record provides a single axis-value mapping correspondence.
AxisValueMap record:
Type | Name | Description |
---|---|---|
F2DOT14 | fromCoordinate | A normalized coordinate value obtained using default normalization. |
F2DOT14 | toCoordinate | The modified, normalized coordinate value. |
Axis value maps can be provided for any axis, but are required only if the normalization mapping for an axis is being modified. If the segment map for a given axis has any value maps, then it must include at least three value maps: -1 to -1, 0 to 0, and 1 to 1. These value mappings are essential to the design of the variation mechanisms and are required even if no additional maps are specified for a given axis. If any of these is missing, then no modification to axis coordinate values will be made for that axis.
All of the axis value map records for a given axis must have different fromCoordinate values, and axis value map records must be arranged in increasing order of the fromCoordinate value. If the fromCoordinate value of a record is less than or equal to the fromCoordinate value of a previous record in the array, then the given record may be ignored.
Also, for any given record except the first, the toCoordinate value must be greater than or equal to the toCoordinate value of the preceding record. This requirement ensures that there are no retrograde behaviors as the user-scale value range is traversed. If a toCoordinate value of a record is less than that of the previous record, then the given record may be ignored.
Processing
Each pair of axis value map records for a given axis defines a segment within the range for that axis. Within a segment, intermediate values are interpolated linearly. For a given user-scale coordinate, the full normalization process, with 'avar' modifications applied, is as follows.
Compute the default normalized coordinate value, defaultNormalizedValue, as described above.
Using the SegmentMaps record for the given axis, scan the AxisValueMap records to find the first record that has a fromCoordinate value greater than or equal to defaultNormalizedValue. Designate this record as endSeg.
If endSeg.fromCoordinate equals defaultNormalizedValue, then the final, modified normalized value is endSeg.toCoordinate. Return this result and end.
The else case (endSeg.fromCoordinate is strictly greater than defaultNormalizedValue — endSeg cannot be the first map record, which is for -1): Designate the preceding record as startSeg.
The final, modified normalized value, finalNormalizedValue, is computed as follows:
Take notice that certain requirements regarding the level of precision used and how rounding is handled must be observed by implementations. See Coordinate scales and normalization in the OpenType Font Variations Overview chapter for a detailed specification.
Axis segments example
The following example illustrates how the 'avar' mappings work. This example is illustrated by the figure shown earlier in this chapter.
Suppose that the 'avar' table of a font has the following mappings for a particular axis:
Record index | fromCoordinate | toCoordinate |
---|---|---|
0 | -1.0 | -1.0 |
1 | -0.75 | -0.5 |
2 | 0 | 0 |
3 | 0.4 | 0.4 |
4 | 0.6 | 0.9 |
5 | 1.0 | 1.0 |
Suppose that the user selects an instance, and the default normalized value for this instance is -0.5. The relevant segment for this value is defined by record 1 and record 2. The final normalized value is computed as follows:
The following table shows how several normalized coordinate values would be modified by this 'avar' data:
Default normalized value | Final normalized value |
---|---|
-1.0 | -1.0 |
-0.75 | -0.5 |
-0.5 | -0.3333 |
-0.25 | -0.1667 |
0 | 0 |
0.25 | 0.25 |
0.5 | 0.65 |
0.75 | 0.9375 |
1.0 | 1.0 |