diff --git a/Lib/ufo2ft/featureWriters/kernFeatureWriter.py b/Lib/ufo2ft/featureWriters/kernFeatureWriter.py index 524c4ddd..aa4695fd 100644 --- a/Lib/ufo2ft/featureWriters/kernFeatureWriter.py +++ b/Lib/ufo2ft/featureWriters/kernFeatureWriter.py @@ -402,7 +402,13 @@ def getKerningPairs( side2Classes: Mapping[str, tuple[str, ...]], ) -> list[KerningPair]: if self.context.isVariable: - return self.getVariableKerningPairs(side1Classes, side2Classes) + return self.getVariableKerningPairs( + self.context.font, + side1Classes, + side2Classes, + self.context.glyphSet, + self.options, + ) glyphSet = self.context.glyphSet font = self.context.font @@ -432,14 +438,15 @@ def getKerningPairs( return result + @staticmethod def getVariableKerningPairs( - self, + designspace: DesignSpaceDocument, side1Classes: Mapping[str, tuple[str, ...]], side2Classes: Mapping[str, tuple[str, ...]], + glyphSet: Mapping[str, str], + options: SimpleNamespace, ) -> list[KerningPair]: - designspace: DesignSpaceDocument = self.context.font - glyphSet = self.context.glyphSet - quantization = self.options.quantization + quantization = options.quantization # Gather utility variables for faster kerning lookups. # TODO: Do we construct these in code elsewhere? diff --git a/Lib/ufo2ft/featureWriters/kernFeatureWriter2.py b/Lib/ufo2ft/featureWriters/kernFeatureWriter2.py index 82fab4db..13c8c959 100644 --- a/Lib/ufo2ft/featureWriters/kernFeatureWriter2.py +++ b/Lib/ufo2ft/featureWriters/kernFeatureWriter2.py @@ -1,11 +1,18 @@ """Old implementation of KernFeatureWriter as of ufo2ft v2.30.0 for backward compat.""" +from __future__ import annotations + from types import SimpleNamespace +from typing import Mapping from fontTools import unicodedata +from fontTools.designspaceLib import DesignSpaceDocument from ufo2ft.constants import INDIC_SCRIPTS, USE_SCRIPTS from ufo2ft.featureWriters import BaseFeatureWriter, ast +from ufo2ft.featureWriters.kernFeatureWriter import ( + KernFeatureWriter as NewKernFeatureWriter, +) from ufo2ft.util import classifyGlyphs, quantize, unicodeScriptDirection SIDE1_PREFIX = "public.kern1." @@ -113,7 +120,9 @@ class KernFeatureWriter(BaseFeatureWriter): def setContext(self, font, feaFile, compiler=None): ctx = super().setContext(font, feaFile, compiler=compiler) ctx.gdefClasses = self.getGDEFGlyphClasses() - ctx.kerning = self.getKerningData(font, feaFile, self.getOrderedGlyphSet()) + ctx.kerning = self.getKerningData( + font, self.options, feaFile, self.getOrderedGlyphSet() + ) feaScripts = ast.getScriptLanguageSystems(feaFile) ctx.scriptGroups = self._groupScriptsByTagAndDirection(feaScripts) @@ -168,9 +177,9 @@ def _write(self): return True @classmethod - def getKerningData(cls, font, feaFile=None, glyphSet=None): + def getKerningData(cls, font, options, feaFile=None, glyphSet=None): side1Classes, side2Classes = cls.getKerningClasses(font, feaFile, glyphSet) - pairs = cls.getKerningPairs(font, side1Classes, side2Classes, glyphSet) + pairs = cls.getKerningPairs(font, side1Classes, side2Classes, glyphSet, options) return SimpleNamespace( side1Classes=side1Classes, side2Classes=side2Classes, pairs=pairs ) @@ -183,6 +192,13 @@ def getKerningGroups(font, glyphSet=None): allGlyphs = set(font.keys()) side1Groups = {} side2Groups = {} + + if isinstance(font, DesignSpaceDocument): + default_font = font.findDefault() + assert default_font is not None + font = default_font.font + assert font is not None + for name, members in font.groups.items(): # prune non-existent or skipped glyphs members = [g for g in members if g in allGlyphs] @@ -208,7 +224,35 @@ def getKerningClasses(cls, font, feaFile=None, glyphSet=None): return side1Classes, side2Classes @staticmethod - def getKerningPairs(font, side1Classes, side2Classes, glyphSet=None): + def getKerningPairs(font, side1Classes, side2Classes, glyphSet=None, options=None): + if isinstance(font, DesignSpaceDocument): + # Reuse the newer kern writers variable kerning extractor. Repack + # some arguments and the return type for this. + side1ClassesRaw: Mapping[str, tuple[str, ...]] = { + group_name: tuple( + glyph + for glyphs in glyph_defs.glyphSet() + for glyph in glyphs.glyphSet() + ) + for group_name, glyph_defs in side1Classes.items() + } + side2ClassesRaw: Mapping[str, tuple[str, ...]] = { + group_name: tuple( + glyph + for glyphs in glyph_defs.glyphSet() + for glyph in glyphs.glyphSet() + ) + for group_name, glyph_defs in side2Classes.items() + } + pairs = NewKernFeatureWriter.getVariableKerningPairs( + font, + side1ClassesRaw, + side2ClassesRaw, + glyphSet or {}, + options or SimpleNamespace(**KernFeatureWriter.options), + ) + return [KerningPair(pair.side1, pair.side2, pair.value) for pair in pairs] + if glyphSet: allGlyphs = set(glyphSet.keys()) else: diff --git a/tests/featureWriters/variableFeatureWriter_test.py b/tests/featureWriters/variableFeatureWriter_test.py index 5ce55779..077fd4f3 100644 --- a/tests/featureWriters/variableFeatureWriter_test.py +++ b/tests/featureWriters/variableFeatureWriter_test.py @@ -59,3 +59,76 @@ def test_variable_features(FontClass): } curs; """ # noqa: B950 ) + + +def test_variable_features_old_kern_writer(FontClass): + tmp = io.StringIO() + designspace = designspaceLib.DesignSpaceDocument.fromfile( + "tests/data/TestVarfea.designspace" + ) + designspace.loadSourceFonts(FontClass) + + default_source = designspace.findDefault() + assert default_source is not None + default_ufo = default_source.font + assert default_ufo is not None + default_ufo.lib["com.github.googlei18n.ufo2ft.featureWriters"] = [ + { + "module": "ufo2ft.featureWriters.kernFeatureWriter2", + "class": "KernFeatureWriter", + }, + { + "module": "ufo2ft.featureWriters.markFeatureWriter", + "class": "MarkFeatureWriter", + }, + { + "module": "ufo2ft.featureWriters.gdefFeatureWriter", + "class": "GdefFeatureWriter", + }, + { + "module": "ufo2ft.featureWriters.cursFeatureWriter", + "class": "CursFeatureWriter", + }, + ] + + _ = compileVariableTTF(designspace, debugFeatureFile=tmp) + + assert dedent("\n" + tmp.getvalue()) == dedent( + """ + markClass dotabove-ar @MC_top; + markClass gravecmb @MC_top; + + lookup kern_rtl { + lookupflag IgnoreMarks; + pos alef-ar.fina alef-ar.fina <(wght=100:15 wght=1000:35) 0 (wght=100:15 wght=1000:35) 0>; + } kern_rtl; + + feature kern { + lookup kern_rtl; + } kern; + + feature mark { + lookup mark2base { + pos base alef-ar.fina + mark @MC_top; + pos base a + mark @MC_top; + } mark2base; + + } mark; + + table GDEF { + LigatureCaretByPos peh-ar.init 100; + } GDEF; + + feature curs { + lookup curs_rtl { + lookupflag RightToLeft IgnoreMarks; + pos cursive alef-ar.fina ; + pos cursive peh-ar.init ; + pos cursive peh-ar.init.BRACKET.varAlt01 ; + } curs_rtl; + + } curs; +""" # noqa: B950 + )