mirror of
https://github.com/google/fonts.git
synced 2024-11-27 13:35:02 +03:00
528 lines
18 KiB
Python
528 lines
18 KiB
Python
from fontTools.ttLib import TTFont
|
|
from fontTools.misc.testTools import getXML
|
|
from axisregistry import (
|
|
build_name_table,
|
|
build_filename,
|
|
build_fvar_instances,
|
|
build_stat,
|
|
build_variations_ps_name,
|
|
_fvar_dflts,
|
|
_fvar_instance_collisions,
|
|
)
|
|
import pytest
|
|
import os
|
|
|
|
CWD = os.path.dirname(__file__)
|
|
DATA_DIR = os.path.join(CWD, "data")
|
|
|
|
roboto_flex_fp = os.path.join(
|
|
DATA_DIR,
|
|
"RobotoFlex[GRAD,XOPQ,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC,opsz,slnt,wdth,wght].ttf",
|
|
)
|
|
mavenpro_fp = os.path.join(DATA_DIR, "MavenPro-Regular.ttf")
|
|
opensans_roman_fp = os.path.join(DATA_DIR, "OpenSans[wdth,wght].ttf")
|
|
opensans_italic_fp = os.path.join(DATA_DIR, "OpenSans-Italic[wdth,wght].ttf")
|
|
opensans_cond_roman_fp = os.path.join(DATA_DIR, "OpenSansCondensed[wght].ttf")
|
|
opensans_cond_italic_fp = os.path.join(DATA_DIR, "OpenSansCondensed-Italic[wght].ttf")
|
|
wonky_fp = os.path.join(DATA_DIR, "Wonky[wdth,wght].ttf")
|
|
|
|
|
|
@pytest.fixture
|
|
def static_font():
|
|
return TTFont(mavenpro_fp)
|
|
|
|
|
|
def _test_names(ttFont, expected):
|
|
name_table = ttFont["name"]
|
|
for k, v in expected.items():
|
|
record = name_table.getName(*k)
|
|
if not record:
|
|
assert record == v, f"{k} record is incorrect, expected {v} got {record}"
|
|
else:
|
|
record_string = record.toUnicode()
|
|
assert (
|
|
record_string == v
|
|
), f"{k} record is incorrect, expected {v} got {record}"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"fp, family_name, style_name, siblings, expected",
|
|
[
|
|
# Maven Pro Regular
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Regular",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-Regular",
|
|
(4, 3, 1, 0x409): "Maven Pro Regular",
|
|
(6, 3, 1, 0x409): "MavenPro-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
# Maven Pro Italic
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Italic",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-Italic",
|
|
(4, 3, 1, 0x409): "Maven Pro Italic",
|
|
(6, 3, 1, 0x409): "MavenPro-Italic",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
# Maven Pro Bold
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Bold",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro",
|
|
(2, 3, 1, 0x409): "Bold",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-Bold",
|
|
(4, 3, 1, 0x409): "Maven Pro Bold",
|
|
(6, 3, 1, 0x409): "MavenPro-Bold",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
# Maven Pro Bold Italic
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Bold Italic",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro",
|
|
(2, 3, 1, 0x409): "Bold Italic",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-BoldItalic",
|
|
(4, 3, 1, 0x409): "Maven Pro Bold Italic",
|
|
(6, 3, 1, 0x409): "MavenPro-BoldItalic",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
# Maven Pro Black
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Black",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro Black",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-Black",
|
|
(4, 3, 1, 0x409): "Maven Pro Black",
|
|
(6, 3, 1, 0x409): "MavenPro-Black",
|
|
(16, 3, 1, 0x409): "Maven Pro",
|
|
(17, 3, 1, 0x409): "Black",
|
|
},
|
|
),
|
|
# Maven Pro Black Italic
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Black Italic",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro Black",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-BlackItalic",
|
|
(4, 3, 1, 0x409): "Maven Pro Black Italic",
|
|
(6, 3, 1, 0x409): "MavenPro-BlackItalic",
|
|
(16, 3, 1, 0x409): "Maven Pro",
|
|
(17, 3, 1, 0x409): "Black Italic",
|
|
},
|
|
),
|
|
# Maven Pro ExtraLight Italic
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"ExtraLight Italic",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro ExtraLight",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenPro-ExtraLightItalic",
|
|
(4, 3, 1, 0x409): "Maven Pro ExtraLight Italic",
|
|
(6, 3, 1, 0x409): "MavenPro-ExtraLightItalic",
|
|
(16, 3, 1, 0x409): "Maven Pro",
|
|
(17, 3, 1, 0x409): "ExtraLight Italic",
|
|
},
|
|
),
|
|
# check non-weight styles get appended to family name
|
|
# Maven Pro UltraExpanded Regular
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"UltraExpanded Regular",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro UltraExpanded",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenProUltraExpanded-Regular",
|
|
(4, 3, 1, 0x409): "Maven Pro UltraExpanded Regular",
|
|
(6, 3, 1, 0x409): "MavenProUltraExpanded-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
# Maven Pro Condensed ExtraLight Italic
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Condensed ExtraLight Italic",
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro Condensed ExtraLight",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenProCondensed-ExtraLightItalic",
|
|
(4, 3, 1, 0x409): "Maven Pro Condensed ExtraLight Italic",
|
|
(6, 3, 1, 0x409): "MavenProCondensed-ExtraLightItalic",
|
|
(16, 3, 1, 0x409): "Maven Pro Condensed",
|
|
(17, 3, 1, 0x409): "ExtraLight Italic",
|
|
},
|
|
),
|
|
## VFs
|
|
# Open Sans Roman
|
|
(
|
|
opensans_roman_fp,
|
|
"Open Sans",
|
|
None,
|
|
[opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
{
|
|
(1, 3, 1, 0x409): "Open Sans",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "3.000;GOOG;OpenSans-Regular",
|
|
(4, 3, 1, 0x409): "Open Sans Regular",
|
|
(6, 3, 1, 0x409): "OpenSans-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
(25, 3, 1, 0x409): "OpenSans",
|
|
},
|
|
),
|
|
# Open Sans Italic
|
|
(
|
|
opensans_italic_fp,
|
|
"Open Sans",
|
|
None,
|
|
[opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
{
|
|
(1, 3, 1, 0x409): "Open Sans",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "3.000;GOOG;OpenSans-Italic",
|
|
(4, 3, 1, 0x409): "Open Sans Italic",
|
|
(6, 3, 1, 0x409): "OpenSans-Italic",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
(25, 3, 1, 0x409): "OpenSansItalic",
|
|
},
|
|
),
|
|
# Open Sans Cond Roman
|
|
(
|
|
opensans_cond_roman_fp,
|
|
"Open Sans Condensed",
|
|
None,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
|
|
{
|
|
(1, 3, 1, 0x409): "Open Sans Condensed",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "3.000;GOOG;OpenSansCondensed-Regular",
|
|
(4, 3, 1, 0x409): "Open Sans Condensed Regular",
|
|
(6, 3, 1, 0x409): "OpenSansCondensed-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
(25, 3, 1, 0x409): "OpenSansCondensed",
|
|
},
|
|
),
|
|
# Open Sans Cond Italic
|
|
(
|
|
opensans_cond_italic_fp,
|
|
"Open Sans Condensed",
|
|
None,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
|
|
{
|
|
(1, 3, 1, 0x409): "Open Sans Condensed",
|
|
(2, 3, 1, 0x409): "Italic",
|
|
(3, 3, 1, 0x409): "3.000;GOOG;OpenSansCondensed-Italic",
|
|
(4, 3, 1, 0x409): "Open Sans Condensed Italic",
|
|
(6, 3, 1, 0x409): "OpenSansCondensed-Italic",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
(25, 3, 1, 0x409): "OpenSansCondensedItalic",
|
|
},
|
|
),
|
|
# Bad names
|
|
(
|
|
mavenpro_fp,
|
|
"Maven Pro",
|
|
"Fat", # this should get appended to the family name
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Maven Pro Fat",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "2.003;NONE;MavenProFat-Regular",
|
|
(4, 3, 1, 0x409): "Maven Pro Fat Regular",
|
|
(6, 3, 1, 0x409): "MavenProFat-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
(
|
|
wonky_fp,
|
|
"Wonky", # name exists in axis reg!
|
|
None,
|
|
[],
|
|
{
|
|
(1, 3, 1, 0x409): "Wonky",
|
|
(2, 3, 1, 0x409): "Regular",
|
|
(3, 3, 1, 0x409): "3.000;GOOG;Wonky-Regular",
|
|
(4, 3, 1, 0x409): "Wonky Regular",
|
|
(6, 3, 1, 0x409): "Wonky-Regular",
|
|
(16, 3, 1, 0x409): None,
|
|
(17, 3, 1, 0x409): None,
|
|
},
|
|
),
|
|
],
|
|
)
|
|
def test_name_table(fp, family_name, style_name, siblings, expected):
|
|
font = TTFont(fp)
|
|
siblings = [TTFont(fp) for fp in siblings]
|
|
build_name_table(font, family_name, style_name, siblings)
|
|
_test_names(font, expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"font_fp, dflt_coords, expected",
|
|
[
|
|
# Condensed variants
|
|
(
|
|
opensans_cond_roman_fp,
|
|
{},
|
|
[
|
|
("Thin", {"wght": 100.0}),
|
|
("ExtraLight", {"wght": 200.0}),
|
|
("Light", {"wght": 300.0}),
|
|
("Regular", {"wght": 400.0}),
|
|
("Medium", {"wght": 500.0}),
|
|
("SemiBold", {"wght": 600.0}),
|
|
("Bold", {"wght": 700.0}),
|
|
("ExtraBold", {"wght": 800.0}),
|
|
],
|
|
),
|
|
(
|
|
opensans_cond_italic_fp,
|
|
{},
|
|
[
|
|
("Thin Italic", {"wght": 100.0}),
|
|
("ExtraLight Italic", {"wght": 200.0}),
|
|
("Light Italic", {"wght": 300.0}),
|
|
("Italic", {"wght": 400.0}),
|
|
("Medium Italic", {"wght": 500.0}),
|
|
("SemiBold Italic", {"wght": 600.0}),
|
|
("Bold Italic", {"wght": 700.0}),
|
|
("ExtraBold Italic", {"wght": 800.0}),
|
|
],
|
|
),
|
|
# Multi axis + roman & ital with dflt coords
|
|
(
|
|
roboto_flex_fp,
|
|
{},
|
|
[
|
|
("Thin", {"wght": 100.0}),
|
|
("ExtraLight", {"wght": 200.0}),
|
|
("Light", {"wght": 300.0}),
|
|
("Regular", {"wght": 400.0}),
|
|
("Medium", {"wght": 500.0}),
|
|
("SemiBold", {"wght": 600.0}),
|
|
("Bold", {"wght": 700.0}),
|
|
("ExtraBold", {"wght": 800.0}),
|
|
("Black", {"wght": 900.0}),
|
|
("Thin Italic", {"wght": 100.0, "slnt": -10}),
|
|
("ExtraLight Italic", {"wght": 200.0, "slnt": -10}),
|
|
("Light Italic", {"wght": 300.0, "slnt": -10}),
|
|
("Italic", {"wght": 400.0, "slnt": -10}),
|
|
("Medium Italic", {"wght": 500.0, "slnt": -10}),
|
|
("SemiBold Italic", {"wght": 600.0, "slnt": -10}),
|
|
("Bold Italic", {"wght": 700.0, "slnt": -10}),
|
|
("ExtraBold Italic", {"wght": 800.0, "slnt": -10}),
|
|
("Black Italic", {"wght": 900.0, "slnt": -10}),
|
|
],
|
|
),
|
|
# multi axis with METADATA.pb registry_default_overrides
|
|
# https://github.com/google/fonts/blob/main/ofl/robotoflex/METADATA.pb
|
|
(
|
|
roboto_flex_fp,
|
|
{
|
|
"XOPQ": 96.0,
|
|
"XTRA": 468.0,
|
|
"YOPQ": 79.0,
|
|
"YTDE": -203.0,
|
|
"YTFI": 738.0,
|
|
"YTLC": 514.0,
|
|
"YTUC": 712.0,
|
|
},
|
|
[
|
|
("Thin", {"wght": 100.0}),
|
|
("ExtraLight", {"wght": 200.0}),
|
|
("Light", {"wght": 300.0}),
|
|
("Regular", {"wght": 400.0}),
|
|
("Medium", {"wght": 500.0}),
|
|
("SemiBold", {"wght": 600.0}),
|
|
("Bold", {"wght": 700.0}),
|
|
("ExtraBold", {"wght": 800.0}),
|
|
("Black", {"wght": 900.0}),
|
|
("Thin Italic", {"wght": 100.0, "slnt": -10}),
|
|
("ExtraLight Italic", {"wght": 200.0, "slnt": -10}),
|
|
("Light Italic", {"wght": 300.0, "slnt": -10}),
|
|
("Italic", {"wght": 400.0, "slnt": -10}),
|
|
("Medium Italic", {"wght": 500.0, "slnt": -10}),
|
|
("SemiBold Italic", {"wght": 600.0, "slnt": -10}),
|
|
("Bold Italic", {"wght": 700.0, "slnt": -10}),
|
|
("ExtraBold Italic", {"wght": 800.0, "slnt": -10}),
|
|
("Black Italic", {"wght": 900.0, "slnt": -10}),
|
|
],
|
|
),
|
|
],
|
|
)
|
|
def test_fvar_instances(font_fp, dflt_coords, expected):
|
|
font = TTFont(font_fp)
|
|
builder = build_fvar_instances(font, dflt_coords)
|
|
fvar = font["fvar"]
|
|
wght_axis = next((a for a in fvar.axes if a.axisTag == "wght"), None)
|
|
wght_min = wght_axis.minValue
|
|
wght_max = wght_axis.maxValue
|
|
instances = fvar.instances
|
|
if not dflt_coords:
|
|
dflt_coords = {k: v["value"] for k, v in _fvar_dflts(font).items()}
|
|
|
|
assert len(instances) == len(expected)
|
|
for inst, (expected_name, coords) in zip(instances, expected):
|
|
inst_name = font["name"].getName(inst.subfamilyNameID, 3, 1, 0x409).toUnicode()
|
|
assert inst_name == expected_name
|
|
for tag, val in coords.items():
|
|
dflt_coords[tag] = val
|
|
assert inst.coordinates == dflt_coords
|
|
|
|
|
|
def dump(table, ttFont=None):
|
|
return "\n".join(getXML(table.toXML, ttFont))
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"fp, sibling_fps",
|
|
[
|
|
(roboto_flex_fp, []),
|
|
(
|
|
opensans_roman_fp,
|
|
[opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
),
|
|
(
|
|
opensans_italic_fp,
|
|
[opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
),
|
|
(
|
|
opensans_cond_roman_fp,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
|
|
),
|
|
(
|
|
opensans_cond_italic_fp,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
|
|
),
|
|
(wonky_fp, []),
|
|
],
|
|
)
|
|
def test_stat(fp, sibling_fps):
|
|
font = TTFont(fp)
|
|
siblings = [TTFont(f) for f in sibling_fps]
|
|
build_stat(font, siblings)
|
|
stat_fp = fp.replace(".ttf", "_STAT.ttx")
|
|
|
|
## output good files
|
|
# with open(stat_fp, "w") as doc:
|
|
# got = dump(font["STAT"], font)
|
|
# doc.write(got)
|
|
|
|
with open(stat_fp) as doc:
|
|
expected = doc.read()
|
|
got = dump(font["STAT"], font)
|
|
assert got == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"fp, expected",
|
|
[
|
|
(mavenpro_fp, "MavenPro-Regular.ttf"),
|
|
(opensans_roman_fp, "OpenSans[wdth,wght].ttf"),
|
|
(opensans_italic_fp, "OpenSans-Italic[wdth,wght].ttf"),
|
|
(opensans_cond_roman_fp, "OpenSansCondensed[wght].ttf"),
|
|
(opensans_cond_italic_fp, "OpenSansCondensed-Italic[wght].ttf"),
|
|
],
|
|
)
|
|
def test_filename(fp, expected):
|
|
font = TTFont(fp)
|
|
assert build_filename(font) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"fp, sibling_fps, result",
|
|
[
|
|
(roboto_flex_fp, [], False),
|
|
(
|
|
opensans_roman_fp,
|
|
[opensans_italic_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
True,
|
|
),
|
|
(
|
|
opensans_italic_fp,
|
|
[opensans_roman_fp, opensans_cond_roman_fp, opensans_cond_italic_fp],
|
|
True,
|
|
),
|
|
(
|
|
opensans_cond_roman_fp,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_italic_fp],
|
|
True,
|
|
),
|
|
(
|
|
opensans_cond_italic_fp,
|
|
[opensans_roman_fp, opensans_italic_fp, opensans_cond_roman_fp],
|
|
True,
|
|
),
|
|
(opensans_roman_fp, [opensans_cond_roman_fp], True),
|
|
(opensans_roman_fp, [opensans_italic_fp], False),
|
|
(wonky_fp, [], False),
|
|
],
|
|
)
|
|
def test_fvar_instance_collisions(fp, sibling_fps, result):
|
|
ttFont = TTFont(fp)
|
|
siblings = [TTFont(fp) for fp in sibling_fps]
|
|
assert _fvar_instance_collisions(ttFont, siblings) == result
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"fp, result",
|
|
[
|
|
(roboto_flex_fp, "RobotoFlex"),
|
|
(opensans_roman_fp, "OpenSans"),
|
|
(opensans_italic_fp, "OpenSansItalic"),
|
|
(opensans_cond_roman_fp, "OpenSansCondensed"),
|
|
(opensans_cond_italic_fp, "OpenSansCondensedItalic"),
|
|
(wonky_fp, "Wonky"),
|
|
],
|
|
)
|
|
def test_build_variations_ps_name(fp, result):
|
|
ttFont = TTFont(fp)
|
|
build_variations_ps_name(ttFont)
|
|
variation_ps_name = ttFont["name"].getName(25, 3, 1, 0x409).toUnicode()
|
|
assert variation_ps_name == result
|