summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkxl/math-act.lmt')
-rw-r--r--Master/texmf-dist/tex/context/base/mkxl/math-act.lmt3929
1 files changed, 3701 insertions, 228 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt b/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
index 9da79976484..370415c8185 100644
--- a/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
+++ b/Master/texmf-dist/tex/context/base/mkxl/math-act.lmt
@@ -9,24 +9,46 @@ if not modules then modules = { } end modules ['math-act'] = {
-- Here we tweak some font properties (if needed). The commented sections
-- have been removed (no longer viable) but can be found in the .lua variant.
-local type, next = type, next
-local fastcopy, insert, remove = table.fastcopy, table.insert, table.remove
+-- The tweaks here evolved from experiments with, discussions about and upgrades of
+-- the math subsystem, a project that Mikael Sundvist and I started end 2021 and
+-- that is still ongoing in 2023 (and probably beyond as we find new challenges as
+-- we go).
+
+local type, next, tonumber = type, next, tonumber
+local fastcopy, copytable, insert, remove, concat = table.fastcopy, table.copy, table.insert, table.remove, table.concat
local formatters = string.formatters
+local byte = string.byte
+local setmetatableindex, sortedkeys, sortedhash = table.setmetatableindex, table.sortedkeys, table.sortedhash
+local lpegmatch = lpeg.match
local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
local trace_collecting = false trackers.register("math.collecting", function(v) trace_collecting = v end)
+local trace_tweaking = false trackers.register("math.tweaks", function(v) trace_tweaking = v end)
local report_math = logs.reporter("mathematics","initializing")
+local report_mathtweak = logs.reporter("mathematics","tweak")
+
+local getfontoffamily = tex.getfontoffamily
+local texget = tex.get
+local fontcharacters = fonts.hashes.characters
+local chardata = characters.data
+local extensibles = mathematics.extensibles
local context = context
local commands = commands
local mathematics = mathematics
local texsetdimen = tex.setdimen
+local texisdimen = tex.isdimen
local abs = math.abs
+local blocks = characters.blocks
+local stepper = utilities.parsers.stepper
+
local helpers = fonts.helpers
local upcommand = helpers.commands.up
+local downcommand = helpers.commands.down
local rightcommand = helpers.commands.right
+local leftcommand = helpers.commands.left
local charcommand = helpers.commands.char
local prependcommands = helpers.prependcommands
@@ -37,6 +59,14 @@ local appendaction = sequencers.appendaction
local fontchars = fonts.hashes.characters
local fontproperties = fonts.hashes.properties
+local mathgaps = mathematics.gaps
+
+local d_scratchleftoffset = texisdimen("scratchleftoffset")
+local d_scratchrightoffset = texisdimen("scratchrightoffset")
+
+local use_math_goodies = true directives.register("math.nogoodies", function(v) use_math_goodies = not v end)
+local checkitalics = false trackers .register("math.checkitalics", function(v) checkitalics = v end)
+
local mathfontparameteractions = sequencers.new {
name = "mathparameters",
arguments = "target,original",
@@ -46,20 +76,56 @@ appendgroup("mathparameters","before") -- user
appendgroup("mathparameters","system") -- private
appendgroup("mathparameters","after" ) -- user
-function fonts.constructors.assignmathparameters(original,target)
+function fonts.constructors.assignmathparameters(original,target) -- wrong way around
local runner = mathfontparameteractions.runner
if runner then
runner(original,target)
end
end
-function mathematics.initializeparameters(target,original)
+-- we need a better reset because the following will scale
+
+local undefined <const> = 0x3FFFFFFF -- maxdimen or undefined_math_parameter
+
+function mathematics.initializeparameters(target,original,nodimensions)
local mathparameters = original.mathparameters
if mathparameters and next(mathparameters) then
- mathparameters = mathematics.dimensions(mathparameters)
- if not mathparameters.SpaceBeforeScript then
- mathparameters.SpaceBeforeScript = mathparameters.SpaceAfterScript
+ if nodimensions ~= "noscale" then
+ mathparameters = mathematics.dimensions(mathparameters)
end
+ --
+ -- if not mathparameters.MinConnectorOverlap then mathparameters.MinConnectorOverlap = undefined end
+ if not mathparameters.SubscriptShiftDownWithSuperscript then mathparameters.SubscriptShiftDownWithSuperscript = mathparameters.SubscriptShiftDown * 1.5 end
+ -- if not mathparameters.FractionDelimiterSize then mathparameters.FractionDelimiterSize = undefined end
+ -- if not mathparameters.FractionDelimiterDisplayStyleSize then mathparameters.FractionDelimiterDisplayStyleSize = undefined end
+ -- if not mathparameters.SkewedDelimiterTolerance then mathparameters.SkewedDelimiterTolerance = undefined end
+ -- some more can be undefined:
+ if not mathparameters.PrimeRaisePercent then mathparameters.PrimeRaisePercent = 0 end
+ if not mathparameters.PrimeRaiseComposedPercent then mathparameters.PrimeRaiseComposedPercent = 0 end
+ if not mathparameters.PrimeShiftUp then mathparameters.PrimeShiftUp = mathparameters.SuperscriptShiftUp end
+ if not mathparameters.PrimeBaselineDropMax then mathparameters.PrimeBaselineDropMax = mathparameters.SuperscriptBaselineDropMax end
+ if not mathparameters.PrimeShiftUpCramped then mathparameters.PrimeShiftUpCramped = mathparameters.SuperscriptShiftUpCramped end
+ if not mathparameters.PrimeSpaceAfter then mathparameters.PrimeSpaceAfter = 0 end
+ if not mathparameters.PrimeWidthPercent then mathparameters.PrimeWidthPercent = 50 end
+ if not mathparameters.SpaceBeforeScript then mathparameters.SpaceBeforeScript = mathparameters.SpaceAfterScript end
+ if not mathparameters.NoLimitSupFactor then mathparameters.NoLimitSupFactor = 0 end
+ if not mathparameters.NoLimitSubFactor then mathparameters.NoLimitSubFactor = 0 end
+ if not mathparameters.AccentTopShiftUp then mathparameters.AccentTopShiftUp = 0 end
+ if not mathparameters.AccentBottomShiftDown then mathparameters.AccentBottomShiftDown = 0 end
+ if not mathparameters.FlattenedAccentTopShiftUp then mathparameters.AccentTopShiftUp = 0 end
+ if not mathparameters.FlattenedAccentBottomShiftDown then mathparameters.AccentBottomShiftDown = 0 end
+ if not mathparameters.AccentBaseDepth then mathparameters.AccentBaseDepth = 0 end
+ if not mathparameters.AccentFlattenedBaseDepth then mathparameters.AccentFlattenedBaseDepth = 0 end
+ if not mathparameters.AccentTopOvershoot then mathparameters.AccentTopOvershoot = 0 end
+ if not mathparameters.AccentBottomOvershoot then mathparameters.AccentBottomOvershoot = 0 end
+ if not mathparameters.AccentSuperscriptDrop then mathparameters.AccentSuperscriptDrop = 0 end
+ if not mathparameters.AccentSuperscriptPercent then mathparameters.AccentSuperscriptPercent = 0 end
+ if not mathparameters.AccentExtendMargin then mathparameters.AccentExtendMargin = 50 end
+ if not mathparameters.DelimiterPercent then mathparameters.DelimiterPercent = 100 end
+ if not mathparameters.DelimiterShortfall then mathparameters.DelimiterShortfall = 0 end
+ if not mathparameters.RadicalKernAfterExtensible then mathparameters.RadicalKernAfterExtensible = 0 end
+ if not mathparameters.RadicalKernBeforeExtensible then mathparameters.RadicalKernBeforeExtensible = 0 end
+ --
target.mathparameters = mathparameters
end
end
@@ -74,182 +140,3651 @@ local how = {
RadicalDegreeBottomRaisePercent = "unscaled",
NoLimitSupFactor = "unscaled",
NoLimitSubFactor = "unscaled",
+ PrimeRaisePercent = "unscaled",
+ PrimeRaiseComposedPercent = "unscaled",
+ PrimeWidthPercent = "unscaled",
+ AccentTopOvershoot = "unscaled",
+ AccentBottomOvershoot = "unscaled",
+ AccentSuperscriptPercent = "unscaled",
+ DelimiterPercent = "unscaled",
+ --
+ RadicalRuleThickness = "vertical",
+ OverbarRuleThickness = "vertical",
+ FractionRuleThickness = "vertical",
+ UnderbarRuleThickness = "vertical",
}
+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
+ end
+end
+
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
+-- if trace_defining then
+-- report_math("zero AccentBaseHeight corrected %a @ %p",target.properties.fullname,target.parameters.size)
+-- end
+-- mathparameters.AccentBaseHeight = target.parameters.xheight -- needs checking
+-- end
+-- end
+
+function mathematics.overloadparameters(target,original)
+ if use_math_goodies 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
+ local goodies = target.goodies
+ if goodies then
+ for i=1,#goodies do
+ local goodie = goodies[i]
+ local mathematics = goodie.mathematics
+ if mathematics then
+ local parameters = mathematics.parameters
+ local bigslots = mathematics.bigslots or mathematics.bigs
+ if parameters then
+ if trace_defining then
+ report_math("overloading math parameters in %a @ %p",target.properties.fullname,target.parameters.size)
+ end
+ for name, value in next, parameters do
+ local tvalue = type(value)
+ local oldvalue = mathparameters[name]
+ local newvalue = oldvalue
+ if tvalue == "number" then
+ newvalue = value
+ elseif tvalue == "string" then
+ -- delay till all set
+ elseif tvalue == "function" then
+ newvalue = value(oldvalue,target,original)
+ elseif not tvalue then
+ newvalue = nil
+ end
+ if trace_defining and oldvalue ~= newvalue then
+ report_math("overloading math parameter %a: %S => %S",name,oldvalue or 0,newvalue)
+ end
+ mathparameters[name] = newvalue
+ end
+ for name, value in next, parameters do
+ local tvalue = type(value)
+ if tvalue == "string" then
+ local newvalue = mathparameters[value]
+ if not newvalue then
+ local code = loadstring("return " .. value,"","t",mathparameters)
+ if type(code) == "function" then
+ local okay, v = pcall(code)
+ if okay then
+ newvalue = v
+ end
+ end
+ end
+ if newvalue then
+ -- split in number and string
+ mathparameters[name] = newvalue
+ elseif trace_defining then
+ report_math("ignoring math parameter %a: %S",name,value)
+ end
+ end
+ end
+ end
+ if bigslots then
+ target.bigslots = bigslots
+ end
+ end
end
- mathparameters[name] = value
end
end
- target.properties.math_is_scaled = true
end
end
--- AccentBaseHeight vs FlattenedAccentBaseHeight
+-- a couple of predefined tweaks:
-function mathematics.checkaccentbaseheight(target,original)
- local mathparameters = target.mathparameters
- if mathparameters and mathparameters.AccentBaseHeight == 0 then
- mathparameters.AccentBaseHeight = target.parameters.x_height -- needs checking
+local datasets = { }
+local mathtweaks = { datasets = datasets }
+mathematics.tweaks = mathtweaks
+
+-- can be a common helper:
+
+local f_u = formatters["%U"]
+
+local function unicodecharlist(t)
+ local r = { }
+ local n = 0
+ for u in sortedhash(t) do
+ n = n + 1 ; r[n] = f_u(u)
end
+ return concat(r," ")
end
-function mathematics.checkprivateparameters(target,original)
- local mathparameters = target.mathparameters
- if mathparameters then
+local function report_tweak(fmt,target,original,...)
+ if fmt then
+ local metadata = (original and original.shared.rawdata.metadata) or
+ (target and target .shared.rawdata.metadata)
local parameters = target.parameters
- local properties = target.properties
if parameters then
- local size = parameters.size
- if size then
- if not mathparameters.FractionDelimiterSize then
- mathparameters.FractionDelimiterSize = 1.01 * size
+ report_mathtweak(
+ "%a, size %P, math size %i, %s",
+ metadata and metadata.fontname or "unknown",
+ parameters.size or 655360,
+ parameters.mathsize or 1,
+ string.formatters[fmt](...)
+ )
+ else
+ print("something is wrong")
+ end
+ else
+ report_mathtweak("")
+ end
+end
+
+local function feedback_tweak(tweak,target,original,done)
+ if not done or (type(done) == "table" and not next(done)) then
+if trace_tweaking then -- for now
+ report_tweak("no need for %a",target,original,tweak)
+end
+ elseif trace_tweaking then
+ report_tweak("tweak %a applied to: %s",target,original,tweak,unicodecharlist(done))
+ end
+end
+
+mathtweaks.subsets = {
+ acenorsuvxz = { 0x1D44E, 0x1D450, 0x1D452, 0x1D45B, 0x1D45C, 0x1D45F, 0x1D460, 0x1D462, 0x1D463, 0x1D465, 0x1D467 },
+ bhklt = { 0x1D44F, 0x1D455, 0x1D458, 0x1D459, 0x1D461 },
+ d = { 0x1D451 },
+ f = { 0x1D453 },
+ gjqy = { 0x1D454, 0x1D457, 0x1D45E, 0x1D466 },
+ i = { 0x1D456 },
+ mw = { 0x1D45A, 0x1D464 },
+ p = { 0x1D45D },
+ dotless = { 0x00049, 0x0004A, 0x00131, 0x00237, 0x1D6A4, 0x1D6A5 },
+ integrals = { 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 },
+ horizontalfences = { 0x0203E, 0x023B4, 0x023B5, 0x023DC, 0x023DD, 0x023DE, 0x023DF, 0x023E0, 0x023E1 }, -- not really used
+}
+
+local function getalso(target,original)
+ local also = target.tweakalso -- maybe
+ if not also then
+ also = { }
+ -- for k, v in sortedhash(target.characters) do
+ for k, v in next, target.characters do
+ local u = v.unicode
+ if u and k ~= u then
+ local a = also[u]
+ if a then
+ a[#a+1] = k
+ else
+ also[u] = { k }
+ end
+ end
+ end
+ target.tweakalso = also
+ end
+ return also
+end
+
+-- {
+-- tweak = "dimensions",
+-- list = {
+-- ["lowercasegreeksansserifbolditalic"] = {
+-- -- delta = 0x003B1 - 0x1D7AA,
+-- slant = -0.2,
+-- line = 0.1,
+-- mode = 1,
+-- width = 0.675,
+-- -- scale = 0.975,
+-- squeeze = 0.975,
+-- extend = .7,
+-- },
+-- },
+-- },
+
+ -- ["0x7C.variants.*"] = { squeeze = 0.10, height = 0.10, depth = 0.10 },
+
+local detail do
+
+ local splitter = lpeg.tsplitat(".")
+
+ detail = function(characters,k)
+ if type(k) == "string" then
+ local t = lpegmatch(splitter,k)
+ local n = #t
+ if n > 0 then
+ local base = tonumber(t[1]) or tonumber(t[1],16)
+ if base then
+ local c = characters[base]
+ if c and n > 1 then
+ local list = t[2]
+ if list == "parts" then
+ local nxt = c.next
+ while nxt do
+ c = characters[nxt]
+ nxt = c.next
+ end
+ c = c.parts
+ if c then
+ local index = t[3]
+ if index == "*" then
+ return t
+ else
+ if index == "top" then
+ index = #c
+ elseif index == "bottom" then
+ index = 1
+ else
+ index = tonumber(index)
+ end
+ if index then
+ c = c[index]
+ if c then
+ return c.glyph
+ end
+ end
+ end
+ end
+ elseif list == "variants" then
+ local index = t[3]
+ if index == "*" then
+ local t = { }
+ local nxt = c.next
+ while nxt do
+ t[#t+1] = nxt
+ c = characters[nxt]
+ nxt = c.next
+ end
+ return t
+ else
+ index = tonumber(index)
+ if index then
+ local nxt = c.next
+ while nxt and index > 1 do
+ c = characters[nxt]
+ nxt = c.next
+ index = index - 1
+ end
+ return nxt
+ end
+ end
+ end
+ end
+ end
+ end
+ else
+ return k
+ end
+ end
+
+end
+
+-- This temporary tweak was used when we (MS & HH) were fixing the Latin Modern
+-- parameters that relate to script placement. We started from the original cmr
+-- ratios combined with the formal specification and ended up with the following
+-- values. In the end we rejected this tweak and setteled for checking and fixing:
+--
+-- SubscriptShiftDown
+-- SubscriptShiftDownWithSuperscript
+-- SuperscriptShiftUp
+-- SuperscriptShiftUpCramped
+--
+-- because it looks a bit arbitrary what values are set. We keep the code below
+-- as documentation.
+
+-- do
+-- -- modern
+-- --
+-- -- local factors = {
+-- -- scripts = {
+-- -- SubscriptBaselineDropMin = 0.116,
+-- -- SubscriptShiftDown = 0.348,
+-- -- SubscriptShiftDownWithSuperscript = 0.573,
+-- -- SubscriptTopMax = 0.800,
+-- -- SuperscriptBaselineDropMax = 0.896,
+-- -- SuperscriptBottomMaxWithSubscript = 0.800,
+-- -- SuperscriptBottomMin = 0.250,
+-- -- SuperscriptShiftUp = 0.958,
+-- -- SuperscriptShiftUpCramped = 0.958,
+-- -- }
+-- -- }
+--
+-- -- -- cambria
+-- --
+-- -- local factors = {
+-- -- scripts = {
+-- -- SubscriptBaselineDropMin = 0.279,
+-- -- SubscriptShiftDown = 0.364,
+-- -- SubscriptShiftDownWithSuperscript = 0.547,
+-- -- SubscriptTopMax = 0.662,
+-- -- SuperscriptBaselineDropMax = 0.401,
+-- -- SuperscriptBottomMaxWithSubscript = 0.667,
+-- -- SuperscriptBottomMin = 0.208,
+-- -- SuperscriptShiftUp = 0.654,
+-- -- SuperscriptShiftUpCramped = 0.654,
+-- -- }
+-- -- }
+--
+-- -- after some tests and inspection
+-- --
+--
+-- local factors = {
+-- scripts = {
+-- SubscriptBaselineDropMin = 0.100, -- harmless but small (seldom triggered)
+-- SubscriptShiftDown = 0.400, -- by inspection in several files
+-- SubscriptShiftDownWithSuperscript = 0.400, -- as above
+-- SubscriptTopMax = 0.800, -- Microsoft recommendation
+-- SuperscriptBaselineDropMax = 0.100, -- see SubscriptBaselineDropMin
+-- SuperscriptBottomMaxWithSubscript = 0.800, -- Microsoft recommendation
+-- SuperscriptBottomMin = 0.250, -- Microsoft recommendation
+-- SuperscriptShiftUp = 0.650, -- by inspection, but also a bit gamble
+-- SuperscriptShiftUpCramped = 0.650, -- see above, non-TeX
+-- }
+-- }
+--
+-- datasets.fixparameters = factors
+--
+-- function mathtweaks.fixparameters(target,original,parameters)
+-- local mathparameters = target.mathparameters
+-- if mathparameters and next(mathparameters) then
+-- local xheight = target.parameters.xheight
+-- -- todo : options
+-- for k, v in next, factors.scripts do
+-- mathparameters[k] = v * xheight
+-- end
+-- end
+-- end
+--
+-- end
+
+do
+
+ local stepper = utilities.parsers.stepper
+ local count = 0
+ local toeffect = fonts.toeffect
+ local privateslot = fonts.helpers.privateslot
+
+ local function adapt(list,target,original,targetcharacters,originalcharacters,k,v,compact,n)
+ k = mathgaps[k] or k
+ local character = targetcharacters[k]
+ if character then
+-- if not character.tweaked then -- todo: add a force
+ local t = type(v)
+ if t == "number" then
+ v = list[v]
+ t = type(v)
+ end
+ if t == "table" and next(v) then
+
+ local axis = tonumber(v.axis)
+ if axis then
+ axis = target.mathparameters.AxisHeight * axis
+ end
+
+ local factor = v.factor
+ if factor then
+ local m = v
+ v = setmetatableindex({
+ width = factor,
+ height = factor,
+ depth = factor,
+ squeeze = factor,
+ extend = factor,
+ }, v)
+ end
+ local originalslot = v.original
+ if not originalslot then
+ local delta = v.delta
+ if delta then
+ originalslot = k + delta
+ end
+ end
+ if originalslot then
+ originalslot = mathgaps[originalslot] or originalslot
+ local data = targetcharacters[originalslot]
+ if data then
+ data = copytable(data)
+ data.unicode = originalslot
+ targetcharacters[k] = data
+ character = data
+ else
+ report_mathtweak("no slot %U",originalslot)
+ return
+ end
+ end
+ --
+ local width = character.width
+ local height = character.height
+ local depth = character.depth
+ local italic = character.italic
+ local topanchor = character.topanchor
+ local bottomanchor = character.bottomanchor
+ --
+ local widthfactor = v.width
+ local heightfactor = v.height
+ local depthfactor = v.depth
+ local italicfactor = v.italic
+ local anchorfactor = v.anchor
+ local advancefactor = v.advance
+ local xoffsetfactor = v.xoffset
+ local yoffsetfactor = v.yoffset
+ local scalefactor = v.scale
+ local total = (height or 0) + (depth or 0)
+ if scalefactor ~= 1 then
+ character.scale = scalefactor
+ end
+ if width and width ~= 0 then
+ if advancefactor then
+ character.advance = advancefactor * width
+ else
+ character.advance = character.advance or width -- so advance is oldwidth
+ end
+ if widthfactor then
+ character.width = widthfactor * width
+ end
+ if xoffsetfactor then
+ character.xoffset = xoffsetfactor * width
+ end
+ end
+ if height and height ~= 0 then
+ if heightfactor then
+ character.height = heightfactor * height
+ end
+ end
+ if depth and depthfactor then
+ character.depth = depthfactor * depth
+ end
+ if yoffsetfactor then
+ character.yoffset = yoffsetfactor * total
+ end
+
+ if axis then
+ character.height = (character.height or 0) - axis
+ character.depth = (character.depth or 0) + axis
+ character.yoffset = (character.yoffset or 0) + axis
+ end
+
+ if italicfactor then
+ if italic then
+ character.italic = italicfactor * italic
+ elseif width and italicfactor ~= 1 then
+ character.italic = italicfactor * width
+ end
+ end
+ if anchorfactor then
+ character.topanchor = anchorfactor * (topanchor or width)
+ end
+-- if anchorfactor then
+-- character.bottomaccent = anchorfactor * (bottomanchor or width)
+-- end
+ -- begin experiment
+ local line = v.wline
+ if line then
+ local parameters = target.parameters
+ v.line = parameters.hfactor * line / parameters.units
+ end
+ -- end experiment
+ character.effect = toeffect(v) -- todo: move wline test inside here
+ -- begin experiment
+ v.line = line
+ -- end experiment
+ if trace_tweaking then
+ report_tweak("adapting dimensions of %U ",target,original,k)
+ end
+ -- missing when private
+ local originaldata = originalcharacters[k] -- or targetcharacters[k]
+ local smaller = originaldata and originaldata.smaller
+ if compact and smaller and smaller ~= k then
+ adapt(list,target,original,targetcharacters,originalcharacters,smaller,v,compact,n+1)
+ end
+ count = count + 1
+ else
+ report_mathtweak("invalid dimension entry %U",k)
+ end
+-- character.tweaked = true
+ if v.all then
+ local nxt = character.next
+ if nxt then
+ adapt(list,target,original,targetcharacters,originalcharacters,nxt,v,compact,n)
+ else
+ local parts = character.parts
+ if parts then
+ for i=1,#parts do
+ adapt(list,target,original,targetcharacters,originalcharacters,parts[i],v,compact,n)
+ end
+ end
+ end
+ end
+-- end
+ else
+ report_tweak("no character %U",target,original,k)
+ end
+ end
+
+ function mathtweaks.dimensions(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local targetcharacters = target.characters
+ local originalcharacters = original.characters
+ local compact = target.parameters.textscale and true or false
+ count = 0
+ for k, v in sortedhash(list) do
+ local t = type(k)
+ if t == "number" then
+ adapt(list,target,original,targetcharacters,originalcharacters,k,v,compact,1)
+ elseif t == "string" then
+ local d = privateslot(k) or detail(targetcharacters,k) -- watch the private here
+ local t = type(d)
+ if t == "table" then
+ for i=1,#d do
+ adapt(list,target,original,targetcharacters,originalcharacters,d[i],v,compact,1)
+ end
+ elseif t == "number" then
+ adapt(list,target,original,targetcharacters,originalcharacters,d,v,compact,1)
+ elseif d then
+ -- some kind of error
+ else
+ local r = blocks[k]
+ if r then
+ local done = false
+ for i=r.first,r.last do
+ adapt(list,target,original,targetcharacters,originalcharacters,i,v,compact,1)
+ end
+ else
+ stepper(k,function(n)
+ adapt(list,target,original,targetcharacters,originalcharacters,n,v,compact,1)
+ end)
+ end
+ end
+ -- elseif t == "table" then
+ -- for i=1,#t do
+ -- adapt(list,target,original,targetcharacters,originalcharacters,t[i],v,compact,1)
+ -- end
+ end
+ end
+ if trace_tweaking and count > 0 then
+ report_mathtweak("%i dimensions adapted",count)
+ end
+ end
+ end
+
+end
+
+do
+
+ function mathtweaks.message(target,original,parameters)
+ report_mathtweak(parameters.text or "no message")
+ end
+
+ function mathtweaks.showinfo(target,original,parameters)
+ local mathparameters = target.mathparameters
+ for k, v in sortedhash(mathparameters) do
+ report_mathtweak("%s : %s",k,v)
+ end
+ end
+
+end
+
+
+do
+
+ function mathtweaks.wipevariants(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local targetcharacters = target.characters
+ -- local originalcharacters = original.characters
+ local count = 0
+ -- local also = getalso(target,original)
+ local done = false
+ for k, v in sortedhash(list) do
+ local ori = targetcharacters[k]
+ local nxt = ori.next
+ local cnt = v
+ if nxt then
+ local prt = nil
+ local lst = { }
+ while nxt do
+ local chr = targetcharacters[nxt]
+ lst[#lst+1] = chr
+ nxt = chr.next
+ if not nxt then
+ prt = chr.parts
+ break
+ end
+ end
+ if prt then
+ count = count + 1
+ if cnt ~= "*" then
+ if #lst < cnt then
+ cnt = #lst
+ end
+ ori = lst[cnt]
+ end
+ ori.parts = prt
+ end
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[k] = true
+ else
+ done = { [k] = true }
+ end
+ end
+ end
+ feedback_tweak("wipevariants",target,original,done)
+ end
+ end
+
+end
+
+do
+
+ function mathtweaks.replace(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local targetcharacters = target.characters
+ local originalcharacters = original.characters
+ local unicodes = original.resources.unicodes
+ if unicodes then
+ local count = 0
+ for k, v in sortedhash(list) do
+ if type(v) == "string" then
+ v = unicodes[v]
+ end
+ if type(v) == "number" then
+ targetcharacters[mathgaps[k] or k] = targetcharacters[mathgaps[v] or v]
+ count = count + 1
+ end
+ end
+ if trace_tweaking and count > 0 then
+ report_tweak("%i permanent replacements",target,original,count)
+ end
+ end
+ end
+ end
+
+ function mathtweaks.substitute(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local targetcharacters = target.characters
+ local originalcharacters = original.characters
+ local getsubstitution = fonts.handlers.otf.getsubstitution
+ local count = 0
+ for k, v in next, list do -- no need for sortedhash(list) unless we report
+ local sub = getsubstitution(original,k,v,true)
+ if sub then
+ targetcharacters[mathgaps[k] or k] = targetcharacters[mathgaps[sub] or sub]
+ count = count + 1
+ end
+ end
+ if trace_tweaking and count > 0 then
+ report_tweak("%i permanent substitutions",target,original,count)
+ end
+ end
+ end
+
+end
+
+do
+
+ -- maybe we'll have a different name
+
+ function mathtweaks.kernpairs(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local targetcharacters = target.characters
+ local originalcharacters = original.characters
+ local done = false
+
+ local function add(v,n)
+ local chardata = targetcharacters[mathgaps[n] or n]
+ if chardata then
+ local width = chardata.width
+ if width then
+ local kerns = chardata.kerns or { }
+ for kk, vv in next, v do
+-- for kk, vv in sortedhash(v) do
+ stepper(kk,function(nn) -- todo: also make stepper accept a table
+ local t = mathgaps[nn] or nn
+ if t then
+ kerns[t] = vv * width
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[t] = true
+ else
+ done = { [t] = true }
+ end
+ end
+ end)
+ end
+ chardata.kerns = kerns
+ end
+ end
+ end
+
+ for k, v in next, list do -- no need for sortedhash(list) unless we report
+ stepper(k,function(n) -- todo: also make stepper accept a table
+ add(v,n)
+ end)
+ end
+
+-- for k, v in next, list do -- no need for sortedhash(list) unless we report
+-- local chardata = targetcharacters[mathgaps[k] or k]
+-- if chardata then
+-- local width = chardata.width
+-- if width then
+-- local kerns = chardata.kerns or { }
+-- for kk, vv in next, v do
+-- local t = mathgaps[kk] or kk
+-- if t then
+-- kerns[t] = vv * width
+-- count = count + 1
+-- end
+-- end
+-- chardata.kerns = kerns
+-- end
+-- end
+-- end
+
+ feedback_tweak("kernpairs",target,original,done)
+ end
+ end
+
+end
+
+do
+
+ local nps = fonts.helpers.newprivateslot
+
+ local list = {
+ { 0x2032, nps("delimited ghost 0x2032"), 1 },
+ { 0x2033, nps("delimited ghost 0x2033"), 2, 0x2032 },
+ { 0x2034, nps("delimited ghost 0x2034"), 3, 0x2032 },
+ { 0x2057, nps("delimited ghost 0x2057"), 4, 0x2032 },
+ { 0x2035, nps("delimited ghost 0x2035"), 1 },
+ { 0x2036, nps("delimited ghost 0x2036"), 2, 0x2035 },
+ { 0x2037, nps("delimited ghost 0x2037"), 3, 0x2035 },
+ }
+
+ datasets.fixprimes = list
+
+ function mathtweaks.fixprimes(target,original,parameters)
+ local targetcharacters = target.characters
+ local factor = parameters.factor or 1
+ local fake = tonumber(parameters.fake)
+ for i=1,#list do
+ local entry = list[i]
+ local unicode = entry[1]
+ local count = entry[3]
+ local used = fonts.handlers.otf.getsubstitution(target,unicode,"ssty",true,"math","dflt") or unicode
+ local data = targetcharacters[used]
+ if data then
+ targetcharacters[unicode] = data
+ local oldheight = data.height or 0
+ local newheight = factor * oldheight
+ data.yoffset = newheight - (oldheight or 0)
+ data.height = newheight
+ data.smaller = nil
+ elseif not fake then
+ report_tweak("missing %i prime %U",target,original,count,unicode)
+ end
+ end
+ if fake then
+ for i=1,#list do
+ local entry = list[i]
+ local count = entry[3]
+ if count > 1 then
+ local unicode = entry[1]
+ local original = entry[4]
+ local data = targetcharacters[original]
+ if data then
+ local oldwidth = data.width
+ local xoffset = fake * oldwidth
+ local newwidth = oldwidth + (count - 1) * xoffset
+ targetcharacters[unicode] = {
+ width = newwidth,
+ height = data.height,
+ unicode = unicode,
+ commands = {
+ { "offset", 0, 0, original },
+ { "offset", xoffset, 0, original },
+ count > 2 and { "offset", 2 * xoffset, 0, original } or nil,
+ count > 3 and { "offset", 3 * xoffset, 0, original } or nil,
+ },
+ }
+ end
+ end
+ end
+ end
+ end
+
+ function mathtweaks.addprimed(target,original,parameters)
+ local characters = target.characters
+ for i=1,#list do
+ local entry = list[i]
+ local basecode = entry[1]
+ local movecode = entry[2]
+ local basedata = characters[basecode]
+ if basedata then
+ local baseheight = basedata.height or 0
+ local basewidth = basedata.width or 0
+ local used = baseheight
+ local total = baseheight
+ characters[movecode] = { -- todo:share
+ width = basewidth,
+ height = used,
+ unicode = basecode, -- 0xFFFD or space or so
+ -- callback = "devirtualize",
+ commands = {
+ downcommand[used],
+ { "rule", used, 0 },
+ },
+ }
+ basedata.partsorientation = "vertical"
+ basedata.parts = {
+ {
+ advance = used,
+ ["end"] = used,
+ extender = 1,
+ glyph = movecode,
+ start = used,
+ },
+ {
+ advance = total,
+ ["end"] = 0,
+ glyph = basecode,
+ start = total,
+ },
+ }
+ if trace_tweaking then
+ report_tweak("primed %U added",target,original,basecode)
+ end
+ end
+ end
+ end
+
+end
+
+do
+
+ local nps = fonts.helpers.newprivateslot
+
+ local privates = {
+ [0x2212] = nps("unary minus"),
+ [0x002B] = nps("unary plus"),
+ [0x00B1] = nps("unary plus minus"),
+ [0x2213] = nps("unary minus plus"),
+ }
+
+ -- these are the values tested with texgyre-bonum
+
+ local predefined = {
+ ["unary minus"] = {
+ original = 0x2212,
+ extend = .5,
+ width = .5,
+ unicode = 0x002D, -- hyphen minus
+ },
+ ["unary plus"] = {
+ original = 0x002B,
+ extend = .5,
+ squeeze = .5,
+ width = .5,
+ height = .5,
+ yoffset = .2,
+ mode = 2,
+ wline = .5,
+ unicode = 0x002B,
+ },
+ ["unary plus minus"] = {
+ original = 0x00B1,
+ extend = .5,
+ squeeze = .5,
+ width = .5,
+ height = .5,
+ yoffset = .2,
+ mode = 2,
+ wline = .5,
+ },
+ ["unary minus plus"] = {
+ original = 0x2213,
+ extend = .5,
+ squeeze = .5,
+ width = .5,
+ height = .5,
+ yoffset = .2,
+ mode = 2,
+ wline = .5,
+ },
+ }
+
+ -- {
+ -- tweak = "addprivates",
+ -- list = {
+ -- -- for specific parameters see act file
+ -- ["unary minus"] = { preset = "unary minus" },
+ -- ["unary plus"] = { preset = "unary plus" },
+ -- ["unary plus minus"] = { preset = "unary plus minus" },
+ -- ["unary minus plus"] = { preset = "unary minus plus" },
+ -- },
+ -- },
+
+ function mathtweaks.addprivates(target,original,parameters)
+ local list = parameters.list or predefined
+ if list then
+ local targetcharacters = target.characters
+ local targetparameters = target.parameters
+ local originalcharacters = original.characters
+ local processedprivates = { }
+ for name, v in sortedhash(list) do
+ if type(v) == "table" then
+ local preset = v.preset
+ if preset then
+ local p = predefined[preset]
+ if p then
+ v = table.combine(p,v)
+ p.preset = nil
+ else
+ goto next
+ end
+ end
+ local charslot = v.original
+ if charslot then
+ local chardata = targetcharacters[charslot]
+ if chardata then
+ local clonedata = copytable(chardata)
+ local cloneslot = nps(name)
+ local unicode = v.unicode or clonedata.unicode
+ clonedata.uncode = unicode
+ targetcharacters[cloneslot] = clonedata
+ if trace_tweaking then
+ report_tweak("cloning %a from %C into %U with tounicode %U",target,original,name,charslot,cloneslot,unicode)
+ end
+ end
+ processedprivates[name] = v
+ end
+ ::next::
+ end
+ end
+ mathtweaks.dimensions(target,original,{
+ tweak = parameters.tweak,
+ list = processedprivates,
+ })
+ end
+ end
+
+end
+
+-- do
+--
+-- function mathtweaks.fixanchors(target,original,parameters)
+-- local targetcharacters= target.characters
+-- local factor = tonumber(parameters.factor) or 0
+-- if factor ~= 0 then
+-- local done = false
+-- for k, v in next, targetcharacters do
+-- local a = v.topanchor
+-- if a and a > 0 then
+-- v.topanchor = a * factor
+-- count = count + 1
+-- if not trace_tweaking then
+-- done = true
+-- elseif done then
+-- done[u] = true
+-- else
+-- done = { [u] = true }
+-- end
+-- end
+-- end
+-- end
+-- feedback_tweak("fixanchors",target,original,done)
+-- end
+--
+-- end
+
+-- do
+--
+-- -- actually this should be a an engine feature driven by category because we don't
+-- -- want this in display mode .. only a test for MS and HH
+--
+-- local issymbol = characters.is_symbol
+--
+-- function mathtweaks.oldstylemath(target,original,parameters)
+-- local chardata = characters.data
+-- local characters = target.characters
+-- local axis = target.mathparameters.AxisHeight
+-- local delta = (parameters.factor or .1) * axis
+-- target.mathparameters.AxisHeight = (axis - delta)
+-- for k, v in sortedhash(characters) do
+-- if issymbol[k] then -- quick hack, engine knows
+-- v.yoffset = -delta
+-- v.height = (v.height or 0) - delta
+-- v.depth = (v.depth or 0) - delta
+-- end
+-- end
+-- end
+--
+-- function mathtweaks.oldstylemath(target,original,parameters)
+-- -- not relevant
+-- end
+--
+-- end
+
+do
+
+ function mathtweaks.simplifykerns(target,original,parameters)
+ local characters = target.characters
+ local done = false
+ -- for u, v in sortedhash(characters) do
+ for u, v in next, characters do
+ local mathkerns = v.mathkerns
+ if mathkerns then
+ local k = mathkerns.topleft
+ if k then
+ k = k[#k].kern
+ if k then
+ v.topleft = k
+ end
+ end
+ local k = mathkerns.topright
+ if k then
+ k = k[#k].kern
+ if k then
+ v.topright = k
+ end
+ end
+ local k = mathkerns.bottomleft
+ if k then
+ k = k[1].kern -- todo get value at baseline
+ if k then
+ v.bottomleft = k
+ end
+ end
+ local k = mathkerns.bottomright
+ if k then
+ k = k[1].kern -- todo get value at baseline
+ if k then
+ v.bottomright = k
+ end
+ end
+ v.mathkerns = nil
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[u] = true
+ else
+ done = { [u] = true }
+ end
+ end
+ end
+ feedback_tweak("simplifykerns",target,original,done)
+ end
+
+end
+
+do
+
+ local function wipe(whatever,target,original,parameters,field,move,integrals)
+ local targetcharacters = target.characters
+ local targetdescriptions = target.descriptions
+ local factor = target.parameters.factor
+ local correct = parameters.correct
+ local done = false
+ 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
+ local function step(s)
+ while s do
+ local u = mathgaps[s] or s
+ local c = targetcharacters[u]
+ if c then
+ if field == "topanchor" then
+ if c.topanchor then
+ c.topanchor = nil
+ else
+ goto smaller
+ end
+ else
+ local okay = false
+ local italic = c.italic
+ if move and not c.advance then -- advance check prevents double move
+ local width = c.width or 0
+ c.advance = width
+ if correct then
+ local llx = getllx(u)
+ if llx then
+ local topanchor = c.topanchor
+ llx = llx * factor
+ width = width + llx
+ c.xoffset = llx
+ if topanchor then
+ c.topanchor = topanchor + llx
+ end
+ -- too bad (schola e^x):
+ -- c.bottomleft = (c.bottomleft or 0) - llx
+ -- c.topleft = (c.topleft or 0) - llx
+ okay = true
+ end
+ end
+ if italic and italic ~= 0 then
+ c.width = width + italic
+ c.bottomright = - italic
+ okay = true
+ else
+ c.width = width
+ end
+ end
+ if italic then
+ c.italic = nil
+ okay = true
+ end
+ if okay then
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[u] = true
+ else
+ done = { [u] = true }
+ end
+ else
+ goto smaller
+ end
+ end
+ goto smaller
+ ::smaller::
+ s = c.smaller
+ ::variants::
+ -- no italics here anyway but we could check them some day
+ else
+ break
+ end
+ end
+ end
+ local list = parameters.list -- todo: ranges
+ if list == "letters" or parameters.letters then
+ local chardata = characters.data
+ -- for k, v in sortedhash(targetcharacters) do
+ for k, v in next, targetcharacters do
+ if v.italic then
+ local d = chardata[v.unicode]
+ local c = d and d.category
+ if c == "ll" or c == "lu" then
+ step(k)
+ end
+ end
+ end
+ return
+ elseif not list or list == "all" or list == true or parameters.all then
+ list = sortedkeys(targetcharacters)
+ elseif type(list) == "string" then
+ list = { list }
+ end
+ for i=1,#list do
+ local l = list[i]
+ local t = type(l)
+ if not l then
+ -- can be false
+ elseif t == "table" then
+ for i=1,#l do
+ step(l[i])
+ end
+ elseif t == "number" then
+ step(l)
+ else
+ local r = blocks[l]
+ if r then
+ for i=r.first,r.last do
+ step(i)
+ end
+ else
+ stepper(l,step)
+ end
+ end
+ end
+ feedback_tweak(whatever,target,original,done)
+ end
+
+ function mathtweaks.wipeanchors(target,original,parameters)
+ wipe("wipeanchors",target,original,parameters,"topanchor")
+ end
+
+ function mathtweaks.wipeitalics(target,original,parameters)
+ if not checkitalics then
+ wipe("wipeitalics",target,original,parameters,"italic")
+ end
+ end
+
+ function mathtweaks.moveitalics(target,original,parameters)
+ wipe("moveitalics",target,original,parameters,"italic",true)
+ end
+
+ -- function mathtweaks.fixdigits(target,original,parameters)
+ -- mathtweaks.fixanchors(target,original,{ list = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } })
+ -- end
+
+end
+
+do
+
+ function mathtweaks.topanchors(target,original,parameters)
+ local characters = target.characters
+ local list = parameters.list
+ if list then
+ local done = false
+ for u, v in sortedhash(list) do
+ local c = characters[k]
+ if c then
+ local w = c.width
+ if w and w ~= 0 then
+ c.topanchor = v * w
+ if trace_tweaking then
+ -- todo
+ end
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[u] = true
+ else
+ done = { [u] = true }
+ end
+ end
+ end
+ end
+ feedback_tweak("topanchors",target,original,done)
+ end
+ end
+
+ function mathtweaks.movelimits(target,original,parameters)
+ local characters = target.characters
+ local list = parameters.list
+ if list then
+ local factor = parameters.factor or 1
+ local also = getalso(target,original)
+ local done = { }
+ local function relocate(u,factor)
+ if done[u] then
+ return
+ end
+ done[u] = true
+ local c = characters[u]
+ if c then
+ local italic = c.italic
+ if italic then
+ if italic ~= 0 then
+ local width = c.width or 0
+ local half = (italic/2) * factor
+ c.topanchor = width + half
+ c.bottomanchor = width - half
+ c.bottomright = - italic * (parameters.icfactor or 1)
+ if trace_tweaking then
+ -- todo
+ end
+ end
+ c.italic = nil
+ end
+ local s = c.smaller
+ if s then
+ relocate(s,factor)
+ end
+ local n = c.next
+ if n then
+ relocate(n,factor)
+ end
+ -- Kind of tricky: we configure the engine to use the vitalic
+ -- so when we tweak we need to set that to zero.
+ local parts = c.parts
+ local italic = c.partsitalic
+ if parts and italic then
+ if italic ~= 0 then
+ local tchar = characters[parts[#parts].glyph]
+ local bchar = characters[parts[1].glyph]
+ local width = tchar.width or 0
+ local half = (italic/2) * factor
+ tchar.topanchor = width + half
+ bchar.bottomanchor = width - half
+ bchar.bottomright = - italic
+ if trace_tweaking then
+ -- todo
+ end
+ tchar.italic = nil
+ bchar.italic = nil
+ end
+ c.vitalic = nil
+ end
+ if also then
+ local a = also[u]
+ if a then
+ for i=1,#a do
+ relocate(a[i],factor)
+ end
+ end
+ end
+ end
+ end
+ if #list > 0 then
+ for i=1,#list do
+ relocate(list[i],factor)
+ end
+ else
+ for k, v in sortedhash(list) do
+ relocate(k,tonumber(v) or factor)
+ end
+ end
+ feedback_tweak("movelimits",target,original,done)
+ end
+ end
+
+end
+
+do
+
+ -- musical timestamp: March 2022, Antonio Sanches (Bad Hombre), live performance in NL
+
+ function mathtweaks.kerns(target,original,parameters)
+ local kerns = parameters.list
+ if kerns then
+ local characters = target.characters
+ local done = false
+ local function setone(uc,data)
+ local function set(unicode)
+ unicode = mathgaps[unicode] or unicode
+ local chardata = characters[unicode]
+ if chardata then
+ local width = chardata.width or 0
+ local k = data.topleft ; if k and k ~= 0 then chardata.topleft = k * width end
+ local k = data.topright ; if k and k ~= 0 then chardata.topright = k * width end
+ local k = data.bottomleft ; if k and k ~= 0 then chardata.bottomleft = k * width end
+ local k = data.bottomright ; if k and k ~= 0 then chardata.bottomright = k * width end
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[unicode] = true
+ else
+ done = { [unicode] = true }
+ end
+ end
+ end
+ local unicode = detail(characters,uc)
+ if type(unicode) == "table" then
+ for i=1,#unicode do
+ set(unicode[i])
+ end
+ elseif unicode then
+ set(unicode)
+ end
+ end
+ for unicode, data in next, kerns do
+ setone(unicode,data) -- withscriptcode(tfmdata,unicode,data,kernone)
+ -- also smaller
+ end
+ feedback_tweak("kerns",target,original,done)
+ end
+ end
+
+end
+
+do
+
+ function mathtweaks.margins(target,original,parameters)
+ local margins = parameters.list
+ if margins then
+ local characters = target.characters
+ local done = false
+ local function setone(unicode,data)
+ unicode = mathgaps[unicode] or unicode
+ local chardata = characters[unicode]
+ if chardata then
+ local width = chardata.width or 0
+ local total = (chardata.height or 0) + (chardata.depth or 0)
+ local k = data.left ; if k and k ~= 0 then chardata.leftmargin = k * width end
+ local k = data.right ; if k and k ~= 0 then chardata.rightmargin = k * width end
+ local k = data.top ; if k and k ~= 0 then chardata.topmargin = k * total end
+ local k = data.bottom ; if k and k ~= 0 then chardata.bottommargin = k * total end
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[unicode] = true
+ else
+ done = { [unicode] = true }
+ end
+ end
+ end
+ for unicode, data in next, margins do
+ setone(unicode,data) -- withscriptcode(tfmdata,unicode,data,kernone)
+ -- also smaller
+ end
+ feedback_tweak("margins",target,original,done)
+ end
+ end
+
+end
+
+do
+
+ -- musical timestamp: June 2022, Porcupine Tree - Rats Return
+
+ -- we can actually share these and flag them as being tweaked
+
+ local function scale(t,width,total)
+ local r = { }
+ for i=1,#t do
+ local ti = t[i]
+ local kern = ti.kern
+ local height = ti.height
+ if kern then
+ kern = width * kern
+ end
+ if height then
+ height = total * height
+ end
+ r[i] = {
+ kern = kern or 0,
+ height = height or 0,
+ }
+ end
+ return r
+ end
+
+ function mathtweaks.staircase(target,original,parameters)
+ local kerns = parameters.list
+ if kerns then
+ local characters = target.characters
+ local function kernone(unicode,data)
+ local chardata = characters[mathgaps[unicode] or unicode]
+ local total = (chardata.height or 0) + (chardata.depth or 0)
+ local width = chardata.width or 0
+ if data then
+ local tl = data.topleft ; if tl then tl = scale(tl,width,total) end
+ local tr = data.topright ; if tr then tr = scale(tr,width,total) end
+ local bl = data.bottomleft ; if bl then bl = scale(bl,width,total) end
+ local br = data.bottomright ; if br then br = scale(br,width,total) end
+ chardata.mathkerns = {
+ topleft = tl,
+ ropright = tr,
+ bottomleft = bl,
+ bottomright = br,
+ }
+ else
+ chardata.mathkerns = nil
+ end
+ end
+ for unicode, data in next, kerns do
+ kernone(unicode,data) -- withscriptcode(tfmdata,unicode,data,kernone)
+ -- also smaller
+ end
+ end
+ end
+
+end
+
+do
+
+ -- local list = {
+ -- [0x203E] = { factor = .4 }, -- overbar
+ -- [0x203E] = { factor = .7 }, -- underbar
+ -- [0x23DE] = { factor = .4 }, -- overbrace
+ -- [0x23DF] = { factor = .7 }, -- underbrace
+ -- [0x23DC] = { factor = .4 }, -- overparent
+ -- [0x23DD] = { factor = .7 }, -- underparent
+ -- [0x23B4] = { factor = .4 }, -- overbracket
+ -- [0x23B5] = { factor = .7 }, -- underbracket
+ -- }
+
+ -- We can patch the dimensions in-place or we can use additional characters in
+ -- the private namespace.
+
+ -- local addprivate = fonts.helpers.addprivate
+ -- local newnextglyph = addprivate(target,formatters["M-N-%H"](nextglyph),newnextdata)
+
+ local nps = fonts.helpers.newprivateslot
+
+ local umbracepiece = nps("um brace piece") -- will be created
+ local lmbracepiece = nps("lm brace piece") -- will be created
+ local cmbracepiece = nps("cm brace piece") -- will be created : center piece for brace builder hack
+
+ local ulbracepiece = nps("ul brace piece")
+ local urbracepiece = nps("ur brace piece")
+ local llbracepiece = nps("ll brace piece")
+ local lrbracepiece = nps("lr brace piece")
+
+ local over = { factor = "over" }
+ local under = { factor = "under" }
+
+ local candidates = {
+ over = {
+ [0x203E] = over, -- overbar
+ [0x23DE] = over, -- overbrace
+ [0x23DC] = over, -- overparent
+ [0x23B4] = over, -- overbracket
+ },
+ under = {
+ [0x23DF] = under, -- underbrace
+ [0x23DD] = under, -- underparent
+ [0x23B5] = under, -- underbracket
+ },
+ accent = {
+ [0x0300] = over, -- widegrave
+ [0x0308] = over, -- wideddot
+ [0x0304] = over, -- widebar
+ [0x0301] = over, -- wideacute
+ [0x0302] = over, -- widehat
+ [0x030C] = over, -- widecheck
+ [0x0306] = over, -- widebreve
+ [0x0307] = over, -- widedot
+ [0x030A] = over, -- widering
+ [0x0303] = over, -- widetilde
+ [0x20DB] = over, -- widedddot
+ },
+ }
+
+ datasets.accentdimensions = candidates
+
+ local function adapt(c,factor,baseheight,basedepth)
+-- if not c.tweaked then
+ local height = c.height or 0
+ local depth = c.depth or 0
+ local yoffset = 0
+ if factor == "over" then
+ local h = height - baseheight
+ yoffset = h - height
+ height = h
+ depth = depth - baseheight
+ elseif factor == "under" then
+ local d = depth - basedepth
+ yoffset = depth - d
+ depth = d
+ height = height - baseheight
+ elseif height > 0 then
+ local h = tonumber(factor) * height
+ yoffset = h - height
+ height = h
+ elseif depth > 0 then
+ local d = tonumber(factor) * depth
+ yoffset = depth - d
+ depth = d
+ end
+ c.yoffset = yoffset ~= 0 and yoffset or nil
+ c.height = height > 0 and height or nil
+ c.depth = depth > 0 and depth or nil
+-- c.tweaked = true
+-- end
+ end
+
+ local function process(target,original,characters,list,baseheight,basedepth)
+ if list then
+ for k, v in sortedhash(list) do -- sort for tracing
+ local c = characters[k]
+ if c and not c.yoffset then
+ local factor = v.factor
+ if factor then
+ adapt(c,factor,baseheight,basedepth)
+ local nc = c.next
+ local nv = 0
+ local ns = 0
+ while nc do
+ local c = characters[nc]
+ if c then
+ adapt(c,factor,baseheight,basedepth)
+ nv = nv + 1
+ nc = c.next
+ if not nc then
+ local hv = c.parts
+ if hv then
+ for i=1,#hv do
+ local c = characters[hv[i].glyph]
+ if c then
+ adapt(c,factor,baseheight,basedepth)
+ ns = ns + 1
+ end
+ end
+ end
+ break
+ end
+ else
+ break
+ end
+ end
+ if trace_tweaking then
+ report_tweak("adapting extensible (%i sizes, %i parts) %U",target,original,k,nv,ns)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ function mathtweaks.accentdimensions(target,original,parameters)
+ local list = parameters.list or { "over", "under" }
+ if list then
+ local characters = target.characters
+ local baseheight = target.mathparameters.AccentBaseHeight or 0
+ local basedepth = target.mathparameters.AccentBaseDepth or 0
+ for k, v in sortedhash(list) do -- sort for tracing
+ local t = type(v)
+ if t == "string" then
+ v = candidates[v]
+ t = type(v)
+ end
+ if t == "table" then
+ process(target,original,characters,v,baseheight,basedepth)
+ end
+ end
+ end
+ end
+
+end
+
+do
+
+ local addprivate = fonts.helpers.addprivate
+ local privateslot = fonts.helpers.privateslot
+
+ -- function mathtweaks.addrules(target,original,parameters)
+ -- local characters = target.characters
+ -- local height = target.mathparameters.OverbarRuleThickness
+ -- local depth = target.mathparameters.UnderbarRuleThickness
+ -- local width = target.parameters.emwidth/2
+ -- local step = 0.8 * width
+ -- characters[0x203E] = { -- over
+ -- width = width,
+ -- height = height,
+ -- depth = 0,
+ -- unicode = 0x203E,
+ -- commands = { { "rule", height, width } },
+ -- parts = {
+ -- { advance = width, ["end"] = step, glyph = 0x203E, start = 0 },
+ -- { advance = width, ["end"] = 0, glyph = 0x203E, start = step, extender = 1 },
+ -- }
+ -- }
+ -- characters[0x0332] = { -- under
+ -- width = width,
+ -- height = 0,
+ -- depth = depth,
+ -- yoffset = -depth,
+ -- unicode = 0x0332,
+ -- commands = { { "rule", height, width } },
+ -- parts = {
+ -- { advance = width, ["end"] = step, glyph = 0x0332, start = 0 },
+ -- { advance = width, ["end"] = 0, glyph = 0x0332, start = step, extender = 1 },
+ -- }
+ -- }
+ -- end
+
+ function mathtweaks.addrules(target,original,parameters)
+ local characters = target.characters
+ local thickness = target.mathparameters.OverbarRuleThickness
+ local width = target.parameters.emwidth / 2
+ local step = 0.8 * width
+ characters[0x203E] = { -- over
+ width = width,
+ height = thickness / 2,
+ depth = thickness / 2,
+ yoffset = - thickness / 2,
+ unicode = 0x203E,
+ commands = { { "rule", thickness, width } },
+ parts = {
+ { advance = width, ["end"] = step, glyph = 0x203E, start = 0 },
+ { advance = width, ["end"] = 0, glyph = 0x203E, start = step, extender = 1 },
+ },
+ partsorientation = "horizontal",
+ }
+ --
+ characters[0x0332] = characters[0x203E]
+ --
+ -- lucida lacks them ...
+ --
+ local half = thickness / 2
+ local double = thickness * 2
+ --
+ if not characters[0x23B4] then
+ local tpiece = addprivate(target,"bracket-piece-top",{
+ width = thickness,
+ height = half,
+ depth = double,
+ yoffset = - double,
+ commands = { { "rule", thickness * 2.5, thickness } },
+ })
+ characters[0x23B4] = { -- over
+ width = width,
+ height = half,
+ depth = double,
+ unicode = 0x23B4,
+ commands = { { "rule", thickness, width } },
+ parts = {
+ { advance = thickness, glyph = tpiece, ["end"] = 0, start = half },
+ { advance = width, glyph = 0x203E, ["end"] = step, start = step, extender = 1 },
+ { advance = thickness, glyph = tpiece, ["end"] = half, start = 0 },
+ },
+ partsorientation = "horizontal",
+ }
+ end
+ if not characters[0x23B5] then
+ local bpiece = addprivate(target,"bracket-piece-bottom",{
+ width = thickness,
+ height = double,
+ depth = half,
+ yoffset = - half,
+ commands = { { "rule", thickness * 2.5, thickness } },
+ })
+ characters[0x23B5] = { -- over
+ width = width,
+ height = double,
+ depth = half,
+ unicode = 0x23B5,
+ commands = { { "rule", thickness, width } },
+ parts = {
+ { advance = thickness, glyph = bpiece, ["end"] = 0, start = half },
+ { advance = width, glyph = 0x203E, ["end"] = step, start = step, extender = 1 },
+ { advance = thickness, glyph = bpiece, ["end"] = half, start = 0 },
+ },
+ partsorientation = "horizontal",
+ }
+ end
+ --
+ end
+
+ local force = false experiments.register("math.arrows", function(v) force = v end)
+
+ local function tighten(target,unicode,left,right,squeeze,yoffset)
+ local name = string.formatters["math tightened %U %.3N %.3N %.3N %.3N"](unicode,left,right,squeeze,yoffset)
+ local slot = privateslot(target,name)
+ if not slot then
+ local characters = target.characters
+ local data = copytable(characters[unicode])
+ local width = data.width
+ data.advance = width
+ data.width = width * (1-left-right)
+ data.xoffset = width * -left
+ if squeeze ~= 1 then
+ data.effect = { squeeze = squeeze }
+ end
+ if yoffset ~= 0 then
+ data.yoffset = (data.height or 0) * yoffset
+ end
+ slot = addprivate(target,name,data)
+ end
+ return slot
+ end
+
+ local function create(target,unicode,list,overloads)
+ local characters = target.characters
+ local chardata = characters[unicode]
+ if chardata then
+ local endpoint = unicode
+ while chardata.next do
+ chardata = characters[chardata.next]
+ end
+ if chardata and (force or overloads[unicode] == false or not chardata.parts) then
+ if not list then
+ -- chardata.parts = nil -- when we test
+ chardata.parts = { { glyph = unicode } }
+ else
+ local overload = overloads[unicode]
+ local parts = { }
+ for i=1,#list do
+ local part = list[i]
+ local glyph = part.glyph or unicode
+ local check = overloads[glyph]
+ local left = (check and check.left ) or part.left or 0
+ local right = (check and check.right ) or part.right or 0
+ local squeeze = check and check.squeeze or 1
+ local yoffset = check and check.yoffset or 0
+ if left~= 0 or right ~= 0 or squeeze ~= 1 or yoffset ~= 0 then
+ glyph = tighten(target,glyph,left,right,squeeze,yoffset)
+ end
+ local width = characters[glyph].width
+ local step = width/2
+ if part.extensible then
+ parts[#parts+1] = {
+ advance = width,
+ glyph = glyph,
+ ["end"] = step,
+ start = step,
+ extender = 1,
+ }
+ else
+ parts[#parts+1] = {
+ advance = width,
+ glyph = glyph,
+ ["end"] = 0,
+ start = step,
+ }
+ end
+ end
+ if #parts == #list then
+ chardata.parts = parts
+ end
end
- if not mathparameters.FractionDelimiterDisplayStyleSize then
- mathparameters.FractionDelimiterDisplayStyleSize = 2.40 * size
+ end
+ end
+ end
+
+ -- Unicode math lacks the arrow snippet while it does have fence snippets. Also, some
+ -- fonts have a relbar that doesn't match the double arrow.
+ --
+ -- {
+ -- tweak = "addarrows",
+ -- list = { [0x3D] = { squeeze = .85, yoffset = .0975 } }
+ -- },
+ --
+ -- We have no begin and end snippet, so I played with centering and rules at the edges
+ --
+ -- [0x21A9] = { -- hookleftarrow
+ -- { glyph = 0x2212, left = slack, extensible = true },
+ -- { glyph = 0x21A9, right = slack },
+ -- { glyph = 0x2212, right = slack, extensible = true },
+ -- }
+ --
+ -- but in the end rejected it.
+
+ local function initialize(left, right, slack)
+ -- We save some space with locals. When no glyph is given the unicode itself is
+ -- used which also saves some.
+ local single = { glyph = 0x2212, left = slack, right = slack, extensible = true }
+ local double = { glyph = 0x003D, left = slack, right = slack, extensible = true }
+ local triple = { glyph = 0x2261, left = slack, right = slack, extensible = true }
+ ----- spacer = { glyph = 0x0020, left = slack, right = slack, extensible = true }
+ local slackslack = { left = slack, right = slack }
+ local leftslack = { left = left, right = slack }
+ local slackright = { left = slack, right = right }
+ ----- centered = { spacer, { }, spacer }
+ local centered = false -- the luametatex engine does this
+ local singleright = { single, slackright }
+ local leftsingle = { leftslack, single }
+ return {
+ --
+ [0x002D] = { { left = slack, right = slack, glyph = 0x2212 }, single }, -- rel
+ --
+ [0x2190] = leftsingle, -- leftarrow
+ [0x219E] = leftsingle, -- twoheadleftarrow
+ [0x21BC] = leftsingle, -- leftharpoonup
+ [0x21BD] = leftsingle, -- leftharpoondown
+ --
+ [0x2192] = singleright, -- rightarrow
+ [0x21A0] = singleright, -- twoheadrightarrow
+ [0x21C0] = singleright, -- rightharpoonup
+ [0x21C1] = singleright, -- rightharpoondown
+ --
+ [0x003D] = { slackslack, double }, -- equaltext
+ [0x2261] = { slackslack, triple }, -- triplerel
+ [0x27F8] = { leftslack, double }, -- Leftarrow
+ [0x27F9] = { double, slackright }, -- Rightarrow
+ --
+ [0x21A9] = centered, -- hookleftarrow
+ [0x21AA] = centered, -- hookrightarrow
+ [0x21CB] = centered, -- leftrightharpoons
+ [0x21CC] = centered, -- rightleftharpoons
+ [0x21C4] = centered, -- rightoverleftarrow
+ [0x21C6] = centered, -- leftoverrightarrow
+ [0x21A6] = centered, -- mapsto
+ --
+ [0x203E] = { slackslack, { left = slack, right = slack, extensible = true } }, -- bar
+ --
+ [0x27F7] = { { glyph = 0x2190, left = left, right = slack }, single, { glyph = 0x2192, left = slack, right = right } }, -- leftrightarrow rightleftarrow
+ [0x27FA] = { { glyph = 0x27F8, left = left, right = slack }, double, { glyph = 0x27F9, left = slack, right = right } }, -- Leftrightarrow Rightleftarrow
+ }
+ end
+
+ datasets.addarrows = { }
+
+ function mathtweaks.addarrows(target,original,parameters)
+ local overloads = parameters.list or { } -- { [unicode] = { left = .1, right = .1 } }
+ local left = parameters.left or 0.05
+ local right = parameters.right or 0.05
+ local slack = parameters.slack or 0.1
+ local arrows = initialize(left,right,slack)
+ -- inspect(arrows)
+ for unicode, list in sortedhash(arrows) do
+ create(target,unicode,list,overloads)
+ end
+ datasets.addarrows = sortedkeys(arrows)
+ if trace_tweaking then
+ report_tweak("arrows added",target,original)
+ end
+ end
+
+end
+
+do -- this could be combined with the previous
+
+ function mathtweaks.addparts(target,original,parameters)
+ local characters = target.characters
+ local list = parameters.list
+ if list then
+ for unicode, data in sortedhash(list) do
+ local template = data.template
+ if template then
+ local source = characters[template]
+ local target = characters[unicode]
+ if source and target then
+ local sequence = data.sequence
+ if sequence then
+ local parts = source.parts
+ if parts then
+ local p = { }
+ for i=1,#sequence do
+ local step = sequence[i]
+ local glyph = step.glyph
+ if glyph == "first" or glyph == "last" then
+ local g = glyph == "first" and 1 or #parts
+ local c = fastcopy(parts[g])
+ local f = step.factor
+ if f then
+ c["end"] = f * (c["end"] or 0)
+ c.start = f * (c.start or 0)
+ end
+ p[#p+1] = c
+ else
+ local c = characters[glyph]
+ if c then
+ p[#p+1] = {
+ glyph = glyph,
+ advance = c.width,
+ start = 0,
+ ["end"] = 0,
+ }
+ end
+ end
+ end
+ if #p > 0 then
+ target.parts = p
+ if not data.horizontal then
+ target.partsorientation = "vertical"
+ end
+ end
+ end
+ end
+ end
end
- elseif properties then
- report_math("invalid parameters in font %a",properties.fullname or "?")
+ end
+ end
+ end
+
+end
+
+function mathtweaks.action(target,original,parameters)
+ local action = parameters.action
+ if type(action) == "function" then
+ action(target,original,parameters)
+ end
+end
+
+do
+
+ local list = {
+ { 0x00A0, "s", 1 }, -- nbsp
+ { 0x2000, "q", 1/2 }, -- enquad
+ { 0x2001, "q", 1 }, -- emquad
+ { 0x2002, "q", 1/2 }, -- enspace
+ { 0x2003, "q", 1 }, -- emspace
+ { 0x2004, "q", 1/3 }, -- threeperemspace
+ { 0x2005, "q", 1/4 }, -- fourperemspace
+ { 0x2006, "q", 1/6 }, -- sixperemspace
+ { 0x2007, "c", byte('0') }, -- figurespace
+ { 0x2008, "c", byte('.') }, -- punctuationspace
+ { 0x2009, "q", 1/8 }, -- breakablethinspace
+ { 0x200A, "q", 1/8 }, -- hairspace
+ { 0x200B, "q", 0 }, -- zerowidthspace
+ { 0x202F, "q", 1/8 }, -- narrownobreakspace
+ { 0x205F, "s", 1/2 }, -- math thinspace
+ }
+
+ datasets.checkspacing = list
+
+ function mathtweaks.checkspacing(target,original,parameters)
+ local characters = target.characters
+ local parameters = target.parameters
+ for i=1,#list do
+ local entry = list[i]
+ local unicode = entry[1]
+ local data = characters[unicode]
+ if not data then
+ local method = entry[2]
+ local fraction = entry[3]
+ local width = 0
+ local height = 0
+ -- local depth = 0
+ if method == "c" then
+ local template = characters[fraction]
+ width = template.width
+ height = template.height
+ -- depth = template.depth
+ elseif method == "s" then
+ width = fraction * parameters.space -- space
+ height = 0
+ -- depth = 0
+ else
+ width = fraction * parameters.quad -- quad
+ height = 0
+ -- depth = 0
+ end
+ if trace_tweaking then
+ report_tweak("setting width of %U to %p",target,original,unicode,width)
+ end
+ characters[unicode] = {
+ width = width,
+ -- advance = width,
+ height = height,
+ -- depth = depth,
+ unicode = unicode,
+ commands = {
+ -- { "slot", 0, 32 },
+ },
+ }
+ end
+ end
+ end
+
+end
+
+do
+
+ -- mirror
+ -- smaller
+
+ local nps = fonts.helpers.newprivateslot
+
+ local radicalbarextender = nps("radical bar extender") -- we reserve it here
+
+ local list = {
+ 0x221A,
+ }
+
+ local function fix(target,original,characters,unicode)
+ local data = characters[unicode]
+ if data then
+ local height = data.height or 0
+ local depth = data.depth or 0
+ if depth > height then
+ if trace_tweaking then
+ report_tweak("swapping height and depth of radical %U",target,original,unicode)
+ end
+ data.height = depth
+ data.depth = height
+ if data.rorrim then
+ -- the original does the magic
+ else
+ data.yoffset = depth - height
+ end
+ end
+ local smaller = data.smaller
+ if smaller then
+ fix(target,original,characters,smaller)
+ end
+ local mirror = data.mirror
+ if mirror then
+ fix(target,original,characters,mirror)
+ end
+ end
+ end
+
+ function mathtweaks.fixradicals(target,original,parameters)
+ local characters = target.characters
+ for i=1,#list do
+ local unicode = list[i]
+ fix(target,original,characters,unicode)
+ end
+ end
+
+ local function fix(target,original,characters,u,l)
+ local data = characters[u]
+ if data then
+ data.innerlocation = l.location == "right" and 2 or 1
+ data.innerxoffset = (l.hfactor or 1) * (data.width or 0)
+ data.inneryoffset = (l.vfactor or 1) * ((data.height or 0) + (data.depth or 0))
+ end
+ end
+
+ function mathtweaks.radicaldegreeanchors(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local characters = target.characters
+ for unicode, l in sortedhash(list) do -- resolve variants
+ local u = detail(characters,unicode) or unicode
+ if type(u) == "table" then
+ for i=1,#u do
+ fix(target,original,characters,u[i],l)
+ end
+ else
+ fix(target,original,characters,u,l)
+ end
+ end
+ end
+ end
+
+end
+
+do
+
+ local done = nil
+
+ local function fix(target,original,characters,unicode,axis)
+ if done[unicode] then
+ return
+ end
+ done[unicode] = true
+ local data = characters[unicode]
+ if data then
+ local height = data.height or 0
+ local depth = data.depth or 0
+ if trace_tweaking then
+ report_tweak("swapping height and depth of %U",target,original,unicode)
+ end
+ local half = (height + depth)/2
+ if data.rorrim then
+ -- the original does the magic
else
- report_math("invalid parameters in font")
+ data.yoffset = depth - (half - axis)
+ end
+ height = half + axis
+ depth = half - axis
+ data.height = height
+ data.depth = depth
+ local smaller = data.smaller
+ if smaller then
+ fix(target,original,characters,smaller,axis)
+ end
+ local mirror = data.mirror
+ if mirror then
+ fix(target,original,characters,mirror,axis)
+ end
+ local next = data.next
+ if next then
+ fix(target,original,characters,next,axis)
+ end
+ end
+ end
+
+ function mathtweaks.fixoldschool(target,original,parameters)
+ local characters = target.characters
+ local list = mathtweaks.subsets.integrals
+ local also = getalso(target,original)
+ local axis = target.mathparameters.AxisHeight
+ done = { }
+ for i=1,#list do
+ local unicode = list[i]
+ fix(target,original,characters,unicode,axis)
+ end
+ if also then
+ local a = also[u]
+ if a then
+ for i=1,#a do
+ fix(target,original,characters,a[i],axis)
+ end
+ end
+ end
+ done = nil
+ end
+
+ -- After the next one I rewarded myself by (again) watching Joe Parrish interpretation
+ -- of Shostakovich 10 Mvmt. II - Metal several times (video on yt, track on bandcamp)
+ -- ... timestamp: awaiting the new Albion (Official) single; their work comes in parts.
+
+ function mathtweaks.fixintegrals(target,original,parameters)
+ local characters = target.characters
+ local integral = characters[0x222B]
+ if integral and not integral.parts then
+ local top = characters[0x2320]
+ local mid = characters[0x23AE]
+ local bot = characters[0x2321]
+ if top and mid and bot then
+ top = top.height
+ mid = mid.height
+ bot = bot.height
+ integral.partsitalic = integral.italic
+ integral.parts = {
+ { advance = bot, ["end"] = bot/3, glyph = 0x2321, start = bot/3 },
+ { advance = mid, ["end"] = mid/2, glyph = 0x23AE, start = mid/2, extender = 1 },
+ { advance = top, ["end"] = top/3, glyph = 0x2320, start = top/3 },
+ }
+ integral.partsorientation = "vertical"
+ if trace_tweaking then
+ report_tweak("fixing the integral extensible",target,original)
+ end
end
- elseif properties then
- report_math("no parameters in font %a",properties.fullname or "?")
else
- report_math("no parameters and properties in font")
+ report_tweak("no need to fix the integral extensible",target,original)
end
end
+
end
-function mathematics.overloadparameters(target,original)
- local mathparameters = target.mathparameters
- if mathparameters and next(mathparameters) then
- local goodies = target.goodies
- if goodies then
- for i=1,#goodies do
- local goodie = goodies[i]
- local mathematics = goodie.mathematics
- local parameters = mathematics and mathematics.parameters
- if parameters then
- if trace_defining then
- report_math("overloading math parameters in %a @ %p",target.properties.fullname,target.parameters.size)
- end
- for name, value in next, parameters do
- local tvalue = type(value)
- if tvalue == "string" then
- report_math("comment for math parameter %a: %s",name,value)
+do
+
+ local list = { 0x2061, 0x2062, 0x2063, 0x2064 }
+
+ datasets.wipecues = list
+
+ function mathtweaks.wipecues(target,original,parameters)
+ local characters = target.characters
+ local tobewiped = parameters.list or list
+ local done = false
+ for i=1,#tobewiped do
+ local unicode = tobewiped[i]
+ characters[unicode] = {
+ width = 0,
+ height = 0,
+ depth = 0,
+ unicode = unicode,
+ }
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[unicode] = true
+ else
+ done = { [unicode] = true }
+ end
+ end
+ feedback_tweak("wipecues",target,original,done)
+ end
+
+end
+
+do
+
+ local mapping = {
+ [0x002F] = 0x2044,
+ }
+
+ datasets.fixslashes = mapping
+
+ function mathtweaks.fixslashes(target,original,parameters)
+ local characters = target.characters
+ -- local done = false
+ for normal, weird in sortedhash(mapping) do
+ local normalone = characters[normal]
+ local weirdone = characters[weird]
+ if normalone and weirdone and not normalone.next then
+ normalone.next = weirdone.next
+ -- if not trace_tweaking then
+ -- done = true
+ -- elseif done then
+ -- done[normal] = true
+ -- else
+ -- done = { [normal] = true }
+ -- end
+ end
+ weirdone = copytable(normalone)
+ characters[weird] = weirdone
+ weirdone.unicode = weird
+ end
+ -- feedback_tweak("fixslashes",target,original,done)
+ if trace_tweaking then
+ report_tweak("slashes fixed",target,original)
+ end
+ end
+
+end
+
+do -- see pagella for an extensive example
+
+ local nps = fonts.helpers.newprivateslot
+
+ local mapping = {
+ [0x0300] = { 0x0060, false, nps("flat 0x0060 1") },
+ [0x0308] = { 0x00A8, false, nps("flat 0x00A8 1") },
+ [0x0304] = { 0x00AF, false, nps("flat 0x00AF 1") },
+ [0x0301] = { 0x00B4, false, nps("flat 0x00B4 1") },
+ [0x0302] = { 0x02C6, true, nps("flat 0x02C6 1") },
+ [0x030C] = { 0x02C7, true, nps("flat 0x02C7 1") },
+ [0x0306] = { 0x02D8, false, nps("flat 0x02D8 1") },
+ [0x0307] = { 0x02D9, false, nps("flat 0x02D9 1") },
+ [0x030A] = { 0x02DA, false, nps("flat 0x02DA 1") },
+ [0x0303] = { 0x02DC, true, nps("flat 0x02DC 1") },
+ [0x20DB] = { 0x20DB, false, nps("flat 0x20DB 1") },
+ }
+
+ datasets.fixaccents = mapping
+ datasets.extendaccents = mapping
+ datasets.flattenaccents = mapping
+ datasets.copyaccents = mapping
+
+ function mathtweaks.fixaccents(target,original,parameters)
+ local characters = target.characters
+ local done = false
+ for stretching, entry in sortedhash(mapping) do
+ local alias = entry[1]
+ local stretchingdata = characters[stretching]
+ if stretchingdata and stretchingdata.width == 0 then
+ local topanchor = stretchingdata.topanchor or 0
+ local width = -topanchor
+ topanchor = width/2
+ stretchingdata.width = width
+ stretchingdata.advance = 0
+ stretchingdata.topanchor = topanchor
+ stretchingdata.commands = { rightcommand[width + topanchor], charcommand[stretching] }
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[stretching] = true
+ else
+ done = { [stretching] = true }
+ end
+ end
+ end
+ feedback_tweak("fixaccents",target,original,done)
+ end
+
+ -- all true|number false
+
+ function mathtweaks.extendaccents(target,original,parameters)
+ local characters = target.characters
+ local all = parameters.all
+ local count = tonumber(all)
+ local done = false
+ for stretching, entry in sortedhash(mapping) do
+ local extend = entry[2]
+ if extend then
+ local last = characters[stretching]
+ local cnt = 1
+ local okay = false
+ while last do
+ if all or (count and cnt > count) then
+ last.extensible = true
+ local flataccent = last.flataccent
+ if flataccent then
+ characters[flataccent].extensible = true
+ okay = true
+ end
+ end
+ local n = last.next
+ if n then
+ last = characters[n]
+ else
+ last.extensible = true
+ local flataccent = last.flataccent
+ if flataccent then
+ characters[flataccent].extensible = true
+ okay = true
+ end
+ break
+ end
+ cnt = cnt + 1
+ end
+ if okay then
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[stretching] = true
+ else
+ done = { [stretching] = true }
+ end
+ end
+ end
+ end
+ feedback_tweak("extendaccents",target,original,done)
+ end
+
+ -- force true false
+ -- height factor 0.8
+ -- offset factor 0.9|calculated
+ -- squeeze factor 0.1|calculated
+
+ function mathtweaks.flattenaccents(target,original,parameters)
+ local characters = target.characters
+ local force = parameters.force
+ local squeeze = parameters.squeeze or 0.8
+ local ofactor = parameters.offset or (squeeze/2)
+ local hfactor = parameters.height or (1 - ofactor)
+ local done = false
+ for stretching, entry in sortedhash(mapping) do
+ local last = characters[stretching]
+ while last do
+ if force or not last.flataccent then
+ local slot = entry[3]
+ local data = copytable(last)
+ local height = data.height or 0
+ data.effect = { squeeze = squeeze }
+ data.height = hfactor * height
+ data.yoffset = ofactor * height
+ characters[slot] = data
+ last.flataccent = slot
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[stretching] = true
+ else
+ done = { [stretching] = true }
+ end
+ end
+ local n = last.next
+ if n then
+ last = characters[n]
+ else
+ break
+ end
+ end
+ end
+ feedback_tweak("flattenaccents",target,original,done)
+ end
+
+ function mathtweaks.copyaccents(target,original,parameters)
+ local characters = target.characters
+ local done = false
+ for stretching, entry in sortedhash(mapping) do
+ local alias = entry[1]
+ if alias ~= stretching then
+ local stretchingdata = characters[stretching]
+ if stretchingdata then
+ -- we need to nil [x|y]offsets
+ characters[alias] = {
+ width = stretchingdata.width,
+ height = stretchingdata.height,
+ depth = stretchingdata.depth,
+ next = stretchingdata.next,
+ commands = { charcommand[stretching] },
+ topanchor = stretchingdata.topanchor,
+ -- unicode = stretching, -- when we alias to combiners
+ unicode = alias, -- when we keep the original
+ }
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[stretching] = true
+ else
+ done = { [stretching] = true }
+ end
+ end
+ end
+ end
+ feedback_tweak("copyaccents",target,original,done)
+ end
+
+end
+
+do
+
+ local single <const> = 0x003D
+ local double <const> = 0x2A75
+ local triple <const> = 0x2A76
+
+ function mathtweaks.addequals(target,original,parameters)
+ local characters = target.characters
+ local basechar = characters[single]
+ local width = basechar.width
+ local height = basechar.height
+ local depth = basechar.depth
+ local advance = (parameters.advance or 1/20) * width
+ local char = charcommand[single]
+ local left = leftcommand[advance]
+ characters[double] = {
+ unicode = double,
+ width = 2*width - 1*advance,
+ height = height,
+ depth = depth,
+-- callback = "devirtualize",
+ commands = { char, left, char },
+ }
+ characters[triple] = {
+ unicode = triple,
+ width = 3*width - 2*advance,
+ height = height,
+ depth = depth,
+-- callback = "devirtualize",
+ commands = { char, left, char, left, char },
+ }
+ if trace_tweaking then
+ report_tweak("double %U and triple %U equals added",target,original,double,triple)
+ end
+ end
+
+end
+
+do
+
+ -- If we really want, we can have variants that also match radicals but in practice
+ -- radicals and actuarians are never seen together. We could also have a smaller
+ -- extender.
+
+ local nps = fonts.helpers.newprivateslot
+
+ local radical <const> = 0x0221A
+ local actuarian <const> = nps("delimited right annuity") -- 0x020E7
+ local nairautca <const> = nps("delimited left annuity" )
+ local placehold <const> = nps("delimited ghost annuity")
+
+ function mathtweaks.addactuarian(target,original,parameters)
+ local characters = target.characters
+ local parameters = target.parameters
+ local linewidth = target.MathConstants.RadicalRuleThickness -- make option
+ local basechar = characters[radical]
+ local baseheight = (basechar.height or 0)/2
+ local basedepth = (basechar.depth or 0)/2
+ local basetotal = baseheight + basedepth
+ local used = baseheight
+ --
+ characters[0x020E7] = {
+ width = 6*linewidth,
+ height = baseheight,
+ depth = basedepth,
+ unicode = actuarian,
+ callback = "devirtualize",
+ commands = {
+ upcommand[baseheight-4*linewidth],
+ { "rule", linewidth, 4*linewidth },
+ downcommand[basetotal/2-linewidth],
+ { "rule", basetotal/2, linewidth },
+ },
+ }
+ --
+ characters[actuarian] = {
+ width = 2*linewidth,
+ height = baseheight,
+ depth = basedepth,
+ unicode = actuarian,
+ callback = "devirtualize",
+ commands = {
+ downcommand[basedepth],
+ { "rule", basetotal, linewidth },
+ },
+ parts = {
+ {
+ advance = basetotal,
+ ["end"] = used,
+ glyph = actuarian,
+ start = 0,
+ },
+ {
+ advance = basetotal,
+ ["end"] = 0,
+ extender = 1,
+ glyph = actuarian,
+ start = used,
+ },
+ }
+ }
+ characters[placehold] = {
+ width = 2*linewidth,
+ height = baseheight,
+ depth = basedepth,
+ unicode = actuarian, -- whatever
+ callback = "devirtualize",
+ commands = {
+ rightcommand[linewidth],
+ downcommand[basedepth],
+ { "rule", basetotal, 0 },
+ },
+ }
+ characters[nairautca] = {
+ width = 2*linewidth,
+ height = baseheight,
+ depth = basedepth,
+ unicode = actuarian, -- whatever
+ callback = "devirtualize",
+ -- commands = {
+ -- rightcommand[linewidth],
+ -- downcommand[basedepth],
+ -- { "rule", basetotal, linewidth },
+ -- },
+ commands = {
+ rightcommand[linewidth],
+ upcommand[baseheight-4*linewidth],
+ { "rule", 4*linewidth, linewidth },
+ },
+ parts = {
+ {
+ advance = basetotal,
+ ["end"] = used,
+ extender = 1,
+ glyph = placehold,
+ start = 0,
+ },
+ {
+ advance = basetotal,
+ ["end"] = 0,
+ glyph = nairautca,
+ start = used,
+ },
+ }
+ }
+ if trace_tweaking then
+ report_tweak("actuarian %U added",target,original,actuarian)
+ end
+ end
+
+end
+
+do
+
+ -- todo: make callback because we can delay it but then we need to stack
+ -- callbacks
+
+ local nps = fonts.helpers.newprivateslot
+
+ local list = {
+ -- { 0x0300, nps("delimited right grave"), nps("delimited ghost grave") },
+ { 0x0308, nps("delimited right ddot"), nps("delimited ghost ddot") },
+ { 0x0304, nps("delimited right bar"), nps("delimited ghost bar") },
+ -- { 0x0301, nps("delimited right acute"), nps("delimited ghost acute") },
+ { 0x0302, nps("delimited right hat"), nps("delimited ghost hat") },
+ { 0x030C, nps("delimited right check"), nps("delimited ghost check") },
+ { 0x0306, nps("delimited right breve"), nps("delimited ghost breve") },
+ { 0x0307, nps("delimited right dot"), nps("delimited ghost dot") },
+ { 0x030A, nps("delimited right ring"), nps("delimited ghost ring") },
+ { 0x0303, nps("delimited right tilde"), nps("delimited ghost tilde") },
+ { 0x20DB, nps("delimited right dddot"), nps("delimited ghost dddot") },
+ { 0x2020, nps("delimited right dagger"), nps("delimited ghost dagger") },
+ { 0x2021, nps("delimited right ddagger"), nps("delimited ghost ddagger") },
+ { 0x2217, nps("delimited right ast"), nps("delimited ghost ast") },
+ { 0x22C6, nps("delimited right star"), nps("delimited ghost star") },
+
+ { 0x231C, nps("delimited left upper corner"), nps("delimited ghost upper corner") },
+ { 0x231D, nps("delimited right upper corner"), nps("delimited ghost upper corner") },
+ { 0x231E, nps("delimited left lower corner"), nps("delimited ghost lower corner"), true },
+ { 0x231F, nps("delimited right lower corner"), nps("delimited ghost lower corner"), true },
+ }
+
+ function mathtweaks.addfourier(target,original,parameters)
+ local characters = target.characters
+ for i=1,#list do
+ local entry = list[i]
+ local basecode = entry[1]
+ local fouriercode = entry[2]
+ local movecode = entry[3]
+ local reverse = entry[4]
+ local basechar = characters[basecode]
+ if basechar then
+ local scale = parameters.scale or 1
+ local variant = parameters.variant
+ if variant then
+ for i=1,variant do
+ local okay = basechar.next
+ if okay then
+ basecode = okay
+ basechar = characters[basecode]
else
- local oldvalue = mathparameters[name]
- local newvalue = oldvalue
- if oldvalue then
- if tvalue == "number" then
- newvalue = value
- elseif tvalue == "function" then
- newvalue = value(oldvalue,target,original)
- elseif not tvalue then
- newvalue = nil
+ break
+ end
+ end
+ end
+ local baseheight = scale * (basechar.height or 0)
+ local basedepth = scale * (basechar.depth or 0)
+ local basewidth = scale * (basechar.width or 0)
+ local used = baseheight/2
+ local total = baseheight + basedepth
+if reverse then
+ used = total / 2 -- basedepth / 2
+end
+ characters[movecode] = {
+ width = basewidth,
+ height = used,
+ unicode = basecode,
+ -- callback = "devirtualize",
+ commands = {
+ downcommand[used],
+ { "rule", used, 0 },
+ },
+ }
+ local parts = {
+ {
+ advance = used,
+ ["end"] = used,
+ extender = 1,
+ glyph = movecode,
+ start = used,
+ },
+ {
+ advance = total,
+ ["end"] = 0,
+ glyph = fouriercode,
+ start = total,
+ },
+ }
+ if reverse then
+ parts[1], parts[2] = parts[2], parts[1]
+ end
+ characters[fouriercode] = {
+ width = basewidth,
+ height = baseheight,
+ depth = basedepth,
+ unicode = basecode,
+ -- callback = "devirtualize",
+ commands = {
+ scale == 1 and charcommand[basecode] or { "slot", 0, basecode, scale, scale },
+ },
+ partsorientation = "vertical",
+ parts = parts,
+ }
+ if trace_tweaking then
+ report_tweak("fourier %U added using %U",target,original,basecode,fouriercode)
+ end
+ end
+ end
+ end
+
+end
+
+do
+
+ -- \im{\left\Uchar"007C \frac{1}{2} \right\Uchar"007C}
+ -- \im{\left\Uchar"2016 \frac{1}{2} \right\Uchar"2016}
+ -- \im{\left\Uchar"2980 \frac{1}{2} \right\Uchar"2980}
+
+ local single <const> = 0x007C
+ local double <const> = 0x2016
+ local triple <const> = 0x2980
+
+ -- local nps = fonts.helpers.newprivateslot
+ --
+ -- local function variantlist(characters,unicode,chardata,what,total,used)
+ -- local parenthesis = characters[0x28].next
+ -- local width = chardata.width
+ -- local height = chardata.height
+ -- local depth = chardata.depth
+ -- local total = height + depth
+ -- local count = 1
+ -- while parenthesis do
+ -- local private = nps(what .. " size " .. count)
+ -- local pardata = characters[parenthesis]
+ -- local parheight = pardata.height
+ -- local pardepth = pardata.depth
+ -- local scale = (parheight+pardepth)/total
+ -- local offset = - pardepth + scale * depth
+ -- chardata.next = private
+ -- chardata = {
+ -- unicode = unicode,
+ -- width = width,
+ -- height = parheight,
+ -- depth = pardepth,
+ -- commands = {
+ -- { "offset", 0, offset, unicode, 1, scale }
+ -- },
+ -- }
+ -- characters[private] = chardata
+ -- parenthesis = pardata.next
+ -- if paranthesis then
+ -- pardata = characters[parenthesis]
+ -- end
+ -- count = count + 1
+ -- end
+ -- chardata.parts = {
+ -- {
+ -- advance = total,
+ -- ["end"] = used,
+ -- glyph = unicode,
+ -- start = 0,
+ -- -- start = used/5,
+ -- },
+ -- {
+ -- advance = total,
+ -- -- ["end"] = 0,
+ -- ["end"] = used/5, -- prevents small gap with inward curved endpoints
+ -- extender = 1,
+ -- glyph = unicode,
+ -- start = used,
+ -- },
+ -- }
+ -- chardata.partsorientation = "vertical"
+ -- end
+
+ local function variantlist(unicode,chardata,total,used)
+ chardata.varianttemplate = 0x0028
+ chardata.parts = {
+ {
+ advance = total,
+ ["end"] = used,
+ glyph = unicode,
+ start = 0,
+ -- start = used/5,
+ },
+ {
+ advance = total,
+ -- ["end"] = 0,
+ ["end"] = used/5, -- prevents small gap with inward curved endpoints
+ extender = 1,
+ glyph = unicode,
+ start = used,
+ },
+ }
+ chardata.partsorientation = "vertical"
+ end
+
+ function mathtweaks.addbars(target,original,parameters)
+ local characters = target.characters
+ local basechar = characters[single]
+ local width = basechar.width
+ local height = basechar.height
+ local depth = basechar.depth
+ local advance = (parameters.advance or 1/10) * width
+ local used = 1.2*height -- large overlap because no smaller pieces
+ local total = height + depth
+ --
+ -- variantlist(characters,single,basechar,"single bar",total,used)
+ variantlist(single,basechar,total,used)
+ --
+ basechar = {
+ unicode = double,
+ width = 2*width - 1*advance,
+ height = height,
+ depth = depth,
+-- callback = "devirtualize",
+ commands = {
+ charcommand[single],
+ leftcommand[advance],
+ charcommand[single],
+ },
+ }
+ characters[double] = basechar
+ -- variantlist(characters,double,basechar,"double bar",total,used)
+ variantlist(double,basechar,total,used)
+ --
+ basechar = {
+ unicode = triple,
+ width = 3*width - 2*advance,
+ height = height,
+ depth = depth,
+-- callback = "devirtualize",
+ commands = {
+ charcommand[single],
+ leftcommand[advance],
+ charcommand[single],
+ leftcommand[advance],
+ charcommand[single],
+ },
+ }
+ characters[triple] = basechar
+ -- variantlist(characters,triple,basechar,"triple bar",total,used)
+ variantlist(triple,basechar,total,used)
+ --
+ if trace_tweaking then
+ report_tweak("single, double and triple bars added",target,original)
+ end
+ end
+
+end
+
+do
+
+ -- lucida: \im{\cdot \ldot \cdots \ldots}
+
+ local snormal <const> = 0x002E
+ local sraised <const> = 0x22C5
+
+ local tnormal <const> = 0x2026
+ local traised <const> = 0x22EF
+
+ function mathtweaks.fixellipses(target,original,parameters)
+ local characters = target.characters
+ local function fix(normal,raised)
+ local normaldata = characters[normal]
+ if normaldata then
+ local raiseddata = copytable(normaldata)
+ characters[raised] = raiseddata
+ raiseddata.unicode = raised
+ local height = raiseddata.height
+ local yoffset = (parameters.yoffset or 2) * height
+ raiseddata.yoffset = yoffset
+ raiseddata.height = height + yoffset
+ if trace_tweaking then
+ report_tweak("taking %U from %U",target,original,raised,normal)
+ end
+ end
+ end
+ fix(snormal,sraised)
+ fix(tnormal,traised)
+ end
+
+end
+
+do
+
+ -- For Ton, who needs the high minus and plus for calculator signs in Dutch
+ -- school math books.
+
+ local list = {
+ { 0x207A, 0x002B, true },
+ { 0x207B, 0x2212, true },
+ { 0x208A, 0x002B, false },
+ { 0x208B, 0x2212, false },
+ }
+
+ datasets.addscripts = list
+
+ local function add(target,original,characters,unicode,template,super,baseheight,scale)
+ if not characters[unicode] then
+ local origdata = characters[template]
+ if origdata then
+ local width = scale * (origdata.width or 0)
+ local height = scale * (origdata.height or 0)
+ local depth = scale * (origdata.depth or 0)
+ local half = - (height + depth) / 2
+ local offset = super and baseheight/2 or -baseheight/4
+ characters[unicode] = {
+ width = width,
+ height = height + offset,
+ depth = depth - offset,
+ unicode = unicode,
+ commands = {
+ { "offset", 0, offset, template, scale, scale }
+ },
+ }
+ if trace_tweaking then
+ report_tweak("adding script %U scaled %0.3f",target,original,unicode,scale)
+ end
+ -- no need for smaller
+ end
+ end
+ end
+
+ function mathtweaks.addscripts(target,original,parameters)
+ local characters = target.characters
+ local baseheight = target.mathparameters.AccentBaseHeight
+ local scaledown = parameters.scale or target.mathparameters.ScriptScriptPercentScaleDown / 100
+ for i=1,#list do
+ local entry = list[i]
+ if entry then
+ add(target,original,characters,entry[1],entry[2],entry[3],baseheight,scaledown)
+ end
+ end
+ end
+
+end
+
+do
+
+ -- We started with the list that xits has in rtlm but most of them can be derived from
+ -- the database, and others need to be added.
+
+ -- Checked while watching/listening to Dave Matthews Band: The Central Park Concert
+ -- (with superb solos by Warren Haynes), a DVD I bought around when we started with the
+ -- LUATEX advanture.
+
+ local mirrors = {
+
+ [0x0002F] = true, -- slashes
+ [0x0005C] = true,
+ [0x000F7] = true,
+ [0x02044] = true,
+ [0x02215] = true,
+
+ [0x02032] = true, -- primes
+ [0x02033] = true,
+ [0x02034] = true,
+ [0x02057] = true,
+ [0x02035] = true,
+ [0x02036] = true,
+ [0x02037] = true,
+
+ [0x0221A] = true, -- radicals
+ [0x0221B] = true,
+ [0x0221C] = true,
+ [0x0221D] = true,
+
+ [0x0222B] = true, -- integrals
+ [0x0222C] = true,
+ [0x0222D] = true,
+ [0x0222E] = true,
+ [0x0222F] = true,
+ [0x02230] = true,
+ [0x02231] = true,
+ [0x02232] = true,
+ [0x02233] = true,
+
+ [0x02A0A] = true, -- seen in xits (to be checked)
+ [0x02A0B] = true,
+ [0x02A0C] = true,
+ [0x02A0D] = true,
+ [0x02A0E] = true,
+
+ [0x02140] = true,
+ [0x02201] = true,
+ [0x02202] = true,
+ [0x02203] = true,
+ [0x02204] = true,
+ [0x02211] = true,
+
+ [0x02239] = true,
+ [0x0225F] = true,
+ [0x0228C] = true,
+ [0x022A7] = true,
+ [0x022AA] = true,
+ [0x022AC] = true,
+ [0x022AD] = true,
+ [0x022AE] = true,
+ [0x022AF] = true,
+ [0x022F5] = true,
+ [0x022F8] = true,
+ [0x022F9] = true,
+ [0x022FF] = true,
+ [0x02320] = true,
+ [0x02321] = true,
+ [0x027C0] = true,
+ [0x029DC] = true,
+ [0x029F4] = true,
+
+ [0x02A0F] = true,
+ [0x02A10] = true,
+ [0x02A11] = true,
+ [0x02A12] = true,
+ [0x02A13] = true,
+ [0x02A14] = true,
+ [0x02A15] = true,
+ [0x02A16] = true,
+ [0x02A17] = true,
+ [0x02A18] = true,
+ [0x02A19] = true,
+ [0x02A1A] = true,
+ [0x02A1B] = true,
+ [0x02A1C] = true,
+ [0x02A20] = true,
+
+ [0x02A74] = true,
+ [0x02AA3] = true,
+ [0x02AE2] = true,
+ [0x02AE6] = true,
+ [0x1D715] = true,
+ }
+
+ local new = fonts.helpers.newprivateslot
+
+ local function add(target,original,characters,unicode,what)
+ local data = characters[unicode]
+ if data then
+ if not data.mirror then
+ local slot = new("mirror."..unicode)
+ local mirror = copytable(data)
+ data.mirror = slot
+ mirror.rorrim = unicode -- so we can check later
+ mirror.commands = {
+ { "offset", data.width, 0, unicode, -1, 1 }
+ }
+ if trace_tweaking then
+ report_tweak("adding mirror %U (%s)",target,original,unicode,what)
+ end
+ characters[slot] = mirror
+ elseif trace_tweaking then
+ report_tweak("skipping mirror %U (%s)",target,original,unicode,what)
+ end
+ local parts = data.parts
+ if parts then
+ for i=1,#parts do
+ add(target,original,characters,parts[i],"hpart")
+ end
+ end
+ local smaller = data.smaller
+ if smaller then
+ add(target,original,characters,"smaller")
+ end
+ local next = data.next
+ if next then
+ add(target,original,characters,next,"next")
+ end
+ end
+ end
+
+ -- todo: also check the rtlm table if present
+
+ function mathtweaks.addmirrors(target,original,parameters)
+ local characters = target.characters
+ -- for unicode, detail in sortedhash(characters) do
+ for unicode, detail in next, characters do
+ local data = chardata[unicode]
+ if data and data.mirror then
+ add(target,original,characters,unicode,"mirror")
+ end
+ end
+ for unicode, detail in sortedhash(mirrors) do
+ if characters[unicode] then
+ add(target,original,characters,unicode,"character")
+ elseif trace_tweaking then
+ report_tweak("ignoring mirror %U (%s)",target,original,unicode,what)
+ end
+ end
+ end
+
+end
+
+do
+
+ local reported = { }
+
+ function mathtweaks.version(target,original,parameters)
+ local metadata = original.shared.rawdata.metadata
+ if metadata then
+ local version = string.strip(metadata.version or "") -- some have trailing spaces
+ if version then
+ local expected = parameters.expected
+ local fontname = metadata.fontname or false
+ local message = parameters.message
+ -- version = tonumber(string.match(version,"%d+.%d+"))
+ if version ~= expected and not reported[fontname] then
+ report_tweak("version %a found, version %a expected",target,original,version,expected)
+ elseif trace_tweaking then
+ report_tweak("version %a found",target,original,version)
+ end
+ if message and message ~= "" and not reported[fontname] then
+ report_tweak()
+ report_tweak("%s",target,original,message)
+ report_tweak()
+ end
+ reported[fontname] = true
+ target.tweakversion = version
+ end
+ end
+ end
+
+end
+
+do
+
+ 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
+
+ function mathtweaks.bigslots(target,original,parameters)
+ local list = parameters.list
+ if list then
+ target.bigslots = list
+ end
+ end
+
+end
+
+-- do
+--
+-- function mathtweaks.diagnose(target,original,parameters)
+-- local characters = target.characters
+-- for k, v in sortedhash(characters) do
+-- local italic = v.italic
+-- if italic then
+-- report_tweak("italics: %C %p",target,original,k,italic)
+-- end
+-- end
+-- end
+--
+-- end
+
+do
+
+ function mathtweaks.setoptions(target,original,parameters)
+ local setlist = parameters.set or parameters.list
+ local resetlist = parameters.reset
+ if setlist or resetlist then
+ local properties = target.properties
+ local codes = tex.mathcontrolcodes
+ local oldcontrol = texget("mathfontcontrol")
+ local newcontrol = oldcontrol
+ -- todo: reset
+ if resetlist then
+ for i=1,#resetlist do
+ local v = tonumber(codes[resetlist[i]])
+ if v then
+ newcontrol = newcontrol & (not v)
+ end
+ end
+ end
+ if setlist then
+ for i=1,#setlist do
+ local v = tonumber(codes[setlist[i]])
+ if v then
+ newcontrol = newcontrol | v
+ end
+ end
+ end
+ newcontrol = newcontrol | codes.usefontcontrol
+ properties.mathcontrol = newcontrol
+ target.mathcontrol = newcontrol
+ if trace_tweaking then
+ report_tweak("forcing math font options 0x%08X instead of 0x%08X",target,original,newcontrol,oldcontrol)
+ end
+ end
+ end
+
+end
+
+do
+
+ function mathtweaks.setovershoots(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local characters = target.characters
+ local emwidth = target.parameters.quad
+ local done = false
+ for i=1,#list do
+ local entry = list[i]
+ local target = entry.target
+ local top = entry.topovershoot
+ local quad = entry.quad
+ if target and top then
+ local range = blocks[target]
+ if range then
+ if quad then
+ quad = emwidth
+ end
+ for r = range.first, range.last do
+ local unicode = mathgaps[r] or r
+ local data = characters[unicode]
+ if data then
+ data.topovershoot = top * (quad or data.width or 0)
+ if not trace_tweaking then
+ done = true
+ elseif done then
+ done[r] = true
+ else
+ done = { [r] = true }
end
- if trace_defining and oldvalue ~= newvalue then
- report_math("overloading math parameter %a: %S => %S",name,oldvalue,newvalue)
+ end
+ end
+ end
+ end
+ end
+ feedback_tweak("setovershoots",target,original,done)
+ end
+ end
+
+ -- there is no real need for thios but let's play nice with memory anyway
+
+ local efindex = 0
+ local effects = setmetatableindex (function (t,k)
+ efindex = efindex + 1
+ local v = "tweakreplacealphabets" .. efindex
+ local e = fonts.specifiers.presetcontext(v,"",k)
+ -- print(k,v,e)
+ t[k] = v
+ return v
+ end)
+
+ function mathtweaks.replacealphabets(target,original,parameters)
+ local list = parameters.list
+ if list then
+ local features = target.specification.features.normal
+ local definedfont = fonts.definers.internal
+ local copiedglyph = fonts.handlers.vf.math.copy_glyph
+ -- does a deep copy, including parts and so
+ local getsubstitution = fonts.handlers.otf.getsubstitution
+ local fontdata = fonts.hashes.identifiers
+ --
+ local fonts = target.fonts
+ local size = target.size
+ local characters = target.characters
+ if not fonts then
+ fonts = { }
+ target.fonts = fonts
+ end
+ if #fonts == 0 then
+ fonts[1] = { id = 0, size = size } -- self, will be resolved later
+ end
+ for i=1,#list do
+ local entry = list[i]
+ local filename = entry.filename or parameters.filename
+ local feature = entry.feature
+ local thesource = entry.source
+ local thetarget = entry.target or thesource
+ if thesource and thetarget then
+ local sourcerange = type(thesource) == "table" and thesource or blocks[thesource] -- .gaps
+ local targetrange = type(thetarget) == "table" and thetarget or blocks[thetarget] -- .gaps
+ if sourcerange and targetrange then
+ local firsttarget = targetrange.first
+ local firstsource = sourcerange.first
+ local lastsource = sourcerange.last or firstsource
+ if firstsource and firsttarget then
+ local offset = firsttarget - firstsource
+ if filename then
+ local rscale = entry.rscale or 1 -- todo
+ size = size * rscale -- maybe use scale in vf command
+ -- load font, todo: set language and script, the effect hack is ugly
+ local fullname = filename
+ local effect = features.effect
+ if effect then
+ fullname = fullname .. "*" .. effects["effect={"..effect.."}"]
+ end
+ local id = definedfont {
+ name = fullname,
+ size = size,
+ }
+ local chars = fontchars[id]
+ local dropin = fontdata[id]
+ local index = false
+ for i=1,#fonts do
+ local f = fonts[i]
+ if f.id == id and f.size == size then
+ index = i
+ break
+ end
+ end
+ if not index then
+ index = #fonts + 1
+ fonts[index] = { id = id, size = size }
+ end
+ -- copy characters
+ for s=firstsource,lastsource do
+ local t = s + offset
+ local sourceunicode = mathgaps[s] or s
+ if chars[sourceunicode] then
+ local targetunicode = mathgaps[t] or t
+ if feature then
+ sourceunicode = getsubstitution(dropin,sourceunicode,feature,true,"math","dflt") or sourceunicode
+ end
+-- if trace_tweaking then
+-- report_tweak("copying %s %U from file %a to %s %U",target,original,thesource,sourceunicode,filename,thetarget,targetunicode)
+-- end
+ characters[targetunicode] = copiedglyph(target,characters,chars,sourceunicode,index)
+ end
+ end
+ --
+ local inherit = entry.inherit
+ if inherit then
+ local mathparameters = target.mathparameters
+ local dropparameters = fontdata[id].mathparameters
+ if dropparameters then
+ for name in sortedhash(inherit) do
+ local value = dropparameters[name]
+ if value then
+ mathparameters[name] = value
+ end
+ end
+ end
+ end
+ --
+ elseif feature then
+ for s=firstsource,lastsource do
+ local t = s + offset
+ local sourceunicode = mathgaps[s] or s
+ local targetunicode = mathgaps[t] or t
+ local variant = getsubstitution(original,sourceunicode,feature,true,"math","dflt")
+ local data = characters[variant]
+ if data then
+-- if trace_tweaking then
+-- report_tweak("copying %s %U from feature %a to %s %U",target,original,thesource,sourceunicode,feature,thetarget,targetunicode)
+-- end
+ characters[targetunicode] = copytable(data)
+ end
end
else
- report_math("invalid math parameter %a",name)
+ for s=firstsource,lastsource do
+ local t = s + offset
+ local sourceunicode = mathgaps[s] or s
+ local targetunicode = mathgaps[t] or t
+ if sourceunicode ~= targetunicode then
+ local data = characters[sourceunicode]
+ if data then
+-- if trace_tweaking then
+-- report_tweak("copying %s %U to %s %U",target,original,thesource,sourceunicode,thetarget,targetunicode)
+-- end
+ characters[targetunicode] = copytable(data)
+ end
+ end
+ end
end
- mathparameters[name] = newvalue
+ else
+ -- error
end
end
end
end
end
end
+
+ function mathtweaks.fallbacks(target,original,parameters)
+ local fallbacks = target.specification.fallbacks
+ if fallbacks then
+ local definitions = fonts.collections.definitions[fallbacks]
+ if definitions then
+ local list = { }
+ for i=1,#definitions do
+ local definition = definitions[i]
+ -- local check = definition.check
+ -- local force = definition.force
+ local first = definition.start
+ local last = definition.stop
+ local offset = definition.offset or first
+ list[#list+1] = {
+ filename = definition.font,
+ rscale = definition.rscale or 1,
+ source = { first = first, last = last },
+ target = { first = offset, last = offset + (last - first) },
+ }
+ end
+ mathtweaks.replacealphabets(target,original,{
+ tweak = "replacealphabets",
+ list = list,
+ } )
+ end
+ end
+ end
+
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)
+local apply_tweaks = true directives.register("math.applytweaks", function(v) apply_tweaks = v end)
+local applied_tweaks = 0
+
+local function tweaklist(target,original,tweaks)
+ if type(tweaks) == "table" then
+ for i=1,#tweaks do
+ local tweak = tweaks[i]
+ 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 feature == nil or features[feature] 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
- for i=1,#tweaks do
- local tweak= tweaks[i]
- local tvalue = type(tweak)
- if tvalue == "function" then
- tweak(target,original)
+ end
+ end
+ end
+ end
+end
+
+function mathtweaks.tweaks(target,original,parameters)
+ tweaklist(target,original,parameters.list)
+end
+
+local function applytweaks(when,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
+ statistics.starttiming(mathtweaks)
+ applied_tweaks = applied_tweaks + 1
+ tweaks = tweaks[when]
+ if trace_defining then
+ report_math("tweaking math of %a @ %p (%s: %s)",target.properties.fullname,target.parameters.size,when,"okay")
end
+ tweaklist(target,original,tweaks)
end
+ statistics.stoptiming(mathtweaks)
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
+local function tweakable(target)
+ local mathparameters = target.mathparameters
+-- local features = target.specification.features
+-- local mathscript = features and features.normal and features.normal.script == "math"
+-- return mathparameters and mathscript -- and target,properties.hasmath
+ return mathparameters
+end
+
function mathematics.tweakbeforecopyingfont(target,original)
- local mathparameters = target.mathparameters -- why not hasmath
- if mathparameters then
+ if use_math_goodies and tweakable(target) then
applytweaks("beforecopying",target,original)
end
end
function mathematics.tweakaftercopyingfont(target,original)
- local mathparameters = target.mathparameters -- why not hasmath
- if mathparameters then
+ if use_math_goodies and tweakable(target) then
applytweaks("aftercopying",target,original)
end
end
-sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
-sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead
-sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters") -- after scaling !
-sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
+statistics.register("math tweaking time",function()
+ if applied_tweaks > 0 then
+ return string.format("%s seconds, %s math goodie tables", statistics.elapsedtime(mathtweaks),applied_tweaks)
+ end
+end)
-sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
-sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
+do
--- no, it's a feature now (see good-mth):
---
--- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions")
+ local defaults = {
+ {
+ source = "uppercasescript",
+ target = "uppercasecalligraphic",
+ },
+ {
+ source = "lowercasescript",
+ target = "lowercasecalligraphic",
+ },
+ {
+ source = "uppercaseboldscript",
+ target = "uppercaseboldcalligraphic",
+ },
+ {
+ source = "lowercaseboldscript",
+ target = "lowercaseboldcalligraphic",
+ },
+ }
--- a couple of predefined tweaks:
+ local reported = table.setmetatableindex("table")
+
+ function mathematics.checkaftercopyingfont(target,original)
+ if tweakable(target) then
+ local chardata = characters.data
+ local characters = target.characters
+ --
+ for i=1,#defaults do
+ -- we assume no ssty here yet .. todo
+ local default = defaults[i]
+ local block = blocks[default.target]
+ local first = block.first
+ local last = block.last
+ if not characters[mathgaps[first] or last] then
+ mathtweaks.replacealphabets(target,original,{
+ tweak = "replacealphabets",
+ list = { default }
+ })
+ end
+ end
+ --
+ local addvariant = mathematics.addvariant
+ local function register(old,new)
+ for i, cold in next, old do
+ local cnew = new[i]
+ addvariant(target,cold,cold,0xFE00)
+ addvariant(target,cnew,cnew,0xFE01)
+ addvariant(target,cnew,cold,0xFE00)
+ addvariant(target,cold,cnew,0xFE01)
+ end
+ end
+ local sr = mathematics.alphabets.sr.tf
+ local ca = mathematics.alphabets.ca.tf
+ register(sr.ucletters,ca.ucletters)
+ register(sr.lcletters,ca.lcletters)
+ --
+ if checkitalics then
+ local italics = 0
+ local metadata = original.shared.rawdata.metadata
+ local fontname = metadata and metadata.fontname or false
+ -- for k, v in sortedhash(characters) do
+ for k, v in next, characters do
+ local italic = v.italic
+ if italic then
+ local unicode = v.unicode
+ if unicode and not reported[fontname][unicode] then -- there can be variants
+ local data = chardata[unicode]
+ local description = data.description or ""
+ local category = data.category or "--"
+ report_tweak("italics: %C %p %s %s",target,original,k,italic,category,description)
+ reported[fontname][unicode] = true
+ end
+ italics = italics + 1
+ end
+ end
+ if italics > 0 then
+ report_tweak("still has %i italics",target,original,italics)
+ goto NEXTSTEP
+ end
+ end
-local tweaks = { }
-mathematics.tweaks = tweaks
+ target.properties.mathitalics = false
+ target.properties.textitalics = false
+ ::NEXTSTEP::
+ -- more to come
+ end
+ end
--- helpers
+end
-local setmetatableindex = table.setmetatableindex
+function mathematics.beforepassingfonttotex(target)
+ if tweakable(target) then
+ applytweaks("beforepassing",target,target)
+ end
+end
-local getfontoffamily = tex.getfontoffamily
+sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
+sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
+----------.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead
+----------.appendaction("mathparameters","system","mathematics.checkprivateparameters") -- after scaling !
-local fontcharacters = fonts.hashes.characters
-local extensibles = utilities.storage.allocate()
-fonts.hashes.extensibles = extensibles
+sequencers.appendaction("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
+sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
+sequencers.appendaction("aftercopyingcharacters", "system","mathematics.checkaftercopyingfont")
+sequencers.appendaction("beforepassingfonttotex", "system","mathematics.beforepassingfonttotex")
-local chardata = characters.data
-local extensibles = mathematics.extensibles
+-- no, it's a feature now (see good-mth):
+--
+-- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions")
-- we use numbers at the tex end (otherwise we could stick to chars)
@@ -261,6 +3796,8 @@ local e_unknown = extensibles.unknown
local unknown = { e_unknown, false, false }
+-- top curly bracket: 23DE
+
local function extensiblecode(font,unicode)
local characters = fontcharacters[font]
local character = characters[unicode]
@@ -279,15 +3816,7 @@ local function extensiblecode(font,unicode)
if not char then
return unknown
end
- if character.horiz_variants then
- if character.vert_variants then
- return { e_mixed, code, character }
- else
- local m = char.mathextensible
- local e = m and extensibles[m]
- return e and { e, code, character } or unknown
- end
- elseif character.vert_variants then
+ if character.parts then
local m = char.mathextensible
local e = m and extensibles[m]
return e and { e, code, character } or unknown
@@ -329,19 +3858,19 @@ local function horizontalcode(family,unicode)
local loffset = 0
local roffset = 0
if kind == e_left then
- local charlist = data[3].horiz_variants
+ local charlist = data[3].parts
if charlist then
local left = charlist[1]
loffset = abs((left["start"] or 0) - (left["end"] or 0))
end
elseif kind == e_right then
- local charlist = data[3].horiz_variants
+ local charlist = data[3].parts
if charlist then
local right = charlist[#charlist]
roffset = abs((right["start"] or 0) - (right["end"] or 0))
end
elseif kind == e_horizontal then
- local charlist = data[3].horiz_variants
+ local charlist = data[3].parts
if charlist then
local left = charlist[1]
local right = charlist[#charlist]
@@ -355,124 +3884,68 @@ end
mathematics.extensiblecode = extensiblecode
mathematics.horizontalcode = horizontalcode
-interfaces.implement {
+interfaces.implement { -- can be public with two times "integerargument"
name = "extensiblecode",
arguments = { "integer", "integer" },
actions = { extensiblecode, context }
}
-interfaces.implement {
+interfaces.implement { -- can be public with two times "integerargument"
name = "horizontalcode",
arguments = { "integer", "integer" },
actions = function(family,unicode)
local kind, loffset, roffset = horizontalcode(family,unicode)
- texsetdimen("scratchleftoffset", loffset)
- texsetdimen("scratchrightoffset",roffset)
+ texsetdimen(d_scratchleftoffset, loffset)
+ texsetdimen(d_scratchrightoffset,roffset)
context(kind)
end
}
-
-local stack = { }
-
-function mathematics.registerfallbackid(n,id,name)
- if trace_collecting then
- report_math("resolved fallback font %i, name %a, id %a, used %a",
- n,name,id,fontproperties[id].fontname)
+function mathematics.variantcode(unicode,variant)
+ local data = fontcharacters[getfontoffamily(texget("fam"))]
+ local char = data and data[unicode]
+ if char then
+ for i=1,variant do
+ local next = char.next
+ if next then
+ unicode = next
+ char = data[next]
+ else
+ break
+ end
+ end
end
- stack[#stack][n] = id
+ return unicode
end
-interfaces.implement { -- will be shared with text
- name = "registerfontfallbackid",
- arguments = { "integer", "integer", "string" },
- actions = mathematics.registerfallbackid,
-}
-
-function mathematics.resolvefallbacks(target,specification,fallbacks)
- local definitions = fonts.collections.definitions[fallbacks]
- if definitions then
- local size = specification.size -- target.size
- local list = { }
- insert(stack,list)
- context.pushcatcodes("prt") -- context.unprotect()
- for i=1,#definitions do
- local definition = definitions[i]
- local name = definition.font
- local features = definition.features or ""
- local size = size * (definition.rscale or 1)
- context.font_fallbacks_register_math(i,name,features,size)
- if trace_collecting then
- report_math("registering fallback font %i, name %a, size %a, features %a",i,name,size,features)
- end
- end
- context.popcatcodes()
- end
-end
-
-function mathematics.finishfallbacks(target,specification,fallbacks)
- local list = remove(stack)
- if list and #list > 0 then
- local definitions = fonts.collections.definitions[fallbacks]
- if definitions and #definitions > 0 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
- end
- if #fonts == 0 then
- fonts[1] = { id = 0, size = size } -- self, 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 = list[i]
- if id then
- local index = #fonts + 1
- fonts[index] = { id = id, size = size }
- local chars = fontchars[id]
- local function remap(unic,unicode,gap)
- if check and not chars[unicode] then
- return
- end
- if force or (not done[unic] and not characters[unic]) then
- if trace_collecting then
- report_math("replacing math character %C by %C using vector %a and font id %a for %a%s%s",
- unic,unicode,fallbacks,id,fontproperties[id].fontname,check and ", checked",gap and ", gap plugged")
- end
- characters[unic] = copiedglyph(target,characters,chars,unicode,index)
- done[unic] = true
- end
- end
- local step = offset - start
- for unicode = start, stop do
- remap(unicode + step,unicode,false)
- end
- if gaps then
- for unic, unicode in next, gaps do
- remap(unic,unicode,true)
- remap(unicode,unicode,true)
- end
- end
- end
+function mathematics.variantcount(unicode)
+ local data = fontcharacters[getfontoffamily(texget("fam"))]
+ local char = data and data[unicode]
+ local count = 0
+ if char then
+ while true do
+ local next = char.next
+ if next then
+ count = count + 1
+ char = data[next]
+ else
+ break
end
- elseif trace_collecting then
- report_math("no fallback characters added to font %a",specification.hash)
end
end
+ return count
end
+
+interfaces.implement {
+ name = "mathvariantcode",
+ public = true,
+ arguments = { "integer", "integer" },
+ actions = { mathematics.variantcode, context },
+}
+
+interfaces.implement {
+ name = "mathvariantcount",
+ public = true,
+ arguments = "integer",
+ actions = { mathematics.variantcount, context },
+}