good-mth.lua /size: 18 Kb    last modification: 2023-12-21 09:44
1
  if not modules then modules = { } end modules ['good-mth'] = {
2    version   = 1.000,
3    comment   = "companion to font-lib.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 type, next, tonumber, unpack = type, next, tonumber, unpack
10local ceil = math.ceil
11local match = string.match
12
13local fonts = fonts
14
15local trace_goodies      = false  trackers.register("fonts.goodies", function(v) trace_goodies = v end)
16local report_goodies     = logs.reporter("fonts","goodies")
17
18local registerotffeature = fonts.handlers.otf.features.register
19
20local fontgoodies        = fonts.goodies or { }
21
22local fontcharacters     = fonts.hashes.characters
23
24local trace_defining     = false  trackers.register("math.defining",   function(v) trace_defining   = v end)
25
26local report_math        = logs.reporter("mathematics","initializing")
27
28local nuts               = nodes.nuts
29
30local setlink            = nuts.setlink
31
32local nodepool           = nuts.pool
33
34local new_kern           = nodepool.kern
35local new_glyph          = nodepool.glyph
36local new_hlist          = nodepool.hlist
37local new_vlist          = nodepool.vlist
38
39local insertnodeafter    = nuts.insertafter
40
41local helpers            = fonts.helpers
42local upcommand          = helpers.commands.up
43local rightcommand       = helpers.commands.right
44local charcommand        = helpers.commands.char
45local prependcommands    = helpers.prependcommands
46
47-- experiment, we have to load the definitions immediately as they precede
48-- the definition so they need to be initialized in the typescript
49
50-- local function withscriptcode(tfmdata,unicode,data,action)
51--     if type(unicode) == "string" then
52--         local p, u = match(unicode,"^(.-):(.-)$")
53--         if u then
54--             u = tonumber(u)
55--             if u then
56--                 local slots = fonts.helpers.mathscriptslots(tfmdata,u)
57--                 if slots then
58--                     if p == "*" then
59--                         action(u,data)
60--                         for i=1,#slots do
61--                             action(slots[i],data)
62--                         end
63--                     else
64--                         p = tonumber(p)
65--                         if p then
66--                             action(slots[p],data)
67--                         end
68--                     end
69--                 end
70--             end
71--         end
72--     else
73--         action(unicode,data)
74--     end
75-- end
76
77-- local function finalize(tfmdata,feature,value)
78--  -- if tfmdata.mathparameters then -- funny, cambria text has this
79--     local goodies = tfmdata.goodies
80--     if goodies then
81--         local virtualized = mathematics.virtualized
82--         for i=1,#goodies do
83--             local goodie = goodies[i]
84--             local mathematics = goodie.mathematics
85--             local dimensions  = mathematics and mathematics.dimensions
86--             if dimensions then
87--                 if trace_defining then
88--                     report_math("overloading dimensions in %a @ %p",tfmdata.properties.fullname,tfmdata.parameters.size)
89--                 end
90--                 local characters   = tfmdata.characters
91--                 local descriptions = tfmdata.descriptions
92--                 local parameters   = tfmdata.parameters
93--                 local factor       = parameters.factor
94--                 local hfactor      = parameters.hfactor
95--                 local vfactor      = parameters.vfactor
96--                 local function overloadone(unicode,data)
97--                     local character = characters[unicode]
98--                     if not character then
99--                         local c = virtualized[unicode]
100--                         if c then
101--                             character = characters[c]
102--                         end
103--                     end
104--                     if character then
105--                         local width  = data.width
106--                         local height = data.height
107--                         local depth  = data.depth
108--                         if trace_defining and (width or height or depth) then
109--                             report_math("overloading dimensions of %C, width %p, height %p, depth %p",
110--                                 unicode,width or 0,height or 0,depth or 0)
111--                         end
112--                         if width  then character.width  = width  * hfactor end
113--                         if height then character.height = height * vfactor end
114--                         if depth  then character.depth  = depth  * vfactor end
115--                         --
116--                         local xoffset = data.xoffset
117--                         local yoffset = data.yoffset
118--                         if xoffset == "llx" then
119--                             local d = descriptions[unicode]
120--                             if d then
121--                                 xoffset         = - d.boundingbox[1] * hfactor
122--                                 character.width = character.width + xoffset
123--                                 xoffset         = rightcommand[xoffset]
124--                             else
125--                                 xoffset = nil
126--                             end
127--                         elseif xoffset and xoffset ~= 0 then
128--                             xoffset = rightcommand[xoffset * hfactor]
129--                         else
130--                             xoffset = nil
131--                         end
132--                         if yoffset and yoffset ~= 0 then
133--                             yoffset = upcommand[yoffset * vfactor]
134--                         else
135--                             yoffset = nil
136--                         end
137--                         if xoffset or yoffset then
138--                             local commands = character.commands
139--                             if commands then
140--                                 prependcommands(commands,yoffset,xoffset)
141--                             else
142--                                 local slot = charcommand[unicode]
143--                                 if xoffset and yoffset then
144--                                     character.commands = { xoffset, yoffset, slot }
145--                                 elseif xoffset then
146--                                     character.commands = { xoffset, slot }
147--                                 else
148--                                     character.commands = { yoffset, slot }
149--                                 end
150--                             end
151--                         end
152--                     elseif trace_defining then
153--                         report_math("no overloading dimensions of %C, not in font",unicode)
154--                     end
155--                 end
156--                 local function overload(dimensions)
157--                     for unicode, data in next, dimensions do
158--                         withscriptcode(tfmdata,unicode,data,overloadone)
159--                     end
160--                 end
161--                 if value == nil then
162--                     value = { "default" }
163--                 end
164--                 if value == "all" or value == true then
165--                     for name, value in next, dimensions do
166--                         overload(value)
167--                     end
168--                 else
169--                     if type(value) == "string" then
170--                         value = utilities.parsers.settings_to_array(value)
171--                     end
172--                     if type(value) == "table" then
173--                         for i=1,#value do
174--                             local d = dimensions[value[i]]
175--                             if d then
176--                                 overload(d)
177--                             end
178--                         end
179--                     end
180--                 end
181--             end
182--         end
183--     end
184-- end
185--
186-- registerotffeature {
187--     name         = "mathdimensions",
188--     description  = "manipulate math dimensions",
189--  -- default      = true,
190--     manipulators = {
191--         base = finalize,
192--         node = finalize,
193--     }
194-- }
195
196local function initialize(goodies)
197    local mathgoodies = goodies.mathematics
198    if mathgoodies then
199        local virtuals = mathgoodies.virtuals
200        local mapfiles = mathgoodies.mapfiles
201        local maplines = mathgoodies.maplines
202        if virtuals then
203            for name, specification in next, virtuals do
204                -- beware, they are all constructed ... we should be more selective
205                mathematics.makefont(name,specification,goodies)
206            end
207        end
208        if mapfiles then
209            for i=1,#mapfiles do
210                fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function
211            end
212        end
213        if maplines then
214            for i=1,#maplines do
215                fonts.mappings.loadline(maplines[i]) -- todo: backend function
216            end
217        end
218    end
219end
220
221fontgoodies.register("mathematics", initialize)
222
223-- local enabled = false   directives.register("fontgoodies.mathkerning",function(v) enabled = v end)
224
225-- local function initialize(tfmdata)
226--  -- if enabled and tfmdata.mathparameters then -- funny, cambria text has this
227--     if tfmdata.mathparameters then -- funny, cambria text has this
228--         local goodies = tfmdata.goodies
229--         if goodies then
230--             local characters = tfmdata.characters
231--             if characters[0x1D44E] then -- 119886
232--                 -- we have at least an italic a
233--                 for i=1,#goodies do
234--                     local mathgoodies = goodies[i].mathematics
235--                     if mathgoodies then
236--                         local kerns = mathgoodies.kerns
237--                         if kerns then
238--                             local function kernone(unicode,data)
239--                                 local chardata = characters[unicode]
240--                                 if chardata and (not chardata.mathkerns or data.force) then
241--                                     chardata.mathkerns = data
242--                                 end
243--                             end
244--                             for unicode, data in next, kerns do
245--                                 withscriptcode(tfmdata,unicode,data,kernone)
246--                             end
247--                             return
248--                         end
249--                     end
250--                 end
251--             else
252--                 return -- no proper math font anyway
253--             end
254--         end
255--     end
256-- end
257--
258-- registerotffeature {
259--     name         = "mathkerns",
260--     description  = "math kerns",
261--  -- default      = true,
262--     initializers = {
263--         base = initialize,
264--         node = initialize,
265--     }
266-- }
267
268-- -- math italics (not really needed)
269-- --
270-- -- it would be nice to have a \noitalics\font option
271--
272-- local function initialize(tfmdata)
273--     local goodies = tfmdata.goodies
274--     if goodies then
275--         local shared = tfmdata.shared
276--         for i=1,#goodies do
277--             local mathgoodies = goodies[i].mathematics
278--             if mathgoodies then
279--                 local mathitalics = mathgoodies.italics
280--                 if mathitalics then
281--                     local properties = tfmdata.properties
282--                     if properties.setitalics then
283--                         mathitalics = mathitalics[file.nameonly(properties.name)] or mathitalics
284--                         if mathitalics then
285--                             if trace_goodies then
286--                                 report_goodies("loading mathitalics for font %a",properties.name)
287--                             end
288--                             local corrections   = mathitalics.corrections
289--                             local defaultfactor = mathitalics.defaultfactor
290--                          -- properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
291--                             if corrections then
292--                                 fontgoodies.registerpostprocessor(tfmdata, function(tfmdata) -- this is another tfmdata (a copy)
293--                                     -- better make a helper so that we have less code being defined
294--                                     local properties = tfmdata.properties
295--                                     local parameters = tfmdata.parameters
296--                                     local characters = tfmdata.characters
297--                                     properties.mathitalic_defaultfactor = defaultfactor
298--                                     properties.mathitalic_defaultvalue  = defaultfactor * parameters.quad
299--                                     if trace_goodies then
300--                                         report_goodies("assigning mathitalics for font %a",properties.name)
301--                                     end
302--                                     local quad    = parameters.quad
303--                                     local hfactor = parameters.hfactor
304--                                     for k, v in next, corrections do
305--                                         local c = characters[k]
306--                                         if c then
307--                                             if v > -1 and v < 1 then
308--                                                 c.italic = v * quad
309--                                             else
310--                                                 c.italic = v * hfactor
311--                                             end
312--                                         else
313--                                             report_goodies("invalid mathitalics entry %U for font %a",k,properties.name)
314--                                         end
315--                                     end
316--                                 end)
317--                             end
318--                             return -- maybe not as these can accumulate
319--                         end
320--                     end
321--                 end
322--             end
323--         end
324--     end
325-- end
326--
327-- registerotffeature {
328--     name         = "mathitalics",
329--     description  = "additional math italic corrections",
330--  -- default      = true,
331--     initializers = {
332--         base = initialize,
333--         node = initialize,
334--     }
335-- }
336
337local function mathradicalaction(n,h,v,font,mchar,echar)
338    local characters = fontcharacters[font]
339    local mchardata  = characters[mchar]
340    local echardata  = characters[echar]
341    local ewidth     = echardata.width
342    local mwidth     = mchardata.width
343    local delta      = h - ewidth
344    local glyph      = new_glyph(font,echar)
345    local head       = glyph
346    if delta > 0 then
347        local count = ceil(delta/mwidth)
348        local kern  = (delta - count * mwidth) / count
349        for i=1,count do
350            local k = new_kern(kern)
351            local g = new_glyph(font,mchar)
352            setlink(k,head)
353            setlink(g,k)
354            head = g
355        end
356    end
357    local height = mchardata.height
358    local list   = new_hlist(head)
359    local kern   = new_kern(height-v)
360    list = setlink(kern,list)
361    local list = new_vlist(kern)
362    insertnodeafter(n,n,list)
363end
364
365local function mathhruleaction(n,h,v,font,bchar,mchar,echar)
366    local characters = fontcharacters[font]
367    local bchardata  = characters[bchar]
368    local mchardata  = characters[mchar]
369    local echardata  = characters[echar]
370    local bwidth     = bchardata.width
371    local mwidth     = mchardata.width
372    local ewidth     = echardata.width
373    local delta      = h - ewidth - bwidth
374    local glyph      = new_glyph(font,echar)
375    local head       = glyph
376    if delta > 0 then
377        local count = ceil(delta/mwidth)
378        local kern  = (delta - count * mwidth) / (count+1)
379        for i=1,count do
380            local k = new_kern(kern)
381            local g = new_glyph(font,mchar)
382            setlink(k,head)
383            setlink(g,k)
384            head = g
385        end
386        local k = new_kern(kern)
387        setlink(k,head)
388        head = k
389    end
390    local g = new_glyph(font,bchar)
391    setlink(g,head)
392    head = g
393    local height = mchardata.height
394    local list   = new_hlist(head)
395    local kern   = new_kern(height-v)
396    list = setlink(kern,list)
397    local list = new_vlist(kern)
398    insertnodeafter(n,n,list)
399end
400
401local function initialize(tfmdata)
402    local goodies = tfmdata.goodies
403    if goodies then
404        local resources = tfmdata.resources
405        local ruledata  = { }
406        for i=1,#goodies do
407            local mathematics = goodies[i].mathematics
408            if mathematics then
409                local rules = mathematics.rules
410                if rules then
411                    for tag, name in next, rules do
412                        ruledata[tag] = name
413                    end
414                end
415            end
416        end
417        if next(ruledata) then
418            local characters = tfmdata.characters
419            local unicodes   = resources.unicodes
420            if characters and unicodes then
421                local mathruleactions = resources.mathruleactions
422                if not mathruleactions then
423                    mathruleactions = { }
424                    resources.mathruleactions = mathruleactions
425                end
426                --
427                local mchar = unicodes[ruledata["radical.extender"] or false]
428                local echar = unicodes[ruledata["radical.end"]      or false]
429                if mchar and echar then
430                    mathruleactions.radicalaction = function(n,h,v,font)
431                        mathradicalaction(n,h,v,font,mchar,echar)
432                    end
433                end
434                --
435                local bchar = unicodes[ruledata["hrule.begin"]    or false]
436                local mchar = unicodes[ruledata["hrule.extender"] or false]
437                local echar = unicodes[ruledata["hrule.end"]      or false]
438                if bchar and mchar and echar then
439                    mathruleactions.hruleaction = function(n,h,v,font)
440                        mathhruleaction(n,h,v,font,bchar,mchar,echar)
441                    end
442                end
443                -- not that nice but we need to register it at the tex end
444             -- context.enablemathrules("\\fontclass")
445            end
446        end
447    end
448end
449
450registerotffeature {
451    name         = "mathrules",
452    description  = "check math rules",
453    default      = true,
454    initializers = {
455        base = initialize,
456        node = initialize,
457    }
458}
459