Skip to content

Commit

Permalink
fix(math): Fix caching of getMathMetrics
Browse files Browse the repository at this point in the history
getMathMetrics’ cache is now per-font, using the key function defined in
SILE.fonts. This fixes the rendering of formulas in documents that have
different math font sizes. The effect is particularly noticeable in the
position of subscripts and superscripts.

getMathMetrics is moved to a member function of the mbox class to
avoid duplicating the logic that computes the `options` table. The
`options` table is renamed to `font` to avoid a clash with a different
table that has the same name in `elements.table`.
  • Loading branch information
matteodelabre committed Aug 3, 2022
1 parent e07e450 commit 3332698
Show file tree
Hide file tree
Showing 8 changed files with 981 additions and 984 deletions.
125 changes: 61 additions & 64 deletions packages/math/base-elements.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,49 90,41 @@ local mathScriptConversionTable = {
}
}

local function retrieveMathTable(options)
SU.debug("math", "Loading math font: " .. options.family)
local face = SILE.font.cache(options, SILE.shaper.getFace)
if not face then
SU.error("Could not find requested font "..options.." or any suitable substitutes")
end
local mathTable = ot.parseMath(hb.get_table(face.data, face.index, "MATH"))
local upem = ot.parseHead(hb.get_table(face.data, face.index, "head")).unitsPerEm
if mathTable == nil then
SU.error("You must use a math font for math rendering.")
end
local constants = {}
for k,v in pairs(mathTable.mathConstants) do
if type(v) == "table" then v = v.value end
if k:sub(-9) == "ScaleDown" then constants[k] = v / 100
else
constants[k] = v * options.size / upem
local mathCache = {}

local function retrieveMathTable(font)
local key = SILE.font._key(font)
if not mathCache[key] then
SU.debug("math", "Loading math font " .. key)
local face = SILE.font.cache(font, SILE.shaper.getFace)
if not face then
SU.error("Could not find requested font ".. font .." or any suitable substitutes")
end
local mathTable = ot.parseMath(hb.get_table(face.data, face.index, "MATH"))
local upem = ot.parseHead(hb.get_table(face.data, face.index, "head")).unitsPerEm
if mathTable == nil then
SU.error("You must use a math font for math rendering.")
end
local constants = {}
for k,v in pairs(mathTable.mathConstants) do
if type(v) == "table" then v = v.value end
if k:sub(-9) == "ScaleDown" then constants[k] = v / 100
else
constants[k] = v * font.size / upem
end
end
local italicsCorrection = {}
for k, v in pairs(mathTable.mathItalicsCorrection) do
italicsCorrection[k] = v.value * font.size / upem
end
mathCache[key] = {
constants = constants,
italicsCorrection = italicsCorrection,
mathVariants = mathTable.mathVariants,
unitsPerEm = upem
}
end
local italicsCorrection = {}
for k, v in pairs(mathTable.mathItalicsCorrection) do
italicsCorrection[k] = v.value * options.size / upem
end
return {
constants = constants,
italicsCorrection = italicsCorrection,
mathVariants = mathTable.mathVariants,
unitsPerEm = upem
}
end

local mathCache

local function getMathMetrics()
if mathCache then return mathCache end
local options = {
family=SILE.settings:get("math.font.family"),
size=SILE.settings:get("math.font.size")
}
local filename = SILE.settings:get("math.font.filename")
if filename and filename ~= "" then options.filename = filename end
mathCache = retrieveMathTable(options)
return mathCache
return mathCache[key]
end

