Skip to content

Commit 803d699

Browse files
Omikhleiaalerque
authored andcommitted
fix(math): Add missing MathML mathvariant mappings
1 parent b5127d6 commit 803d699

4 files changed

Lines changed: 372 additions & 90 deletions

File tree

packages/math/base-elements.lua

Lines changed: 3 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ local nodefactory = require("types.node")
22
local hb = require("justenoughharfbuzz")
33
local ot = require("core.opentype-parser")
44
local syms = require("packages.math.unicode-symbols")
5+
local mathvariants = require("packages.math.unicode-mathvariants")
6+
local convertMathVariantScript = mathvariants.convertMathVariantScript
57

68
local atomType = syms.atomType
79
local symbolDefaults = syms.symbolDefaults
@@ -19,32 +21,6 @@ local mathMode = {
1921
scriptScriptCramped = 7,
2022
}
2123

22-
local scriptType = {
23-
upright = 1,
24-
bold = 2, -- also have Greek and digits
25-
italic = 3, -- also have Greek
26-
boldItalic = 4, -- also have Greek
27-
script = 5,
28-
boldScript = 6,
29-
fraktur = 7,
30-
boldFraktur = 8,
31-
doubleStruck = 9, -- also have digits
32-
sansSerif = 10, -- also have digits
33-
sansSerifBold = 11, -- also have Greek and digits
34-
sansSerifItalic = 12,
35-
sansSerifBoldItalic = 13, -- also have Greek
36-
monospace = 14, -- also have digits
37-
}
38-
39-
local mathVariantToScriptType = function (attr)
40-
return attr == "normal" and scriptType.upright
41-
or attr == "bold" and scriptType.bold
42-
or attr == "italic" and scriptType.italic
43-
or attr == "bold-italic" and scriptType.boldItalic
44-
or attr == "double-struck" and scriptType.doubleStruck
45-
or SU.error('Invalid value "' .. attr .. '" for option mathvariant')
46-
end
47-
4824
local function isDisplayMode (mode)
4925
return mode <= 1
5026
end
@@ -61,50 +37,6 @@ local function isScriptScriptMode (mode)
6137
return mode == mathMode.scriptScript or mode == mathMode.scriptScriptCramped
6238
end
6339

64-
local mathScriptConversionTable = {
65-
capital = {
66-
[scriptType.upright] = function (codepoint)
67-
return codepoint
68-
end,
69-
[scriptType.bold] = function (codepoint)
70-
return codepoint + 0x1D400 - 0x41
71-
end,
72-
[scriptType.italic] = function (codepoint)
73-
return codepoint + 0x1D434 - 0x41
74-
end,
75-
[scriptType.boldItalic] = function (codepoint)
76-
return codepoint + 0x1D468 - 0x41
77-
end,
78-
[scriptType.doubleStruck] = function (codepoint)
79-
return codepoint == 0x43 and 0x2102
80-
or codepoint == 0x48 and 0x210D
81-
or codepoint == 0x4E and 0x2115
82-
or codepoint == 0x50 and 0x2119
83-
or codepoint == 0x51 and 0x211A
84-
or codepoint == 0x52 and 0x211D
85-
or codepoint == 0x5A and 0x2124
86-
or codepoint + 0x1D538 - 0x41
87-
end,
88-
},
89-
small = {
90-
[scriptType.upright] = function (codepoint)
91-
return codepoint
92-
end,
93-
[scriptType.bold] = function (codepoint)
94-
return codepoint + 0x1D41A - 0x61
95-
end,
96-
[scriptType.italic] = function (codepoint)
97-
return codepoint == 0x68 and 0x210E or codepoint + 0x1D44E - 0x61
98-
end,
99-
[scriptType.boldItalic] = function (codepoint)
100-
return codepoint + 0x1D482 - 0x61
101-
end,
102-
[scriptType.doubleStruck] = function (codepoint)
103-
return codepoint + 0x1D552 - 0x61
104-
end,
105-
},
106-
}
107-
10840
local mathCache = {}
10941

11042
local function retrieveMathTable (font)
@@ -952,16 +884,7 @@ function elements.text:_init (kind, attributes, script, text)
952884
self.script = script
953885
self.text = text
954886
if self.script ~= "upright" then
955-
local converted = ""
956-
for _, uchr in luautf8.codes(self.text) do
957-
local dst_char = luautf8.char(uchr)
958-
if uchr >= 0x41 and uchr <= 0x5A then -- Latin capital letter
959-
dst_char = luautf8.char(mathScriptConversionTable.capital[self.script](uchr))
960-
elseif uchr >= 0x61 and uchr <= 0x7A then -- Latin non-capital letter
961-
dst_char = luautf8.char(mathScriptConversionTable.small[self.script](uchr))
962-
end
963-
converted = converted .. dst_char
964-
end
887+
local converted = convertMathVariantScript(self.text, self.script)
965888
self.originalText = self.text
966889
self.text = converted
967890
end
@@ -1521,8 +1444,6 @@ end
15211444

