diff --git a/Tests/varLib/data/KerningMerging.designspace b/Tests/varLib/data/KerningMerging.designspace new file mode 100644 index 000000000..f7ce7ef8c --- /dev/null +++ b/Tests/varLib/data/KerningMerging.designspace @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/master_kerning_merging/0.ttx b/Tests/varLib/data/master_kerning_merging/0.ttx new file mode 100644 index 000000000..858f9275d --- /dev/null +++ b/Tests/varLib/data/master_kerning_merging/0.ttx @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test Thin + + + Regular + + + 0.000;NONE;Test-Thin + + + Test Thin + + + Version 0.000 + + + Test-Thin + + + Test + + + Thin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/master_kerning_merging/1.ttx b/Tests/varLib/data/master_kerning_merging/1.ttx new file mode 100644 index 000000000..d60a70845 --- /dev/null +++ b/Tests/varLib/data/master_kerning_merging/1.ttx @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test + + + Regular + + + 0.000;NONE;Test-Regular + + + Test Regular + + + Version 0.000 + + + Test-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/master_kerning_merging/2.ttx b/Tests/varLib/data/master_kerning_merging/2.ttx new file mode 100644 index 000000000..e01a7b1f6 --- /dev/null +++ b/Tests/varLib/data/master_kerning_merging/2.ttx @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test Black + + + Regular + + + 0.000;NONE;Test-Black + + + Test Black + + + Version 0.000 + + + Test-Black + + + Test + + + Black + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index f3037339e..207a4bc8e 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -2,6 +2,7 @@ from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * from fontTools.ttLib import TTFont, newTable from fontTools.varLib import build +from fontTools.varLib.mutator import instantiateVariableFont from fontTools.varLib import main as varLib_main, load_masters from fontTools.designspaceLib import ( DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor, @@ -496,6 +497,98 @@ class BuildTest(unittest.TestCase): self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix) + def test_kerning_merging(self): + """Test the correct merging of class-based pair kerning. + + Problem description at https://github.com/fonttools/fonttools/pull/1638. + Test font and Designspace generated by + https://gist.github.com/madig/183d0440c9f7d05f04bd1280b9664bd1. + """ + ds_path = self.get_test_input("KerningMerging.designspace") + ttx_dir = self.get_test_input("master_kerning_merging") + + ds = DesignSpaceDocument.fromfile(ds_path) + for source in ds.sources: + ttx_dump = TTFont() + ttx_dump.importXML( + os.path.join( + ttx_dir, os.path.basename(source.filename).replace(".ttf", ".ttx") + ) + ) + source.font = reload_font(ttx_dump) + + varfont, _, _ = build(ds) + varfont = reload_font(varfont) + + class_kerning_tables = [ + t + for l in varfont["GPOS"].table.LookupList.Lookup + for t in l.SubTable + if t.Format == 2 + ] + assert len(class_kerning_tables) == 1 + class_kerning_table = class_kerning_tables[0] + + # Test that no class kerned against class zero (containing all glyphs not + # classed) has a `XAdvDevice` table attached, which in the variable font + # context is a "VariationIndex" table and points to kerning deltas in the GDEF + # table. Variation deltas of any kerning class against class zero should + # probably never exist. + for class1_record in class_kerning_table.Class1Record: + class2_zero = class1_record.Class2Record[0] + assert getattr(class2_zero.Value1, "XAdvDevice", None) is None + + # Assert the variable font's kerning table (without deltas) is equal to the + # default font's kerning table. The bug fixed in + # https://github.com/fonttools/fonttools/pull/1638 caused rogue kerning + # values to be written to the variable font. + assert _extract_flat_kerning(varfont, class_kerning_table) == { + ("A", ".notdef"): 0, + ("A", "A"): 0, + ("A", "B"): -20, + ("A", "C"): 0, + ("A", "D"): -20, + ("B", ".notdef"): 0, + ("B", "A"): 0, + ("B", "B"): 0, + ("B", "C"): 0, + ("B", "D"): 0, + } + + instance_thin = instantiateVariableFont(varfont, {"wght": 100}) + instance_thin_kerning_table = ( + instance_thin["GPOS"].table.LookupList.Lookup[0].SubTable[0] + ) + assert _extract_flat_kerning(instance_thin, instance_thin_kerning_table) == { + ("A", ".notdef"): 0, + ("A", "A"): 0, + ("A", "B"): 0, + ("A", "C"): 10, + ("A", "D"): 0, + ("B", ".notdef"): 0, + ("B", "A"): 0, + ("B", "B"): 0, + ("B", "C"): 10, + ("B", "D"): 0, + } + + instance_black = instantiateVariableFont(varfont, {"wght": 900}) + instance_black_kerning_table = ( + instance_black["GPOS"].table.LookupList.Lookup[0].SubTable[0] + ) + assert _extract_flat_kerning(instance_black, instance_black_kerning_table) == { + ("A", ".notdef"): 0, + ("A", "A"): 0, + ("A", "B"): 0, + ("A", "C"): 0, + ("A", "D"): 40, + ("B", ".notdef"): 0, + ("B", "A"): 0, + ("B", "B"): 0, + ("B", "C"): 0, + ("B", "D"): 40, + } + def test_load_masters_layerName_without_required_font(): ds = DesignSpaceDocument() @@ -511,5 +604,20 @@ def test_load_masters_layerName_without_required_font(): load_masters(ds) +def _extract_flat_kerning(font, pairpos_table): + extracted_kerning = {} + for glyph_name_1 in pairpos_table.Coverage.glyphs: + class_def_1 = pairpos_table.ClassDef1.classDefs.get(glyph_name_1, 0) + for glyph_name_2 in font.getGlyphOrder(): + class_def_2 = pairpos_table.ClassDef2.classDefs.get(glyph_name_2, 0) + kern_value = ( + pairpos_table.Class1Record[class_def_1] + .Class2Record[class_def_2] + .Value1.XAdvance + ) + extracted_kerning[(glyph_name_1, glyph_name_2)] = kern_value + return extracted_kerning + + if __name__ == "__main__": sys.exit(unittest.main())