Repository: googlefonts/ufo2ft
Pull request #714 (2023-02-28 18:01:47)

Make DottedCircleFilter work on an empty font
Closes https://github.com/googlefonts/ufo2ft/issues/709.


Patch:
diff --git a/Lib/ufo2ft/filters/__init__.py b/Lib/ufo2ft/filters/__init__.py
index 912f337..bc813c7 100644
--- a/Lib/ufo2ft/filters/__init__.py
+++ b/Lib/ufo2ft/filters/__init__.py
@@ -45,6 +45,8 @@ def getFilterClass(filterName, pkg="ufo2ft.filters"):
     # TODO add support for third-party plugin discovery?
     # if filter name is 'Foo Bar', the module should be called 'fooBar'
     filterName = filterName.replace(" ", "")
+    if filterName.endswith("Filter"):
+        filterName = filterName[: -len("Filter")]
     moduleName = filterName[0].lower() + filterName[1:]
     module = importlib.import_module(".".join([pkg, moduleName]))
     # if filter name is 'Foo Bar', the class should be called 'FooBarFilter'
diff --git a/Lib/ufo2ft/filters/dottedCircleFilter.py b/Lib/ufo2ft/filters/dottedCircleFilter.py
index 3fa70d4..553ace0 100644
--- a/Lib/ufo2ft/filters/dottedCircleFilter.py
+++ b/Lib/ufo2ft/filters/dottedCircleFilter.py
@@ -53,6 +53,7 @@ from ufo2ft.constants import OPENTYPE_CATEGORIES_KEY
 from ufo2ft.featureCompiler import parseLayoutFeatures
 from ufo2ft.featureWriters import ast
 from ufo2ft.filters import BaseFilter
+from ufo2ft.fontInfoData import getAttrWithFallback
 from ufo2ft.util import _getNewGlyphFactory, _GlyphSet, _LazyFontName, _setGlyphMargin
 
 logger = logging.getLogger(__name__)
@@ -152,11 +153,12 @@ class DottedCircleFilter(BaseFilter):
         glyph = _getNewGlyphFactory(proto)(name="uni25CC", unicodes=[0x25CC])
         pen = glyph.getPen()
 
-        bigradius = (font.info.xHeight - 2 * self.options.margin) / 2
+        xHeight = getAttrWithFallback(font.info, "xHeight")
+        bigradius = (xHeight - 2 * self.options.margin) / 2
         littleradius = bigradius / 6
         left = self.options.sidebearing + littleradius
         right = self.options.sidebearing + bigradius * 2 - littleradius
-        middleY = font.info.xHeight / 2
+        middleY = xHeight / 2
         middleX = (left + right) / 2
         subangle = 2 * math.pi / self.options.dots
         for t in range(self.options.dots):


Test patch:
diff --git a/tests/filters/dottedCircle_test.py b/tests/filters/dottedCircle_test.py
index 9f9f4e5..980d367 100644
--- a/tests/filters/dottedCircle_test.py
+++ b/tests/filters/dottedCircle_test.py
@@ -1,5 +1,6 @@
 from ufo2ft.filters.dottedCircleFilter import DottedCircleFilter
 from ufo2ft.util import _GlyphSet
+from ufo2ft.filters import loadFilters
 
 
 def test_dotted_circle_filter(FontClass, datadir):
@@ -33,3 +34,21 @@ def test_dotted_circle_filter(FontClass, datadir):
     assert len(dotted_circle) == 12
     assert int(dotted_circle.width) == 688
     assert dotted_circle.unicodes == [0x25CC]
+
+
+def test_empty_font(FontClass):
+    """Check that the filter works on an empty font, i.e. uses fallbacks where
+    appropriate."""
+
+    font = FontClass()
+    font.lib["com.github.googlei18n.ufo2ft.filters"] = [
+        {"name": "DottedCircleFilter", "pre": True}
+    ]
+
+    pre_filters, _ = loadFilters(font)
+    (philter,) = pre_filters
+    glyphset = _GlyphSet.from_layer(font)
+
+    modified = philter(font, glyphset)
+
+    assert "uni25CC" in modified
diff --git a/tests/filters/filters_test.py b/tests/filters/filters_test.py
index 2fe9ab3..be2425e 100644
--- a/tests/filters/filters_test.py
+++ b/tests/filters/filters_test.py
@@ -38,6 +38,7 @@ def fooBar():
 def test_getFilterClass():
     assert getFilterClass("Foo Bar") == FooBarFilter
     assert getFilterClass("FooBar") == FooBarFilter
+    assert getFilterClass("FooBarFilter") == FooBarFilter
     assert getFilterClass("fooBar") == FooBarFilter
     with pytest.raises(ImportError):
         getFilterClass("Baz")