15221445
elements.mathMode = mathMode
15231446
elements.atomType = atomType
1524-
elements.scriptType = scriptType
1525-
elements.mathVariantToScriptType = mathVariantToScriptType
15261447
elements.symbolDefaults = symbolDefaults
15271448
elements.newSubscript = newSubscript
15281449
elements.newUnderOver = newUnderOver

packages/math/texlike.lua

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ local function printMathML (tree)
444444
if tree.options then
445445
local options = {}
446446
for k, v in pairs(tree.options) do
447-
table.insert(options, k .. "=" .. v)
447+
table.insert(options, k .. "=" .. tostring(v))
448448
end
449449
if #options > 0 then
450450
result = result .. "[" .. table.concat(options, ", ") .. "]"
@@ -530,6 +530,21 @@ compileToMathML(
530530
\def{quad}{\mspace[width=1em]}
531531
\def{qquad}{\mspace[width=2em]}
532532
533+
% MathML says a single-character identifier must be in italic by default.
534+
% TeX however has the following Greek capital macros rendered in upright shape.
535+
% It so common that you've probably never seen Γ(x) written with an italic gamma.
536+
\def{Gamma}{\mi[mathvariant=normal]{Γ}}
537+
\def{Delta}{\mi[mathvariant=normal]{Δ}}
538+
\def{Theta}{\mi[mathvariant=normal]{Θ}}
539+
\def{Lambda}{\mi[mathvariant=normal]{Λ}}
540+
\def{Xi}{\mi[mathvariant=normal]{Ξ}}
541+
\def{Pi}{\mi[mathvariant=normal]{Π}}
542+
\def{Sigma}{\mi[mathvariant=normal]{Σ}}
543+
\def{Upsilon}{\mi[mathvariant=normal]{Υ}}
544+
\def{Phi}{\mi[mathvariant=normal]{Φ}}
545+
\def{Psi}{\mi[mathvariant=normal]{Ψ}}
546+
\def{Omega}{\mi[mathvariant=normal]{Ω}}
547+
533548
% Modulus operator forms
534549
\def{bmod}{\mo{mod}}
535550
\def{pmod}{\quad(\mo{mod} #1)}

packages/math/typesetter.lua

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
-- Interpret a MathML or TeX-like AST, typeset it and add it to the output.
22
local b = require("packages.math.base-elements")
33
local syms = require("packages.math.unicode-symbols")
4+
local mathvariants = require("packages.math.unicode-mathvariants")
5+
local mathVariantToScriptType, scriptType = mathvariants.mathVariantToScriptType, mathvariants.scriptType
46

57
-- Shorthands for atom types, used in the `atom` command option
68
local atomTypeShort = {
@@ -47,16 +49,16 @@ function ConvertMathML (_, content)
4749
local special = content.options.special
4850
return b.phantom(convertChildren(content), special)
4951
elseif content.command == "mi" then
50-
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
52+
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
5153
local text = content[1]
5254
if type(text) ~= "string" then
5355
SU.error("mi command contains content which is not text")
5456
end
55-
script = script or (luautf8.len(text) == 1 and b.scriptType.italic or b.scriptType.upright)
57+
script = script or (luautf8.len(text) == 1 and scriptType.italic or scriptType.upright)
5658
return b.text("identifier", {}, script, text)
5759
elseif content.command == "mo" then
58-
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
59-
or b.scriptType.upright
60+
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
61+
or scriptType.upright
6062
local text = content[1]
6163
local attributes = {}
6264
if syms.symbolDefaults[text] then
@@ -76,8 +78,8 @@ function ConvertMathML (_, content)
7678
end
7779
return b.text("operator", attributes, script, text)
7880
elseif content.command == "mn" then
79-
local script = content.options.mathvariant and b.mathVariantToScriptType(content.options.mathvariant)
80-
or b.scriptType.upright
81+
local script = content.options.mathvariant and mathVariantToScriptType(content.options.mathvariant)
82+
or scriptType.upright
8183
local text = content[1]
8284
if type(text) ~= "string" then
8385
SU.error("mn command contains content which is not text")
@@ -156,7 +158,7 @@ function ConvertMathML (_, content)
156158
-- and soft wrap opportunities: ignored here.
157159
-- There's also some explanations about CSS, italic correction etc. which we ignore too.
158160
text = text:gsub("[\n\r]", " ")
159-
return b.text("string", {}, b.scriptType.upright, text:gsub("%s+", " "))
161+
return b.text("string", {}, scriptType.upright, text:gsub("%s+", " "))
160162
else
161163
SU.error("Unknown math command " .. content.command)
162164
end

0 commit comments

Comments
 (0)