diff --git a/DocX/Equation.cs b/DocX/Equation.cs new file mode 100644 index 00000000..1d4ecc60 --- /dev/null +++ b/DocX/Equation.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Novacode; + +namespace OMath +{ + public class Equation + { + private static XNamespace mathNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/math"; + private static XNamespace wordNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + + + private XElement xml; + public XElement Xml { get => xml; set => xml = value; } + + #region Constructors + + private Equation(XElement el) + { + Xml = new XElement(mathNamespace + "oMath", el); + } + public Equation() + { + Xml = new XElement(mathNamespace + "oMath"); + } + public Equation(string content) + { + Xml = new XElement(mathNamespace + "oMath", CreateLiteral(content)); + } + + #endregion + + + #region Operators + + public static implicit operator Equation(string content) + { + Equation eq = new Equation(); + eq.AppendElement(CreateLiteral(content)); + return eq; + } + public static Equation operator +(Equation eq1, Equation eq2) + { + Equation eq = new Equation(); + eq.Append(eq1); + eq.Append("+"); + eq.Append(eq2); + return eq; + } + public static Equation operator -(Equation eq1, Equation eq2) + { + Equation eq = new Equation(); + eq.Append(eq1); + eq.Append("-"); + eq.Append(eq2); + return eq; + } + public static Equation operator *(Equation eq1, Equation eq2) + { + Equation eq = new Equation(); + eq.Append(eq1); + eq.Append("*"); + eq.Append(eq2); + return eq; + } + public static Equation operator /(Equation eq1, Equation eq2) + { + return Equation.Fraction(eq1, eq2); + } + + #endregion + + #region Methods + /// + /// Append and xml element to the object xml + /// + /// + public void AppendElement(XElement content) + { + Xml.Add(content); + } + /// + /// Append an equation to the current equation + /// + /// Equation to append + public void Append(Equation content) + { + Xml.Add(content.Xml.Elements()); + } + #endregion + + #region Equation Creation + private static XElement CreateLiteral(object literal) + { + XElement lit = new XElement(mathNamespace + "r"); + XElement rPr = new XElement(wordNamespace + "rPr"); + XElement rFonts = new XElement(wordNamespace + "rFonts"); + + rFonts.Add(new XAttribute(wordNamespace + "ascii", "Cambria Math")); + rFonts.Add(new XAttribute(wordNamespace + "hAnsi", "Cambria Math")); + rPr.Add(rFonts); + lit.Add(rPr); + lit.Add(new XElement(mathNamespace + "t", literal)); + return lit; + } + + private static XElement CreateBox(object content) + { + XElement borderBox = new XElement(mathNamespace + "borderBox"); + borderBox.Add(new XElement(mathNamespace + "e", content)); + return borderBox; + } + /// + /// Creates a Box around the equation + /// + /// Content of the Box + /// + public static Equation Box(Equation content) + { + return new Equation(CreateBox(content.Xml.Elements())); + } + + #region Parenthesis + private static XElement CreateParenthesis(object content, char opening = '(', char closing = ')') + { + XElement parenthesis = new XElement(mathNamespace + "d"); + + if (opening != '(' || closing != ')') + { + XElement dPr = new XElement(mathNamespace + "dPr"); + XElement begChr = new XElement(mathNamespace + "begChr", new XAttribute(mathNamespace + "val", opening.ToString())); + XElement endChr = new XElement(mathNamespace + "endChr", new XAttribute(mathNamespace + "val", closing.ToString())); + dPr.Add(begChr); + dPr.Add(endChr); + parenthesis.Add(dPr); + } + XElement _content = new XElement(mathNamespace + "e", content); + parenthesis.Add(_content); + + return parenthesis; + } + /// + /// Creates parenthesis, brackets and other types of enclousures + /// + /// Equation inside the enclousure + /// Opening char for the enclousure. Default is '(' + /// Closing char for the enclousure. Default is ')' + /// + public static Equation Parenthesis(Equation content, char opening = '(', char closing = ')') + { + return new Equation(CreateParenthesis(content, opening, closing)); + } + #endregion + + #region Root + private static XElement CreateRoot(object radicand, object degree = null) + { + XElement rad = new XElement(mathNamespace + "rad"); + + XElement deg = new XElement(mathNamespace + "deg"); + if (degree == null) + { + XElement radProp = new XElement(mathNamespace + "radPr"); + XElement degHide = new XElement(mathNamespace + "degHide"); + degHide.Add(new XAttribute(mathNamespace + "val", 1)); + radProp.Add(degHide); + rad.Add(radProp); + } + else + { + deg.Add(degree); + } + rad.Add(deg); + + XElement content = new XElement(mathNamespace + "e", radicand); + //content.Add(new XElement(mathNamespace + "r", new XElement(mathNamespace + "t", radicand))); + rad.Add(content); + + return rad; + } + /// + /// Creates a n degree root + /// + /// Radicand of the root + /// Degree of the root + /// + public static Equation Root(Equation radicand, Equation degree) + { + Equation eq = new Equation(CreateRoot(radicand.Xml.Elements(), degree.Xml.Elements())); + return eq; + } + /// + /// Creates a square root + /// + /// Content of the square root + /// + public static Equation Root(Equation radicand) + { + Equation eq = new Equation(CreateRoot(radicand.Xml.Elements())); + return eq; + } + #endregion + + #region Fraction + private static XElement CreateFraction(object num, object den) + { + XElement numerator = new XElement(mathNamespace + "num", num); + + XElement denominator = new XElement(mathNamespace + "den", den); + + XElement fraction = new XElement(mathNamespace + "f"); + fraction.Add(numerator); + fraction.Add(denominator); + return fraction; + } + /// + /// Creates a fraction + /// + /// Numerator of the fraction + /// Denominator of the fraction + /// + public static Equation Fraction(Equation num, Equation den) + { + Equation eq = new Equation(); + eq.AppendElement(CreateFraction(num.Xml.Elements(), den.Xml.Elements())); + return eq; + } + #endregion + + #region SuperSubscripts + private static XElement CreateSuperscript(object content, object superior) + { + XElement sSup = new XElement(mathNamespace + "sSup"); + XElement e = new XElement(mathNamespace + "e", content); + XElement sup = new XElement(mathNamespace + "sup", superior); + sSup.Add(e); + sSup.Add(sup); + return sSup; + } + /// + /// Creates a superscript + /// + /// Base element + /// Expoent + /// + public static Equation Superscript(Equation content, Equation superior) + { + return new Equation(CreateSuperscript(content.Xml.Elements(), superior.Xml.Elements())); + } + + private static XElement CreateSubscript(object content, object inferior) + { + XElement sSub = new XElement(mathNamespace + "sSub"); + XElement e = new XElement(mathNamespace + "e", content); + XElement sub = new XElement(mathNamespace + "sub", inferior); + sSub.Add(e); + sSub.Add(sub); + return sSub; + } + /// + /// Creates a subscript + /// + /// Base element + /// subscript + /// + public static Equation Subscript(Equation content, Equation inferior) + { + return new Equation(CreateSubscript(content.Xml.Elements(), inferior.Xml.Elements())); + } + + private static XElement CreateSubSuperscript(object content, object inferior, object superior) + { + XElement sSubSup = new XElement(mathNamespace + "sSubSup"); + XElement e = new XElement(mathNamespace + "e", content); + XElement sub = new XElement(mathNamespace + "sub", inferior); + XElement sup = new XElement(mathNamespace + "sup", superior); + sSubSup.Add(e); + sSubSup.Add(sub); + sSubSup.Add(sup); + return sSubSup; + } + /// + /// Creates an element with a superscript and a subscript + /// + /// Base element + /// Subscript + /// Expoent + /// + public static Equation SubSuperscript(Equation content, Equation inferior, Equation superior) + { + return new Equation(CreateSubSuperscript(content.Xml.Elements(),inferior.Xml.Elements(), superior.Xml.Elements())); + } + + private static XElement CreatePrescript(object content, object inferior, object superior) + { + XElement sPre = new XElement(mathNamespace + "sPre"); + XElement e = new XElement(mathNamespace + "e", content); + XElement sub = new XElement(mathNamespace + "sub", inferior); + XElement sup = new XElement(mathNamespace + "sup", superior); + sPre.Add(sub); + sPre.Add(sup); + sPre.Add(e); + return sPre; + } + /// + /// Creates an element with a superscrit and a subscript before the base element + /// + /// Base element + /// Subscript + /// Superscript + /// + public static Equation Prescript(Equation content, Equation inferior, Equation superior) + { + return new Equation(CreateSubSuperscript(content.Xml.Elements(), inferior.Xml.Elements(), superior.Xml.Elements())); + } + #endregion + + #region Integral, Sums, Products + public enum IntegralPosition { Top, Front }; + public enum IntegralType { Simple, Double, Triple, Line, Surface, Volume, Sum, Product, Union, Intersection, Disjunction, Conjunction } + private static Dictionary integralTypeDictionary = new Dictionary() + { + {IntegralType.Simple,' ' }, + {IntegralType.Double,'∬' }, + {IntegralType.Triple,'∭' }, + {IntegralType.Line,'∮' }, + {IntegralType.Surface,'∯' }, + {IntegralType.Volume,'∰' }, + {IntegralType.Sum,'∑' }, + {IntegralType.Product,'∏' }, + {IntegralType.Union,'⋃' }, + {IntegralType.Intersection,'⋂' }, + {IntegralType.Disjunction,'⋁' }, + {IntegralType.Conjunction,'⋀' } + }; + private static XElement CreateIntegral(object content, object inferior = null, object superior = null, string position = "undOvr", char chr = ' ') + { + XElement nary = new XElement(mathNamespace + "nary"); + XElement naryPr = new XElement(mathNamespace + "naryPr"); + if (chr != ' ') + { + naryPr.Add( + new XElement( + mathNamespace + "chr", + new XAttribute( + mathNamespace + "val", + chr + ))); + } + naryPr.Add( + new XElement( + mathNamespace + "limLoc", + new XAttribute( + mathNamespace + "val", + position + ))); + if (inferior == null) + { + naryPr.Add( + new XElement( + mathNamespace + "subHide", + new XAttribute( + mathNamespace + "val", + "1" + ))); + + } + else + { + nary.Add(new XElement(mathNamespace + "sub", inferior)); + } + if (superior == null) + { + naryPr.Add( + new XElement( + mathNamespace + "supHide", + new XAttribute( + mathNamespace + "val", + "1" + ))); + } + else + { + nary.Add(new XElement(mathNamespace + "sup", superior)); + } + nary.Add(naryPr); + nary.Add(new XElement(mathNamespace + "e", content)); + return nary; + } + /// + /// Creates integrals, sums, products, union, intersection, disjuntion or cojunction + /// + /// Content of the equation + /// Bottom limit. To hide, use null. Default is null + /// Upper limit. To hide, use null. Default is null + /// Position of limits, could be Top for above and bellow the symbol, + /// or Front, to be in front of the symbol + /// Symbol for the equation. Default is Simple (Integral) + /// + public static Equation Integral(Equation content, Equation inferior=null, Equation superior=null, IntegralPosition position=IntegralPosition.Front, IntegralType it = IntegralType.Simple) + { + string pos = position == IntegralPosition.Top ? "undOvr" : "subSup"; + return new Equation(CreateIntegral(content.Xml.Elements(), inferior.Xml.Elements(), superior.Xml.Elements(), pos, integralTypeDictionary[it])); + } + #endregion + + #region Matrix + private static XElement CreateMatrix(object[,] content) + { + XElement m = new XElement(mathNamespace + "m"); + int columns = content.GetLength(1); + XElement mPr = new XElement(mathNamespace + "mPr", + new XElement(mathNamespace+"mcs", + new XElement(mathNamespace+"mc", + new XElement(mathNamespace+"mcPr", + new XElement(mathNamespace+"count", + new XAttribute(mathNamespace+"val",columns)), + new XElement(mathNamespace + "mcJc", + new XAttribute(mathNamespace + "val", "center") + ))))); + m.Add(mPr); + for(int row = 0; row < content.GetLength(0); row++) + { + XElement mr = new XElement(mathNamespace + "mr"); + for(int col = 0; col < columns; col++) + { + mr.Add(new XElement(mathNamespace + "e", content[row, col])); + } + m.Add(mr); + } + return m; + } + /// + /// Creates a matrix + /// + /// Bidimensional array with the elements of the matrix + /// + public static Equation Matrix(Equation[,] content) + { + object[,] mat = new object[content.GetLength(0), content.GetLength(1)]; + for(int r=0;r< content.GetLength(0); r++) + { + for (int c = 0; c < content.GetLength(1); c++) + { + mat[r, c] = content[r, c].Xml.Elements(); + } + } + return new Equation(CreateMatrix(mat)); + } + #endregion + + #region Enfasis + private static XElement CreateEnfasis(object content, char enf=' ') + { + XElement acc = new XElement(mathNamespace + "acc"); + if (enf!=' ') + { + XElement accPr = new XElement(mathNamespace + "accPr"); + accPr.Add(new XElement(mathNamespace + "chr", new XAttribute(mathNamespace + "val", enf))); + acc.Add(accPr); + } + acc.Add(new XElement(mathNamespace + "e", content)); + return acc; + } + /// + /// Creates a element with a char above + /// + /// The base element + /// The char above the base element + /// + public static Equation Enfasis(Equation content, char enf) + { + return new Equation(CreateEnfasis(content.Xml.Elements(), enf)); + } + #endregion + + #region Limit + public enum LimitPosition { Bottom, Top }; + private static XElement CreateLimit(object content, object limit, LimitPosition limitPosition=LimitPosition.Bottom) + { + string type = limitPosition == LimitPosition.Top ? "limUpp" : "limLow"; + XElement lim = new XElement(mathNamespace + type); + lim.Add(new XElement(mathNamespace + "e", content)); + lim.Add(new XElement(mathNamespace + "lim", limit)); + return lim; + } + /// + /// Creates an element with an element below in a smaller font + /// + /// Base element + /// Smaller font element + /// + public static Equation Limit(Equation content, Equation limit) + { + return new Equation(CreateLimit(content.Xml.Elements(), limit.Xml.Elements())); + } + /// + /// Creates an element with an element above or below in a smaller font + /// + /// Base element + /// Smaller font element + /// Position of the smaller element + /// + public static Equation Limit(Equation content, Equation limit, LimitPosition limitPosition) + { + return new Equation(CreateLimit(content.Xml.Elements(), limit.Xml.Elements(),limitPosition)); + } + #endregion + + #region Function + private static XElement CreateFunction(XElement name, object argument) + { + XElement func = new XElement(mathNamespace + "func"); + + foreach (var n in name.Descendants(mathNamespace + "r")) + { + XElement rPr = n.Element(mathNamespace + "rPr"); + if (rPr == null) + { + n.AddFirst(new XElement(mathNamespace + "rPr")); + rPr = n.Element(mathNamespace + "rPr"); + } + rPr.Add(new XElement(mathNamespace + "sty", new XAttribute(mathNamespace + "val", "p"))); + } + func.Add(new XElement(mathNamespace + "fName", name)); + + func.Add(new XElement(mathNamespace + "e", argument)); + return func; + } + private static XElement CreateFunction(IEnumerable name, object argument) + { + XElement func = new XElement(mathNamespace + "func"); + foreach(var nam in name) + { + foreach (var n in nam.Descendants(mathNamespace + "r")) + { + XElement rPr = n.Element(mathNamespace + "rPr"); + if (rPr == null) + { + n.AddFirst(new XElement(mathNamespace + "rPr")); + rPr = n.Element(mathNamespace + "rPr"); + } + rPr.Add(new XElement(mathNamespace + "sty", new XAttribute(mathNamespace + "val", "p"))); + } + } + func.Add(new XElement(mathNamespace + "fName", name)); + func.Add(new XElement(mathNamespace + "e", argument)); + return func; + } + /// + /// Creates a function with a font name in normal font + /// and an argument with math style font in front of the name element + /// + /// Normal font element + /// Math font element + /// + public static Equation Function(Equation name, Equation argument) + { + return new Equation(CreateFunction(name.Xml.Elements(), argument.Xml.Elements())); + } + #endregion + + #endregion + } +} diff --git a/DocX/ExtensionsEquations.cs b/DocX/ExtensionsEquations.cs new file mode 100644 index 00000000..1f4b3327 --- /dev/null +++ b/DocX/ExtensionsEquations.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Novacode; +using System.Xml.Linq; + +namespace OMath +{ + public static class ExtensionsEquations + { + public static Novacode.Paragraph InsertEquation(this DocX doc, Equation equation) + { + Paragraph eqParagraph = doc.InsertEquation(""); + XElement xml = eqParagraph.Xml; + XNamespace mathNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/math"; + XElement omath = xml.Descendants(mathNamespace + "oMathPara").First(); + omath.Elements().Remove(); + omath.Add(equation.Xml); + + return eqParagraph; + } + } +}