323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
from fontTools.ttLib.tables import otTables
 | 
						|
from fontTools.otlLib.builder import buildStatTable
 | 
						|
from fontTools.varLib import instancer
 | 
						|
 | 
						|
import pytest
 | 
						|
 | 
						|
 | 
						|
def test_pruningUnusedNames(varfont):
 | 
						|
    varNameIDs = instancer.names.getVariationNameIDs(varfont)
 | 
						|
 | 
						|
    assert varNameIDs == set(range(256, 297 + 1))
 | 
						|
 | 
						|
    fvar = varfont["fvar"]
 | 
						|
    stat = varfont["STAT"].table
 | 
						|
 | 
						|
    with instancer.names.pruningUnusedNames(varfont):
 | 
						|
        del fvar.axes[0]  # Weight (nameID=256)
 | 
						|
        del fvar.instances[0]  # Thin (nameID=258)
 | 
						|
        del stat.DesignAxisRecord.Axis[0]  # Weight (nameID=256)
 | 
						|
        del stat.AxisValueArray.AxisValue[0]  # Thin (nameID=258)
 | 
						|
 | 
						|
    assert not any(n for n in varfont["name"].names if n.nameID in {256, 258})
 | 
						|
 | 
						|
    with instancer.names.pruningUnusedNames(varfont):
 | 
						|
        del varfont["fvar"]
 | 
						|
        del varfont["STAT"]
 | 
						|
 | 
						|
    assert not any(n for n in varfont["name"].names if n.nameID in varNameIDs)
 | 
						|
    assert "ltag" not in varfont
 | 
						|
 | 
						|
 | 
						|
