|
1 | 1 | -- Interpret a MathML or TeX-like AST, typeset it and add it to the output. |
| 2 | +local lpeg = require("lpeg") |
2 | 3 | local atoms = require("packages.math.atoms") |
3 | 4 | local b = require("packages.math.base-elements") |
4 | 5 | local syms = require("packages.math.unicode-symbols") |
@@ -158,6 +159,42 @@ function ConvertMathML (_, content) |
158 | 159 | if type(text) ~= "string" then |
159 | 160 | SU.error("mo command contains content which is not text") |
160 | 161 | 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 |
161 | 198 | return b.text("operator", attributes, script, text) |
162 | 199 | elseif content.command == "mn" then |
163 | 200 | local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant) |
|
0 commit comments