math-vfu.lua /size: 48 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['math-vfu'] = {
2    version   = 1.001,
3    comment   = "companion to math-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
9-- All these math vectors .. thanks to Aditya and Mojca they become
10-- better and better. If you have problems with math fonts or miss
11-- characters report it to the ConTeXt mailing list. Also thanks to
12-- Boguslaw for finding a couple of errors.
13--
14-- This mechanism will stay around. Even when we've switched to the
15-- real fonts, one can still say:
16--
17-- \enablemode[lmmath,pxmath,txmath]
18--
19-- to get the virtual counterparts. There are still areas where the
20-- virtuals are better.
21
22-- 20D6 -> 2190
23-- 20D7 -> 2192
24
25local type, next, tonumber = type, next, tonumber
26local max = math.max
27local fastcopy = table.copy
28
29local fonts, nodes, mathematics = fonts, nodes, mathematics
30
31local trace_virtual = false  trackers.register("math.virtual", function(v) trace_virtual = v end)
32local trace_timings = false  trackers.register("math.timings", function(v) trace_timings = v end)
33
34local add_optional  = false  directives.register("math.virtual.optional",function(v) add_optional = v end)
35
36local report_virtual    = logs.reporter("fonts","virtual math")
37
38local allocate          = utilities.storage.allocate
39local setmetatableindex = table.setmetatableindex
40local formatters        = string.formatters
41
42local chardata          = characters.data
43
44local mathencodings     = allocate()
45fonts.encodings.math    = mathencodings -- better is then: fonts.encodings.vectors
46local vfmath            = allocate()
47fonts.handlers.vf.math  = vfmath
48
49local helpers           = fonts.helpers
50local vfcommands        = helpers.commands
51local rightcommand      = vfcommands.right
52local leftcommand       = vfcommands.left
53local downcommand       = vfcommands.down
54local upcommand         = vfcommands.up
55local push              = vfcommands.push
56local pop               = vfcommands.pop
57
58local shared            = { }
59
60-- local back = { "slot", 1, 0x2215 }
61--
62-- local function negate(main,characters,id,size,unicode,basecode)
63--     if not characters[unicode] then
64--         local basechar = characters[basecode]
65--         if basechar then
66--             local ht, wd = basechar.height, basechar.width
67--             characters[unicode] = {
68--                 width    = wd,
69--                 height   = ht,
70--                 depth    = basechar.depth,
71--                 italic   = basechar.italic,
72--                 kerns    = basechar.kerns,
73--                 commands = {
74--                     { "slot", 1, basecode },
75--                     push,
76--                     downcommand[ht/5],
77--                     leftcommand[wd/2],
78--                     back,
79--                     push,
80--                 }
81--             }
82--         end
83--     end
84-- end
85--
86-- \Umathchardef\braceld="0 "1 "FF07A
87-- \Umathchardef\bracerd="0 "1 "FF07B
88-- \Umathchardef\bracelu="0 "1 "FF07C
89-- \Umathchardef\braceru="0 "1 "FF07D
90
91local function brace(main,characters,id,size,unicode,first,rule,left,right,rule,last)
92    if not characters[unicode] then
93        characters[unicode] = {
94            horiz_variants = {
95                { extender = 0, glyph = first },
96                { extender = 1, glyph = rule  },
97                { extender = 0, glyph = left  },
98                { extender = 0, glyph = right },
99                { extender = 1, glyph = rule  },
100                { extender = 0, glyph = last  },
101            }
102        }
103    end
104end
105
106local function extension(main,characters,id,size,unicode,first,middle,last)
107    local chr = characters[unicode]
108    if not chr then
109        return -- skip
110    end
111    local fw = characters[first]
112    if not fw then
113        return
114    end
115    local mw = characters[middle]
116    if not mw then
117        return
118    end
119    local lw = characters[last]
120    if not lw then
121        return
122    end
123    fw = fw.width
124    mw = mw.width
125    lw = lw.width
126    if fw == 0 then
127        fw = 1
128    end
129    if lw == 0 then
130        lw = 1
131    end
132    chr.horiz_variants = {
133        { extender = 0, glyph = first,  ["end"] = fw/2, start = 0,    advance = fw },
134        { extender = 1, glyph = middle, ["end"] = mw/2, start = mw/2, advance = mw },
135        { extender = 0, glyph = last,   ["end"] = 0,    start = lw/2, advance = lw },
136    }
137end
138
139local function parent(main,characters,id,size,unicode,first,rule,last)
140    if not characters[unicode] then
141        characters[unicode] = {
142            horiz_variants = {
143                { extender = 0, glyph = first },
144                { extender = 1, glyph = rule  },
145                { extender = 0, glyph = last  },
146            }
147        }
148    end
149end
150
151local step = 0.2 -- 0.1 is nicer but gives larger files
152
153local function make(main,characters,id,size,n,m)
154    local old = 0xFF000 + n
155    local c   = characters[old]
156    if c then
157        local upslot    = 0xFF100 + n
158        local dnslot    = 0xFF200 + n
159        local uprule    = 0xFF300 + m
160        local dnrule    = 0xFF400 + m
161        local xu        = main.parameters.x_height + 0.3*size
162        local xd        = 0.3*size
163        local w         = c.width or 0
164        local h         = c.height or 0
165        local d         = c.depth or 0
166        local thickness = h - d
167        local rulewidth = step*size -- we could use an overlap
168        local slot      = { "slot", id, old }
169        local rule      = { "rule", thickness, rulewidth  }
170        local up        = upcommand[xu]
171        local dn        = downcommand[xd]
172        local ht        = xu + 3*thickness
173        local dp        = 0
174        if not characters[uprule] then
175            characters[uprule] = {
176                width    = rulewidth,
177                height   = ht,
178                depth    = dp,
179                commands = { push, up, rule, pop },
180            }
181        end
182        characters[upslot] = {
183            width    = w,
184            height   = ht,
185            depth    = dp,
186            commands = { push, up, slot, pop },
187        }
188        local ht = 0
189        local dp = xd + 3*thickness
190        if not characters[dnrule] then
191            characters[dnrule] = {
192                width    = rulewidth,
193                height   = ht,
194                depth    = dp,
195                commands = { push, dn, rule, pop }
196            }
197        end
198        characters[dnslot] = {
199            width    = w,
200            height   = ht,
201            depth    = dp,
202            commands = { push, dn, slot, pop },
203        }
204    end
205end
206
207local function clipped(main,characters,id,size,unicode,original) -- push/pop needed?
208    local minus = characters[original]
209    if minus then
210        local mu    = size/18
211        local step  = 3*mu
212        local width = minus.width
213        if width > step then
214            width = width - step
215            step  = step / 2
216        else
217            width = width / 2
218            step  = width
219        end
220        characters[unicode] = {
221            width    = width,
222            height   = minus.height,
223            depth    = minus.depth,
224            commands = {
225                push,
226                leftcommand[step],
227                { "slot", id, original },
228                pop,
229            }
230        }
231    end
232end
233
234local function raise(main,characters,id,size,unicode,private,n,id_of_smaller) -- this is a real fake mess
235    local raised = fonts.hashes.characters[main.fonts[id_of_smaller].id][private]  -- characters[private]
236    if raised then
237        local up   = 0.85 * main.parameters.x_height
238        local slot = { "slot", id_of_smaller, private }
239        local commands = {
240            push, upcommand[up], slot,
241        }
242        for i=2,n do
243            commands[#commands+1] = slot
244        end
245        commands[#commands+1] = pop
246        characters[unicode] = {
247            width    = n * raised.width,
248            height   = (raised.height or 0) + up,
249            depth    = (raised.depth or 0) - up,
250            italic   = raised.italic,
251            commands = commands,
252        }
253    end
254end
255
256local function dots(main,characters,id,size,unicode)
257    local c = characters[0x002E]
258    if c then
259        local w         = c.width
260        local h         = c.height
261        local d         = c.depth
262        local mu        = size/18
263        local right3mu  = rightcommand[3*mu]
264        local right1mu  = rightcommand[1*mu]
265        local up1size   = upcommand[.1*size]
266        local up4size   = upcommand[.4*size]
267        local up7size   = upcommand[.7*size]
268        local right2muw = rightcommand[2*mu + w]
269        local slot      = { "slot", id, 0x002E }
270        if unicode == 0x22EF then
271            local c = characters[0x022C5]
272            if c then
273                local width  = c.width
274                local height = c.height
275                local depth  = c.depth
276                local slot   = { "slot", id, 0x022C5 }
277                characters[unicode] = {
278                    width    = 3*width + 2*3*mu,
279                    height   = height,
280                    depth    = depth,
281                    commands = {
282                        push, slot, right3mu, slot, right3mu, slot, pop,
283                    }
284                }
285            end
286        elseif unicode == 0x22EE then
287            -- weird height !
288            characters[unicode] = {
289                width    = w,
290                height   = h+(1.4)*size,
291                depth    = 0,
292                commands = {
293                    push, push, slot, pop, up4size, push, slot, pop, up4size, slot, pop,
294                }
295            }
296        elseif unicode == 0x22F1 then
297            characters[unicode] = {
298                width    = 3*w + 6*size/18,
299                height   = 1.5*size,
300                depth    = 0,
301                commands = {
302                    push,
303                    right1mu,
304                    push, up7size, slot, pop,
305                    right2muw,
306                    push, up4size, slot, pop,
307                    right2muw,
308                    push, up1size, slot, pop,
309                    right1mu,
310                    pop
311                }
312            }
313        elseif unicode == 0x22F0 then
314            characters[unicode] = {
315                width    = 3*w + 6*size/18,
316                height   = 1.5*size,
317                depth    = 0,
318                commands = {
319                    push,
320                    right1mu,
321                    push, up1size, slot, pop,
322                    right2muw,
323                    push, up4size, slot, pop,
324                    right2muw,
325                    push, up7size, slot, pop,
326                    right1mu,
327                    pop
328                }
329            }
330        else
331            characters[unicode] = {
332                width    = 3*w + 2*3*mu,
333                height   = h,
334                depth    = d,
335                commands = {
336                    push, slot, right3mu, slot, right3mu, slot, pop,
337                }
338            }
339        end
340    end
341end
342
343local function vertbar(main,characters,id,size,parent,scale,unicode)
344    local cp = characters[parent]
345    if cp then
346        local sc = scale * size
347        local pc = { "slot", id, parent }
348        characters[unicode] = {
349            width    = cp.width,
350            height   = cp.height + sc,
351            depth    = cp.depth + sc,
352            next     = cp.next, -- can be extensible
353            commands = {
354                push, upcommand  [sc], pc, pop,
355                push, downcommand[sc], pc, pop,
356                                       pc,
357            },
358        }
359        cp.next = unicode
360    end
361end
362
363local function jointwo(main,characters,id,size,unicode,u1,d12,u2,what)
364    local c1 = characters[u1]
365    local c2 = characters[u2]
366    if c1 and c2 then
367        local w1 = c1.width
368        local w2 = c2.width
369        local mu = size/18
370        characters[unicode] = {
371            width    = w1 + w2 - d12 * mu,
372            height   = max(c1.height or 0, c2.height or 0),
373            depth    = max(c1.depth  or 0, c2.depth  or 0),
374            commands = {
375                { "slot", id, u1 },
376                leftcommand[d12*mu],
377                { "slot", id, u2 },
378            },
379        }
380    end
381end
382
383local function jointhree(main,characters,id,size,unicode,u1,d12,u2,d23,u3)
384    local c1 = characters[u1]
385    local c2 = characters[u2]
386    local c3 = characters[u3]
387    if c1 and c2 and c3 then
388        local w1 = c1.width
389        local w2 = c2.width
390        local w3 = c3.width
391        local mu = size/18
392        characters[unicode] = {
393            width    = w1 + w2 + w3 - d12*mu - d23*mu,
394            height   = max(c1.height or 0, c2.height or 0, c3.height or 0),
395            depth    = max(c1.depth  or 0, c2.depth  or 0, c3.depth  or 0),
396            commands = {
397                { "slot", id, u1 },
398                leftcommand[d12*mu],
399                { "slot", id, u2 },
400                leftcommand[d23*mu],
401                { "slot", id, u3 },
402            }
403        }
404    end
405end
406
407local function stack(main,characters,id,size,unicode,u1,d12,u2)
408    local c1 = characters[u1]
409    if not c1 then
410        return
411    end
412    local c2 = characters[u2]
413    if not c2 then
414        return
415    end
416    local w1 = c1.width  or 0
417    local h1 = c1.height or 0
418    local d1 = c1.depth  or 0
419    local w2 = c2.width  or 0
420    local h2 = c2.height or 0
421    local d2 = c2.depth  or 0
422    local mu = size/18
423    characters[unicode] = {
424        width    = w1,
425        height   = h1 + h2 + d12,
426        depth    = d1,
427        commands = {
428            { "slot", id, u1 },
429            leftcommand[w1/2 + w2/2],
430            downcommand[-h1 + d2 -d12*mu],
431            { "slot", id, u2 },
432        }
433    }
434end
435
436local function repeated(main,characters,id,size,unicode,u,n,private,fraction) -- math-fbk.lua
437    local c = characters[u]
438    if c then
439        local width  = c.width
440        local italic = fraction*width -- c.italic or 0 -- larger ones have funny italics
441        local tc     = { "slot", id, u }
442        local tr     = leftcommand[italic] -- see hack elsewhere
443        local commands = { }
444        for i=1,n-1 do
445            commands[#commands+1] = tc
446            commands[#commands+1] = tr
447        end
448        commands[#commands+1] = tc
449        local next = c.next
450        if next then
451            repeated(main,characters,id,size,private,next,n,private+1,fraction)
452            next = private
453        end
454        characters[unicode] = {
455            width    = width + (n-1)*(width-italic),
456            height   = c.height,
457            depth    = c.depth,
458            italic   = italic,
459            commands = commands,
460            next     = next,
461        }
462    end
463end
464
465local function cloned(main,characters,id,size,source,target)
466    local data = characters[source]
467    if data then
468        characters[target] = data
469        return true
470    end
471end
472
473-- we use the fact that context defines the smallest sizes first .. a real dirty and ugly hack
474
475local data_of_smaller = nil
476local size_of_smaller = 0
477
478function vfmath.addmissing(main,id,size)
479
480    local id_of_smaller = nil
481
482    if size < size_of_smaller or size_of_smaller == 0 then
483        data_of_smaller = main.fonts[id]
484        id_of_smaller = id
485    else
486        id_of_smaller = #main.fonts + 1
487        main.fonts[id_of_smaller] = data_of_smaller
488    end
489
490    -- here id is the index in fonts (normally 14 or so) and that slot points to self
491
492    local characters    = main.characters
493    local shared        = main.shared
494    local variables     = main.goodies.mathematics and main.goodies.mathematics.variables or { }
495    local joinrelfactor = variables.joinrelfactor or 3
496
497    for i=0x7A,0x7D do
498        make(main,characters,id,size,i,1)
499    end
500
501    brace    (main,characters,id,size,0x23DE,0xFF17A,0xFF301,0xFF17D,0xFF17C,0xFF301,0xFF17B)
502    brace    (main,characters,id,size,0x23DF,0xFF27C,0xFF401,0xFF27B,0xFF27A,0xFF401,0xFF27D)
503
504    parent   (main,characters,id,size,0x23DC,0xFF17A,0xFF301,0xFF17B)
505    parent   (main,characters,id,size,0x23DD,0xFF27C,0xFF401,0xFF27D)
506
507 -- negate   (main,characters,id,size,0x2260,0x003D)
508    dots     (main,characters,id,size,0x2026) -- ldots
509    dots     (main,characters,id,size,0x22EE) -- vdots
510    dots     (main,characters,id,size,0x22EF) -- cdots
511    dots     (main,characters,id,size,0x22F1) -- ddots
512    dots     (main,characters,id,size,0x22F0) -- udots
513
514    vertbar  (main,characters,id,size,0x0007C,0.10,0xFF601) -- big  : 0.85 bodyfontsize
515    vertbar  (main,characters,id,size,0xFF601,0.30,0xFF602) -- Big  : 1.15 bodyfontsize
516    vertbar  (main,characters,id,size,0xFF602,0.30,0xFF603) -- bigg : 1.45 bodyfontsize
517    vertbar  (main,characters,id,size,0xFF603,0.30,0xFF604) -- Bigg : 1.75 bodyfontsize
518    vertbar  (main,characters,id,size,0x02016,0.10,0xFF605)
519    vertbar  (main,characters,id,size,0xFF605,0.30,0xFF606)
520    vertbar  (main,characters,id,size,0xFF606,0.30,0xFF607)
521    vertbar  (main,characters,id,size,0xFF607,0.30,0xFF608)
522
523    clipped  (main,characters,id,size,0xFF501,0x0002D) -- minus
524    clipped  (main,characters,id,size,0xFF502,0x02190) -- lefthead
525    clipped  (main,characters,id,size,0xFF503,0x02192) -- righthead
526    clipped  (main,characters,id,size,0xFF504,0xFE321) -- mapsto
527    clipped  (main,characters,id,size,0xFF505,0xFE322) -- lhook
528    clipped  (main,characters,id,size,0xFF506,0xFE323) -- rhook
529    clipped  (main,characters,id,size,0xFF507,0xFE324) -- mapsfrom
530    clipped  (main,characters,id,size,0xFF508,0x021D0) -- double lefthead
531    clipped  (main,characters,id,size,0xFF509,0x021D2) -- double righthead
532    clipped  (main,characters,id,size,0xFF50A,0x0003D) -- equal
533    clipped  (main,characters,id,size,0xFF50B,0x0219E) -- lefttwohead
534    clipped  (main,characters,id,size,0xFF50C,0x021A0) -- righttwohead
535    clipped  (main,characters,id,size,0xFF50D,0xFF350) -- lr arrow combi snippet
536    clipped  (main,characters,id,size,0xFF50E,0xFF351) -- lr arrow combi snippet
537    clipped  (main,characters,id,size,0xFF50F,0xFF352) -- lr arrow combi snippet
538    clipped  (main,characters,id,size,0xFF510,0x02261) -- equiv
539
540    extension(main,characters,id,size,0x2190,0xFF502,0xFF501,0xFF501)                 -- \leftarrow
541    extension(main,characters,id,size,0x2192,0xFF501,0xFF501,0xFF503)                 -- \rightarrow
542
543    extension(main,characters,id,size,0x002D,0xFF501,0xFF501,0xFF501)                 -- \rel
544    extension(main,characters,id,size,0x003D,0xFF50A,0xFF50A,0xFF50A)                 -- \equal
545    extension(main,characters,id,size,0x2261,0xFF510,0xFF510,0xFF510)                 -- \equiv
546
547    jointwo  (main,characters,id,size,0x21A6,0xFE321,0,0x02192)                       -- \mapstochar\rightarrow
548    jointwo  (main,characters,id,size,0x21A9,0x02190,joinrelfactor,0xFE323)           -- \leftarrow\joinrel\rhook
549    jointwo  (main,characters,id,size,0x21AA,0xFE322,joinrelfactor,0x02192)           -- \lhook\joinrel\rightarrow
550    jointwo  (main,characters,id,size,0x27F5,0x02190,joinrelfactor,0x0002D)           -- \leftarrow\joinrel\relbar
551    jointwo  (main,characters,id,size,0x27F6,0x0002D,joinrelfactor,0x02192,2)         -- \relbar\joinrel\rightarrow
552    jointwo  (main,characters,id,size,0x27F7,0x02190,joinrelfactor,0x02192)           -- \leftarrow\joinrel\rightarrow
553    jointwo  (main,characters,id,size,0x27F8,0x021D0,joinrelfactor,0x0003D)           -- \Leftarrow\joinrel\Relbar
554    jointwo  (main,characters,id,size,0x27F9,0x0003D,joinrelfactor,0x021D2)           -- \Relbar\joinrel\Rightarrow
555    jointwo  (main,characters,id,size,0x27FA,0x021D0,joinrelfactor,0x021D2)           -- \Leftarrow\joinrel\Rightarrow
556    jointhree(main,characters,id,size,0x27FB,0x02190,joinrelfactor,0x0002D,0,0xFE324) -- \leftarrow\joinrel\relbar\mapsfromchar
557    jointhree(main,characters,id,size,0x27FC,0xFE321,0,0x0002D,joinrelfactor,0x02192) -- \mapstochar\relbar\joinrel\rightarrow
558
559    extension(main,characters,id,size,0x21A6,0xFF504,0xFF501,0xFF503)                 -- \mapstochar\rightarrow
560    extension(main,characters,id,size,0x21A9,0xFF502,0xFF501,0xFF506)                 -- \leftarrow\joinrel\rhook
561    extension(main,characters,id,size,0x21AA,0xFF505,0xFF501,0xFF503)                 -- \lhook\joinrel\rightarrow
562    extension(main,characters,id,size,0x27F5,0xFF502,0xFF501,0xFF501)                 -- \leftarrow\joinrel\relbar
563    extension(main,characters,id,size,0x27F6,0xFF501,0xFF501,0xFF503)                 -- \relbar\joinrel\rightarrow
564    extension(main,characters,id,size,0x27F7,0xFF502,0xFF501,0xFF503)                 -- \leftarrow\joinrel\rightarrow
565    extension(main,characters,id,size,0x27F8,0xFF508,0xFF50A,0xFF50A)                 -- \Leftarrow\joinrel\Relbar
566    extension(main,characters,id,size,0x27F9,0xFF50A,0xFF50A,0xFF509)                 -- \Relbar\joinrel\Rightarrow
567    extension(main,characters,id,size,0x27FA,0xFF508,0xFF50A,0xFF509)                 -- \Leftarrow\joinrel\Rightarrow
568    extension(main,characters,id,size,0x27FB,0xFF502,0xFF501,0xFF507)                 -- \leftarrow\joinrel\relbar\mapsfromchar
569    extension(main,characters,id,size,0x27FC,0xFF504,0xFF501,0xFF503)                 -- \mapstochar\relbar\joinrel\rightarrow
570
571    extension(main,characters,id,size,0x219E,0xFF50B,0xFF501,0xFF501)                 -- \twoheadleftarrow\joinrel\relbar
572    extension(main,characters,id,size,0x21A0,0xFF501,0xFF501,0xFF50C)                 -- \relbar\joinrel\twoheadrightarrow
573    extension(main,characters,id,size,0x21C4,0xFF50D,0xFF50E,0xFF50F)                 -- leftoverright
574
575    -- 21CB leftrightharpoon
576    -- 21CC rightleftharpoon
577
578    stack(main,characters,id,size,0x2259,0x0003D,3,0x02227)                       -- \buildrel\wedge\over=
579
580    jointwo(main,characters,id,size,0x22C8,0x022B3,joinrelfactor,0x022B2)           -- \mathrel\triangleright\joinrel\mathrel\triangleleft (4 looks better than 3)
581    jointwo(main,characters,id,size,0x22A7,0x0007C,joinrelfactor,0x0003D)           -- \mathrel|\joinrel=
582    jointwo(main,characters,id,size,0x2260,0x00338,0,0x0003D)                       -- \not\equal
583    jointwo(main,characters,id,size,0x2284,0x00338,0,0x02282)                       -- \not\subset
584    jointwo(main,characters,id,size,0x2285,0x00338,0,0x02283)                       -- \not\supset
585    jointwo(main,characters,id,size,0x2209,0x00338,0,0x02208)                       -- \not\in
586    jointwo(main,characters,id,size,0x2254,0x03A,0,0x03D)                           -- := (≔)
587
588    repeated(main,characters,id,size,0x222C,0x222B,2,0xFF800,1/3)
589    repeated(main,characters,id,size,0x222D,0x222B,3,0xFF810,1/3)
590
591    if cloned(main,characters,id,size,0x2032,0xFE325) then
592        raise(main,characters,id,size,0x2032,0xFE325,1,id_of_smaller) -- prime
593        raise(main,characters,id,size,0x2033,0xFE325,2,id_of_smaller) -- double prime
594        raise(main,characters,id,size,0x2034,0xFE325,3,id_of_smaller) -- triple prime
595        -- to satisfy the prime resolver
596        characters[0xFE932] = characters[0x2032]
597        characters[0xFE933] = characters[0x2033]
598        characters[0xFE934] = characters[0x2034]
599    end
600
601    -- there are more (needs discussion first):
602
603 -- characters[0x20D6] = characters[0x2190]
604 -- characters[0x20D7] = characters[0x2192]
605
606    characters[0x02B9] = characters[0x2032] -- we're nice
607
608    data_of_smaller = main.fonts[id]
609    size_of_smaller = size
610
611end
612
613local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage
614
615local reported = { }
616local reverse  = { } -- index -> unicode
617
618setmetatableindex(reverse, function(t,name)
619    if trace_virtual then
620        report_virtual("initializing math vector %a",name)
621    end
622    local m = mathencodings[name]
623    local r = { }
624    for u, i in next, m do
625        r[i] = u
626    end
627    reverse[name] = r
628    return r
629end)
630
631local function copy_glyph(main,target,original,unicode,slot)
632    local addprivate = fonts.helpers.addprivate
633    local olddata    = original[unicode]
634    if olddata then
635        local newdata = {
636            width     = olddata.width,
637            height    = olddata.height,
638            depth     = olddata.depth,
639            italic    = olddata.italic,
640            kerns     = olddata.kerns,
641            tounicode = olddata.tounicode,
642            commands  = { { "slot", slot, unicode } },
643        }
644        local glyphdata = newdata
645        local nextglyph = olddata.next
646        while nextglyph do
647            local oldnextdata = original[nextglyph]
648            local newnextdata = {
649                width     = oldnextdata.width,
650                height    = oldnextdata.height,
651                depth     = oldnextdata.depth,
652                tounicode = olddata.tounicode,
653                commands  = { { "slot", slot, nextglyph } },
654            }
655            local newnextglyph = addprivate(main,formatters["M-N-%H"](nextglyph),newnextdata)
656            newdata.next = newnextglyph
657            local nextnextglyph = oldnextdata.next
658            if nextnextglyph == nextglyph then
659                break
660            else
661                olddata   = oldnextdata
662                newdata   = newnextdata
663                nextglyph = nextnextglyph
664            end
665        end
666        local hv = olddata.horiz_variants
667        if hv then
668            hv = fastcopy(hv)
669            newdata.horiz_variants = hv
670            for i=1,#hv do
671                local hvi = hv[i]
672                local oldglyph = hvi.glyph
673                local olddata = original[oldglyph]
674                local newdata = {
675                    width     = olddata.width,
676                    height    = olddata.height,
677                    depth     = olddata.depth,
678                    tounicode = olddata.tounicode,
679                    commands  = { { "slot", slot, oldglyph } },
680                }
681                hvi.glyph = addprivate(main,formatters["M-H-%H"](oldglyph),newdata)
682            end
683        end
684        local vv = olddata.vert_variants
685        if vv then
686            vv = fastcopy(vv)
687            newdata.vert_variants = vv
688            for i=1,#vv do
689                local vvi = vv[i]
690                local oldglyph = vvi.glyph
691                local olddata = original[oldglyph]
692                local newdata = {
693                    width     = olddata.width,
694                    height    = olddata.height,
695                    depth     = olddata.depth,
696                    tounicode = olddata.tounicode,
697                    commands  = { { "slot", slot, oldglyph } },
698                }
699                vvi.glyph = addprivate(main,formatters["M-V-%H"](oldglyph),newdata)
700            end
701        end
702        return newdata
703    end
704end
705
706vfmath.copy_glyph = copy_glyph
707
708function vfmath.define(specification,set,goodies)
709    local name     = specification.name -- symbolic name
710    local size     = specification.size -- given size
711    local loaded   = { }
712    local fontlist = { }
713    local names    = { }
714    local main     = nil
715    local start    = (trace_virtual or trace_timings) and os.clock()
716    local okset    = { }
717    local n        = 0
718    for s=1,#set do
719        local ss     = set[s]
720        local ssname = ss.name
721        if add_optional and ss.optional then
722            if trace_virtual then
723                report_virtual("loading font %a subfont %s with name %a at %p is skipped",name,s,ssname,size)
724            end
725        else
726            if ss.features then
727                ssname = ssname .. "*" .. ss.features
728            end
729            if ss.main then
730                main = s
731            end
732            local alreadyloaded = names[ssname] -- for px we load one twice (saves .04 sec)
733            local f, id
734            if alreadyloaded then
735                f, id = alreadyloaded.f, alreadyloaded.id
736                if trace_virtual then
737                    report_virtual("loading font %a subfont %s with name %a is reused",name,s,ssname)
738                end
739            else
740                f, id = fonts.constructors.readanddefine(ssname,size)
741                names[ssname] = { f = f, id = id }
742            end
743            if not f or id == 0 then
744                report_virtual("loading font %a subfont %s with name %a at %p is skipped, not found",name,s,ssname,size)
745            else
746                n = n + 1
747                okset[n] = ss
748                loaded[n] = f
749                fontlist[n] = { id = id, size = size }
750                if not shared[s] then
751                    shared[n] = { }
752                end
753                if trace_virtual then
754                    report_virtual("loading font %a subfont %s with name %a at %p as id %s using encoding %a",name,s,ssname,size,id,ss.vector)
755                end
756                if not ss.checked then
757                    ss.checked = true
758                    local vector = mathencodings[ss.vector]
759                    if vector then
760                        -- we resolve named glyphs only once as we can assume that vectors
761                        -- are unique to a font set (when we read an afm we get those names
762                        -- mapped onto the private area)
763                        for unicode, index in next, vector do
764                            if not tonumber(index) then
765                                local u = f.unicodes
766                                u = u and u[index]
767                                if u then
768                                    if trace_virtual then
769                                        report_virtual("resolving name %a to %s",index,u) -- maybe more detail for u
770                                    end
771                                else
772                                    report_virtual("unable to resolve name %a",index)
773                                end
774                                vector[unicode] = u
775                            end
776                        end
777                    end
778                end
779            end
780        end
781    end
782    -- beware, loaded[1] is already passed to tex (we need to make a simple copy then .. todo)
783    local parent         = loaded[1] or { } -- a text font
784    local characters     = { }
785    local parameters     = { }
786    local mathparameters = { }
787    local descriptions   = { }
788    local metadata       = { }
789    local properties     = { }
790    local goodies        = { }
791    local main           = {
792        metadata         = metadata,
793        properties       = properties,
794        characters       = characters,
795        descriptions     = descriptions,
796        parameters       = parameters,
797        mathparameters   = mathparameters,
798        fonts            = fontlist,
799        goodies          = goodies,
800    }
801    --
802    --
803    for key, value in next, parent do
804        if type(value) ~= "table" then
805            main[key] = value
806        end
807    end
808    --
809    if parent.characters then
810        for unicode, character in next, parent.characters do
811            characters[unicode] = character
812        end
813    else
814        report_virtual("font %a has no characters",name)
815    end
816    --
817    if parent.parameters then
818        for key, value in next, parent.parameters do
819            parameters[key] = value
820        end
821    else
822        report_virtual("font %a has no parameters",name)
823    end
824    --
825    local description = { name = "<unset>" }
826    setmetatableindex(descriptions,function() return description end)
827    --
828    if parent.properties then
829        setmetatableindex(properties,parent.properties)
830    end
831    --
832    if parent.goodies then
833        setmetatableindex(goodies,parent.goodies)
834    end
835    --
836    properties.hasitalics  = true
837    properties.hasmath     = true
838    --
839    local fullname = properties.fullname -- parent via mt
840    if fullname then
841        unique = unique + 1
842        properties.fullname = fullname .. "-" .. unique
843    end
844    --
845    -- we need to set some values in main as well (still?)
846    --
847    main.fullname      = properties.fullname
848    main.nomath        = false
849    --
850    properties.virtualized = true
851    main.type              = "virtual"
852    --
853    parameters.x_height = parameters.x_height or 0
854    --
855    local already_reported = false
856    local parameters_done = false
857    for s=1,n do
858        local ss, fs = okset[s], loaded[s]
859        if not fs then
860            -- skip, error
861        elseif add_optional and ss.optional then
862            -- skip, redundant
863        else
864            local newparameters     = fs.parameters
865            local newmathparameters = fs.mathparameters
866            if newmathparameters then
867                if not parameters_done or ss.parameters then
868                    mathparameters  = newmathparameters
869                    parameters_done = true
870                end
871            elseif not newparameters then
872                report_virtual("no parameters set in font %a",name)
873            elseif ss.extension then
874                mathparameters.math_x_height          = newparameters.x_height or 0        -- math_x_height          : height of x
875                mathparameters.default_rule_thickness = newparameters[ 8]      or 0        -- default_rule_thickness : thickness of \over bars
876                mathparameters.big_op_spacing1        = newparameters[ 9]      or 0        -- big_op_spacing1        : minimum clearance above a displayed op
877                mathparameters.big_op_spacing2        = newparameters[10]      or 0        -- big_op_spacing2        : minimum clearance below a displayed op
878                mathparameters.big_op_spacing3        = newparameters[11]      or 0        -- big_op_spacing3        : minimum baselineskip above displayed op
879                mathparameters.big_op_spacing4        = newparameters[12]      or 0        -- big_op_spacing4        : minimum baselineskip below displayed op
880                mathparameters.big_op_spacing5        = newparameters[13]      or 0        -- big_op_spacing5        : padding above and below displayed limits
881            --  report_virtual("loading and virtualizing font %a at size %p, setting ex parameters",name,size)
882            elseif ss.parameters then
883                mathparameters.x_height      = newparameters.x_height or mathparameters.x_height
884                mathparameters.x_height      = mathparameters.x_height or fp.x_height or 0 -- x_height               : height of x
885                mathparameters.num1          = newparameters[ 8] or 0                      -- num1                   : numerator shift-up in display styles
886                mathparameters.num2          = newparameters[ 9] or 0                      -- num2                   : numerator shift-up in non-display, non-\atop
887                mathparameters.num3          = newparameters[10] or 0                      -- num3                   : numerator shift-up in non-display \atop
888                mathparameters.denom1        = newparameters[11] or 0                      -- denom1                 : denominator shift-down in display styles
889                mathparameters.denom2        = newparameters[12] or 0                      -- denom2                 : denominator shift-down in non-display styles
890                mathparameters.sup1          = newparameters[13] or 0                      -- sup1                   : superscript shift-up in uncramped display style
891                mathparameters.sup2          = newparameters[14] or 0                      -- sup2                   : superscript shift-up in uncramped non-display
892                mathparameters.sup3          = newparameters[15] or 0                      -- sup3                   : superscript shift-up in cramped styles
893                mathparameters.sub1          = newparameters[16] or 0                      -- sub1                   : subscript shift-down if superscript is absent
894                mathparameters.sub2          = newparameters[17] or 0                      -- sub2                   : subscript shift-down if superscript is present
895                mathparameters.sup_drop      = newparameters[18] or 0                      -- sup_drop               : superscript baseline below top of large box
896                mathparameters.sub_drop      = newparameters[19] or 0                      -- sub_drop               : subscript baseline below bottom of large box
897                mathparameters.delim1        = newparameters[20] or 0                      -- delim1                 : size of \atopwithdelims delimiters in display styles
898                mathparameters.delim2        = newparameters[21] or 0                      -- delim2                 : size of \atopwithdelims delimiters in non-displays
899                mathparameters.axis_height   = newparameters[22] or 0                      -- axis_height            : height of fraction lines above the baseline
900            --  report_virtual("loading and virtualizing font %a at size %p, setting sy parameters",name,size)
901            end
902            if ss.overlay then
903                local fc    = fs.characters
904                local first = ss.first
905                if first then
906                    local last = ss.last or first
907                    for unicode = first, last do
908                        characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
909                    end
910                else
911                    for unicode, data in next, fc do
912                        characters[unicode] = copy_glyph(main,characters,fc,unicode,s)
913                    end
914                end
915            else
916                local vectorname = ss.vector
917                if vectorname then
918                    local offset      = 0xFF000 -- todo: -- private
919                    local vector      = mathencodings[vectorname]
920                    local rotcev      = reverse[vectorname]
921                    local isextension = ss.extension
922                    if vector and rotcev then
923                        local fc       = fs.characters
924                        local fd       = fs.descriptions
925                        local si       = shared[s]
926                        local fontname = fs.properties.name or "unknown"
927                        local skewchar = ss.skewchar
928                        for unicode, index in next, vector do
929                            local fci = fc[index]
930                            if not fci then
931                                local rf = reported[fontname]
932                                if not rf then rf = { } reported[fontname] = rf end
933                                local rv = rf[vectorname]
934                                if not rv then rv = { } rf[vectorname] = rv end
935                                local ru = rv[unicode]
936                                if not ru then
937                                    if trace_virtual then
938                                        report_virtual("unicode slot %U has no index %H in vector %a for font %a (%S)",unicode,index,vectorname,fontname,chardata[unicode].description)
939                                    elseif not already_reported then
940                                        report_virtual("the mapping is incomplete for %a at %p",name,size)
941                                        already_reported = true
942                                    end
943                                    rv[unicode] = true
944                                end
945                            else
946                                local ref = si[index]
947                                if not ref then
948                                    ref = { { 'slot', s, index } }
949                                    si[index] = ref
950                                end
951                                local kerns  = fci.kerns
952                                local width  = fci.width
953                                local italic = fci.italic
954                                if italic and italic > 0 then
955                                        -- int_a^b
956                                    if isextension then
957                                        width = width + italic -- for obscure reasons the integral as a width + italic correction
958                                    end
959                                end
960                                if kerns then
961                                    local krn = { }
962                                    for k, v in next, kerns do -- kerns is sparse
963                                        local rk = rotcev[k]
964                                        if rk then
965                                            krn[rk] = v -- kerns[k]
966                                        end
967                                    end
968                                    if not next(krn) then
969                                        krn = nil
970                                    end
971                                    local t = {
972                                        width    = width,
973                                        height   = fci.height,
974                                        depth    = fci.depth,
975                                        italic   = italic,
976                                        kerns    = krn,
977                                        commands = ref,
978                                    }
979                                    if skewchar then
980                                        local k = kerns[skewchar]
981                                        if k then
982                                            t.top_accent = width/2 + k
983                                        end
984                                    end
985                                    characters[unicode] = t
986                                else
987                                    characters[unicode] = {
988                                        width    = width,
989                                        height   = fci.height,
990                                        depth    = fci.depth,
991                                        italic   = italic,
992                                        commands = ref,
993                                    }
994                                end
995                            end
996                        end
997                        if isextension then
998                            -- todo: if multiple ex, then 256 offsets per instance
999                            local extension = mathencodings["large-to-small"]
1000                            local variants_done = fs.variants_done
1001                            for index, fci in next, fc do -- the raw ex file
1002                                if type(index) == "number" then
1003                                    local ref = si[index]
1004                                    if not ref then
1005                                        ref = { { 'slot', s, index } }
1006                                        si[index] = ref
1007                                    end
1008                                    local italic = fci.italic
1009                                    local t = {
1010                                        width    = fci.width,
1011                                        height   = fci.height,
1012                                        depth    = fci.depth,
1013                                        italic   = italic,
1014                                        commands = ref,
1015                                    }
1016                                    local n = fci.next
1017                                    if n then
1018                                        t.next = offset + n
1019                                    elseif variants_done then
1020                                        local vv = fci.vert_variants
1021                                        if vv then
1022                                            t.vert_variants = vv
1023                                        end
1024                                        local hv = fci.horiz_variants
1025                                        if hv then
1026                                            t.horiz_variants = hv
1027                                        end
1028                                    else
1029                                        local vv = fci.vert_variants
1030                                        if vv then
1031                                            for i=1,#vv do
1032                                                local vvi = vv[i]
1033                                                vvi.glyph = vvi.glyph + offset
1034                                            end
1035                                            t.vert_variants = vv
1036                                        end
1037                                        local hv = fci.horiz_variants
1038                                        if hv then
1039                                            for i=1,#hv do
1040                                                local hvi = hv[i]
1041                                                hvi.glyph = hvi.glyph + offset
1042                                            end
1043                                            t.horiz_variants = hv
1044                                        end
1045                                    end
1046                                    characters[offset + index] = t
1047                                end
1048                            end
1049                            fs.variants_done = true
1050                            for unicode, index in next, extension do
1051                                local cu = characters[unicode]
1052                                if cu then
1053                                    cu.next = offset + index
1054                                else
1055                                    local fci = fc[index]
1056                                    if not fci then
1057                                        -- do nothing
1058                                    else
1059                                        -- probably never entered
1060                                        local ref = si[index]
1061                                        if not ref then
1062                                            ref = { { 'slot', s, index } }
1063                                            si[index] = ref
1064                                        end
1065                                        local kerns = fci.kerns
1066                                        if kerns then
1067                                            local krn = { }
1068                                         -- for k=1,#kerns do
1069                                         --     krn[offset + k] = kerns[k]
1070                                         -- end
1071                                            for k, v in next, kerns do -- is kerns sparse?
1072                                                krn[offset + k] = v
1073                                            end
1074                                            characters[unicode] = {
1075                                                width    = fci.width,
1076                                                height   = fci.height,
1077                                                depth    = fci.depth,
1078                                                italic   = fci.italic,
1079                                                commands = ref,
1080                                                kerns    = krn,
1081                                                next     = offset + index,
1082                                            }
1083                                        else
1084                                            characters[unicode] = {
1085                                                width    = fci.width,
1086                                                height   = fci.height,
1087                                                depth    = fci.depth,
1088                                                italic   = fci.italic,
1089                                                commands = ref,
1090                                                next     = offset + index,
1091                                            }
1092                                        end
1093                                    end
1094                                end
1095                            end
1096                        end
1097                    else
1098                        report_virtual("error in loading %a, problematic vector %a",name,vectorname)
1099                    end
1100                end
1101            end
1102            mathematics.extras.copy(main) --not needed here (yet)
1103        end
1104    end
1105    --
1106    main.mathparameters = mathparameters -- still traditional ones
1107    -- This should change (some day) as it's the only place where we look forward,
1108    -- so better is to also reserve the id already which then involves some more
1109    -- management (so not now).
1110    fontlist[#fontlist+1] = {
1111     -- id   = font.nextid(),
1112        id   = 0, -- self
1113        size = size,
1114    }
1115    vfmath.addmissing(main,#fontlist,size)
1116    --
1117    mathematics.addfallbacks(main)
1118    --
1119    main.properties.math_is_scaled = true -- signal
1120    fonts.constructors.assignmathparameters(main,main)
1121    --
1122    main.MathConstants = main.mathparameters -- we directly pass it to TeX (bypasses the scaler) so this is needed
1123    --
1124    if trace_virtual or trace_timings then
1125        report_virtual("loading and virtualizing font %a at size %p took %0.3f seconds",name,size,os.clock()-start)
1126    end
1127    --
1128    main.oldmath = true
1129    return main
1130end
1131
1132function mathematics.makefont(name,set,goodies)
1133    fonts.definers.methods.variants[name] = function(specification)
1134        return vfmath.define(specification,set,goodies)
1135    end
1136end
1137
1138-- helpers
1139
1140function vfmath.setletters(font_encoding, name, uppercase, lowercase)
1141    local enc = font_encoding[name]
1142    for i = 0,25 do
1143        enc[uppercase+i] = i + 0x41
1144        enc[lowercase+i] = i + 0x61
1145    end
1146end
1147
1148function vfmath.setdigits(font_encoding, name, digits)
1149    local enc = font_encoding[name]
1150    for i = 0,9 do
1151        enc[digits+i] = i + 0x30
1152    end
1153end
1154