| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- 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()
|