Skip to content

Commit

Permalink
varLib: add --drop-implied-oncurves option
Browse files Browse the repository at this point in the history
For the test, I used the Tests/varLib/data/Build.designspace as starting point, modified the 'a' glyph so that 1 on-curve point (the first one) becomes impliable for all the masters.
  • Loading branch information
anthrotype committed Jun 2, 2023
1 parent b6bb9df commit 144d5a1
Show file tree
Hide file tree
Showing 6 changed files with 1,286 additions and 2 deletions.
64 changes: 62 additions & 2 deletions Lib/fontTools/varLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from fontTools.misc.textTools import Tag, tostr
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, dropImpliedOnCurvePoints
from fontTools.ttLib.tables.ttProgram import Program
from fontTools.ttLib.tables.TupleVariation import TupleVariation
from fontTools.ttLib.tables import otTables as ot
Expand All @@ -40,7 +40,7 @@
from fontTools.colorLib.builder import buildColrV1
from fontTools.colorLib.unbuilder import unbuildColrV1
from functools import partial
from collections import OrderedDict, namedtuple
from collections import OrderedDict, defaultdict, namedtuple
import os.path
import logging
from copy import deepcopy
Expand Down Expand Up @@ -965,13 +965,54 @@ def set_default_weight_width_slant(font, location):
font["post"].italicAngle = italicAngle


def drop_implied_oncurve_points(*masters: TTFont) -> int:
"""Drop impliable on-curve points from all the simple glyphs in masters.
In TrueType glyf outlines, on-curve points can be implied when they are located
exactly at the midpoint of the line connecting two consecutive off-curve points.
The input masters' glyf tables are assumed to contain same-named glyphs that are
interpolatable. Oncurve points are only dropped if they can be implied for all
the masters. The fonts are modified in-place.
Args:
masters: The TTFont(s) to modify
Returns:
The total number of points that were dropped if any.
Reference:
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
"""

count = 0
glyph_masters = defaultdict(list)
# multiple DS source may point to the same TTFont object and we want to
# avoid processing the same glyph twice as they are modified in-place
for font in {id(m): m for m in masters}.values():
glyf = font["glyf"]
for glyphName in glyf.keys():
glyph_masters[glyphName].append(glyf[glyphName])
count = 0
for glyphName, glyphs in glyph_masters.items():
try:
dropped = dropImpliedOnCurvePoints(*glyphs)
except ValueError as e:

Check warning on line 1000 in Lib/fontTools/varLib/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/varLib/__init__.py#L1000

Added line #L1000 was not covered by tests
# we don't fail for incompatible glyphs in _add_gvar so we shouldn't here
log.warning("Failed to drop implied oncurves for %r: %s", glyphName, e)

Check warning on line 1002 in Lib/fontTools/varLib/__init__.py

View check run for this annotation

Codecov / codecov/patch

Lib/fontTools/varLib/__init__.py#L1002

Added line #L1002 was not covered by tests
else:
count += len(dropped)
return count


def build_many(
designspace: DesignSpaceDocument,
master_finder=lambda s: s,
exclude=[],
optimize=True,
skip_vf=lambda vf_name: False,
colr_layer_reuse=True,
drop_implied_oncurves=False,
):
"""
Build variable fonts from a designspace file, version 5 which can define
Expand Down Expand Up @@ -1015,6 +1056,7 @@ def build_many(
exclude=exclude,
optimize=optimize,
colr_layer_reuse=colr_layer_reuse,
drop_implied_oncurves=drop_implied_oncurves,
)[0]
if doBuildStatFromDSv5:
buildVFStatTable(vf, designspace, name)
Expand All @@ -1028,6 +1070,7 @@ def build(
exclude=[],
optimize=True,
colr_layer_reuse=True,
drop_implied_oncurves=False,
):
"""
Build variation font from a designspace file.
Expand Down Expand Up @@ -1055,6 +1098,14 @@ def build(
except AttributeError:
master_ttfs.append(None) # in-memory fonts have no path

if drop_implied_oncurves and "glyf" in master_fonts[ds.base_idx]:
drop_count = drop_implied_oncurve_points(*master_fonts)
log.info(
"Dropped %s on-curve points from simple glyphs in the 'glyf' table",
drop_count,
)


# Copy the base master to work from it
vf = deepcopy(master_fonts[ds.base_idx])

Expand Down Expand Up @@ -1228,6 +1279,14 @@ def main(args=None):
action="store_false",
help="do not rebuild variable COLR table to optimize COLR layer reuse",
)
parser.add_argument(
"--drop-implied-oncurves",
action="store_true",
help=(
"drop on-curve points that can be implied when exactly in the middle of "
"two off-curve points (only applies to TrueType fonts)"
),
)
parser.add_argument(
"--master-finder",
default="master_ttf_interpolatable/{stem}.ttf",
Expand Down Expand Up @@ -1312,6 +1371,7 @@ def main(args=None):
exclude=options.exclude,
optimize=options.optimize,
colr_layer_reuse=options.colr_layer_reuse,
drop_implied_oncurves=options.drop_implied_oncurves,
)

for vf_name, vf in vfs.items():
Expand Down
20 changes: 20 additions & 0 deletions Tests/varLib/data/DropOnCurves.designspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version='1.0' encoding='utf-8'?>
<designspace format="3">
<axes>
<axis default="400" maximum="1000" minimum="400" name="weight" tag="wght" />
</axes>
<sources>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master1.ttx" name="master_1" stylename="Master1">
<location>
<dimension name="weight" xvalue="400" />
</location>
</source>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master2.ttx" name="master_2" stylename="Master2">
<location>
<dimension name="weight" xvalue="1000" />
</location>
</source>
</sources>
<instances>
</instances>
</designspace>

0 comments on commit 144d5a1

Please sign in to comment.