From 5a64229646b07858c976f0deebbafc4e5c286951 Mon Sep 17 00:00:00 2001 From: Ben Kiel Date: Wed, 23 Jun 2021 08:35:22 -0500 Subject: [PATCH 1/2] Test groups for equality before doing fontMath kerning math. If not equal, throw a value error. --- Lib/fontMath/mathKerning.py | 16 +++------ Lib/fontMath/test/test_mathKerning.py | 49 ++++++++++++++++----------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/Lib/fontMath/mathKerning.py b/Lib/fontMath/mathKerning.py index dd25bbb..4f7ebbb 100644 --- a/Lib/fontMath/mathKerning.py +++ b/Lib/fontMath/mathKerning.py @@ -168,6 +168,10 @@ def __sub__(self, other): return k def _processMathOne(self, other, funct): + g1 = self.groups() + g2 = other.groups() + if g1 != g2: + raise ValueError("Kerning groups must be exactly the same.") comboPairs = set(self._kerning.keys()) | set(other._kerning.keys()) kerning = dict.fromkeys(comboPairs, None) for k in comboPairs: @@ -175,17 +179,7 @@ def _processMathOne(self, other, funct): v2 = other.get(k) v = funct(v1, v2) kerning[k] = v - g1 = self.groups() - g2 = other.groups() - if g1 == g2 or not g1 or not g2: - groups = g1 or g2 - else: - comboGroups = set(g1.keys()) | set(g2.keys()) - groups = dict.fromkeys(comboGroups, None) - for groupName in comboGroups: - s1 = set(g1.get(groupName, [])) - s2 = set(g2.get(groupName, [])) - groups[groupName] = sorted(list(s1 | s2)) + groups = g1 or g2 ks = MathKerning(kerning, groups) return ks diff --git a/Lib/fontMath/test/test_mathKerning.py b/Lib/fontMath/test/test_mathKerning.py index e7fdcb0..f712e09 100644 --- a/Lib/fontMath/test/test_mathKerning.py +++ b/Lib/fontMath/test/test_mathKerning.py @@ -115,7 +115,7 @@ def test_copy(self): obj2 = obj1.copy() self.assertEqual(sorted(obj1.items()), sorted(obj2.items())) - def test_add(self): + def test_add_different_groups(self): kerning1 = { ("A", "A"): 1, ("B", "B"): 1, @@ -140,21 +140,8 @@ def test_add(self): "public.kern1.D": ["D", "H"], "public.kern2.D": ["D", "H"], } - obj = MathKerning(kerning1, groups1) + MathKerning(kerning2, groups2) - self.assertEqual( - sorted(obj.items()), - [(('B', 'B'), 2), - (('NotIn1', 'NotIn1'), 1), - (('NotIn2', 'NotIn2'), 1), - (('public.kern1.D', 'public.kern2.D'), 2), - (('public.kern1.NotIn1', 'C'), 1), - (('public.kern1.NotIn2', 'C'), 1)]) - self.assertEqual( - obj.groups()["public.kern1.D"], - ['D', 'H']) - self.assertEqual( - obj.groups()["public.kern2.D"], - ['D', 'H']) + with self.assertRaises(ValueError): + obj = MathKerning(kerning1, groups1) + MathKerning(kerning2, groups2) def test_add_same_groups(self): kerning1 = { @@ -195,7 +182,7 @@ def test_add_same_groups(self): obj.groups()["public.kern2.D"], ['D', 'H']) - def test_sub(self): + def test_sub_different_groups(self): kerning1 = { ("A", "A"): 1, ("B", "B"): 1, @@ -220,14 +207,36 @@ def test_sub(self): "public.kern1.D": ["D"], "public.kern2.D": ["D", "H"], } + with self.assertRaises(ValueError): + obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2) + + def test_sub_same_groups(self): + kerning1 = { + ("A", "A"): 1, + ("B", "B"): 1, + ("NotIn2", "NotIn2"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups1 = { + "public.kern1.D": ["D", "H"], + "public.kern2.D": ["D", "H"], + } + kerning2 = { + ("A", "A"): -1, + ("B", "B"): 1, + ("NotIn1", "NotIn1"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups2 = { + "public.kern1.D": ["D", "H"], + "public.kern2.D": ["D", "H"], + } obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2) self.assertEqual( sorted(obj.items()), [(('A', 'A'), 2), (('NotIn1', 'NotIn1'), -1), - (('NotIn2', 'NotIn2'), 1), - (('public.kern1.NotIn1', 'C'), -1), - (('public.kern1.NotIn2', 'C'), 1)]) + (('NotIn2', 'NotIn2'), 1)]) self.assertEqual( obj.groups()["public.kern1.D"], ['D', 'H']) From 4852acfdd48be2b1abc34308d94e087620930c09 Mon Sep 17 00:00:00 2001 From: Ben Kiel Date: Wed, 23 Jun 2021 09:10:57 -0500 Subject: [PATCH 2/2] Add a `strictGroups` flag to turn on strict group checking --- Lib/fontMath/mathKerning.py | 18 +++-- Lib/fontMath/test/test_mathKerning.py | 95 +++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/Lib/fontMath/mathKerning.py b/Lib/fontMath/mathKerning.py index 4f7ebbb..47958cf 100644 --- a/Lib/fontMath/mathKerning.py +++ b/Lib/fontMath/mathKerning.py @@ -19,13 +19,14 @@ class kerning dictionary. class MathKerning(object): - def __init__(self, kerning=None, groups=None): + def __init__(self, kerning=None, groups=None, strictGroups=False): if kerning is None: kerning = {} if groups is None: groups = {} self.update(kerning) self.updateGroups(groups) + self.strictGroups = strictGroups # ------- # Loading @@ -170,8 +171,9 @@ def __sub__(self, other): def _processMathOne(self, other, funct): g1 = self.groups() g2 = other.groups() - if g1 != g2: - raise ValueError("Kerning groups must be exactly the same.") + if self.strictGroups or other.strictGroups: + if g1 != g2: + raise ValueError("Kerning groups must be exactly the same.") comboPairs = set(self._kerning.keys()) | set(other._kerning.keys()) kerning = dict.fromkeys(comboPairs, None) for k in comboPairs: @@ -179,7 +181,15 @@ def _processMathOne(self, other, funct): v2 = other.get(k) v = funct(v1, v2) kerning[k] = v - groups = g1 or g2 + if g1 == g2 or not g1 or not g2: + groups = g1 or g2 + else: + comboGroups = set(g1.keys()) | set(g2.keys()) + groups = dict.fromkeys(comboGroups, None) + for groupName in comboGroups: + s1 = set(g1.get(groupName, [])) + s2 = set(g2.get(groupName, [])) + groups[groupName] = sorted(list(s1 | s2)) ks = MathKerning(kerning, groups) return ks diff --git a/Lib/fontMath/test/test_mathKerning.py b/Lib/fontMath/test/test_mathKerning.py index f712e09..183fdca 100644 --- a/Lib/fontMath/test/test_mathKerning.py +++ b/Lib/fontMath/test/test_mathKerning.py @@ -115,7 +115,7 @@ def test_copy(self): obj2 = obj1.copy() self.assertEqual(sorted(obj1.items()), sorted(obj2.items())) - def test_add_different_groups(self): + def test_add_different_groups_strict(self): kerning1 = { ("A", "A"): 1, ("B", "B"): 1, @@ -141,7 +141,50 @@ def test_add_different_groups(self): "public.kern2.D": ["D", "H"], } with self.assertRaises(ValueError): - obj = MathKerning(kerning1, groups1) + MathKerning(kerning2, groups2) + obj = MathKerning(kerning1, groups1, strictGroups=True) + MathKerning(kerning2, groups2) + with self.assertRaises(ValueError): + obj = MathKerning(kerning1, groups1) + MathKerning(kerning2, groups2, strictGroups=True) + + def test_add_different_groups(self): + kerning1 = { + ("A", "A"): 1, + ("B", "B"): 1, + ("NotIn2", "NotIn2"): 1, + ("public.kern1.NotIn2", "C"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups1 = { + "public.kern1.NotIn1": ["C"], + "public.kern1.D": ["D", "H"], + "public.kern2.D": ["D", "H"], + } + kerning2 = { + ("A", "A"): -1, + ("B", "B"): 1, + ("NotIn1", "NotIn1"): 1, + ("public.kern1.NotIn1", "C"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups2 = { + "public.kern1.NotIn2": ["C"], + "public.kern1.D": ["D", "H"], + "public.kern2.D": ["D", "H"], + } + obj = MathKerning(kerning1, groups1) + MathKerning(kerning2, groups2) + self.assertEqual( + sorted(obj.items()), + [(('B', 'B'), 2), + (('NotIn1', 'NotIn1'), 1), + (('NotIn2', 'NotIn2'), 1), + (('public.kern1.D', 'public.kern2.D'), 2), + (('public.kern1.NotIn1', 'C'), 1), + (('public.kern1.NotIn2', 'C'), 1)]) + self.assertEqual( + obj.groups()["public.kern1.D"], + ['D', 'H']) + self.assertEqual( + obj.groups()["public.kern2.D"], + ['D', 'H']) def test_add_same_groups(self): kerning1 = { @@ -182,7 +225,7 @@ def test_add_same_groups(self): obj.groups()["public.kern2.D"], ['D', 'H']) - def test_sub_different_groups(self): + def test_sub_different_groups_strict(self): kerning1 = { ("A", "A"): 1, ("B", "B"): 1, @@ -208,16 +251,20 @@ def test_sub_different_groups(self): "public.kern2.D": ["D", "H"], } with self.assertRaises(ValueError): - obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2) + obj = MathKerning(kerning1, groups1, strictGroups=True) - MathKerning(kerning2, groups2) + with self.assertRaises(ValueError): + obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2, strictGroups=True) - def test_sub_same_groups(self): + def test_sub_different_groups(self): kerning1 = { ("A", "A"): 1, ("B", "B"): 1, ("NotIn2", "NotIn2"): 1, + ("public.kern1.NotIn2", "C"): 1, ("public.kern1.D", "public.kern2.D"): 1, } groups1 = { + "public.kern1.NotIn1": ["C"], "public.kern1.D": ["D", "H"], "public.kern2.D": ["D", "H"], } @@ -225,12 +272,50 @@ def test_sub_same_groups(self): ("A", "A"): -1, ("B", "B"): 1, ("NotIn1", "NotIn1"): 1, + ("public.kern1.NotIn1", "C"): 1, ("public.kern1.D", "public.kern2.D"): 1, } groups2 = { + "public.kern1.NotIn2": ["C"], + "public.kern1.D": ["D"], + "public.kern2.D": ["D", "H"], + } + obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2) + self.assertEqual( + sorted(obj.items()), + [(('A', 'A'), 2), + (('NotIn1', 'NotIn1'), -1), + (('NotIn2', 'NotIn2'), 1), + (('public.kern1.NotIn1', 'C'), -1), + (('public.kern1.NotIn2', 'C'), 1)]) + self.assertEqual( + obj.groups()["public.kern1.D"], + ['D', 'H']) + self.assertEqual( + obj.groups()["public.kern2.D"], + ['D', 'H']) + + def test_sub_same_groups(self): + kerning1 = { + ("A", "A"): 1, + ("B", "B"): 1, + ("NotIn2", "NotIn2"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups1 = { "public.kern1.D": ["D", "H"], "public.kern2.D": ["D", "H"], } + kerning2 = { + ("A", "A"): -1, + ("B", "B"): 1, + ("NotIn1", "NotIn1"): 1, + ("public.kern1.D", "public.kern2.D"): 1, + } + groups2 = { + "public.kern1.D": ["D"], + "public.kern2.D": ["D", "H"], + } obj = MathKerning(kerning1, groups1) - MathKerning(kerning2, groups2) self.assertEqual( sorted(obj.items()),