xref: /aosp_15_r20/external/fonttools/Lib/fontTools/ufoLib/converters.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes"""
2*e1fe3e4aSElliott HughesConversion functions.
3*e1fe3e4aSElliott Hughes"""
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughes# adapted from the UFO spec
6*e1fe3e4aSElliott Hughes
7*e1fe3e4aSElliott Hughes
8*e1fe3e4aSElliott Hughesdef convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
9*e1fe3e4aSElliott Hughes    # gather known kerning groups based on the prefixes
10*e1fe3e4aSElliott Hughes    firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
11*e1fe3e4aSElliott Hughes    # Make lists of groups referenced in kerning pairs.
12*e1fe3e4aSElliott Hughes    for first, seconds in list(kerning.items()):
13*e1fe3e4aSElliott Hughes        if first in groups and first not in glyphSet:
14*e1fe3e4aSElliott Hughes            if not first.startswith("public.kern1."):
15*e1fe3e4aSElliott Hughes                firstReferencedGroups.add(first)
16*e1fe3e4aSElliott Hughes        for second in list(seconds.keys()):
17*e1fe3e4aSElliott Hughes            if second in groups and second not in glyphSet:
18*e1fe3e4aSElliott Hughes                if not second.startswith("public.kern2."):
19*e1fe3e4aSElliott Hughes                    secondReferencedGroups.add(second)
20*e1fe3e4aSElliott Hughes    # Create new names for these groups.
21*e1fe3e4aSElliott Hughes    firstRenamedGroups = {}
22*e1fe3e4aSElliott Hughes    for first in firstReferencedGroups:
23*e1fe3e4aSElliott Hughes        # Make a list of existing group names.
24*e1fe3e4aSElliott Hughes        existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
25*e1fe3e4aSElliott Hughes        # Remove the old prefix from the name
26*e1fe3e4aSElliott Hughes        newName = first.replace("@MMK_L_", "")
27*e1fe3e4aSElliott Hughes        # Add the new prefix to the name.
28*e1fe3e4aSElliott Hughes        newName = "public.kern1." + newName
29*e1fe3e4aSElliott Hughes        # Make a unique group name.
30*e1fe3e4aSElliott Hughes        newName = makeUniqueGroupName(newName, existingGroupNames)
31*e1fe3e4aSElliott Hughes        # Store for use later.
32*e1fe3e4aSElliott Hughes        firstRenamedGroups[first] = newName
33*e1fe3e4aSElliott Hughes    secondRenamedGroups = {}
34*e1fe3e4aSElliott Hughes    for second in secondReferencedGroups:
35*e1fe3e4aSElliott Hughes        # Make a list of existing group names.
36*e1fe3e4aSElliott Hughes        existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
37*e1fe3e4aSElliott Hughes        # Remove the old prefix from the name
38*e1fe3e4aSElliott Hughes        newName = second.replace("@MMK_R_", "")
39*e1fe3e4aSElliott Hughes        # Add the new prefix to the name.
40*e1fe3e4aSElliott Hughes        newName = "public.kern2." + newName
41*e1fe3e4aSElliott Hughes        # Make a unique group name.
42*e1fe3e4aSElliott Hughes        newName = makeUniqueGroupName(newName, existingGroupNames)
43*e1fe3e4aSElliott Hughes        # Store for use later.
44*e1fe3e4aSElliott Hughes        secondRenamedGroups[second] = newName
45*e1fe3e4aSElliott Hughes    # Populate the new group names into the kerning dictionary as needed.
46*e1fe3e4aSElliott Hughes    newKerning = {}
47*e1fe3e4aSElliott Hughes    for first, seconds in list(kerning.items()):
48*e1fe3e4aSElliott Hughes        first = firstRenamedGroups.get(first, first)
49*e1fe3e4aSElliott Hughes        newSeconds = {}
50*e1fe3e4aSElliott Hughes        for second, value in list(seconds.items()):
51*e1fe3e4aSElliott Hughes            second = secondRenamedGroups.get(second, second)
52*e1fe3e4aSElliott Hughes            newSeconds[second] = value
53*e1fe3e4aSElliott Hughes        newKerning[first] = newSeconds
54*e1fe3e4aSElliott Hughes    # Make copies of the referenced groups and store them
55*e1fe3e4aSElliott Hughes    # under the new names in the overall groups dictionary.
56*e1fe3e4aSElliott Hughes    allRenamedGroups = list(firstRenamedGroups.items())
57*e1fe3e4aSElliott Hughes    allRenamedGroups += list(secondRenamedGroups.items())
58*e1fe3e4aSElliott Hughes    for oldName, newName in allRenamedGroups:
59*e1fe3e4aSElliott Hughes        group = list(groups[oldName])
60*e1fe3e4aSElliott Hughes        groups[newName] = group
61*e1fe3e4aSElliott Hughes    # Return the kerning and the groups.
62*e1fe3e4aSElliott Hughes    return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
63*e1fe3e4aSElliott Hughes
64*e1fe3e4aSElliott Hughes
65*e1fe3e4aSElliott Hughesdef findKnownKerningGroups(groups):
66*e1fe3e4aSElliott Hughes    """
67*e1fe3e4aSElliott Hughes    This will find kerning groups with known prefixes.
68*e1fe3e4aSElliott Hughes    In some cases not all kerning groups will be referenced
69*e1fe3e4aSElliott Hughes    by the kerning pairs. The algorithm for locating groups
70*e1fe3e4aSElliott Hughes    in convertUFO1OrUFO2KerningToUFO3Kerning will miss these
71*e1fe3e4aSElliott Hughes    unreferenced groups. By scanning for known prefixes
72*e1fe3e4aSElliott Hughes    this function will catch all of the prefixed groups.
73*e1fe3e4aSElliott Hughes
74*e1fe3e4aSElliott Hughes    These are the prefixes and sides that are handled:
75*e1fe3e4aSElliott Hughes    @MMK_L_ - side 1
76*e1fe3e4aSElliott Hughes    @MMK_R_ - side 2
77*e1fe3e4aSElliott Hughes
78*e1fe3e4aSElliott Hughes    >>> testGroups = {
79*e1fe3e4aSElliott Hughes    ...     "@MMK_L_1" : None,
80*e1fe3e4aSElliott Hughes    ...     "@MMK_L_2" : None,
81*e1fe3e4aSElliott Hughes    ...     "@MMK_L_3" : None,
82*e1fe3e4aSElliott Hughes    ...     "@MMK_R_1" : None,
83*e1fe3e4aSElliott Hughes    ...     "@MMK_R_2" : None,
84*e1fe3e4aSElliott Hughes    ...     "@MMK_R_3" : None,
85*e1fe3e4aSElliott Hughes    ...     "@MMK_l_1" : None,
86*e1fe3e4aSElliott Hughes    ...     "@MMK_r_1" : None,
87*e1fe3e4aSElliott Hughes    ...     "@MMK_X_1" : None,
88*e1fe3e4aSElliott Hughes    ...     "foo" : None,
89*e1fe3e4aSElliott Hughes    ... }
90*e1fe3e4aSElliott Hughes    >>> first, second = findKnownKerningGroups(testGroups)
91*e1fe3e4aSElliott Hughes    >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
92*e1fe3e4aSElliott Hughes    True
93*e1fe3e4aSElliott Hughes    >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
94*e1fe3e4aSElliott Hughes    True
95*e1fe3e4aSElliott Hughes    """
96*e1fe3e4aSElliott Hughes    knownFirstGroupPrefixes = ["@MMK_L_"]
97*e1fe3e4aSElliott Hughes    knownSecondGroupPrefixes = ["@MMK_R_"]
98*e1fe3e4aSElliott Hughes    firstGroups = set()
99*e1fe3e4aSElliott Hughes    secondGroups = set()
100*e1fe3e4aSElliott Hughes    for groupName in list(groups.keys()):
101*e1fe3e4aSElliott Hughes        for firstPrefix in knownFirstGroupPrefixes:
102*e1fe3e4aSElliott Hughes            if groupName.startswith(firstPrefix):
103*e1fe3e4aSElliott Hughes                firstGroups.add(groupName)
104*e1fe3e4aSElliott Hughes                break
105*e1fe3e4aSElliott Hughes        for secondPrefix in knownSecondGroupPrefixes:
106*e1fe3e4aSElliott Hughes            if groupName.startswith(secondPrefix):
107*e1fe3e4aSElliott Hughes                secondGroups.add(groupName)
108*e1fe3e4aSElliott Hughes                break
109*e1fe3e4aSElliott Hughes    return firstGroups, secondGroups
110*e1fe3e4aSElliott Hughes
111*e1fe3e4aSElliott Hughes
112*e1fe3e4aSElliott Hughesdef makeUniqueGroupName(name, groupNames, counter=0):
113*e1fe3e4aSElliott Hughes    # Add a number to the name if the counter is higher than zero.
114*e1fe3e4aSElliott Hughes    newName = name
115*e1fe3e4aSElliott Hughes    if counter > 0:
116*e1fe3e4aSElliott Hughes        newName = "%s%d" % (newName, counter)
117*e1fe3e4aSElliott Hughes    # If the new name is in the existing group names, recurse.
118*e1fe3e4aSElliott Hughes    if newName in groupNames:
119*e1fe3e4aSElliott Hughes        return makeUniqueGroupName(name, groupNames, counter + 1)
120*e1fe3e4aSElliott Hughes    # Otherwise send back the new name.
121*e1fe3e4aSElliott Hughes    return newName
122*e1fe3e4aSElliott Hughes
123*e1fe3e4aSElliott Hughes
124*e1fe3e4aSElliott Hughesdef test():
125*e1fe3e4aSElliott Hughes    """
126*e1fe3e4aSElliott Hughes    No known prefixes.
127*e1fe3e4aSElliott Hughes
128*e1fe3e4aSElliott Hughes    >>> testKerning = {
129*e1fe3e4aSElliott Hughes    ...     "A" : {
130*e1fe3e4aSElliott Hughes    ...         "A" : 1,
131*e1fe3e4aSElliott Hughes    ...         "B" : 2,
132*e1fe3e4aSElliott Hughes    ...         "CGroup" : 3,
133*e1fe3e4aSElliott Hughes    ...         "DGroup" : 4
134*e1fe3e4aSElliott Hughes    ...     },
135*e1fe3e4aSElliott Hughes    ...     "BGroup" : {
136*e1fe3e4aSElliott Hughes    ...         "A" : 5,
137*e1fe3e4aSElliott Hughes    ...         "B" : 6,
138*e1fe3e4aSElliott Hughes    ...         "CGroup" : 7,
139*e1fe3e4aSElliott Hughes    ...         "DGroup" : 8
140*e1fe3e4aSElliott Hughes    ...     },
141*e1fe3e4aSElliott Hughes    ...     "CGroup" : {
142*e1fe3e4aSElliott Hughes    ...         "A" : 9,
143*e1fe3e4aSElliott Hughes    ...         "B" : 10,
144*e1fe3e4aSElliott Hughes    ...         "CGroup" : 11,
145*e1fe3e4aSElliott Hughes    ...         "DGroup" : 12
146*e1fe3e4aSElliott Hughes    ...     },
147*e1fe3e4aSElliott Hughes    ... }
148*e1fe3e4aSElliott Hughes    >>> testGroups = {
149*e1fe3e4aSElliott Hughes    ...     "BGroup" : ["B"],
150*e1fe3e4aSElliott Hughes    ...     "CGroup" : ["C"],
151*e1fe3e4aSElliott Hughes    ...     "DGroup" : ["D"],
152*e1fe3e4aSElliott Hughes    ... }
153*e1fe3e4aSElliott Hughes    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
154*e1fe3e4aSElliott Hughes    ...     testKerning, testGroups, [])
155*e1fe3e4aSElliott Hughes    >>> expected = {
156*e1fe3e4aSElliott Hughes    ...     "A" : {
157*e1fe3e4aSElliott Hughes    ...         "A": 1,
158*e1fe3e4aSElliott Hughes    ...         "B": 2,
159*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 3,
160*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 4
161*e1fe3e4aSElliott Hughes    ...     },
162*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": {
163*e1fe3e4aSElliott Hughes    ...         "A": 5,
164*e1fe3e4aSElliott Hughes    ...         "B": 6,
165*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 7,
166*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 8
167*e1fe3e4aSElliott Hughes    ...     },
168*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": {
169*e1fe3e4aSElliott Hughes    ...         "A": 9,
170*e1fe3e4aSElliott Hughes    ...         "B": 10,
171*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 11,
172*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 12
173*e1fe3e4aSElliott Hughes    ...     }
174*e1fe3e4aSElliott Hughes    ... }
175*e1fe3e4aSElliott Hughes    >>> kerning == expected
176*e1fe3e4aSElliott Hughes    True
177*e1fe3e4aSElliott Hughes    >>> expected = {
178*e1fe3e4aSElliott Hughes    ...     "BGroup": ["B"],
179*e1fe3e4aSElliott Hughes    ...     "CGroup": ["C"],
180*e1fe3e4aSElliott Hughes    ...     "DGroup": ["D"],
181*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": ["B"],
182*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": ["C"],
183*e1fe3e4aSElliott Hughes    ...     "public.kern2.CGroup": ["C"],
184*e1fe3e4aSElliott Hughes    ...     "public.kern2.DGroup": ["D"],
185*e1fe3e4aSElliott Hughes    ... }
186*e1fe3e4aSElliott Hughes    >>> groups == expected
187*e1fe3e4aSElliott Hughes    True
188*e1fe3e4aSElliott Hughes
189*e1fe3e4aSElliott Hughes    Known prefixes.
190*e1fe3e4aSElliott Hughes
191*e1fe3e4aSElliott Hughes    >>> testKerning = {
192*e1fe3e4aSElliott Hughes    ...     "A" : {
193*e1fe3e4aSElliott Hughes    ...         "A" : 1,
194*e1fe3e4aSElliott Hughes    ...         "B" : 2,
195*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 3,
196*e1fe3e4aSElliott Hughes    ...         "@MMK_R_DGroup" : 4
197*e1fe3e4aSElliott Hughes    ...     },
198*e1fe3e4aSElliott Hughes    ...     "@MMK_L_BGroup" : {
199*e1fe3e4aSElliott Hughes    ...         "A" : 5,
200*e1fe3e4aSElliott Hughes    ...         "B" : 6,
201*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 7,
202*e1fe3e4aSElliott Hughes    ...         "@MMK_R_DGroup" : 8
203*e1fe3e4aSElliott Hughes    ...     },
204*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup" : {
205*e1fe3e4aSElliott Hughes    ...         "A" : 9,
206*e1fe3e4aSElliott Hughes    ...         "B" : 10,
207*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 11,
208*e1fe3e4aSElliott Hughes    ...         "@MMK_R_DGroup" : 12
209*e1fe3e4aSElliott Hughes    ...     },
210*e1fe3e4aSElliott Hughes    ... }
211*e1fe3e4aSElliott Hughes    >>> testGroups = {
212*e1fe3e4aSElliott Hughes    ...     "@MMK_L_BGroup" : ["B"],
213*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup" : ["C"],
214*e1fe3e4aSElliott Hughes    ...     "@MMK_L_XGroup" : ["X"],
215*e1fe3e4aSElliott Hughes    ...     "@MMK_R_CGroup" : ["C"],
216*e1fe3e4aSElliott Hughes    ...     "@MMK_R_DGroup" : ["D"],
217*e1fe3e4aSElliott Hughes    ...     "@MMK_R_XGroup" : ["X"],
218*e1fe3e4aSElliott Hughes    ... }
219*e1fe3e4aSElliott Hughes    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
220*e1fe3e4aSElliott Hughes    ...     testKerning, testGroups, [])
221*e1fe3e4aSElliott Hughes    >>> expected = {
222*e1fe3e4aSElliott Hughes    ...     "A" : {
223*e1fe3e4aSElliott Hughes    ...         "A": 1,
224*e1fe3e4aSElliott Hughes    ...         "B": 2,
225*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 3,
226*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 4
227*e1fe3e4aSElliott Hughes    ...     },
228*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": {
229*e1fe3e4aSElliott Hughes    ...         "A": 5,
230*e1fe3e4aSElliott Hughes    ...         "B": 6,
231*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 7,
232*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 8
233*e1fe3e4aSElliott Hughes    ...     },
234*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": {
235*e1fe3e4aSElliott Hughes    ...         "A": 9,
236*e1fe3e4aSElliott Hughes    ...         "B": 10,
237*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 11,
238*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 12
239*e1fe3e4aSElliott Hughes    ...     }
240*e1fe3e4aSElliott Hughes    ... }
241*e1fe3e4aSElliott Hughes    >>> kerning == expected
242*e1fe3e4aSElliott Hughes    True
243*e1fe3e4aSElliott Hughes    >>> expected = {
244*e1fe3e4aSElliott Hughes    ...     "@MMK_L_BGroup": ["B"],
245*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup": ["C"],
246*e1fe3e4aSElliott Hughes    ...     "@MMK_L_XGroup": ["X"],
247*e1fe3e4aSElliott Hughes    ...     "@MMK_R_CGroup": ["C"],
248*e1fe3e4aSElliott Hughes    ...     "@MMK_R_DGroup": ["D"],
249*e1fe3e4aSElliott Hughes    ...     "@MMK_R_XGroup": ["X"],
250*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": ["B"],
251*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": ["C"],
252*e1fe3e4aSElliott Hughes    ...     "public.kern1.XGroup": ["X"],
253*e1fe3e4aSElliott Hughes    ...     "public.kern2.CGroup": ["C"],
254*e1fe3e4aSElliott Hughes    ...     "public.kern2.DGroup": ["D"],
255*e1fe3e4aSElliott Hughes    ...     "public.kern2.XGroup": ["X"],
256*e1fe3e4aSElliott Hughes    ... }
257*e1fe3e4aSElliott Hughes    >>> groups == expected
258*e1fe3e4aSElliott Hughes    True
259*e1fe3e4aSElliott Hughes
260*e1fe3e4aSElliott Hughes    >>> from .validators import kerningValidator
261*e1fe3e4aSElliott Hughes    >>> kerningValidator(kerning)
262*e1fe3e4aSElliott Hughes    (True, None)
263*e1fe3e4aSElliott Hughes
264*e1fe3e4aSElliott Hughes    Mixture of known prefixes and groups without prefixes.
265*e1fe3e4aSElliott Hughes
266*e1fe3e4aSElliott Hughes    >>> testKerning = {
267*e1fe3e4aSElliott Hughes    ...     "A" : {
268*e1fe3e4aSElliott Hughes    ...         "A" : 1,
269*e1fe3e4aSElliott Hughes    ...         "B" : 2,
270*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 3,
271*e1fe3e4aSElliott Hughes    ...         "DGroup" : 4
272*e1fe3e4aSElliott Hughes    ...     },
273*e1fe3e4aSElliott Hughes    ...     "BGroup" : {
274*e1fe3e4aSElliott Hughes    ...         "A" : 5,
275*e1fe3e4aSElliott Hughes    ...         "B" : 6,
276*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 7,
277*e1fe3e4aSElliott Hughes    ...         "DGroup" : 8
278*e1fe3e4aSElliott Hughes    ...     },
279*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup" : {
280*e1fe3e4aSElliott Hughes    ...         "A" : 9,
281*e1fe3e4aSElliott Hughes    ...         "B" : 10,
282*e1fe3e4aSElliott Hughes    ...         "@MMK_R_CGroup" : 11,
283*e1fe3e4aSElliott Hughes    ...         "DGroup" : 12
284*e1fe3e4aSElliott Hughes    ...     },
285*e1fe3e4aSElliott Hughes    ... }
286*e1fe3e4aSElliott Hughes    >>> testGroups = {
287*e1fe3e4aSElliott Hughes    ...     "BGroup" : ["B"],
288*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup" : ["C"],
289*e1fe3e4aSElliott Hughes    ...     "@MMK_R_CGroup" : ["C"],
290*e1fe3e4aSElliott Hughes    ...     "DGroup" : ["D"],
291*e1fe3e4aSElliott Hughes    ... }
292*e1fe3e4aSElliott Hughes    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
293*e1fe3e4aSElliott Hughes    ...     testKerning, testGroups, [])
294*e1fe3e4aSElliott Hughes    >>> expected = {
295*e1fe3e4aSElliott Hughes    ...     "A" : {
296*e1fe3e4aSElliott Hughes    ...         "A": 1,
297*e1fe3e4aSElliott Hughes    ...         "B": 2,
298*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 3,
299*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 4
300*e1fe3e4aSElliott Hughes    ...     },
301*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": {
302*e1fe3e4aSElliott Hughes    ...         "A": 5,
303*e1fe3e4aSElliott Hughes    ...         "B": 6,
304*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 7,
305*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 8
306*e1fe3e4aSElliott Hughes    ...     },
307*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": {
308*e1fe3e4aSElliott Hughes    ...         "A": 9,
309*e1fe3e4aSElliott Hughes    ...         "B": 10,
310*e1fe3e4aSElliott Hughes    ...         "public.kern2.CGroup": 11,
311*e1fe3e4aSElliott Hughes    ...         "public.kern2.DGroup": 12
312*e1fe3e4aSElliott Hughes    ...     }
313*e1fe3e4aSElliott Hughes    ... }
314*e1fe3e4aSElliott Hughes    >>> kerning == expected
315*e1fe3e4aSElliott Hughes    True
316*e1fe3e4aSElliott Hughes    >>> expected = {
317*e1fe3e4aSElliott Hughes    ...     "BGroup": ["B"],
318*e1fe3e4aSElliott Hughes    ...     "@MMK_L_CGroup": ["C"],
319*e1fe3e4aSElliott Hughes    ...     "@MMK_R_CGroup": ["C"],
320*e1fe3e4aSElliott Hughes    ...     "DGroup": ["D"],
321*e1fe3e4aSElliott Hughes    ...     "public.kern1.BGroup": ["B"],
322*e1fe3e4aSElliott Hughes    ...     "public.kern1.CGroup": ["C"],
323*e1fe3e4aSElliott Hughes    ...     "public.kern2.CGroup": ["C"],
324*e1fe3e4aSElliott Hughes    ...     "public.kern2.DGroup": ["D"],
325*e1fe3e4aSElliott Hughes    ... }
326*e1fe3e4aSElliott Hughes    >>> groups == expected
327*e1fe3e4aSElliott Hughes    True
328*e1fe3e4aSElliott Hughes    """
329*e1fe3e4aSElliott Hughes
330*e1fe3e4aSElliott Hughes
331*e1fe3e4aSElliott Hughesif __name__ == "__main__":
332*e1fe3e4aSElliott Hughes    import doctest
333*e1fe3e4aSElliott Hughes
334*e1fe3e4aSElliott Hughes    doctest.testmod()
335