public static Structures.Result <IMathAtom> Table( string environment, List <List <IMathList> > rows) { Style style; var table = new Table(environment) { Cells = rows }; switch (environment) { case null: table.InterRowAdditionalSpacing = 1; for (int i = 0; i < table.NColumns; i++) { table.SetAlignment(ColumnAlignment.Left, i); } return(table); case var _ when _matrixEnvironments.TryGetValue(environment, out var delimiters): table.Environment = "matrix"; // TableEnvironment is set to matrix as delimiters are converted to latex outside the table. table.InterColumnSpacing = 18; style = new Style(LineStyle.Text); foreach (var row in table.Cells) { foreach (var cell in row) { cell.Insert(0, style); } } if (delimiters != null) { var inner = new Inner { LeftBoundary = BoundaryAtom(delimiters.Value.First), RightBoundary = BoundaryAtom(delimiters.Value.Second), InnerList = MathLists.WithAtoms(table) }; return(inner); } else { return(table); } case "eqalign": case "split": case "aligned": if (table.NColumns != 2) { return(environment + " environment can have only 2 columns"); } else { // add a spacer before each of the second column elements, in order to create the correct spacing for "=" and other relations. var spacer = Create(MathAtomType.Ordinary, string.Empty); foreach (var row in table.Cells) { if (row.Count > 1) { row[1].Insert(0, spacer); } } table.InterRowAdditionalSpacing = 1; table.SetAlignment(ColumnAlignment.Right, 0); table.SetAlignment(ColumnAlignment.Left, 1); return(table); } case "displaylines": case "gather": if (table.NColumns != 1) { return(environment + " environment can only have 1 column."); } table.InterRowAdditionalSpacing = 1; table.InterColumnSpacing = 0; table.SetAlignment(ColumnAlignment.Center, 0); return(table); case "eqnarray": if (table.NColumns != 3) { return(environment + " must have exactly 3 columns."); } else { table.InterRowAdditionalSpacing = 1; table.InterColumnSpacing = 18; table.SetAlignment(ColumnAlignment.Right, 0); table.SetAlignment(ColumnAlignment.Center, 1); table.SetAlignment(ColumnAlignment.Left, 2); return(table); } case "cases": if (table.NColumns < 1 || table.NColumns > 2) { return("cases environment must have 1 to 2 columns"); } else { table.InterColumnSpacing = 18; table.SetAlignment(ColumnAlignment.Left, 0); if (table.NColumns == 2) { table.SetAlignment(ColumnAlignment.Left, 1); } style = new Style(LineStyle.Text); foreach (var row in table.Cells) { foreach (var cell in row) { cell.Insert(0, style); } } // add delimiters var inner = new Inner { LeftBoundary = BoundaryAtom("{"), RightBoundary = BoundaryAtom(".") }; var space = ForLatexSymbolName(","); inner.InnerList = MathLists.WithAtoms(space, table); return(inner); } default: return("Unknown environment " + environment); } }
public static IMathAtom Table( string environment, List <List <IMathList> > rows, out string errorMessage) { errorMessage = null; var table = new Table(environment) { Cells = rows }; IMathAtom r = null; if (environment != null && _matrixEnvironments.TryGetValue(environment, out var delimiters)) { table.Environment = "matrix"; // TableEnvironment is set to matrix as delimiters are converted to latex outside the table. table.InterColumnSpacing = 18; var style = new Style(LineStyle.Text); foreach (var row in table.Cells) { foreach (var cell in row) { cell.Insert(0, style); } } if (delimiters != null) { var inner = new Inner { LeftBoundary = BoundaryAtom(delimiters.Value.First), RightBoundary = BoundaryAtom(delimiters.Value.Second), InnerList = MathLists.WithAtoms(table) }; r = inner; } else { r = table; } } else if (environment == null) { table.InterRowAdditionalSpacing = 1; for (int i = 0; i < table.NColumns; i++) { table.SetAlignment(ColumnAlignment.Left, i); } r = table; } else if (environment == "eqalign" || environment == "split" || environment == "aligned") { if (table.NColumns != 2) { errorMessage = environment + " environment can have only 2 columns"; } else { // add a spacer before each of the second column elements, in order to create the correct spacing for "=" and other relations. var spacer = Create(MathAtomType.Ordinary, string.Empty); foreach (var row in table.Cells) { if (row.Count > 1) { row[1].Insert(0, spacer); } } table.InterRowAdditionalSpacing = 1; table.SetAlignment(ColumnAlignment.Right, 0); table.SetAlignment(ColumnAlignment.Left, 1); r = table; } } else if (environment == "displaylines" || environment == "gather") { if (table.NColumns != 1) { errorMessage = environment + " environment can only have 1 column."; return(null); } table.InterRowAdditionalSpacing = 1; table.InterColumnSpacing = 0; table.SetAlignment(ColumnAlignment.Center, 0); r = table; } else if (environment == "eqnarray") { if (table.NColumns != 3) { errorMessage = environment + " must have exactly 3 columns."; } else { table.InterRowAdditionalSpacing = 1; table.InterColumnSpacing = 18; table.SetAlignment(ColumnAlignment.Right, 0); table.SetAlignment(ColumnAlignment.Center, 1); table.SetAlignment(ColumnAlignment.Left, 2); r = table; } } else if (environment == "cases") { if (table.NColumns < 1 || table.NColumns > 2) { errorMessage = "cases environment must have 1 to 2 columns"; } else { table.InterColumnSpacing = 18; table.SetAlignment(ColumnAlignment.Left, 0); if (table.NColumns == 2) { table.SetAlignment(ColumnAlignment.Left, 1); } var style = new Style(LineStyle.Text); foreach (var row in table.Cells) { foreach (var cell in row) { cell.Insert(0, style); } } // add delimiters var inner = new Inner { LeftBoundary = BoundaryAtom("{"), RightBoundary = BoundaryAtom(".") }; var space = ForLatexSymbolName(","); inner.InnerList = MathLists.WithAtoms(space, table); r = inner; } } return(r); }
internal MathList StopCommand(string command, MathList list, char stopChar) { if (command == "right") { if (_currentInnerAtom == null) { SetError("Missing \\left"); return(null); } _currentInnerAtom.RightBoundary = _BoundaryAtomForDelimiterType("right"); if (_currentInnerAtom.RightBoundary == null) { return(null); } return(list); } if (fractionCommands.ContainsKey(command)) { bool rule = (command == "over"); var fraction = new Fraction(rule); var delimiters = fractionCommands[command]; if (delimiters != null) { fraction.LeftDelimiter = delimiters.Value.First; fraction.RightDelimiter = delimiters.Value.Second; } fraction.Numerator = list; fraction.Denominator = BuildInternal(false, stopChar); if (_error != null) { return(null); } return(MathLists.WithAtoms(fraction)); } else if (command == "\\" || command == "cr") { if (_currentEnvironment == null) { var table = BuildTable(null, list, true, stopChar); if (table == null) { return(null); } return(MathLists.WithAtoms(table)); } else { // stop the current list and increment the row count _currentEnvironment.NRows++; return(list); } } else if (command == "end") { if (_currentEnvironment == null) { SetError(@"Missing \begin"); return(null); } var env = ReadEnvironment(); if (env == null) { return(null); } if (env != _currentEnvironment.Name) { SetError($"Begin environment name {_currentEnvironment.Name} does not match end environment name {env}"); return(null); } _currentEnvironment.Ended = true; return(list); } return(null); }
public static string MathListToString(IMathList mathList) { var builder = new StringBuilder(); var currentFontStyle = FontStyle.Default; foreach (var atom in mathList) { if (currentFontStyle != atom.FontStyle) { if (currentFontStyle != FontStyle.Default) { // close the previous font style builder.Append("}"); } if (atom.FontStyle != FontStyle.Default) { // open a new font style var fontStyleName = atom.FontStyle.FontName(); builder.Append(@"\" + fontStyleName + "{"); } } currentFontStyle = atom.FontStyle; switch (atom.AtomType) { case MathAtomType.Fraction: { var fraction = (IFraction)atom; var numerator = MathListToString(fraction.Numerator); var denominator = MathListToString(fraction.Denominator); if (fraction.HasRule) { builder.Append(@"\frac{" + numerator + "}{" + denominator + "}"); } else { string command = null; if (fraction.LeftDelimiter == null && fraction.RightDelimiter == null) { command = "atop"; } else if (fraction.LeftDelimiter == "(" && fraction.RightDelimiter == ")") { command = "choose"; } else if (fraction.LeftDelimiter == "{" && fraction.RightDelimiter == "}") { command = "brace"; } else if (fraction.LeftDelimiter == "[" && fraction.RightDelimiter == "]") { command = "brack"; } else { command = $"atopwithdelims{fraction.LeftDelimiter}{fraction.RightDelimiter}"; } builder.Append("{" + numerator + @" \" + command + " " + denominator + "}"); } } break; case MathAtomType.Radical: { builder.Append(@"\sqrt"); var radical = (IRadical)atom; if (radical.Degree != null) { builder.Append($"[{MathListToString(radical.Degree)}]"); } builder.Append("{" + MathListToString(radical.Radicand) + "}"); break; } case MathAtomType.Inner: { var inner = (IMathInner)atom; if (inner.LeftBoundary == null && inner.RightBoundary == null) { builder.Append("{" + MathListToString(inner.InnerList) + "}"); } else { if (inner.LeftBoundary == null) { builder.Append(@"\left. "); } else { builder.Append(@"\left" + DelimiterToString(inner.LeftBoundary) + " "); } builder.Append(MathListToString(inner.InnerList)); if (inner.RightBoundary == null) { builder.Append(@"\right. "); } else { builder.Append(@"\right" + DelimiterToString(inner.RightBoundary) + " "); } } break; } case MathAtomType.Table: { var table = (IMathTable)atom; if (table.Environment != null) { builder.Append(@"\begin{" + table.Environment + "}"); } for (int i = 0; i < table.NRows; i++) { var row = table.Cells[i]; for (int j = 0; j < row.Count; j++) { var cell = row[j]; if (table.Environment == "matrix") { if (cell.Count >= 1 && cell[0].AtomType == MathAtomType.Style) { // remove the first atom. var atoms = cell.Atoms.GetRange(1, cell.Count - 1); cell = MathLists.WithAtoms(atoms.ToArray()); } } if (table.Environment == "eqalign" || table.Environment == "aligned" || table.Environment == "split") { if (j == 1 && cell.Count >= 1 && cell[0].AtomType == MathAtomType.Ordinary && string.IsNullOrEmpty(cell[0].Nucleus)) { // empty nucleus added for spacing. Remove it. var atoms = cell.Atoms.GetRange(1, cell.Count - 1); cell = MathLists.WithAtoms(atoms.ToArray()); } } builder.Append(MathListToString(cell)); if (j < row.Count - 1) { builder.Append("&"); } } if (i < table.NRows - 1) { builder.Append(@"\\ "); } } if (table.Environment != null) { builder.Append(@"\end{" + table.Environment + "}"); } break; } case MathAtomType.Overline: { var over = (IOverline)atom; builder.Append(@"\overline{" + MathListToString(over.InnerList) + "}"); break; } case MathAtomType.Underline: { var under = (IUnderline)atom; builder.Append(@"\underline{" + MathListToString(under.InnerList) + "}"); break; } case MathAtomType.Accent: { var accent = (IAccent)atom; var list = accent.InnerList; accent.InnerList = null; //for lookup builder.Append(@"\" + MathAtoms.Commands[(MathAtom)atom] + "{" + MathListToString(list) + "}"); break; } case MathAtomType.LargeOperator: { var op = (LargeOperator)atom; var command = MathAtoms.LatexSymbolNameForAtom(op); var originalOperator = (LargeOperator)MathAtoms.ForLatexSymbolName(command); builder.Append($@"\{command} "); if (originalOperator.Limits != op.Limits) { switch (op.Limits) { case true: builder.Append(@"\limits "); break; case false: if (!op.NoLimits) { builder.Append(@"\nolimits "); } break; case null: break; } } break; } case MathAtomType.Space: { var space = (ISpace)atom; var intSpace = (int)space.Length; if (SpaceToCommands.ContainsKey(intSpace) && intSpace == space.Length) { var command = SpaceToCommands[intSpace]; builder.Append(@"\" + command + " "); } else { if (space.IsMu) { builder.Append(@"\mkern" + space.Length.ToString("0.0") + "mu"); } else { builder.Append(@"\kern" + space.Length.ToString("0.0") + "pt"); } } break; } case MathAtomType.Style: { var style = (IStyle)atom; var command = StyleToCommands[style.LineStyle]; builder.Append(@"\" + command + " "); break; } case MathAtomType.Color: { var color = (IColor)atom; builder.Append($@"\color{{{color.ColorString}}}{{{MathListToString(color.InnerList)}}}"); break; } case MathAtomType.Group: builder.AppendInBraces(MathListToString(((Group)atom).InnerList), NullHandling.EmptyContent); break; case MathAtomType.Prime: builder.Append('\'', ((Prime)atom).Length); break; default: { var aNucleus = atom.Nucleus; if (String.IsNullOrEmpty(aNucleus)) { builder.Append(@"{}"); } else if (aNucleus == "\u2236") { builder.Append(":"); } else if (aNucleus == "\u2212") { builder.Append("-"); } else { var command = MathAtoms.LatexSymbolNameForAtom((MathAtom)atom); if (command == null) { if (atom is Extension.I_ExtensionAtom ext) { builder.Append(Extension._MathListDestructor.MathAtomToString(ext)); } else { builder.Append(aNucleus); } } else { builder.Append(@"\" + command + " "); } } break; } } if (atom.Subscript != null) { var scriptString = MathListToString(atom.Subscript); builder.Append(scriptString.Length == 1 ? $"_{scriptString}" : $"_{{{scriptString}}}"); } if (atom.Superscript != null) { var scriptString = MathListToString(atom.Superscript); builder.Append(scriptString.Length == 1 ? $"^{scriptString}" : $"^{{{scriptString}}}"); } } if (currentFontStyle != FontStyle.Default) { builder.Append("}"); } return(builder.ToString()); }
internal IMathList BuildInternal(bool oneCharOnly, char stopChar) { if (oneCharOnly && stopChar > 0) { throw new InvalidOperationException("Cannot set both oneCharOnly and stopChar"); } var r = new MathList(); IMathAtom prevAtom = null; while (HasCharacters) { if (_error != null) { return(null); } IMathAtom atom; var ch = GetNextCharacter(); if (oneCharOnly) { if (ch == '^' || ch == '}' || ch == '_' || ch == '&') { // this is not the character we are looking for. They are for the caller to look at. UnlookCharacter(); return(r); } } if (stopChar > 0 && ch == stopChar) { return(r); } switch (ch) { case '^': if (prevAtom == null || prevAtom.Superscript != null || !prevAtom.ScriptsAllowed) { prevAtom = MathAtoms.Create(MathAtomType.Ordinary, string.Empty); r.Add(prevAtom); } // this is a superscript for the previous atom. // note, if the next char is StopChar, it will be consumed and doesn't count as stop. prevAtom.Superscript = this.BuildInternal(true); continue; case '_': if (prevAtom == null || prevAtom.Subscript != null || !prevAtom.ScriptsAllowed) { prevAtom = MathAtoms.Create(MathAtomType.Ordinary, string.Empty); r.Add(prevAtom); } // this is a subscript for the previous atom. // note, if the next char is StopChar, it will be consumed and doesn't count as stop. prevAtom.Subscript = this.BuildInternal(true); continue; case '{': IMathList sublist; if (_currentEnvironment != null && _currentEnvironment.Name == null) { // \\ or \cr which do not have a corrosponding \end var oldEnv = _currentEnvironment; _currentEnvironment = null; sublist = BuildInternal(false, '}'); _currentEnvironment = oldEnv; } else { sublist = BuildInternal(false, '}'); } if (sublist == null) { return(null); } prevAtom = sublist.Atoms.LastOrDefault(); if (oneCharOnly) { r.Append(sublist); return(r); } #warning TODO Example //https://phabricator.wikimedia.org/T99369 //https://phab.wmfusercontent.org/file/data/xsimlcnvo42siudvwuzk/PHID-FILE-bdcqexocj5b57tj2oezn/math_rendering.png //dt, \text{d}t, \partial t, \nabla\psi \\ \underline\overline{dy/dx, \text{d}y/\text{d}x, \frac{dy}{dx}, \frac{\text{d}y}{\text{d}x}, \frac{\partial^2}{\partial x_1\partial x_2}y} \\ \prime, atom = new Group { InnerList = sublist }; break; case '}': if (oneCharOnly || stopChar != 0) { throw new InvalidCodePathException("This should have been handled before."); } SetError("Missing opening brace"); return(null); case '\\': var command = ReadCommand(); var done = StopCommand(command, r, stopChar); if (done != null) { return(done); } if (_error != null) { return(null); } if (ApplyModifier(command, prevAtom)) { continue; } var fontStyleQ = MathAtoms.FontStyle(command); if (fontStyleQ.HasValue) { var fontStyle = fontStyleQ.Value; var oldSpacesAllowed = _textMode; var oldFontStyle = _currentFontStyle; _textMode = (command == "text"); _currentFontStyle = fontStyle; var childList = BuildInternal(true); if (childList == null) { return(null); } _currentFontStyle = oldFontStyle; _textMode = oldSpacesAllowed; prevAtom = childList.Atoms.LastOrDefault(); r.Append(childList); if (oneCharOnly) { return(r); } continue; } atom = AtomForCommand(command, stopChar); if (atom == null) { SetError(_error ?? "Internal error"); return(null); } break; case '&': { // column separation in tables if (_currentEnvironment != null) { return(r); } var table = BuildTable(null, r, false, stopChar); if (table == null) { return(null); } return(MathLists.WithAtoms(table)); } case '\'': // this case is NOT in iosMath int i = 1; while (ExpectCharacter('\'')) { i++; } atom = new Prime(i); break; default: if (_textMode && ch == ' ') { atom = MathAtoms.ForLatexSymbolName(" "); } else { atom = MathAtoms.ForCharacter(ch); if (atom == null) { // not a recognized character continue; } } break; } if (atom == null) { throw new InvalidCodePathException("Atom shouldn't be null"); } atom.FontStyle = _currentFontStyle; r.Add(atom); prevAtom = atom; if (oneCharOnly) { return(r); // we consumed our character. } } if (stopChar > 0) { if (stopChar == '}') { SetError("Missing closing brace"); } else { // we never found our stop character. SetError("Expected character not found: " + stopChar.ToString()); } } return(r); }