classes.c15_kern_table

 1from loguru import logger
 2from fontTools.ttLib import TTFont
 3
 4
 5class KKernTable:
 6    def __init__(self, fontPath: str):
 7        self.fontPath = fontPath
 8        self.font = TTFont(fontPath)
 9        self.kerningPairs = self.prepareKerningTable()
10
11    def prepareKerningTable(self):
12        """Prepare kerning data structure for quick lookup."""
13        kerning = {}
14        if "GPOS" not in self.font:
15            print("No GPOS table found")
16            self.kerningPairs = kerning
17            return
18
19        gpos = self.font["GPOS"].table
20        for lookup in gpos.LookupList.Lookup:
21            if lookup.LookupType == 2:  # Pair Adjustment
22                for subtable in lookup.SubTable:
23                    # Format 1: PairSet
24                    if (
25                        hasattr(subtable, "Format")
26                        and subtable.Format == 1
27                        and hasattr(subtable, "PairSet")
28                    ):
29                        left_glyphs = subtable.Coverage.glyphs
30                        for idx, left in enumerate(left_glyphs):
31                            pairSet = subtable.PairSet[idx]
32                            for pairValueRecord in pairSet.PairValueRecord:
33                                right = pairValueRecord.SecondGlyph
34                                value = pairValueRecord.Value1.XAdvance or 0
35                                kerning[(left, right)] = value
36                    # Format 2: Class-based
37                    elif (
38                        hasattr(subtable, "Format")
39                        and subtable.Format == 2
40                        and hasattr(subtable, "Class1Record")
41                    ):
42                        classDef1 = subtable.ClassDef1.classDefs
43                        classDef2 = subtable.ClassDef2.classDefs
44                        class1Records = subtable.Class1Record
45                        for left, class1 in classDef1.items():
46                            for right, class2 in classDef2.items():
47                                valueRecord = class1Records[class1].Class2Record[class2]
48                                if valueRecord.Value1 and valueRecord.Value1.XAdvance:
49                                    value = valueRecord.Value1.XAdvance
50                                    kerning[(left, right)] = value
51        return kerning
52
53    def findKerningForPair(self, left: str, right: str) -> int | None:
54        """Quickly look up kerning value for a given glyph pair."""
55        return self.kerningPairs.get((left, right))
56
57    def findKerningForString(self, text: str) -> int | None:
58        if len(text) > 2:
59            logger.warning(
60                f"Input string '{text}' has more than 2 characters. Only the first two will be considered."
61            )
62
63        if len(text) >= 2:
64            return self.findKerningForPair(text[0], text[1])
65        else:
66            return None
class KKernTable:
 6class KKernTable:
 7    def __init__(self, fontPath: str):
 8        self.fontPath = fontPath
 9        self.font = TTFont(fontPath)
