# Source code for fontTools.ufoLib.kerning

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