As we make an effort to support conditions with "minimum" or "maximum" absent, as well as with "minimum" or "maximum" None, this commit confirms that the split functions can handle these to some degree also.
212 lines
6.7 KiB
Python
212 lines
6.7 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
|