Skip to content

Commit

Permalink
Merge pull request #2147 from Omikhleia/fix-math-mathvariants
Browse files Browse the repository at this point in the history
Fix math mathvariants
  • Loading branch information
alerque authored Oct 30, 2024
2 parents b5127d6 + 840aa7f commit 5dffc31
Show file tree
Hide file tree
Showing 12 changed files with 954 additions and 653 deletions.
85 changes: 3 additions & 82 deletions packages/math/base-elements.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ local nodefactory = require("types.node")
local hb = require("justenoughharfbuzz")
local ot = require("core.opentype-parser")
local syms = require("packages.math.unicode-symbols")
local mathvariants = require("packages.math.unicode-mathvariants")
local convertMathVariantScript = mathvariants.convertMathVariantScript

local atomType = syms.atomType
local symbolDefaults = syms.symbolDefaults
Expand All @@ -19,32 +21,6 @@ local mathMode = {
scriptScriptCramped = 7,
}

local scriptType = {
upright = 1,
bold = 2, -- also have Greek and digits
italic = 3, -- also have Greek
boldItalic = 4, -- also have Greek
script = 5,
boldScript = 6,
fraktur = 7,
boldFraktur = 8,
doubleStruck = 9, -- also have digits
sansSerif = 10, -- also have digits
sansSerifBold = 11, -- also have Greek and digits
sansSerifItalic = 12,
sansSerifBoldItalic = 13, -- also have Greek
monospace = 14, -- also have digits
}

local mathVariantToScriptType = function (attr)
return attr == "normal" and scriptType.upright
or attr == "bold" and scriptType.bold
or attr == "italic" and scriptType.italic
or attr == "bold-italic" and scriptType.boldItalic
or attr == "double-struck" and scriptType.doubleStruck
or SU.error('Invalid value "' .. attr .. '" for option mathvariant')
end

local function isDisplayMode (mode)
return mode <= 1
end
Expand All @@ -61,50 +37,6 @@ local function isScriptScriptMode (mode)
return mode == mathMode.scriptScript or mode == mathMode.scriptScriptCramped
end

local mathScriptConversionTable = {
capital = {
[scriptType.upright] = function (codepoint)
return codepoint
end,
[scriptType.bold] = function (codepoint)
return codepoint + 0x1D400 - 0x41
end,
[scriptType.italic] = function (codepoint)
return codepoint + 0x1D434 - 0x41
end,
[scriptType.boldItalic] = function (codepoint)
return codepoint + 0x1D468 - 0x41
end,
[scriptType.doubleStruck] = function (codepoint)
return codepoint == 0x43 and 0x2102
or codepoint == 0x48 and 0x210D
or codepoint == 0x4E and 0x2115
or codepoint == 0x50 and 0x2119
or codepoint == 0x51 and 0x211A
or codepoint == 0x52 and 0x211D
or codepoint == 0x5A and 0x2124
or codepoint + 0x1D538 - 0x41
end,
},
small = {
[scriptType.upright] = function (codepoint)
return codepoint
end,
[scriptType.bold] = function (codepoint)
return codepoint + 0x1D41A - 0x61
end,
[scriptType.italic] = function (codepoint)
return codepoint == 0x68 and 0x210E or codepoint + 0x1D44E - 0x61
end,
[scriptType.boldItalic] = function (codepoint)
return codepoint + 0x1D482 - 0x61
end,
[scriptType.doubleStruck] = function (codepoint)
return codepoint + 0x1D552 - 0x61
end,
},
}

local mathCache = {}

local function retrieveMathTable (font)
Expand Down Expand Up @@ -952,16 +884,7 @@ function elements.text:_init (kind, attributes, script, text)
self.script = script
self.text = text
if self.script ~= "upright" then
local converted = ""
for _, uchr in luautf8.codes(self.text) do
local dst_char = luautf8.char(uchr)
if uchr >= 0x41 and uchr <= 0x5A then -- Latin capital letter
dst_char = luautf8.char(mathScriptConversionTable.capital[self.script](uchr))
elseif uchr >= 0x61 and uchr <= 0x7A then -- Latin non-capital letter
dst_char = luautf8.char(mathScriptConversionTable.small[self.script](uchr))
end
converted = converted .. dst_char
end
local converted = convertMathVariantScript(self.text, self.script)
self.originalText = self.text
self.text = converted
end
Expand Down Expand Up @@ -1521,8 +1444,6 @@ end

