From a829b0dc72cbc2895c6c503c917ac64ced8c55cb Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Mon, 3 Oct 2011 15:17:31 +0000 Subject: [PATCH] Validate the groups. git-svn-id: http://svn.robofab.com/branches/ufo3k@362 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c --- Lib/ufoLib/__init__.py | 47 +++++++++++++------------- Lib/ufoLib/validators.py | 71 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/Lib/ufoLib/__init__.py b/Lib/ufoLib/__init__.py index 3118be545..71cdcb1d5 100755 --- a/Lib/ufoLib/__init__.py +++ b/Lib/ufoLib/__init__.py @@ -126,11 +126,23 @@ class UFOReader(object): if testGroups != self._upConvertedKerningData["originalGroups"]: raise UFOLibError("The data in groups.plist has been modified since it was converted to UFO 3 format.") else: + groups = self._readGroups() + invalidFormatMessage = "groups.plist is not properly formatted." + if not isinstance(data, dict): + raise UFOLibError(invalidFormatMessage) + for groupName, glyphList in data.items(): + if not isinstance(groupName, basestring): + raise UFOLibError(invalidFormatMessage) + elif not isinstance(glyphList, list): + raise UFOLibError(invalidFormatMessage) + for glyphName in glyphList: + if not isinstance(glyphName, basestring): + raise UFOLibError(invalidFormatMessage) self._upConvertedKerningData = dict( kerning={}, originalKerning=self._readKerning(), groups={}, - originalGroups=self._readGroups() + originalGroups=groups ) # convert kerning and groups kerning, groups = convertUFO1OrUFO2KerningToUFO3Kerning( @@ -223,17 +235,6 @@ class UFOReader(object): if not self._checkForFile(path): return {} data = self._readPlist(path) - invalidFormatMessage = "groups.plist is not properly formatted." - if not isinstance(data, dict): - raise UFOLibError(invalidFormatMessage) - for groupName, glyphList in data.items(): - if not isinstance(groupName, basestring): - raise UFOLibError(invalidFormatMessage) - elif not isinstance(glyphList, list): - raise UFOLibError(invalidFormatMessage) - for glyphName in glyphList: - if not isinstance(glyphName, basestring): - raise UFOLibError(invalidFormatMessage) return data def readGroups(self): @@ -243,10 +244,14 @@ class UFOReader(object): # handle up conversion if self._formatVersion < 3: self._upConvertKerning() - return self._upConvertedKerningData["groups"] + groups = self._upConvertedKerningData["groups"] # normal else: - return self._readGroups() + groups = self._readGroups() + valid, message = validateGroups(groups) + if not valid: + raise UFOLibError(message) + return groups # fontinfo.plist @@ -714,17 +719,9 @@ class UFOWriter(object): Write groups.plist. This method requires a dict of glyph groups as an argument. """ - invalidFormatMessage = "The groups are not properly formatted." - if not isinstance(groups, dict): - raise UFOLibError(invalidFormatMessage) - for groupName, glyphList in groups.items(): - if not isinstance(groupName, basestring): - raise UFOLibError(invalidFormatMessage) - if not isinstance(glyphList, list): - raise UFOLibError(invalidFormatMessage) - for glyphName in glyphList: - if not isinstance(glyphName, basestring): - raise UFOLibError(invalidFormatMessage) + valid, message = validateGroups(groups) + if not valid: + raise UFOLibError(message) self._makeDirectory() path = os.path.join(self._path, GROUPS_FILENAME) groupsNew = {} diff --git a/Lib/ufoLib/validators.py b/Lib/ufoLib/validators.py index 0e556844f..f5e158f0b 100644 --- a/Lib/ufoLib/validators.py +++ b/Lib/ufoLib/validators.py @@ -673,7 +673,6 @@ def imageValidator(value): return False return True - # ------------------- # layercontents.plist # ------------------- @@ -732,6 +731,76 @@ def layerContentsValidator(value, ufoPath): return False, "The required default glyph set is not in the UFO." return True, None +# ------------ +# groups.plist +# ------------ + +def validateGroups(value): + """ + >>> groups = {"A" : ["A", "A"], "A2" : ["A"]} + >>> validateGroups(groups) + (True, None) + + >>> groups = {"" : ["A"]} + >>> validateGroups(groups) + (False, 'A group has an empty name.') + + >>> groups = {"public.awesome" : ["A"]} + >>> validateGroups(groups) + (False, 'The group data contains a group with an illegal public.* group name.') + + >>> groups = {"public.kern1." : ["A"]} + >>> validateGroups(groups) + (False, 'The group data contains a kerning group with an incomplete name.') + >>> groups = {"public.kern2." : ["A"]} + >>> validateGroups(groups) + (False, 'The group data contains a kerning group with an incomplete name.') + + >>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]} + >>> validateGroups(groups) + (True, None) + + >>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]} + >>> validateGroups(groups) + (False, 'The glyph "A" occurs in too many kerning groups.') + """ + bogusFormatMessage = "The group data is not in the correct format." + if not isinstance(value, dict): + return False, bogusFormatMessage + firstSideMapping = {} + secondSideMapping = {} + for groupName, glyphList in value.items(): + if not isinstance(groupName, basestring): + return False, bogusFormatMessage + if not isinstance(glyphList, (list, tuple)): + return False, bogusFormatMessage + if not groupName: + return False, "A group has an empty name." + if groupName.startswith("public."): + if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."): + return False, "The group data contains a group with an illegal public.* group name." + else: + if len("public.kernN.") == len(groupName): + return False, "The group data contains a kerning group with an incomplete name." + if groupName.startswith("public.kern1."): + d = firstSideMapping + else: + d = secondSideMapping + for glyphName in glyphList: + if not isinstance(glyphName, basestring): + return False, "The group data %s contains an invalid member." % groupName + if glyphName in d: + return False, "The glyph \"%s\" occurs in too many kerning groups." % glyphName + d[glyphName] = groupName + return True, None + + +# ------------- +# lib.plist/lib +# ------------- + + + if __name__ == "__main__": import doctest doctest.testmod()