335 lines
10 KiB
Python
335 lines
10 KiB
Python
"""
|
|
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()
|