elements.mathMode = mathMode
elements.atomType = atomType
elements.scriptType = scriptType
elements.mathVariantToScriptType = mathVariantToScriptType
elements.symbolDefaults = symbolDefaults
elements.newSubscript = newSubscript
elements.newUnderOver = newUnderOver
Expand Down
32 changes: 31 additions & 1 deletion packages/math/texlike.lua
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ local function printMathML (tree)
if tree.options then
local options = {}
for k, v in pairs(tree.options) do
table.insert(options, k .. "=" .. v)
table.insert(options, k .. "=" .. tostring(v))
end
if #options > 0 then
result = result .. "[" .. table.concat(options, ", ") .. "]"
Expand Down Expand Up @@ -530,6 +530,36 @@ compileToMathML(
\def{quad}{\mspace[width=1em]}
\def{qquad}{\mspace[width=2em]}
% MathML says a single-character identifier must be in italic by default.
% TeX however has the following Greek capital macros rendered in upright shape.
% It so common that you've probably never seen Γ(x) written with an italic gamma.
\def{Gamma}{\mi[mathvariant=normal]{Γ}}
\def{Delta}{\mi[mathvariant=normal]{Δ}}
\def{Theta}{\mi[mathvariant=normal]{Θ}}
\def{Lambda}{\mi[mathvariant=normal]{Λ}}
\def{Xi}{\mi[mathvariant=normal]{Ξ}}
\def{Pi}{\mi[mathvariant=normal]{Π}}
\def{Sigma}{\mi[mathvariant=normal]{Σ}}
\def{Upsilon}{\mi[mathvariant=normal]{Υ}}
\def{Phi}{\mi[mathvariant=normal]{Φ}}
\def{Psi}{\mi[mathvariant=normal]{Ψ}}
\def{Omega}{\mi[mathvariant=normal]{Ω}}
% Some calligraphic (script), fraktur, double-struck styles:
% Convenience for compatibility with LaTeX.
\def{mathcal}{\mi[mathvariant=script]{#1}}
\def{mathfrak}{\mi[mathvariant=fraktur]{#1}}
\def{mathbb}{\mi[mathvariant=double-struck]{#1}}
% Some style-switching commands for compatibility with LaTeX math.
% Caveat emptor: LaTeX would allow these to apply to a whole formula.
% We can't do that in MathML, as mathvariant applies to token elements only.
% Also note that LaTeX and related packages may have many more such commands.
% We only provide a few common ('historical') ones here.
\def{mathrm}{\mi[mathvariant=normal]{#1}}
\def{mathbf}{\mi[mathvariant=bold]{#1}}
\def{mathit}{\mi[mathvariant=italic]{#1}}
\def{mathsf}{\mi[mathvariant=sans-serif]{#1}}
\def{mathtt}{\mi[mathvariant=monospace]{#1}}
% Modulus operator forms
\def{bmod}{\mo{mod}}
\def{pmod}{\quad(\mo{mod} #1)}
Expand Down
16 changes: 9 additions & 7 deletions packages/math/typesetter.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
-- Interpret a MathML or TeX-like AST, typeset it and add it to the output.
local b = require("packages.math.base-elements")
local syms = require("packages.math.unicode-symbols")
local mathvariants = require("packages.math.unicode-mathvariants")
local mathVariantToScriptType, scriptType = mathvariants.mathVariantToScriptType, mathvariants.scriptType

-- Shorthands for atom types, used in the `atom` command option
local atomTypeShort = {
Expand Down Expand Up @@ -47,16 +49,16 @@ function ConvertMathML (_, content)
local special = content.options.special
return b.phantom(convertChildren(content), special)
elseif content.command == "mi" then
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
local text = content[1]
if type(text) ~= "string" then
SU.error("mi command contains content which is not text")
end
script = script or (luautf8.len(text) == 1 and b.scriptType.italic or b.scriptType.upright)
script = script or (luautf8.len(text) == 1 and scriptType.italic or scriptType.upright)
return b.text("identifier", {}, script, text)
elseif content.command == "mo" then
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
or b.scriptType.upright
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
or scriptType.upright
local text = content[1]
local attributes = {}
if syms.symbolDefaults[text] then
Expand All @@ -76,8 +78,8 @@ function ConvertMathML (_, content)
end
return b.text("operator", attributes, script, text)
elseif content.command == "mn" then
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
or b.scriptType.upright
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
or scriptType.upright
local text = content[1]
if type(text) ~= "string" then
SU.error("mn command contains content which is not text")
Expand Down Expand Up @@ -156,7 +158,7 @@ function ConvertMathML (_, content)
-- and soft wrap opportunities: ignored here.
-- There's also some explanations about CSS, italic correction etc. which we ignore too.
text = text:gsub("[\n\r]", " ")
return b.text("string", {}, b.scriptType.upright, text:gsub("%s+", " "))
return b.text("string", {}, scriptType.upright, text:gsub("%s+", " "))
else
SU.error("Unknown math command " .. content.command)
end
Expand Down
Loading

0 comments on commit 5dffc31

Please sign in to comment.