def _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x409]):
 | 
						|
    nametable = varfont["name"]
 | 
						|
    font_names = {
 | 
						|
        (r.nameID, r.platformID, r.platEncID, r.langID): r.toUnicode()
 | 
						|
        for r in nametable.names
 | 
						|
    }
 | 
						|
    for k in expected:
 | 
						|
        if k[-1] not in platforms:
 | 
						|
            continue
 | 
						|
        assert font_names[k] == expected[k]
 | 
						|
 | 
						|
    font_nameids = set(i[0] for i in font_names)
 | 
						|
    if isNonRIBBI:
 | 
						|
        assert 16 in font_nameids
 | 
						|
        assert 17 in font_nameids
 | 
						|
 | 
						|
    if "fvar" not in varfont:
 | 
						|
        assert 25 not in font_nameids
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "limits, expected, isNonRIBBI",
 | 
						|
    [
 | 
						|
        # Regular
 | 
						|
        (
 | 
						|
            {"wght": 400},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Regular",
 | 
						|
            },
 | 
						|
            False,
 | 
						|
        ),
 | 
						|
        # Regular Normal (width axis Normal isn't included since it is elided)
 | 
						|
        (
 | 
						|
            {"wght": 400, "wdth": 100},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Regular",
 | 
						|
            },
 | 
						|
            False,
 | 
						|
        ),
 | 
						|
        # Black
 | 
						|
        (
 | 
						|
            {"wght": 900},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Black",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Black",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Black",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Black",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
        # Thin
 | 
						|
        (
 | 
						|
            {"wght": 100},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Thin",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Thin",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Thin",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Thin",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
        # Thin Condensed
 | 
						|
        (
 | 
						|
            {"wght": 100, "wdth": 79},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Thin Condensed",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-ThinCondensed",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-ThinCondensed",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Thin Condensed",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
        # Condensed with unpinned weights
 | 
						|
        (
 | 
						|
            {"wdth": 79, "wght": instancer.AxisRange(400, 900)},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Condensed",
 | 
						|
                (2, 3, 1, 0x409): "Regular",
 | 
						|
                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Condensed",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Condensed",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Condensed",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
)
 | 
						|
def test_updateNameTable_with_registered_axes_ribbi(
 | 
						|
    varfont, limits, expected, isNonRIBBI
 | 
						|
):
 | 
						|
    instancer.names.updateNameTable(varfont, limits)
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI)
 | 
						|
 | 
						|
 | 
						|
def test_updatetNameTable_axis_order(varfont):
 | 
						|
    axes = [
 | 
						|
        dict(
 | 
						|
            tag="wght",
 | 
						|
            name="Weight",
 | 
						|
            values=[
 | 
						|
                dict(value=400, name="Regular"),
 | 
						|
            ],
 | 
						|
        ),
 | 
						|
        dict(
 | 
						|
            tag="wdth",
 | 
						|
            name="Width",
 | 
						|
            values=[
 | 
						|
                dict(value=75, name="Condensed"),
 | 
						|
            ],
 | 
						|
        ),
 | 
						|
    ]
 | 
						|
    nametable = varfont["name"]
 | 
						|
    buildStatTable(varfont, axes)
 | 
						|
    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
 | 
						|
    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Regular Condensed"
 | 
						|
 | 
						|
    # Swap the axes so the names get swapped
 | 
						|
    axes[0], axes[1] = axes[1], axes[0]
 | 
						|
 | 
						|
    buildStatTable(varfont, axes)
 | 
						|
    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
 | 
						|
    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Condensed Regular"
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "limits, expected, isNonRIBBI",
 | 
						|
    [
 | 
						|
        # Regular | Normal
 | 
						|
        (
 | 
						|
            {"wght": 400},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (2, 3, 1, 0x409): "Normal",
 | 
						|
            },
 | 
						|
            False,
 | 
						|
        ),
 | 
						|
        # Black | Negreta
 | 
						|
        (
 | 
						|
            {"wght": 900},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Negreta",
 | 
						|
                (2, 3, 1, 0x409): "Normal",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Negreta",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
        # Black Condensed | Negreta Zhuštěné
 | 
						|
        (
 | 
						|
            {"wght": 900, "wdth": 79},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Negreta Zhuštěné",
 | 
						|
                (2, 3, 1, 0x409): "Normal",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Negreta Zhuštěné",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
)
 | 
						|
def test_updateNameTable_with_multilingual_names(varfont, limits, expected, isNonRIBBI):
 | 
						|
    name = varfont["name"]
 | 
						|
    # langID 0x405 is the Czech Windows langID
 | 
						|
    name.setName("Test Variable Font", 1, 3, 1, 0x405)
 | 
						|
    name.setName("Normal", 2, 3, 1, 0x405)
 | 
						|
    name.setName("Normal", 261, 3, 1, 0x405)  # nameID 261=Regular STAT entry
 | 
						|
    name.setName("Negreta", 266, 3, 1, 0x405)  # nameID 266=Black STAT entry
 | 
						|
    name.setName("Zhuštěné", 279, 3, 1, 0x405)  # nameID 279=Condensed STAT entry
 | 
						|
 | 
						|
    instancer.names.updateNameTable(varfont, limits)
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x405])
 | 
						|
 | 
						|
 | 
						|
def test_updateNameTable_missing_axisValues(varfont):
 | 
						|
    with pytest.raises(ValueError, match="Cannot find Axis Values \['wght=200'\]"):
 | 
						|
        instancer.names.updateNameTable(varfont, {"wght": 200})
 | 
						|
 | 
						|
 | 
						|
def test_updateNameTable_missing_stat(varfont):
 | 
						|
    del varfont["STAT"]
 | 
						|
    with pytest.raises(
 | 
						|
        ValueError, match="Cannot update name table since there is no STAT table."
 | 
						|
    ):
 | 
						|
        instancer.names.updateNameTable(varfont, {"wght": 400})
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "limits, expected, isNonRIBBI",
 | 
						|
    [
 | 
						|
        # Regular | Normal
 | 
						|
        (
 | 
						|
            {"wght": 400},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (2, 3, 1, 0x409): "Italic",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-Italic",
 | 
						|
            },
 | 
						|
            False,
 | 
						|
        ),
 | 
						|
        # Black Condensed Italic
 | 
						|
        (
 | 
						|
            {"wght": 900, "wdth": 79},
 | 
						|
            {
 | 
						|
                (1, 3, 1, 0x409): "Test Variable Font Black Condensed",
 | 
						|
                (2, 3, 1, 0x409): "Italic",
 | 
						|
                (6, 3, 1, 0x409): "TestVariableFont-BlackCondensedItalic",
 | 
						|
                (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
                (17, 3, 1, 0x409): "Black Condensed Italic",
 | 
						|
            },
 | 
						|
            True,
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
)
 | 
						|
def test_updateNameTable_vf_with_italic_attribute(
 | 
						|
    varfont, limits, expected, isNonRIBBI
 | 
						|
):
 | 
						|
    font_link_axisValue = varfont["STAT"].table.AxisValueArray.AxisValue[4]
 | 
						|
    # Unset ELIDABLE_AXIS_VALUE_NAME flag
 | 
						|
    font_link_axisValue.Flags &= ~instancer.names.ELIDABLE_AXIS_VALUE_NAME
 | 
						|
    font_link_axisValue.ValueNameID = 294  # Roman --> Italic
 | 
						|
 | 
						|
    instancer.names.updateNameTable(varfont, limits)
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI)
 | 
						|
 | 
						|
 | 
						|
def test_updateNameTable_format4_axisValues(varfont):
 | 
						|
    # format 4 axisValues should dominate the other axisValues
 | 
						|
    stat = varfont["STAT"].table
 | 
						|
 | 
						|
    axisValue = otTables.AxisValue()
 | 
						|
    axisValue.Format = 4
 | 
						|
    axisValue.Flags = 0
 | 
						|
    varfont["name"].setName("Dominant Value", 297, 3, 1, 0x409)
 | 
						|
    axisValue.ValueNameID = 297
 | 
						|
    axisValue.AxisValueRecord = []
 | 
						|
    for tag, value in (("wght", 900), ("wdth", 79)):
 | 
						|
        rec = otTables.AxisValueRecord()
 | 
						|
        rec.AxisIndex = next(
 | 
						|
            i for i, a in enumerate(stat.DesignAxisRecord.Axis) if a.AxisTag == tag
 | 
						|
        )
 | 
						|
        rec.Value = value
 | 
						|
        axisValue.AxisValueRecord.append(rec)
 | 
						|
    stat.AxisValueArray.AxisValue.append(axisValue)
 | 
						|
 | 
						|
    instancer.names.updateNameTable(varfont, {"wdth": 79, "wght": 900})
 | 
						|
    expected = {
 | 
						|
        (1, 3, 1, 0x409): "Test Variable Font Dominant Value",
 | 
						|
        (2, 3, 1, 0x409): "Regular",
 | 
						|
        (16, 3, 1, 0x409): "Test Variable Font",
 | 
						|
        (17, 3, 1, 0x409): "Dominant Value",
 | 
						|
    }
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI=True)
 | 
						|
 | 
						|
 | 
						|
