123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- """
- Conversion functions.
- """
- # adapted from the UFO spec
- def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
- # gather known kerning groups based on the prefixes
- firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
- # Make lists of groups referenced in kerning pairs.
- for first, seconds in list(kerning.items()):
- if first in groups and first not in glyphSet:
- if not first.startswith("public.kern1."):
- firstReferencedGroups.add(first)
- for second in list(seconds.keys()):
- if second in groups and second not in glyphSet:
- if not second.startswith("public.kern2."):
- secondReferencedGroups.add(second)
- # Create new names for these groups.
- firstRenamedGroups = {}
- for first in firstReferencedGroups:
- # Make a list of existing group names.
- existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
- # Remove the old prefix from the name
- newName = first.replace("@MMK_L_", "")
- # Add the new prefix to the name.
- newName = "public.kern1." + newName
- # Make a unique group name.
- newName = makeUniqueGroupName(newName, existingGroupNames)
- # Store for use later.
- firstRenamedGroups[first] = newName
- secondRenamedGroups = {}
- for second in secondReferencedGroups:
- # Make a list of existing group names.
- existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
- # Remove the old prefix from the name
- newName = second.replace("@MMK_R_", "")
- # Add the new prefix to the name.
- newName = "public.kern2." + newName
- # Make a unique group name.
- newName = makeUniqueGroupName(newName, existingGroupNames)
- # Store for use later.
- secondRenamedGroups[second] = newName
- # Populate the new group names into the kerning dictionary as needed.
- newKerning = {}
- for first, seconds in list(kerning.items()):
- first = firstRenamedGroups.get(first, first)
- newSeconds = {}
- for second, value in list(seconds.items()):
- second = secondRenamedGroups.get(second, second)
- newSeconds[second] = value
- newKerning[first] = newSeconds
- # Make copies of the referenced groups and store them
- # under the new names in the overall groups dictionary.
- allRenamedGroups = list(firstRenamedGroups.items())
- allRenamedGroups += list(secondRenamedGroups.items())
- for oldName, newName in allRenamedGroups:
- group = list(groups[oldName])
- groups[newName] = group
- # Return the kerning and the groups.
- return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
- def findKnownKerningGroups(groups):
- """
- This will find kerning groups with known prefixes.
- In some cases not all kerning groups will be referenced
- by the kerning pairs. The algorithm for locating groups
- in convertUFO1OrUFO2KerningToUFO3Kerning will miss these
- unreferenced groups. By scanning for known prefixes
- this function will catch all of the prefixed groups.
- These are the prefixes and sides that are handled:
- @MMK_L_ - side 1
- @MMK_R_ - side 2
- >>> testGroups = {
- ... "@MMK_L_1" : None,
- ... "@MMK_L_2" : None,
- ... "@MMK_L_3" : None,
- ... "@MMK_R_1" : None,
- ... "@MMK_R_2" : None,
- ... "@MMK_R_3" : None,
- ... "@MMK_l_1" : None,
- ... "@MMK_r_1" : None,
- ... "@MMK_X_1" : None,
- ... "foo" : None,
- ... }
- >>> first, second = findKnownKerningGroups(testGroups)
- >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
- True
- >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
- True
- """
- knownFirstGroupPrefixes = ["@MMK_L_"]
- knownSecondGroupPrefixes = ["@MMK_R_"]
- firstGroups = set()
- secondGroups = set()
- for groupName in list(groups.keys()):
- for firstPrefix in knownFirstGroupPrefixes:
- if groupName.startswith(firstPrefix):
- firstGroups.add(groupName)
- break
- for secondPrefix in knownSecondGroupPrefixes:
- if groupName.startswith(secondPrefix):
- secondGroups.add(groupName)
- break
- return firstGroups, secondGroups
- def makeUniqueGroupName(name, groupNames, counter=0):
- # Add a number to the name if the counter is higher than zero.
- newName = name
- if counter > 0:
- newName = "%s%d" % (newName, counter)
- # If the new name is in the existing group names, recurse.
- if newName in groupNames:
- return makeUniqueGroupName(name, groupNames, counter + 1)
- # Otherwise send back the new name.
- return newName
- def test():
- """
- No known prefixes.
- >>> testKerning = {
- ... "A" : {
- ... "A" : 1,
- ... "B" : 2,
- ... "CGroup" : 3,
- ... "DGroup" : 4
- ... },
- ... "BGroup" : {
- ... "A" : 5,
- ... "B" : 6,
- ... "CGroup" : 7,
- ... "DGroup" : 8
- ... },
- ... "CGroup" : {
- ... "A" : 9,
- ... "B" : 10,
- ... "CGroup" : 11,
- ... "DGroup" : 12
- ... },
- ... }
- >>> testGroups = {
- ... "BGroup" : ["B"],
- ... "CGroup" : ["C"],
- ... "DGroup" : ["D"],
- ... }
- >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
- ... testKerning, testGroups, [])
- >>> expected = {
- ... "A" : {
- ... "A": 1,
- ... "B": 2,
- ... "public.kern2.CGroup": 3,
- ... "public.kern2.DGroup": 4
- ... },
- ... "public.kern1.BGroup": {
- ... "A": 5,
- ... "B": 6,
- ... "public.kern2.CGroup": 7,
- ... "public.kern2.DGroup": 8
- ... },
- ... "public.kern1.CGroup": {
- ... "A": 9,
- ... "B": 10,
- ... "public.kern2.CGroup": 11,
- ... "public.kern2.DGroup": 12
- ... }
- ... }
- >>> kerning == expected
- True
- >>> expected = {
- ... "BGroup": ["B"],
- ... "CGroup": ["C"],
- ... "DGroup": ["D"],
- ... "public.kern1.BGroup": ["B"],
- ... "public.kern1.CGroup": ["C"],
- ... "public.kern2.CGroup": ["C"],
- ... "public.kern2.DGroup": ["D"],
- ... }
- >>> groups == expected
- True
- Known prefixes.
- >>> testKerning = {
- ... "A" : {
- ... "A" : 1,
- ... "B" : 2,
- ... "@MMK_R_CGroup" : 3,
- ... "@MMK_R_DGroup" : 4
- ... },
- ... "@MMK_L_BGroup" : {
- ... "A" : 5,
- ... "B" : 6,
- ... "@MMK_R_CGroup" : 7,
- ... "@MMK_R_DGroup" : 8
- ... },
- ... "@MMK_L_CGroup" : {
- ... "A" : 9,
- ... "B" : 10,
- ... "@MMK_R_CGroup" : 11,
- ... "@MMK_R_DGroup" : 12
- ... },
- ... }
- >>> testGroups = {
- ... "@MMK_L_BGroup" : ["B"],
- ... "@MMK_L_CGroup" : ["C"],
- ... "@MMK_L_XGroup" : ["X"],
- ... "@MMK_R_CGroup" : ["C"],
- ... "@MMK_R_DGroup" : ["D"],
- ... "@MMK_R_XGroup" : ["X"],
- ... }
- >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
- ... testKerning, testGroups, [])
- >>> expected = {
- ... "A" : {
- ... "A": 1,
- ... "B": 2,
- ... "public.kern2.CGroup": 3,
- ... "public.kern2.DGroup": 4
- ... },
- ... "public.kern1.BGroup": {
- ... "A": 5,
- ... "B": 6,
- ... "public.kern2.CGroup": 7,
- ... "public.kern2.DGroup": 8
- ... },
- ... "public.kern1.CGroup": {
- ... "A": 9,
- ... "B": 10,
- ... "public.kern2.CGroup": 11,
- ... "public.kern2.DGroup": 12
- ... }
- ... }
- >>> kerning == expected
- True
- >>> expected = {
- ... "@MMK_L_BGroup": ["B"],
- ... "@MMK_L_CGroup": ["C"],
- ... "@MMK_L_XGroup": ["X"],
- ... "@MMK_R_CGroup": ["C"],
- ... "@MMK_R_DGroup": ["D"],
- ... "@MMK_R_XGroup": ["X"],
- ... "public.kern1.BGroup": ["B"],
- ... "public.kern1.CGroup": ["C"],
- ... "public.kern1.XGroup": ["X"],
- ... "public.kern2.CGroup": ["C"],
- ... "public.kern2.DGroup": ["D"],
- ... "public.kern2.XGroup": ["X"],
- ... }
- >>> groups == expected
- True
- >>> from .validators import kerningValidator
- >>> kerningValidator(kerning)
- (True, None)
- Mixture of known prefixes and groups without prefixes.
- >>> testKerning = {
- ... "A" : {
- ... "A" : 1,
- ... "B" : 2,
- ... "@MMK_R_CGroup" : 3,
- ... "DGroup" : 4
- ... },
- ... "BGroup" : {
- ... "A" : 5,
- ... "B" : 6,
- ... "@MMK_R_CGroup" : 7,
- ... "DGroup" : 8
- ... },
- ... "@MMK_L_CGroup" : {
- ... "A" : 9,
- ... "B" : 10,
- ... "@MMK_R_CGroup" : 11,
- ... "DGroup" : 12
- ... },
- ... }
- >>> testGroups = {
- ... "BGroup" : ["B"],
- ... "@MMK_L_CGroup" : ["C"],
- ... "@MMK_R_CGroup" : ["C"],
- ... "DGroup" : ["D"],
- ... }
- >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
- ... testKerning, testGroups, [])
- >>> expected = {
- ... "A" : {
- ... "A": 1,
- ... "B": 2,
- ... "public.kern2.CGroup": 3,
- ... "public.kern2.DGroup": 4
- ... },
- ... "public.kern1.BGroup": {
- ... "A": 5,
- ... "B": 6,
- ... "public.kern2.CGroup": 7,
- ... "public.kern2.DGroup": 8
- ... },
- ... "public.kern1.CGroup": {
- ... "A": 9,
- ... "B": 10,
- ... "public.kern2.CGroup": 11,
- ... "public.kern2.DGroup": 12
- ... }
- ... }
- >>> kerning == expected
- True
- >>> expected = {
- ... "BGroup": ["B"],
- ... "@MMK_L_CGroup": ["C"],
- ... "@MMK_R_CGroup": ["C"],
- ... "DGroup": ["D"],
- ... "public.kern1.BGroup": ["B"],
- ... "public.kern1.CGroup": ["C"],
- ... "public.kern2.CGroup": ["C"],
- ... "public.kern2.DGroup": ["D"],
- ... }
- >>> groups == expected
- True
- """
- if __name__ == "__main__":
- import doctest
- doctest.testmod()
|