-- Style transition functions for superscript and subscript
Expand Down Expand Up @@ -239,20 231,20 @@ elements.mbox = pl.class({
_type = "Mbox",
__tostring = function (s) return s._type end,
_init = function(self)
self.options = {}
self.font = {}
self.children = {} -- The child nodes
self.relX = SILE.length(0) -- x position relative to its parent box
self.relY = SILE.length(0) -- y position relative to its parent box
self.value = {}
self.mode = mathMode.display
self.atom = atomType.ordinary
local options = {
local font = {
family=SILE.settings:get("math.font.family"),
size=SILE.settings:get("math.font.size")
}
local filename = SILE.settings:get("math.font.filename")
if filename and filename ~= "" then options.filename = filename end
self.options = SILE.font.loadDefaults(options)
if filename and filename ~= "" then font.filename = filename end
self.font = SILE.font.loadDefaults(font)
end,

styleChildren = function(_)
Expand All @@ -267,8 259,12 @@ elements.mbox = pl.class({
SU.error("output is a virtual function that need to be overriden by its child classes")
end,

getMathMetrics = function(self)
return retrieveMathTable(self.font)
end,

getScaleDown = function(self)
local constants = getMathMetrics().constants
local constants = self:getMathMetrics().constants
local scaleDown
if isScriptMode(self.mode) then
scaleDown = constants.scriptPercentScaleDown
Expand Down Expand Up @@ -516,15 512,15 @@ elements.subscript = pl.class({
calculateItalicsCorrection = function(self)
local lastGid = getRightMostGlyphId(self.base)
if lastGid > 0 then
local mathMetrics = getMathMetrics()
local mathMetrics = self:getMathMetrics()
if mathMetrics.italicsCorrection[lastGid] then
return mathMetrics.italicsCorrection[lastGid]
end
end
return 0
end,
shape = function(self)
local mathMetrics = getMathMetrics()
local mathMetrics = self:getMathMetrics()
local constants = mathMetrics.constants
local scaleDown = self:getScaleDown()
if self.base then
Expand Down Expand Up @@ -647,7 643,7 @@ elements.underOver = pl.class({
elements.subscript.shape(self)
return
end
local constants = getMathMetrics().constants
local constants = self:getMathMetrics().constants
local scaleDown = self:getScaleDown()
-- Determine relative Ys
if self.base then
Expand Down Expand Up @@ -721,14 717,14 @@ elements.underOver = pl.class({
calculateItalicsCorrection = function(self)
local lastGid = getRightMostGlyphId(self.base)
if lastGid > 0 then
local mathMetrics = getMathMetrics()
local mathMetrics = self:getMathMetrics()
if mathMetrics.italicsCorrection[lastGid] then
local c = mathMetrics.italicsCorrection[lastGid]
-- If this is a big operator, and we are in display style, then the
-- base glyph may be bigger than the font size. We need to adjust the
-- italic correction accordingly.
if self.base.atom == atomType.bigOperator and isDisplayMode(self.mode) then
c = c * (self.base and self.base.options.size / self.options.size or 1.0)
c = c * (self.base and self.base.font.size / self.font.size or 1.0)
end
return c
end
Expand Down Expand Up @@ -830,10 826,10 @@ elements.text = pl.class({
end,

shape = function(self)
self.options.size = self.options.size * self:getScaleDown()
local face = SILE.font.cache(self.options, SILE.shaper.getFace)
local mathMetrics = getMathMetrics()
local glyphs = SILE.shaper:shapeToken(self.text, self.options)
self.font.size = self.font.size * self:getScaleDown()
local face = SILE.font.cache(self.font, SILE.shaper.getFace)
local mathMetrics = self:getMathMetrics()
local glyphs = SILE.shaper:shapeToken(self.text, self.font)
-- Use bigger variants for big operators in display style
if isDisplayMode(self.mode) and self.largeop then
-- We copy the glyph list to avoid modifying the shaper's cache. Yes.
Expand All @@ -855,7 851,7 @@ elements.text = pl.class({
if biggest then
glyphs[1].gid = biggest.variantGlyph
local dimen = hb.get_glyph_dimensions(face.data,
face.index, self.options.size, biggest.variantGlyph)
face.index, self.font.size, biggest.variantGlyph)
glyphs[1].width = dimen.width
glyphs[1].glyphAdvance = dimen.glyphAdvance
--[[ I am told (https://github.com/alif-type/xits/issues/90) that,
Expand Down Expand Up @@ -906,9 902,9 @@ elements.text = pl.class({

stretchyReshape = function(self, depth, height)
-- Required depth height of stretched glyph, in font units
local mathMetrics = getMathMetrics()
local mathMetrics = self:getMathMetrics()
local upem = mathMetrics.unitsPerEm
local sz = self.options.size
local sz = self.font.size
local requiredAdvance = (depth height):tonumber() * upem/sz
SU.debug("math", "stretch: rA = "..requiredAdvance)
-- Choose variant of the closest size. The criterion we use is to have
Expand All @@ -919,7 915,7 @@ elements.text = pl.class({
-- their parts for cases when the biggest variant is not big enough.
-- We copy the glyph list to avoid modifying the shaper's cache. Yes.
local glyphs = pl.tablex.deepcopy(self.value.items)
local constructions = getMathMetrics().mathVariants
local constructions = self:getMathMetrics().mathVariants
.vertGlyphConstructions[glyphs[1].gid]
if constructions then
local variants = constructions.mathGlyphVariantRecord
Expand All @@ -945,9 941,9 @@ elements.text = pl.class({
-- variants have a different width than the original, because
-- the shaping phase is already done. Need to do better.
glyphs[1].gid = closest.variantGlyph
local face = SILE.font.cache(self.options, SILE.shaper.getFace)
local face = SILE.font.cache(self.font, SILE.shaper.getFace)
local dimen = hb.get_glyph_dimensions(face.data,
face.index, self.options.size, closest.variantGlyph)
face.index, self.font.size, closest.variantGlyph)
glyphs[1].width = dimen.width
glyphs[1].height = dimen.height
glyphs[1].depth = dimen.depth
Expand All @@ -973,7 969,7 @@ elements.text = pl.class({
compensatedY = y
end
SILE.outputter:setCursor(scaleWidth(x, line), compensatedY.length)
SILE.outputter:setFont(self.options)
SILE.outputter:setFont(self.font)
-- There should be no stretch or shrink on the width of a text
-- element.
local width = self.width.length
Expand Down Expand Up @@ -1012,7 1008,7 @@ elements.fraction = pl.class({
self.width = widest.width

-- Determine relative ordinates and height
local constants = getMathMetrics().constants
local constants = self:getMathMetrics().constants
local scaleDown = self:getScaleDown()
self.axisHeight = constants.axisHeight * scaleDown
self.ruleThickness = constants.fractionRuleThickness * scaleDown
Expand Down Expand Up @@ -1089,6 1085,7 @@ elements.table = pl.class({
_type = "table",

_init = function(self, children, options)
elements.mbox._init(self)
self.children = children
self.options = options
self.nrows = #self.children
Expand Down Expand Up @@ -1195,7 1192,7 @@ elements.table = pl.class({
self.width = thisColRelX

-- Center myself vertically around the axis, and update relative Ys of rows accordingly
local axisHeight = getMathMetrics().constants.axisHeight * self:getScaleDown()
local axisHeight = self:getMathMetrics().constants.axisHeight * self:getScaleDown()
self.height = self.vertSize / 2 axisHeight
self.depth = self.vertSize / 2 - axisHeight
for _,row in ipairs(self.children) do
Expand Down
Loading

0 comments on commit 3332698

Please sign in to comment.