if not modules then modules = { } end modules ['math-vfu'] = { version = 1.001, comment = "companion to math-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- All these math vectors .. thanks to Aditya and Mojca they become better and -- better. If you have problems with math fonts or miss characters report it to the -- ConTeXt mailing list. Also thanks to Boguslaw for finding a couple of errors. -- Although this mechanism was a candidate for obsolence, the fact that Iwona and -- Antykwa are nice fonts (especially for display) I decided to keep it around but -- in a bit upgraded way. It is also a test for virtual tfm/pfb fonts that we keep -- around but hardly gets tested. However, much is still pretty old code, dating -- from when we emulated \UNICODE\ math and \OPENTYPE\ math fonts using traditional -- fonts. -- Musical timestamp: Januari 2023 Riverside ID.Entity release date, after all we -- only use the following code for ther Polish Iwona, Kurier and Antykwa fonts. local type, next, tonumber = type, next, tonumber local max = math.max local fastcopy, sortedhash = table.copy, table.sortedhash local fonts, mathematics = fonts, mathematics local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end) local trace_timings = false trackers.register("math.timings", function(v) trace_timings = v end) local add_optional = false directives.register("math.virtual.optional",function(v) add_optional = v end) local report_virtual = logs.reporter("fonts","virtual math") local allocate = utilities.storage.allocate local setmetatableindex = table.setmetatableindex local formatters = string.formatters local chardata = characters.data local mathencodings = allocate() fonts.encodings.math = mathencodings -- better is then: fonts.encodings.vectors local vfmath = allocate() fonts.handlers.vf.math = vfmath local helpers = fonts.helpers local addprivate = helpers.addprivate local vfcommands = helpers.commands local rightcommand = vfcommands.right local leftcommand = vfcommands.left local downcommand = vfcommands.down local upcommand = vfcommands.up local push = vfcommands.push local pop = vfcommands.pop local slotcommand = vfcommands.slot local function brace(main,characters,id,size,unicode,first,rule,left,right,rule,last) if not characters[unicode] then local template = characters[first] if template then if not characters[rule] then local width = template.width / 4 local height = template.height characters[rule] = { height = 3*height, depth = 2*height, width = width, commands = { push, { "rule", height, width }, pop }, } end characters[unicode] = { keepvirtual = true, partsorientation = "horizontal", parts = { { glyph = first }, { glyph = rule, extender = 1 }, { glyph = left }, { glyph = right }, { glyph = rule, extender = 1 }, { glyph = last }, } } end end end local function horibar(main,characters,id,size,unicode,rule) if not characters[unicode] then if not characters[rule] then local width = main.parameters .quad/4 or 4*65536 local height = main.mathparameters.defaultrulethickness or 4*65536/10 characters[rule] = { height = height, width = width, commands = { push, { "rule", height, width }, pop }, } end characters[unicode] = { keepvirtual = true, partsorientation = "horizontal", parts = { { glyph = rule }, { glyph = rule, extender = 1 }, } } end end local function parent(main,characters,id,size,unicode,first,rule,last,where) if not characters[unicode] then local template = characters[first] if template then if not characters[rule] then local width = template.width / 4 local height = template.height characters[rule] = { height = where == "top" and height or 3*height, depth = where == "top" and 2*height or 0, width = width, commands = { push, { "rule", height, width }, pop }, } end characters[unicode] = { keepvirtual = true, partsorientation = "horizontal", parts = { { glyph = first }, { glyph = rule, extender = 1 }, { glyph = last }, } } end end end -- local function extension(main,characters,id,size,unicode,first,middle,last) -- local chr = characters[unicode] -- if not chr then -- return -- skip -- end -- local fw = characters[first] -- if not fw then -- return -- end -- local mw = characters[middle] -- if not mw then -- return -- end -- local lw = characters[last] -- if not lw then -- return -- end -- fw = fw.width -- mw = mw.width -- lw = lw.width -- if fw == 0 then -- fw = 1 -- end -- if lw == 0 then -- lw = 1 -- end -- chr.partsorientation = "horizontal" -- chr.parts = { -- { extender = 0, glyph = first, ["end"] = fw/2, start = 0, advance = fw }, -- { extender = 1, glyph = middle, ["end"] = mw/2, start = mw/2, advance = mw }, -- { extender = 0, glyph = last, ["end"] = 0, start = lw/2, advance = lw }, -- } -- end -- local step = 0.2 -- 0.1 is nicer but gives larger files -- local function clipped(main,characters,id,size,unicode,original) -- push/pop needed? -- local minus = characters[original] -- if minus then -- local mu = size/18 -- local step = 3*mu -- local width = minus.width -- if width > step then -- width = width - step -- step = step / 2 -- else -- width = width / 2 -- step = width -- end -- characters[unicode] = { -- width = width, -- height = minus.height, -- depth = minus.depth, -- commands = { -- push, -- leftcommand[step], -- slotcommand[id][original], -- pop, -- } -- } -- end -- end local function dots(main,characters,id,size,unicode) local c = characters[0x002E] if c then local w = c.width local h = c.height local d = c.depth local mu = size/18 local right3mu = rightcommand[3*mu] local right1mu = rightcommand[1*mu] local up1size = upcommand[.1*size] local up4size = upcommand[.4*size] local up7size = upcommand[.7*size] local right2muw = rightcommand[2*mu + w] local slot = slotcommand[id][0x002E] if unicode == 0x22EF then local c = characters[0x022C5] if c then local width = c.width local height = c.height local depth = c.depth local slot = slotcommand[id][0x022C5] characters[unicode] = { width = 3*width + 2*3*mu, height = height, depth = depth, commands = { push, slot, right3mu, slot, right3mu, slot, pop, } } end elseif unicode == 0x22EE then characters[unicode] = { width = w, height = h+0.8*size, depth = 0, commands = { push, push, slot, pop, up4size, push, slot, pop, up4size, slot, pop, } } elseif unicode == 0x22F1 then characters[unicode] = { width = 3*w + 6*size/18, height = h+0.7*size, depth = 0, commands = { push, right1mu, push, up7size, slot, pop, right2muw, push, up4size, slot, pop, right2muw, push, up1size, slot, pop, right1mu, pop } } elseif unicode == 0x22F0 then characters[unicode] = { width = 3*w + 6*size/18, height = h+0.7*size, depth = 0, commands = { push, right1mu, push, up1size, slot, pop, right2muw, push, up4size, slot, pop, right2muw, push, up7size, slot, pop, right1mu, pop } } else characters[unicode] = { width = 3*w + 2*3*mu, height = h, depth = d, commands = { push, slot, right3mu, slot, right3mu, slot, pop, } } end end end -- local function vertbar(main,characters,id,size,parent,scale,unicode) -- local cp = characters[parent] -- if cp then -- local sc = scale * size -- local pc = slotcommand[id][parent] -- characters[unicode] = { -- width = cp.width, -- height = cp.height + sc, -- depth = cp.depth + sc, -- next = cp.next, -- can be extensible -- commands = { -- push, upcommand [sc], pc, pop, -- push, downcommand[sc], pc, pop, -- pc, -- }, -- } -- cp.next = unicode -- end -- end local function jointwo(main,characters,id,size,unicode,u1,d12,u2,what) local c1 = characters[u1] local c2 = characters[u2] if c1 and c2 then local w1 = c1.width local w2 = c2.width local width if d12 == false then d12 = 0 width = w2 else d12 = d12 * size/18 -- mu width = w1 + w2 - d12 end characters[unicode] = { width = width, height = max(c1.height or 0, c2.height or 0), depth = max(c1.depth or 0, c2.depth or 0), commands = { -- { "inspect" }, -- { "trace" }, slotcommand[id][u1], -- { "trace" }, d12 ~= 0 and leftcommand[d12] or false, slotcommand[id][u2], -- { "trace" }, }, } end end local function jointhree(main,characters,id,size,unicode,u1,d12,u2,d23,u3) local c1 = characters[u1] local c2 = characters[u2] local c3 = characters[u3] if c1 and c2 and c3 then local w1 = c1.width local w2 = c2.width local w3 = c3.width d12 = d12 * size/18 -- mu d23 = d23 * size/18 -- mu characters[unicode] = { width = w1 + w2 + w3 - d12 - d23, height = max(c1.height or 0, c2.height or 0, c3.height or 0), depth = max(c1.depth or 0, c2.depth or 0, c3.depth or 0), commands = { -- push, slotcommand[id][u1], -- pop, d12 ~= 0 and leftcommand[d12] or false, -- push, slotcommand[id][u2], -- pop, d23 ~= 0 and leftcommand[d23] or false, -- push, slotcommand[id][u3], -- pop, } } end end local function stack(main,characters,id,size,unicode,u1,d12,u2) local c1 = characters[u1] if not c1 then return end local c2 = characters[u2] if not c2 then return end local w1 = c1.width or 0 local h1 = c1.height or 0 local d1 = c1.depth or 0 local w2 = c2.width or 0 local h2 = c2.height or 0 local d2 = c2.depth or 0 local mu = size/18 characters[unicode] = { width = w1, height = h1 + h2 + d12*mu, depth = d1, commands = { slotcommand[id][u1], leftcommand[w1/2 + w2/2], downcommand[-h1 + d2 -d12*mu], slotcommand[id][u2], } } end local function repeated(main,characters,id,size,unicode,u,n,fraction) local c = characters[u] if c then if n == 1 then -- skip this one else local width = c.width local italic = fraction*width -- c.italic or 0 -- larger ones have funny italics local tc = slotcommand[id][u] local tr = leftcommand[italic] -- see hack elsewhere local commands = { } for i=1,n-1 do commands[#commands+1] = tc commands[#commands+1] = tr end commands[#commands+1] = tc local next = c.next if next then local p = addprivate(main,formatters["M-R-%H"](next)) repeated(main,characters,id,size,p,next,n,fraction) next = p end characters[unicode] = { width = width + (n-1)*(width-italic), height = c.height, depth = c.depth, italic = italic, commands = commands, keepvirtual = true, next = next, } end end end -- local function cloned(main,characters,id,size,source,target) -- local data = characters[source] -- if data then -- characters[target] = data -- return true -- end -- end -- we use the fact that context defines the smallest sizes first .. a real dirty and ugly hack -- todo: use privates as we don't need access by number local data_of_smaller = nil local size_of_smaller = 0 function vfmath.addmissing(main,id,size) local id_of_smaller = nil if size < size_of_smaller or size_of_smaller == 0 then data_of_smaller = main.fonts[id] id_of_smaller = id else id_of_smaller = #main.fonts + 1 main.fonts[id_of_smaller] = data_of_smaller end -- here id is the index in fonts (normally 14 or so) and that slot points to self local characters = main.characters local shared = main.shared local variables = main.goodies.mathematics and main.goodies.mathematics.variables or { } local joinrelfactor = variables.joinrelfactor or 3 brace (main,characters,id,size,0x23DE,0xFE07A,0xFE070,0xFE07D,0xFE07C,0xFE070,0xFE07B) brace (main,characters,id,size,0x23DF,0xFE07C,0xFE070,0xFE07B,0xFE07A,0xFE070,0xFE07D) parent (main,characters,id,size,0x23DC,0xFE07A,0xFE071,0xFE07B,"top") parent (main,characters,id,size,0x23DD,0xFE07C,0xFE072,0xFE07D,"bottom") dots (main,characters,id,size,0x2026) -- ldots dots (main,characters,id,size,0x22EE) -- vdots dots (main,characters,id,size,0x22EF) -- cdots dots (main,characters,id,size,0x22F1) -- ddots dots (main,characters,id,size,0x22F0) -- udots horibar (main,characters,id,size,0x203E,0xFE073) -- overbar underbar -- vertbar (main,characters,id,size,0x0007C,0.10,0xFF601) -- big : 0.85 bodyfontsize -- vertbar (main,characters,id,size,0xFF601,0.30,0xFF602) -- Big : 1.15 bodyfontsize -- vertbar (main,characters,id,size,0xFF602,0.30,0xFF603) -- bigg : 1.45 bodyfontsize -- vertbar (main,characters,id,size,0xFF603,0.30,0xFF604) -- Bigg : 1.75 bodyfontsize -- vertbar (main,characters,id,size,0x02016,0.10,0xFF605) -- vertbar (main,characters,id,size,0xFF605,0.30,0xFF606) -- vertbar (main,characters,id,size,0xFF606,0.30,0xFF607) -- vertbar (main,characters,id,size,0xFF607,0.30,0xFF608) -- clipped (main,characters,id,size,0xFF501,0x0002D) -- minus -- clipped (main,characters,id,size,0xFF502,0x02190) -- lefthead -- clipped (main,characters,id,size,0xFF503,0x02192) -- righthead -- clipped (main,characters,id,size,0xFF504,0xFE321) -- mapsto -- clipped (main,characters,id,size,0xFF505,0xFE322) -- lhook -- clipped (main,characters,id,size,0xFF506,0xFE323) -- rhook -- clipped (main,characters,id,size,0xFF507,0xFE324) -- mapsfrom -- clipped (main,characters,id,size,0xFF508,0x021D0) -- double lefthead -- clipped (main,characters,id,size,0xFF509,0x021D2) -- double righthead -- clipped (main,characters,id,size,0xFF50A,0x0003D) -- equal -- clipped (main,characters,id,size,0xFF50B,0x0219E) -- lefttwohead -- clipped (main,characters,id,size,0xFF50C,0x021A0) -- righttwohead -- clipped (main,characters,id,size,0xFF50D,0xFF350) -- lr arrow combi snippet -- clipped (main,characters,id,size,0xFF50E,0xFF351) -- lr arrow combi snippet -- clipped (main,characters,id,size,0xFF50F,0xFF352) -- lr arrow combi snippet -- clipped (main,characters,id,size,0xFF510,0x02261) -- equiv -- extension(main,characters,id,size,0x2190,0xFF502,0xFF501,0xFF501) -- \leftarrow -- extension(main,characters,id,size,0x2192,0xFF501,0xFF501,0xFF503) -- \rightarrow -- extension(main,characters,id,size,0x002D,0xFF501,0xFF501,0xFF501) -- \rel -- extension(main,characters,id,size,0x003D,0xFF50A,0xFF50A,0xFF50A) -- \equal -- extension(main,characters,id,size,0x2261,0xFF510,0xFF510,0xFF510) -- \equiv -- jointwo (main,characters,id,size,0x21A6,0xFE321,0,0x02192) -- \mapstochar\rightarrow -- jointwo (main,characters,id,size,0x21A9,0x02190,joinrelfactor,0xFE323) -- \leftarrow\joinrel\rhook -- jointwo (main,characters,id,size,0x21AA,0xFE322,joinrelfactor,0x02192) -- \lhook\joinrel\rightarrow jointwo (main,characters,id,size,0x27F5,0x02190,joinrelfactor,0x0002D) -- \leftarrow\joinrel\relbar jointwo (main,characters,id,size,0x27F6,0x0002D,joinrelfactor,0x02192,2) -- \relbar\joinrel\rightarrow jointwo (main,characters,id,size,0x27F7,0x02190,joinrelfactor,0x02192) -- \leftarrow\joinrel\rightarrow jointwo (main,characters,id,size,0x27F8,0x021D0,joinrelfactor,0x0003D) -- \Leftarrow\joinrel\Relbar jointwo (main,characters,id,size,0x27F9,0x0003D,joinrelfactor,0x021D2) -- \Relbar\joinrel\Rightarrow jointwo (main,characters,id,size,0x27FA,0x021D0,joinrelfactor,0x021D2) -- \Leftarrow\joinrel\Rightarrow -- jointhree(main,characters,id,size,0x27FB,0x02190,joinrelfactor,0x0002D,0,0xFE324) -- \leftarrow\joinrel\relbar\mapsfromchar -- jointhree(main,characters,id,size,0x27FC,0xFE321,0,0x0002D,joinrelfactor,0x02192) -- \mapstochar\relbar\joinrel\rightarrow -- extension(main,characters,id,size,0x21A6,0xFF504,0xFF501,0xFF503) -- \mapstochar\rightarrow -- extension(main,characters,id,size,0x21A9,0xFF502,0xFF501,0xFF506) -- \leftarrow\joinrel\rhook -- extension(main,characters,id,size,0x21AA,0xFF505,0xFF501,0xFF503) -- \lhook\joinrel\rightarrow -- extension(main,characters,id,size,0x27F5,0xFF502,0xFF501,0xFF501) -- \leftarrow\joinrel\relbar -- extension(main,characters,id,size,0x27F6,0xFF501,0xFF501,0xFF503) -- \relbar\joinrel\rightarrow -- extension(main,characters,id,size,0x27F7,0xFF502,0xFF501,0xFF503) -- \leftarrow\joinrel\rightarrow -- extension(main,characters,id,size,0x27F8,0xFF508,0xFF50A,0xFF50A) -- \Leftarrow\joinrel\Relbar -- extension(main,characters,id,size,0x27F9,0xFF50A,0xFF50A,0xFF509) -- \Relbar\joinrel\Rightarrow -- extension(main,characters,id,size,0x27FA,0xFF508,0xFF50A,0xFF509) -- \Leftarrow\joinrel\Rightarrow -- extension(main,characters,id,size,0x27FB,0xFF502,0xFF501,0xFF507) -- \leftarrow\joinrel\relbar\mapsfromchar -- extension(main,characters,id,size,0x27FC,0xFF504,0xFF501,0xFF503) -- \mapstochar\relbar\joinrel\rightarrow -- extension(main,characters,id,size,0x219E,0xFF50B,0xFF501,0xFF501) -- \twoheadleftarrow\joinrel\relbar -- extension(main,characters,id,size,0x21A0,0xFF501,0xFF501,0xFF50C) -- \relbar\joinrel\twoheadrightarrow -- extension(main,characters,id,size,0x21C4,0xFF50D,0xFF50E,0xFF50F) -- leftoverright -- 21CB leftrightharpoon -- 21CC rightleftharpoon stack(main,characters,id,size,0x2259,0x0003D,3,0x02227) -- \buildrel\wedge\over= jointwo(main,characters,id,size,0x22C8,0x022B3,joinrelfactor,0x022B2) -- \mathrel\triangleright\joinrel\mathrel\triangleleft (4 looks better than 3) jointwo(main,characters,id,size,0x22A7,0x0007C,joinrelfactor,0x0003D) -- \mathrel|\joinrel= jointwo(main,characters,id,size,0x2260,0x00338,0,0x0003D) -- \not\equal jointwo(main,characters,id,size,0x2284,0x00338,false,0x02282) -- \not\subset jointwo(main,characters,id,size,0x2285,0x00338,false,0x02283) -- \not\supset jointwo(main,characters,id,size,0x2209,0x00338,false,0x02208) -- \not\in jointwo(main,characters,id,size,0x2254,0x03A,0,0x03D) -- := (≔) repeated(main,characters,id,size,0x222B,0x222B,1,1/2) repeated(main,characters,id,size,0x222C,0x222B,2,1/2) repeated(main,characters,id,size,0x222D,0x222B,3,1/2) characters[0x02B9] = characters[0x2032] -- we're nice data_of_smaller = main.fonts[id] size_of_smaller = size end local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage local reported = { } local reverse = { } -- index -> unicode setmetatableindex(reverse, function(t,name) if trace_virtual then report_virtual("initializing math vector %a",name) end local m = mathencodings[name] local r = { } for u, i in next, m do r[i] = u end reverse[name] = r return r end) -- Used in fallbacks (might move): local function copy_glyph(main,target,original,unicode,slot) local olddata = original[unicode] if olddata then local newdata = { width = olddata.width, height = olddata.height, depth = olddata.depth, italic = olddata.italic, topanchor = olddata.topanchor, kerns = olddata.kerns, mathkerns = olddata.mathkerns, tounicode = olddata.tounicode, commands = { slotcommand[slot][unicode] }, } local glyphdata = newdata local nextglyph = olddata.next while nextglyph do local oldnextdata = original[nextglyph] if oldnextdata then local newnextdata = { width = oldnextdata.width, height = oldnextdata.height, depth = oldnextdata.depth, italic = oldnextdata.italic, topanchor = oldnextdata.topanchor, kerns = olddata.kerns, mathkerns = olddata.mathkerns, tounicode = olddata.tounicode, smaller = olddata.smaller, commands = { slotcommand[slot][nextglyph] }, } local newnextglyph = addprivate(main,formatters["M-N-%H"](nextglyph),newnextdata) newdata.next = newnextglyph local nextnextglyph = oldnextdata.next if nextnextglyph == nextglyph then break else olddata = oldnextdata newdata = newnextdata nextglyph = nextnextglyph end else break -- safeguard (when testing stuff) end end local oldparts = olddata.parts if oldparts then newparts = fastcopy(oldparts) newdata.parts = newparts newdata.partsorientation = olddata.partsorientation newdata.partsitalic = olddata.partsitalic for i=1,#newparts do local newpart = newparts[i] local oldglyph = newpart.glyph local olddata = original[oldglyph] local newdata = { width = olddata.width, height = olddata.height, depth = olddata.depth, tounicode = olddata.tounicode, commands = { slotcommand[slot][oldglyph] }, } newpart.glyph = addprivate(main,formatters["M-P-%H"](oldglyph),newdata) end end local smaller = olddata.smaller if smaller then local smallerdata = copy_glyph(main,target,original,smaller,slot) glyphdata.smaller = addprivate(main,formatters["M-S-%H"](smaller),smallerdata) end return glyphdata end end vfmath.copy_glyph = copy_glyph -- It's time to get rid of this type 1 mess ... take iwona: uppercase /A .. Z but -- lowercase /a.math ... /z.math ... anyway, we now follow a slightly different -- route: use the "order" field. I can probably make it a bit leaner but it's not -- worth spending much time on now. local noitalics = true -- false can be used to test the engine -- revision timestamp 2023: after watching ten times "Over The Mountain (feat. -- Sierra Hull)" - Cory Wong (Live @ Brooklyn Steel FEB 2022): -- -- https://www.youtube.com/watch?v=lT-W-UEcsns -- https://www.youtube.com/watch?v=TPGbj1gFalA -- The following code is now only used for iwona and antykwa, so I simplified it a -- bit to suit that purpose. It might get even simpler. local function virtualize(s,uni,fci,skewchar,move,mathparameters,unicode,parameters) if fci then local kerns = fci.kerns local width = fci.width local height = fci.height local depth = fci.depth local italic = fci.italic local advance = width local bottomright local topanchor local yoffset if kerns and skewchar then local k = kerns[skewchar] if k then topanchor = width/2 + k end end if italic and noitalics then width = width + italic bottomright = - italic italic = nil end if move then -- 0x222B local axis = move * mathparameters.axisheight local half = (height + depth ) / 2 yoffset = depth - (half - axis) height = half + axis depth = half - axis end -- return { advance = advance, width = width, height = height, depth = depth, italic = italic, bottomright = bottomright, topanchor = topanchor, yoffset = yoffset, commands = { slotcommand[s][uni] }, -- keepvirtual = true, next = fci.next, parts = fci.parts, partsorientation = fci.partsorientation, partsitalic = fci.partsitalic, unicode = unicode, name = fci.name, } else -- error end end function vfmath.define(specification,set,goodies) local name = specification.name -- symbolic name local size = specification.size -- given size local loaded = { } local fontlist = { } local names = { } local main = nil local start = (trace_virtual or trace_timings) and os.clock() local okset = { } local n = 0 local f_extra = formatters["virtual.extra.%05X"] for s=1,#set do local ss = set[s] local ssname = ss.name if add_optional and ss.optional then if trace_virtual then report_virtual("loading font %a subfont %s with name %a at %p is skipped",name,s,ssname,size) end else if ss.features then ssname = ssname .. "*" .. ss.features end if ss.main then main = s end local alreadyloaded = names[ssname] -- for px we load one twice (saves .04 sec) local f, id if alreadyloaded then f, id = alreadyloaded.f, alreadyloaded.id if trace_virtual then report_virtual("loading font %a subfont %s with name %a is reused",name,s,ssname) end else f, id = fonts.constructors.readanddefine(ssname,size) names[ssname] = { f = f, id = id, fontname = ssname, -- diagnostics } end if not f or id == 0 then report_virtual("loading font %a subfont %s with name %a at %p is skipped, not found",name,s,ssname,size) else n = n + 1 okset[n] = ss loaded[n] = f fontlist[n] = { id = id, size = size, fontname = ssname, -- diagnostics } if trace_virtual then 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) end end end end -- beware, loaded[1] is already passed to tex (we need to make a simple copy then .. todo) local parent = loaded[1] or { } -- a text font local characters = { } local parameters = { } local mathparameters = { } local descriptions = { } local metadata = { } local properties = { hasitalics = true, hasmath = true, } local goodies = { } local main = { metadata = metadata, properties = properties, characters = characters, descriptions = descriptions, parameters = parameters, mathparameters = mathparameters, fonts = fontlist, goodies = goodies, -- fullname = properties.fullname, nomath = false, } -- for key, value in next, parent do if type(value) ~= "table" then main[key] = value end end -- if parent.characters then for unicode, character in next, parent.characters do characters[unicode] = character end else report_virtual("font %a has no characters",name) end -- if parent.parameters then for key, value in next, parent.parameters do parameters[key] = value end else report_virtual("font %a has no parameters",name) end -- local description = { name = "" } setmetatableindex(descriptions,function() return description end) -- if parent.properties then setmetatableindex(properties,parent.properties) end -- if parent.goodies then setmetatableindex(goodies,parent.goodies) end -- local fullname = properties.fullname -- parent via mt if fullname then unique = unique + 1 properties.fullname = fullname .. "-" .. unique end -- if not parameters.xheight then parameters.xheight = 0 end -- local already_reported = false local parameters_done = false local offset = 0 -- 0xFF000 -- todo: -- private -- for s=1,n do local ss = okset[s] local fs = loaded[s] if not fs then -- skip, error elseif add_optional and ss.optional then -- skip, redundant else local newparameters = fs.parameters local newmathparameters = fs.mathparameters and ss.parameters ~= false if newmathparameters then if not parameters_done or ss.parameters then mathparameters = newmathparameters parameters_done = true end elseif not newparameters then report_virtual("no parameters set in font %a",name) elseif ss.extension then mathparameters.xheight = newparameters.xheight or 0 -- mathxheight : height of x mathparameters.defaultrulethickness = newparameters[ 8] or 0 -- defaultrulethickness : thickness of \over bars mathparameters.bigopspacing1 = newparameters[ 9] or 0 -- bigopspacing1 : minimum clearance above a displayed op mathparameters.bigopspacing2 = newparameters[10] or 0 -- bigopspacing2 : minimum clearance below a displayed op mathparameters.bigopspacing3 = newparameters[11] or 0 -- bigopspacing3 : minimum baselineskip above displayed op mathparameters.bigopspacing4 = newparameters[12] or 0 -- bigopspacing4 : minimum baselineskip below displayed op mathparameters.bigopspacing5 = newparameters[13] or 0 -- bigopspacing5 : padding above and below displayed limits -- report_virtual("loading and virtualizing font %a at size %p, setting ex parameters",name,size) elseif ss.parameters then mathparameters.xheight = newparameters.xheight or mathparameters.xheight or fs.xheight or 0 -- xheight : height of x mathparameters.num1 = newparameters[ 8] or 0 -- num1 : numerator shift-up in display styles mathparameters.num2 = newparameters[ 9] or 0 -- num2 : numerator shift-up in non-display, non-\atop mathparameters.num3 = newparameters[10] or 0 -- num3 : numerator shift-up in non-display \atop mathparameters.denom1 = newparameters[11] or 0 -- denom1 : denominator shift-down in display styles mathparameters.denom2 = newparameters[12] or 0 -- denom2 : denominator shift-down in non-display styles mathparameters.sup1 = newparameters[13] or 0 -- sup1 : superscript shift-up in uncramped display style mathparameters.sup2 = newparameters[14] or 0 -- sup2 : superscript shift-up in uncramped non-display mathparameters.sup3 = newparameters[15] or 0 -- sup3 : superscript shift-up in cramped styles mathparameters.sub1 = newparameters[16] or 0 -- sub1 : subscript shift-down if superscript is absent mathparameters.sub2 = newparameters[17] or 0 -- sub2 : subscript shift-down if superscript is present mathparameters.sup_drop = newparameters[18] or 0 -- sup_drop : superscript baseline below top of large box mathparameters.sub_drop = newparameters[19] or 0 -- sub_drop : subscript baseline below bottom of large box mathparameters.delim1 = newparameters[20] or 0 -- delim1 : size of \atopwithdelims delimiters in display styles mathparameters.delim2 = newparameters[21] or 0 -- delim2 : size of \atopwithdelims delimiters in non-displays mathparameters.axisheight = newparameters[22] or 0 -- axisheight : height of fraction lines above the baseline -- report_virtual("loading and virtualizing font %a at size %p, setting sy parameters",name,size) end -- We no longer care about kerns and ligatures here. We use backmack because we need to know -- the original order and the loader has made a unicode font of it and weird glyph names have -- spoiled that a bit too. if ss.overlay then -- This branch / option will go away. local fc = fs.characters local first = ss.first if first then local last = ss.last or first for unicode = first, last do characters[unicode] = copy_glyph(main,characters,fc,unicode,s) end else for unicode, data in next, fc do characters[unicode] = copy_glyph(main,characters,fc,unicode,s) end end else local vectorname = ss.vector if vectorname then local vector = mathencodings[vectorname] local isextension = ss.extension if vector then local fc = fs.characters local fd = fs.descriptions local fp = fs.parameters local fontname = fs.properties.name or "unknown" local skewchar = ss.skewchar local backmap = ss.backmap local badones = ss.badones local ignore = ss.ignore local done = { } local extras = { } if backmap == false then -- backmap = { } elseif not backmap then backmap = { } for unicode, character in next, fc do backmap[character.order or character.index or unicode] = unicode end ss.backmap = backmap end for unicode, index in sortedhash(vector) do local uni = backmap and backmap[index] or index local fci = fc[uni] if not fci then local rf = reported[fontname] if not rf then rf = { } reported[fontname] = rf end local rv = rf[vectorname] if not rv then rv = { } rf[vectorname] = rv end local ru = rv[unicode] if not ru then if trace_virtual then local d = chardata[unicode].description if index then report_virtual("character %C has no index %H in vector %a for font %a (%S)",unicode,index,vectorname,fontname,d) else report_virtual("character %C has no entry in vector %a for font %a (%S)",unicode,vectorname,fontname,d) end elseif not already_reported then report_virtual("the mapping is incomplete for %a at %p",name,size) already_reported = true end rv[unicode] = true end else local name = fci.name or "" if ignore and ignore[name] then -- get rid of ugly slanted antykwa { } else local u = mathematics.gaps[unicode] or unicode local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[name]),mathparameters,u,fp) done[uni] = t characters[unicode] = t fci.unicode = u end end end if isextension then local extension = mathencodings["large-to-small"] for uni, fci in sortedhash(fc) do if not done[uni] then local t = virtualize(s,uni,fci,skewchar,tonumber(badones and badones[fci.name or ""]),mathparameters,nil,fp) local o = addprivate(main,f_extra(offset)) extras[uni] = o characters[o] = t done[uni] = t offset = offset + 1 end end for uni, fci in sortedhash(done) do local next = fci.next if next then fci.next = extras[backmap and backmap[next] or next] end local parts = fci.parts if parts then local p = table.copy(parts) for i=1,#p do local part = p[i] local glyph = part.glyph part.glyph = extras[backmap and backmap[glyph] or glyph] or glyph end fci.keepvirtual = true fci.parts = p fci.partsorientation = "vertical" fci.partsitalic = fci.partsitalic or fci.italic end end for unicode, index in sortedhash(extension) do local fci = characters[unicode] if fci then fci.next = extras[backmap[index] or index] end end local extension = mathencodings["large-to-small-private"] for unicode, index in sortedhash(extension) do if not characters[unicode] then local uni = backmap and backmap[index] or index local fci = fc[uni] characters[unicode] = virtualize(s,uni,fci,skewchar,false,mathparameters,unicode,fp) end end end else report_virtual("error in loading %a, problematic vector %a",name,vectorname) end end end -- mathematics.extras.copy(main) -- Not needed here (yet) ... might go. end end -- main.mathparameters = mathparameters -- still traditional ones fontlist[#fontlist+1] = { id = 0, size = size, fontname = name, -- diagnostics } vfmath.addmissing(main,#fontlist,size) -- mathematics.addfallbacks(main) -- main.properties.math_is_scaled = true -- signal fonts.constructors.assignmathparameters(main,main) -- mathematics.initializeparameters(main,main,"noscale") main.mathconstants = main.mathparameters -- we directly pass it to TeX (bypasses the scaler) so this is needed main.MathConstants = main.mathconstants main.nomath = false -- if trace_virtual or trace_timings then report_virtual("loading and virtualizing font %a at size %p took %0.3f seconds",name,size,os.clock()-start) end -- return main end function mathematics.makefont(name,set,goodies) fonts.definers.methods.variants[name] = function(specification) return vfmath.define(specification,set,goodies) end end -- helpers (todo: gaps) function vfmath.setletters(font_encoding, name, uppercase, lowercase) local enc = font_encoding[name] for i = 0,25 do enc[uppercase+i] = i + 0x41 enc[lowercase+i] = i + 0x61 end end function vfmath.setdigits(font_encoding, name, digits) local enc = font_encoding[name] for i = 0,9 do enc[digits+i] = i + 0x30 end end