varLib.instancer: make 'overlap' an enum; add --remove-overlaps CLI option

'overlap' parameter in instantiateVariableFont was a bool, now it is a tri-state IntEnum, with value 2 meaning 'remove the overlaps'. The old bool False/True (0/1) values continue to work like before.

Also added a new --remove-overlaps commandline option to fonttools varLib.instancer script.
This commit is contained in:
Cosimo Lupo 2020-09-27 17:48:52 +01:00
parent 7531b71717
commit 85947cabb3
No known key found for this signature in database
GPG Key ID: 179A8F0895A02F4F

View File

@ -87,6 +87,7 @@ from fontTools.varLib.merger import MutatorMerger
from contextlib import contextmanager
import collections
from copy import deepcopy
from enum import IntEnum
import logging
from itertools import islice
import os
@ -121,6 +122,12 @@ class NormalizedAxisRange(AxisRange):
return self
class OverlapsMode(IntEnum):
KEEP_AND_DONT_SET_FLAGS = 0
KEEP_AND_SET_FLAGS = 1
REMOVE = 2
def instantiateTupleVariationStore(
variations, axisLimits, origCoords=None, endPts=None
):
@ -578,7 +585,7 @@ class _TupleVarStoreAdapter(object):
def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits):
""" Compute deltas at partial location, and update varStore in-place.
"""Compute deltas at partial location, and update varStore in-place.
Remove regions in which all axes were instanced, or fall outside the new axis
limits. Scale the deltas of the remaining regions where only some of the axes
@ -1175,9 +1182,13 @@ def populateAxisDefaults(varfont, axisLimits):
def instantiateVariableFont(
varfont, axisLimits, inplace=False, optimize=True, overlap=True
varfont,
axisLimits,
inplace=False,
optimize=True,
overlap=OverlapsMode.KEEP_AND_SET_FLAGS,
):
""" Instantiate variable font, either fully or partially.
"""Instantiate variable font, either fully or partially.
Depending on whether the `axisLimits` dictionary references all or some of the
input varfont's axes, the output font will either be a full instance (static
@ -1198,13 +1209,20 @@ def instantiateVariableFont(
remaining 'gvar' table's deltas. Possibly faster, and might work around
rendering issues in some buggy environments, at the cost of a slightly
larger file size.
overlap (bool): variable fonts usually contain overlapping contours, and some
font rendering engines on Apple platforms require that the `OVERLAP_SIMPLE`
and `OVERLAP_COMPOUND` flags in the 'glyf' table be set to force rendering
using a non-zero fill rule. Thus we always set these flags on all glyphs
to maximise cross-compatibility of the generated instance. You can disable
this by setting `overalap` to False.
overlap (OverlapsMode): variable fonts usually contain overlapping contours, and
some font rendering engines on Apple platforms require that the
`OVERLAP_SIMPLE` and `OVERLAP_COMPOUND` flags in the 'glyf' table be set to
force rendering using a non-zero fill rule. Thus we always set these flags
on all glyphs to maximise cross-compatibility of the generated instance.
You can disable this by passing OverlapsMode.KEEP_AND_DONT_SET_FLAGS.
If you want to remove the overlaps altogether and merge overlapping
contours and components, you can pass OverlapsMode.REMOVE. Note that this
requires the skia-pathops package (available to pip install).
The overlap parameter only has effect when generating full static instances.
"""
# 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
overlap = OverlapsMode(int(overlap))
sanityCheckVariableTables(varfont)
axisLimits = populateAxisDefaults(varfont, axisLimits)
@ -1245,8 +1263,14 @@ def instantiateVariableFont(
instantiateFvar(varfont, axisLimits)
if "fvar" not in varfont:
if "glyf" in varfont and overlap:
setMacOverlapFlags(varfont["glyf"])
if "glyf" in varfont:
if overlap == OverlapsMode.KEEP_AND_SET_FLAGS:
setMacOverlapFlags(varfont["glyf"])
elif overlap == OverlapsMode.REMOVE:
from fontTools.ttLib.removeOverlaps import removeOverlaps
log.info("Removing overlaps from glyf table")
removeOverlaps(varfont)
varLib.set_default_weight_width_slant(
varfont,
@ -1346,6 +1370,13 @@ def parseArgs(args):
help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags (only applicable "
"when generating a full instance)",
)
parser.add_argument(
"--remove-overlaps",
dest="remove_overlaps",
action="store_true",
help="Merge overlapping contours and components (only applicable "
"when generating a full instance). Requires skia-pathops",
)
loggingGroup = parser.add_mutually_exclusive_group(required=False)
loggingGroup.add_argument(
"-v", "--verbose", action="store_true", help="Run more verbosely."
@ -1355,6 +1386,11 @@ def parseArgs(args):
)
options = parser.parse_args(args)
if options.remove_overlaps:
options.overlap = OverlapsMode.REMOVE
else:
options.overlap = OverlapsMode(int(options.overlap))
infile = options.input
if not os.path.isfile(infile):
parser.error("No such file '{}'".format(infile))