font-imp-sanitize.lmt /size: 6728 b    last modification: 2024-01-16 10:22
1if not modules then modules = { } end modules ['font-san'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA ADE & śrīrāma",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9--     \definefontfeature[demo][sanitizer=demo]
10--     \definedfont[Serif*default,demo] test context test
11--     \definedfont[Serif*default]      test context test
12
13local keys, insert, copytable = table.keys, table.insert, table.copy
14local utfvalues, utfbyte = utf.values, utf.byte
15
16local sanitizers = { }
17
18local trace  = false
19local report = logs.reporter("fonts", "sanitizers")
20
21trackers.register("fonts.sanitizers", function(v) trace = v end)
22
23function fonts.registersanitizer(name,vector)
24    sanitizers[name] = vector
25end
26
27local function split(s)
28    local t = { }
29    for value in utfvalues(s) do
30        t[#t+1] = value
31    end
32    return t
33end
34
35local function wrap(t)
36    for i=1,#t do
37        t[i] = { t[i] }
38    end
39    return t
40end
41
42local function initializesanitizer(tfmdata,value)
43    local resources = tfmdata.resources
44    local sanitized = resources.sanitized
45    if not sanitized then
46        local sanitizer = false
47        local goodies   = tfmdata.goodies
48        if goodies then
49            for i=1,#goodies do
50                local goodie = goodies[i]
51                local sanitizers = goodie.sanitizers
52                sanitizer = sanitizers and sanitizers[value]
53                if sanitizer then
54                    report("using sanitizer %a from goodie file",value)
55                    break
56                end
57            end
58        end
59        if not sanitizer then
60            sanitizer = sanitizers[value]
61            if sanitizer then
62                report("using sanitizer %a",value)
63            end
64        end
65        resources.sanitized = true
66        if sanitizer then
67            local mapping = sanitizer.mapping
68            if mapping then
69                local keys = keys(mapping)
70                local maps = { }
71                table.sort(keys, function(a,b) return a > b end)
72                for i = 1,#keys do
73                    local k = keys[i]
74                    keys[i] = split(k)
75                    maps[i] = split(mapping[k])
76                end
77                for i=1,#maps do
78                    local k = keys[i]
79                    local m = maps[i]
80                    if trace then
81                     -- m = copytable(m)
82                     -- insert(m,1,utfbyte("["))
83                     -- insert(m,  utfbyte("]"))
84                        local t = { utfbyte("[") }
85                        t[#t+1] = 0x200D -- zwnj
86                        for i=1,#m do
87                            t[#t+1] = m[i]
88                            t[#t+1] = 0x200D -- zwnj
89                        end
90                        t[#t+1] = utfbyte("]")
91                    end
92                    maps[i] = { type = "multiple", data = { [k[1]] = m } }
93                    keys[i] = { current = wrap(k) , lookups = { i } }
94                end
95                local specification = {
96                    name    = "sanitizer",
97                    type    = "chainsubstitution",
98                    prepend = 1,
99                    nocheck = true,
100                    default = true,
101                    lookups = maps,
102                    data    = { rules = keys },
103                }
104                local s = fonts.handlers.otf.enhancers.addfeature(
105                    tfmdata,"sanitizer",specification, true
106                )
107                insert(resources.sequences,1,s)
108            end
109        end
110    end
111end
112
113-- has to come *after* devanagari because we need to push it in front
114-- ... it could be a built-in
115
116fonts.constructors.features.otf.register {
117    name         = "sanitizer",
118    description  = "inject sanitizer features",
119    initializers = {
120        node = initializesanitizer,
121    },
122}
123
124local dev2rkrf = {
125    ["के्र"] = "क्रे",
126    ["कै्र"] = "क्रै",
127    ["खे्र"] = "ख्रे",
128    ["खै्र"] = "ख्रै",
129    ["गे्र"] = "ग्रे",
130    ["गै्र"] = "ग्रै",
131    ["घे्र"] = "घ्रे",
132    ["घै्र"] = "घ्रै",
133 -- not required: placed here just to show how bad some fonts are :(
134 -- ["ङे्र"] = "ङ्रे",
135 -- ["ङै्र"] = "ङ्रै",
136    ["चे्र"] = "च्रे",
137    ["चै्र"] = "च्रै",
138    ["छे्र"] = "छ्रे",
139    ["छै्र"] = "छ्रै",
140    ["जे्र"] = "ज्रे",
141    ["जै्र"] = "ज्रै",
142    ["झे्र"] = "झ्रे",
143    ["झै्र"] = "झ्रै",
144    ["ञे्र"] = "ञ्रे",
145    ["ञै्र"] = "ञ्रै",
146    ["णे्र"] = "ण्रे",
147    ["णै्र"] = "ण्रै",
148    ["ते्र"] = "त्रे",
149    ["तै्र"] = "त्रै",
150    ["थे्र"] = "थ्रे",
151    ["थै्र"] = "थ्रै",
152    ["दे्र"] = "द्रे",
153    ["दै्र"] = "द्रै",
154    ["धे्र"] = "ध्रे",
155    ["धै्र"] = "ध्रै",
156    ["ने्र"] = "न्रे",
157    ["नै्र"] = "न्रै",
158    ["पे्र"] = "प्रे",
159    ["पै्र"] = "प्रै",
160    ["फे्र"] = "फ्रे",
161    ["फै्र"] = "फ्रै",
162    ["बे्र"] = "ब्रे",
163    ["बै्र"] = "ब्रै",
164    ["भे्र"] = "भ्रे",
165    ["भै्र"] = "भ्रै",
166    ["मे्र"] = "म्रे",
167    ["मै्र"] = "म्रै",
168    ["ये्र"] = "य्रे",
169    ["यै्र"] = "य्रै",
170 -- some fonts might need this, some might not: weird
171 -- for example: Shobhika does not need it but Noto Serif does :(
172 -- ["ले्र"] = "ल्रे",
173 -- ["लै्र"] = "ल्रै",
174    ["वे्र"] = "व्रे",
175    ["वै्र"] = "व्रै",
176    ["से्र"] = "स्रे",
177    ["सै्र"] = "स्रै",
178    ["शे्र"] = "श्रे",
179    ["शै्र"] = "श्रै",
180    ["षे्र"] = "ष्रे",
181    ["षै्र"] = "ष्रै",
182    ["हे्र"] = "ह्रे",
183    ["है्र"] = "ह्रै",
184 -- some fonts might need this, some might not: weird
185 -- ["ळे्र"] = "ळ्रे",
186 -- ["ळै्र"] = "ळ्रै",
187}
188
189local demomapping = {
190    luatex   = "LuaTeX",
191    metapost = "MetaPost",
192    context  = "ConTeXt",
193    metafun  = "MetaFun",
194}
195
196fonts.registersanitizer("dev2rkrf", { mapping = dev2rkrf })
197fonts.registersanitizer("demo",     { mapping = demomapping })
198