OpenType GX
an exploratory proposal

Behdad Esfahbod
February 2016

Table of Contents

Architecture

Practical considerations

Overlap removal

Cubic-to-quadratic Bézier conversion

CFF glyph shape point numbers

Extrapolation

Proposal

Font variation metadata

Glyph shape variation

Font metrics variation

Positioning variation

Conditional substitutions and positioning

Glyph metrics variation

Hinting data variation

Implementation status

File size savings

Deployment

CSS

Further discussion

Implementation in tiers

Anisotropic interpolation

Appendix A—Background

Multiple Master

TrueType GX font variations

Acknowledgements

References

Specifications

Source code repositories

Revision history

Introduction

Let's revive and revise TrueType GX font variations, and integrate it with OpenType. In common terms this enables run-time interpolation between font masters, also known as dynamic interpolation. There are two main benefits to doing this: 1. font file size savings, specially on mobile devices, and 2. making the entire gamut of weight, width, etc available to users, not just a series of interpolated instances.

Technology for run-time interpolation was designed in the 90s independently by Adobe and Apple. Adobe's technology enabled run-time interpolation of Type 1 fonts and was called Multiple Master (MM from here on). Apple's technology applied to TrueType and was called TrueType GX font variations (GXVar from here on). Both technologies live on to this day in one or two implementations, but never became popular. In this proposal, we will propose integrating the core variation tables from GXVar into OpenType, and extend existing OpenType tables (GSUB, GPOS, etc) to integrate with glyph variations.

Both MM and GXVar implement variations by just repositioning points (on-curve and control) of an outline, while keeping the outline structure (curve types & number of points) intact. This is nice, because these can be generated from a series of interpolation-compatible font masters that satisfy such requirements. For background on how GXVar and MM work, see Appendix A—Background.

Architecture

We are going to follow the same architectural model as GXVar. To that extent, we will integrate some tables from GXVar directly for some aspects, extend existing OpenType tables for other aspects, and diverge from GXVar tables for some more aspects yet. In the future, we might redesign some of the tables that are imported verbatim from GXVar to address their shortcomings.

In particular, in the Proposal section we will discuss how to implement variations to the following aspects of a font:

An important design decision of the proposal is that the variation-enabled OpenType font extension will be non-invasive, the same way that the GXVar is. That is, to a client that does not know about variations, the font will perform as a valid (even if bulky) OpenType font representing the base face of the variation font. This significantly simplifies the backward-compatibility of this extension, specially in the face of web fonts.

Another important design decision of the proposal is that it should be possible to create a variation-enabled OpenType font from input masters and configurations that font designers already use in their workflow. An embodiment of this decision is to be able to consume MutatorMath input data (designspace as well as master UFOs) for example, while not limiting other workflows (Glyphs app, FontLab, etc).

Practical considerations

The following practical considerations help understand the proposal better:

Overlap removal

Normally, overlaps in glyph contours are removed during font production. This, however, disturbs interpolation-compatibility of the masters as well as resulting in inferior interpolation results in cases that it does not break interpolation-compatibility. The good news is that there is not a technical requirement to remove overlaps. TrueType outlines are filled with a nonzero-winding rule which works just fine with overlapping (and self-intersecting) contours. CFF fonts have an unspecified fill rule. Applying the same nonzero-winding rule to them makes them work with overlapping contours as well, so specifying that will be a requirement of this proposal.

Cubic-to-quadratic Bézier conversion

Most designers design font masters in an interpolation-compatible way using cubic Bézier curves. TrueType fonts as well as GXVar, however, use quadratic Bézier curves. While adding cubic curves to TrueType ‘glyf’ table is a worthwhile exercise by itself, it is not a requirement of this proposal. Instead, we have implemented (cu2qu) an Open Source cubic-to-quadratic curve converter in Python that operates on multiple interpolation-compatible cubic outlines and produces interpolation-compatible quadratic outlines. This can be used in implementations that might initially only support variations in TrueType-flavored fonts but not CFF-flavored ones.

