varLib: add set_default_weight_width_slant

When building a variable font, varLib.build must make sure that
the OS/2.usWeightClass is equal to the wght axis default location,
that the OS/2.usWidthClass is set to the equivalent value (1-9) of
the wdth axis default location, and finally that post.italicAngle
is set to the same default value as slnt axis.

Sometimes the base master doesn't have these values correctly
set leading to discrepancies between OS/2 and post, on the one
hand, and the fvar axes' default values.
This commit is contained in:
Cosimo Lupo 2019-06-20 12:22:24 +01:00
parent 76489788d6
commit 1122a2612b
No known key found for this signature in database
GPG Key ID: 20D4A261E4A0E642
4 changed files with 122 additions and 2 deletions

View File

@ -857,6 +857,45 @@ def load_designspace(designspace):
) )
# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass
WDTH_VALUE_TO_OS2_WIDTH_CLASS = {
50: 1,
62.5: 2,
75: 3,
87.5: 4,
100: 5,
112.5: 6,
125: 7,
150: 8,
200: 9,
}
def set_default_weight_width_slant(font, location):
if "OS/2" in font:
if "wght" in location:
weight_class = otRound(max(1, min(location["wght"], 1000)))
if font["OS/2"].usWeightClass != weight_class:
log.info("Setting OS/2.usWidthClass = %s", weight_class)
font["OS/2"].usWeightClass = weight_class
if "wdth" in location:
# map 'wdth' axis (50..200) to OS/2.usWidthClass (1..9), rounding to closest
widthValue = min(max(location["wdth"], 50), 200)
widthClass = otRound(
models.piecewiseLinearMap(widthValue, WDTH_VALUE_TO_OS2_WIDTH_CLASS)
)
if font["OS/2"].usWidthClass != widthClass:
log.info("Setting OS/2.usWidthClass = %s", widthClass)
font["OS/2"].usWidthClass = widthClass
if "slnt" in location and "post" in font:
italicAngle = max(-90, min(location["slnt"], 90))
if font["post"].italicAngle != italicAngle:
log.info("Setting post.italicAngle = %s", italicAngle)
font["post"].italicAngle = italicAngle
def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
""" """
Build variation font from a designspace file. Build variation font from a designspace file.
@ -930,6 +969,10 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
post.extraNames = [] post.extraNames = []
post.mapping = {} post.mapping = {}
set_default_weight_width_slant(
vf, location={axis.axisTag: axis.defaultValue for axis in vf["fvar"].axes}
)
for tag in exclude: for tag in exclude:
if tag in vf: if tag in vf:
del vf[tag] del vf[tag]

View File

@ -55,7 +55,7 @@
will be recalculated by the compiler --> will be recalculated by the compiler -->
<version value="4"/> <version value="4"/>
<xAvgCharWidth value="506"/> <xAvgCharWidth value="506"/>
<usWeightClass value="400"/> <usWeightClass value="368"/>
<usWidthClass value="5"/> <usWidthClass value="5"/>
<fsType value="00000000 00000100"/> <fsType value="00000000 00000100"/>
<ySubscriptXSize value="650"/> <ySubscriptXSize value="650"/>

View File

@ -55,7 +55,7 @@
will be recalculated by the compiler --> will be recalculated by the compiler -->
<version value="4"/> <version value="4"/>
<xAvgCharWidth value="580"/> <xAvgCharWidth value="580"/>
<usWeightClass value="400"/> <usWeightClass value="350"/>
<usWidthClass value="5"/> <usWidthClass value="5"/>
<fsType value="00000000 00000100"/> <fsType value="00000000 00000100"/>
<ySubscriptXSize value="650"/> <ySubscriptXSize value="650"/>

View File

@ -4,6 +4,7 @@ from fontTools.ttLib import TTFont, newTable
from fontTools.varLib import build from fontTools.varLib import build
from fontTools.varLib.mutator import instantiateVariableFont from fontTools.varLib.mutator import instantiateVariableFont
from fontTools.varLib import main as varLib_main, load_masters from fontTools.varLib import main as varLib_main, load_masters
from fontTools.varLib import set_default_weight_width_slant
from fontTools.designspaceLib import ( from fontTools.designspaceLib import (
DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor, DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor,
) )
@ -661,5 +662,81 @@ def _extract_flat_kerning(font, pairpos_table):
return extracted_kerning return extracted_kerning
@pytest.fixture
def ttFont():
f = TTFont()
f["OS/2"] = newTable("OS/2")
f["OS/2"].usWeightClass = 400
f["OS/2"].usWidthClass = 100
f["post"] = newTable("post")
f["post"].italicAngle = 0
return f
class SetDefaultWeightWidthSlantTest(object):
@pytest.mark.parametrize(
"location, expected",
[
({"wght": 0}, 1),
({"wght": 1}, 1),
({"wght": 100}, 100),
({"wght": 1000}, 1000),
({"wght": 1001}, 1000),
],
)
def test_wght(self, ttFont, location, expected):
set_default_weight_width_slant(ttFont, location)
assert ttFont["OS/2"].usWeightClass == expected
@pytest.mark.parametrize(
"location, expected",
[
({"wdth": 0}, 1),
({"wdth": 56}, 1),
({"wdth": 57}, 2),
({"wdth": 62.5}, 2),
({"wdth": 75}, 3),
({"wdth": 87.5}, 4),
({"wdth": 100}, 5),
({"wdth": 112.5}, 6),
({"wdth": 125}, 7),
({"wdth": 150}, 8),
({"wdth": 200}, 9),
({"wdth": 201}, 9),
({"wdth": 1000}, 9),
],
)
def test_wdth(self, ttFont, location, expected):
set_default_weight_width_slant(ttFont, location)
assert ttFont["OS/2"].usWidthClass == expected
@pytest.mark.parametrize(
"location, expected",
[
({"slnt": -91}, -90),
({"slnt": -90}, -90),
({"slnt": 0}, 0),
({"slnt": 11.5}, 11.5),
({"slnt": 90}, 90),
({"slnt": 91}, 90),
],
)
def test_slnt(self, ttFont, location, expected):
set_default_weight_width_slant(ttFont, location)
assert ttFont["post"].italicAngle == expected
def test_all(self, ttFont):
set_default_weight_width_slant(
ttFont, {"wght": 500, "wdth": 150, "slnt": -12.0}
)
assert ttFont["OS/2"].usWeightClass == 500
assert ttFont["OS/2"].usWidthClass == 8
assert ttFont["post"].italicAngle == -12.0
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(unittest.main()) sys.exit(unittest.main())