From ff01e27763e1ca582335c1eb86c92033a569dc1b Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Tue, 6 Dec 2011 18:46:31 +0000 Subject: [PATCH] Added a kerning validator. This must be run by the caller since groups are required. This should resolve ticket #8. git-svn-id: http://svn.robofab.com/branches/ufo3k@507 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c --- Lib/ufoLib/__init__.py | 12 ++++++ Lib/ufoLib/validators.py | 87 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/Lib/ufoLib/__init__.py b/Lib/ufoLib/__init__.py index a22d95edf..fa0259530 100755 --- a/Lib/ufoLib/__init__.py +++ b/Lib/ufoLib/__init__.py @@ -352,6 +352,13 @@ class UFOReader(object): def readKerning(self): """ Read kerning.plist. Returns a dict. + + This performs structural validation of the kerning data, + but it does not check the validity of the kerning as + dictated in the UFO spec. To do that, pass the kerning + obtained from this method and the groups obtained from + readGroups to the kerningvalidator function in the + validators module. """ # handle up conversion if self._formatVersion < 3: @@ -834,6 +841,11 @@ class UFOWriter(object): """ Write kerning.plist. This method requires a dict of kerning pairs as an argument. + + This performs basic structural validation of the kerning, + but it does not check for compliance with the spec in + regards to conflicting pairs. The assumption is that the + kerning data being passed is standards compliant. """ invalidFormatMessage = "The kerning is not properly formatted." if not isDictEnough(kerning): diff --git a/Lib/ufoLib/validators.py b/Lib/ufoLib/validators.py index 05d5e786d..d1594fa13 100644 --- a/Lib/ufoLib/validators.py +++ b/Lib/ufoLib/validators.py @@ -3,7 +3,6 @@ import os import calendar - # ------- # Generic # ------- @@ -887,6 +886,92 @@ def groupsValidator(value): d[glyphName] = groupName return True, None +# ------------- +# kerning.plist +# ------------- + +def kerningValidator(kerning, groups): + """ + This validates a passed kerning dictionary + using the provided groups. The validation + checks to make sure that there are no conflicting + glyph + group and group + glyph exceptions. + + >>> groups = { + ... "public.kern1.O" : ["O", "D", "Q"], + ... "public.kern2.E" : ["E", "F"] + ... } + >>> kerning = { + ... ("public.kern1.O", "public.kern2.E") : -100, + ... ("public.kern1.O", "F") : -200, + ... ("D", "F") : -300, + ... } + >>> kerningValidator(kerning, groups) + True + >>> kerning = { + ... ("public.kern1.O", "public.kern2.E") : -100, + ... ("public.kern1.O", "F") : -200, + ... ("Q", "public.kern2.E") : -250, + ... ("D", "F") : -300, + ... } + >>> kerningValidator(kerning, groups) + False + """ + # flatten the groups + flatFirstGroups = {} + flatSecondGroups = {} + for groupName, glyphList in groups.items(): + if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."): + continue + if groupName.startswith("public.kern1."): + d = flatFirstGroups + elif groupName.startswith("public.kern2."): + d = flatSecondGroups + for glyphName in glyphList: + d[glyphName] = groupName + # search for conflicts + for first, second in kerning.keys(): + firstIsGroup = first.startswith("public.kern1.") + secondIsGroup = second.startswith("public.kern2.") + # skip anything other than glyph + group and group + glyph + if firstIsGroup and secondIsGroup: + continue + if not firstIsGroup and not secondIsGroup: + continue + # if the first is a glyph and it isn't in a group, skip + if not firstIsGroup: + if first not in flatFirstGroups: + continue + # if the second is a glyph and it isn't in a group, skip + if not secondIsGroup: + if second not in flatSecondGroups: + continue + # skip unknown things + if firstIsGroup and first not in groups: + continue + if firstIsGroup and second not in flatSecondGroups: + continue + if secondIsGroup and second not in groups: + continue + if secondIsGroup and first not in flatFirstGroups: + continue + # validate group + glyph + if firstIsGroup: + firstOptions = groups[first] + secondGroup = flatSecondGroups[second] + for glyph in firstOptions: + if (glyph, secondGroup) in kerning: + return False + # validate glyph + group + if secondIsGroup: + secondOptions = groups[second] + firstGroup = flatFirstGroups[first] + for glyph in secondOptions: + if (firstGroup, glyph) in kerning: + return False + # fallback + return True + # ------------- # lib.plist/lib # -------------