| """ |
| Conversion functions. |
| """ |
| |
| from __future__ import absolute_import, unicode_literals |
| |
| |
| # adapted from the UFO spec |
| |
| def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups): |
| # 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: |
| if not first.startswith("public.kern1."): |
| firstReferencedGroups.add(first) |
| for second in list(seconds.keys()): |
| if second in groups: |
| 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() |