diff --git a/src/SharpEquations/Internal/LaTeX/LaTeXReader.cs b/src/SharpEquations/Internal/LaTeX/LaTeXReader.cs index 2561710..adf23e4 100644 --- a/src/SharpEquations/Internal/LaTeX/LaTeXReader.cs +++ b/src/SharpEquations/Internal/LaTeX/LaTeXReader.cs @@ -648,6 +648,14 @@ private static string RewriteTeXOnly(string latex) i = afterUnderBase; continue; } + if (cw is "overbrace" or "underbrace" && Argument(tex, j) is var (braced, afterBraced) && afterBraced >= 0 && + Scripts(tex, afterBraced) is var (braceUnder, braceOver, afterBraceScripts) && braceUnder != null && braceOver != null) + { + // \overbrace3^a_x: for TeX the brace is an operator with limits, both scripts under and over the whole construct + result.Append(RewriteTeXOnly("\\underset{" + braceUnder + "}{\\overset{" + braceOver + "}{\\" + cw + "{" + braced + "}}}")); + i = afterBraceScripts; + continue; + } if (cw == "underset" && Arguments(tex, j, 2) is var (underArguments, afterUnderset) && new Text(underArguments[1].Trim()) is var overText && overText.Find("\\overset", 0) == 0 && Arguments(overText, 8, 2) is var (overArguments, afterOverset) && afterOverset == overText.Length) @@ -718,8 +726,14 @@ private static string RewriteTeXOnly(string latex) } if (cw == "mathop" && BracedArgument(tex, j) is var (operatorText, afterOperator) && afterOperator >= 0) { - // \mathop{\mathrm{argmin}}: an operator name + // \mathop{\mathrm{argmin}}: an operator name; \mathop{x}, of one symbol, a group, which takes the limits var name = operatorText.Trim(); + if (Js.Len(name) == 1) + { + result.Append('{').Append(RewriteTeXOnly(name)).Append('}'); + i = afterOperator; + continue; + } if (name.StartsWith("\\mathrm{", StringComparison.Ordinal) && name[name.Length - 1] == '}') name = name.Substring(8, name.Length - 9); result.Append(name.All(char.IsLetter) ? "\\operatorname{" + name + "}" : RewriteTeXOnly(operatorText)); diff --git a/tests/SharpEquations.Tests/LaTeX/LaTeXReaderTests.cs b/tests/SharpEquations.Tests/LaTeX/LaTeXReaderTests.cs index 9d7ffc0..be1d89d 100644 --- a/tests/SharpEquations.Tests/LaTeX/LaTeXReaderTests.cs +++ b/tests/SharpEquations.Tests/LaTeX/LaTeXReaderTests.cs @@ -137,6 +137,12 @@ public class LaTeXReaderTests [Arguments(@"\langle a \vert b \rangle", @"\langle a|b\rangle", @"โŸจ๐‘Žโ”‚๐‘โŸฉ")] [Arguments(@"\left. x \right. ", @"x", @"๐‘ฅ")] [Arguments(@"\mathop{\sum}_i", @"\sum_i", @"โˆ‘_๐‘–")] + [Arguments(@"\mathop{x}\limits_0^1", @"\underset{0}{\overset{1}{x}}", @"๐‘ฅโ”ฌ0โ”ด1")] + [Arguments(@"\underset{0}{\overset{1}{x}}", @"\underset{0}{\overset{1}{x}}", @"๐‘ฅโ”ฌ0โ”ด1")] + [Arguments(@"\overbrace3^a_x", @"\underset{x}{\overset{a}{\overbrace{3}}}", @"โž3โ”ฌ๐‘ฅโ”ด๐‘Ž")] + [Arguments(@"\underset{x}{\overset{a}{\overbrace{3}}}", @"\underset{x}{\overset{a}{\overbrace{3}}}", @"โž3โ”ฌ๐‘ฅโ”ด๐‘Ž")] + [Arguments(@"\underbrace{a+b}_n^x+c", @"\underset{n}{\overset{x}{\underbrace{a+b}}}+c", @"โŸ(๐‘Ž+๐‘)โ”ฌ๐‘›โ”ด๐‘ฅ+๐‘")] + [Arguments(@"\underset{n}{\overset{x}{\underbrace{a+b}}}+c", @"\underset{n}{\overset{x}{\underbrace{a+b}}}+c", @"โŸ(๐‘Ž+๐‘)โ”ฌ๐‘›โ”ด๐‘ฅ+๐‘")] [Arguments(@"\xleftarrow{g}", @"\overset{g}{\leftarrow}", @"โ†โ”ด๐‘”")] [Arguments(@"\pmod n", @"(\mod\,n)", @"(modโกโ€‰๐‘›)")] [Arguments(@"\hspace*{2cm}x", @"x", @"๐‘ฅ")] diff --git a/tests/SharpEquations.Tests/LaTeX/Latex2MathMLCorpusTests.cs b/tests/SharpEquations.Tests/LaTeX/Latex2MathMLCorpusTests.cs index 91ee8c2..bca3f97 100644 --- a/tests/SharpEquations.Tests/LaTeX/Latex2MathMLCorpusTests.cs +++ b/tests/SharpEquations.Tests/LaTeX/Latex2MathMLCorpusTests.cs @@ -198,12 +198,12 @@ private static string Canonical(Equation equation) // LaTeX and amsmath that the reader does not read yet: an operator name with nothing after it (\lg, \liminf, // \injlim, \operatorname*{argmax}_x), which UnicodeMath reads as letters, and the names of amsmath that are two - // words (lim inf); \limits after \mathop; both scripts of \overbrace and \underbrace; \middle between empty - // delimiters and bars with nothing between them (|\,|); \newenvironment and ## in a macro + // words (lim inf); \middle between empty delimiters and bars with nothing between them (|\,|); \newenvironment + // and ## in a macro private static readonly HashSet NotReadYet = new HashSet { "lg", "liminf", "limsup", "injlim", "projlim", "varinjlim", "operatornamewithlimits", "operatorname-star", - "issue-125-4-limits", "overbrace-c", "underbrace-c", "middle", "spaces", + "middle", "spaces", "newenvironment-multi-node", "newcommand-escaped-hash", };