You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.7 KiB
Python
142 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from fontTools.annotations import KerningPair, KerningDict, KerningGroups, IntFloat
|
|
|
|
StrDict = dict[str, str]
|
|
|
|
|
|
def lookupKerningValue(
|
|
pair: KerningPair,
|
|
kerning: KerningDict,
|
|
groups: KerningGroups,
|
|
fallback: IntFloat = 0,
|
|
glyphToFirstGroup: Optional[StrDict] = None,
|
|
glyphToSecondGroup: Optional[StrDict] = None,
|
|
) -> IntFloat:
|
|
"""Retrieve the kerning value (if any) between a pair of elements.
|
|
|
|
The elments can be either individual glyphs (by name) or kerning
|
|
groups (by name), or any combination of the two.
|
|
|
|
Args:
|
|
pair:
|
|
A tuple, in logical order (first, second) with respect
|
|
to the reading direction, to query the font for kerning
|
|
information on. Each element in the tuple can be either
|
|
a glyph name or a kerning group name.
|
|
kerning:
|
|
A dictionary of kerning pairs.
|
|
groups:
|
|
A set of kerning groups.
|
|
fallback:
|
|
The fallback value to return if no kern is found between
|
|
the elements in ``pair``. Defaults to 0.
|
|
glyphToFirstGroup:
|
|
A dictionary mapping glyph names to the first-glyph kerning
|
|
groups to which they belong. Defaults to ``None``.
|
|
glyphToSecondGroup:
|
|
A dictionary mapping glyph names to the second-glyph kerning
|
|
groups to which they belong. Defaults to ``None``.
|
|
|
|
Returns:
|
|
The kerning value between the element pair. If no kerning for
|
|
the pair is found, the fallback value is returned.
|
|
|
|
Note: This function expects the ``kerning`` argument to be a flat
|
|
dictionary of kerning pairs, not the nested structure used in a
|
|
kerning.plist file.
|
|
|
|
Examples::
|
|
|
|
>>> groups = {
|
|
... "public.kern1.O" : ["O", "D", "Q"],
|
|
... "public.kern2.E" : ["E", "F"]
|
|
... }
|
|
>>> kerning = {
|
|
... ("public.kern1.O", "public.kern2.E") : -100,
|
|
... ("public.kern1.O", "F") : -200,
|
|
... ("D", "F") : -300
|
|
... }
|
|
>>> lookupKerningValue(("D", "F"), kerning, groups)
|
|
-300
|
|
>>> lookupKerningValue(("O", "F"), kerning, groups)
|
|
-200
|
|
>>> lookupKerningValue(("O", "E"), kerning, groups)
|
|
-100
|
|
>>> lookupKerningValue(("O", "O"), kerning, groups)
|
|
0
|
|
>>> lookupKerningValue(("E", "E"), kerning, groups)
|
|
0
|
|
>>> lookupKerningValue(("E", "O"), kerning, groups)
|
|
0
|
|
>>> lookupKerningValue(("X", "X"), kerning, groups)
|
|
0
|
|
>>> lookupKerningValue(("public.kern1.O", "public.kern2.E"),
|
|
... kerning, groups)
|
|
-100
|
|
>>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups)
|
|
-200
|
|
>>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups)
|
|
-100
|
|
>>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups)
|
|
0
|
|
"""
|
|
# quickly check to see if the pair is in the kerning dictionary
|
|
if pair in kerning:
|
|
return kerning[pair]
|
|
# ensure both or no glyph-to-group mappings are provided
|
|
if (glyphToFirstGroup is None) != (glyphToSecondGroup is None):
|
|
raise ValueError(
|
|
"Must provide both 'glyphToFirstGroup' and 'glyphToSecondGroup', or neither."
|
|
)
|
|
# create glyph to group mapping
|
|
if glyphToFirstGroup is None:
|
|
glyphToFirstGroup = {}
|
|
glyphToSecondGroup = {}
|
|
for group, groupMembers in groups.items():
|
|
if group.startswith("public.kern1."):
|
|
for glyph in groupMembers:
|
|
glyphToFirstGroup[glyph] = group
|
|
elif group.startswith("public.kern2."):
|
|
for glyph in groupMembers:
|
|
glyphToSecondGroup[glyph] = group
|
|
# ensure type safety for mappings
|
|
assert glyphToFirstGroup is not None
|
|
assert glyphToSecondGroup is not None
|
|
# get group names and make sure first and second are glyph names
|
|
first, second = pair
|
|
firstGroup = secondGroup = None
|
|
if first.startswith("public.kern1."):
|
|
firstGroup = first
|
|
firstGlyph = None
|
|
else:
|
|
firstGroup = glyphToFirstGroup.get(first)
|
|
firstGlyph = first
|
|
if second.startswith("public.kern2."):
|
|
secondGroup = second
|
|
secondGlyph = None
|
|
else:
|
|
secondGroup = glyphToSecondGroup.get(second)
|
|
secondGlyph = second
|
|
# make an ordered list of pairs to look up
|
|
pairs = [
|
|
(a, b)
|
|
for a in (firstGlyph, firstGroup)
|
|
for b in (secondGlyph, secondGroup)
|
|
if a is not None and b is not None
|
|
]
|
|
# look up the pairs and return any matches
|
|
for pair in pairs:
|
|
if pair in kerning:
|
|
return kerning[pair]
|
|
# use the fallback value
|
|
return fallback
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
doctest.testmod()
|