Example #1
0
        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);
            }
        }
Example #2
0
        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);
        }
Example #3
0
 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);
 }
Example #4
0
        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());
        }
Example #5
0
        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);
        }