CFF glyph shape point numbers

While the CFF specification does not explicitly define indices for points in a glyph outline, there is a straightforward way to define those: load glyph into a series of moveto, lineto, and curveto operations, ignoring any flexing, then assign point indices to the point operands of those operations starting from zero. We will use this numbering to extend GXVar glyph shape variations to CFF-flavored OpenType fonts.

Extrapolation

The GXVar model does not support extrapolation across axes beyond the outermost masters provided. If such extrapolation is desirable, the builder process can perform the extrapolation at font build time and include those as extra input masters.

Proposal

In this section we discuss concrete proposals to add variations to different aspects of OpenType fonts:

Font variation metadata

For specification of the variation axes and pre-defined instances, we propose copying the ‘fvar’ and ‘avar’ tables from GXVar into OpenType.

The ‘fvar’ table describes variation axes and their default values as well as support range (eg. “weight” axis defaults to 1.0 and is supported from 0.5 to 2.0), as well as a set of named instances, which are pre-defined interpolated font instances in the interpolation design space. The named instances refer to ‘name’ table entries for the face name (“Bold Semi-Condensed”, etc).

The ‘avar’ table enables for a non-linear mapping from user axis coordinates to the internal normalized axis values. This enables interpolation curves like the Luc(as) de Groot’s interpolation curves, or the Impallari formula, transparently from the user. The mapping is decided by the font build process and is specified as a piece-wise linear function (like everything else in this proposal). Under this model, any continuous curve can be specified to any desired fidelity by using a more or less granular piecewise-linear approximation.

In the GXVar model, each variation axis is specified by a four-letter tag (much like the sfnt and GSUB/GPOS tags). It also specifies the default value for each interpolation axis, which is the value associated with the base font.

The GXVar model also includes the following four predefined axes:

Tag

Name

Default

Description

'wght'

Weight

1.0

Used for making a font range from light to black.

'wdth'

Width

1.0

Used for fonts ranging from narrow to wide.

'slnt'

Slant

0.0

Specifies the angle (in degrees) of italic slant.

'opsz'

Optical Size

12.0

Specifies the optical point size.

(Table courtesy of Apple)

The meaning for the slant and optical size axis values is well-defined. For weight and width, we propose defining the value in the following way:

We suggest making the following adjustments to those four pre-defined axes:

Note that because the ‘avar’ mechanism for non-linear mapping of user axis values to normalized axis values exists, these proposed redefinitions of the axes do not expose any practical limitations on what can be achieved by the format in terms of the functional behavior.

Glyph shape variation

For glyph shape variation, ie. the main font variation data, we propose copying the ‘gvar’ table from GXVar. Note that the Apple spec does not document some important aspects of how ‘gvar’ is implemented in Apple products and fonts, namely the use of IUP-like interpolation for untouched points. IUP here refers to the “Interpolate Untouched Points” TrueType hinting instruction. This needs to be clarified before integrating the ‘gvar’ table into OpenType.

The Apple model only applies to TrueType-flavored outlines (ie. the ‘glyf’ table). OpenType also includes CFF-flavored outlines (‘CFF ’ table) and it is desirable that the OpenType variation font model can support both. Since the ‘gvar’ table is non-invasive, ie. it encodes deltas for glyph outline coordinate data from the base font, it can equally apply to CFF outlines, as long as the points in a CFF glyph outline are properly numbered, for example, as described in CFF glyph shape point numbers. In such a world, glyph loading can be summarized as a succession of the following steps:

Font metrics variation

Font variations need a way to specify variations in font-wide metrics, like ascent, descent, line-gap, caret slant, underline / strikethrough position, superscript / subscript position and size, etc.

