diff options
Diffstat (limited to 'tex/context/base/mkiv/math-act.lua')
-rw-r--r-- | tex/context/base/mkiv/math-act.lua | 436 |
1 files changed, 204 insertions, 232 deletions
diff --git a/tex/context/base/mkiv/math-act.lua b/tex/context/base/mkiv/math-act.lua index 5aa3f0ff0..a77fdc020 100644 --- a/tex/context/base/mkiv/math-act.lua +++ b/tex/context/base/mkiv/math-act.lua @@ -6,10 +6,13 @@ if not modules then modules = { } end modules ['math-act'] = { license = "see context related readme files" } --- Here we tweak some font properties (if needed). +-- Here we tweak some font properties (if needed). Per mid octover 2022 we also provide +-- an lmtx emulation mode which means that we removed some other code. Some of that was +-- experimental, some transitional, some is now obsolete). Using emulation mode also +-- means that we are unlikely to test some aspects of the math engines extensively. local type, next = type, next -local fastcopy, insert, remove = table.fastcopy, table.insert, table.remove +local fastcopy, insert, remove, copytable = table.fastcopy, table.insert, table.remove, table.copy local formatters = string.formatters local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end) @@ -75,41 +78,43 @@ local how = { NoLimitSubFactor = "unscaled", } -function mathematics.scaleparameters(target,original) - if not target.properties.math_is_scaled then - local mathparameters = target.mathparameters - if mathparameters and next(mathparameters) then - local parameters = target.parameters - local factor = parameters.factor - local hfactor = parameters.hfactor - local vfactor = parameters.vfactor - for name, value in next, mathparameters do - local h = how[name] - if h == "unscaled" then - -- kept - elseif h == "horizontal" then - value = value * hfactor - elseif h == "vertical"then - value = value * vfactor - else - value = value * factor - end - mathparameters[name] = value +local function scaleparameters(mathparameters,parameters) + if mathparameters and next(mathparameters) and parameters then + local factor = parameters.factor + local hfactor = parameters.hfactor + local vfactor = parameters.vfactor + for name, value in next, mathparameters do + local h = how[name] + if h == "unscaled" then + -- kept + elseif h == "horizontal" then + value = value * hfactor + elseif h == "vertical"then + value = value * vfactor + else + value = value * factor end + mathparameters[name] = value end - target.properties.math_is_scaled = true end end --- AccentBaseHeight vs FlattenedAccentBaseHeight - -function mathematics.checkaccentbaseheight(target,original) - local mathparameters = target.mathparameters - if mathparameters and mathparameters.AccentBaseHeight == 0 then - mathparameters.AccentBaseHeight = target.parameters.x_height -- needs checking +function mathematics.scaleparameters(target,original) + if not target.properties.math_is_scaled then + scaleparameters(target.mathparameters,target.parameters) + target.properties.math_is_scaled = true end end +-- -- AccentBaseHeight vs FlattenedAccentBaseHeight +-- +-- function mathematics.checkaccentbaseheight(target,original) +-- local mathparameters = target.mathparameters +-- if mathparameters and mathparameters.AccentBaseHeight == 0 then +-- mathparameters.AccentBaseHeight = target.parameters.x_height -- needs checking +-- end +-- end + function mathematics.checkprivateparameters(target,original) local mathparameters = target.mathparameters if mathparameters then @@ -180,29 +185,65 @@ function mathematics.overloadparameters(target,original) end end +local mathtweaks = { subsets = table.setmetatableindex("table") } +mathematics.tweaks = mathtweaks + +local apply_tweaks = true + +directives.register("math.applytweaks", function(v) + apply_tweaks = v; +end) + local function applytweaks(when,target,original) - local goodies = original.goodies - if goodies then - for i=1,#goodies do - local goodie = goodies[i] - local mathematics = goodie.mathematics - local tweaks = mathematics and mathematics.tweaks - if type(tweaks) == "table" then - tweaks = tweaks[when] - if type(tweaks) == "table" then - if trace_defining then - report_math("tweaking math of %a @ %p (%s)",target.properties.fullname,target.parameters.size,when) - end - for i=1,#tweaks do - local tweak= tweaks[i] - local tvalue = type(tweak) - if tvalue == "function" then - tweak(target,original) + if apply_tweaks then + local goodies = original.goodies + if goodies then + local tweaked = target.tweaked or { } + if tweaked[when] then + if trace_defining then + report_math("tweaking math of %a @ %p (%s: %s)",target.properties.fullname,target.parameters.size,when,"done") + end + else + for i=1,#goodies do + local goodie = goodies[i] + local mathematics = goodie.mathematics + local tweaks = mathematics and mathematics.tweaks + if type(tweaks) == "table" then + tweaks = tweaks[when] + if type(tweaks) == "table" then + if trace_defining then + report_math("tweaking math of %a @ %p (%s: %s)",target.properties.fullname,target.parameters.size,when,"okay") + end + for i=1,#tweaks do + local tweak = tweaks[i] + local tvalue = type(tweak) + if type(tweak) == "table" then + local action = mathtweaks[tweak.tweak or ""] + if action then + local feature = tweak.feature + local features = target.specification.features.normal + if not feature or features[feature] == true then + local version = tweak.version + if version and version ~= target.tweakversion then + report_math("skipping tweak %a version %a",tweak.tweak,version) + elseif original then + action(target,original,tweak) + else + action(target,tweak) + end + end + end + end + end end end end + tweaked[when] = true + target.tweaked = tweaked end end + else + report_math("not tweaking math of %a @ %p (%s)",target.properties.fullname,target.parameters.size,when) end end @@ -222,118 +263,130 @@ end sequencers.appendaction("mathparameters","system","mathematics.overloadparameters") sequencers.appendaction("mathparameters","system","mathematics.scaleparameters") -sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead +----------.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters") -- after scaling ! sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont") sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont") --- no, it's a feature now (see good-mth): --- --- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions") - --- a couple of predefined tweaks: - -local tweaks = { subsets = { } } -mathematics.tweaks = tweaks - --- function tweaks.fixbadprime(target,original) --- target.characters[0xFE325] = target.characters[0x2032] --- end - --- these could go to math-fbk - --- local virtualized = mathematics.virtualized --- --- local function accent_to_extensible(target,newchr,original,oldchr,height,depth,swap) --- local characters = target.characters --- -- if not characters[newchr] then -- xits needs an enforce --- local addprivate = fonts.helpers.addprivate --- local olddata = characters[oldchr] --- if olddata then --- if swap then --- swap = characters[swap] --- height = swap.depth --- depth = 0 --- else --- height = height or 0 --- depth = depth or 0 --- end --- local correction = swap and { "down", (olddata.height or 0) - height } or { "down", olddata.height } --- local newdata = { --- commands = { correction, { "slot", 1, oldchr } }, --- width = olddata.width, --- height = height, --- depth = depth, --- } --- characters[newchr] = newdata --- local nextglyph = olddata.next --- while nextglyph do --- local oldnextdata = characters[nextglyph] --- local newnextdata = { --- commands = { correction, { "slot", 1, nextglyph } }, --- width = oldnextdata.width, --- height = height, --- depth = depth, --- } --- local newnextglyph = addprivate(target,formatters["original-%H"](nextglyph),newnextdata) --- newdata.next = newnextglyph --- local nextnextglyph = oldnextdata.next --- if nextnextglyph == nextglyph then --- break --- else --- olddata = oldnextdata --- newdata = newnextdata --- nextglyph = nextnextglyph --- end --- end --- local hv = olddata.horiz_variants --- if hv then --- hv = fastcopy(hv) --- newdata.horiz_variants = hv --- for i=1,#hv do --- local hvi = hv[i] --- local oldglyph = hvi.glyph --- local olddata = characters[oldglyph] --- local newdata = { --- commands = { correction, { "slot", 1, oldglyph } }, --- width = olddata.width, --- height = height, --- depth = depth, --- } --- hvi.glyph = addprivate(target,formatters["original-%H"](oldglyph),newdata) --- end --- end --- end --- -- end --- end - --- function tweaks.fixoverline(target,original) --- local height, depth = 0, 0 --- local mathparameters = target.mathparameters --- if mathparameters then --- height = mathparameters.OverbarVerticalGap --- depth = mathparameters.UnderbarVerticalGap --- else --- height = target.parameters.xheight/4 --- depth = height --- end --- accent_to_extensible(target,0x203E,original,0x0305,height,depth) --- -- also crappy spacing for our purpose: push to top of baseline --- accent_to_extensible(target,0xFE3DE,original,0x23DE,height,depth,0x23DF) --- accent_to_extensible(target,0xFE3DC,original,0x23DC,height,depth,0x23DD) --- accent_to_extensible(target,0xFE3B4,original,0x23B4,height,depth,0x23B5) --- -- for symmetry --- target.characters[0xFE3DF] = original.characters[0x23DF] --- target.characters[0xFE3DD] = original.characters[0x23DD] --- target.characters[0xFE3B5] = original.characters[0x23B5] --- -- inspect(fonts.helpers.expandglyph(target.characters,0x203E)) --- -- inspect(fonts.helpers.expandglyph(target.characters,0x23DE)) --- end +do + + -- More than a year of testing, development, tweaking (and improving) fonts has resulted + -- in a math engine in \LUAMETATEX\ that is quite flexible. Basically we can drop italic + -- correction there. In \MKIV\ we can emulate this to some extend but we still need a bit + -- of mix because \LUAMETATEX\ lacks some features. A variant of the tweak below is now + -- also used in the plain code we ship. In \MKIV\ we dropped a few features that were a + -- prelude to this and, because most users switched to \LMTX, it is unlikely that other + -- tweaks wil be backported. There is also no need to adapt \LUATEX\ and eventually all + -- italic code might be removed from \LUAMETATEX\ (unless we want to be able to test the + -- alternative; I can live with a little ballast, especially because it took time to load + -- it). + + local italics = nil + local integrals = table.tohash { + 0x0222B, 0x0222C, 0x0222D, 0x0222E, 0x0222F, 0x02230, 0x02231, 0x02232, 0x02233, + 0x02A0B, 0x02A0C, 0x02A0D, 0x02A0E, 0x02A0F, 0x02A10, 0x02A11, 0x02A12, 0x02A13, + 0x02A14, 0x02A15, 0x02A16, 0x02A17, 0x02A18, 0x02A19, 0x02A1A, 0x02A1B, 0x02A1C, + 0x02320, 0x02321 + } + + function mathtweaks.emulatelmtx(target,original,parameters) + -- gaps are not known yet + if not italic then + italics = { } + local gaps = mathematics.gaps + for name, data in next, characters.blocks do + if data.math and data.italic then + for i=data.first,data.last do + italics[i] = true + local g = gaps[i] + if g then + italics[g] = true + end + end + end + end +-- table.save("temp.log", table.sortedkeys(italics)) + end + -- + local targetcharacters = target.characters + local targetdescriptions = target.descriptions + local factor = target.parameters.factor + local function getllx(u) + local d = targetdescriptions[u] + if d then + local b = d.boundingbox + if b then + local llx = b[1] + if llx < 0 then + return - llx + end + end + end + return false + end + -- beware: here we also do the weird ones + for u, c in next, targetcharacters do + local uc = c.unicode or u + if integrals[uc] then + -- skip this one + else + local accent = c.top_accent + local italic = c.italic + local width = c.width or 0 + local llx = getllx(u) + local bl, br, tl, tr + if llx then + llx = llx * factor + width = width + llx + bl = - llx + tl = bl + c.commands = { rightcommand[llx], charcommand[u] } + if accent then + accent = accent + llx + end + end + if accent then + if italics[uc] then + c.top_accent = accent + else + c.top_accent = nil + end + end + if italic and italic ~= 0 then + width = width + italic + br = - italic + end + c.width = width + if italic then + c.italic = nil + end + if bl or br or tl or tr then + -- watch out: singular and _ because we are post copying / scaling + c.mathkern = { + bottom_left = bl and { { height = 0, kern = bl } } or nil, + bottom_right = br and { { height = 0, kern = br } } or nil, + top_left = tl and { { height = c.height or 0, kern = tl } } or nil, + top_right = tr and { { height = c.height or 0, kern = tr } } or nil, + } + end + end + end + end --- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweaks.fixoverline") -- for the moment always + function mathtweaks.parameters(target,original,parameters) + local newparameters = parameters.list + local oldparameters = target.mathparameters + if newparameters and oldparameters then + newparameters = copytable(newparameters) + scaleparameters(newparameters,target.parameters) + for name, newvalue in next, newparameters do + oldparameters[name] = newvalue + end + end + end --- helpers +end local setmetatableindex = table.setmetatableindex @@ -467,87 +520,6 @@ interfaces.implement { end } --- experiment - --- check: when true, only set when present in font --- force: when false, then not set when already set - --- todo: tounicode - --- function mathematics.injectfallbacks(target,original) --- local properties = original.properties --- if properties and properties.hasmath then --- local specification = target.specification --- if specification then --- local fallbacks = specification.fallbacks --- if fallbacks then --- local definitions = fonts.collections.definitions[fallbacks] --- if definitions then --- if trace_collecting then --- report_math("adding fallback characters to font %a",specification.hash) --- end --- local definedfont = fonts.definers.internal --- local copiedglyph = fonts.handlers.vf.math.copy_glyph --- local fonts = target.fonts --- local size = specification.size -- target.size --- local characters = target.characters --- if not fonts then --- fonts = { } --- target.fonts = fonts --- if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then --- target.type = "virtual" --- target.properties.virtualized = true --- end --- end --- if #fonts == 0 then --- fonts[1] = { id = 0, size = size } -- sel, will be resolved later --- end --- local done = { } --- for i=1,#definitions do --- local definition = definitions[i] --- local name = definition.font --- local start = definition.start --- local stop = definition.stop --- local gaps = definition.gaps --- local check = definition.check --- local force = definition.force --- local rscale = definition.rscale or 1 --- local offset = definition.offset or start --- local id = definedfont { name = name, size = size * rscale } --- local index = #fonts + 1 --- fonts[index] = { id = id, size = size } --- local chars = fontchars[id] --- local function remap(unic,unicode,gap) --- -- local unic = unicode + offset - start --- if check and not chars[unicode] then --- -- not in font --- elseif force or (not done[unic] and not characters[unic]) then --- if trace_collecting then --- report_math("remapping math character, vector %a, font %a, character %C%s%s", --- fallbacks,name,unic,check and ", checked",gap and ", gap plugged") --- end --- characters[unic] = copiedglyph(target,characters,chars,unicode,index) --- done[unic] = true --- end --- end --- for unicode = start, stop do --- local unic = unicode + offset - start --- remap(unic,unicode,false) --- end --- if gaps then --- for unic, unicode in next, gaps do --- remap(unic,unicode,true) --- end --- end --- end --- end --- end --- end --- end --- end --- --- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.finishfallbacks") - local stack = { } function mathematics.registerfallbackid(n,id,name) |