[varLib.mutator] Support partial VF instances
See original GitHub issueThe current fontTools.varlib.mutator module can fully instantiate a variable font into a static font at a given location along all the axes.
The Google Fonts team is interested in adding support for generating “partial” instances: i.e one wants to keep wght
axis but pin the wdth
axis to a specific location, either the default or a custom one, dropping all the variation data relating to the pinned axes.
Or alternatively one may want to restrict the range (min and/or max) of an axis, producing a new VF with possibly smaller file size.
We have created a new branch partial-instancer
in here where we pushed a new module called fontTools.varLib.partialInstancer
(the name may change, I’m thinking just “instancer” sounds better). It’s is based on the mutator module and has a similar interface. When it’s complete, potentially it could subsume the functionality of mutator, since the partial instancing can be seen as a more general problem.
The module is still in early stages and can currently only pin-and-drop gvar variation data. Limiting axis ranges is not supported either. We shall probably focus on implementing the axis dropping and pinning first for the rest of the variation tables, as that appear to be a relatively easier problem than limiting ranges, and one that guarantees some size reduction.
Feel free to chime in with suggestions or comments. I’ll keep you updated with the progress.
Issue Analytics
- State:
- Created 5 years ago
- Comments:20 (17 by maintainers)
Top GitHub Comments
I suggest we break this operation down to separate operations that can be chained together. That way the code would be much easier to reason about. I suggest implementing the following:
Dropping an entire axis. This is simplest: any deltaset that has a non-zero peak for the said axis is just dropped completely.
Instantiating an axis to a particular value. The previous operation is a special-case of this one. In this operation, any deltaset that interacts with the axis will get its deltas multiplied by the scalar support of the axis at the desired instantiation point for the deltaset’s tent. If the scalar is 0, drop the deltaset. If it’s 1, leave as is, otherwise multiply the values by it.
Limit min/max of an axis. For each deltaset that participates in the axis, if the deltaset’s tent is fully outside new min/max of axis, drop it. If the tent is fully inside the new range, keep it (just scale the tent min/peak/max to the new normalized axis). If the tent overlaps but also extends beyond new min/max, it becomes interesting. I’ll address that separately in the following comment.
Move the base location. This is harder in the general case and to be done later, separately, if ever.
Imagine we want to change maximum of ‘axis’ from 1.0 (normalized, always 1.0) to X. Now imagine we have a deltaset with min/peak/max of A<B<C such that A<=X<C. Then do the following:
Divide A,B,C by X to get new normalized values. We now expect that A<=1.0<C. Now, two cases:
if the peak falls outside: Ie. 1.0<B. Then scale deltaset by (1-A)/(B-A) and then set B and C to 1.0.
if the peak falls inside: If C <= 2.0, keep everything. The format allows for encoding coordinate values in the [-2,+2) range. +2 is not supported but we can store +1.9999999 instead of it. So, just keep the tent as is, extending beyond the axis maximum.
if the peak falls inside and C > 2, we need to chop the tent overlap shape into two deltas, because the shape of a triangle with part of one side cut off cannot be represented as a triangle itself. It can be represented as sum of two triangles. This increases font file but is doable.