221 lines
6.9 KiB
Python
221 lines
6.9 KiB
Python
import math
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fontTools.designspaceLib import DesignSpaceDocument
|
|
from fontTools.designspaceLib.split import (
|
|
_conditionSetFrom,
|
|
convert5to4,
|
|
splitInterpolable,
|
|
splitVariableFonts,
|
|
)
|
|
from fontTools.designspaceLib.types import ConditionSet, Range
|
|
|
|
from .fixtures import datadir
|
|
|
|
UPDATE_REFERENCE_OUT_FILES_INSTEAD_OF_TESTING = False
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"test_ds,expected_interpolable_spaces",
|
|
[
|
|
(
|
|
"test_v5_aktiv.designspace",
|
|
[
|
|
(
|
|
{},
|
|
{
|
|
"AktivGroteskVF_Italics_Wght",
|
|
"AktivGroteskVF_Italics_WghtWdth",
|
|
"AktivGroteskVF_Wght",
|
|
"AktivGroteskVF_WghtWdth",
|
|
"AktivGroteskVF_WghtWdthItal",
|
|
},
|
|
)
|
|
],
|
|
),
|
|
(
|
|
"test_v5_sourceserif.designspace",
|
|
[
|
|
(
|
|
{"italic": 0},
|
|
{"SourceSerif4Variable-Roman"},
|
|
),
|
|
(
|
|
{"italic": 1},
|
|
{"SourceSerif4Variable-Italic"},
|
|
),
|
|
],
|
|
),
|
|
(
|
|
"test_v5_MutatorSans_and_Serif.designspace",
|
|
[
|
|
(
|
|
{"serif": 0},
|
|
{
|
|
"MutatorSansVariable_Weight_Width",
|
|
"MutatorSansVariable_Weight",
|
|
"MutatorSansVariable_Width",
|
|
},
|
|
),
|
|
(
|
|
{"serif": 1},
|
|
{
|
|
"MutatorSerifVariable_Width",
|
|
},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
)
|
|
def test_split(datadir, tmpdir, test_ds, expected_interpolable_spaces):
|
|
data_in = datadir / test_ds
|
|
temp_in = Path(tmpdir) / test_ds
|
|
shutil.copy(data_in, temp_in)
|
|
doc = DesignSpaceDocument.fromfile(temp_in)
|
|
|
|
for i, (location, sub_doc) in enumerate(splitInterpolable(doc)):
|
|
expected_location, expected_vf_names = expected_interpolable_spaces[i]
|
|
assert location == expected_location
|
|
vfs = list(splitVariableFonts(sub_doc))
|
|
assert expected_vf_names == set(vf[0] for vf in vfs)
|
|
|
|
loc_str = "_".join(
|
|
f"{name}_{value}" for name, value in sorted(location.items())
|
|
)
|
|
data_out = datadir / "split_output" / f"{temp_in.stem}_{loc_str}.designspace"
|
|
temp_out = Path(tmpdir) / "out" / f"{temp_in.stem}_{loc_str}.designspace"
|
|
temp_out.parent.mkdir(exist_ok=True)
|
|
sub_doc.write(temp_out)
|
|
|
|
if UPDATE_REFERENCE_OUT_FILES_INSTEAD_OF_TESTING:
|
|
data_out.write_text(temp_out.read_text(encoding="utf-8"), encoding="utf-8")
|
|
else:
|
|
assert data_out.read_text(encoding="utf-8") == temp_out.read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
for vf_name, vf_doc in vfs:
|
|
data_out = (datadir / "split_output" / vf_name).with_suffix(".designspace")
|
|
temp_out = (Path(tmpdir) / "out" / vf_name).with_suffix(".designspace")
|
|
temp_out.parent.mkdir(exist_ok=True)
|
|
vf_doc.write(temp_out)
|
|
|
|
if UPDATE_REFERENCE_OUT_FILES_INSTEAD_OF_TESTING:
|
|
data_out.write_text(
|
|
temp_out.read_text(encoding="utf-8"), encoding="utf-8"
|
|
)
|
|
else:
|
|
assert data_out.read_text(encoding="utf-8") == temp_out.read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"test_ds,expected_vfs",
|
|
[
|
|
(
|
|
"test_v5_aktiv.designspace",
|
|
{
|
|
"AktivGroteskVF_Italics_Wght",
|
|
"AktivGroteskVF_Italics_WghtWdth",
|
|
"AktivGroteskVF_Wght",
|
|
"AktivGroteskVF_WghtWdth",
|
|
"AktivGroteskVF_WghtWdthItal",
|
|
},
|
|
),
|
|
(
|
|
"test_v5_sourceserif.designspace",
|
|
{
|
|
"SourceSerif4Variable-Italic",
|
|
"SourceSerif4Variable-Roman",
|
|
},
|
|
),
|
|
],
|
|
)
|
|
def test_convert5to4(datadir, tmpdir, test_ds, expected_vfs):
|
|
data_in = datadir / test_ds
|
|
temp_in = tmpdir / test_ds
|
|
shutil.copy(data_in, temp_in)
|
|
doc = DesignSpaceDocument.fromfile(temp_in)
|
|
|
|
variable_fonts = convert5to4(doc)
|
|
|
|
assert variable_fonts.keys() == expected_vfs
|
|
for vf_name, vf in variable_fonts.items():
|
|
data_out = (datadir / "convert5to4_output" / vf_name).with_suffix(
|
|
".designspace"
|
|
)
|
|
temp_out = (Path(tmpdir) / "out" / vf_name).with_suffix(".designspace")
|
|
temp_out.parent.mkdir(exist_ok=True)
|
|
vf.write(temp_out)
|
|
|
|
if UPDATE_REFERENCE_OUT_FILES_INSTEAD_OF_TESTING:
|
|
data_out.write_text(temp_out.read_text(encoding="utf-8"), encoding="utf-8")
|
|
else:
|
|
assert data_out.read_text(encoding="utf-8") == temp_out.read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
["unbounded_condition"],
|
|
[
|
|
({"name": "Weight", "minimum": 500},),
|
|
({"name": "Weight", "maximum": 500},),
|
|
({"name": "Weight", "minimum": 500, "maximum": None},),
|
|
({"name": "Weight", "minimum": None, "maximum": 500},),
|
|
],
|
|
)
|
|
def test_optional_min_max(unbounded_condition):
|
|
"""Check that split functions can handle conditions that are partially
|
|
unbounded without tripping over None values and missing keys."""
|
|
doc = DesignSpaceDocument()
|
|
|
|
doc.addAxisDescriptor(
|
|
name="Weight", tag="wght", minimum=400, maximum=1000, default=400
|
|
)
|
|
|
|
doc.addRuleDescriptor(
|
|
name="unbounded",
|
|
conditionSets=[[unbounded_condition]],
|
|
)
|
|
|
|
assert len(list(splitInterpolable(doc))) == 1
|
|
assert len(list(splitVariableFonts(doc))) == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
["condition", "expected_set"],
|
|
[
|
|
(
|
|
{"name": "axis", "minimum": 0.5},
|
|
{"axis": Range(minimum=0.5, maximum=math.inf)},
|
|
),
|
|
(
|
|
{"name": "axis", "maximum": 0.5},
|
|
{"axis": Range(minimum=-math.inf, maximum=0.5)},
|
|
),
|
|
(
|
|
{"name": "axis", "minimum": 0.5, "maximum": None},
|
|
{"axis": Range(minimum=0.5, maximum=math.inf)},
|
|
),
|
|
(
|
|
{"name": "axis", "minimum": None, "maximum": 0.5},
|
|
{"axis": Range(minimum=-math.inf, maximum=0.5)},
|
|
),
|
|
],
|
|
)
|
|
def test_optional_min_max_internal(condition, expected_set: ConditionSet):
|
|
"""Check that split's internal helper functions produce the correct output
|
|
for conditions that are partially unbounded."""
|
|
assert _conditionSetFrom([condition]) == expected_set
|
|
|
|
|
|
def test_avar2(datadir):
|
|
ds = DesignSpaceDocument()
|
|
ds.read(datadir / "test_avar2.designspace")
|
|
out = list(splitInterpolable(ds))[0][1]
|
|
assert len(out.axisMappings) == 3
|