File: ArabicShaper.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 (127 lines) | stat: -rw-r--r-- 4,090 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
from .BaseShaper import BaseShaper
from youseedee import ucd_data


jts = {"U": 0, "L": 1, "R": 2, "D": 3, "ALAPH": 4, "DALATH_RISH": 5}
state_table = [
    #   jt_U,          jt_L,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
    # State 0: prev was U, not willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("none", "isol", 1),
        ("none", "isol", 2),
        ("none", "isol", 1),
        ("none", "isol", 6),
    ],
    # State 1: prev was R or ISOL/ALAPH, not willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("none", "isol", 1),
        ("none", "isol", 2),
        ("none", "fin2", 5),
        ("none", "isol", 6),
    ],
    # State 2: prev was D/L in ISOL form, willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("init", "fina", 1),
        ("init", "fina", 3),
        ("init", "fina", 4),
        ("init", "fina", 6),
    ],
    # State 3: prev was D in FINA form, willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("medi", "fina", 1),
        ("medi", "fina", 3),
        ("medi", "fina", 4),
        ("medi", "fina", 6),
    ],
    # State 4: prev was FINA ALAPH, not willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("med2", "isol", 1),
        ("med2", "isol", 2),
        ("med2", "fin2", 5),
        ("med2", "isol", 6),
    ],
    # State 5: prev was FIN2/FIN3 ALAPH, not willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("isol", "isol", 1),
        ("isol", "isol", 2),
        ("isol", "fin2", 5),
        ("isol", "isol", 6),
    ],
    # State 6: prev was DALATH/RISH, not willing to join.
    [
        ("none", "none", 0),
        ("none", "isol", 2),
        ("none", "isol", 1),
        ("none", "isol", 2),
        ("none", "fin3", 5),
        ("none", "isol", 6),
    ],
]

arabic_features = ["isol", "fina", "fin2", "fin3", "medi", "med2", "init"]


class ArabicShaper(BaseShaper):
    def collect_features(self, shaper):
        # shaper.add_features("stch")
        shaper.add_features("ccmp", "locl")
        shaper.add_pause()
        shaper.add_features(*arabic_features)
        shaper.add_pause()
        shaper.add_features("rlig", "rclt", "calt")
        shaper.add_pause()
        shaper.add_features("mset")

    def substitute_default(self):
        super().substitute_default()
        state = 0
        prev_item = None
        for item in self.buffer.items:
            item.arabic_joining = "NONE"
            ucd = ucd_data(item.codepoint)
            joining = ucd.get("Joining_Type")
            if not joining:
                if ucd.get("General_Category") in ["Mn", "Cf", "Em"]:
                    joining = "T"
                else:
                    joining = "U"
            if joining == "T":
                continue
            if joining == "C":
                joining = "D"  # Mongolian
            if ucd.get("Joining_Group") == "ALAPH":
                joining = "ALAPH"
            if ucd.get("Joining_Group") == "DALATH RISH":
                joining = "DALATH_RISH"
            prev, this, state = state_table[state][jts[joining]]
            if prev_item:
                prev_item.arabic_joining = prev
            item.arabic_joining = this
            prev_item = item
        if self.buffer.script == "Mongolian":
            self.mongolian_variation_selectors()
        self.plan.msg(
            "Assigned Arabic joining", self.buffer, serialize_options=["arabic_joining"]
        )
        for f in arabic_features:
            if f not in self.plan.fontfeatures.features:
                continue
            for item in self.buffer.items:
                item.feature_masks[f] = item.arabic_joining != f

    def mongolian_variation_selectors(self):
        for ix, item in enumerate(self.buffer.items):
            if 0x180B <= item.codepoint <= 0x180D and ix > 0:
                item.arabic_joining = self.buffer.items[ix - 1].arabic_joining