converters: Conversion functions for kerning and groups

Functions for converting UFO1 or UFO2 files into UFO3 format.

Currently provides functionality for converting kerning rules and kerning groups. Conversion is only supported _from_ UFO1 or UFO2, and _to_ UFO3.

fontTools.ufoLib.converters.convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=())[source]

Convert kerning data in UFO1 or UFO2 syntax into UFO3 syntax.

Parameters:
  • kerning – A dictionary containing the kerning rules defined in the UFO font, as used in UFOReader objects.

  • groups – A dictionary containing the groups defined in the UFO font, as used in UFOReader objects.

  • glyphSet – Optional; a set of glyph objects to skip (default: None).

Returns:

  1. A dictionary representing the converted kerning data.

  2. A copy of the groups dictionary, with all groups renamed to UFO3 syntax.

  3. A dictionary containing the mapping of old group names to new group names.

fontTools.ufoLib.converters.findKnownKerningGroups(groups)[source]

Find all kerning groups in a UFO1 or UFO2 font that use known prefixes.

In some cases, not all kerning groups will be referenced by the kerning pairs in a UFO. 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.

The prefixes and sides by this function are:

@MMK_L_ - side 1 @MMK_R_ - side 2

as defined in the UFO1 specification.

Parameters:

groups – A dictionary containing the groups defined in the UFO font, as read by UFOReader.

Returns:

Two sets; the first containing the names of all first-side kerning groups identified in the groups dictionary, and the second containing the names of all second-side kerning groups identified.

”First-side” and “second-side” are with respect to the writing direction of the script.

Example:

>>> 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

fontTools.ufoLib.converters.makeUniqueGroupName(name, groupNames, counter=0)[source]

Make a kerning group name that will be unique within the set of group names.

If the requested kerning group name already exists within the set, this will return a new name by adding an incremented counter to the end of the requested name.

Parameters:
  • name – The requested kerning group name.

  • groupNames – A list of the existing kerning group names.

  • counter – Optional; a counter of group names already seen (default: 0). If counter is not provided, the function will recurse, incrementing the value of counter until it finds the first unused name+counter combination, and return that result.

Returns:

A unique kerning group name composed of the requested name suffixed by the smallest available integer counter.

fontTools.ufoLib.converters.test()[source]

Tests for convertUFO1OrUFO2KerningToUFO3Kerning().

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