Skip to content

Commit 72faad5

Browse files
authored
fix(math): Suppress invisible operators in MathML (#2177)
1 parent f0ddaed commit 72faad5

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

packages/math/typesetter.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
-- Interpret a MathML or TeX-like AST, typeset it and add it to the output.
2+
local lpeg = require("lpeg")
23
local atoms = require("packages.math.atoms")
34
local b = require("packages.math.base-elements")
45
local syms = require("packages.math.unicode-symbols")
@@ -158,6 +159,42 @@ function ConvertMathML (_, content)
158159
if type(text) ~= "string" then
159160
SU.error("mo command contains content which is not text")
160161
end
162+
local cp = text and luautf8.len(text) == 1 and luautf8.codepoint(text, 1)
163+
if cp and cp >= 0x2061 and cp <= 0x2064 then
164+
-- "Invisible operators"
165+
-- - Several test cases in Joe Javawaski's Browser Test and the
166+
-- - The MathML Test Suite use these too, with ad hoc spacing attributes.
167+
-- MathML Core doesn't mention anything special about these.
168+
-- MathML4 §8.3: "They are especially important new additions to the UCS
169+
-- because they provide textual clues which can increase the quality of
170+
-- print rendering (...)" (Note the absence of indication on how "print"
171+
-- rendering is supposed to be improved.)
172+
-- MathML4 §3.1.1: "they usually render invisibly (...) but may influence
173+
-- visual spacing." (Note the ill-defined "usually" and "may" in this
174+
-- specification.)
175+
-- The best we can do is to suppress these operators, but handle any
176+
-- explicitspacing (despite not handling rsup/rspace attributes on other
177+
-- operators in our TeX-based spacing logic).
178+
-- stylua: ignore start
179+
local number = lpeg.R("09")^0 * (lpeg.P(".")^-1 * lpeg.R("09")^1)^0 / tonumber
180+
-- stylua: ignore end
181+
-- 0 something is 0 in whatever unit (ex. "0", "0mu", "0em" etc.)
182+
local rspace, lspace = number:match(attributes.rspace), number:match(attributes.lspace)
183+
if rspace == 0 and lspace == 0 then
184+
return nil -- Just skip the invisible operator.
185+
end
186+
-- Skip it but honor the non-zero spacing.
187+
if rspace == 0 then
188+
return b.space(attributes.lspace, 0, 0)
189+
end
190+
if lspace == 0 then
191+
return b.space(attributes.rspace, 0, 0)
192+
end
193+
-- I haven't found examples of invisible operators with both rspace and lspace set,
194+
-- but it may happen, whatever spaces around something invisible mean.
195+
-- We'll just stack the spaces in this case (as we can only return one box).
196+
return b.stackbox("H", { b.space(attributes.lspace, 0, 0), b.space(attributes.rspace, 0, 0) })
197+
end
161198
return b.text("operator", attributes, script, text)
162199
elseif content.command == "mn" then
163200
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)

0 commit comments

Comments
 (0)