Previously we were calling glyf.setCoordinates method only when a glyph had some
variation deltas to be applied to the default glyf coordinates.
However, some composite glyph may contain no variation delta but their base glyphs
may change, thus we still need to update the sidebearings and bounding box of the
composite glyphs.
continue checking subsequent condition tables in case the other may
be format=1 and may reference a pinned axis; in which case, these
conditions need to be dropped from the condition set, or the whole
record needs to be dropped if the instance coordinate is outside the
condition range.
Condition tables within a condition set are associated with a AND
boolean operator, so if any one doesn't match, the whole set doesn't
apply. Even if we don't recognize one condition format, if we do
ascertain that another condition table does not match the current
partial instance location, we can drop the FeatureVariation record
since it doesn't apply.
there was a logic issue in the function that checks whether a FeatureVariationRecord
has a unique set of condition (was returning False instead of True for unsupported condition).
It's safer to always keep such records with unknown condition formats as new formats
may be added in the future. A warning is already issued in these cases.
After partial instancing, multiple FeatureVariationRecords may end up with
the same set of conditions (e.g. if one references two axes, one of which
is dropped, and a subsequent one also references the same axis that was
kept in the preceding record's condition set, and the min/max values are
the same for both records).
Therefore, we make sure only the first unique record with a given
configuration of conditions is kept. Any additional records with identical
conditions will never match the current context so they can be dropped.
ConditionTable.AxisIndex needs to change when dropping axes, to
refer to the same axis in the modified fvar.axes array.
There was also another bug when a condition was not met,
and the `applies` flag (initialised to `True`) was not set to
`False`, thus substutions were incorrectly applied.
This allows to drop an axis (aka L1 instancing) without knowing the
axis' actual default value from fvar table. One can simply call
`instantiateVariableFont` function with a `None` value for a given
axis (i.e. axis_limits={'wght': None}); the `None` value is replaced
by the axis default value as per fvar table.
The same can be done from the console script as well.
The special string literal 'None' is parsed as the Python `None`
object. E.g.:
$ fonttools varLib.instancer MyFont-VF.ttf wght=None
Do not round them to integer, but let the caller do the rounding immediately before adding them to the default instance (or just before compiling the binary table as with glyf).
This ensures that the glyphs' left sidebearings are calculated in the same way as they were by varLib.mutator.
If we round deltas too early, then we may get off-by-one differences.
See the glyf table setCoordinates method where left sidebearings are computed.
varLib.mutator does the same.
Ideally we would keep STAT if has any extra (inter-family) DesignAxis or it font was only partially instanced. We can improve on this later as needed.
for avar, we drop segments of the axes being pinned.
for fvar, we drop the pinned axes and all the named instances whose coordinates are different from the pinned location.
if the original VarStore had any regions in VarRegionList that wasn't
even referenced in any VarData VarRegionIndex, this makes sure we
remove those as well from VarRegionList (and remap the VarRegionIndex
accordingly)
We don't actually apply deltas to hmtx since these have already been applied
from the gvar deltas when we call glyf.setCoordinates method using the glyf
phantom points.
We simply call instantiateItemVariationStore on HVAR.VarStore to remove
regions and scale remaining deltas, but ignore the return value.
We only run VarStore.optimize() if the HVAR originally has an AdvWidthMap,
if it does not then it uses a direct implicit GID->VariationIndex mapping
for advance widths deltas, and we keep the VariationIndex unchanged by
not optimizing VarStore.
If all axes in fvar are being instanced, then we simply delete HVAR
(just like varLib.mutator currently does).
VVAR is not supported yet because we do not set the 3rd and 4th phantom points
from gvar to the vmtx table yet (this should be done in glyf.setCoordinates).
Also, supporting CFF2 would need more work, in that HVAR there is required
and we need to apply the deltas to hmtx/vmtx in here.
If we modify the default instance coordinates, then the inferred deltas that
are left in gvar are no longer valid, so we need to calculate them using the
original default coordinates.
They are then re-optimized using the modified default coordinates.
Also, the default deltas returned from instantiateTupleVariationStore are now
already rounded to integer.
The instantiateTupleVariationStore function now groups TupleVariation
tables that have the same axes 'tents', then merges them into a single
TupleVariation by summing their deltas. The rounding to integer happens
after summing the scaled deltas as floats, to reduce off-by-one errors.
To be able to sum gvar TupleVariation, it needs to calculate the inferred
deltas so it now takes two optional lists (origCoords and endPts) that
are passed on to iup_delta function. These only make sense for gvar
type of TupleVariation, of course, and are unused for cvar tuples.
It also run iup_delta_optimize on the gvar deltas that are left after
partial instancing and whose inferred deltas had to be interpolated.
This can be disabled with --no-optimize CLI option.
Also added calcInferredDeltas and optimize methods to TupleVariation
class, which use functions from varLib.iup module, plus tests
that exercise them.