Repository: googlefonts/ufo2ft
Pull request #333 (2019-05-13 16:26:07)

preProcessor: load custom filters in TTFInterpolatablePreProcessor
when running compileInterpolatableOTFsFromDS, we were already applying any custom
filters in the UFO lib (that's taken care of by compileOTFs via OTFPreProcessor).
However, when running compileInterpolatableTTFsFromDS, we were not applying any custom filters -- now we do, although no guarantee is made as to whether those custom filters will keep interpolation-compatibility in the processed glyph sets.


Patch:
diff --git a/Lib/ufo2ft/preProcessor.py b/Lib/ufo2ft/preProcessor.py
index 59fb47a..bf41967 100644
--- a/Lib/ufo2ft/preProcessor.py
+++ b/Lib/ufo2ft/preProcessor.py
@@ -151,9 +151,16 @@ class TTFInterpolatablePreProcessor(object):
     The constructor takes a list of UFO fonts, and the ``process`` method
     returns the modified glyphsets (list of dicts) in the same order.
 
-    Currently, only the conversion from cubic to quadratic, and the
-    decomposition of mixed contour/component glyphs is performed,
-    and no additional custom filter is applied.
+    The pre-processor performs the conversion from cubic to quadratic on
+    all the UFOs at once, then decomposes mixed contour/component glyphs.
+
+    Additional pre/post custom filter are also applied to each single UFOs,
+    respectively before or after the default filters, if they are specified
+    in the UFO's lib.plist under the private key
+    "com.github.googlei18n.ufo2ft.filters".
+    NOTE: If you use any custom filters, the resulting glyphsets may no longer
+    be interpolation compatible, depending on the particular filter used or
+    whether they are applied to only some vs all of the UFOs.
 
     The ``conversionError``, ``reverseDirection`` and ``rememberCurveType``
     arguments work in the same way as in the ``TTFPreProcessor``.
@@ -191,9 +198,20 @@ class TTFInterpolatablePreProcessor(object):
         self._reverseDirection = reverseDirection
         self._rememberCurveType = rememberCurveType
 
+        self.preFilters, self.postFilters = [], []
+        for ufo in ufos:
+            pre, post = loadFilters(ufo)
+            self.preFilters.append(pre)
+            self.postFilters.append(post)
+
     def process(self):
         from cu2qu.ufo import fonts_to_quadratic
 
+        # first apply all custom pre-filters
+        for funcs, ufo, glyphSet in zip(self.preFilters, self.ufos, self.glyphSets):
+            for func in funcs:
+                func(ufo, glyphSet)
+
         fonts_to_quadratic(
             self.glyphSets,
             max_err=self._conversionErrors,
@@ -206,4 +224,9 @@ class TTFInterpolatablePreProcessor(object):
         for ufo, glyphSet in zip(self.ufos, self.glyphSets):
             decompose(ufo, glyphSet)
 
+        # finally apply all custom post-filters
+        for funcs, ufo, glyphSet in zip(self.postFilters, self.ufos, self.glyphSets):
+            for func in funcs:
+                func(ufo, glyphSet)
+
         return self.glyphSets


Test patch:
diff --git a/tests/preProcessor_test.py b/tests/preProcessor_test.py
index a4ed5ca..a5a11fa 100644
--- a/tests/preProcessor_test.py
+++ b/tests/preProcessor_test.py
@@ -3,6 +3,7 @@ import os
 import logging
 import ufo2ft
 from ufo2ft.preProcessor import TTFPreProcessor, TTFInterpolatablePreProcessor
+from ufo2ft.filters import UFO2FT_FILTERS_KEY
 from cu2qu.ufo import CURVE_TYPE_LIB_KEY
 from fontTools import designspaceLib
 
@@ -115,6 +116,22 @@ class TTFInterpolatablePreProcessorTest(object):
             assert glyph_has_qcurve(ufo1, "c")
             assert glyph_has_qcurve(ufo2, "c")
 
+    def test_custom_filters(self, FontClass):
+        ufo1 = FontClass(getpath("TestFont.ufo"))
+        ufo1.lib[UFO2FT_FILTERS_KEY] = [
+            {"name": "transformations", "kwargs": {"OffsetX": -40}, "pre": True}
+        ]
+        ufo2 = FontClass(getpath("TestFont.ufo"))
+        ufo2.lib[UFO2FT_FILTERS_KEY] = [
+            {"name": "transformations", "kwargs": {"OffsetY": 10}}
+        ]
+        ufos = [ufo1, ufo2]
+
+        glyphSets = TTFInterpolatablePreProcessor(ufos).process()
+
+        assert (glyphSets[0]["a"][0][0].x - glyphSets[1]["a"][0][0].x) == -40
+        assert (glyphSets[1]["a"][0][0].y - glyphSets[0]["a"][0][0].y) == 10
+
 
 class SkipExportGlyphsTest(object):
     def test_skip_export_glyphs_filter(self, FontClass):