GXVar addresses this need by using the ‘fmtx’ table. The current version of the ‘fmtx’ table specifies these variations by way of appointing certain points within a (dummy) glyph index in the font. While this is a smart solution, it requires adding a new glyph to the variation font, which is rather invasive to the font as a whole, as other tables need to be adjusted to include data for this new glyph. While this is achievable, in our opinion, a solution that directly encodes the variations of various font metrics without requiring a new glyph is preferable. Moreover, the current version of the table leaves out variations of metrics like underline / strikethrough position, superscript / subscript position and size, and possibly other metrics.

Instead, a new version of the ‘fmtx’ table can be defined that includes variation data for the following tables directly: ‘hhea’, ‘vhea’, and ‘OS/2’. The old version will still be supported. Note that of all the values in the ‘head’ table, the xMin, yMin, xMax, yMax values can be interpolated but are hardly used. The ‘head’ table unitsPerEm value on the other hand, while can be interpolated, is too fundamental to the definition of the font, and as such we propose no variation to be supported: all masters must have the same unitsPerEm. This requirement might be reconsidered.

Positioning variation

For complex positioning variation, we consider two technologies:

To extend GPOS / GDEF for variations is straightforward: everywhere where an X / Y value is specified, we need to also specify variations to that value. Initially we were considering extending the ValueFormat, AnchorFormat, and CaretValue tables. However, in discussion with others, the idea of extending the DeviceTable was brought up. We have since refocused on this idea for the following reasons:

As such, we propose an extension to the OpenType Device table. Whereas currently the Device table is defined as:

Device table

Type

Name

Description

uint16

StartSize

Smallest size to correct-in ppem

uint16

EndSize

Largest size to correct-in ppem

uint16

DeltaFormat

Format of DeltaValue array data: 1, 2, or 3

uint16

DeltaValue[ ]

Array of compressed data

(Table courtesy of Microsoft)

We note that only DeltaFormat values of 1, 2, and 3 are currently defined. We propose a structure, VariationDevice, that can be differentiated based on its “DeltaFormat”, to encode GPOS/GDEF value variations. This new struct, as well as structs it depends on are provided as C++ structs below, with code to compute the delta, given a list of instance coordinates numbered based on the axis indices from ‘fvar’ table:

struct VariationAxis

