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:
parent
76489788d6
commit
1122a2612b
@ -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]
|
||||||
|
@ -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"/>
|
||||||
|
@ -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"/>
|
||||||
|
@ -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())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user