font-imp-math.lmt /size: 12 Kb    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['font-imp-math'] = {
2    version   = 1.001,
3    comment   = "companion to font-ini.mkiv and hand-ini.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local next, type, tonumber = next, type, tonumber
10local round = math.round
11
12local fonts              = fonts
13local helpers            = fonts.helpers
14local registerotffeature = fonts.handlers.otf.features.register
15
16local setmetatableindex  = table.setmetatableindex
17local sortedhash         = table.sortedhash
18local copytable          = table.copy
19
20local getmacro           = token.getmacro
21
22local texconditionals    = tex.conditionals
23
24-- tfmdata.properties.mathnolimitsmode = tonumber(value) or 0
25
26-- local splitter  = lpeg.splitat(",",tonumber)
27-- local lpegmatch = lpeg.match
28--
29-- local function initialize(tfmdata,value)
30--     local mathparameters = tfmdata.mathparameters
31--     if mathparameters then
32--         local sup, sub
33--         if type(value) == "string" then
34--             sup, sub = lpegmatch(splitter,value) -- settings_to_array
35--             if not sup then
36--                 sub, sup = 0, 0
37--             elseif not sub then
38--                 sub, sup = sup, 0
39--             end
40--         elseif type(value) == "number" then
41--             sup, sub = 0, value
42--         end
43--         if sup then
44--             mathparameters.NoLimitSupFactor = sup
45--         end
46--         if sub then
47--             mathparameters.NoLimitSubFactor = sub
48--         end
49--     end
50-- end
51--
52-- registerotffeature {
53--     name         = "mathnolimitsmode",
54--     description  = "influence nolimits placement",
55--     initializers = {
56--         base = initialize,
57--         node = initialize,
58--     }
59-- }
60
61-- this will become a mode in the engine -- done
62--
63-- local function initialize(tfmdata,value)
64--     tfmdata.properties.nostackmath = value and true
65-- end
66--
67-- registerotffeature {
68--     name        = "nostackmath",
69--     description = "disable math stacking mechanism",
70--     initializers = {
71--         base = initialize,
72--         node = initialize,
73--     }
74-- }
75
76-- A quick and dirty and low level implementation but okay for testing:
77
78function fonts.helpers.mathscriptslots(tfmdata,textcode)
79    local rawdata           = tfmdata.shared.rawdata
80    local rawresources      = rawdata and rawdata.resources
81    local rawfeatures       = rawresources and rawresources.features
82    local basesubstitutions = rawfeatures and rawfeatures.gsub
83    local sequences         = basesubstitutions and tfmdata.resources.sequences
84    if sequences then
85        local characters = tfmdata.characters
86        if characters[textcode] then
87            for s=1,#sequences do
88                local sequence  = sequences[s]
89                local sfeatures = sequence.features
90                if sfeatures and sfeatures.ssty then
91                    local steps = sequence.steps
92                    for i=1,#steps do
93                        local coverage = steps[i].coverage
94                        if coverage then
95                            local okay = coverage[textcode]
96                            if okay then
97                                -- can be single or table
98                                return okay
99                            end
100                        end
101                    end
102                end
103            end
104        end
105    end
106end
107
108local function manipulate(tfmdata,value)
109 -- if texconditionals["c_font_compact"] then
110    if value then -- so basically always
111        local rawdata           = tfmdata.shared.rawdata
112        local rawresources      = rawdata and rawdata.resources
113        local rawfeatures       = rawresources and rawresources.features
114        local basesubstitutions = rawfeatures and rawfeatures.gsub
115        local sequences         = basesubstitutions and tfmdata.resources.sequences
116        if sequences then
117            local characters = tfmdata.characters
118            for s=1,#sequences do
119                local sequence  = sequences[s]
120                local sfeatures = sequence.features
121                if sfeatures and sfeatures.ssty then
122                    local steps = sequence.steps
123                    for i=1,#steps do
124                        local coverage = steps[i].coverage
125                        if coverage then
126                            for textcode, v in next, coverage do
127                                local textdata = characters[textcode]
128                                if textdata then
129                                    local scriptcode, scriptscriptcode
130                                    local sstykind = type(v)
131                                    if sstykind == "table" then
132                                        scriptcode       = v[1]
133                                        scriptscriptcode = v[2]
134                                    elseif sstykind == "number" then
135                                        scriptcode       = v
136                                        scriptscriptcode = v
137                                    else
138                                        -- weird
139                                    end
140                                    if scriptcode then
141                                        local scriptdata       = characters[scriptcode]
142                                        local scriptscriptdata = characters[scriptscriptcode]
143                                        if scriptdata and scriptdata ~= textdata then
144                                            textdata.smaller = scriptcode
145                                            if scriptscriptdata and scriptdata ~= scriptscriptdata then
146                                                scriptdata.smaller = scriptscriptcode
147                                            end
148                                        end
149                                    end
150                                end
151                            end
152                        end
153                    end
154                end
155            end
156        end
157    end
158end
159
160local function initialize(tfmdata,value)
161    -- Here it really gets enabled as the scales are used.
162    if texconditionals["c_font_compact"] then
163        local rawdata       = tfmdata.shared.rawdata
164        local rawresources  = rawdata and rawdata.resources
165        local mathconstants = rawresources.mathconstants
166        if mathconstants then
167            local parameters = tfmdata.parameters
168            local properties = tfmdata.properties
169            local sizes      = {
170                1000,
171                mathconstants.ScriptPercentScaleDown * 10,
172                mathconstants.ScriptScriptPercentScaleDown * 10,
173            }
174            for i=1,3 do
175                value = getmacro("font_basics_mapped_fontsize_"..i,true)
176                local s = tonumber(value)
177                if s then
178                    sizes[i] = s
179                end
180            end
181            parameters.textscale         = sizes[1]
182            parameters.scriptscale       = sizes[2]
183            parameters.scriptscriptscale = sizes[3]
184            properties.compactmath       = true
185         -- logs.report("compact math","scales: % t",sizes)
186        end
187    end
188end
189
190-- local specification = {
191--     name         = "compactmath",
192--     description  = "use one math font",
193--     manipulators = {
194--         base = manipulate,
195--         node = manipulate,
196--     },
197--     initializers = {
198--         base = initialize,
199--         node = initialize,
200--     }
201-- }
202
203registerotffeature {
204    name         = "compactmath",
205    description  = "use one math font",
206    initializers = {
207        base = function(...) manipulate(...) initialize(...) end,
208        node = function(...) manipulate(...) initialize(...) end,
209    }
210}
211
212-- The problem is that the traditional code path doesn't add an italic to the subscript,
213-- simply because it assumes that the width has that already subtracted. So, we cannot
214-- compensate in the following way. We're stuck with the fact that the texgyre fonts
215-- assume a traditional tex engine.
216
217-- local function manipulate(tfmdata,key,value)
218--     if value == "correct" then
219--         local chardata = characters.data
220--         for unicode, data in next, tfmdata.characters do
221--             local italic = data.italic
222--             if italic then
223--                 -- todo: only letters
224--                 data.width = (data.width or 0) + italic
225--                 local cd = chardata[unicode]
226--                 if cd then
227--                     local visual = cd.visual
228--                     if visual == "it" or visual == "bi" then
229--                         italic = -italic
230--                     else
231--                         italic = 0
232--                     end
233--                 else
234--                     italic = 0
235--                 end
236--                 data.italic = italic
237--             end
238--         end
239--     end
240-- end
241
242local function initialize(tfmdata,value)
243    if type(value) == "string" then
244        local rawdata       = tfmdata.shared.rawdata
245        local rawresources  = rawdata and rawdata.resources
246        local mathconstants = rawresources.mathconstants
247        if mathconstants then
248            local bitmap = tex.stringtocodesbitmap(value,tex.mathcontrolcodes)
249         -- logs.report("mathfont","setting math control to %08X",bitmap)
250            tfmdata.properties.mathcontrol = bitmap
251        end
252    end
253end
254
255registerotffeature {
256    name         = "mathcontrol",
257    description  = "control specific old/new math handling",
258    initializers = {
259        base = initialize,
260        node = initialize,
261    }
262}
263
264local function initialize(tfmdata,value)
265    if value then
266        local validlookups, lookuplist = fonts.handlers.otf.collectlookups(tfmdata.shared.rawdata,"flac","math","dflt")
267        if validlookups then
268            -- it's quite likely just one step
269            local characters = tfmdata.characters
270            local changed = tfmdata.changed
271            for i=1,#lookuplist do
272                local lookup   = lookuplist[i]
273                local steps    = lookup.steps
274                local nofsteps = lookup.nofsteps
275                for i=1,nofsteps do
276                    local coverage = steps[i].coverage
277                    if coverage then
278                        for k, v in next, coverage do
279                            local c = characters[k]
280                            local f = characters[v]
281                            if c and f then
282                                c.flataccent = v
283                                if not f.unicode then
284                                    f.unicode = c.unicode
285                                end
286                            end
287                        end
288                    end
289                end
290            end
291        end
292    end
293end
294
295registerotffeature {
296    name         = "flattenaccents",
297    description  = "mapping accents to flat ones",
298    initializers = {
299        base = initialize,
300        node = initialize,
301    }
302}
303
304-- todo: document our privates
305
306-- This horrible hack is needed because when opentype math showed up and math was
307-- added to unicode folks had forgotten about different script shapes so we not only
308-- have a retrospective variant selector but inconsistent defaults. What a mess it
309-- is. So the quick and dirty solution is:
310--
311-- add copies in private slots
312-- use a pseudo feature to access those
313-- and optionally afterwards replace the original slots
314
315-- local coverage = { }
316--
317-- local function initialize(tfmdata,value)
318--     if value then
319--         if not next(coverage) then
320--             for k, char in next, mathematics.alphabets.sr.tf.lcletters do
321--                 coverage[char] = 0xFE800 + k
322--             end
323--             for k, char in next, mathematics.alphabets.sr.tf.ucletters do
324--                 coverage[char] = 0xFE800 + k
325--             end
326--             fonts.handlers.otf.addfeature {
327--                 name = "savemathscripts",
328--                 type = "substitution",
329--                 data = coverage,
330--             }
331--         end
332--         local characters   = tfmdata.characters
333--         local descriptions = tfmdata.descriptions
334--         for char, private in next, coverage do
335--             local data = characters[char]
336--             if data and not characters[private] then
337--                 -- otherwise we need a virtual
338--                 characters  [private] = copytable(data)
339--                 descriptions[private] = copytable(descriptions[char])
340--             end
341--         end
342--     end
343-- end
344--
345-- registerotffeature {
346--     name         = "copymathscripts",
347--     description  = "copy math script",
348--     prepend      = 1,
349--     initializers = {
350--         base = initialize,
351--         node = initialize,
352--     }
353-- }
354