{

USHORT axisIndex;

F2DOT14 startCoord;

F2DOT14 peakCoord;

F2DOT14 endCoord;

inline float evaluate (int *coords, unsigned coord_len) const

{

int coord = axisIndex < coord_len ? coords[axisIndex] : 0;

/* TODO: Make sure this matches desired ‘gvar’ behavior. */

if (coord == 0) return 0;

assert(!(startCoord < 0 && endCoord > 0);

if (coord < startCoord || coord > endCoord) return 0.;

if (coord == peakCoord) return 1.;

/* Interpolate */

if (coord < peakCoord)

return float (coord - startCoord) / (peakCoord - startCoord);

else

return float (endCoord - coord) / (endCoord - peakCoord);

}

};

struct VariationTuple

{

USHORT numAxes;

OffsetTo<VariationAxis> axis[numAxes]; /* Sorted by axisIndex. */

inline float evaluate (int *coords, unsigned coord_len) const

{

float v = 1.;

for (unsigned int i = 0; i < numAxes; i++)

v *= (this+axes[i]).evaluate(coords, coord_len);

return v;

}

};

struct VariationMap

{

USHORT numTuples;

OffsetTo<VariationTuple> tuples[numTuples];

inline float evaluate (unsigned i, int *coords, unsigned coord_len) const

{

return (this+tuples[i]).evaluate(coords, coord_len);

}

};

struct VariationDevice

{

OffsetTo<VariationMap> variationMap;

USHORT deltaCount;

USHORT deltaFormat; /* Format=0x10 */

SHORT deltaValue[deltaCount];

inline float get_delta (int *coords, unsigned coord_count) const

{

float v = 0;

const VariationMap &map = this+variationMap;

unsigned int count = MIN((unsigned int) deltaCount, map.numTuples);

for (unsigned int i = 0; i < count; i++)

v += deltaValue[i] * map.evaluate(i, coords, coord_count);

return v;

}

};

I have to be first to agree that this sounds rather heavy. But I believe it’s better than it looks like at first glance. The following principles guided this design:

Let us now take a look at how many bytes we have spend on average, per value that needs variation:

In the above scheme, a VariationTuple represents a master, and a VariationMap represents a set of masters that have deltas (aka variations) for the value at hand. As such, the number of distinct VariationMap structs in a GPOS table would be limited to (2^n - 1) where n is the number of masters. In reality, I doubt that most fonts will have more than a few different VariationMap structs. Since we refer to VariationMap structs by offset, multiple values that need variations can refer to the same VariationMap object, offset-limit permitting. As such, we can discount the amortized overhead of the VariationMap objects as negligible.

To encode variations for each value, two bytes are used by the offset to Device table. Six bytes are consumed by the VariationDevice table’s fixed members, and from there on, two bytes per each master used for this value’s variations (other than the base master which does not need any variations encoded). That is, for a two-master font, 10 bytes are used to encode variations for each value. That’s definitely a lot. But this becomes much better as we add more masters. For a six-master font, the cost to encode variations of each value is 18 bytes, which is a mere three bytes per master per value. In general, my gut feeling is that the combined GPOS table will be smaller than the sum of the sizes of the GPOS tables for all masters. The only exception is the class-based kerning, because the matrix there is extremely compact and low-overhead. In fonts that class-based kerning subtables dominate the GPOS bytes, up to 50% growth compared to sum of master GPOS tables can be expected, but that is the worst case.

Conditional substitutions and positioning

Apple has their own technology for glyph substitutions, namely, the ‘morx’ table. Variations support for that table remains as an exercise for Apple. In the meanwhile, we consider such support in the GSUB table, which is the OpenType Layout answer to the same need.

While GPOS/GDEF value variations support bulk of the need for complex layout in variation fonts, it is also desirable to be able to include conditional substitutions. For example, some ligatures might be better turned off in parts of the design space (eg. bolder weights). We propose implementing this as a generic addition to GSUB and GPOS tables in the following way:

We are going to enable substituting individual lookups with alternate versions (or nothing) based on variation coordinates. To that end, we first need a new LookupFlag bit:

LookupFlag bit enumeration

Type

Name

Description

0x0001

RightToLeft

...

0x0002

IgnoreBaseGlyphs

If set, skips over base glyphs

0x0004

IgnoreLigatures

If set, skips over ligatures

0x0008

IgnoreMarks

If set, skips over all combining marks

0x0010

UseMarkFilteringSet

If set, indicates that the lookup table structure is followed by a MarkFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.

0x0020

UseVariationAlternates

If set, indicates that the lookup table structure is followed by LongOffset to a VariationAlternates field. The layout engine substitutes the alternate lookup if font variations are in use.

0x00C0

Reserved

For future use (Set to zero)

0xFF00

MarkAttachmentType

If not zero, skips over all marks of attachment type different from specified.

The new VariationAlternates struct and its supporting structures:

struct VariationGridAxis

{

USHORT axisIndex;

USHORT numAxisCoords;

F2DOT14 axisCoords[numAxisCoords]; /* Sorted by coordinate */

inline int locate (int *coords, unsigned int coord_len) const

{

int coord = axisIndex < coord_len ? coords[axisIndex] : 0;

unsigned int i = 0;

while (i + 1 < numAxisCoords && coord < axisCoords[i + 1])

i++;

return axisCoords[i] <= coord ? i : -1;

}

inline unsigned int get_len (void) const { return axisCoords.len - 1; }

};

struct VariationGrid

{

USHORT numAxes;

OffsetTo<VariationGridAxis> axis[numAxes]; /* Sorted by axisIndex. */

inline int locate (int *coords, unsigned int coord_len) const

{

int location = 0;

for (unsigned int i = 0; i < numAxes; i++)

{

const VariationGridAxis &axis = (this+axes[i]);

int axisLoc = axis.locate (coords, coord_len);

if (axisLoc == -1)

return -1;

location = location * (axis.get_len () - 1) + axisLoc; /* XXX Check for overflow?. */

}

return location;

}

};

struct VariationAlternates

{

OffsetTo<VariationGrid> grid;

USHORT numLookupIndex;

USHORT lookupIndex[numLookupIndex];

inline unsigned int get_lookup_index (int *coords, unsigned int coord_len) const

{

int location = (this+grid).locate (coords, coord_len);

If (location == -1)

return 0xFFFE; /* Special value 2. */

return location < numLookupIndex ? lookupIndex[location] : 0xFFFF; /* Special value 1. */

}

};

A VariationGrid structure defines a hyper-grid on a select number of design-space axes, that need to use an alternate lookup instead of the current lookup. For example, imagine we want to substitute lookup 10 with lookup 11 if ‘wght’ > 0.5. Then our VariationGridAxis for ‘wght’ axis needs to encode two numbers in axisCoords: 0.5, and 1.0 (encoded in F2DOT14). What these means sit that we want to override what lookup is applied if 0.5 <= ‘wght’ <= 1.0 (need to figure out the equality conditions). Whereas if we wanted to use one alternate for 0.5 <= ‘wght’ < 0.75 and another if 0.75 <= ‘wght’ <= 1.0 then we’d encode the three numbers 0.5, 0.75, and 1.0 in VariationGridAxis’s axisCoord for this axis.

When the hyper-grid is specified, each location in the grid is addressed using an index, by enumerating cells starting at zero and with axes ranked by the order of their appearance. Eg, if there is only one axis with three coordinate values specified, the hyper-grid is a simple vector of two cells, which are addressed 0 and 1. If there are two such axes, then the hypergrid has 4 cells, numbered 0 to 3 in this order: (0,0), (0,1), (1,0), (1,1), as one expects. Then, the alternate lookup indices for each cell are encoded in the lookupIndex of the VariationAlternates struct.

For instance coordinates outside the grid, a special value of 0xFFFE is returned by VariationAlternates::get_lookup_index(). This value has a special meaning that means “apply current lookup”. It can be used in the grid table itself as well. A special value of 0xFFFF (Not_Found), on the other hand, means “don’t apply any lookup”. If a cell contains the number of the current lookup itself, it must be interpreted like 0xFFFE: that is, apply the current lookup immediately. Any other value must be a valid lookup index and will cause the lookup at that index in the table’s LookupList to be processed instead of current lookup. Note that the new lookup itself might have VariationAlternates which would need to be processed.

Old proposal follows:

Extend each and every (possibly except for the Extension types) GSUB/GPOS lookup type, to add a new subtable type for variation-conditional processing.

TODO After discussing with Martin on Feb 9, we like to change the proposal in this section.

Note that while the more intuitive way to adapt GSUB/GPOS for variation-conditional processing might be to include a new lookup-type that encodes an enabling condition and refers to a regular lookup subtable(-list) for processing, or to extend the Extension lookup type with a new subtable format, we propose a new subtable type for each lookup type instead, for good reason: with subtables of a lookup, it is possible to reason / prove certain properties of mixing subtables. For example, two SingleSubstitute subtables A and B appearing in that order are equivalent to the union of A and B’s mappings, with A’s mappings taking precedence over B’s for glyphs that appear in both mappings. Similar reasoning can be done for other lookup types, whereas in general, no such reasoning is possible for separate lookups. (TODO Flesh this out / make sure this is comprehensible.)

We propose expanding each lookup type with the following new sub-format:

FormatF subtable

Type

Name

Description

USHORT

Format

Format identifier. Set to 0x0F.

Offset

Condition

Offset to Condition table.

ULONG

SubtableOffset

Offset to the inferior subtable, relative to the start of the FormatF subtable.

where Condition points to a data-structure for specifying parts of the design space, TODO to be specified here.

Glyph metrics variation

The default glyph metrics (horizontal advance and left-side bearing as well as vertical advance and top-side bearing) variations are already encoded in the ‘gvar’ table by way of four phantom points, the same way that such variations are handled in TrueType hinting. This, however, makes loading the variation in advance widths contingent on loading the glyph variation data from ‘gvar’. While this is not a big deal in general, it might prove to slow down OpenType shaping process significantly. As such, we should explore encoding variation data for horizontal advance (‘hmtx’), vertical advance (‘vmtx’), and vertical origin (‘VORG’ for CFF fonts) in a more optimized variation table(s).

Hinting data variation

When considering hinting, we have a few distinct situations to consider: TrueType or CFF, hinting, static auto-hinting, or dynamic auto-hinting:

Note that in theory, nothing stops a client to extend itself to apply TrueType-style hinting to CFF data or CFF-style hinting to TrueType data. But any such extension is beyond the scope of this proposal.

Implementation status

Support for ‘fvar’, ‘avar’, and ‘gvar’ for TrueType-flavored fonts has existed in Apple’s OS X forever (what about iOS?). It has also been implemented in FreeType, though, the implementation did not support IUP-like interpolation before, but that was fixed in May 2015 after a long group detective work. Note that you need a very recent version of FreeType for all these to work correctly (late-April 2016 is good.)

Support for the ‘fvar’, ‘avar’, and ‘gvar’ tables was added to the FontTools library in early 2015. This is a sample script to create a GXVar font using FontTools, though a more complicated master configuration needs more work (see below).

Support for GSUB/GPOS/GDEF extensions is being worked on in FontTools and HarfBuzz in parallel. This should allow us to test our model as well as build tools to combine GSUB/GPOS of multiple source masters into a variation-extended set of tables.

TODO

Supported to some extent: skia, chrome, fontconfig.

To do:

CFF-flavored freetype

To build:

Instantiator, aka mutator

fontmake + mutatormath

GSUB/GPOS/GDEF integrator

coming up with avar mapping from master weights, etc, based on font statistics

File size savings

TODO

Deployment

CSS

Possibly reuse font-feature-settings. Consider fluid scaling with viewport units, CSS calc() and element/container queries that can take advantage of a variable font.

Further discussion

Implementation in tiers

This proposal can be implemented in tiers. The following is one such model, presented here just for the sake of discussion:

Support for hinting variations is left out of the tiering above. Other aspects (font metrics, accelerated glyph metrics, etc) are also left out of the tiering above.

Anisotropic interpolation

TODO:

Appendix A—Background

Interpolation has become common in recent years, thanks to tools like Superpolator / MutatorMath, as well as modern font editors like Glyphs and RoboFont. At its core, interpolation is simple and trivial: a designer provides multiple manually-designed masters that are "interpolation-compatible", and several instances can then be generated by software using simple linear interpolation.

All systems allow for arbitrary axes of interpolation. Predefined axes include weight, width, optical size, but to the math underneath, these mean nothing. They are just independent axes.

Multiple Master

Multiple Master is a Type 1 font technology. It was included in early drafts of OpenType (1.25) but removed from the final version. In its documented version, MM only supports cube combinations, that is, there is a master at every corner, and only corners, of the design space. This limitation was lifted in later version of Multiple Master, but never documented as far as we can see.

The way variations were implemented in the version of Type 2 charstrings that was aimed for the OpenType-with-MM version was through a blend operator. Quoting from “The Type 2 Charstring Format”, Adobe Developer Support, Technical Note #5177, 5 May 1998:

For example, for a multiple master font with three master designs, and the individual masters need the equivalent of

100 30 rmoveto

120 50 rmoveto

95 30 rmoveto

this would be encoded as:

100 30 20 –5 20 0 2 blend rmoveto

Those numbers are basically:

100 30 (120-100) (95-100) (50-30) (30-3) 2 blend rmoveto

Basically the blend operator here is told to blend two numbers. The run-time environment should already know how many masters are involved and what the mixing factor for each is. The blend then pops the six operators, blends them, and pushes the resulting two values on the stack again. This means that the MM approach to variations is invasive. That is, to a font consumer that does not know about MM, the font is unusable.

The MM draft in OpenType included support for variations of Value values in GPOS. We considered reviving this for our proposal but decided to not do so.

TrueType GX font variations

TrueType GX font variations (GXVar from here on) takes a rather different approach to the problem: The base font is encoded as is (using TrueType ‘glyf’ table, etc), and the variations are encoded in new tables (‘fvar’, ‘avar’, ‘gvar’, etc). This is non-invasive in that to a consumer system that does not know about glyph variations, the base font still works.

Another nice property of the GXVar model is that there is no notion of masters whatsoever. The masters are distilled into variation functions that are encoded in the font, but the variation functions for different glyphs can be different. What this means in real life is that not all glyphs need to be present in all masters. Moreover, arbitrary numbers and configurations of masters are supported. What the GXVar model does imply however, is how masters mix. GXVar supports, essentially, a piecewise linear mixture of masters. We take a deeper look into that.

At its core, the interpolated fonts problem comes that to interpolating single numerical values. These values can be and X or Y coordinate of a point in a glyph outline, or can be a kerning value, or font height, etc. But they are all the same problem: the value is different in different masters, and we need to encode how the value varies over the design space. Since GXVar encodes the base font completely, the variations only need to encode the differences of the value compared to the base, also known as deltas.

Lets look at the simplest example. We have a Regular and a Bold master, there is the origin, which represents the Regular master, and then the Bold master at coordinate weight=+1.0. We made up the +1.0 number; the actual number is insignificant for the purpose of this discussion. Then, to evaluate… TODO finish this section based on / by linking to writings from Nathan Willis.

Acknowledgements

Behdad Esfahbod wrote the initial version of this document and maintains the text.

Multiple people started talking about dynamic font variations since ATypI 2014 in Barcelona. The ideas presented in this document are the results of author(s)’s discussions with the following people, via email or face-to-face meetings, at ATypI 2014 & 2015, the OpenType Layout ad-hoc Working Group’s meeting in Mountain View in October 2015, as well as other meetings (omissions are my fault):

Adam Twardoch, Benjamin Wagner, Bob Jung, Christian Robertson, Christopher Chapman, Cosimo Lupo, Dave Crossland, Dave Lemon, Dave Opstad, David Kuettel, Denis Jacquerye, Doug Felt, Eric Muller, Erik van Blokland, Frederik Berlaen, Georg Seifert, Greg Hitchcock, James Godfrey-kittle, John Hudson, John Jenkins, Jonathan Kew, Jungshik Shin, Just van Rossum, Karsten Luecke, Ken Lunde, Kenji Baheux, Martin Hosken, Luc(as) de Groot, Mike Reed, Nathan Willis, Ned Holbrook, Peter Constable, Peter Lofting, Raph Levien, Roderick Sheeter, Roozbeh Pournader, Sairus Patel, Sascha Brawer, Sergey Malkin, Tom Rickner, Werner Lemberg.

The author was also informed by the following writings:

as well as the various TrueType GX font variation tables from Apple TrueType Reference Manual.

References

TODO Move all external link here.

Specifications

Source code repositories

Revision history

2016-03-13 Rewrote Conditional substitutions and positioning section with new proposal.

2016-03-10 Fleshed out positioning variation with proposed Device table extension.

2016-03-04 Rewrote proposed “Font variation metadata” predefined axis behavior changes.

2016-02-05 Initial semi-public revision.