Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 22 additions & 15 deletions src/SharpEquations/Internal/LaTeX/LaTeXReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,15 +1281,17 @@ void Close((int Open, List<int> Bars) frame, bool inBrackets, int close)

/// <summary>
/// A bracket that a group opens and does not close, as in \phantom{(}, escaped: UnicodeMath would close it with an
/// empty fence at the end of the group; so are a closing bracket alone in a group (x^{)}) and a bracket
/// that is a script and does not close (x_(). The text of \text and the like is left as it is.
/// empty fence at the end of the group; so are a closing bracket alone in a group (x^{)}) and a bracket that is a
/// script and does not close (x_(). A script in brackets that close is grouped whole (x^(a+b) as x^{(a+b)}). The
/// text of \text and the like is left as it is.
/// </summary>
private static string EscapeOpenBrackets(string latex)
{
var tex = new Text(latex);
var groups = new Stack<List<int>>();
var escaped = new List<int>();
var scripts = new HashSet<int>();
// the scripts grouped, from the index of their first character to that of their last
var scripts = new Dictionary<int, int>();
for (int i = 0; i < tex.Length; i++)
{
switch (tex[i])
Expand Down Expand Up @@ -1330,27 +1332,32 @@ private static string EscapeOpenBrackets(string latex)
break;
case "_" or "^":
{
// a bracket that is a script and closes nothing, x^), or opens what does not close, x_(: Word's LaTeX
// groups a script in brackets (x^(a+b)), which UnicodeMath would read as the opening of what follows
// a bracket that is a script and closes nothing, x^), or opens what does not close, x_(, which
// UnicodeMath would read as the opening of what follows; a script in brackets that close, x^(a+b),
// is grouped whole, as Word's LaTeX writes it and Word reads it (x^{(a+b)})
int k = i + 1;
while (tex[k] == " ")
k++;
if (tex[k] is ")" or "]" || tex[k] is "(" or "[" && !BracketCloses(tex, k))
var close = tex[k] is "(" or "[" ? ClosingBracket(tex, k) : -1;
if (close >= 0)
scripts[k] = close;
else if (tex[k] is "(" or "[" or ")" or "]")
{
escaped.Add(k);
scripts.Add(k);
scripts[k] = k;
i = k;
}
break;
}
}
}
if (escaped.Count == 0)
if (escaped.Count == 0 && scripts.Count == 0)
return latex;
var ends = scripts.Values.ToList();
var result = new StringBuilder();
for (int i = 0; i < tex.Length; i++)
{
if (scripts.Contains(i))
if (scripts.ContainsKey(i))
result.Append('{');
if (escaped.Contains(i))
{
Expand All @@ -1364,7 +1371,7 @@ private static string EscapeOpenBrackets(string latex)
}
else
result.Append(tex[i]);
if (scripts.Contains(i))
foreach (var _ in ends.Where(end => end == i))
result.Append('}');
}
return result.ToString();
Expand All @@ -1379,8 +1386,8 @@ private static string Blank(Text tex, int i, int step)
return i >= 0 && i < tex.Length ? tex[i] : null;
}

/// <summary>Whether the bracket at the index closes before the group around it ends.</summary>
private static bool BracketCloses(Text tex, int open)
/// <summary>The index of the bracket that closes the one at the index before the group around it ends; -1 if none.</summary>
private static int ClosingBracket(Text tex, int open)
{
int depth = 0, braces = 0;
for (int i = open; i < tex.Length; i++)
Expand All @@ -1394,18 +1401,18 @@ private static bool BracketCloses(Text tex, int open)
braces++;
break;
case "}" when braces == 0:
return false;
return -1;
case "}":
braces--;
break;
case "(" or "[":
depth++;
break;
case ")" or "]" when --depth == 0:
return true;
return i;
}
}
return false;
return -1;
}

/// <summary>The count arguments (braced or single characters) at the index, and the index after them; null if missing.</summary>
Expand Down
6 changes: 5 additions & 1 deletion tests/SharpEquations.Tests/LaTeX/LaTeXReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public async Task ReadsLessCommonCommands(string latex, string expectedLaTeX, st

// a slash and a bar are ordinary characters of LaTeX, which UnicodeMath reads as a fraction and an absolute value;
// so is a bracket that a group opens and does not close, which UnicodeMath would close, or a bracket alone as a
// script; a bar in text, after \bigm (a relation) or {|} is a bar alone, which no other bar pairs with
// script (a script in brackets that close is grouped whole, x^(a+b), as Word reads it); a bar in text, after \bigm (a relation) or {|} is a bar alone, which no other bar pairs with
[Test]
[Arguments(@"a/b", @"a/b", @"𝑎\/𝑏")]
[Arguments(@"(a+b)/2", @"(a+b)/2", @"(𝑎+𝑏)\/2")]
Expand All @@ -199,6 +199,10 @@ public async Task ReadsLessCommonCommands(string latex, string expectedLaTeX, st
[Arguments(@"x_(", @"x_{(}", @"𝑥_\(")]
[Arguments(@"x^{)}", @"x^{)}", @"𝑥^\)")]
[Arguments(@"x^{ ] }", @"x^{]}", @"𝑥^\]")]
[Arguments(@"x^(a+b)", @"x^{(a+b)}", @"𝑥^((𝑎+𝑏))")]
[Arguments(@"x_[i]y", @"x_{[i]}y", @"𝑥_[𝑖] 𝑦")]
[Arguments(@"x^ (a)(b)", @"x^{(a)}(b)", @"𝑥^((𝑎)) (𝑏)")]
[Arguments(@"e^(i\pi)_n", @"e_n^{(i\pi)}", @"𝑒_𝑛^((𝑖𝜋))")]
[Arguments(@"\text{a|b}", @"\text{a|b}", @"""a|b""")]
[Arguments(@"a\vert b", @"a|b", @"𝑎\|𝑏")]
[Arguments(@"\vert x\vert", @"|x|", @"|𝑥|")]
Expand Down
Loading