font-imp-math.lmt /size: 12 Kb    last modification: 2025-02-21 11:03
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,key,value)
109 -- if texconditionals["c_font_compact"] then
110    if key and 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
203local function bah(tfmdata,value,features)
204    -- has to come first
205    manipulate(tfmdata,"compactmath",features.compactmath)
206    -- so we might eventually merge these
207    initialize(tfmdata,value,features)
208end
209
210registerotffeature {
211    name         = "compactmath",
212    description  = "use one math font",
213    initializers = {
214        -- hm, different arguments
215        base = bah,
216        node = bah,
217    }
218}
219
220-- The problem is that the traditional code path doesn't add an italic to the subscript,
221-- simply because it assumes that the width has that already subtracted. So, we cannot
222-- compensate in the following way. We're stuck with the fact that the texgyre fonts
223-- assume a traditional tex engine.
224
225-- local function manipulate(tfmdata,key,value)
226--     if value == "correct" then
227--         local chardata = characters.data
228--         for unicode, data in next, tfmdata.characters do
229--             local italic = data.italic
230--             if italic then
231--                 -- todo: only letters
232--                 data.width = (data.width or 0) + italic
233--                 local cd = chardata[unicode]
234--                 if cd then
235--                     local visual = cd.visual
236--                     if visual == "it" or visual == "bi" then
237--                         italic = -italic
238--                     else
239--                         italic = 0
240--                     end
241--                 else
242--                     italic = 0
243--                 end
244--                 data.italic = italic
245--             end
246--         end
247--     end
248-- end
249
250local function initialize(tfmdata,value)
251    if type(value) == "string" then
252        local rawdata       = tfmdata.shared.rawdata
253        local rawresources  = rawdata and rawdata.resources
254        local mathconstants = rawresources.mathconstants
255        if mathconstants then
256            local bitmap = tex.stringtocodesbitmap(value,tex.mathcontrolcodes)
257         -- logs.report("mathfont","setting math control to %08X",bitmap)
258            tfmdata.properties.mathcontrol = bitmap
259        end
260    end
261end
262
263registerotffeature {
264    name         = "mathcontrol",
265    description  = "control specific old/new math handling",
266    initializers = {
267        base = initialize,
268        node = initialize,
269    }
270}
271
272local function initialize(tfmdata,value)
273    if value then
274        local validlookups, lookuplist = fonts.handlers.otf.collectlookups(tfmdata.shared.rawdata,"flac","math","dflt")
275        if validlookups then
276            -- it's quite likely just one step
277            local characters   = tfmdata.characters
278            local descriptions = tfmdata.descriptions
279            local changed      = tfmdata.changed
280            for i=1,#lookuplist do
281                local lookup   = lookuplist[i]
282                local steps    = lookup.steps
283                local nofsteps = lookup.nofsteps
284                for i=1,nofsteps do
285                    local coverage = steps[i].coverage
286                    if coverage then
287                        for k, v in next, coverage do
288                            local c = characters[k]
289                            local f = characters[v]
290                            if c and f then
291                                c.flataccent = v
292local dk = descriptions[k]
293local dv = descriptions[v]
294if not dv.unicode then
295    dv.unicode = dk.unicode or k
296end
297--                                 if not f.unicode then
298--                                     f.unicode = c.unicode
299--                                 end
300                            end
301                        end
302                    end
303                end
304            end
305        end
306    end
307end
308
309registerotffeature {
310    name         = "flattenaccents",
311    description  = "mapping accents to flat ones",
312    initializers = {
313        base = initialize,
314        node = initialize,
315    }
316}
317
318-- todo: document our privates
319
320-- This horrible hack is needed because when opentype math showed up and math was
321-- added to unicode folks had forgotten about different script shapes so we not only
322-- have a retrospective variant selector but inconsistent defaults. What a mess it
323-- is. So the quick and dirty solution is:
324--
325-- add copies in private slots
326-- use a pseudo feature to access those
327-- and optionally afterwards replace the original slots
328
329-- local coverage = { }
330--
331-- local function initialize(tfmdata,value)
332--     if value then
333--         if not next(coverage) then
334--             for k, char in next, mathematics.alphabets.sr.tf.lcletters do
335--                 coverage[char] = 0xFE800 + k
336--             end
337--             for k, char in next, mathematics.alphabets.sr.tf.ucletters do
338--                 coverage[char] = 0xFE800 + k
339--             end
340--             fonts.handlers.otf.addfeature {
341--                 name = "savemathscripts",
342--                 type = "substitution",
343--                 data = coverage,
344--             }
345--         end
346--         local characters   = tfmdata.characters
347--         local descriptions = tfmdata.descriptions
348--         for char, private in next, coverage do
349--             local data = characters[char]
350--             if data and not characters[private] then
351--                 -- otherwise we need a virtual
352--                 characters  [private] = copytable(data)
353--                 descriptions[private] = copytable(descriptions[char])
354--             end
355--         end
356--     end
357-- end
358--
359-- registerotffeature {
360--     name         = "copymathscripts",
361--     description  = "copy math script",
362--     prepend      = 1,
363--     initializers = {
364--         base = initialize,
365--         node = initialize,
366--     }
367-- }
368