10        self.kerningPairs = self.prepareKerningTable()
11
12    def prepareKerningTable(self):
13        """Prepare kerning data structure for quick lookup."""
14        kerning = {}
15        if "GPOS" not in self.font:
16            print("No GPOS table found")
17            self.kerningPairs = kerning
18            return
19
20        gpos = self.font["GPOS"].table
21        for lookup in gpos.LookupList.Lookup:
22            if lookup.LookupType == 2:  # Pair Adjustment
23                for subtable in lookup.SubTable:
24                    # Format 1: PairSet
25                    if (
26                        hasattr(subtable, "Format")
27                        and subtable.Format == 1
28                        and hasattr(subtable, "PairSet")
29                    ):
30                        left_glyphs = subtable.Coverage.glyphs
31                        for idx, left in enumerate(left_glyphs):
32                            pairSet = subtable.PairSet[idx]
33                            for pairValueRecord in pairSet.PairValueRecord:
34                                right = pairValueRecord.SecondGlyph
35                                value = pairValueRecord.Value1.XAdvance or 0
36                                kerning[(left, right)] = value
37                    # Format 2: Class-based
38                    elif (
39                        hasattr(subtable, "Format")
40                        and subtable.Format == 2
41                        and hasattr(subtable, "Class1Record")
42                    ):
43                        classDef1 = subtable.ClassDef1.classDefs
44                        classDef2 = subtable.ClassDef2.classDefs
45                        class1Records = subtable.Class1Record
46                        for left, class1 in classDef1.items():
47                            for right, class2 in classDef2.items():
48                                valueRecord = class1Records[class1].Class2Record[class2]
49                                if valueRecord.Value1 and valueRecord.Value1.XAdvance:
50                                    value = valueRecord.Value1.XAdvance
51                                    kerning[(left, right)] = value
52        return kerning
53
54    def findKerningForPair(self, left: str, right: str) -> int | None:
55        """Quickly look up kerning value for a given glyph pair."""
56        return self.kerningPairs.get((left, right))
57
58    def findKerningForString(self, text: str) -> int | None:
59        if len(text) > 2:
60            logger.warning(
61                f"Input string '{text}' has more than 2 characters. Only the first two will be considered."
62            )
63
64        if len(text) >= 2:
65            return self.findKerningForPair(text[0], text[1])
66        else:
67            return None
KKernTable(fontPath: str)
 7    def __init__(self, fontPath: str):
 8        self.fontPath = fontPath
 9        self.font = TTFont(fontPath)
10        self.kerningPairs = self.prepareKerningTable()
fontPath
font
kerningPairs
def prepareKerningTable(self):
12    def prepareKerningTable(self):
13        """Prepare kerning data structure for quick lookup."""
14        kerning = {}
15        if "GPOS" not in self.font:
16            print("No GPOS table found")
17            self.kerningPairs = kerning
18            return
19
20        gpos = self.font["GPOS"].table
21        for lookup in gpos.LookupList.Lookup:
22            if lookup.LookupType == 2:  # Pair Adjustment
23                for subtable in lookup.SubTable:
24                    # Format 1: PairSet
25                    if (
26                        hasattr(subtable, "Format")
27                        and subtable.Format == 1
28                        and hasattr(subtable, "PairSet")
29                    ):
30                        left_glyphs = subtable.Coverage.glyphs
31                        for idx, left in enumerate(left_glyphs):
32                            pairSet = subtable.PairSet[idx]
33                            for pairValueRecord in pairSet.PairValueRecord:
34                                right = pairValueRecord.SecondGlyph
35                                value = pairValueRecord.Value1.XAdvance or 0
36                                kerning[(left, right)] = value
37                    # Format 2: Class-based
38                    elif (
39                        hasattr(subtable, "Format")
40                        and subtable.Format == 2
41                        and hasattr(subtable, "Class1Record")
42                    ):
43                        classDef1 = subtable.ClassDef1.classDefs
44                        classDef2 = subtable.ClassDef2.classDefs
45                        class1Records = subtable.Class1Record
46                        for left, class1 in classDef1.items():
47                            for right, class2 in classDef2.items():
48                                valueRecord = class1Records[class1].Class2Record[class2]
49                                if valueRecord.Value1 and valueRecord.Value1.XAdvance:
50                                    value = valueRecord.Value1.XAdvance
51                                    kerning[(left, right)] = value
52        return kerning

Prepare kerning data structure for quick lookup.

def findKerningForPair(self, left: str, right: str) -> int | None:
54    def findKerningForPair(self, left: str, right: str) -> int | None:
55        """Quickly look up kerning value for a given glyph pair."""
56        return self.kerningPairs.get((left, right))

Quickly look up kerning value for a given glyph pair.

def findKerningForString(self, text: str) -> int | None:
58    def findKerningForString(self, text: str) -> int | None:
59        if len(text) > 2:
60            logger.warning(
61                f"Input string '{text}' has more than 2 characters. Only the first two will be considered."
62            )
63
64        if len(text) >= 2:
65            return self.findKerningForPair(text[0], text[1])
66        else:
67            return None