Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Math more fixes and features #2151

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9ce5271
fix(math): Default table row/column spacing must depend on the math f…
Omikhleia Oct 25, 2024
0df93b1
feat(math): Support MathML maction (basic) and mstyle (partial)
Omikhleia Oct 25, 2024
4bbeaa6
fix(math): Paired open/close atoms shall be forced in a mrow in TeX-l…
Omikhleia Oct 26, 2024
f4c513a
fix(math): Handle LaTeX-like math top accent commands correctly
Omikhleia Oct 26, 2024
59f4bfd
feat(core): Add px unit as 0.75pt as per CSS3
Omikhleia Oct 26, 2024
e0559e8
fix(math): Support linethickness attribute on MathML mfrac
Omikhleia Oct 26, 2024
d579096
feat(math): Support MathML mpadded and refactor TeX-like phantoms
Omikhleia Oct 29, 2024
19906b9
fix(math): Wrong condition on moveable limits (workaround)
Omikhleia Oct 29, 2024
7c58886
refactor(math): Prepare for horizontal reshaping of stretchy symbols
Omikhleia Oct 30, 2024
bde46cd
fix(math): Honor stretchy attribute on MathML mo elements
Omikhleia Oct 30, 2024
589c058
fix(math): Stretchy symbols should work on large content (workaround)
Omikhleia Oct 31, 2024
a42181f
fix(math): Wrong vertical alignment of integral signs
Omikhleia Oct 31, 2024
b6e054e
fix(math): Support basic operator stretching along the inline axis
Omikhleia Nov 1, 2024
6f67be5
fix(math): Empty mrow's in munderover should not affect the baseline
Omikhleia Nov 1, 2024
6827eb9
fix(math): Correct sub/superscript position for subformulas vs. symbols
Omikhleia Nov 1, 2024
1f1a1a0
fix(math): Top-level math element must behave as an (horizontally sta…
Omikhleia Nov 2, 2024
ef249c3
test(math): Upgrade math test expectations
Omikhleia Nov 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
328 changes: 242 additions & 86 deletions packages/math/base-elements.lua

Large diffs are not rendered by default.

111 changes: 101 additions & 10 deletions packages/math/texlike.lua
Original file line number Diff line number Diff line change
Expand Up @@ -260,23 +260,40 @@ local compileToStr = function (argEnv, mathlist)
end
end

local function isBigOperator (tree)
local function isOperatorKind (tree, typeOfAtom, typeOfSymbol)
if not tree then
return false -- safeguard
end
if tree.command ~= "mo" then
return false
end
-- Case \mo[atom=big]{ops}
-- E.g. \mo[atom=big]{lim}
if tree.options and tree.options.atom == "big" then
if tree.options and tree.options.atom == typeOfAtom then
return true
end
-- Case \mo{ops} where ops is registered as big operator (unicode-symbols)
-- Case \mo{ops} where ops is registered with the resquested type
-- E.g. \mo{∑) or \sum
if tree[1] and symbolDefaults[tree[1]] and symbolDefaults[tree[1]].atom == atomType.bigOperator then
if tree[1] and symbolDefaults[tree[1]] and symbolDefaults[tree[1]].atom == typeOfSymbol then
return true
end
return false
end

local function isBigOperator (tree)
return isOperatorKind(tree, "big", atomType.bigOperator)
end
local function isCloseOperator (tree)
return isOperatorKind(tree, "close", atomType.closeSymbol)
end
local function isOpeningOperator (tree)
return isOperatorKind(tree, "open", atomType.openingSymbol)
end

local function isAccentSymbol (symbol)
return symbolDefaults[symbol] and symbolDefaults[symbol].atom == atomType.accentSymbol
end

local function compileToMathML_aux (_, arg_env, tree)
if type(tree) == "string" then
return tree
Expand Down Expand Up @@ -323,12 +340,56 @@ local function compileToMathML_aux (_, arg_env, tree)
-- Turn mathlist into `mrow` except if it has exactly one `mtr` or `mtd`
-- child.
-- Note that `def`s have already been compiled away at this point.
if #tree == 1 and (tree[1].command == "mtr" or tree[1].command == "mtd") then
return tree[1]
if #tree == 1 then
if tree[1].command == "mtr" or tree[1].command == "mtd" then
return tree[1]
else
tree.command = "mrow"
end
else
-- Re-wrap content from opening to closing operator in an implicit mrow,
-- so stretchy operators apply to the correct span of content.
local children = {}
local stack = {}
for _, child in ipairs(tree) do
if isOpeningOperator(child) then
table.insert(stack, children)
local mrow = {
command = "mrow",
options = {},
child,
}
table.insert(children, mrow)
children = mrow
elseif isCloseOperator(child) then
table.insert(children, child)
if #stack > 0 then
children = table.remove(stack)
end
elseif
(child.command == "msubsup" or child.command == "msub" or child.command == "msup")
and isCloseOperator(child[1]) -- child[1] is the base
then
if #stack > 0 then
-- Special case for closing operator with sub/superscript:
-- (....)^i must be interpreted as {(....)}^i, not as (...{)}^i
-- Push the closing operator into the mrow
table.insert(children, child[1])
-- Move the mrow into the msubsup, replacing the closing operator
child[1] = children
-- And insert the msubsup into the parent
children = table.remove(stack)
children[#children] = child
else
table.insert(children, child)
end
else
table.insert(children, child)
end
end
tree = #stack > 0 and stack[1] or children
tree.command = "mrow"
end
tree.command = "mrow"
elseif tree.id == "atom" then
local codepoints = {}
for _, cp in luautf8.codes(tree[1]) do
Expand Down Expand Up @@ -424,7 +485,36 @@ local function compileToMathML_aux (_, arg_env, tree)
return res
elseif tree.id == "command" and symbols[tree.command] then
local atom = { id = "atom", [1] = symbols[tree.command] }
tree = compileToMathML_aux(nil, arg_env, atom)
if isAccentSymbol(symbols[tree.command]) and #tree > 0 then
-- LaTeX-style accents \vec{v} = <mover accent="true"><mi>v</mi><mo>→</mo></mover>
local accent = {
id = "command",
command = "mover",
options = {
accent = "true",
},
}
accent[1] = compileToMathML_aux(nil, arg_env, tree[1])
accent[2] = compileToMathML_aux(nil, arg_env, atom)
tree = accent
elseif #tree > 0 then
-- Play cool with LaTeX-style commands that don't take arguments:
-- Edge case for non-accent symbols so we don't loose bracketed groups
-- that might have been seen as command arguments.
-- Ex. \langle{x}\rangle (without space after \langle)
local sym = compileToMathML_aux(nil, arg_env, atom)
-- Compile all children in-place
for i, child in ipairs(tree) do
tree[i] = compileToMathML_aux(nil, arg_env, child)
end
-- Insert symbol at the beginning,
-- And add a wrapper mrow to be unwrapped in the parent.
table.insert(tree, 1, sym)
tree.command = "mrow"
tree.id = "wrapper"
else
tree = compileToMathML_aux(nil, arg_env, atom)
end
elseif tree.id == "argument" then
if arg_env[tree.index] then
return arg_env[tree.index]
Expand Down Expand Up @@ -566,8 +656,9 @@ compileToMathML(

% Phantom commands from TeX/LaTeX
\def{phantom}{\mphantom{#1}}
\def{hphantom}{\mphantom[special=h]{#1}}
\def{vphantom}{\mphantom[special=v]{#1}}
\def{hphantom}{\mpadded[height=0, depth=0]{\mphantom{#1}}}
\def{vphantom}{\mpadded[width=0]{\mphantom{#1}}}
%\mphantom[special=v]{#1}}}
]==],
})
)
Expand Down
31 changes: 29 additions & 2 deletions packages/math/typesetter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,24 @@ local function convertChildren (tree)
return mboxes
end

local function convertFirstChild (tree)
-- We need to loop until the first non-nil box is found, because
-- we may have blank lines in the tree.
for _, n in ipairs(tree) do
local box = ConvertMathML(nil, n)
if box then
return box
end
end
end

-- convert MathML into mbox
function ConvertMathML (_, content)
if content == nil or content.command == nil then
return nil
end
if content.command == "math" or content.command == "mathml" then -- toplevel
return b.stackbox("V", convertChildren(content))
return b.stackbox("H", convertChildren(content))
elseif content.command == "mrow" then
return b.stackbox("H", convertChildren(content))
elseif content.command == "mphantom" then
Expand All @@ -61,11 +72,16 @@ function ConvertMathML (_, content)
or scriptType.upright
local text = content[1]
local attributes = {}
-- Attributes from the (default) oerator table
if syms.symbolDefaults[text] then
for attribute, value in pairs(syms.symbolDefaults[text]) do
attributes[attribute] = value
end
end
-- Overwrite with attributes from the element
for attribute, value in pairs(content.options) do
attributes[attribute] = value
end
if content.options.atom then
if not atomTypeShort[content.options.atom] then
SU.error("Unknown atom type " .. content.options.atom)
Expand Down Expand Up @@ -131,7 +147,7 @@ function ConvertMathML (_, content)
if #children ~= 2 then
SU.error("Wrong number of children in mfrac: " .. #children)
end
return b.fraction(children[1], children[2])
return b.fraction(content.options, children[1], children[2])
elseif content.command == "msqrt" then
local children = convertChildren(content)
-- "The <msqrt> element generates an anonymous <mrow> box called the msqrt base
Expand Down Expand Up @@ -159,6 +175,17 @@ function ConvertMathML (_, content)
-- There's also some explanations about CSS, italic correction etc. which we ignore too.
text = text:gsub("[\n\r]", " ")
return b.text("string", {}, scriptType.upright, text:gsub("%s+", " "))
elseif content.command == "maction" then
-- MathML Core 3.6: display as mrow, ignoring all but the first child
return b.stackbox("H", { convertFirstChild(content) })
elseif content.command == "mstyle" then
-- It's an mrow, but with some style attributes that we ignore.
SU.warn("MathML mstyle is not fully supported yet")
return b.stackbox("H", convertChildren(content))
elseif content.command == "mpadded" then
-- MathML Core 3.3.6.1: The <mpadded> element generates an anonymous <mrow> box
-- called the "impadded inner box"
return b.padded(content.options, b.stackbox("H", convertChildren(content)))
else
SU.error("Unknown math command " .. content.command)
end
Expand Down
56 changes: 25 additions & 31 deletions tests/bug-1495-inline-math-layout.expected
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,11 @@ T 30 w=6.4800 (nil)
Mx 217.4262
T 68 80 84 w=13.2200 (nil)
Mx 230.6462
Mx 2.8200
T 9 a=3.5600 (nil)
T 9 w=3.5600 (nil)
Mx 234.2062
T 3421 w=5.9100 (nil)
Mx 240.1162
Mx 2.8200
T 10 a=3.5600 (nil)
T 10 w=3.5600 (nil)
Mx 247.3974
T 30 w=6.4800 (nil)
Mx 257.5986
Expand All @@ -88,53 +86,49 @@ My 52.6701
T 87 68 72 w=12.6514 (tae)
Mx 27.5333
T 17 w=2.2900 (.)
Mx 32.4913
Mx 32.7068
T 48 68 72 70 72 81 68 86 w=40.2295 (Maecenas)
Mx 75.3888
Mx 75.8198
T 89 72 81 72 81 68 87 76 86 w=39.8096 (venenatis)
Mx 117.8663
Mx 118.5129
T 88 79 87 85 76 70 72 86 w=30.9717 (ultrices)
Mx 148.8380
Mx 149.4846
T 17 w=2.2900 (.)
Mx 153.7961
Mx 154.6581
T 51 75 68 86 72 79 79 88 86 w=38.3984 (Phasellus)
Mx 194.8625
Mx 195.9401
Set font Libertinus Math;10;400;Regular;normal;;;LTR
T 2750 w=3.8100 (nil)
Mx 198.6725
Mx 199.7501
My 49.5701
Set font Libertinus Math;8;400;Regular;normal;;;LTR
T 2753 w=1.8320 (nil)
Mx 200.5045
Mx 201.5821
T 3413 w=3.5152 (nil)
Mx 207.2916
Mx 208.9129
My 52.6701
Set font Libertinus Math;10;400;Regular;normal;;;LTR
T 30 w=6.4800 (nil)
Mx 216.6135
Mx 218.7785
T 68 80 84 w=13.2200 (nil)
Mx 229.8335
Mx 3.0000
T 3798 a=4.2100 (nil)
Mx 234.0435
Mx 231.9985
T 9 w=3.5600 (nil)
Mx 235.5585
T 3413 w=4.5600 (nil)
Mx 238.6035
Mx 2.9900
T 3799 a=4.2100 (nil)
Mx 245.0614
Mx 240.1185
T 10 w=3.5600 (nil)
Mx 246.1439
T 12 w=6.4800 (nil)
Mx 253.7892
Mx 255.0892
T 2753 w=2.2900 (nil)
Mx 257.7459
Mx 259.0459
T 84 74 79 w=12.0300 (nil)
Mx 269.7759
Mx 3.0000
T 3798 a=4.2100 (nil)
Mx 273.9859
Mx 271.0759
T 9 w=3.5600 (nil)
Mx 274.6359
T 3413 w=4.5600 (nil)
Mx 278.5459
Mx 2.9900
T 3799 a=4.2100 (nil)
Mx 279.1959
T 10 w=3.5600 (nil)
Mx 14.8819
My 64.6701
Set font Gentium Plus;10;400;;normal;;;LTR
Expand Down
Loading
Loading