def test_updateNameTable_elided_axisValues(varfont):
 | 
						|
    stat = varfont["STAT"].table
 | 
						|
    # set ELIDABLE_AXIS_VALUE_NAME flag for all axisValues
 | 
						|
    for axisValue in stat.AxisValueArray.AxisValue:
 | 
						|
        axisValue.Flags |= instancer.names.ELIDABLE_AXIS_VALUE_NAME
 | 
						|
 | 
						|
    stat.ElidedFallbackNameID = 266  # Regular --> Black
 | 
						|
    instancer.names.updateNameTable(varfont, {"wght": 400})
 | 
						|
    # Since all axis values are elided, the elided fallback name
 | 
						|
    # must be used to construct the style names. Since we
 | 
						|
    # changed it to Black, we need both a typoSubFamilyName and
 | 
						|
    # the subFamilyName set so it conforms to the RIBBI model.
 | 
						|
    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Black"}
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI=True)
 | 
						|
 | 
						|
 | 
						|
def test_updateNameTable_existing_subfamily_name_is_not_regular(varfont):
 | 
						|
    # Check the subFamily name will be set to Regular when we update a name
 | 
						|
    # table to a non-RIBBI style and the current subFamily name is a RIBBI
 | 
						|
    # style which isn't Regular.
 | 
						|
    varfont["name"].setName("Bold", 2, 3, 1, 0x409)  # subFamily Regular --> Bold
 | 
						|
 | 
						|
    instancer.names.updateNameTable(varfont, {"wght": 100})
 | 
						|
    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Thin"}
 | 
						|
    _test_name_records(varfont, expected, isNonRIBBI=True)
 |