public void ConvertFracXYPlus2ReturnsFracXYPlus2InMML() { String latexExpression = @"$\frac{x}{y}+2$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertFracXYPlus2ReturnsFracXYPlus2InMMLEventListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\frac{x}{y}+2"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mfrac> <mrow> <mi>x</mi> </mrow> <mrow> <mi>y</mi> </mrow> </mfrac> <mo>+</mo> <mn>2</mn> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
public static string ConvertLatextToMathMl(string latexExp) { LatexMathToMathMLConverter lmm; //For demo Try with following Expression //String latexExpression = @"$\frac{\mathrm d}{\mathrm d x} \big( k g(x \big)$"; lmm = new LatexMathToMathMLConverter(); return(lmm.ConvertToMathMLTree(latexExpression: latexExp)); }
public void Convert() { String latexExpression = @"\begin{document} $\frac{\mathrm d}{\mathrm d x} \big( k g(x \big)$ \end{document}"; lmm = new LatexMathToMathMLConverter(); lmm.ValidateResult = true; lmm.BeforeXmlFormat += MyEventListener; lmm.ExceptionEvent += ExceptionListener; lmm.Convert(latexExpression); }
/// <summary> /// Appends the opening MathML <math> tag to the specified StringBuilder instance. /// </summary> /// <param name="bld">The StringBuilder instance.</param> /// <param name="altText">The alternative text.</param> /// <param name="inline">Indicates whether the math block is inline.</param> public static void AppendMathProlog(StringBuilder bld, string altText, bool inline, LatexMathToMathMLConverter customization) { bld.Append("<math xmlns=\"http://www.w3.org/1998/Math/MathML\" alttext=\""); bld.Append(altText); bld.Append("\" "); bld.Append(inline ? "display=\"inline\"" : "display=\"block\""); bld.Append(" class=\"" + "normalsize" + "\""); bld.Append(">\n<mstyle displaystyle=\"true\" />"); }
public static string ConvertOutline(IList<LatexExpression> outline, LatexMathToMathMLConverter customization) { var bld = new StringBuilder(); var backupTextSize = customization.CurrentTextSize; var backupTextStyle = customization.CurrentTextStyle; foreach (var child in outline) { bld.Append(child.Convert()); } customization.CurrentTextSize = backupTextSize; customization.CurrentTextStyle = backupTextStyle; return bld.ToString(); }
public void ConvertOperatorsDotsReturnsCorrectMML() { String latexExpression = @"$\dots \dotsm \vdots \ddots$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsDotsReturnsCorrectMMLListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\dots \dotsm \vdots \ddots"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mo>…</mo> <mo>⋯</mo> <mo>⋱</mo> <mo>⋮</mo> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
public void ConvertOperatorsBinaryOperators() { String latexExpression = @"$\pm \mp \times \div \ast \star \dagger \ddagger \cap \cup \uplus \sqcap \sqcup \vee \wedge \cdot \diamond \bigtriangleup \bigtriangledown \triangleleft \triangledown \bigcirc \bullet \wr \oplus \ominus \otimes \oslash \odot \circ \setminus \amalg$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsBinaryOperatorsListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\dots \dotsm \vdots \ddots"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mo>…</mo> <mo>⋯</mo> <mo>⋱</mo> <mo>⋮</mo> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
/// <summary> /// Builds a new LatexExpression instance (front). /// </summary> /// <param name="parent">The parent of the builded expression.</param> /// <param name="parentChildNumber">Index of the parent child outline.</param> /// <param name="indexInParentChild">Index in the parent child outline.</param> /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param> /// <param name="beginning">The beginning string.</param> /// <param name="rdr">The reader to read ahead.</param> /// <param name="end">The stub of the unparsed part.</param> /// <param name="mathMode">The math mode switch.</param> /// <param name="whitespaceBefore">Indicates whether there was at least one whitespace char before the returned result.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> /// <returns></returns> private static LatexExpression ReadFromTextReader(LatexExpression parent, int parentChildNumber, ref int indexInParentChild, ref bool verbatimMode, bool mathMode, LatexMathToMathMLConverter customization, string beginning, TextReader rdr, out string end, ref bool whitespaceBefore) { if (beginning == null && rdr == null) { end = null; return null; } return ReadFromTextReaderInner(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, mathMode, customization, beginning ?? rdr.ReadLine(), rdr, out end, ref whitespaceBefore); }
/// <summary> /// Parses the document and builds the document object tree. /// </summary> /// <param name="rdr">The reader to read the document from.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> /// <returns>The root of the document object tree.</returns> public static LatexExpression CreateRoot(TextReader rdr, LatexMathToMathMLConverter customization) { bool verbatimMode = false; int rootIndex = 0; var root = new LatexExpression(null, 0, ref rootIndex, ref verbatimMode, ExpressionType.Root, false, customization, null, null, null) { Expressions = new List<List<LatexExpression>>(1) }; var children = new List<LatexExpression>(); string beginning = null; string end; LatexExpression cmd; rootIndex = 0; bool mathMode = false; bool whitespaceBefore = false; while ((cmd = ReadFromTextReader(root, 0, ref rootIndex, ref verbatimMode, mathMode, customization, beginning, rdr, out end, ref whitespaceBefore)) != null) { CheckMathMode(cmd, ref mathMode); beginning = end; children.Add(cmd); } root.Expressions.Add(children); return root; }
/// <summary> /// Initializes a new instance of the LatexExpression class. /// </summary> /// <param name="parent">The parent of the builded expression.</param> /// <param name="parentChildNumber">Index of the parent child outline.</param> /// <param name="indexInParentChild">Index in the parent child outline.</param> /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param> /// <param name="type">The expression type.</param> /// <param name="mathMode">The math mode switch.</param> /// <param name="name">The expression name.</param> /// <param name="options">The options of the expression.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> /// <param name="values">The child outlines of the expression.</param> private LatexExpression(LatexExpression parent, int parentChildNumber, ref int indexInParentChild, ref bool verbatimMode, ExpressionType type, bool mathMode, LatexMathToMathMLConverter customization, string name, string options, params string[] values) { Parent = parent; ParentChildNumber = parentChildNumber; IndexInParentChild = indexInParentChild++; Customization = customization; Name = name; ExprType = type; MathMode = mathMode; #region Switch verbatim mode on/off if (type == ExpressionType.Verbatim) { verbatimMode = false; } if (Expressions != null && Name == "begin" && Expressions[0][0].Name == "verbatim" && Expressions.Count == 1) { verbatimMode = true; } #endregion ParseOptions(options); if (values != null) { ParseExpressions(values); } #region If a math block, add the alternative text if (type == ExpressionType.InlineMath || type == ExpressionType.BlockMath) { // ReSharper disable PossibleNullReferenceException Expressions.Add(new List<LatexExpression>()); int index = 0; Expressions[1].Add(new LatexExpression(this, 1, ref index, ref verbatimMode, ExpressionType.PlainText, false, Customization, values[0], null, null)); // ReSharper restore PossibleNullReferenceException } #endregion #region Post-parse plain text if in math mode if (ExprType == ExpressionType.PlainText && mathMode && Name.Length > 1 && (Parent.ExprType != ExpressionType.Block || Parent.Name != "PlainText") && (Parent.ExprType != ExpressionType.Command || Parent.Name != "begin" && Parent.Name != "end")) { ExprType = ExpressionType.Block; Name = "PlainText"; #region Parse var list = new List<string>(); var buf = "" + name[0]; for (int pos = 1; pos < name.Length; pos++) { var chrPre = name[pos - 1]; var chr = name[pos]; if (char.IsWhiteSpace(chr)) { list.Add(buf); buf = ""; continue; } if (char.IsDigit(chr)) { #region Digit if (char.IsDigit(chrPre)) { buf += chr; continue; } if (char.IsLetter(chr) || chr == '(') { list.Add("InvisibleTimes"); } list.Add(buf); buf = "" + chr; continue; #endregion } if (char.IsLetter(chr)) { #region Letter if (char.IsLetter(chrPre)) { buf += chr; continue; } if (char.IsDigit(chr) || chr == '(') { list.Add("InvisibleTimes"); } list.Add(buf); buf = "" + chr; continue; #endregion } #region >=, <= if (chr == '=') { if (chrPre == '>' || chrPre == '<') { buf += chr; } } #endregion list.Add(buf); buf = "" + chr; } list.Add(buf); #endregion Expressions = new List<List<LatexExpression>>(list.Count) {new List<LatexExpression>()}; int i; for (i = 0; i < list.Count;) { if (list[i].Trim() != "") { Expressions[0].Add(new LatexExpression(this, 0, ref i, ref verbatimMode, ExpressionType.PlainText, true, customization, list[i].Trim(), null, null)); } else { i++; } } } #endregion }
/// <summary> /// Initializes a new instance of the LatexExpression class of type ExpressionType.Block. /// </summary> /// <param name="name">The name of the block.</param> /// <param name="parent">The parent of the builded expression.</param> /// <param name="parentChildNumber">Index of the parent child outline.</param> /// <param name="indexInParentChild">Index in the parent child outline.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> public LatexExpression(string name, LatexExpression parent, int parentChildNumber, int indexInParentChild, LatexMathToMathMLConverter customization) { Name = name; Customization = customization; ExprType = ExpressionType.Block; MathMode = parent.MathMode | parent.ExprType == ExpressionType.BlockMath | parent.ExprType == ExpressionType.InlineMath; Parent = parent; ParentChildNumber = parentChildNumber; IndexInParentChild = indexInParentChild; Expressions = new List<List<LatexExpression>> {new List<LatexExpression>()}; }
/// <summary> /// Initializes a new instance of the LatexParser class. /// </summary> /// <param name="source">The source string to build the tree from.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> public LatexParser(string source, LatexMathToMathMLConverter customization) { if (String.IsNullOrEmpty(source)) { throw new ArgumentException("The parameter can not be null or empty.", "source"); } _source = source; _customization = customization; }
/// <summary> /// Post-parses arrays. /// </summary> /// <param name="outline">The outline of a LatexExpression instance.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> private static void PostParseArrays(IList<LatexExpression> outline, LatexMathToMathMLConverter customization) { for (int i = 0; i < outline.Count; i++) { if (outline[i].ExprType == ExpressionType.Block && (ArrayLikeBlockNames.Contains(outline[i].Name))) { int parentChildNumber = outline[i].Expressions.Count == 1? 0 : 1; var main = outline[i].Expressions[parentChildNumber]; var latex_array = new List<List<List<LatexExpression>>>(3) {new List<List<LatexExpression>>(3)}; #region Build array int rowIndex = 0; for (int j = 0; ; j++) { var cell = new List<LatexExpression>(2); for (; j < main.Count && !(main[j].Name == "\\" && main[j].ExprType == ExpressionType.Command) && !(main[j].Name == "&" && main[j].ExprType == ExpressionType.PlainText) ; j++) { // \hline won't do the same in XHTML if (main[j].ExprType == ExpressionType.Command && main[j].Name == "hline") continue; if (main[j].ExprType == ExpressionType.Comment) continue; cell.Add(main[j]); } if (cell.Count > 0) { latex_array[rowIndex].Add(cell); } if (j == main.Count) { break; } if (main[j].Name == "\\") { rowIndex++; latex_array.Add(new List<List<LatexExpression>>(3)); } } if (latex_array[rowIndex].Count == 0) { latex_array.RemoveAt(rowIndex); } #endregion #region Link array main = new List<LatexExpression>(latex_array.Count); for (int j = 0; j < latex_array.Count; j++) { // Add row main.Add(new LatexExpression("", outline[i], parentChildNumber, j, customization)); for (int k = 0; k < latex_array[j].Count; k++) { // Add column cell main[j].Expressions[0].Add(new LatexExpression("", main[j], 0, k, customization)); for (int m = 0; m < latex_array[j][k].Count; m++) { latex_array[j][k][m].Parent = main[j].Expressions[0][k]; latex_array[j][k][m].ParentChildNumber = 0; latex_array[j][k][m].IndexInParentChild = m; // Add cell atom main[j].Expressions[0][k].Expressions[0].Add(latex_array[j][k][m]); } } } outline[i].Expressions[parentChildNumber] = main; #endregion } } for (int i = 0; i < outline.Count; i++) { if (outline[i].Expressions != null) { foreach (var subTree in outline[i].Expressions) { PostParseArrays(subTree, customization); } } } }
public void ConvertOperatorsWhitespacesReturnsMMLWithCorrectSpacing() { String latexExpression = @"$A,\qquad B, \quad C, \; D, \: E, \, F, \! G $"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsWhitespacesReturnsMMLWithCorrectSpacingListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""A,\qquad B, \quad C, \; D, \: E, \, F, \! G "" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mi>A</mi> <mo>,</mo> <mspace width=""4em""/><mi>B</mi> <mo>,</mo> <mspace width=""2em""/><mi>C</mi> <mo>,</mo> <mspace width=""1.1em""/><mi>D</mi> <mo>,</mo> <mspace width=""0.9em""/><mi>E</mi> <mo>,</mo> <mspace width=""0.7em""/><mi>F</mi> <mo>,</mo> <mspace width=""0.3em""/><mi>G</mi> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
/// <summary> /// Builds the document object tree. /// </summary> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> /// <remarks>The parsing procedure consists of stand-alone passes, so that it resembles a compiler pipeline.</remarks> public void Parse(LatexMathToMathMLConverter customization) { foreach (var rule in PreformatRules) { _source = _source.Replace(rule[0], rule[1]); } var rdr = new StringReader(_source); const byte PASS_COUNT = 14; byte step = 1; // Build the tree LogInfo("CreateRoot"); _root = LatexExpression.CreateRoot(rdr, customization); OnProgressEvent(step++, PASS_COUNT); //TODO Go through the usage of _customCommands // Rebuild tree with custom commands _customCommands = new Dictionary<string, LatexExpression>(); LogInfo("RecursiveParseCustomCommands"); // Incapsulate fragments between \begin and \end LogInfo("IncapsulateCommands"); IncapsulateCommands(Root.Expressions[0]); OnProgressEvent(step++, PASS_COUNT); // Post-parse arrays LogInfo("PostParseArrays"); PostParseArrays(Root.Expressions[0], customization); OnProgressEvent(step++, PASS_COUNT); // Build super- and subscripts LogInfo("BuildScripts"); BuildScripts(Root.Expressions[0], customization); OnProgressEvent(step++, PASS_COUNT); // Simplify math blocks that begin with baseless scripts LogInfo("SimplifyScripts"); SimplifyBaselessScripts(Root.Expressions[0]); OnProgressEvent(step++, PASS_COUNT); // Numerate blocks (needed for the next step) LogInfo("NumerateBlocks"); NumerateBlocks(Root.FindDocument().Expressions[0]); Root.Customization.Counters.Add("document", 0); OnProgressEvent(step++, PASS_COUNT); // Simplify math blocks that begin with baseless scripts LogInfo("PreProcessLabels"); PreprocessLabels(Root.FindDocument().Expressions[0]); Root.Customization.Counters.Clear(); OnProgressEvent(step++, PASS_COUNT); // Deal with algorithmic blocks LogInfo("PreprocessAlgorithms"); PreprocessAlgorithms(Root.FindDocument().Expressions[0]); OnProgressEvent(step++, PASS_COUNT); LogInfo("Finished"); }
/// <summary> /// Performs the conversion procedure of math blocks. /// </summary> /// <param name="outline">The sequence of expressions to convert.</param> /// <param name="altText">The alternative text.</param> /// <param name="inline">Indicates whether the math block is inline.</param> /// <returns>The converted XML string.</returns> private static string CommonConvert(IList<LatexExpression> outline, string altText, bool inline, LatexMathToMathMLConverter customization) { if (outline.Count == 0) return ""; var bld = new StringBuilder(); AppendMathProlog(bld, altText, inline, customization); bld.Append("<mrow>\n"); bld.Append(SequenceConverter.ConvertOutline(outline, customization)); bld.Append("</mrow>\n"); AppendMathEpilog(bld); return bld.ToString(); }
public void ConvertXSquaredReturnXSquaredInMML() { String latexExpression = "$x^2$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter= new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertXSquaredReturnXSquaredInMMLEventListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""x^2"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <msup> <mrow> <mi>x</mi> </mrow> <mrow> <mn>2</mn> </mrow> </msup> </mrow> </math>"; Thread.Sleep(400); Console.WriteLine(result); Console.WriteLine(expected); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
public void ConvertSymbolsGreekLettersReturnsCorrectMML() { String latexExpression = @"$A, \alpha, B, \beta, \Gamma, \gamma, \Delta, \delta, E, \epsilon, \varepsilon, Z, \zeta, H, \eta, \Theta, \theta, \vartheta, I, \iota, K, \kappa, \varkappa, \Lambda, \lambda, M, \mu, N, \nu, \Xi, \xi, O, o, \Pi, \pi, \varpi, P, \rho, \varrho, \Sigma, \sigma, \varsigma, T, \tau, \Upsilon, \upsilon, \Phi, \phi, \varphi, X, \chi, \Psi, \psi, \Omega, \omega$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertSymbolsGreekLettersReturnsCorrectMMLListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""A, \alpha, B, \beta, \Gamma, \gamma, \Delta, \delta, E, \epsilon, \varepsilon, Z, \zeta, H, \eta, \Theta, \theta, \vartheta, I, \iota, K, \kappa, \varkappa, \Lambda, \lambda, M, \mu, N, \nu, \Xi, \xi, O, o, \Pi, \pi, \varpi, P, \rho, \varrho, \Sigma, \sigma, \varsigma, T, \tau, \Upsilon, \upsilon, \Phi, \phi, \varphi, X, \chi, \Psi, \psi, \Omega, \omega"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mi>A</mi> <mo>,</mo> <mi>α<!-- α --></mi><mo>,</mo> <mi>B</mi> <mo>,</mo> <mi>β<!-- β --></mi><mo>,</mo> <mi>Γ<!-- Γ --></mi><mo>,</mo> <mi>γ<!-- γ --></mi><mo>,</mo> <mi>Δ<!-- Δ --></mi><mo>,</mo> <mi>δ<!-- δ --></mi><mo>,</mo> <mi>E</mi> <mo>,</mo> <mi>ε<!-- ε --></mi><mo>,</mo> <mi>ε<!-- ε --></mi><mo>,</mo> <mi>Z</mi> <mo>,</mo> <mi>ζ<!-- ζ --></mi><mo>,</mo> <mi>H</mi> <mo>,</mo> <mi>η<!-- η --></mi><mo>,</mo> <mi>Θ<!-- Θ --></mi><mo>,</mo> <mi>θ<!-- θ --></mi><mo>,</mo> <mi>ϑ<!-- &theta --></mi><mo>,</mo> <mi>I</mi> <mo>,</mo> <mi>ι<!-- ι --></mi><mo>,</mo> <mi>K</mi> <mo>,</mo> <mi>κ<!-- κ --></mi><mo>,</mo> <mi>ϰ<!-- ϰ --></mi><mo>,</mo> <mi>Λ<!-- Λ --></mi><mo>,</mo> <mi>λ<!-- λ --></mi><mo>,</mo> <mi>M</mi> <mo>,</mo> <mi>μ<!-- μ --></mi><mo>,</mo> <mi>N</mi> <mo>,</mo> <mi>ν<!-- ν --></mi><mo>,</mo> <mi>Ξ<!-- Ξ --></mi><mo>,</mo> <mi>ξ<!-- ξ --></mi><mo>,</mo> <mi>O</mi> <mo>,</mo> <mi>o</mi> <mo>,</mo> <mi>Π<!-- Π --></mi><mo>,</mo> <mi>π<!-- π --></mi><mo>,</mo> <mi>ϖ<!-- ϖ --></mi><mo>,</mo> <mi>P</mi> <mo>,</mo> <mi>ρ<!-- ρ --></mi><mo>,</mo> <mi>ϱ<!--&rhov --></mi><mo>,</mo> <mi>Σ<!--&Sigma --></mi><mo>,</mo> <mi>σ<!-- σ --></mi><mo>,</mo> <mi>ς<!--ς --></mi><mo>,</mo> <mi>T</mi> <mo>,</mo> <mi>τ<!-- τ --></mi><mo>,</mo> <mi>Υ<!-- Υ --></mi><mo>,</mo> <mi>υ<!-- υ --></mi><mo>,</mo> <mi>Φ<!-- Φ --></mi><mo>,</mo> <mi>φ<!-- φ --></mi><mo>,</mo> <mi>ϕ<!-- ϕ --></mi><mo>,</mo> <mi>X</mi> <mo>,</mo> <mi>χ<!-- χ --></mi><mo>,</mo> <mi>Ψ<!-- Ψ --></mi><mo>,</mo> <mi>ψ<!-- ψ --></mi><mo>,</mo> <mi>Ω<!-- Ω --></mi><mo>,</mo> <mi>ω<!-- ω --></mi></mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
public void ConvertSymbolsForAllInQuadExistsLeqEpsilonLeGeqGeReturnsCorrectMML() { String latexExpression = @"$\forall x \in X, \quad \exists y \leq \epsilon \le \geq \ge$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertSymbolsForAllInQuadExistsLeqEpsilonReturnsCorrectMMLListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\forall x \in X, \quad \exists y \leq \epsilon \le \geq \ge"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mo>∀<!-- ∀ --></mo> <mi>x</mi> <mo>∈<!-- ∈ --></mo> <mi>X</mi> <mo>,</mo> <mspace width=""2em""/><mo>∃<!-- ∃ --></mo> <mi>y</mi> <mo>≤ <!-- leq --> </mo> <mi>ε<!-- ε --></mi><mo>≤ <!-- le --> </mo> <mo>≥ <!-- geq --> </mo> <mo>≥ <!-- ge --> </mo> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
public void ConvertOperatorsRelationShipOperatorsReturnsCorrectMML() { String latexExpression = @"$< <= \leq \ll \subset \subseteq \nsubseteq \sqsubset \sqsubseteq \preceq > >=\geq \gg \supset \supseteq \nsupseteq \sqsupset \sqsupseteq \succeq = \doteq \equiv \approx \cong \simeq \sim \propto \neq \parallel \asymp \vdash \in \smile \models \perp \prec \sphericalangle$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsRelationShipOperatorsReturnsCorrectMMLListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""&lt; &lt;= \leq \ll \subset \subseteq \nsubseteq \sqsubset \sqsubseteq \preceq &gt; &gt;=\geq \gg \supset \supseteq \nsupseteq \sqsupset \sqsupseteq \succeq = \doteq \equiv \approx \cong \simeq \sim \propto \neq \parallel \asymp \vdash \in \smile \models \perp \prec \sphericalangle"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mo><</mo> <mo>≤</mo> <mo>=</mo> <mo>≤ <!-- leq --> </mo> <mo>≪<!-- ≪ --></mo> <mo>⊂<!-- ⊂ --></mo> <mo>⊆<!-- ⊆ --></mo> <mo>⊈<!-- ⊈ --></mo> <mo>⊏<!-- ⊏ --></mo> <mo>⊑<!-- ⊑ --></mo> <mo>≼<!-- &cupre; --></mo> <mo>></mo> <mo>≥</mo> <mo>=</mo> <mo>≥ <!-- geq --> </mo> <mo>≫<!-- ≫ --></mo> <mo>⊃<!-- ⊃ --></mo> <mo>⊇<!-- ⊇ --></mo> <mo>⊉<!-- ⊉ --></mo> <mo>⊐<!-- ⊐ --></mo> <mo>⊒<!-- ⊒ --></mo> <mo>≽<!-- ≽ --></mo> <mo>=</mo> <mo>≐<!-- ≐ --></mo> <mo>≡<!-- equiv --></mo> <mo>≈<!-- ≈ --></mo> <mo>≅<!-- ≅ --></mo> <mo>≃<!-- ≃ --></mo> <mo>∼<!-- ∼ --></mo> <mo>∝<!-- ∝ --></mo> <mo>≠<!-- ≠ --></mo> <mo>⃦</mo> <mo>≍<!-- ≈ --></mo> <mo>⊢<!-- ⊢ --></mo> <mo>∈<!-- ∈ --></mo> <mo>⏝</mo> <mo>⊧<!-- ⊧ --></mo> <mo>⊥<!-- ⊥ --></mo> <mo>≺<!-- ≺ --></mo> <mo>∢<!-- ∢ --></mo> </mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }
/// <summary> /// Builds a new LatexExpression instance (main). /// </summary> /// <param name="parent">The parent of the builded expression.</param> /// <param name="parentChildNumber">Index of the parent child outline.</param> /// <param name="indexInParentChild">Index in the parent child outline.</param> /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param> /// <param name="beginning">The beginning string.</param> /// <param name="rdr">The reader to read ahead.</param> /// <param name="mathMode">The math mode switch.</param> /// <param name="end">The stub of the unparsed part.</param> /// <param name="whitespaceBefore">Indicates whether there was at least one whitespace char before the returned result.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> /// <returns>The parsed LatexExpression instance.</returns> private static LatexExpression ReadFromTextReaderInner(LatexExpression parent, int parentChildNumber, ref int indexInParentChild, ref bool verbatimMode, bool mathMode, LatexMathToMathMLConverter customization, string beginning, TextReader rdr, out string end, ref bool whitespaceBefore) { string name; #region Verbatim if (verbatimMode) { name = ReadToVerbatimEnd(beginning, rdr, out end); return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.Verbatim, false, customization, name, null, null); } #endregion bool whitespaceBeforeGuard; string str = ReadToNotEmptyString(beginning, rdr, out whitespaceBeforeGuard); whitespaceBefore |= whitespaceBeforeGuard; if (str == null) { end = null; return null; } string value; switch (str[0]) { case '\\': #region Command string options; string[] values; name = Regex.Match(str.Substring(1), @"^[a-zA-Z]+\*?").Value; if (String.IsNullOrEmpty(name)) { name = "" + str[1]; options = null; if (name != "[") { values = null; end = str.Substring(2); } else // Math { value = ParseBraces(ref str, rdr, "\\[", "\\]", out whitespaceBefore); end = str; return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.BlockMath, mathMode, customization, name, null, value); } } else { ParseCommandOptionsAndValues( str.Substring(name.Length + 1), rdr, out end, out options, out values, out whitespaceBefore); } return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.Command, mathMode, customization, name, options, values); #endregion case '$': #region Math ExpressionType type; if (str[1] == '$') { name = "$$"; value = ParseBraces(ref str, rdr, "$$", "$$", out whitespaceBefore); type = ExpressionType.BlockMath; } else { name = "$"; value = ParseBraces(ref str, rdr, "$", "$", out whitespaceBefore); type = ExpressionType.InlineMath; } end = str; return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, type, false, customization, name, null, value); #endregion case '%': #region Comment end = null; return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.Comment, false, customization, str.Substring(1), null, null); #endregion case '{': #region Block value = ParseBraces(ref str, rdr, "{", "}", out whitespaceBefore); end = str; return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.Block, mathMode, customization, "{}", null, value); #endregion case '&': #region Table cell end = str.Substring(1); return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.PlainText, mathMode, customization, "&", null, null); #endregion case '^': case '_': #region Block for sub and sup if (mathMode) { name = "" + str[0]; str = ReadToNotEmptyString(str.Substring(1), rdr, out whitespaceBefore); switch (str[0]) { case '{': value = ParseBraces(ref str, rdr, "{", "}", out whitespaceBefore); end = str; break; case '\\': var cmdName = Regex.Match(str.Substring(1), @"^[a-zA-Z]+\*?").Value; ParseCommandOptionsAndValues( str.Substring(cmdName.Length + 1), rdr, out end, out options, out values, out whitespaceBefore); value = "\\" + cmdName + options; if (values != null) { for (int i = 0; i < values.Length; i++) { value += "{" + values[i] + "}"; } } break; default: value = "" + str[0]; if (char.IsDigit(str[0])) { value = Regex.Match(str, @"\d+").Value; } if (char.IsLetter(str[0])) { value = Regex.Match(str, @"[a-zA-Z]+").Value; } end = str.Substring(value.Length); break; } return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.Block, true, customization, name, null, value); } goto default; #endregion default: #region Plain text block name = ""; var stopChars = mathMode ? new[] { '\\', '$', '{', '%', '&', '^', '_' } : new[] { '\\', '$', '{', '%', '&' }; int stopPos; while ((stopPos = GetStopPos(str, stopChars)) == -1) { if (rdr != null) { name += str + " "; str = rdr.ReadLine(); if (str == null) // End of the document { name = name.Substring(0, name.Length - 1); break; } if (str.Trim() == "") { str = "\\paragraph"; stopPos = 0; break; } } else { stopPos = str.Length; break; } } if (str == null) // End of the document? { end = null; } else { name += str.Substring(0, stopPos); if (stopPos < str.Length && (str[stopPos] == '^' || str[stopPos] == '_')) { var match = Regex.Match(name, @"[a-zA-Z]+\s*\Z"); var chunk = match.Value; if (chunk.Trim() == "") { match = Regex.Match(name, @"\d+\s*\Z"); chunk = match.Value; if (chunk.Trim() == "") { match = Regex.Match(name, @"\S\s*\Z"); chunk = match.Value.Trim(); } } if (name != chunk) { end = name.Substring(match.Index) + str.Substring(stopPos); name = name.Substring(0, match.Index); } else { end = str.Substring(stopPos); } } else { end = str.Substring(stopPos); } } return new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode, ExpressionType.PlainText, mathMode, customization, (whitespaceBefore? " " : "") + SmartTextTrim(name), null, null); #endregion } }
/// <summary> /// Recursively build scripts (for msup, munder, etc.) /// </summary> /// <param name="list">The outline of a LatexExpression instance.</param> /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param> private static void BuildScripts(List<LatexExpression> list, LatexMathToMathMLConverter customization) { for (int i = 0; i < list.Count; i++) { if (list[i].Expressions != null) { foreach (var subTree in list[i].Expressions) { BuildScripts(subTree, customization); } } if (list[i].ExprType == ExpressionType.Block && (list[i].Name == "^" || list[i].Name == "_") && i > 0) { if (list[i - 1].ExprType == ExpressionType.Command && list[i - 1].Name == "limits") { list[i - 1].EraseFromParent(); i--; } #region Place the previous expression to the script block if (i < list.Count - 1 && list[i + 1].ExprType == ExpressionType.Block && (list[i + 1].Name == "^" || list[i + 1].Name == "_")) { var block = new LatexExpression("script" + list[i].Name + list[i + 1].Name, list[i].Parent, list[i].ParentChildNumber, i - 1, customization); block.MathMode = list[i].MathMode; block.Expressions[0].Add(new LatexExpression(list[i - 1])); block.Expressions[0].Add(new LatexExpression(list[i])); block.Expressions[0].Add(new LatexExpression(list[i + 1])); block.Expressions[0][0].Parent = block; block.Expressions[0][0].ParentChildNumber = 0; block.Expressions[0][0].IndexInParentChild = 0; block.Expressions[0][0].MathMode = block.MathMode; block.Expressions[0][1].Parent = block; block.Expressions[0][1].ParentChildNumber = 0; block.Expressions[0][1].IndexInParentChild = 1; block.Expressions[0][1].MathMode = block.MathMode; block.Expressions[0][2].Parent = block; block.Expressions[0][2].ParentChildNumber = 0; block.Expressions[0][2].IndexInParentChild = 2; block.Expressions[0][2].MathMode = block.MathMode; list[i - 1] = block; list[i].EraseFromParent(); list[i].EraseFromParent(); } else { var block = new LatexExpression("script" + list[i].Name, list[i].Parent, list[i].ParentChildNumber, i - 1, customization); block.MathMode = list[i].MathMode; block.Expressions[0].Add(new LatexExpression(list[i - 1])); block.Expressions[0].Add(new LatexExpression(list[i])); block.Expressions[0][0].Parent = block; block.Expressions[0][0].ParentChildNumber = 0; block.Expressions[0][0].IndexInParentChild = 0; block.Expressions[0][0].MathMode = block.MathMode; block.Expressions[0][1].Parent = block; block.Expressions[0][1].ParentChildNumber = 0; block.Expressions[0][1].IndexInParentChild = 1; block.Expressions[0][1].MathMode = block.MathMode; list[i - 1] = block; list[i].EraseFromParent(); } i--; #endregion } } }
public void ConvertOperatorsSinCosReturnsSinCos() { String latexExpression = @"$\cos (2\theta) = \cos^2 \theta - \sin^2 \theta$"; String latexToConvert = begin + latexExpression + end; latex2MathMLConverter = new LatexMathToMathMLConverter(); latex2MathMLConverter.BeforeXmlFormat += ConvertFracXYPlus2ReturnsFracXYPlus2InMMLEventListener; latex2MathMLConverter.Convert(latexToConvert); String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\cos (2\theta) = \cos^2 \theta - \sin^2 \theta"" display=""inline"" class=""normalsize""> <mstyle displaystyle=""true"" /><mrow> <mi>cos</mi> <mfenced> <mrow> <mn>2</mn> <mi>θ<!-- θ --></mi></mrow> </mfenced> <mo>=</mo> <msup> <mrow> <mi>cos</mi> </mrow> <mrow> <mn>2</mn> </mrow> </msup> <mi>θ<!-- θ --></mi><mo>-</mo> <msup> <mrow> <mi>sin</mi> </mrow> <mrow> <mn>2</mn> </mrow> </msup> <mi>θ<!-- θ --></mi></mrow> </math>"; Thread.Sleep(400); NUnit.Framework.Assert.That(result, Is.EqualTo(expected)); }