File: GSUBUnparser.py

package info (click to toggle)
python-fontfeatures 1.9.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,096 kB
  • sloc: python: 9,112; makefile: 22
file content (155 lines) | stat: -rw-r--r-- 5,751 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""GSUBUnparser: Convert binary GSUB lookups to fontFeatures objects."""
from .GTableUnparser import GTableUnparser
import fontFeatures


# These are silly little functions which help to document the intent
def glyph(x):
    """Wraps a glyph name in an array, to document the fact that it occupies a slot."""
    assert isinstance(x, str)
    return [x]


def singleglyph(x):
    """Wraps a glyph name in two arrays, to document the fact that it is the only occupant of a slot."""
    return [glyph(x)]


class GSUBUnparser(GTableUnparser):
    """Unparse a GSUB table into a fontFeatures object. See :py:class:`fontFeatures.ttLib.GTableUnparser`."""

    _table = "GSUB"
    lookupTypes = {
        1: "SingleSubstitution",
        2: "MultipleSubstitution",
        3: "AlternateSubstitution",
        4: "LigatureSubstitution",
        5: "Contextual",
        6: "ChainedContextual",
        7: "Extension",
        8: "ReverseContextualSubstitution",
    }

    _attrs = {
        "lookup": "SubstLookupRecord",
        "format1_ruleset": "SubRuleSet",
        "format1_rule": "SubRule",
        "format2_classset": "SubClassSet",
        "format2_rule": "SubClassRule",
        "chain_format1_ruleset": "ChainSubRuleSet",
        "chain_format1_rule": "ChainSubRule",
        "chain_format2_classset": "ChainSubClassSet",
        "chain_format2_rule": "ChainSubClassRule",
    }

    def isChaining(self, lookupType):
        """Returns true if the given lookup type is a chaining lookup."""
        return lookupType >= 5

    def unparseReverseContextualSubstitution(self, lookup):
        """Turn a GPOS8 (reverse contextual substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(
            name=self.getname("ReverseContextualSubstitution" + self.gensym())
        )
        self._fix_flags(b, lookup)
        for sub in lookup.SubTable:
            prefix = []
            outputs = []
            suffix = []
            if hasattr(sub, "BacktrackCoverage"):
                for coverage in reversed(sub.BacktrackCoverage):
                    prefix.append(coverage.glyphs)
            if hasattr(sub, "LookAheadCoverage"):
                for i, coverage in enumerate(sub.LookAheadCoverage):
                    suffix.append(coverage.glyphs)
            outputs = [sub.Substitute]
            inputs = [sub.Coverage.glyphs]
            b.addRule(
                fontFeatures.Substitution(
                    inputs,
                    outputs,
                    prefix,
                    suffix,
                    flags=lookup.LookupFlag,
                    reverse=True,
                )
            )
        return b, []

    def unparseLigatureSubstitution(self, lookup):
        """Turn a GPOS4 (ligature substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(
            name=self.getname("LigatureSubstitution" + self.gensym())
        )
        self._fix_flags(b, lookup)
        for sub in lookup.SubTable:
            for first, ligatures in sub.ligatures.items():
                for lig in ligatures:
                    substarray = [glyph(first)]
                    for x in lig.Component:
                        substarray.append(glyph(x))
                    b.addRule(
                        fontFeatures.Substitution(
                            substarray,
                            singleglyph(lig.LigGlyph),
                            address=self.currentLookup,
                            flags=lookup.LookupFlag,
                        )
                    )
        return b, []

    def unparseMultipleSubstitution(self, lookup):
        """Turn a GPOS2 (multiple substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(
            name=self.getname("MultipleSubstitution" + self.gensym())
        )
        self._fix_flags(b, lookup)

        for sub in lookup.SubTable:
            for in_glyph, out_glyphs in sub.mapping.items():
                b.addRule(
                    fontFeatures.Substitution(
                        singleglyph(in_glyph),
                        [glyph(x) for x in out_glyphs],
                        address=self.currentLookup,
                        flags=lookup.LookupFlag,
                    )
                )
        return b, []

    def unparseAlternateSubstitution(self, lookup):
        """Turn a GPOS3 (alternate substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(
            name=self.getname("AlternateSubstitution" + self.gensym())
        )
        self._fix_flags(b, lookup)
        for sub in lookup.SubTable:
            for in_glyph, out_glyphs in sub.alternates.items():
                b.addRule(
                    fontFeatures.Substitution(
                        singleglyph(in_glyph),
                        [out_glyphs],
                        address=self.currentLookup,
                        flags=lookup.LookupFlag,
                        force_alt=True,
                    )
                )
        return b, []

    def unparseSingleSubstitution(self, lookup):
        """Turn a GPOS1 (single substitution) subtable into a fontFeatures Routine."""
        b = fontFeatures.Routine(
            name=self.getname("SingleSubstitution" + self.gensym())
        )
        self._fix_flags(b, lookup)
        for sub in lookup.SubTable:
            for k, v in sub.mapping.items():
                b.addRule(
                    fontFeatures.Substitution(
                        [[k]],
                        [[v]],
                        address=self.currentLookup,
                        flags=lookup.LookupFlag,
                    )
                )
        return b, []