--- /dev/null
+/****************************************************************************
+**
+** This file is part of a Qt Solutions component.
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+**
+****************************************************************************/
+
+#include <QtGui/QApplication>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+#include <QtGui/QDesktopWidget>
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEvent>
+
+#include "qtmmlwidget.h"
+
+// *******************************************************************
+// Declarations
+// *******************************************************************
+
+#define ROUND(a) (int)((a)+.5)
+
+static bool g_draw_frames = false;
+static const double g_mfrac_spacing = 0.1;
+static const double g_mroot_base_margin = 0.1;
+static const double g_script_size_multiplier = 0.7071; // sqrt(1/2)
+static const int g_min_font_point_size = 8;
+static const QChar g_radical_char = QChar(0x1A, 0x22);
+static const unsigned g_oper_spec_rows = 9;
+
+struct Mml
+{
+ enum NodeType {
+ NoNode = 0, MiNode, MnNode, MfracNode, MrowNode, MsqrtNode,
+ MrootNode, MsupNode, MsubNode, MsubsupNode, MoNode,
+ MstyleNode, TextNode, MphantomNode, MfencedNode,
+ MtableNode, MtrNode, MtdNode, MoverNode, MunderNode,
+ MunderoverNode, MerrorNode, MtextNode, MpaddedNode,
+ MspaceNode, MalignMarkNode, UnknownNode
+ };
+
+ enum MathVariant {
+ NormalMV = 0x0000,
+ BoldMV = 0x0001,
+ ItalicMV = 0x0002,
+ DoubleStruckMV = 0x0004,
+ ScriptMV = 0x0008,
+ FrakturMV = 0x0010,
+ SansSerifMV = 0x0020,
+ MonospaceMV = 0x0040
+ };
+
+ enum FormType { PrefixForm, InfixForm, PostfixForm };
+ enum ColAlign { ColAlignLeft, ColAlignCenter, ColAlignRight };
+ enum RowAlign { RowAlignTop, RowAlignCenter, RowAlignBottom,
+ RowAlignAxis, RowAlignBaseline };
+ enum FrameType { FrameNone, FrameSolid, FrameDashed };
+
+ struct FrameSpacing {
+ FrameSpacing(int hor = 0, int ver = 0)
+ : m_hor(hor), m_ver(ver) {}
+ int m_hor, m_ver;
+ };
+};
+
+struct OperSpec {
+ enum StretchDir { NoStretch, HStretch, VStretch, HVStretch };
+
+ const char *name;
+ Mml::FormType form;
+ const char *attributes[g_oper_spec_rows];
+ StretchDir stretch_dir;
+};
+
+struct NodeSpec
+{
+ Mml::NodeType type;
+ const char *tag;
+ const char *type_str;
+ int child_spec;
+ const char *child_types;
+ const char *attributes;
+
+ enum ChildSpec {
+ ChildAny = -1, // any number of children allowed
+ ChildIgnore = -2, // do not build subexpression of children
+ ImplicitMrow = -3 // if more than one child, build mrow
+ };
+};
+
+struct EntitySpec
+{
+ const char *name;
+ const char *value;
+};
+
+typedef QMap<QString, QString> MmlAttributeMap;
+class MmlNode;
+
+class MmlDocument : public Mml
+{
+ public:
+ MmlDocument();
+ ~MmlDocument();
+ void clear();
+
+ bool setContent(QString text, QString *errorMsg = 0,
+ int *errorLine = 0, int *errorColumn = 0);
+ void paint(QPainter *p, const QPoint &pos) const;
+ void dump() const;
+ QSize size() const;
+ void layout();
+
+ QString fontName(QtMmlWidget::MmlFont type) const;
+ void setFontName(QtMmlWidget::MmlFont type, const QString &name);
+
+ int baseFontPointSize() const
+ { return m_base_font_point_size; }
+ void setBaseFontPointSize(int size)
+ { m_base_font_point_size = size; }
+ QColor foregroundColor() const
+ { return m_foreground_color; }
+ void setForegroundColor(const QColor &color)
+ { m_foreground_color = color; }
+ QColor backgroundColor() const
+ { return m_background_color; }
+ void setBackgroundColor(const QColor &color)
+ { m_background_color = color; }
+
+ private:
+ void _dump(const MmlNode *node, QString &indent) const;
+ bool insertChild(MmlNode *parent, MmlNode *new_node, QString *errorMsg);
+
+ MmlNode *domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg);
+ MmlNode *createNode(NodeType type, const MmlAttributeMap &mml_attr,
+ const QString &mml_value, QString *errorMsg);
+ MmlNode *createImplicitMrowNode(const QDomNode &dom_node, bool *ok,
+ QString *errorMsg);
+
+ void insertOperator(MmlNode *node, const QString &text);
+
+ MmlNode *m_root_node;
+
+ QString m_normal_font_name;
+ QString m_fraktur_font_name;
+ QString m_sans_serif_font_name;
+ QString m_script_font_name;
+ QString m_monospace_font_name;
+ QString m_doublestruck_font_name;
+ int m_base_font_point_size;
+ QColor m_foreground_color;
+ QColor m_background_color;
+};
+
+class MmlNode : public Mml
+{
+ friend class MmlDocument;
+
+ public:
+ MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map);
+ virtual ~MmlNode();
+
+ // Mml stuff
+ NodeType nodeType() const
+ { return m_node_type; }
+
+ virtual QString toStr() const;
+
+ void setRelOrigin(const QPoint &rel_origin);
+ QPoint relOrigin() const
+ { return m_rel_origin; }
+ void stretchTo(const QRect &rect);
+ bool isStretched() const
+ { return m_stretched; }
+ QPoint devicePoint(const QPoint &p) const;
+
+ QRect myRect() const
+ { return m_my_rect; }
+ QRect parentRect() const;
+ virtual QRect deviceRect() const;
+ void updateMyRect();
+ virtual void setMyRect(const QRect &rect)
+ { m_my_rect = rect; }
+
+ virtual void stretch();
+ virtual void layout();
+ virtual void paint(QPainter *p);
+
+ int basePos() const;
+ int overlinePos() const;
+ int underlinePos() const;
+ int em() const;
+ int ex() const;
+
+ QString explicitAttribute(const QString &name, const QString &def = QString::null) const;
+ QString inheritAttributeFromMrow(const QString &name, const QString &def = QString::null) const;
+
+ virtual QFont font() const;
+ virtual QColor color() const;
+ virtual QColor background() const;
+ virtual int scriptlevel(const MmlNode *child = 0) const;
+
+
+ // Node stuff
+ MmlDocument *document() const
+ { return m_document; }
+ MmlNode *parent() const
+ { return m_parent; }
+ MmlNode *firstChild() const
+ { return m_first_child; }
+ MmlNode *nextSibling() const
+ { return m_next_sibling; }
+ MmlNode *previousSibling() const
+ { return m_previous_sibling; }
+ MmlNode *lastSibling() const;
+ MmlNode *firstSibling() const;
+ bool isLastSibling() const
+ { return m_next_sibling == 0; }
+ bool isFirstSibling() const
+ { return m_previous_sibling == 0; }
+ bool hasChildNodes() const
+ { return m_first_child != 0; }
+
+ protected:
+ virtual void layoutSymbol();
+ virtual void paintSymbol(QPainter *p) const;
+ virtual QRect symbolRect() const
+ { return QRect(0, 0, 0, 0); }
+
+ MmlNode *parentWithExplicitAttribute(const QString &name, NodeType type = NoNode);
+ int interpretSpacing(const QString &value, bool *ok) const;
+
+ private:
+ MmlAttributeMap m_attribute_map;
+ bool m_stretched;
+ QRect m_my_rect, m_parent_rect;
+ QPoint m_rel_origin;
+
+ NodeType m_node_type;
+ MmlDocument *m_document;
+
+ MmlNode *m_parent,
+ *m_first_child,
+ *m_next_sibling,
+ *m_previous_sibling;
+};
+
+class MmlTokenNode : public MmlNode
+{
+ public:
+ MmlTokenNode(NodeType type, MmlDocument *document,
+ const MmlAttributeMap &attribute_map)
+ : MmlNode(type, document, attribute_map) {}
+
+ QString text() const;
+};
+
+class MmlMphantomNode : public MmlNode
+{
+ public:
+ MmlMphantomNode(MmlDocument *document,
+ const MmlAttributeMap &attribute_map)
+ : MmlNode(MphantomNode, document, attribute_map) {}
+
+ virtual void paint(QPainter *) {}
+};
+
+class MmlUnknownNode : public MmlNode
+{
+ public:
+ MmlUnknownNode(MmlDocument *document,
+ const MmlAttributeMap &attribute_map)
+ : MmlNode(UnknownNode, document, attribute_map) {}
+};
+
+class MmlMfencedNode : public MmlNode
+{
+ public:
+ MmlMfencedNode(MmlDocument *document,
+ const MmlAttributeMap &attribute_map)
+ : MmlNode(MfencedNode, document, attribute_map) {}
+};
+
+class MmlMalignMarkNode : public MmlNode
+{
+ public:
+ MmlMalignMarkNode(MmlDocument *document)
+ : MmlNode(MalignMarkNode, document, MmlAttributeMap()) {}
+};
+
+class MmlMfracNode : public MmlNode
+{
+ public:
+ MmlMfracNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MfracNode, document, attribute_map) {}
+
+ MmlNode *numerator() const;
+ MmlNode *denominator() const;
+
+ protected:
+ virtual void layoutSymbol();
+ virtual void paintSymbol(QPainter *p) const;
+ virtual QRect symbolRect() const;
+};
+
+class MmlMrowNode : public MmlNode
+{
+ public:
+ MmlMrowNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MrowNode, document, attribute_map) {}
+};
+
+class MmlRootBaseNode : public MmlNode
+{
+ public:
+ MmlRootBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(type, document, attribute_map) {}
+
+ MmlNode *base() const;
+ MmlNode *index() const;
+
+ virtual int scriptlevel(const MmlNode *child = 0) const;
+
+ protected:
+ virtual void layoutSymbol();
+ virtual void paintSymbol(QPainter *p) const;
+ virtual QRect symbolRect() const;
+ int tailWidth() const;
+};
+
+class MmlMrootNode : public MmlRootBaseNode
+{
+ public:
+ MmlMrootNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlRootBaseNode(MrootNode, document, attribute_map) {}
+};
+
+class MmlMsqrtNode : public MmlRootBaseNode
+{
+ public:
+ MmlMsqrtNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlRootBaseNode(MsqrtNode, document, attribute_map) {}
+
+};
+
+
+class MmlTextNode : public MmlNode
+{
+ public:
+ MmlTextNode(const QString &text, MmlDocument *document);
+
+ virtual QString toStr() const;
+ QString text() const
+ { return m_text; }
+
+ // TextNodes are not xml elements, so they can't have attributes of
+ // their own. Everything is taken from the parent.
+ virtual QFont font() const
+ { return parent()->font(); }
+ virtual int scriptlevel(const MmlNode* = 0) const
+ { return parent()->scriptlevel(this); }
+ virtual QColor color() const
+ { return parent()->color(); }
+ virtual QColor background() const
+ { return parent()->background(); }
+
+ protected:
+ virtual void paintSymbol(QPainter *p) const;
+ virtual QRect symbolRect() const;
+
+ QString m_text;
+};
+
+class MmlMiNode : public MmlTokenNode
+{
+ public:
+ MmlMiNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTokenNode(MiNode, document, attribute_map) {}
+};
+
+class MmlMnNode : public MmlTokenNode
+{
+ public:
+ MmlMnNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTokenNode(MnNode, document, attribute_map) {}
+};
+
+class MmlSubsupBaseNode : public MmlNode
+{
+ public:
+ MmlSubsupBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(type, document, attribute_map) {}
+
+ MmlNode *base() const;
+ MmlNode *sscript() const;
+
+ virtual int scriptlevel(const MmlNode *child = 0) const;
+};
+
+class MmlMsupNode : public MmlSubsupBaseNode
+{
+ public:
+ MmlMsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlSubsupBaseNode(MsupNode, document, attribute_map) {}
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMsubNode : public MmlSubsupBaseNode
+{
+ public:
+ MmlMsubNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlSubsupBaseNode(MsubNode, document, attribute_map) {}
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMsubsupNode : public MmlNode
+{
+ public:
+ MmlMsubsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MsubsupNode, document, attribute_map) {}
+
+ MmlNode *base() const;
+ MmlNode *superscript() const;
+ MmlNode *subscript() const;
+
+ virtual int scriptlevel(const MmlNode *child = 0) const;
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMoNode : public MmlTokenNode
+{
+ public:
+ MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map);
+
+ QString dictionaryAttribute(const QString &name) const;
+ virtual void stretch();
+ virtual int lspace() const;
+ virtual int rspace() const;
+
+ virtual QString toStr() const;
+
+ protected:
+ virtual void layoutSymbol();
+ virtual QRect symbolRect() const;
+
+ virtual FormType form() const;
+
+ private:
+ const OperSpec *m_oper_spec;
+};
+
+class MmlMstyleNode : public MmlNode
+{
+ public:
+ MmlMstyleNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MstyleNode, document, attribute_map) {}
+};
+
+class MmlTableBaseNode : public MmlNode
+{
+ public:
+ MmlTableBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(type, document, attribute_map) {}
+};
+
+class MmlMtableNode : public MmlTableBaseNode
+{
+ public:
+ MmlMtableNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTableBaseNode(MtableNode, document, attribute_map) {}
+
+ int rowspacing() const;
+ int columnspacing() const;
+ int framespacing_hor() const;
+ int framespacing_ver() const;
+ FrameType frame() const;
+ FrameType columnlines(int idx) const;
+ FrameType rowlines(int idx) const;
+
+ protected:
+ virtual void layoutSymbol();
+ virtual QRect symbolRect() const;
+ virtual void paintSymbol(QPainter *p) const;
+
+ private:
+ struct CellSizeData
+ {
+ void init(const MmlNode *first_row);
+ QList<int> col_widths, row_heights;
+ int numCols() const { return col_widths.count(); }
+ int numRows() const { return row_heights.count(); }
+ uint colWidthSum() const;
+ uint rowHeightSum() const;
+ };
+
+ CellSizeData m_cell_size_data;
+ int m_content_width, m_content_height;
+};
+
+class MmlMtrNode : public MmlTableBaseNode
+{
+ public:
+ MmlMtrNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTableBaseNode(MtrNode, document, attribute_map) {}
+ void layoutCells(const QList<int> &col_widths, int col_spc);
+};
+
+class MmlMtdNode : public MmlTableBaseNode
+{
+ public:
+ MmlMtdNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTableBaseNode(MtdNode, document, attribute_map)
+ { m_scriptlevel_adjust = 0; }
+ virtual void setMyRect(const QRect &rect);
+
+ ColAlign columnalign();
+ RowAlign rowalign();
+ uint colNum();
+ uint rowNum();
+ virtual int scriptlevel(const MmlNode *child = 0) const;
+
+ private:
+ int m_scriptlevel_adjust; // added or subtracted to scriptlevel to
+ // make contents fit the cell
+};
+
+class MmlMoverNode : public MmlNode
+{
+ public:
+ MmlMoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MoverNode, document, attribute_map) {}
+ virtual int scriptlevel(const MmlNode *node = 0) const;
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMunderNode : public MmlNode
+{
+ public:
+ MmlMunderNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MunderNode, document, attribute_map) {}
+ virtual int scriptlevel(const MmlNode *node = 0) const;
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMunderoverNode : public MmlNode
+{
+ public:
+ MmlMunderoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MunderoverNode, document, attribute_map) {}
+ virtual int scriptlevel(const MmlNode *node = 0) const;
+
+ protected:
+ virtual void layoutSymbol();
+};
+
+class MmlMerrorNode : public MmlNode
+{
+ public:
+ MmlMerrorNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MerrorNode, document, attribute_map) {}
+};
+
+class MmlMtextNode : public MmlNode
+{
+ public:
+ MmlMtextNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MtextNode, document, attribute_map) {}
+};
+
+class MmlMpaddedNode : public MmlNode
+{
+ public:
+ MmlMpaddedNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MpaddedNode, document, attribute_map) {}
+
+ public:
+ int lspace() const;
+ int width() const;
+ int height() const;
+ int depth() const;
+
+ protected:
+ int interpretSpacing(QString value, int base_value, bool *ok) const;
+ virtual void layoutSymbol();
+ virtual QRect symbolRect() const;
+};
+
+class MmlMspaceNode : public MmlNode
+{
+ public:
+ MmlMspaceNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlNode(MspaceNode, document, attribute_map) {}
+};
+
+static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type);
+static const NodeSpec *mmlFindNodeSpec(const QString &tag);
+static bool mmlCheckChildType(Mml::NodeType parent_type,
+ Mml::NodeType child_type, QString *error_str);
+static bool mmlCheckAttributes(Mml::NodeType child_type,
+ const MmlAttributeMap &attr, QString *error_str);
+static QString mmlDictAttribute(const QString &name, const OperSpec *spec);
+static const OperSpec *mmlFindOperSpec(const QString &name, Mml::FormType form);
+static int interpretSpacing(QString name, int em, int ex, bool *ok);
+static int interpretPercentSpacing(QString value, int base, bool *ok);
+static uint interpretMathVariant(const QString &value, bool *ok);
+static Mml::FormType interpretForm(const QString &value, bool *ok);
+static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok);
+static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok);
+static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok);
+static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok);
+static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok);
+static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex);
+static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok);
+static QString interpretListAttr(const QString &value_list, int idx, const QString &def);
+static QString rectToStr(const QRect &rect);
+static QString entityDeclarations();
+
+
+#define MML_ATT_COMMON " class style id xref actiontype "
+#define MML_ATT_FONTSIZE " fontsize fontweight fontstyle fontfamily color "
+#define MML_ATT_MATHVARIANT " mathvariant mathsize mathcolor mathbackground "
+#define MML_ATT_FONTINFO MML_ATT_FONTSIZE MML_ATT_MATHVARIANT
+#define MML_ATT_OPINFO " form fence separator lspace rspace stretchy symmetric " \
+ " maxsize minsize largeop movablelimits accent "
+#define MML_ATT_SIZEINFO " width height depth "
+#define MML_ATT_TABLEINFO " align rowalign columnalign columnwidth groupalign " \
+ " alignmentscope side rowspacing columnspacing rowlines " \
+ " columnlines width frame framespacing equalrows " \
+ " equalcolumns displaystyle "
+#define MML_ATT_MFRAC " bevelled numalign denomalign linethickness "
+#define MML_ATT_MSTYLE MML_ATT_FONTINFO MML_ATT_OPINFO \
+ " scriptlevel lquote rquote linethickness displaystyle " \
+ " scriptsizemultiplier scriptminsize background " \
+ " veryverythinmathspace verythinmathspace thinmathspace " \
+ " mediummathspace thickmathspace verythickmathspace " \
+ " veryverythickmathspace open close separators " \
+ " subscriptshift superscriptshift accentunder tableinfo " \
+ " rowspan columnspan edge selection bevelled "
+#define MML_ATT_MTABLE " align rowalign columnalign groupalign alignmentscope " \
+ " columnwidth width rowspacing columnspacing rowlines columnlines " \
+ " frame framespacing equalrows equalcolumns displaystyle side " \
+ " minlabelspacing "
+
+static const NodeSpec g_node_spec_data[] = {
+
+// type tag type_str child_spec child_types attributes ""=none, 0=any
+// ----------------------- --------------- ------------------- ----------------------- ------------------------ ------------------------------------
+ { Mml::MiNode, "mi", "MiNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO },
+ { Mml::MnNode, "mn", "MnNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO },
+ { Mml::MfracNode, "mfrac", "MfracNode", 2, 0, MML_ATT_COMMON MML_ATT_MFRAC },
+ { Mml::MrowNode, "mrow", "MrowNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " display mode " },
+ { Mml::MsqrtNode, "msqrt", "MsqrtNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
+ { Mml::MrootNode, "mroot", "MrootNode", 2, 0, MML_ATT_COMMON },
+ { Mml::MsupNode, "msup", "MsupNode", 2, 0, MML_ATT_COMMON " subscriptshift " },
+ { Mml::MsubNode, "msub", "MsubNode", 2, 0, MML_ATT_COMMON " superscriptshift " },
+ { Mml::MsubsupNode, "msubsup", "MsubsupNode", 3, 0, MML_ATT_COMMON " subscriptshift superscriptshift " },
+ { Mml::MoNode, "mo", "MoNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO MML_ATT_OPINFO },
+ { Mml::MstyleNode, "mstyle", "MstyleNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON MML_ATT_MSTYLE },
+ { Mml::MphantomNode, "mphantom", "MphantomNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
+ { Mml::MalignMarkNode, "malignmark", "MalignMarkNode", 0, 0, "" },
+ { Mml::MfencedNode, "mfenced", "MfencedNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " open close separators " },
+ { Mml::MtableNode, "mtable", "MtableNode", NodeSpec::ChildAny, " MtrNode ", MML_ATT_COMMON MML_ATT_MTABLE },
+ { Mml::MtrNode, "mtr", "MtrNode", NodeSpec::ChildAny, " MtdNode ", MML_ATT_COMMON " rowalign columnalign groupalign " },
+ { Mml::MtdNode, "mtd", "MtdNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " rowspan columnspan rowalign columnalign groupalign " },
+ { Mml::MoverNode, "mover", "MoverNode", 2, 0, MML_ATT_COMMON " accent " },
+ { Mml::MunderNode, "munder", "MunderNode", 2, 0, MML_ATT_COMMON " accentunder " },
+ { Mml::MunderoverNode, "munderover", "MunderoverNode", 3, 0, MML_ATT_COMMON " accentunder accent " },
+ { Mml::MerrorNode, "merror", "MerrorNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
+ { Mml::MtextNode, "mtext", "MtextNode", 1, " TextNode ", MML_ATT_COMMON " width height depth linebreak " },
+ { Mml::MpaddedNode, "mpadded", "MpaddedNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth lspace " },
+ { Mml::MspaceNode, "mspace", "MspaceNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth linebreak " },
+ { Mml::TextNode, 0, "TextNode", NodeSpec::ChildIgnore, 0, "" },
+ { Mml::UnknownNode, 0, "UnknownNode", NodeSpec::ChildAny, 0, 0 },
+ { Mml::NoNode, 0, 0, 0, 0, 0 }
+};
+
+static const char *g_oper_spec_names[g_oper_spec_rows] = { "accent", "fence", "largeop", "lspace", "minsize", "movablelimits", "rspace", "separator", "stretchy" /* stretchdir */ };
+static const OperSpec g_oper_spec_data[] = {
+
+ { "!!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!!"
+ { "!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!"
+ { "!=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "!="
+ { "⩓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩓"
+ { "⁡" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁡"
+ { "≔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≔"
+ { "∖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "∖"
+ { "∵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∵"
+ { "˘" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˘"
+ { "⋒" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋒"
+ { "ⅅ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // ⅅ"
+ { "¸" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¸"
+ { "·" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "·"
+ { "⊙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊙"
+ { "⊖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊖"
+ { "⊕" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊕"
+ { "⊗" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊗"
+ { "∲" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true"}, OperSpec::VStretch }, // ∲"
+ { "”" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ”"
+ { "’" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "’"
+ { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷"
+ { "≡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≡"
+ { "∮" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "∮"
+ { "∐" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∐"
+ { "∳", Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // &CounterClockwiseContourInteg
+ { "⨯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⨯"
+ { "⋓" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋓"
+ { "≍" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≍"
+ { "∇" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∇"
+ { "´" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "´"
+ { "˙" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˙"
+ { "˝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ˝"
+ { "`" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "`"
+ { "&DiacriticalLeftArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftArrow;"
+ { "&DiacriticalLeftRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightArrow;"
+ { "&DiacriticalLeftRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightVector;"
+ { "&DiacriticalLeftVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftVector;"
+ { "&DiacriticalRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightArrow;"
+ { "&DiacriticalRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightVector;"
+ { "˜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "˜"
+ { "⋄" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋄"
+ { "ⅆ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "ⅆ"
+ { "≐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≐"
+ { "∯" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ∯"
+ { "¨" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¨"
+ { "⇓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇓"
+ { "⇐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇐"
+ { "⇔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // ⇔"
+ { "⫤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫤"
+ { "⟸" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟸"
+ { "⟺" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟺"
+ { "⟹" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟹"
+ { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒"
+ { "⊨" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊨"
+ { "⇑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇑"
+ { "⇕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇕"
+ { "∥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∥"
+ { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↓"
+ { "⤓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤓"
+ { "⇵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇵"
+ { "̑" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "̑"
+ { "⥐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥐"
+ { "⥞" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥞"
+ { "↽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↽"
+ { "⥖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥖"
+ { "⥟" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥟"
+ { "⇁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇁"
+ { "⥗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥗"
+ { "⊤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊤"
+ { "↧" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↧"
+ { "∈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∈"
+ { "⩵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩵"
+ { "≂" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂"
+ { "⇌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇌"
+ { "∃" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∃"
+ { "∀" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∀"
+ { "≥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≥"
+ { "⋛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋛"
+ { "≧" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧"
+ { "⪢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪢"
+ { "≷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≷"
+ { "⩾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩾"
+ { "≳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≳"
+ { "ˇ" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "ˇ"
+ { "^" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "^"
+ { "─" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::HStretch }, // "─"
+ { "≎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎"
+ { "≏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏"
+ { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒"
+ { "∫" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "∫"
+ { "⋂" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋂"
+ { "⁣" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // "⁣"
+ { "⁢" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁢"
+ { "⟨" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟨"
+ { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "←"
+ { "⇤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇤"
+ { "⇆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇆"
+ { "&LeftBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&LeftBracketingBar;"
+ { "⌈" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌈"
+ { "⟦" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⟦"
+ { "&LeftDoubleBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &LeftDoubleBracketingBar;"
+ { "⥡" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥡"
+ { "⇃" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇃"
+ { "⥙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥙"
+ { "⌊" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌊"
+ { "↔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↔"
+ { "⥎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥎"
+ { "&LeftSkeleton;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&LeftSkeleton;"
+ { "⊣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊣"
+ { "↤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↤"
+ { "⥚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥚"
+ { "⊲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊲"
+ { "⧏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏"
+ { "⊴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊴"
+ { "⥑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥑"
+ { "⥠" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥠"
+ { "↿" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↿"
+ { "⥘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥘"
+ { "↼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↼"
+ { "⥒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "⥒"
+ { "⋚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋚"
+ { "≦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≦"
+ { "≶" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≶"
+ { "⪡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡"
+ { "⩽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽"
+ { "≲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≲"
+ { "⟵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟵"
+ { "⟷" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟷"
+ { "⟶" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟶"
+ { "↙" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↙"
+ { "↘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↘"
+ { "∓" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∓"
+ { "≫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ≫"
+ { "≪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪"
+ { "⫬" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫬"
+ { "≢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≢"
+ { "≭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≭"
+ { "∦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ∦"
+ { "∉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∉"
+ { "≠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≠"
+ { "≂̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂̸"
+ { "∄" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∄"
+ { "≯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≯"
+ { "≱" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≱"
+ { "≧̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧̸"
+ { "≫̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≫̸"
+ { "≹" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≹"
+ { "⩾̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⩾̸"
+ { "≵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≵"
+ { "≎̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎̸"
+ { "≏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏̸"
+ { "⋪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋪"
+ { "⧏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏̸"
+ { "⋬" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋬"
+ { "≮" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≮"
+ { "≰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≰"
+ { "&NotLessFullEqual;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotLessFullEqual;"
+ { "≸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≸"
+ { "≪̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪̸"
+ { "⩽̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽̸"
+ { "≴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≴"
+ { "⪢̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⪢̸"
+ { "⪡̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡̸"
+ { "⊀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊀"
+ { "⪯̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯̸"
+ { "⋠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋠"
+ { "&NotPrecedesTilde;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotPrecedesTilde;"
+ { "∌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∌"
+ { "⋫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋫"
+ { "⧐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐̸"
+ { "⋭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋭"
+ { "⊏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏̸"
+ { "⋢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋢"
+ { "⊐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐̸"
+ { "⋣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋣"
+ { "⊂⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊂⃒"
+ { "⊈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊈"
+ { "⊁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊁"
+ { "⪰̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰̸"
+ { "⋡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋡"
+ { "≿̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿̸"
+ { "⊃⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃⃒"
+ { "⊉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊉"
+ { "≁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≁"
+ { "≄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≄"
+ { "≇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≇"
+ { "≉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≉"
+ { "∤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∤"
+ { "“" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // “"
+ { "‘" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "‘"
+ { "⩔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩔"
+ { "‾" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "‾"
+ { "⏞" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏞"
+ { "⎴" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎴"
+ { "⏜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⏜"
+ { "∂" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∂"
+ { "±" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "±"
+ { "≺" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≺"
+ { "⪯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯"
+ { "≼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≼"
+ { "≾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≾"
+ { "∏" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∏"
+ { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷"
+ { "∝" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∝"
+ { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋"
+ { "⇋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇋"
+ { "⥯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⥯"
+ { "⟩" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟩"
+ { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "→"
+ { "⇥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇥"
+ { "⇄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇄"
+ { "&RightBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&RightBracketingBar;"
+ { "⌉" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌉"
+ { "⟧" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟧"
+ { "&RightDoubleBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &RightDoubleBracketingBar;"
+ { "⥝" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥝"
+ { "⇂" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇂"
+ { "⥕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥕"
+ { "⌋" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌋"
+ { "&RightSkeleton;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&RightSkeleton;"
+ { "⊢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊢"
+ { "↦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↦"
+ { "⥛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥛"
+ { "⊳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊳"
+ { "⧐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐"
+ { "⊵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊵"
+ { "⥏" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥏"
+ { "⥜" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥜"
+ { "↾" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↾"
+ { "⥔" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥔"
+ { "⇀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇀"
+ { "⥓" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥓"
+ { "⥰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⥰"
+ { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "↓"
+ { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "←"
+ { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "→"
+ { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::VStretch }, // "↑"
+ { "∘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∘"
+ { "√" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "√"
+ { "□" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "□"
+ { "⊓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊓"
+ { "⊏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏"
+ { "⊑" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊑"
+ { "⊐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐"
+ { "⊒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊒"
+ { "⊔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊔"
+ { "⋆" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋆"
+ { "⋐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋐"
+ { "⊆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊆"
+ { "≻" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≻"
+ { "⪰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰"
+ { "≽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≽"
+ { "≿" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿"
+ { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋"
+ { "∑" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∑"
+ { "⊃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃"
+ { "⊇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊇"
+ { "∴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∴"
+ { "∼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∼"
+ { "≃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≃"
+ { "≅" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≅"
+ { "≈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≈"
+ { "⃛" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⃛"
+ { "_" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "_"
+ { "⏟" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏟"
+ { "⎵" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎵"
+ { "⏝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏝"
+ { "⋃" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋃"
+ { "⊎" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⊎"
+ { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↑"
+ { "⤒" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤒"
+ { "⇅" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇅"
+ { "↕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↕"
+ { "⥮" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥮"
+ { "⊥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊥"
+ { "↥" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↥"
+ { "↖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↖"
+ { "↗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↗"
+ { "⋁" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋁"
+ { "∣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∣"
+ { "|" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::VStretch }, // "|"
+ { "❘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "❘"
+ { "≀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "≀"
+ { "⋀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋀"
+ { "&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&"
+ { "&&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&&"
+ { "≤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≤"
+ { "<" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<"
+ { "<=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<="
+ { "<>" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "<>"
+ { "'" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "'"
+ { "(" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "("
+ { ")" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ")"
+ { "*" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "*"
+ { "**" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "**"
+ { "*=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "*="
+ { "+" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+"
+ { "+" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "+"
+ { "++" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "++"
+ { "+=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+="
+ { "," , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // ","
+ { "-" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-"
+ { "-" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "-"
+ { "--" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "--"
+ { "-=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-="
+ { "->" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "->"
+ { "." , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "."
+ { ".." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ".."
+ { "..." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "..."
+ { "/" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::VStretch }, // "/"
+ { "//" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "//"
+ { "/=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "/="
+ { ":" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":"
+ { ":=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":="
+ { ";" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // ";"
+ { ";" , Mml::PostfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // ";"
+ { "=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "="
+ { "==" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "=="
+ { ">" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">"
+ { ">=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">="
+ { "?" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "?"
+ { "@" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "@"
+ { "[" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "["
+ { "]" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "]"
+ { "^" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "^"
+ { "_" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "_"
+ { "lim" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "lim"
+ { "max" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "max"
+ { "min" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "min"
+ { "{" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "{"
+ { "|" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "|"
+ { "||" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, 0 }, OperSpec::NoStretch }, // "||"
+ { "}" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "}"
+ { "~" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "~"
+
+ { 0 , Mml::InfixForm, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, OperSpec::NoStretch }
+};
+
+static const OperSpec g_oper_spec_defaults =
+ { 0 , Mml::InfixForm, { "false", "false", "false", "thickmathspace", "1", "false", "thickmathspace", "false", "false" }, OperSpec::NoStretch };
+
+static const uint g_oper_spec_count = sizeof(g_oper_spec_data)/sizeof(OperSpec) - 1;
+
+static const EntitySpec g_xml_entity_data[] = {
+ { "angzarr", "⍼" },
+ { "cirmid", "⫯" },
+ { "cudarrl", "⤸" },
+ { "cudarrr", "⤵" },
+ { "cularr", "↶" },
+ { "cularrp", "⤽" },
+ { "curarr", "↷" },
+ { "curarrm", "⤼" },
+ { "Darr", "↡" },
+ { "dArr", "⇓" },
+ { "ddarr", "⇊" },
+ { "DDotrahd", "⤑" },
+ { "dfisht", "⥿" },
+ { "dHar", "⥥" },
+ { "dharl", "⇃" },
+ { "dharr", "⇂" },
+ { "duarr", "⇵" },
+ { "duhar", "⥯" },
+ { "dzigrarr", "⟿" },
+ { "erarr", "⥱" },
+ { "hArr", "⇔" },
+ { "harr", "↔" },
+ { "harrcir", "⥈" },
+ { "harrw", "↭" },
+ { "hoarr", "⇿" },
+ { "imof", "⊷" },
+ { "lAarr", "⇚" },
+ { "Larr", "↞" },
+ { "larrbfs", "⤟" },
+ { "larrfs", "⤝" },
+ { "larrhk", "↩" },
+ { "larrlp", "↫" },
+ { "larrpl", "⤹" },
+ { "larrsim", "⥳" },
+ { "larrtl", "↢" },
+ { "lAtail", "⤛" },
+ { "latail", "⤙" },
+ { "lBarr", "⤎" },
+ { "lbarr", "⤌" },
+ { "ldca", "⤶" },
+ { "ldrdhar", "⥧" },
+ { "ldrushar", "⥋" },
+ { "ldsh", "↲" },
+ { "lfisht", "⥼" },
+ { "lHar", "⥢" },
+ { "lhard", "↽" },
+ { "lharu", "↼" },
+ { "lharul", "⥪" },
+ { "llarr", "⇇" },
+ { "llhard", "⥫" },
+ { "loarr", "⇽" },
+ { "lrarr", "⇆" },
+ { "lrhar", "⇋" },
+ { "lrhard", "⥭" },
+ { "lsh", "↰" },
+ { "lurdshar", "⥊" },
+ { "luruhar", "⥦" },
+ { "Map", "⤅" },
+ { "map", "↦" },
+ { "midcir", "⫰" },
+ { "mumap", "⊸" },
+ { "nearhk", "⤤" },
+ { "neArr", "⇗" },
+ { "nearr", "↗" },
+ { "nesear", "⤨" },
+ { "nhArr", "⇎" },
+ { "nharr", "↮" },
+ { "nlArr", "⇍" },
+ { "nlarr", "↚" },
+ { "nrArr", "⇏" },
+ { "nrarr", "↛" },
+ { "nrarrc", "⤳̸" },
+ { "nrarrw", "↝̸" },
+ { "nvHarr", "⤄" },
+ { "nvlArr", "⤂" },
+ { "nvrArr", "⤃" },
+ { "nwarhk", "⤣" },
+ { "nwArr", "⇖" },
+ { "nwarr", "↖" },
+ { "nwnear", "⤧" },
+ { "olarr", "↺" },
+ { "orarr", "↻" },
+ { "origof", "⊶" },
+ { "rAarr", "⇛" },
+ { "Rarr", "↠" },
+ { "rarrap", "⥵" },
+ { "rarrbfs", "⤠" },
+ { "rarrc", "⤳" },
+ { "rarrfs", "⤞" },
+ { "rarrhk", "↪" },
+ { "rarrlp", "↬" },
+ { "rarrpl", "⥅" },
+ { "rarrsim", "⥴" },
+ { "Rarrtl", "⤖" },
+ { "rarrtl", "↣" },
+ { "rarrw", "↝" },
+ { "rAtail", "⤜" },
+ { "ratail", "⤚" },
+ { "RBarr", "⤐" },
+ { "rBarr", "⤏" },
+ { "rbarr", "⤍" },
+ { "rdca", "⤷" },
+ { "rdldhar", "⥩" },
+ { "rdsh", "↳" },
+ { "rfisht", "⥽" },
+ { "rHar", "⥤" },
+ { "rhard", "⇁" },
+ { "rharu", "⇀" },
+ { "rharul", "⥬" },
+ { "rlarr", "⇄" },
+ { "rlhar", "⇌" },
+ { "roarr", "⇾" },
+ { "rrarr", "⇉" },
+ { "rsh", "↱" },
+ { "ruluhar", "⥨" },
+ { "searhk", "⤥" },
+ { "seArr", "⇘" },
+ { "searr", "↘" },
+ { "seswar", "⤩" },
+ { "simrarr", "⥲" },
+ { "slarr", "←" },
+ { "srarr", "→" },
+ { "swarhk", "⤦" },
+ { "swArr", "⇙" },
+ { "swarr", "↙" },
+ { "swnwar", "⤪" },
+ { "Uarr", "↟" },
+ { "uArr", "⇑" },
+ { "Uarrocir", "⥉" },
+ { "udarr", "⇅" },
+ { "udhar", "⥮" },
+ { "ufisht", "⥾" },
+ { "uHar", "⥣" },
+ { "uharl", "↿" },
+ { "uharr", "↾" },
+ { "uuarr", "⇈" },
+ { "vArr", "⇕" },
+ { "varr", "↕" },
+ { "xhArr", "⟺" },
+ { "xharr", "⟷" },
+ { "xlArr", "⟸" },
+ { "xlarr", "⟵" },
+ { "xmap", "⟼" },
+ { "xrArr", "⟹" },
+ { "xrarr", "⟶" },
+ { "zigrarr", "⇝" },
+ { "ac", "∾" },
+ { "acE", "∾̳" },
+ { "amalg", "⨿" },
+ { "barvee", "⊽" },
+ { "Barwed", "⌆" },
+ { "barwed", "⌅" },
+ { "bsolb", "⧅" },
+ { "Cap", "⋒" },
+ { "capand", "⩄" },
+ { "capbrcup", "⩉" },
+ { "capcap", "⩋" },
+ { "capcup", "⩇" },
+ { "capdot", "⩀" },
+ { "caps", "∩︀" },
+ { "ccaps", "⩍" },
+ { "ccups", "⩌" },
+ { "ccupssm", "⩐" },
+ { "coprod", "∐" },
+ { "Cup", "⋓" },
+ { "cupbrcap", "⩈" },
+ { "cupcap", "⩆" },
+ { "cupcup", "⩊" },
+ { "cupdot", "⊍" },
+ { "cupor", "⩅" },
+ { "cups", "∪︀" },
+ { "cuvee", "⋎" },
+ { "cuwed", "⋏" },
+ { "Dagger", "‡" },
+ { "dagger", "†" },
+ { "diam", "⋄" },
+ { "divonx", "⋇" },
+ { "eplus", "⩱" },
+ { "hercon", "⊹" },
+ { "intcal", "⊺" },
+ { "iprod", "⨼" },
+ { "loplus", "⨭" },
+ { "lotimes", "⨴" },
+ { "lthree", "⋋" },
+ { "ltimes", "⋉" },
+ { "midast", "*" },
+ { "minusb", "⊟" },
+ { "minusd", "∸" },
+ { "minusdu", "⨪" },
+ { "ncap", "⩃" },
+ { "ncup", "⩂" },
+ { "oast", "⊛" },
+ { "ocir", "⊚" },
+ { "odash", "⊝" },
+ { "odiv", "⨸" },
+ { "odot", "⊙" },
+ { "odsold", "⦼" },
+ { "ofcir", "⦿" },
+ { "ogt", "⧁" },
+ { "ohbar", "⦵" },
+ { "olcir", "⦾" },
+ { "olt", "⧀" },
+ { "omid", "⦶" },
+ { "ominus", "⊖" },
+ { "opar", "⦷" },
+ { "operp", "⦹" },
+ { "oplus", "⊕" },
+ { "osol", "⊘" },
+ { "Otimes", "⨷" },
+ { "otimes", "⊗" },
+ { "otimesas", "⨶" },
+ { "ovbar", "⌽" },
+ { "plusacir", "⨣" },
+ { "plusb", "⊞" },
+ { "pluscir", "⨢" },
+ { "plusdo", "∔" },
+ { "plusdu", "⨥" },
+ { "pluse", "⩲" },
+ { "plussim", "⨦" },
+ { "plustwo", "⨧" },
+ { "prod", "∏" },
+ { "race", "⧚" },
+ { "roplus", "⨮" },
+ { "rotimes", "⨵" },
+ { "rthree", "⋌" },
+ { "rtimes", "⋊" },
+ { "sdot", "⋅" },
+ { "sdotb", "⊡" },
+ { "setmn", "∖" },
+ { "simplus", "⨤" },
+ { "smashp", "⨳" },
+ { "solb", "⧄" },
+ { "sqcap", "⊓" },
+ { "sqcaps", "⊓︀" },
+ { "sqcup", "⊔" },
+ { "sqcups", "⊔︀" },
+ { "ssetmn", "∖" },
+ { "sstarf", "⋆" },
+ { "subdot", "⪽" },
+ { "sum", "∑" },
+ { "supdot", "⪾" },
+ { "timesb", "⊠" },
+ { "timesbar", "⨱" },
+ { "timesd", "⨰" },
+ { "tridot", "◬" },
+ { "triminus", "⨺" },
+ { "triplus", "⨹" },
+ { "trisb", "⧍" },
+ { "tritime", "⨻" },
+ { "uplus", "⊎" },
+ { "veebar", "⊻" },
+ { "wedbar", "⩟" },
+ { "wreath", "≀" },
+ { "xcap", "⋂" },
+ { "xcirc", "◯" },
+ { "xcup", "⋃" },
+ { "xdtri", "▽" },
+ { "xodot", "⨀" },
+ { "xoplus", "⨁" },
+ { "xotime", "⨂" },
+ { "xsqcup", "⨆" },
+ { "xuplus", "⨄" },
+ { "xutri", "△" },
+ { "xvee", "⋁" },
+ { "xwedge", "⋀" },
+ { "dlcorn", "⌞" },
+ { "drcorn", "⌟" },
+ { "gtlPar", "⦕" },
+ { "langd", "⦑" },
+ { "lbrke", "⦋" },
+ { "lbrksld", "⦏" },
+ { "lbrkslu", "⦍" },
+ { "lceil", "⌈" },
+ { "lfloor", "⌊" },
+ { "lmoust", "⎰" },
+ { "lparlt", "⦓" },
+ { "ltrPar", "⦖" },
+ { "rangd", "⦒" },
+ { "rbrke", "⦌" },
+ { "rbrksld", "⦎" },
+ { "rbrkslu", "⦐" },
+ { "rceil", "⌉" },
+ { "rfloor", "⌋" },
+ { "rmoust", "⎱" },
+ { "rpargt", "⦔" },
+ { "ulcorn", "⌜" },
+ { "urcorn", "⌝" },
+ { "gnap", "⪊" },
+ { "gnE", "≩" },
+ { "gne", "⪈" },
+ { "gnsim", "⋧" },
+ { "gvnE", "≩︀" },
+ { "lnap", "⪉" },
+ { "lnE", "≨" },
+ { "lne", "⪇" },
+ { "lnsim", "⋦" },
+ { "lvnE", "≨︀" },
+ { "nap", "≉" },
+ { "napE", "⩰̸" },
+ { "napid", "≋̸" },
+ { "ncong", "≇" },
+ { "ncongdot", "⩭̸" },
+ { "nequiv", "≢" },
+ { "ngE", "≧̸" },
+ { "nge", "≱" },
+ { "nges", "⩾̸" },
+ { "nGg", "⋙̸" },
+ { "ngsim", "≵" },
+ { "nGt", "≫⃒" },
+ { "ngt", "≯" },
+ { "nGtv", "≫̸" },
+ { "nlE", "≦̸" },
+ { "nle", "≰" },
+ { "nles", "⩽̸" },
+ { "nLl", "⋘̸" },
+ { "nlsim", "≴" },
+ { "nLt", "≪⃒" },
+ { "nlt", "≮" },
+ { "nltri", "⋪" },
+ { "nltrie", "⋬" },
+ { "nLtv", "≪̸" },
+ { "nmid", "∤" },
+ { "npar", "∦" },
+ { "npr", "⊀" },
+ { "nprcue", "⋠" },
+ { "npre", "⪯̸" },
+ { "nrtri", "⋫" },
+ { "nrtrie", "⋭" },
+ { "nsc", "⊁" },
+ { "nsccue", "⋡" },
+ { "nsce", "⪰̸" },
+ { "nsim", "≁" },
+ { "nsime", "≄" },
+ { "nsmid", "∤" },
+ { "nspar", "∦" },
+ { "nsqsube", "⋢" },
+ { "nsqsupe", "⋣" },
+ { "nsub", "⊄" },
+ { "nsubE", "⫅̸" },
+ { "nsube", "⊈" },
+ { "nsup", "⊅" },
+ { "nsupE", "⫆̸" },
+ { "nsupe", "⊉" },
+ { "ntgl", "≹" },
+ { "ntlg", "≸" },
+ { "nvap", "≍⃒" },
+ { "nVDash", "⊯" },
+ { "nVdash", "⊮" },
+ { "nvDash", "⊭" },
+ { "nvdash", "⊬" },
+ { "nvge", "≥⃒" },
+ { "nvgt", ">⃒" },
+ { "nvle", "≤⃒" },
+ { "nvlt", "<⃒" },
+ { "nvltrie", "⊴⃒" },
+ { "nvrtrie", "⊵⃒" },
+ { "nvsim", "∼⃒" },
+ { "parsim", "⫳" },
+ { "prnap", "⪹" },
+ { "prnE", "⪵" },
+ { "prnsim", "⋨" },
+ { "rnmid", "⫮" },
+ { "scnap", "⪺" },
+ { "scnE", "⪶" },
+ { "scnsim", "⋩" },
+ { "simne", "≆" },
+ { "solbar", "⌿" },
+ { "subnE", "⫋" },
+ { "subne", "⊊" },
+ { "supnE", "⫌" },
+ { "supne", "⊋" },
+ { "vnsub", "⊂⃒" },
+ { "vnsup", "⊃⃒" },
+ { "vsubnE", "⫋︀" },
+ { "vsubne", "⊊︀" },
+ { "vsupnE", "⫌︀" },
+ { "vsupne", "⊋︀" },
+ { "ang", "∠" },
+ { "ange", "⦤" },
+ { "angmsd", "∡" },
+ { "angmsdaa", "⦨" },
+ { "angmsdab", "⦩" },
+ { "angmsdac", "⦪" },
+ { "angmsdad", "⦫" },
+ { "angmsdae", "⦬" },
+ { "angmsdaf", "⦭" },
+ { "angmsdag", "⦮" },
+ { "angmsdah", "⦯" },
+ { "angrtvb", "⊾" },
+ { "angrtvbd", "⦝" },
+ { "bbrk", "⎵" },
+ { "bemptyv", "⦰" },
+ { "beth", "ℶ" },
+ { "boxbox", "⧉" },
+ { "bprime", "‵" },
+ { "bsemi", "⁏" },
+ { "cemptyv", "⦲" },
+ { "cirE", "⧃" },
+ { "cirscir", "⧂" },
+ { "comp", "∁" },
+ { "daleth", "ℸ" },
+ { "demptyv", "⦱" },
+ { "ell", "ℓ" },
+ { "empty", "∅" },
+ { "emptyv", "∅" },
+ { "gimel", "ℷ" },
+ { "iiota", "℩" },
+ { "image", "ℑ" },
+ { "imath", "ı" },
+ { "jmath", "j" },
+ { "laemptyv", "⦴" },
+ { "lltri", "◺" },
+ { "lrtri", "⊿" },
+ { "mho", "℧" },
+ { "nang", "∠⃒" },
+ { "nexist", "∄" },
+ { "oS", "Ⓢ" },
+ { "planck", "ℏ" },
+ { "plankv", "ℏ" },
+ { "raemptyv", "⦳" },
+ { "range", "⦥" },
+ { "real", "ℜ" },
+ { "tbrk", "⎴" },
+ { "ultri", "◸" },
+ { "urtri", "◹" },
+ { "vzigzag", "⦚" },
+ { "weierp", "℘" },
+ { "apE", "⩰" },
+ { "ape", "≊" },
+ { "apid", "≋" },
+ { "asymp", "≈" },
+ { "Barv", "⫧" },
+ { "bcong", "≌" },
+ { "bepsi", "϶" },
+ { "bowtie", "⋈" },
+ { "bsim", "∽" },
+ { "bsime", "⋍" },
+ { "bsolhsub", "\⊂" },
+ { "bump", "≎" },
+ { "bumpE", "⪮" },
+ { "bumpe", "≏" },
+ { "cire", "≗" },
+ { "Colon", "∷" },
+ { "Colone", "⩴" },
+ { "colone", "≔" },
+ { "congdot", "⩭" },
+ { "csub", "⫏" },
+ { "csube", "⫑" },
+ { "csup", "⫐" },
+ { "csupe", "⫒" },
+ { "cuepr", "⋞" },
+ { "cuesc", "⋟" },
+ { "Dashv", "⫤" },
+ { "dashv", "⊣" },
+ { "easter", "⩮" },
+ { "ecir", "≖" },
+ { "ecolon", "≕" },
+ { "eDDot", "⩷" },
+ { "eDot", "≑" },
+ { "efDot", "≒" },
+ { "eg", "⪚" },
+ { "egs", "⪖" },
+ { "egsdot", "⪘" },
+ { "el", "⪙" },
+ { "els", "⪕" },
+ { "elsdot", "⪗" },
+ { "equest", "≟" },
+ { "equivDD", "⩸" },
+ { "erDot", "≓" },
+ { "esdot", "≐" },
+ { "Esim", "⩳" },
+ { "esim", "≂" },
+ { "fork", "⋔" },
+ { "forkv", "⫙" },
+ { "frown", "⌢" },
+ { "gap", "⪆" },
+ { "gE", "≧" },
+ { "gEl", "⪌" },
+ { "gel", "⋛" },
+ { "ges", "⩾" },
+ { "gescc", "⪩" },
+ { "gesdot", "⪀" },
+ { "gesdoto", "⪂" },
+ { "gesdotol", "⪄" },
+ { "gesl", "⋛︀" },
+ { "gesles", "⪔" },
+ { "Gg", "⋙" },
+ { "gl", "≷" },
+ { "gla", "⪥" },
+ { "glE", "⪒" },
+ { "glj", "⪤" },
+ { "gsim", "≳" },
+ { "gsime", "⪎" },
+ { "gsiml", "⪐" },
+ { "Gt", "≫" },
+ { "gtcc", "⪧" },
+ { "gtcir", "⩺" },
+ { "gtdot", "⋗" },
+ { "gtquest", "⩼" },
+ { "gtrarr", "⥸" },
+ { "homtht", "∻" },
+ { "lap", "⪅" },
+ { "lat", "⪫" },
+ { "late", "⪭" },
+ { "lates", "⪭︀" },
+ { "lE", "≦" },
+ { "lEg", "⪋" },
+ { "leg", "⋚" },
+ { "les", "⩽" },
+ { "lescc", "⪨" },
+ { "lesdot", "⩿" },
+ { "lesdoto", "⪁" },
+ { "lesdotor", "⪃" },
+ { "lesg", "⋚︀" },
+ { "lesges", "⪓" },
+ { "lg", "≶" },
+ { "lgE", "⪑" },
+ { "Ll", "⋘" },
+ { "lsim", "≲" },
+ { "lsime", "⪍" },
+ { "lsimg", "⪏" },
+ { "Lt", "≪" },
+ { "ltcc", "⪦" },
+ { "ltcir", "⩹" },
+ { "ltdot", "⋖" },
+ { "ltlarr", "⥶" },
+ { "ltquest", "⩻" },
+ { "ltrie", "⊴" },
+ { "mcomma", "⨩" },
+ { "mDDot", "∺" },
+ { "mid", "∣" },
+ { "mlcp", "⫛" },
+ { "models", "⊧" },
+ { "mstpos", "∾" },
+ { "Pr", "⪻" },
+ { "pr", "≺" },
+ { "prap", "⪷" },
+ { "prcue", "≼" },
+ { "prE", "⪳" },
+ { "pre", "⪯" },
+ { "prsim", "≾" },
+ { "prurel", "⊰" },
+ { "ratio", "∶" },
+ { "rtrie", "⊵" },
+ { "rtriltri", "⧎" },
+ { "Sc", "⪼" },
+ { "sc", "≻" },
+ { "scap", "⪸" },
+ { "sccue", "≽" },
+ { "scE", "⪴" },
+ { "sce", "⪰" },
+ { "scsim", "≿" },
+ { "sdote", "⩦" },
+ { "simg", "⪞" },
+ { "simgE", "⪠" },
+ { "siml", "⪝" },
+ { "simlE", "⪟" },
+ { "smid", "∣" },
+ { "smile", "⌣" },
+ { "smt", "⪪" },
+ { "smte", "⪬" },
+ { "smtes", "⪬︀" },
+ { "spar", "∥" },
+ { "sqsub", "⊏" },
+ { "sqsube", "⊑" },
+ { "sqsup", "⊐" },
+ { "sqsupe", "⊒" },
+ { "Sub", "⋐" },
+ { "subE", "⫅" },
+ { "subedot", "⫃" },
+ { "submult", "⫁" },
+ { "subplus", "⪿" },
+ { "subrarr", "⥹" },
+ { "subsim", "⫇" },
+ { "subsub", "⫕" },
+ { "subsup", "⫓" },
+ { "Sup", "⋑" },
+ { "supdsub", "⫘" },
+ { "supE", "⫆" },
+ { "supedot", "⫄" },
+ { "suphsol", "⊅" },
+ { "suphsub", "⫗" },
+ { "suplarr", "⥻" },
+ { "supmult", "⫂" },
+ { "supplus", "⫀" },
+ { "supsim", "⫈" },
+ { "supsub", "⫔" },
+ { "supsup", "⫖" },
+ { "thkap", "≈" },
+ { "topfork", "⫚" },
+ { "trie", "≜" },
+ { "twixt", "≬" },
+ { "Vbar", "⫫" },
+ { "vBar", "⫨" },
+ { "vBarv", "⫩" },
+ { "VDash", "⊫" },
+ { "Vdash", "⊩" },
+ { "vDash", "⊨" },
+ { "vdash", "⊢" },
+ { "Vdashl", "⫦" },
+ { "vltri", "⊲" },
+ { "vprop", "∝" },
+ { "vrtri", "⊳" },
+ { "Vvdash", "⊪" },
+ { "alpha", "α" },
+ { "beta", "β" },
+ { "chi", "χ" },
+ { "Delta", "Δ" },
+ { "delta", "δ" },
+ { "epsi", "ε" },
+ { "epsiv", "ɛ" },
+ { "eta", "η" },
+ { "Gamma", "Γ" },
+ { "gamma", "γ" },
+ { "Gammad", "Ϝ" },
+ { "gammad", "ϝ" },
+ { "iota", "ι" },
+ { "kappa", "κ" },
+ { "kappav", "ϰ" },
+ { "Lambda", "Λ" },
+ { "lambda", "λ" },
+ { "mu", "μ" },
+ { "nu", "ν" },
+ { "Omega", "Ω" },
+ { "omega", "ω" },
+ { "Phi", "Φ" },
+ { "phi", "ϕ" },
+ { "phiv", "φ" },
+ { "Pi", "Π" },
+ { "pi", "π" },
+ { "piv", "ϖ" },
+ { "Psi", "Ψ" },
+ { "psi", "ψ" },
+ { "rho", "ρ" },
+ { "rhov", "ϱ" },
+ { "Sigma", "Σ" },
+ { "sigma", "σ" },
+ { "sigmav", "ς" },
+ { "tau", "τ" },
+ { "Theta", "Θ" },
+ { "theta", "θ" },
+ { "thetav", "ϑ" },
+ { "Upsi", "ϒ" },
+ { "upsi", "υ" },
+ { "Xi", "Ξ" },
+ { "xi", "ξ" },
+ { "zeta", "ζ" },
+ { "Cfr", "ℭ" },
+ { "Hfr", "ℌ" },
+ { "Ifr", "ℑ" },
+ { "Rfr", "ℜ" },
+ { "Zfr", "ℨ" },
+ { "Copf", "ℂ" },
+ { "Hopf", "ℍ" },
+ { "Nopf", "ℕ" },
+ { "Popf", "ℙ" },
+ { "Qopf", "ℚ" },
+ { "Ropf", "ℝ" },
+ { "Zopf", "ℤ" },
+ { "Bscr", "ℬ" },
+ { "Escr", "ℰ" },
+ { "escr", "ℯ" },
+ { "Fscr", "ℱ" },
+ { "gscr", "ℊ" },
+ { "Hscr", "ℋ" },
+ { "Iscr", "ℐ" },
+ { "Lscr", "ℒ" },
+ { "Mscr", "ℳ" },
+ { "oscr", "ℴ" },
+ { "pscr", "𝓅" },
+ { "Rscr", "ℛ" },
+ { "acd", "∿" },
+ { "aleph", "ℵ" },
+ { "And", "⩓" },
+ { "and", "∧" },
+ { "andand", "⩕" },
+ { "andd", "⩜" },
+ { "andslope", "⩘" },
+ { "andv", "⩚" },
+ { "angrt", "∟" },
+ { "angsph", "∢" },
+ { "angst", "Å" },
+ { "ap", "≈" },
+ { "apacir", "⩯" },
+ { "awconint", "∳" },
+ { "awint", "⨑" },
+ { "becaus", "∵" },
+ { "bernou", "ℬ" },
+ { "bne", "=⃥" },
+ { "bnequiv", "≡⃥" },
+ { "bNot", "⫭" },
+ { "bnot", "⌐" },
+ { "bottom", "⊥" },
+ { "cap", "∩" },
+ { "Cconint", "∰" },
+ { "cirfnint", "⨐" },
+ { "compfn", "∘" },
+ { "cong", "≅" },
+ { "Conint", "∯" },
+ { "conint", "∮" },
+ { "ctdot", "⋯" },
+ { "cup", "∪" },
+ { "cwconint", "∲" },
+ { "cwint", "∱" },
+ { "cylcty", "⌭" },
+ { "disin", "⋲" },
+ { "Dot", "¨" },
+ { "DotDot", "⃜" },
+ { "dsol", "⧶" },
+ { "dtdot", "⋱" },
+ { "dwangle", "⦦" },
+ { "epar", "⋕" },
+ { "eparsl", "⧣" },
+ { "equiv", "≡" },
+ { "eqvparsl", "⧥" },
+ { "exist", "∃" },
+ { "fnof", "ƒ" },
+ { "forall", "∀" },
+ { "fpartint", "⨍" },
+ { "ge", "≥" },
+ { "hamilt", "ℋ" },
+ { "iff", "⇔" },
+ { "iinfin", "⧜" },
+ { "infin", "∞" },
+ { "Int", "∬" },
+ { "int", "∫" },
+ { "intlarhk", "⨗" },
+ { "isin", "∈" },
+ { "isindot", "⋵" },
+ { "isinE", "⋹" },
+ { "isins", "⋴" },
+ { "isinsv", "⋳" },
+ { "isinv", "∈" },
+ { "lagran", "ℒ" },
+ { "Lang", "《" },
+ { "lang", "〈" },
+ { "lArr", "⇐" },
+ { "lbbrk", "〔" },
+ { "le", "≤" },
+ { "loang", "〘" },
+ { "lobrk", "〚" },
+ { "lopar", "⦅" },
+ { "lowast", "∗" },
+ { "minus", "−" },
+ { "mnplus", "∓" },
+ { "nabla", "∇" },
+ { "ne", "≠" },
+ { "nedot", "≐̸" },
+ { "nhpar", "⫲" },
+ { "ni", "∋" },
+ { "nis", "⋼" },
+ { "nisd", "⋺" },
+ { "niv", "∋" },
+ { "Not", "⫬" },
+ { "notin", "∉" },
+ { "notindot", "⋵̸" },
+ { "notinva", "∉" },
+ { "notinvb", "⋷" },
+ { "notinvc", "⋶" },
+ { "notni", "∌" },
+ { "notniva", "∌" },
+ { "notnivb", "⋾" },
+ { "notnivc", "⋽" },
+ { "nparsl", "⫽⃥" },
+ { "npart", "∂̸" },
+ { "npolint", "⨔" },
+ { "nvinfin", "⧞" },
+ { "olcross", "⦻" },
+ { "Or", "⩔" },
+ { "or", "∨" },
+ { "ord", "⩝" },
+ { "order", "ℴ" },
+ { "oror", "⩖" },
+ { "orslope", "⩗" },
+ { "orv", "⩛" },
+ { "par", "∥" },
+ { "parsl", "⫽" },
+ { "part", "∂" },
+ { "permil", "‰" },
+ { "perp", "⊥" },
+ { "pertenk", "‱" },
+ { "phmmat", "ℳ" },
+ { "pointint", "⨕" },
+ { "Prime", "″" },
+ { "prime", "′" },
+ { "profalar", "⌮" },
+ { "profline", "⌒" },
+ { "profsurf", "⌓" },
+ { "prop", "∝" },
+ { "qint", "⨌" },
+ { "qprime", "⁗" },
+ { "quatint", "⨖" },
+ { "radic", "√" },
+ { "Rang", "》" },
+ { "rang", "〉" },
+ { "rArr", "⇒" },
+ { "rbbrk", "〕" },
+ { "roang", "〙" },
+ { "robrk", "〛" },
+ { "ropar", "⦆" },
+ { "rppolint", "⨒" },
+ { "scpolint", "⨓" },
+ { "sim", "∼" },
+ { "simdot", "⩪" },
+ { "sime", "≃" },
+ { "smeparsl", "⧤" },
+ { "square", "□" },
+ { "squarf", "▪" },
+ { "sub", "⊂" },
+ { "sube", "⊆" },
+ { "sup", "⊃" },
+ { "supe", "⊇" },
+ { "tdot", "⃛" },
+ { "there4", "∴" },
+ { "tint", "∭" },
+ { "top", "⊤" },
+ { "topbot", "⌶" },
+ { "topcir", "⫱" },
+ { "tprime", "‴" },
+ { "utdot", "⋰" },
+ { "uwangle", "⦧" },
+ { "vangrt", "⦜" },
+ { "veeeq", "≚" },
+ { "Verbar", "‖" },
+ { "wedgeq", "≙" },
+ { "xnis", "⋻" },
+ { "boxDL", "╗" },
+ { "boxDl", "╖" },
+ { "boxdL", "╕" },
+ { "boxdl", "┐" },
+ { "boxDR", "╔" },
+ { "boxDr", "╓" },
+ { "boxdR", "╒" },
+ { "boxdr", "┌" },
+ { "boxH", "═" },
+ { "boxh", "─" },
+ { "boxHD", "╦" },
+ { "boxHd", "╤" },
+ { "boxhD", "╥" },
+ { "boxhd", "┬" },
+ { "boxHU", "╩" },
+ { "boxHu", "╧" },
+ { "boxhU", "╨" },
+ { "boxhu", "┴" },
+ { "boxUL", "╝" },
+ { "boxUl", "╜" },
+ { "boxuL", "╛" },
+ { "boxul", "┘" },
+ { "boxUR", "╚" },
+ { "boxUr", "╙" },
+ { "boxuR", "╘" },
+ { "boxur", "└" },
+ { "boxV", "║" },
+ { "boxv", "│" },
+ { "boxVH", "╬" },
+ { "boxVh", "╫" },
+ { "boxvH", "╪" },
+ { "boxvh", "┼" },
+ { "boxVL", "╣" },
+ { "boxVl", "╢" },
+ { "boxvL", "╡" },
+ { "boxvl", "┤" },
+ { "boxVR", "╠" },
+ { "boxVr", "╟" },
+ { "boxvR", "╞" },
+ { "boxvr", "├" },
+ { "Acy", "А" },
+ { "acy", "а" },
+ { "Bcy", "Б" },
+ { "bcy", "б" },
+ { "CHcy", "Ч" },
+ { "chcy", "ч" },
+ { "Dcy", "Д" },
+ { "dcy", "д" },
+ { "Ecy", "Э" },
+ { "ecy", "э" },
+ { "Fcy", "Ф" },
+ { "fcy", "ф" },
+ { "Gcy", "Г" },
+ { "gcy", "г" },
+ { "HARDcy", "Ъ" },
+ { "hardcy", "ъ" },
+ { "Icy", "И" },
+ { "icy", "и" },
+ { "IEcy", "Е" },
+ { "iecy", "е" },
+ { "IOcy", "Ё" },
+ { "iocy", "ё" },
+ { "Jcy", "Й" },
+ { "jcy", "й" },
+ { "Kcy", "К" },
+ { "kcy", "к" },
+ { "KHcy", "Х" },
+ { "khcy", "х" },
+ { "Lcy", "Л" },
+ { "lcy", "л" },
+ { "Mcy", "М" },
+ { "mcy", "м" },
+ { "Ncy", "Н" },
+ { "ncy", "н" },
+ { "numero", "№" },
+ { "Ocy", "О" },
+ { "ocy", "о" },
+ { "Pcy", "П" },
+ { "pcy", "п" },
+ { "Rcy", "Р" },
+ { "rcy", "р" },
+ { "Scy", "С" },
+ { "scy", "с" },
+ { "SHCHcy", "Щ" },
+ { "shchcy", "щ" },
+ { "SHcy", "Ш" },
+ { "shcy", "ш" },
+ { "SOFTcy", "Ь" },
+ { "softcy", "ь" },
+ { "Tcy", "Т" },
+ { "tcy", "т" },
+ { "TScy", "Ц" },
+ { "tscy", "ц" },
+ { "Ucy", "У" },
+ { "ucy", "у" },
+ { "Vcy", "В" },
+ { "vcy", "в" },
+ { "YAcy", "Я" },
+ { "yacy", "я" },
+ { "Ycy", "Ы" },
+ { "ycy", "ы" },
+ { "YUcy", "Ю" },
+ { "yucy", "ю" },
+ { "Zcy", "З" },
+ { "zcy", "з" },
+ { "ZHcy", "Ж" },
+ { "zhcy", "ж" },
+ { "DJcy", "Ђ" },
+ { "djcy", "ђ" },
+ { "DScy", "Ѕ" },
+ { "dscy", "ѕ" },
+ { "DZcy", "Џ" },
+ { "dzcy", "џ" },
+ { "GJcy", "Ѓ" },
+ { "gjcy", "ѓ" },
+ { "Iukcy", "І" },
+ { "iukcy", "і" },
+ { "Jsercy", "Ј" },
+ { "jsercy", "ј" },
+ { "Jukcy", "Є" },
+ { "jukcy", "є" },
+ { "KJcy", "Ќ" },
+ { "kjcy", "ќ" },
+ { "LJcy", "Љ" },
+ { "ljcy", "љ" },
+ { "NJcy", "Њ" },
+ { "njcy", "њ" },
+ { "TSHcy", "Ћ" },
+ { "tshcy", "ћ" },
+ { "Ubrcy", "Ў" },
+ { "ubrcy", "ў" },
+ { "YIcy", "Ї" },
+ { "yicy", "ї" },
+ { "acute", "´" },
+ { "breve", "˘" },
+ { "caron", "ˇ" },
+ { "cedil", "¸" },
+ { "circ", "ˆ" },
+ { "dblac", "˝" },
+ { "die", "¨" },
+ { "dot", "˙" },
+ { "grave", "`" },
+ { "macr", "¯" },
+ { "ogon", "˛" },
+ { "ring", "˚" },
+ { "tilde", "˜" },
+ { "uml", "¨" },
+ { "Aacute", "Á" },
+ { "aacute", "á" },
+ { "Acirc", "Â" },
+ { "acirc", "â" },
+ { "AElig", "Æ" },
+ { "aelig", "æ" },
+ { "Agrave", "À" },
+ { "agrave", "à" },
+ { "Aring", "Å" },
+ { "aring", "å" },
+ { "Atilde", "Ã" },
+ { "atilde", "ã" },
+ { "Auml", "Ä" },
+ { "auml", "ä" },
+ { "Ccedil", "Ç" },
+ { "ccedil", "ç" },
+ { "Eacute", "É" },
+ { "eacute", "é" },
+ { "Ecirc", "Ê" },
+ { "ecirc", "ê" },
+ { "Egrave", "È" },
+ { "egrave", "è" },
+ { "ETH", "Ð" },
+ { "eth", "ð" },
+ { "Euml", "Ë" },
+ { "euml", "ë" },
+ { "Iacute", "Í" },
+ { "iacute", "í" },
+ { "Icirc", "Î" },
+ { "icirc", "î" },
+ { "Igrave", "Ì" },
+ { "igrave", "ì" },
+ { "Iuml", "Ï" },
+ { "iuml", "ï" },
+ { "Ntilde", "Ñ" },
+ { "ntilde", "ñ" },
+ { "Oacute", "Ó" },
+ { "oacute", "ó" },
+ { "Ocirc", "Ô" },
+ { "ocirc", "ô" },
+ { "Ograve", "Ò" },
+ { "ograve", "ò" },
+ { "Oslash", "Ø" },
+ { "oslash", "ø" },
+ { "Otilde", "Õ" },
+ { "otilde", "õ" },
+ { "Ouml", "Ö" },
+ { "ouml", "ö" },
+ { "szlig", "ß" },
+ { "THORN", "Þ" },
+ { "thorn", "þ" },
+ { "Uacute", "Ú" },
+ { "uacute", "ú" },
+ { "Ucirc", "Û" },
+ { "ucirc", "û" },
+ { "Ugrave", "Ù" },
+ { "ugrave", "ù" },
+ { "Uuml", "Ü" },
+ { "uuml", "ü" },
+ { "Yacute", "Ý" },
+ { "yacute", "ý" },
+ { "yuml", "ÿ" },
+ { "Abreve", "Ă" },
+ { "abreve", "ă" },
+ { "Amacr", "Ā" },
+ { "amacr", "ā" },
+ { "Aogon", "Ą" },
+ { "aogon", "ą" },
+ { "Cacute", "Ć" },
+ { "cacute", "ć" },
+ { "Ccaron", "Č" },
+ { "ccaron", "č" },
+ { "Ccirc", "Ĉ" },
+ { "ccirc", "ĉ" },
+ { "Cdot", "Ċ" },
+ { "cdot", "ċ" },
+ { "Dcaron", "Ď" },
+ { "dcaron", "ď" },
+ { "Dstrok", "Đ" },
+ { "dstrok", "đ" },
+ { "Ecaron", "Ě" },
+ { "ecaron", "ě" },
+ { "Edot", "Ė" },
+ { "edot", "ė" },
+ { "Emacr", "Ē" },
+ { "emacr", "ē" },
+ { "ENG", "Ŋ" },
+ { "eng", "ŋ" },
+ { "Eogon", "Ę" },
+ { "eogon", "ę" },
+ { "gacute", "ǵ" },
+ { "Gbreve", "Ğ" },
+ { "gbreve", "ğ" },
+ { "Gcedil", "Ģ" },
+ { "Gcirc", "Ĝ" },
+ { "gcirc", "ĝ" },
+ { "Gdot", "Ġ" },
+ { "gdot", "ġ" },
+ { "Hcirc", "Ĥ" },
+ { "hcirc", "ĥ" },
+ { "Hstrok", "Ħ" },
+ { "hstrok", "ħ" },
+ { "Idot", "İ" },
+ { "IJlig", "IJ" },
+ { "ijlig", "ij" },
+ { "Imacr", "Ī" },
+ { "imacr", "ī" },
+ { "inodot", "ı" },
+ { "Iogon", "Į" },
+ { "iogon", "į" },
+ { "Itilde", "Ĩ" },
+ { "itilde", "ĩ" },
+ { "Jcirc", "Ĵ" },
+ { "jcirc", "ĵ" },
+ { "Kcedil", "Ķ" },
+ { "kcedil", "ķ" },
+ { "kgreen", "ĸ" },
+ { "Lacute", "Ĺ" },
+ { "lacute", "ĺ" },
+ { "Lcaron", "Ľ" },
+ { "lcaron", "ľ" },
+ { "Lcedil", "Ļ" },
+ { "lcedil", "ļ" },
+ { "Lmidot", "Ŀ" },
+ { "lmidot", "ŀ" },
+ { "Lstrok", "Ł" },
+ { "lstrok", "ł" },
+ { "Nacute", "Ń" },
+ { "nacute", "ń" },
+ { "napos", "ʼn" },
+ { "Ncaron", "Ň" },
+ { "ncaron", "ň" },
+ { "Ncedil", "Ņ" },
+ { "ncedil", "ņ" },
+ { "Odblac", "Ő" },
+ { "odblac", "ő" },
+ { "OElig", "Œ" },
+ { "oelig", "œ" },
+ { "Omacr", "Ō" },
+ { "omacr", "ō" },
+ { "Racute", "Ŕ" },
+ { "racute", "ŕ" },
+ { "Rcaron", "Ř" },
+ { "rcaron", "ř" },
+ { "Rcedil", "Ŗ" },
+ { "rcedil", "ŗ" },
+ { "Sacute", "Ś" },
+ { "sacute", "ś" },
+ { "Scaron", "Š" },
+ { "scaron", "š" },
+ { "Scedil", "Ş" },
+ { "scedil", "ş" },
+ { "Scirc", "Ŝ" },
+ { "scirc", "ŝ" },
+ { "Tcaron", "Ť" },
+ { "tcaron", "ť" },
+ { "Tcedil", "Ţ" },
+ { "tcedil", "ţ" },
+ { "Tstrok", "Ŧ" },
+ { "tstrok", "ŧ" },
+ { "Ubreve", "Ŭ" },
+ { "ubreve", "ŭ" },
+ { "Udblac", "Ű" },
+ { "udblac", "ű" },
+ { "Umacr", "Ū" },
+ { "umacr", "ū" },
+ { "Uogon", "Ų" },
+ { "uogon", "ų" },
+ { "Uring", "Ů" },
+ { "uring", "ů" },
+ { "Utilde", "Ũ" },
+ { "utilde", "ũ" },
+ { "Wcirc", "Ŵ" },
+ { "wcirc", "ŵ" },
+ { "Ycirc", "Ŷ" },
+ { "ycirc", "ŷ" },
+ { "Yuml", "Ÿ" },
+ { "Zacute", "Ź" },
+ { "zacute", "ź" },
+ { "Zcaron", "Ž" },
+ { "zcaron", "ž" },
+ { "Zdot", "Ż" },
+ { "zdot", "ż" },
+ { "apos", "'" },
+ { "ast", "*" },
+ { "brvbar", "¦" },
+ { "bsol", "\" },
+ { "cent", "¢" },
+ { "colon", ":" },
+ { "comma", "," },
+ { "commat", "@" },
+ { "copy", "©" },
+ { "curren", "¤" },
+ { "darr", "↓" },
+ { "deg", "°" },
+ { "divide", "÷" },
+ { "dollar", "$" },
+ { "equals", "=" },
+ { "excl", "!" },
+ { "frac12", "½" },
+ { "frac14", "¼" },
+ { "frac18", "⅛" },
+ { "frac34", "¾" },
+ { "frac38", "⅜" },
+ { "frac58", "⅝" },
+ { "frac78", "⅞" },
+ { "gt", ">" },
+ { "half", "½" },
+ { "horbar", "―" },
+ { "hyphen", "‐" },
+ { "iexcl", "¡" },
+ { "iquest", "¿" },
+ { "laquo", "«" },
+ { "larr", "←" },
+ { "lcub", "{" },
+ { "ldquo", "“" },
+ { "lowbar", "_" },
+ { "lpar", "(" },
+ { "lsqb", "[" },
+ { "lsquo", "‘" },
+ { "lt", "<" },
+ { "micro", "µ" },
+ { "middot", "·" },
+ { "nbsp", " " },
+ { "not", "¬" },
+ { "num", "#" },
+ { "ohm", "Ω" },
+ { "ordf", "ª" },
+ { "ordm", "º" },
+ { "para", "¶" },
+ { "percnt", "%" },
+ { "period", "." },
+ { "plus", "+" },
+ { "plusmn", "±" },
+ { "pound", "£" },
+ { "quest", "?" },
+ { "quot", """ },
+ { "raquo", "»" },
+ { "rarr", "→" },
+ { "rcub", "}" },
+ { "rdquo", "”" },
+ { "reg", "®" },
+ { "rpar", ")" },
+ { "rsqb", "]" },
+ { "rsquo", "’" },
+ { "sect", "§" },
+ { "semi", ";" },
+ { "shy", "­" },
+ { "sol", "/" },
+ { "sung", "♪" },
+ { "sup1", "¹" },
+ { "sup2", "²" },
+ { "sup3", "³" },
+ { "times", "×" },
+ { "trade", "™" },
+ { "uarr", "↑" },
+ { "verbar", "|" },
+ { "yen", "¥" },
+ { "blank", "␣" },
+ { "blk12", "▒" },
+ { "blk14", "░" },
+ { "blk34", "▓" },
+ { "block", "█" },
+ { "bull", "•" },
+ { "caret", "⁁" },
+ { "check", "✓" },
+ { "cir", "○" },
+ { "clubs", "♣" },
+ { "copysr", "℗" },
+ { "cross", "✗" },
+ { "Dagger", "‡" },
+ { "dagger", "†" },
+ { "dash", "‐" },
+ { "diams", "♦" },
+ { "dlcrop", "⌍" },
+ { "drcrop", "⌌" },
+ { "dtri", "▿" },
+ { "dtrif", "▾" },
+ { "emsp", " " },
+ { "emsp13", " " },
+ { "emsp14", " " },
+ { "ensp", " " },
+ { "female", "♀" },
+ { "ffilig", "ffi" },
+ { "fflig", "ff" },
+ { "ffllig", "ffl" },
+ { "filig", "fi" },
+ { "flat", "♭" },
+ { "fllig", "fl" },
+ { "frac13", "⅓" },
+ { "frac15", "⅕" },
+ { "frac16", "⅙" },
+ { "frac23", "⅔" },
+ { "frac25", "⅖" },
+ { "frac35", "⅗" },
+ { "frac45", "⅘" },
+ { "frac56", "⅚" },
+ { "hairsp", " " },
+ { "hearts", "♥" },
+ { "hellip", "…" },
+ { "hybull", "⁃" },
+ { "incare", "℅" },
+ { "ldquor", "„" },
+ { "lhblk", "▄" },
+ { "loz", "◊" },
+ { "lozf", "⧫" },
+ { "lsquor", "‚" },
+ { "ltri", "◃" },
+ { "ltrif", "◂" },
+ { "male", "♂" },
+ { "malt", "✠" },
+ { "marker", "▮" },
+ { "mdash", "—" },
+ { "mldr", "…" },
+ { "natur", "♮" },
+ { "ndash", "–" },
+ { "nldr", "‥" },
+ { "numsp", " " },
+ { "phone", "☎" },
+ { "puncsp", " " },
+ { "rdquor", "”" },
+ { "rect", "▭" },
+ { "rsquor", "’" },
+ { "rtri", "▹" },
+ { "rtrif", "▸" },
+ { "rx", "℞" },
+ { "sext", "✶" },
+ { "sharp", "♯" },
+ { "spades", "♠" },
+ { "squ", "□" },
+ { "squf", "▪" },
+ { "star", "☆" },
+ { "starf", "★" },
+ { "target", "⌖" },
+ { "telrec", "⌕" },
+ { "thinsp", " " },
+ { "uhblk", "▀" },
+ { "ulcrop", "⌏" },
+ { "urcrop", "⌎" },
+ { "utri", "▵" },
+ { "utrif", "▴" },
+ { "vellip", "⋮" },
+ { "af", "⁡" },
+ { "asympeq", "≍" },
+ { "Cross", "⨯" },
+ { "DD", "ⅅ" },
+ { "dd", "ⅆ" },
+ { "DownArrowBar", "⤓" },
+ { "DownBreve", "̑" },
+ { "DownLeftRightVector", "⥐" },
+ { "DownLeftTeeVector", "⥞" },
+ { "DownLeftVectorBar", "⥖" },
+ { "DownRightTeeVector", "⥟" },
+ { "DownRightVectorBar", "⥗" },
+ { "ee", "ⅇ" },
+ { "EmptySmallSquare", "◻" },
+ { "EmptyVerySmallSquare", "▫" },
+ { "Equal", "⩵" },
+ { "FilledSmallSquare", "◼" },
+ { "FilledVerySmallSquare", "▪" },
+ { "GreaterGreater", "⪢" },
+ { "Hat", "^" },
+ { "HorizontalLine", "─" },
+ { "ic", "⁣" },
+ { "ii", "ⅈ" },
+ { "it", "⁢" },
+ { "larrb", "⇤" },
+ { "LeftDownTeeVector", "⥡" },
+ { "LeftDownVectorBar", "⥙" },
+ { "LeftRightVector", "⥎" },
+ { "LeftTeeVector", "⥚" },
+ { "LeftTriangleBar", "⧏" },
+ { "LeftUpDownVector", "⥑" },
+ { "LeftUpTeeVector", "⥠" },
+ { "LeftUpVectorBar", "⥘" },
+ { "LeftVectorBar", "⥒" },
+ { "LessLess", "⪡" },
+ { "mapstodown", "↧" },
+ { "mapstoleft", "↤" },
+ { "mapstoup", "↥" },
+ { "MediumSpace", " " },
+ { "nbump", "≎̸" },
+ { "nbumpe", "≏̸" },
+ { "nesim", "≂̸" },
+ { "NewLine", "
" },
+ { "NoBreak", "⁠" },
+ { "NotCupCap", "≭" },
+ { "NotHumpEqual", "≏̸" },
+ { "NotLeftTriangleBar", "⧏̸" },
+ { "NotNestedGreaterGreater", "⪢̸" },
+ { "NotNestedLessLess", "⪡̸" },
+ { "NotRightTriangleBar", "⧐̸" },
+ { "NotSquareSubset", "⊏̸" },
+ { "NotSquareSuperset", "⊐̸" },
+ { "NotSucceedsTilde", "≿̸" },
+ { "OverBar", "¯" },
+ { "OverBrace", "︷" },
+ { "OverBracket", "⎴" },
+ { "OverParenthesis", "︵" },
+ { "planckh", "ℎ" },
+ { "Product", "∏" },
+ { "rarrb", "⇥" },
+ { "RightDownTeeVector", "⥝" },
+ { "RightDownVectorBar", "⥕" },
+ { "RightTeeVector", "⥛" },
+ { "RightTriangleBar", "⧐" },
+ { "RightUpDownVector", "⥏" },
+ { "RightUpTeeVector", "⥜" },
+ { "RightUpVectorBar", "⥔" },
+ { "RightVectorBar", "⥓" },
+ { "RoundImplies", "⥰" },
+ { "RuleDelayed", "⧴" },
+ { "Tab", "	" },
+ { "ThickSpace", "   " },
+ { "UnderBar", "̲" },
+ { "UnderBrace", "︸" },
+ { "UnderBracket", "⎵" },
+ { "UnderParenthesis", "︶" },
+ { "UpArrowBar", "⤒" },
+ { "Upsilon", "Υ" },
+ { "VerticalLine", "|" },
+ { "VerticalSeparator", "❘" },
+ { "ZeroWidthSpace", "​" },
+ { "angle", "∠" },
+ { "ApplyFunction", "⁡" },
+ { "approx", "≈" },
+ { "approxeq", "≊" },
+ { "Assign", "≔" },
+ { "backcong", "≌" },
+ { "backepsilon", "϶" },
+ { "backprime", "‵" },
+ { "backsim", "∽" },
+ { "backsimeq", "⋍" },
+ { "Backslash", "∖" },
+ { "barwedge", "⌅" },
+ { "Because", "∵" },
+ { "because", "∵" },
+ { "Bernoullis", "ℬ" },
+ { "between", "≬" },
+ { "bigcap", "⋂" },
+ { "bigcirc", "◯" },
+ { "bigcup", "⋃" },
+ { "bigodot", "⨀" },
+ { "bigoplus", "⨁" },
+ { "bigotimes", "⨂" },
+ { "bigsqcup", "⨆" },
+ { "bigstar", "★" },
+ { "bigtriangledown", "▽" },
+ { "bigtriangleup", "△" },
+ { "biguplus", "⨄" },
+ { "bigvee", "⋁" },
+ { "bigwedge", "⋀" },
+ { "bkarow", "⤍" },
+ { "blacklozenge", "⧫" },
+ { "blacksquare", "▪" },
+ { "blacktriangle", "▴" },
+ { "blacktriangledown", "▾" },
+ { "blacktriangleleft", "◂" },
+ { "blacktriangleright", "▸" },
+ { "bot", "⊥" },
+ { "boxminus", "⊟" },
+ { "boxplus", "⊞" },
+ { "boxtimes", "⊠" },
+ { "Breve", "˘" },
+ { "bullet", "•" },
+ { "Bumpeq", "≎" },
+ { "bumpeq", "≏" },
+ { "CapitalDifferentialD", "ⅅ" },
+ { "Cayleys", "ℭ" },
+ { "Cedilla", "¸" },
+ { "CenterDot", "·" },
+ { "centerdot", "·" },
+ { "checkmark", "✓" },
+ { "circeq", "≗" },
+ { "circlearrowleft", "↺" },
+ { "circlearrowright", "↻" },
+ { "circledast", "⊛" },
+ { "circledcirc", "⊚" },
+ { "circleddash", "⊝" },
+ { "CircleDot", "⊙" },
+ { "circledR", "®" },
+ { "circledS", "Ⓢ" },
+ { "CircleMinus", "⊖" },
+ { "CirclePlus", "⊕" },
+ { "CircleTimes", "⊗" },
+ { "ClockwiseContourIntegral", "∲" },
+ { "CloseCurlyDoubleQuote", "”" },
+ { "CloseCurlyQuote", "’" },
+ { "clubsuit", "♣" },
+ { "coloneq", "≔" },
+ { "complement", "∁" },
+ { "complexes", "ℂ" },
+ { "Congruent", "≡" },
+ { "ContourIntegral", "∮" },
+ { "Coproduct", "∐" },
+ { "CounterClockwiseContourIntegral", "∳" },
+ { "CupCap", "≍" },
+ { "curlyeqprec", "⋞" },
+ { "curlyeqsucc", "⋟" },
+ { "curlyvee", "⋎" },
+ { "curlywedge", "⋏" },
+ { "curvearrowleft", "↶" },
+ { "curvearrowright", "↷" },
+ { "dbkarow", "⤏" },
+ { "ddagger", "‡" },
+ { "ddotseq", "⩷" },
+ { "Del", "∇" },
+ { "DiacriticalAcute", "´" },
+ { "DiacriticalDot", "˙" },
+ { "DiacriticalDoubleAcute", "˝" },
+ { "DiacriticalGrave", "`" },
+ { "DiacriticalTilde", "˜" },
+ { "Diamond", "⋄" },
+ { "diamond", "⋄" },
+ { "diamondsuit", "♦" },
+ { "DifferentialD", "ⅆ" },
+ { "digamma", "ϝ" },
+ { "div", "÷" },
+ { "divideontimes", "⋇" },
+ { "doteq", "≐" },
+ { "doteqdot", "≑" },
+ { "DotEqual", "≐" },
+ { "dotminus", "∸" },
+ { "dotplus", "∔" },
+ { "dotsquare", "⊡" },
+ { "doublebarwedge", "⌆" },
+ { "DoubleContourIntegral", "∯" },
+ { "DoubleDot", "¨" },
+ { "DoubleDownArrow", "⇓" },
+ { "DoubleLeftArrow", "⇐" },
+ { "DoubleLeftRightArrow", "⇔" },
+ { "DoubleLeftTee", "⫤" },
+ { "DoubleLongLeftArrow", "⟸" },
+ { "DoubleLongLeftRightArrow", "⟺" },
+ { "DoubleLongRightArrow", "⟹" },
+ { "DoubleRightArrow", "⇒" },
+ { "DoubleRightTee", "⊨" },
+ { "DoubleUpArrow", "⇑" },
+ { "DoubleUpDownArrow", "⇕" },
+ { "DoubleVerticalBar", "∥" },
+ { "DownArrow", "↓" },
+ { "Downarrow", "⇓" },
+ { "downarrow", "↓" },
+ { "DownArrowUpArrow", "⇵" },
+ { "downdownarrows", "⇊" },
+ { "downharpoonleft", "⇃" },
+ { "downharpoonright", "⇂" },
+ { "DownLeftVector", "↽" },
+ { "DownRightVector", "⇁" },
+ { "DownTee", "⊤" },
+ { "DownTeeArrow", "↧" },
+ { "drbkarow", "⤐" },
+ { "Element", "∈" },
+ { "emptyset", "∅" },
+ { "eqcirc", "≖" },
+ { "eqcolon", "≕" },
+ { "eqsim", "≂" },
+ { "eqslantgtr", "⪖" },
+ { "eqslantless", "⪕" },
+ { "EqualTilde", "≂" },
+ { "Equilibrium", "⇌" },
+ { "Exists", "∃" },
+ { "expectation", "ℰ" },
+ { "ExponentialE", "ⅇ" },
+ { "exponentiale", "ⅇ" },
+ { "fallingdotseq", "≒" },
+ { "ForAll", "∀" },
+ { "Fouriertrf", "ℱ" },
+ { "geq", "≥" },
+ { "geqq", "≧" },
+ { "geqslant", "⩾" },
+ { "gg", "≫" },
+ { "ggg", "⋙" },
+ { "gnapprox", "⪊" },
+ { "gneq", "⪈" },
+ { "gneqq", "≩" },
+ { "GreaterEqual", "≥" },
+ { "GreaterEqualLess", "⋛" },
+ { "GreaterFullEqual", "≧" },
+ { "GreaterLess", "≷" },
+ { "GreaterSlantEqual", "⩾" },
+ { "GreaterTilde", "≳" },
+ { "gtrapprox", "⪆" },
+ { "gtrdot", "⋗" },
+ { "gtreqless", "⋛" },
+ { "gtreqqless", "⪌" },
+ { "gtrless", "≷" },
+ { "gtrsim", "≳" },
+ { "gvertneqq", "≩︀" },
+ { "Hacek", "ˇ" },
+ { "hbar", "ℏ" },
+ { "heartsuit", "♥" },
+ { "HilbertSpace", "ℋ" },
+ { "hksearow", "⤥" },
+ { "hkswarow", "⤦" },
+ { "hookleftarrow", "↩" },
+ { "hookrightarrow", "↪" },
+ { "hslash", "ℏ" },
+ { "HumpDownHump", "≎" },
+ { "HumpEqual", "≏" },
+ { "iiiint", "⨌" },
+ { "iiint", "∭" },
+ { "Im", "ℑ" },
+ { "ImaginaryI", "ⅈ" },
+ { "imagline", "ℐ" },
+ { "imagpart", "ℑ" },
+ { "Implies", "⇒" },
+ { "in", "∈" },
+ { "integers", "ℤ" },
+ { "Integral", "∫" },
+ { "intercal", "⊺" },
+ { "Intersection", "⋂" },
+ { "intprod", "⨼" },
+ { "InvisibleComma", "⁣" },
+ { "InvisibleTimes", "⁢" },
+ { "langle", "〈" },
+ { "Laplacetrf", "ℒ" },
+ { "lbrace", "{" },
+ { "lbrack", "[" },
+ { "LeftAngleBracket", "〈" },
+ { "LeftArrow", "←" },
+ { "Leftarrow", "⇐" },
+ { "leftarrow", "←" },
+ { "LeftArrowBar", "⇤" },
+ { "LeftArrowRightArrow", "⇆" },
+ { "leftarrowtail", "↢" },
+ { "LeftCeiling", "⌈" },
+ { "LeftDoubleBracket", "〚" },
+ { "LeftDownVector", "⇃" },
+ { "LeftFloor", "⌊" },
+ { "leftharpoondown", "↽" },
+ { "leftharpoonup", "↼" },
+ { "leftleftarrows", "⇇" },
+ { "LeftRightArrow", "↔" },
+ { "Leftrightarrow", "⇔" },
+ { "leftrightarrow", "↔" },
+ { "leftrightarrows", "⇆" },
+ { "leftrightharpoons", "⇋" },
+ { "leftrightsquigarrow", "↭" },
+ { "LeftTee", "⊣" },
+ { "LeftTeeArrow", "↤" },
+ { "leftthreetimes", "⋋" },
+ { "LeftTriangle", "⊲" },
+ { "LeftTriangleEqual", "⊴" },
+ { "LeftUpVector", "↿" },
+ { "LeftVector", "↼" },
+ { "leq", "≤" },
+ { "leqq", "≦" },
+ { "leqslant", "⩽" },
+ { "lessapprox", "⪅" },
+ { "lessdot", "⋖" },
+ { "lesseqgtr", "⋚" },
+ { "lesseqqgtr", "⪋" },
+ { "LessEqualGreater", "⋚" },
+ { "LessFullEqual", "≦" },
+ { "LessGreater", "≶" },
+ { "lessgtr", "≶" },
+ { "lesssim", "≲" },
+ { "LessSlantEqual", "⩽" },
+ { "LessTilde", "≲" },
+ { "ll", "≪" },
+ { "llcorner", "⌞" },
+ { "Lleftarrow", "⇚" },
+ { "lmoustache", "⎰" },
+ { "lnapprox", "⪉" },
+ { "lneq", "⪇" },
+ { "lneqq", "≨" },
+ { "LongLeftArrow", "⟵" },
+ { "Longleftarrow", "⟸" },
+ { "longleftarrow", "⟵" },
+ { "LongLeftRightArrow", "⟷" },
+ { "Longleftrightarrow", "⟺" },
+ { "longleftrightarrow", "⟷" },
+ { "longmapsto", "⟼" },
+ { "LongRightArrow", "⟶" },
+ { "Longrightarrow", "⟹" },
+ { "longrightarrow", "⟶" },
+ { "looparrowleft", "↫" },
+ { "looparrowright", "↬" },
+ { "LowerLeftArrow", "↙" },
+ { "LowerRightArrow", "↘" },
+ { "lozenge", "◊" },
+ { "lrcorner", "⌟" },
+ { "Lsh", "↰" },
+ { "lvertneqq", "≨︀" },
+ { "maltese", "✠" },
+ { "mapsto", "↦" },
+ { "measuredangle", "∡" },
+ { "Mellintrf", "ℳ" },
+ { "MinusPlus", "∓" },
+ { "mp", "∓" },
+ { "multimap", "⊸" },
+ { "napprox", "≉" },
+ { "natural", "♮" },
+ { "naturals", "ℕ" },
+ { "nearrow", "↗" },
+ { "NegativeMediumSpace", "​" },
+ { "NegativeThickSpace", "​" },
+ { "NegativeThinSpace", "​" },
+ { "NegativeVeryThinSpace", "​" },
+ { "NestedGreaterGreater", "≫" },
+ { "NestedLessLess", "≪" },
+ { "nexists", "∄" },
+ { "ngeq", "≱" },
+ { "ngeqq", "≧̸" },
+ { "ngeqslant", "⩾̸" },
+ { "ngtr", "≯" },
+ { "nLeftarrow", "⇍" },
+ { "nleftarrow", "↚" },
+ { "nLeftrightarrow", "⇎" },
+ { "nleftrightarrow", "↮" },
+ { "nleq", "≰" },
+ { "nleqq", "≦̸" },
+ { "nleqslant", "⩽̸" },
+ { "nless", "≮" },
+ { "NonBreakingSpace", " " },
+ { "NotCongruent", "≢" },
+ { "NotDoubleVerticalBar", "∦" },
+ { "NotElement", "∉" },
+ { "NotEqual", "≠" },
+ { "NotEqualTilde", "≂̸" },
+ { "NotExists", "∄" },
+ { "NotGreater", "≯" },
+ { "NotGreaterEqual", "≱" },
+ { "NotGreaterFullEqual", "≦̸" },
+ { "NotGreaterGreater", "≫̸" },
+ { "NotGreaterLess", "≹" },
+ { "NotGreaterSlantEqual", "⩾̸" },
+ { "NotGreaterTilde", "≵" },
+ { "NotHumpDownHump", "≎̸" },
+ { "NotLeftTriangle", "⋪" },
+ { "NotLeftTriangleEqual", "⋬" },
+ { "NotLess", "≮" },
+ { "NotLessEqual", "≰" },
+ { "NotLessGreater", "≸" },
+ { "NotLessLess", "≪̸" },
+ { "NotLessSlantEqual", "⩽̸" },
+ { "NotLessTilde", "≴" },
+ { "NotPrecedes", "⊀" },
+ { "NotPrecedesEqual", "⪯̸" },
+ { "NotPrecedesSlantEqual", "⋠" },
+ { "NotReverseElement", "∌" },
+ { "NotRightTriangle", "⋫" },
+ { "NotRightTriangleEqual", "⋭" },
+ { "NotSquareSubsetEqual", "⋢" },
+ { "NotSquareSupersetEqual", "⋣" },
+ { "NotSubset", "⊂⃒" },
+ { "NotSubsetEqual", "⊈" },
+ { "NotSucceeds", "⊁" },
+ { "NotSucceedsEqual", "⪰̸" },
+ { "NotSucceedsSlantEqual", "⋡" },
+ { "NotSuperset", "⊃⃒" },
+ { "NotSupersetEqual", "⊉" },
+ { "NotTilde", "≁" },
+ { "NotTildeEqual", "≄" },
+ { "NotTildeFullEqual", "≇" },
+ { "NotTildeTilde", "≉" },
+ { "NotVerticalBar", "∤" },
+ { "nparallel", "∦" },
+ { "nprec", "⊀" },
+ { "npreceq", "⪯̸" },
+ { "nRightarrow", "⇏" },
+ { "nrightarrow", "↛" },
+ { "nshortmid", "∤" },
+ { "nshortparallel", "∦" },
+ { "nsimeq", "≄" },
+ { "nsubset", "⊂⃒" },
+ { "nsubseteq", "⊈" },
+ { "nsubseteqq", "⫅̸" },
+ { "nsucc", "⊁" },
+ { "nsucceq", "⪰̸" },
+ { "nsupset", "⊃⃒" },
+ { "nsupseteq", "⊉" },
+ { "nsupseteqq", "⫆̸" },
+ { "ntriangleleft", "⋪" },
+ { "ntrianglelefteq", "⋬" },
+ { "ntriangleright", "⋫" },
+ { "ntrianglerighteq", "⋭" },
+ { "nwarrow", "↖" },
+ { "oint", "∮" },
+ { "OpenCurlyDoubleQuote", "“" },
+ { "OpenCurlyQuote", "‘" },
+ { "orderof", "ℴ" },
+ { "parallel", "∥" },
+ { "PartialD", "∂" },
+ { "pitchfork", "⋔" },
+ { "PlusMinus", "±" },
+ { "pm", "±" },
+ { "Poincareplane", "ℌ" },
+ { "prec", "≺" },
+ { "precapprox", "⪷" },
+ { "preccurlyeq", "≼" },
+ { "Precedes", "≺" },
+ { "PrecedesEqual", "⪯" },
+ { "PrecedesSlantEqual", "≼" },
+ { "PrecedesTilde", "≾" },
+ { "preceq", "⪯" },
+ { "precnapprox", "⪹" },
+ { "precneqq", "⪵" },
+ { "precnsim", "⋨" },
+ { "precsim", "≾" },
+ { "primes", "ℙ" },
+ { "Proportion", "∷" },
+ { "Proportional", "∝" },
+ { "propto", "∝" },
+ { "quaternions", "ℍ" },
+ { "questeq", "≟" },
+ { "rangle", "〉" },
+ { "rationals", "ℚ" },
+ { "rbrace", "}" },
+ { "rbrack", "]" },
+ { "Re", "ℜ" },
+ { "realine", "ℛ" },
+ { "realpart", "ℜ" },
+ { "reals", "ℝ" },
+ { "ReverseElement", "∋" },
+ { "ReverseEquilibrium", "⇋" },
+ { "ReverseUpEquilibrium", "⥯" },
+ { "RightAngleBracket", "〉" },
+ { "RightArrow", "→" },
+ { "Rightarrow", "⇒" },
+ { "rightarrow", "→" },
+ { "RightArrowBar", "⇥" },
+ { "RightArrowLeftArrow", "⇄" },
+ { "rightarrowtail", "↣" },
+ { "RightCeiling", "⌉" },
+ { "RightDoubleBracket", "〛" },
+ { "RightDownVector", "⇂" },
+ { "RightFloor", "⌋" },
+ { "rightharpoondown", "⇁" },
+ { "rightharpoonup", "⇀" },
+ { "rightleftarrows", "⇄" },
+ { "rightleftharpoons", "⇌" },
+ { "rightrightarrows", "⇉" },
+ { "rightsquigarrow", "↝" },
+ { "RightTee", "⊢" },
+ { "RightTeeArrow", "↦" },
+ { "rightthreetimes", "⋌" },
+ { "RightTriangle", "⊳" },
+ { "RightTriangleEqual", "⊵" },
+ { "RightUpVector", "↾" },
+ { "RightVector", "⇀" },
+ { "risingdotseq", "≓" },
+ { "rmoustache", "⎱" },
+ { "Rrightarrow", "⇛" },
+ { "Rsh", "↱" },
+ { "searrow", "↘" },
+ { "setminus", "∖" },
+ { "ShortDownArrow", "↓" },
+ { "ShortLeftArrow", "←" },
+ { "shortmid", "∣" },
+ { "shortparallel", "∥" },
+ { "ShortRightArrow", "→" },
+ { "ShortUpArrow", "↑" },
+ { "simeq", "≃" },
+ { "SmallCircle", "∘" },
+ { "smallsetminus", "∖" },
+ { "spadesuit", "♠" },
+ { "Sqrt", "√" },
+ { "sqsubset", "⊏" },
+ { "sqsubseteq", "⊑" },
+ { "sqsupset", "⊐" },
+ { "sqsupseteq", "⊒" },
+ { "Square", "□" },
+ { "SquareIntersection", "⊓" },
+ { "SquareSubset", "⊏" },
+ { "SquareSubsetEqual", "⊑" },
+ { "SquareSuperset", "⊐" },
+ { "SquareSupersetEqual", "⊒" },
+ { "SquareUnion", "⊔" },
+ { "Star", "⋆" },
+ { "straightepsilon", "ε" },
+ { "straightphi", "ϕ" },
+ { "Subset", "⋐" },
+ { "subset", "⊂" },
+ { "subseteq", "⊆" },
+ { "subseteqq", "⫅" },
+ { "SubsetEqual", "⊆" },
+ { "subsetneq", "⊊" },
+ { "subsetneqq", "⫋" },
+ { "succ", "≻" },
+ { "succapprox", "⪸" },
+ { "succcurlyeq", "≽" },
+ { "Succeeds", "≻" },
+ { "SucceedsEqual", "⪰" },
+ { "SucceedsSlantEqual", "≽" },
+ { "SucceedsTilde", "≿" },
+ { "succeq", "⪰" },
+ { "succnapprox", "⪺" },
+ { "succneqq", "⪶" },
+ { "succnsim", "⋩" },
+ { "succsim", "≿" },
+ { "SuchThat", "∋" },
+ { "Sum", "∑" },
+ { "Superset", "⊃" },
+ { "SupersetEqual", "⊇" },
+ { "Supset", "⋑" },
+ { "supset", "⊃" },
+ { "supseteq", "⊇" },
+ { "supseteqq", "⫆" },
+ { "supsetneq", "⊋" },
+ { "supsetneqq", "⫌" },
+ { "swarrow", "↙" },
+ { "Therefore", "∴" },
+ { "therefore", "∴" },
+ { "thickapprox", "≈" },
+ { "thicksim", "∼" },
+ { "ThinSpace", " " },
+ { "Tilde", "∼" },
+ { "TildeEqual", "≃" },
+ { "TildeFullEqual", "≅" },
+ { "TildeTilde", "≈" },
+ { "toea", "⤨" },
+ { "tosa", "⤩" },
+ { "triangle", "▵" },
+ { "triangledown", "▿" },
+ { "triangleleft", "◃" },
+ { "trianglelefteq", "⊴" },
+ { "triangleq", "≜" },
+ { "triangleright", "▹" },
+ { "trianglerighteq", "⊵" },
+ { "TripleDot", "⃛" },
+ { "twoheadleftarrow", "↞" },
+ { "twoheadrightarrow", "↠" },
+ { "ulcorner", "⌜" },
+ { "Union", "⋃" },
+ { "UnionPlus", "⊎" },
+ { "UpArrow", "↑" },
+ { "Uparrow", "⇑" },
+ { "uparrow", "↑" },
+ { "UpArrowDownArrow", "⇅" },
+ { "UpDownArrow", "↕" },
+ { "Updownarrow", "⇕" },
+ { "updownarrow", "↕" },
+ { "UpEquilibrium", "⥮" },
+ { "upharpoonleft", "↿" },
+ { "upharpoonright", "↾" },
+ { "UpperLeftArrow", "↖" },
+ { "UpperRightArrow", "↗" },
+ { "upsilon", "υ" },
+ { "UpTee", "⊥" },
+ { "UpTeeArrow", "↥" },
+ { "upuparrows", "⇈" },
+ { "urcorner", "⌝" },
+ { "varepsilon", "ɛ" },
+ { "varkappa", "ϰ" },
+ { "varnothing", "∅" },
+ { "varphi", "φ" },
+ { "varpi", "ϖ" },
+ { "varpropto", "∝" },
+ { "varrho", "ϱ" },
+ { "varsigma", "ς" },
+ { "varsubsetneq", "⊊︀" },
+ { "varsubsetneqq", "⫋︀" },
+ { "varsupsetneq", "⊋︀" },
+ { "varsupsetneqq", "⫌︀" },
+ { "vartheta", "ϑ" },
+ { "vartriangleleft", "⊲" },
+ { "vartriangleright", "⊳" },
+ { "Vee", "⋁" },
+ { "vee", "∨" },
+ { "Vert", "‖" },
+ { "vert", "|" },
+ { "VerticalBar", "∣" },
+ { "VerticalTilde", "≀" },
+ { "VeryThinSpace", " " },
+ { "Wedge", "⋀" },
+ { "wedge", "∧" },
+ { "wp", "℘" },
+ { "wr", "≀" },
+ { "zeetrf", "ℨ" },
+ { 0, 0 }
+};
+
+// *******************************************************************
+// MmlDocument
+// *******************************************************************
+
+QString MmlDocument::fontName(QtMmlWidget::MmlFont type) const
+{
+ switch (type) {
+ case QtMmlWidget::NormalFont:
+ return m_normal_font_name;
+ case QtMmlWidget::FrakturFont:
+ return m_fraktur_font_name;
+ case QtMmlWidget::SansSerifFont:
+ return m_sans_serif_font_name;
+ case QtMmlWidget::ScriptFont:
+ return m_script_font_name;
+ case QtMmlWidget::MonospaceFont:
+ return m_monospace_font_name;
+ case QtMmlWidget::DoublestruckFont:
+ return m_doublestruck_font_name;
+ };
+
+ return QString::null;
+}
+
+void MmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name)
+{
+ switch (type) {
+ case QtMmlWidget::NormalFont:
+ m_normal_font_name = name;
+ break;
+ case QtMmlWidget::FrakturFont:
+ m_fraktur_font_name = name;
+ break;
+ case QtMmlWidget::SansSerifFont:
+ m_sans_serif_font_name = name;
+ break;
+ case QtMmlWidget::ScriptFont:
+ m_script_font_name = name;
+ break;
+ case QtMmlWidget::MonospaceFont:
+ m_monospace_font_name = name;
+ break;
+ case QtMmlWidget::DoublestruckFont:
+ m_doublestruck_font_name = name;
+ break;
+ };
+}
+
+Mml::NodeType domToMmlNodeType(const QDomNode &dom_node)
+{
+ Mml::NodeType mml_type = Mml::NoNode;
+
+ switch (dom_node.nodeType()) {
+ case QDomNode::ElementNode: {
+ QString tag = dom_node.nodeName();
+ const NodeSpec *spec = mmlFindNodeSpec(tag);
+
+ // treat urecognised tags as mrow
+ if (spec == 0)
+ mml_type = Mml::UnknownNode;
+ else
+ mml_type = spec->type;
+
+ break;
+ }
+ case QDomNode::TextNode:
+ mml_type = Mml::TextNode;
+ break;
+
+ case QDomNode::DocumentNode:
+ mml_type = Mml::MrowNode;
+ break;
+
+ case QDomNode::EntityReferenceNode:
+// qWarning("EntityReferenceNode: name=\"" + dom_node.nodeName() + "\" value=\"" + dom_node.nodeValue() + "\"");
+ break;
+
+ case QDomNode::AttributeNode:
+ case QDomNode::CDATASectionNode:
+ case QDomNode::EntityNode:
+ case QDomNode::ProcessingInstructionNode:
+ case QDomNode::CommentNode:
+ case QDomNode::DocumentTypeNode:
+ case QDomNode::DocumentFragmentNode:
+ case QDomNode::NotationNode:
+ case QDomNode::BaseNode:
+ case QDomNode::CharacterDataNode:
+ break;
+ }
+
+ return mml_type;
+}
+
+
+MmlDocument::MmlDocument()
+{
+ m_root_node = 0;
+
+ // Some defaults which happen to work on my computer,
+ // but probably won't work on other's
+#if defined(Q_WS_WIN)
+ m_normal_font_name = "Times New Roman";
+#else
+ m_normal_font_name = "Century Schoolbook L";
+#endif
+ m_fraktur_font_name = "Fraktur";
+ m_sans_serif_font_name = "Luxi Sans";
+ m_script_font_name = "Urw Chancery L";
+ m_monospace_font_name = "Luxi Mono";
+ m_doublestruck_font_name = "Doublestruck";
+ m_base_font_point_size = 16;
+ m_foreground_color = Qt::black;
+ m_background_color = Qt::white;
+}
+
+MmlDocument::~MmlDocument()
+{
+ clear();
+}
+
+void MmlDocument::clear()
+{
+ delete m_root_node;
+ m_root_node = 0;
+}
+
+void MmlDocument::dump() const
+{
+ if (m_root_node == 0)
+ return;
+
+ QString indent;
+ _dump(m_root_node, indent);
+}
+
+void MmlDocument::_dump(const MmlNode *node, QString &indent) const
+{
+ if (node == 0) return;
+
+ qWarning((indent + node->toStr()).toLatin1().data());
+
+ indent += " ";
+ const MmlNode *child = node->firstChild();
+ for (; child != 0; child = child->nextSibling())
+ _dump(child, indent);
+ indent.truncate(indent.length() - 2);
+}
+
+bool MmlDocument::setContent(QString text, QString *errorMsg,
+ int *errorLine, int *errorColumn)
+{
+ clear();
+
+ QString prefix = "<?xml version=\"2.0\"?>\n";
+ prefix.append(entityDeclarations());
+
+ uint prefix_lines = 0;
+ for (int i = 0; i < prefix.length(); ++i) {
+ if (prefix.at(i) == '\n')
+ ++prefix_lines;
+ }
+
+ QDomDocument dom;
+ if (!dom.setContent(prefix + text, false, errorMsg, errorLine, errorColumn)) {
+ if (errorLine != 0)
+ *errorLine -= prefix_lines;
+ return false;
+ }
+
+ // we don't have access to line info from now on
+ if (errorLine != 0) *errorLine = -1;
+ if (errorColumn != 0) *errorColumn = -1;
+
+ bool ok;
+ MmlNode *root_node = domToMml(dom, &ok, errorMsg);
+ if (!ok)
+ return false;
+
+ if (root_node == 0) {
+ if (errorMsg != 0)
+ *errorMsg = "empty document";
+ return false;
+ }
+
+ insertChild(0, root_node, 0);
+ layout();
+
+/* QFile of("/tmp/dump.xml");
+ of.open(IO_WriteOnly);
+ QTextStream os(&of);
+ os.setEncoding(QTextStream::UnicodeUTF8);
+ os << dom.toString(); */
+
+ return true;
+}
+
+void MmlDocument::layout()
+{
+ if (m_root_node == 0)
+ return;
+
+ m_root_node->layout();
+ m_root_node->stretch();
+// dump();
+}
+
+bool MmlDocument::insertChild(MmlNode *parent, MmlNode *new_node,
+ QString *errorMsg)
+{
+ if (new_node == 0)
+ return true;
+
+ Q_ASSERT(new_node->parent() == 0
+ && new_node->nextSibling() == 0
+ && new_node->previousSibling() == 0);
+
+ if (parent != 0) {
+ if (!mmlCheckChildType(parent->nodeType(), new_node->nodeType(), errorMsg))
+ return false;
+ }
+
+ if (parent == 0) {
+ if (m_root_node == 0)
+ m_root_node = new_node;
+ else {
+ MmlNode *n = m_root_node->lastSibling();
+ n->m_next_sibling = new_node;
+ new_node->m_previous_sibling = n;
+ }
+ }
+ else {
+ new_node->m_parent = parent;
+ if (parent->hasChildNodes()) {
+ MmlNode *n = parent->firstChild()->lastSibling();
+ n->m_next_sibling = new_node;
+ new_node->m_previous_sibling = n;
+ }
+ else parent->m_first_child = new_node;
+ }
+
+ return true;
+}
+
+MmlNode *MmlDocument::createNode(NodeType type,
+ const MmlAttributeMap &mml_attr,
+ const QString &mml_value,
+ QString *errorMsg)
+{
+ Q_ASSERT(type != NoNode);
+
+ MmlNode *mml_node = 0;
+
+ if (!mmlCheckAttributes(type, mml_attr, errorMsg))
+ return 0;
+
+ switch (type) {
+ case MiNode:
+ mml_node = new MmlMiNode(this, mml_attr);
+ break;
+ case MnNode:
+ mml_node = new MmlMnNode(this, mml_attr);
+ break;
+ case MfracNode:
+ mml_node = new MmlMfracNode(this, mml_attr);
+ break;
+ case MrowNode:
+ mml_node = new MmlMrowNode(this, mml_attr);
+ break;
+ case MsqrtNode:
+ mml_node = new MmlMsqrtNode(this, mml_attr);
+ break;
+ case MrootNode:
+ mml_node = new MmlMrootNode(this, mml_attr);
+ break;
+ case MsupNode:
+ mml_node = new MmlMsupNode(this, mml_attr);
+ break;
+ case MsubNode:
+ mml_node = new MmlMsubNode(this, mml_attr);
+ break;
+ case MsubsupNode:
+ mml_node = new MmlMsubsupNode(this, mml_attr);
+ break;
+ case MoNode:
+ mml_node = new MmlMoNode(this, mml_attr);
+ break;
+ case MstyleNode:
+ mml_node = new MmlMstyleNode(this, mml_attr);
+ break;
+ case TextNode:
+ mml_node = new MmlTextNode(mml_value, this);
+ break;
+ case MphantomNode:
+ mml_node = new MmlMphantomNode(this, mml_attr);
+ break;
+ case MfencedNode:
+ mml_node = new MmlMfencedNode(this, mml_attr);
+ break;
+ case MtableNode:
+ mml_node = new MmlMtableNode(this, mml_attr);
+ break;
+ case MtrNode:
+ mml_node = new MmlMtrNode(this, mml_attr);
+ break;
+ case MtdNode:
+ mml_node = new MmlMtdNode(this, mml_attr);
+ break;
+ case MoverNode:
+ mml_node = new MmlMoverNode(this, mml_attr);
+ break;
+ case MunderNode:
+ mml_node = new MmlMunderNode(this, mml_attr);
+ break;
+ case MunderoverNode:
+ mml_node = new MmlMunderoverNode(this, mml_attr);
+ break;
+ case MalignMarkNode:
+ mml_node = new MmlMalignMarkNode(this);
+ break;
+ case MerrorNode:
+ mml_node = new MmlMerrorNode(this, mml_attr);
+ break;
+ case MtextNode:
+ mml_node = new MmlMtextNode(this, mml_attr);
+ break;
+ case MpaddedNode:
+ mml_node = new MmlMpaddedNode(this, mml_attr);
+ break;
+ case MspaceNode:
+ mml_node = new MmlMspaceNode(this, mml_attr);
+ break;
+ case UnknownNode:
+ mml_node = new MmlUnknownNode(this, mml_attr);
+ break;
+ case NoNode:
+ mml_node = 0;
+ break;
+ }
+
+ return mml_node;
+}
+
+void MmlDocument::insertOperator(MmlNode *node, const QString &text)
+{
+ MmlNode *text_node = createNode(TextNode, MmlAttributeMap(), text, 0);
+ MmlNode *mo_node = createNode(MoNode, MmlAttributeMap(), QString::null, 0);
+
+ bool ok = insertChild(node, mo_node, 0);
+ Q_ASSERT( ok );
+ ok = insertChild(mo_node, text_node, 0);
+ Q_ASSERT( ok );
+}
+
+MmlNode *MmlDocument::domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg)
+{
+ // create the node
+
+ Q_ASSERT(ok != 0);
+
+ NodeType mml_type = domToMmlNodeType(dom_node);
+
+ if (mml_type == NoNode) {
+ *ok = true;
+ return 0;
+ }
+
+ QDomNamedNodeMap dom_attr = dom_node.attributes();
+ MmlAttributeMap mml_attr;
+ for (unsigned i = 0; i < dom_attr.length(); ++i) {
+ QDomNode attr_node = dom_attr.item(i);
+ Q_ASSERT(!attr_node.nodeName().isNull());
+ Q_ASSERT(!attr_node.nodeValue().isNull());
+ mml_attr[attr_node.nodeName()] = attr_node.nodeValue();
+ }
+
+ QString mml_value;
+ if (mml_type == TextNode)
+ mml_value = dom_node.nodeValue();
+ MmlNode *mml_node = createNode(mml_type, mml_attr, mml_value, errorMsg);
+ if (mml_node == 0) {
+ *ok = false;
+ return 0;
+ }
+
+ // create the node's children according to the child_spec
+
+ const NodeSpec *spec = mmlFindNodeSpec(mml_type);
+ QDomNodeList dom_child_list = dom_node.childNodes();
+ int child_cnt = dom_child_list.count();
+ MmlNode *mml_child = 0;
+
+ QString separator_list;
+ if (mml_type == MfencedNode)
+ separator_list = mml_node->explicitAttribute("separators", ",");
+
+ switch (spec->child_spec) {
+ case NodeSpec::ChildIgnore:
+ break;
+
+ case NodeSpec::ImplicitMrow:
+
+ if (child_cnt > 0) {
+ mml_child = createImplicitMrowNode(dom_node, ok, errorMsg);
+ if (!*ok) {
+ delete mml_node;
+ return 0;
+ }
+
+ if (!insertChild(mml_node, mml_child, errorMsg)) {
+ delete mml_node;
+ delete mml_child;
+ *ok = false;
+ return 0;
+ }
+ }
+
+ break;
+
+ default:
+ // exact ammount of children specified - check...
+ if (spec->child_spec != child_cnt) {
+ if (errorMsg != 0)
+ *errorMsg = QString("element ")
+ + spec->tag
+ + " requires exactly "
+ + QString::number(spec->child_spec)
+ + " arguments, got "
+ + QString::number(child_cnt);
+ delete mml_node;
+ *ok = false;
+ return 0;
+ }
+
+ // ...and continue just as in ChildAny
+
+ case NodeSpec::ChildAny:
+ if (mml_type == MfencedNode)
+ insertOperator(mml_node, mml_node->explicitAttribute("open", "("));
+
+ for (int i = 0; i < child_cnt; ++i) {
+ QDomNode dom_child = dom_child_list.item(i);
+
+ MmlNode *mml_child = domToMml(dom_child, ok, errorMsg);
+ if (!*ok) {
+ delete mml_node;
+ return 0;
+ }
+
+ if (mml_type == MtableNode && mml_child->nodeType() != MtrNode) {
+ MmlNode *mtr_node = createNode(MtrNode, MmlAttributeMap(), QString::null, 0);
+ insertChild(mml_node, mtr_node, 0);
+ if (!insertChild(mtr_node, mml_child, errorMsg)) {
+ delete mml_node;
+ delete mml_child;
+ *ok = false;
+ return 0;
+ }
+ }
+ else if (mml_type == MtrNode && mml_child->nodeType() != MtdNode) {
+ MmlNode *mtd_node = createNode(MtdNode, MmlAttributeMap(), QString::null, 0);
+ insertChild(mml_node, mtd_node, 0);
+ if (!insertChild(mtd_node, mml_child, errorMsg)) {
+ delete mml_node;
+ delete mml_child;
+ *ok = false;
+ return 0;
+ }
+ }
+ else {
+ if (!insertChild(mml_node, mml_child, errorMsg)) {
+ delete mml_node;
+ delete mml_child;
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (i < child_cnt - 1 && mml_type == MfencedNode && !separator_list.isEmpty()) {
+ QChar separator;
+ if (i >= (int)separator_list.length())
+ separator = separator_list.at(separator_list.length() - 1);
+ else
+ separator = separator_list[i];
+ insertOperator(mml_node, QString(separator));
+ }
+ }
+
+ if (mml_type == MfencedNode)
+ insertOperator(mml_node, mml_node->explicitAttribute("close", ")"));
+
+ break;
+ }
+
+ *ok = true;
+ return mml_node;
+}
+
+MmlNode *MmlDocument::createImplicitMrowNode(const QDomNode &dom_node, bool *ok,
+ QString *errorMsg)
+{
+ QDomNodeList dom_child_list = dom_node.childNodes();
+ int child_cnt = dom_child_list.count();
+
+ if (child_cnt == 0) {
+ *ok = true;
+ return 0;
+ }
+
+ if (child_cnt == 1)
+ return domToMml(dom_child_list.item(0), ok, errorMsg);
+
+ MmlNode *mml_node = createNode(MrowNode, MmlAttributeMap(),
+ QString::null, errorMsg);
+ Q_ASSERT(mml_node != 0); // there is no reason in heaven or hell for this to fail
+
+ for (int i = 0; i < child_cnt; ++i) {
+ QDomNode dom_child = dom_child_list.item(i);
+
+ MmlNode *mml_child = domToMml(dom_child, ok, errorMsg);
+ if (!*ok) {
+ delete mml_node;
+ return 0;
+ }
+
+ if (!insertChild(mml_node, mml_child, errorMsg)) {
+ delete mml_node;
+ delete mml_child;
+ *ok = false;
+ return 0;
+ }
+ }
+
+ return mml_node;
+}
+
+void MmlDocument::paint(QPainter *p, const QPoint &pos) const
+{
+ if (m_root_node == 0)
+ return;
+
+/* p->save();
+ p->setPen(Qt::blue);
+ p->drawLine(pos.x() - 5, pos.y(), pos.x() + 5, pos.y());
+ p->drawLine(pos.x(), pos.y() - 5, pos.x(), pos.y() + 5);
+ p->restore(); */
+
+ QRect mr = m_root_node->myRect();
+ m_root_node->setRelOrigin(pos - mr.topLeft());
+ m_root_node->paint(p);
+}
+
+QSize MmlDocument::size() const
+{
+ if (m_root_node == 0)
+ return QSize(0, 0);
+ return m_root_node->deviceRect().size();
+}
+
+
+
+
+// *******************************************************************
+// MmlNode
+// *******************************************************************
+
+
+MmlNode::MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
+{
+ m_parent = 0;
+ m_first_child = 0;
+ m_next_sibling = 0;
+ m_previous_sibling = 0;
+
+ m_node_type = type;
+ m_document = document;
+ m_attribute_map = attribute_map;
+
+ m_my_rect = m_parent_rect = QRect(0, 0, 0, 0);
+ m_rel_origin = QPoint(0, 0);
+ m_stretched = false;
+}
+
+MmlNode::~MmlNode()
+{
+ MmlNode *n = firstChild();
+ while (n != 0) {
+ MmlNode *tmp = n->nextSibling();
+ delete n;
+ n = tmp;
+ }
+}
+
+static QString rectToStr(const QRect &rect)
+{
+ return QString("[(%1, %2), %3x%4]")
+ .arg(rect.x())
+ .arg(rect.y())
+ .arg(rect.width())
+ .arg(rect.height());
+}
+
+QString MmlNode::toStr() const
+{
+ const NodeSpec *spec = mmlFindNodeSpec(nodeType());
+ Q_ASSERT(spec != 0);
+
+ return QString("%1 %2 mr=%3 pr=%4 dr=%5 ro=(%7, %8) str=%9")
+ .arg(spec->type_str)
+ .arg((unsigned long)this, 0, 16)
+ .arg(rectToStr(myRect()))
+ .arg(rectToStr(parentRect()))
+ .arg(rectToStr(deviceRect()))
+ .arg(m_rel_origin.x())
+ .arg(m_rel_origin.y())
+ .arg((int)isStretched());
+}
+
+int MmlNode::interpretSpacing(const QString &value, bool *ok) const
+{
+ return ::interpretSpacing(value, em(), ex(), ok);
+}
+
+int MmlNode::basePos() const
+{
+ QFontMetrics fm(font());
+ return fm.strikeOutPos();
+}
+
+int MmlNode::underlinePos() const
+{
+ QFontMetrics fm(font());
+ return basePos() + fm.underlinePos();
+}
+int MmlNode::overlinePos() const
+{
+ QFontMetrics fm(font());
+ return basePos() - fm.overlinePos();
+}
+
+MmlNode *MmlNode::lastSibling() const
+{
+ const MmlNode *n = this;
+ while (!n->isLastSibling())
+ n = n->nextSibling();
+ return const_cast<MmlNode*>(n);
+}
+
+MmlNode *MmlNode::firstSibling() const
+{
+ const MmlNode *n = this;
+ while (!n->isFirstSibling())
+ n = n->previousSibling();
+ return const_cast<MmlNode*>(n);
+}
+
+int MmlNode::em() const
+{
+ return QFontMetrics(font()).boundingRect('m').width();
+}
+
+int MmlNode::ex() const
+{
+ return QFontMetrics(font()).boundingRect('x').height();
+}
+
+int MmlNode::scriptlevel(const MmlNode *) const
+{
+ int parent_sl;
+ const MmlNode *p = parent();
+ if (p == 0)
+ parent_sl = 0;
+ else
+ parent_sl = p->scriptlevel(this);
+
+ QString expl_sl_str = explicitAttribute("scriptlevel");
+ if (expl_sl_str.isNull())
+ return parent_sl;
+
+ if (expl_sl_str.startsWith("+") || expl_sl_str.startsWith("-")) {
+ bool ok;
+ int expl_sl = expl_sl_str.toInt(&ok);
+ if (ok) {
+ return parent_sl + expl_sl;
+ }
+ else {
+ qWarning(("MmlNode::scriptlevel(): bad value " + expl_sl_str).toLatin1().data());
+ return parent_sl;
+ }
+ }
+
+ bool ok;
+ int expl_sl = expl_sl_str.toInt(&ok);
+ if (ok)
+ return expl_sl;
+
+
+ if (expl_sl_str == "+")
+ return parent_sl + 1;
+ else if (expl_sl_str == "-")
+ return parent_sl - 1;
+ else {
+ qWarning(("MmlNode::scriptlevel(): could not parse value: \"" + expl_sl_str + "\"").toLatin1().data());
+ return parent_sl;
+ }
+}
+
+QPoint MmlNode::devicePoint(const QPoint &p) const
+{
+ QRect mr = myRect();
+ QRect dr = deviceRect();
+
+ if (isStretched())
+ return dr.topLeft() + QPoint((p.x() - mr.left())*dr.width()/mr.width(),
+ (p.y() - mr.top())*dr.height()/mr.height());
+ else
+ return dr.topLeft() + p - mr.topLeft();
+}
+
+QString MmlNode::inheritAttributeFromMrow(const QString &name,
+ const QString &def) const
+{
+ const MmlNode *p = this;
+ for (; p != 0; p = p->parent()) {
+ if (p == this || p->nodeType() == MstyleNode) {
+ QString value = p->explicitAttribute(name);
+ if (!value.isNull())
+ return value;
+ }
+ }
+
+ return def;
+}
+
+QColor MmlNode::color() const
+{
+ // If we are child of <merror> return red
+ const MmlNode *p = this;
+ for (; p != 0; p = p->parent()) {
+ if (p->nodeType() == MerrorNode)
+ return QColor("red");
+ }
+
+ QString value_str = inheritAttributeFromMrow("mathcolor");
+ if (value_str.isNull())
+ value_str = inheritAttributeFromMrow("color");
+ if (value_str.isNull())
+ return QColor();
+
+ return QColor(value_str);
+}
+
+QColor MmlNode::background() const
+{
+ QString value_str = inheritAttributeFromMrow("mathbackground");
+ if (value_str.isNull())
+ value_str = inheritAttributeFromMrow("background");
+ if (value_str.isNull())
+ return QColor();
+
+ return QColor(value_str);
+}
+
+static void updateFontAttr(MmlAttributeMap &font_attr, const MmlNode *n,
+ const QString &name, const QString &preferred_name = QString::null)
+{
+ if (font_attr.contains(preferred_name) || font_attr.contains(name))
+ return;
+ QString value = n->explicitAttribute(name);
+ if (!value.isNull())
+ font_attr[name] = value;
+}
+
+static MmlAttributeMap collectFontAttributes(const MmlNode *node)
+{
+ MmlAttributeMap font_attr;
+
+ for (const MmlNode *n = node; n != 0; n = n->parent()) {
+ if (n == node || n->nodeType() == Mml::MstyleNode) {
+ updateFontAttr(font_attr, n, "mathvariant");
+ updateFontAttr(font_attr, n, "mathsize");
+
+ // depreciated attributes
+ updateFontAttr(font_attr, n, "fontsize", "mathsize");
+ updateFontAttr(font_attr, n, "fontweight", "mathvariant");
+ updateFontAttr(font_attr, n, "fontstyle", "mathvariant");
+ updateFontAttr(font_attr, n, "fontfamily", "mathvariant");
+ }
+ }
+
+ return font_attr;
+}
+
+QFont MmlNode::font() const
+{
+ QFont fn(document()->fontName(QtMmlWidget::NormalFont),
+ document()->baseFontPointSize());
+
+ int ps = fn.pointSize();
+ int sl = scriptlevel();
+ if (sl >= 0) {
+ for (int i = 0; i < sl; ++i)
+ ps = (int)(ps*g_script_size_multiplier);
+ }
+ else {
+ for (int i = 0; i > sl; --i)
+ ps = (int)(ps/g_script_size_multiplier);
+ }
+ if (ps < g_min_font_point_size)
+ ps = g_min_font_point_size;
+ fn.setPointSize(ps);
+
+ int em = QFontMetrics(fn).boundingRect('m').width();
+ int ex = QFontMetrics(fn).boundingRect('x').height();
+
+ MmlAttributeMap font_attr = collectFontAttributes(this);
+
+ if (font_attr.contains("mathvariant")) {
+ QString value = font_attr["mathvariant"];
+
+ bool ok;
+ uint mv = interpretMathVariant(value, &ok);
+
+ if (ok) {
+ if (mv & ScriptMV)
+ fn.setFamily(document()->fontName(QtMmlWidget::ScriptFont));
+
+ if (mv & FrakturMV)
+ fn.setFamily(document()->fontName(QtMmlWidget::FrakturFont));
+
+ if (mv & SansSerifMV)
+ fn.setFamily(document()->fontName(QtMmlWidget::SansSerifFont));
+
+ if (mv & MonospaceMV)
+ fn.setFamily(document()->fontName(QtMmlWidget::MonospaceFont));
+
+ if (mv & DoubleStruckMV)
+ fn.setFamily(document()->fontName(QtMmlWidget::DoublestruckFont));
+
+ if (mv & BoldMV)
+ fn.setBold(true);
+
+ if (mv & ItalicMV)
+ fn.setItalic(true);
+ }
+ }
+
+ if (font_attr.contains("mathsize")) {
+ QString value = font_attr["mathsize"];
+ fn = interpretMathSize(value, fn, em, ex, 0);
+ }
+
+ fn = interpretDepreciatedFontAttr(font_attr, fn, em, ex);
+
+ if (nodeType() == MiNode
+ && !font_attr.contains("mathvariant")
+ && !font_attr.contains("fontstyle")) {
+ const MmlMiNode *mi_node = (const MmlMiNode*) this;
+ if (mi_node->text().length() == 1)
+ fn.setItalic(true);
+ }
+
+ if (nodeType() == MoNode) {
+ fn.setItalic(false);
+ fn.setBold(false);
+ }
+
+ return fn;
+}
+
+QString MmlNode::explicitAttribute(const QString &name, const QString &def) const
+{
+ MmlAttributeMap::const_iterator it = m_attribute_map.find(name);
+ if (it != m_attribute_map.end())
+ return *it;
+ return def;
+}
+
+
+QRect MmlNode::parentRect() const
+{
+ if (isStretched())
+ return m_parent_rect;
+
+ QRect mr = myRect();
+ QPoint ro = relOrigin();
+
+ return QRect(ro + mr.topLeft(), mr.size());
+}
+
+
+void MmlNode::stretchTo(const QRect &rect)
+{
+ m_parent_rect = rect;
+ m_stretched = true;
+}
+
+void MmlNode::setRelOrigin(const QPoint &rel_origin)
+{
+ m_rel_origin = rel_origin + QPoint(-myRect().left(), 0);
+ m_stretched = false;
+}
+
+void MmlNode::updateMyRect()
+{
+ m_my_rect = symbolRect();
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling())
+ m_my_rect |= child->parentRect();
+}
+
+void MmlNode::layout()
+{
+ m_parent_rect = QRect(0, 0, 0, 0);
+ m_stretched = false;
+ m_rel_origin = QPoint(0, 0);
+
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling())
+ child->layout();
+
+ layoutSymbol();
+
+ updateMyRect();
+
+ if (parent() == 0)
+ m_rel_origin = QPoint(0, 0);
+}
+
+
+QRect MmlNode::deviceRect() const
+{
+ if (parent() == 0)
+ return QRect(relOrigin() + myRect().topLeft(), myRect().size());
+
+/* if (!isStretched()) {
+ QRect pdr = parent()->deviceRect();
+ QRect pmr = parent()->myRect();
+ QRect pr = parentRect();
+ QRect mr = myRect();
+ return QRect(pdr.left() + pr.left() - pmr.left(),
+ pdr.top() + pr.top() - pmr.top(),
+ mr.width(), mr.height());
+ }
+*/
+ QRect pdr = parent()->deviceRect();
+ QRect pr = parentRect();
+ QRect pmr = parent()->myRect();
+
+ float scale_w = 0;
+ if (pmr.width() != 0)
+ scale_w = (float)pdr.width()/pmr.width();
+ float scale_h = 0;
+ if (pmr.height() != 0)
+ scale_h = (float)pdr.height()/pmr.height();
+
+ return QRect(pdr.left() + ROUND((pr.left() - pmr.left())*scale_w),
+ pdr.top() + ROUND((pr.top() - pmr.top())*scale_h),
+ ROUND((pr.width()*scale_w)),
+ ROUND((pr.height()*scale_h)));
+}
+
+void MmlNode::layoutSymbol()
+{
+ // default behaves like an mrow
+
+ // now lay them out in a neat row, aligning their origins to my origin
+ int w = 0;
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling()) {
+ child->setRelOrigin(QPoint(w, 0));
+ w += child->parentRect().width() + 1;
+ }
+}
+
+void MmlNode::paint(QPainter *p)
+{
+ if (!myRect().isValid())
+ return;
+ p->save();
+ p->setViewport(deviceRect());
+ p->setWindow(myRect());
+
+
+ QColor fg = color();
+ QColor bg = background();
+ if (bg.isValid())
+ p->fillRect(myRect(), bg);
+ if (fg.isValid())
+ p->setPen(color());
+
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling())
+ child->paint(p);
+
+ paintSymbol(p);
+
+ p->restore();
+}
+
+void MmlNode::paintSymbol(QPainter *p) const
+{
+ if (g_draw_frames && myRect().isValid()) {
+ p->save();
+ p->setPen(Qt::red);
+ p->drawRect(m_my_rect);
+ QPen pen = p->pen();
+ pen.setStyle(Qt::DotLine);
+ p->setPen(pen);
+ p->drawLine(myRect().left(), 0, myRect().right(), 0);
+ p->restore();
+ }
+}
+
+void MmlNode::stretch()
+{
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling())
+ child->stretch();
+}
+
+QString MmlTokenNode::text() const
+{
+ QString result;
+
+ const MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling()) {
+ if (child->nodeType() != TextNode) continue;
+ if (!result.isEmpty())
+ result += ' ';
+ result += ((MmlTextNode*)child)->text();
+ }
+
+ return result;
+}
+
+MmlNode *MmlMfracNode::numerator() const
+{
+ MmlNode *node = firstChild();
+ Q_ASSERT(node != 0);
+ return node;
+}
+
+MmlNode *MmlMfracNode::denominator() const
+{
+ MmlNode *node = numerator()->nextSibling();
+ Q_ASSERT(node != 0);
+ return node;
+}
+
+QRect MmlMfracNode::symbolRect() const
+{
+ int num_width = numerator()->myRect().width();
+ int denom_width = denominator()->myRect().width();
+ int my_width = qMax(num_width, denom_width) + 4;
+
+ return QRect(-my_width/2, 0, my_width, 1);
+}
+
+void MmlMfracNode::layoutSymbol()
+{
+ MmlNode *num = numerator();
+ MmlNode *denom = denominator();
+
+ QRect num_rect = num->myRect();
+ QRect denom_rect = denom->myRect();
+
+ int spacing = (int)(g_mfrac_spacing*(num_rect.height() + denom_rect.height()));
+
+ num->setRelOrigin(QPoint(-num_rect.width()/2, - spacing - num_rect.bottom()));
+ denom->setRelOrigin(QPoint(-denom_rect.width()/2, spacing - denom_rect.top()));
+}
+
+static bool zeroLineThickness(const QString &s)
+{
+ if (s.length() == 0 || !s[0].isDigit())
+ return false;
+
+ for (int i = 0; i < s.length(); ++i) {
+ QChar c = s.at(i);
+ if (c.isDigit() && c != '0')
+ return false;
+ }
+ return true;
+}
+
+void MmlMfracNode::paintSymbol(QPainter *p) const
+{
+ QString linethickness_str = inheritAttributeFromMrow("linethickness", "1");
+
+ /* InterpretSpacing returns an int, which might be 0 even if the thickness
+ is > 0, though very very small. That's ok, because the painter then paints
+ a line of thickness 1. However, we have to run this check if the line
+ thickness really is zero */
+ if (!zeroLineThickness(linethickness_str)) {
+ bool ok;
+ int linethickness = interpretSpacing(linethickness_str, &ok);
+ if (!ok)
+ linethickness = 1;
+
+ p->save();
+ QPen pen = p->pen();
+ pen.setWidth(linethickness);
+ p->setPen(pen);
+ QSize s = myRect().size();
+ p->drawLine(-s.width()/2, 0, s.width()/2, 0);
+ p->restore();
+ }
+}
+
+MmlNode *MmlRootBaseNode::base() const
+{
+ MmlNode *node = firstChild();
+// Q_ASSERT(node != 0);
+ return node;
+}
+
+MmlNode *MmlRootBaseNode::index() const
+{
+ MmlNode *b = base();
+ if (b == 0)
+ return 0;
+ return b->nextSibling();
+}
+
+int MmlRootBaseNode::scriptlevel(const MmlNode *child) const
+{
+ int sl = MmlNode::scriptlevel();
+
+ MmlNode *i = index();
+ if (child != 0 && child == i)
+ return sl + 1;
+ else
+ return sl;
+}
+
+
+QRect MmlRootBaseNode::symbolRect() const
+{
+ MmlNode *b = base();
+ QRect base_rect;
+ if (b == 0)
+ base_rect = QRect(0, 0, 1, 1);
+ else
+ base_rect = base()->myRect();
+
+ int margin = (int)(g_mroot_base_margin*base_rect.height());
+ int tw = tailWidth();
+
+ return QRect(-tw, base_rect.top() - margin, tw,
+ base_rect.height() + 2*margin);
+}
+
+int MmlRootBaseNode::tailWidth() const
+{
+ QFontMetrics fm(font());
+ return fm.boundingRect(g_radical_char).width();
+}
+
+void MmlRootBaseNode::layoutSymbol()
+{
+ MmlNode *b = base();
+ QSize base_size;
+ if (b != 0) {
+ b->setRelOrigin(QPoint(0, 0));
+ base_size = base()->myRect().size();
+ } else
+ base_size = QSize(1, 1);
+
+ MmlNode *i = index();
+ if (i != 0) {
+ int tw = tailWidth();
+
+ QRect i_rect = i->myRect();
+ i->setRelOrigin(QPoint(-tw/2 - i_rect.width(),
+ -i_rect.bottom() - 4));
+ }
+}
+
+void MmlRootBaseNode::paintSymbol(QPainter *p) const
+{
+ QFont fn = font();
+
+ p->save();
+
+ QRect sr = symbolRect();
+
+ QRect r = sr;
+ r.moveTopLeft(devicePoint(sr.topLeft()));
+ p->setViewport(r);
+ p->setWindow(QFontMetrics(fn).boundingRect(g_radical_char));
+ p->setFont(font());
+ p->drawText(0, 0, QString(g_radical_char));
+
+ p->restore();
+
+ p->drawLine(sr.right(), sr.top(), myRect().right(), sr.top());
+}
+
+MmlTextNode::MmlTextNode(const QString &text, MmlDocument *document)
+ : MmlNode(TextNode, document, MmlAttributeMap())
+{
+ m_text = text.trimmed();
+ if (m_text == QString(QChar(0x62, 0x20)) // ⁢
+ || m_text == QString(QChar(0x63, 0x20)) // ⁣
+ || m_text == QString(QChar(0x61, 0x20))) // ⁡
+ m_text = "";
+}
+
+QString MmlTextNode::toStr() const
+{
+ return MmlNode::toStr() + ", text=\"" + m_text + "\"";
+}
+
+void MmlTextNode::paintSymbol(QPainter *p) const
+{
+ MmlNode::paintSymbol(p);
+
+ QFont fn = font();
+
+ QFontInfo fi(fn);
+// qWarning("MmlTextNode::paintSymbol(): requested: %s, used: %s, size=%d, italic=%d, bold=%d, text=\"%s\" sl=%d",
+// fn.family().latin1(), fi.family().latin1(), fi.pointSize(), (int)fi.italic(), (int)fi.bold(), m_text.latin1(), scriptlevel());
+
+ QFontMetrics fm(fn);
+
+ p->save();
+ p->setFont(fn);
+
+ p->drawText(0, fm.strikeOutPos(), m_text);
+ p->restore();
+}
+
+QRect MmlTextNode::symbolRect() const
+{
+ QFontMetrics fm(font());
+
+ QRect br = fm.boundingRect(m_text);
+ br.translate(0, fm.strikeOutPos());
+
+ return br;
+}
+
+MmlNode *MmlSubsupBaseNode::base() const
+{
+ MmlNode *b = firstChild();
+ Q_ASSERT(b != 0);
+ return b;
+}
+
+MmlNode *MmlSubsupBaseNode::sscript() const
+{
+ MmlNode *s = base()->nextSibling();
+ Q_ASSERT(s != 0);
+ return s;
+}
+
+int MmlSubsupBaseNode::scriptlevel(const MmlNode *child) const
+{
+ int sl = MmlNode::scriptlevel();
+
+ MmlNode *s = sscript();
+ if (child != 0 && child == s)
+ return sl + 1;
+ else
+ return sl;
+}
+
+void MmlMsupNode::layoutSymbol()
+{
+ MmlNode *b = base();
+ MmlNode *s = sscript();
+
+ b->setRelOrigin(QPoint(-b->myRect().width(), 0));
+ s->setRelOrigin(QPoint(0, b->myRect().top()));
+}
+
+void MmlMsubNode::layoutSymbol()
+{
+ MmlNode *b = base();
+ MmlNode *s = sscript();
+
+ b->setRelOrigin(QPoint(-b->myRect().width(), 0));
+ s->setRelOrigin(QPoint(0, b->myRect().bottom()));
+}
+
+MmlNode *MmlMsubsupNode::base() const
+{
+ MmlNode *b = firstChild();
+ Q_ASSERT(b != 0);
+ return b;
+}
+
+MmlNode *MmlMsubsupNode::subscript() const
+{
+ MmlNode *sub = base()->nextSibling();
+ Q_ASSERT(sub != 0);
+ return sub;
+}
+
+MmlNode *MmlMsubsupNode::superscript() const
+{
+ MmlNode *sup = subscript()->nextSibling();
+ Q_ASSERT(sup != 0);
+ return sup;
+}
+
+void MmlMsubsupNode::layoutSymbol()
+{
+ MmlNode *b = base();
+ MmlNode *sub = subscript();
+ MmlNode *sup = superscript();
+
+ b->setRelOrigin(QPoint(-b->myRect().width(), 0));
+ sub->setRelOrigin(QPoint(0, b->myRect().bottom()));
+ sup->setRelOrigin(QPoint(0, b->myRect().top()));
+}
+
+int MmlMsubsupNode::scriptlevel(const MmlNode *child) const
+{
+ int sl = MmlNode::scriptlevel();
+
+ MmlNode *sub = subscript();
+ MmlNode *sup = superscript();
+
+ if (child != 0 && (child == sup || child == sub))
+ return sl + 1;
+ else
+ return sl;
+}
+
+QString MmlMoNode::toStr() const
+{
+ return MmlNode::toStr() + QString(" form=%1").arg((int)form());
+}
+
+void MmlMoNode::layoutSymbol()
+{
+ MmlNode *child = firstChild();
+ if (child == 0)
+ return;
+
+ child->setRelOrigin(QPoint(0, 0));
+
+ if (m_oper_spec == 0)
+ m_oper_spec = mmlFindOperSpec(text(), form());
+}
+
+MmlMoNode::MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
+ : MmlTokenNode(MoNode, document, attribute_map)
+{
+ m_oper_spec = 0;
+}
+
+QString MmlMoNode::dictionaryAttribute(const QString &name) const
+{
+ const MmlNode *p = this;
+ for (; p != 0; p = p->parent()) {
+ if (p == this || p->nodeType() == MstyleNode) {
+ QString expl_attr = p->explicitAttribute(name);
+ if (!expl_attr.isNull())
+ return expl_attr;
+ }
+ }
+
+ return mmlDictAttribute(name, m_oper_spec);
+}
+
+Mml::FormType MmlMoNode::form() const
+{
+ QString value_str = inheritAttributeFromMrow("form");
+ if (!value_str.isNull()) {
+ bool ok;
+ FormType value = interpretForm(value_str, &ok);
+ if (ok)
+ return value;
+ else
+ qWarning("Could not convert %s to form", value_str.toLatin1().data());
+
+ }
+
+ // Default heuristic.
+ if (firstSibling() == (MmlNode*)this && lastSibling() != (MmlNode*)this)
+ return PrefixForm;
+ else if (lastSibling() == (MmlNode*)this && firstSibling() != (MmlNode*)this)
+ return PostfixForm;
+ else return InfixForm;
+
+}
+
+void MmlMoNode::stretch()
+{
+ if (parent() == 0)
+ return;
+
+ if (m_oper_spec == 0)
+ return;
+
+ if (m_oper_spec->stretch_dir == OperSpec::HStretch
+ && parent()->nodeType() == MrowNode
+ && (nextSibling() != 0 || previousSibling() != 0))
+ return;
+
+ QRect pmr = parent()->myRect();
+ QRect pr = parentRect();
+
+ switch (m_oper_spec->stretch_dir) {
+ case OperSpec::VStretch:
+ stretchTo(QRect(pr.left(), pmr.top(), pr.width(), pmr.height()));
+ break;
+ case OperSpec::HStretch:
+ stretchTo(QRect(pmr.left(), pr.top(), pmr.width(), pr.height()));
+ break;
+ case OperSpec::HVStretch:
+ stretchTo(pmr);
+ break;
+ case OperSpec::NoStretch:
+ break;
+ }
+}
+
+int MmlMoNode::lspace() const
+{
+ Q_ASSERT(m_oper_spec != 0);
+ if (parent() == 0
+ || (parent()->nodeType() != MrowNode
+ && parent()->nodeType() != MfencedNode
+ && parent()->nodeType() != UnknownNode)
+ || (previousSibling() == 0 && nextSibling() == 0))
+ return 0;
+ else
+ return interpretSpacing(dictionaryAttribute("lspace"), 0);
+}
+
+int MmlMoNode::rspace() const
+{
+ Q_ASSERT(m_oper_spec != 0);
+ if (parent() == 0
+ || (parent()->nodeType() != MrowNode
+ && parent()->nodeType() != MfencedNode
+ && parent()->nodeType() != UnknownNode)
+ || (previousSibling() == 0 && nextSibling() == 0))
+ return 0;
+ else
+ return interpretSpacing(dictionaryAttribute("rspace"), 0);
+}
+
+QRect MmlMoNode::symbolRect() const
+{
+ const MmlNode *child = firstChild();
+
+ if (child == 0)
+ return QRect(0, 0, 0, 0);
+
+ QRect cmr = child->myRect();
+
+ return QRect(-lspace(), cmr.top(),
+ cmr.width() + lspace() + rspace(), cmr.height());
+}
+
+int MmlMtableNode::rowspacing() const
+{
+ QString value = explicitAttribute("rowspacing");
+ if (value.isNull())
+ return ex();
+ bool ok;
+ int r = interpretSpacing(value, &ok);
+
+ if (ok)
+ return r;
+ else
+ return ex();
+}
+
+int MmlMtableNode::columnspacing() const
+{
+ QString value = explicitAttribute("columnspacing");
+ if (value.isNull())
+ return (int)(0.8*em());
+ bool ok;
+ int r = interpretSpacing(value, &ok);
+
+ if (ok)
+ return r;
+ else
+ return (int)(0.8*em());
+}
+
+uint MmlMtableNode::CellSizeData::colWidthSum() const
+{
+ uint w = 0;
+ for (int i = 0; i < col_widths.count(); ++i)
+ w += col_widths[i];
+ return w;
+}
+
+uint MmlMtableNode::CellSizeData::rowHeightSum() const
+{
+ uint h = 0;
+ for (int i = 0; i < row_heights.count(); ++i)
+ h += row_heights[i];
+ return h;
+}
+
+void MmlMtableNode::CellSizeData::init(const MmlNode *first_row)
+{
+ col_widths.clear();
+ row_heights.clear();
+
+ const MmlNode *mtr = first_row;
+ for (; mtr != 0; mtr = mtr->nextSibling()) {
+
+ Q_ASSERT(mtr->nodeType() == MtrNode);
+
+ int col_cnt = 0;
+ const MmlNode *mtd = mtr->firstChild();
+ for (; mtd != 0; mtd = mtd->nextSibling(), ++col_cnt) {
+
+ Q_ASSERT(mtd->nodeType() == MtdNode);
+
+ QRect mtdmr = mtd->myRect();
+
+ if (col_cnt == col_widths.count())
+ col_widths.append(mtdmr.width());
+ else
+ col_widths[col_cnt] = qMax(col_widths[col_cnt], mtdmr.width());
+ }
+
+ row_heights.append(mtr->myRect().height());
+ }
+}
+
+void MmlMtableNode::layoutSymbol()
+{
+ // Obtain natural widths of columns
+ m_cell_size_data.init(firstChild());
+
+ int col_spc = columnspacing();
+ int row_spc = rowspacing();
+ int frame_spc_hor = framespacing_hor();
+ QString columnwidth_attr = explicitAttribute("columnwidth", "auto");
+
+ // Is table width set by user? If so, set col_width_sum and never ever change it.
+ int col_width_sum = m_cell_size_data.colWidthSum();
+ bool width_set_by_user = false;
+ QString width_str = explicitAttribute("width", "auto");
+ if (width_str != "auto") {
+ bool ok;
+
+ int w = interpretSpacing(width_str, &ok);
+ if (ok) {
+ col_width_sum = w
+ - col_spc*(m_cell_size_data.numCols() - 1)
+ - frame_spc_hor*2;
+ width_set_by_user = true;
+ }
+ }
+
+ // Find out what kind of columns we are dealing with and set the widths of
+ // statically sized columns.
+ int fixed_width_sum = 0; // sum of widths of statically sized set columns
+ int auto_width_sum = 0; // sum of natural widths of auto sized columns
+ int relative_width_sum = 0; // sum of natural widths of relatively sized columns
+ double relative_fraction_sum = 0; // total fraction of width taken by relatively
+ // sized columns
+ int i;
+ for (i = 0; i < m_cell_size_data.numCols(); ++i) {
+ QString value = interpretListAttr(columnwidth_attr, i, "auto");
+
+ // Is it an auto sized column?
+ if (value == "auto" || value == "fit") {
+ auto_width_sum += m_cell_size_data.col_widths[i];
+ continue;
+ }
+
+ // Is it a statically sized column?
+ bool ok;
+ int w = interpretSpacing(value, &ok);
+ if (ok) {
+ // Yup, sets its width to the user specified value
+ m_cell_size_data.col_widths[i] = w;
+ fixed_width_sum += w;
+ continue;
+ }
+
+ // Is it a relatively sized column?
+ if (value.endsWith("%")) {
+ value.truncate(value.length() - 1);
+ double factor = value.toFloat(&ok);
+ if (ok && !value.isEmpty()) {
+ factor /= 100.0;
+ relative_width_sum += m_cell_size_data.col_widths[i];
+ relative_fraction_sum += factor;
+ if (!width_set_by_user) {
+ // If the table width was not set by the user, we are free to increase
+ // it so that the width of this column will be >= than its natural width
+ int min_col_width_sum = ROUND(m_cell_size_data.col_widths[i]/factor);
+ if (min_col_width_sum > col_width_sum)
+ col_width_sum = min_col_width_sum;
+ }
+ continue;
+ }
+ else
+ qWarning("MmlMtableNode::layoutSymbol(): could not parse value %s%%", value.toLatin1().data());
+ }
+
+ // Relatively sized column, but we failed to parse the factor. Treat is like an auto
+ // column.
+ auto_width_sum += m_cell_size_data.col_widths[i];
+ }
+
+ // Work out how much space remains for the auto olumns, after allocating
+ // the statically sized and the relatively sized columns.
+ int required_auto_width_sum = col_width_sum
+ - ROUND(relative_fraction_sum*col_width_sum)
+ - fixed_width_sum;
+
+ if (!width_set_by_user && required_auto_width_sum < auto_width_sum) {
+ if (relative_fraction_sum < 1)
+ col_width_sum = ROUND((fixed_width_sum + auto_width_sum)/(1 - relative_fraction_sum));
+ else
+ col_width_sum = fixed_width_sum + auto_width_sum + relative_width_sum;
+ required_auto_width_sum = auto_width_sum;
+ }
+
+ // Ratio by which we have to shring/grow all auto sized columns to make it all fit
+ double auto_width_scale = 1;
+ if (auto_width_sum > 0)
+ auto_width_scale = (float)required_auto_width_sum/auto_width_sum;
+
+ // Set correct sizes for the auto sized and the relatively sized columns.
+ for (i = 0; i < m_cell_size_data.numCols(); ++i) {
+ QString value = interpretListAttr(columnwidth_attr, i, "auto");
+
+ // Is it a relatively sized column?
+ if (value.endsWith("%")) {
+ bool ok;
+ int w = interpretPercentSpacing(value, col_width_sum, &ok);
+ if (ok)
+ m_cell_size_data.col_widths[i] = w;
+ else
+ // We're treating parsing errors here as auto sized columns
+ m_cell_size_data.col_widths[i]
+ = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]);
+ }
+ // Is it an auto sized column?
+ else if (value == "auto") {
+ m_cell_size_data.col_widths[i]
+ = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]);
+ }
+ }
+
+ QString s;
+ QList<int> &col_widths = m_cell_size_data.col_widths;
+ for (i = 0; i < col_widths.count(); ++i) {
+ s += QString("[w=%1 %2%%]")
+ .arg(col_widths[i])
+ .arg(100*col_widths[i]/m_cell_size_data.colWidthSum());
+ }
+// qWarning(s);
+
+ m_content_width = m_cell_size_data.colWidthSum()
+ + col_spc*(m_cell_size_data.numCols() - 1);
+ m_content_height = m_cell_size_data.rowHeightSum()
+ + row_spc*(m_cell_size_data.numRows() - 1);
+
+ int bottom = -m_content_height/2;
+ MmlNode *child = firstChild();
+ for (; child != 0; child = child->nextSibling()) {
+ Q_ASSERT(child->nodeType() == MtrNode);
+ MmlMtrNode *row = (MmlMtrNode*) child;
+
+ row->layoutCells(m_cell_size_data.col_widths, col_spc);
+ QRect rmr = row->myRect();
+ row->setRelOrigin(QPoint(0, bottom - rmr.top()));
+ bottom += rmr.height() + row_spc;
+ }
+}
+
+QRect MmlMtableNode::symbolRect() const
+{
+ int frame_spc_hor = framespacing_hor();
+ int frame_spc_ver = framespacing_ver();
+
+ return QRect(-frame_spc_hor,
+ -m_content_height/2 - frame_spc_ver,
+ m_content_width + 2*frame_spc_hor,
+ m_content_height + 2*frame_spc_ver);
+}
+
+Mml::FrameType MmlMtableNode::frame() const
+{
+ QString value = explicitAttribute("frame", "none");
+ return interpretFrameType(value, 0, 0);
+}
+
+Mml::FrameType MmlMtableNode::columnlines(int idx) const
+{
+ QString value = explicitAttribute("columnlines", "none");
+ return interpretFrameType(value, idx, 0);
+}
+
+Mml::FrameType MmlMtableNode::rowlines(int idx) const
+{
+ QString value = explicitAttribute("rowlines", "none");
+ return interpretFrameType(value, idx, 0);
+}
+
+void MmlMtableNode::paintSymbol(QPainter *p) const
+{
+ FrameType f = frame();
+ if (f != FrameNone) {
+ p->save();
+
+ QPen pen = p->pen();
+ if (f == FrameDashed)
+ pen.setStyle(Qt::DashLine);
+ else
+ pen.setStyle(Qt::SolidLine);
+ p->setPen(pen);
+ p->drawRect(myRect());
+
+ p->restore();
+ }
+
+ p->save();
+
+ int col_spc = columnspacing();
+ int row_spc = rowspacing();
+
+ QPen pen = p->pen();
+ int col_offset = 0;
+ int i;
+ for (i = 0; i < m_cell_size_data.numCols() - 1; ++i) {
+ FrameType f = columnlines(i);
+ col_offset += m_cell_size_data.col_widths[i];
+
+ if (f != FrameNone) {
+ if (f == FrameDashed)
+ pen.setStyle(Qt::DashLine);
+ else if (f == FrameSolid)
+ pen.setStyle(Qt::SolidLine);
+
+ p->setPen(pen);
+ int x = col_offset + col_spc/2;
+ p->drawLine(x, -m_content_height/2, x, m_content_height/2 );
+ }
+ col_offset += col_spc;
+ }
+
+ int row_offset = 0;
+ for (i = 0; i < m_cell_size_data.numRows() - 1; ++i) {
+ FrameType f = rowlines(i);
+ row_offset += m_cell_size_data.row_heights[i];
+
+ if (f != FrameNone) {
+ if (f == FrameDashed)
+ pen.setStyle(Qt::DashLine);
+ else if (f == FrameSolid)
+ pen.setStyle(Qt::SolidLine);
+
+ p->setPen(pen);
+ int y = row_offset + row_spc/2 - m_content_height/2;
+ p->drawLine(0, y, m_content_width, y);
+ }
+ row_offset += row_spc;
+ }
+
+ p->restore();
+}
+
+int MmlMtableNode::framespacing_ver() const
+{
+ if (frame() == FrameNone)
+ return (int)(0.2*em());
+
+ QString value = explicitAttribute("framespacing", "0.4em 0.5ex");
+
+ bool ok;
+ FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok);
+ if (ok)
+ return fs.m_ver;
+ else
+ return (int)(0.5*ex());
+}
+
+int MmlMtableNode::framespacing_hor() const
+{
+ if (frame() == FrameNone)
+ return (int)(0.2*em());
+
+ QString value = explicitAttribute("framespacing", "0.4em 0.5ex");
+
+ bool ok;
+ FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok);
+ if (ok)
+ return fs.m_hor;
+ else
+ return (int)(0.4*em());
+}
+
+void MmlMtrNode::layoutCells(const QList<int> &col_widths,
+ int col_spc)
+{
+ QRect mr = myRect();
+
+ MmlNode *child = firstChild();
+ int col_offset = 0;
+ uint colnum = 0;
+ for (; child != 0; child = child->nextSibling(), ++colnum) {
+ Q_ASSERT(child->nodeType() == MtdNode);
+ MmlMtdNode *mtd = (MmlMtdNode*) child;
+
+ QRect r = QRect(0, mr.top(), col_widths[colnum], mr.height());
+ mtd->setMyRect(r);
+ mtd->setRelOrigin(QPoint(col_offset, 0));
+ col_offset += col_widths[colnum] + col_spc;
+ }
+
+ updateMyRect();
+}
+
+int MmlMtdNode::scriptlevel(const MmlNode *child) const
+{
+ int sl = MmlNode::scriptlevel();
+ if (child != 0 && child == firstChild())
+ return sl + m_scriptlevel_adjust;
+ else
+ return sl;
+}
+
+void MmlMtdNode::setMyRect(const QRect &rect)
+{
+ MmlNode::setMyRect(rect);
+ MmlNode *child = firstChild();
+ if (child == 0)
+ return;
+
+ if (rect.width() < child->myRect().width()) {
+ while (rect.width() < child->myRect().width()
+ && child->font().pointSize() > g_min_font_point_size) {
+
+// qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d",
+// rect.width(), child->myRect().width(), m_scriptlevel_adjust);
+
+ ++m_scriptlevel_adjust;
+ child->layout();
+ }
+
+// qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d",
+// rect.width(), child->myRect().width(), m_scriptlevel_adjust);
+ }
+
+ QRect mr = myRect();
+ QRect cmr = child->myRect();
+
+ QPoint child_rel_origin;
+
+ switch (columnalign()) {
+ case ColAlignLeft:
+ child_rel_origin.setX(0);
+ break;
+ case ColAlignCenter:
+ child_rel_origin.setX(mr.left() + (mr.width() - cmr.width())/2);
+ break;
+ case ColAlignRight:
+ child_rel_origin.setX(mr.right() - cmr.width());
+ break;
+ }
+
+ switch (rowalign()) {
+ case RowAlignTop:
+ child_rel_origin.setY(mr.top() - cmr.top());
+ break;
+ case RowAlignCenter:
+ case RowAlignBaseline:
+ child_rel_origin.setY(mr.top() -cmr.top() + (mr.height() - cmr.height())/2);
+ break;
+ case RowAlignBottom:
+ child_rel_origin.setY(mr.bottom() - cmr.bottom());
+ break;
+ case RowAlignAxis:
+ child_rel_origin.setY(0);
+ break;
+ }
+
+ child->setRelOrigin(child_rel_origin);
+}
+
+uint MmlMtdNode::colNum()
+{
+ MmlNode *syb = previousSibling();
+
+ uint i = 0;
+ for (; syb != 0; syb = syb->previousSibling())
+ ++i;
+
+ return i;
+}
+
+uint MmlMtdNode::rowNum()
+{
+ MmlNode *row = parent()->previousSibling();
+
+ uint i = 0;
+ for (; row != 0; row = row->previousSibling())
+ ++i;
+
+ return i;
+}
+
+MmlMtdNode::ColAlign MmlMtdNode::columnalign()
+{
+ QString val = explicitAttribute("columnalign");
+ if (!val.isNull())
+ return interpretColAlign(val, 0, 0);
+
+ MmlNode *node = parent(); // <mtr>
+ if (node == 0)
+ return ColAlignCenter;
+
+ uint colnum = colNum();
+ val = node->explicitAttribute("columnalign");
+ if (!val.isNull())
+ return interpretColAlign(val, colnum, 0);
+
+ node = node->parent(); // <mtable>
+ if (node == 0)
+ return ColAlignCenter;
+
+ val = node->explicitAttribute("columnalign");
+ if (!val.isNull())
+ return interpretColAlign(val, colnum, 0);
+
+ return ColAlignCenter;
+}
+
+MmlMtdNode::RowAlign MmlMtdNode::rowalign()
+{
+ QString val = explicitAttribute("rowalign");
+ if (!val.isNull())
+ return interpretRowAlign(val, 0, 0);
+
+ MmlNode *node = parent(); // <mtr>
+ if (node == 0)
+ return RowAlignAxis;
+
+ uint rownum = rowNum();
+ val = node->explicitAttribute("rowalign");
+ if (!val.isNull())
+ return interpretRowAlign(val, rownum, 0);
+
+ node = node->parent(); // <mtable>
+ if (node == 0)
+ return RowAlignAxis;
+
+ val = node->explicitAttribute("rowalign");
+ if (!val.isNull())
+ return interpretRowAlign(val, rownum, 0);
+
+ return RowAlignAxis;
+}
+
+void MmlMoverNode::layoutSymbol()
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *over = base->nextSibling();
+ Q_ASSERT(over != 0);
+
+ QRect base_rect = base->myRect();
+ QRect over_rect = over->myRect();
+
+ int spacing = (int)(g_mfrac_spacing*(over_rect.height()
+ + base_rect.height()));
+
+ base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
+ over->setRelOrigin(QPoint(-over_rect.width()/2,
+ base_rect.top() - spacing - over_rect.bottom()));
+}
+
+int MmlMoverNode::scriptlevel(const MmlNode *node) const
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *over = base->nextSibling();
+ Q_ASSERT(over != 0);
+
+ int sl = MmlNode::scriptlevel();
+ if (node != 0 && node == over)
+ return sl + 1;
+ else
+ return sl;
+}
+
+void MmlMunderNode::layoutSymbol()
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *under = base->nextSibling();
+ Q_ASSERT(under != 0);
+
+ QRect base_rect = base->myRect();
+ QRect under_rect = under->myRect();
+
+ int spacing = (int)(g_mfrac_spacing*(under_rect.height() + base_rect.height()));
+
+ base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
+ under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top()));
+}
+
+int MmlMunderNode::scriptlevel(const MmlNode *node) const
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *under = base->nextSibling();
+ Q_ASSERT(under != 0);
+
+ int sl = MmlNode::scriptlevel();
+ if (node != 0 && node == under)
+ return sl + 1;
+ else
+ return sl;
+}
+
+void MmlMunderoverNode::layoutSymbol()
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *under = base->nextSibling();
+ Q_ASSERT(under != 0);
+ MmlNode *over = under->nextSibling();
+ Q_ASSERT(over != 0);
+
+ QRect base_rect = base->myRect();
+ QRect under_rect = under->myRect();
+ QRect over_rect = over->myRect();
+
+ int spacing = (int)(g_mfrac_spacing*( base_rect.height()
+ + under_rect.height()
+ + over_rect.height()) );
+
+ base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
+ under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top()));
+ over->setRelOrigin(QPoint(-over_rect.width()/2, base_rect.top() - spacing - under_rect.bottom()));
+}
+
+int MmlMunderoverNode::scriptlevel(const MmlNode *node) const
+{
+ MmlNode *base = firstChild();
+ Q_ASSERT(base != 0);
+ MmlNode *under = base->nextSibling();
+ Q_ASSERT(under != 0);
+ MmlNode *over = under->nextSibling();
+ Q_ASSERT(over != 0);
+
+ int sl = MmlNode::scriptlevel();
+ if (node != 0 && (node == under || node == over))
+ return sl + 1;
+ else
+ return sl;
+}
+
+int MmlMpaddedNode::interpretSpacing(QString value, int base_value, bool *ok) const
+{
+ if (ok != 0)
+ *ok = false;
+
+ value.replace(' ', "");
+
+ QString sign, factor_str, pseudo_unit;
+ bool percent = false;
+
+ // extract the sign
+ int idx = 0;
+ if (idx < value.length() && (value.at(idx) == '+' || value.at(idx) == '-'))
+ sign = value.at(idx++);
+
+ // extract the factor
+ while (idx < value.length() && (value.at(idx).isDigit() || value.at(idx) == '.'))
+ factor_str.append(value.at(idx++));
+
+ // extract the % sign
+ if (idx < value.length() && value.at(idx) == '%') {
+ percent = true;
+ ++idx;
+ }
+
+ // extract the pseudo-unit
+ pseudo_unit = value.mid(idx);
+
+ bool float_ok;
+ double factor = factor_str.toFloat(&float_ok);
+ if (!float_ok || factor < 0) {
+ qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
+ return 0;
+ }
+
+ if (percent)
+ factor /= 100.0;
+
+ QRect cr;
+ if (firstChild() == 0)
+ cr = QRect(0, 0, 0, 0);
+ else
+ cr = firstChild()->myRect();
+
+ int unit_size;
+
+ if (pseudo_unit.isEmpty())
+ unit_size = base_value;
+ else if (pseudo_unit == "width")
+ unit_size = cr.width();
+ else if (pseudo_unit == "height")
+ unit_size = -cr.top();
+ else if (pseudo_unit == "depth")
+ unit_size = cr.bottom();
+ else {
+ bool unit_ok;
+ unit_size = MmlNode::interpretSpacing("1" + pseudo_unit, &unit_ok);
+ if (!unit_ok) {
+ qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
+ return 0;
+ }
+ }
+
+ if (ok != 0)
+ *ok = true;
+
+ if (sign.isNull())
+ return (int)(factor*unit_size);
+ else if (sign == "+")
+ return base_value + (int)(factor*unit_size);
+ else // sign == "-"
+ return base_value - (int)(factor*unit_size);
+}
+
+int MmlMpaddedNode::lspace() const
+{
+ QString value = explicitAttribute("lspace");
+
+ if (value.isNull())
+ return 0;
+
+ bool ok;
+ int lspace = interpretSpacing(value, 0, &ok);
+
+ if (ok)
+ return lspace;
+
+ return 0;
+}
+
+int MmlMpaddedNode::width() const
+{
+ int child_width = 0;
+ if (firstChild() != 0)
+ child_width = firstChild()->myRect().width();
+
+ QString value = explicitAttribute("width");
+ if (value.isNull())
+ return child_width;
+
+ bool ok;
+ int w = interpretSpacing(value, child_width, &ok);
+ if (ok)
+ return w;
+
+ return child_width;
+}
+
+int MmlMpaddedNode::height() const
+{
+ QRect cr;
+ if (firstChild() == 0)
+ cr = QRect(0, 0, 0, 0);
+ else
+ cr = firstChild()->myRect();
+
+ QString value = explicitAttribute("height");
+ if (value.isNull())
+ return -cr.top();
+
+ bool ok;
+ int h = interpretSpacing(value, -cr.top(), &ok);
+ if (ok)
+ return h;
+
+ return -cr.top();
+}
+
+int MmlMpaddedNode::depth() const
+{
+ QRect cr;
+ if (firstChild() == 0)
+ cr = QRect(0, 0, 0, 0);
+ else
+ cr = firstChild()->myRect();
+
+ QString value = explicitAttribute("depth");
+ if (value.isNull())
+ return cr.bottom();
+
+ bool ok;
+ int h = interpretSpacing(value, cr.bottom(), &ok);
+ if (ok)
+ return h;
+
+ return cr.bottom();
+}
+
+void MmlMpaddedNode::layoutSymbol()
+{
+ MmlNode *child = firstChild();
+ if (child == 0)
+ return;
+
+ child->setRelOrigin(QPoint(0, 0));
+}
+
+
+QRect MmlMpaddedNode::symbolRect() const
+{
+ return QRect(-lspace(), -height(), lspace() + width(), height() + depth());
+}
+
+// *******************************************************************
+// QtMmlWidget
+// *******************************************************************
+
+/*!
+ \class QtMmlWidget
+
+ \brief The QtMmlWidget class renders mathematical formulas written in MathML 2.0.
+
+ QtMmlWidget implements the Presentation Markup subset of the
+ MathML 2.0 specification, with a few \link overview.html exceptions\endlink.
+
+ \code
+ QtMmlWidget *mmlWidget = new QtMmlWidget(this);
+ QString errorMsg;
+ int errorLine;
+ int errorColumn;
+ bool ok = mmlWidget->setContent(mmlText, &errorMsg, &errorLine, &errorColumn);
+ if (!ok) {
+ qWarning("MathML error: %s, Line: %d, Column: %d",
+ errorMsg.latin1(), errorLine, errorColumn);
+ }
+ \endcode
+
+*/
+
+
+/*!
+ \enum QtMmlWidget::MmlFont
+
+ This ennumerated type is used in fontName() and setFontName() to
+ specify a font type.
+
+ \value NormalFont The default font type, used to render
+ expressions for which no mathvariant or fontfamily is specified,
+ or for which the "normal" mathvariant is specified.
+
+ \value FrakturFont The font type used to render expressions for
+ which the "fraktur" mathvariant is specified.
+
+ \value SansSerifFont The font type used to render expressions
+ for which the "sans-serif" mathvariant is specified.
+
+ \value ScriptFont The font type used to render expressions
+ for which the "script" mathvariant is specified.
+
+ \value MonospaceFont The font type used to render expressions
+ for which the "monospace" mathvariant is specified.
+
+ \value DoublestruckFont The font type used to render expressions
+ for which the "doublestruck" mathvariant is specified.
+
+ \sa setFontName() fontName() setBaseFontPointSize() baseFontPointSize()
+*/
+
+/*!
+ Constructs a QtMmlWidget object. The \a parent
+ parameter is passed to QFrame's constructor.
+*/
+
+QtMmlWidget::QtMmlWidget(QWidget *parent)
+ : QFrame(parent)
+{
+ m_doc = new MmlDocument;
+}
+
+/*!
+ Destructs a QtMmlWidget object.
+*/
+
+QtMmlWidget::~QtMmlWidget()
+{
+ delete m_doc;
+}
+
+/*!
+ Returns the name of the font used to render the font \a type.
+
+ \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
+*/
+
+QString QtMmlWidget::fontName(MmlFont type) const
+{
+ return m_doc->fontName(type);
+}
+
+/*!
+ Sets the name of the font used to render the font \a type to \a name.
+
+ \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
+*/
+
+void QtMmlWidget::setFontName(MmlFont type, const QString &name)
+{
+ m_doc->setFontName(type, name);
+ m_doc->layout();
+ update();
+}
+
+/*!
+ If \a b is true, draws a red bounding rectangle around each
+ expression; if \a b is false, no such rectangle is drawn.
+ This is mostly useful for debugging MathML expressions.
+
+ \sa drawFrames()
+*/
+
+void QtMmlWidget::setDrawFrames(bool b)
+{
+ g_draw_frames = b;
+ update();
+}
+
+/*!
+ Returns true if each expression should be drawn with a red
+ bounding rectangle; otherwise returns false.
+ This is mostly useful for debugging MathML expressions.
+
+ \sa setDrawFrames()
+*/
+
+bool QtMmlWidget::drawFrames() const
+{
+ return g_draw_frames;
+}
+
+/*!
+ Clears the contents of this widget.
+*/
+void QtMmlWidget::clear()
+{
+ m_doc->clear();
+}
+
+/*!
+ Returns the point size of the font used to render expressions
+ whose scriptlevel is 0.
+
+ \sa setBaseFontPointSize() fontName() setFontName()
+*/
+
+int QtMmlWidget::baseFontPointSize() const
+{
+ return m_doc->baseFontPointSize();
+}
+
+/*!
+ Sets the point \a size of the font used to render expressions
+ whose scriptlevel is 0.
+
+ \sa baseFontPointSize() fontName() setFontName()
+*/
+
+void QtMmlWidget::setBaseFontPointSize(int size)
+{
+ if (size < g_min_font_point_size)
+ return;
+
+ m_doc->setBaseFontPointSize(size);
+ m_doc->layout();
+ update();
+}
+
+/*!
+ Returns the size of the formula in pixels.
+*/
+
+QSize QtMmlWidget::sizeHint() const
+{
+ QSize size = m_doc->size();
+ if (size == QSize(0, 0))
+ return QSize(100, 50);
+ return m_doc->size();
+}
+
+/*!
+ Sets the MathML expression to be rendered. The expression is given
+ in the string \a text. If the expression is successfully parsed,
+ this method returns true; otherwise it returns false. If an error
+ occured \a errorMsg is set to a diagnostic message, while \a
+ errorLine and \a errorColumn contain the location of the error.
+ Any of \a errorMsg, \a errorLine and \a errorColumn may be 0,
+ in which case they are not set.
+
+ \a text should contain MathML 2.0 presentation markup elements enclosed
+ in a <math> element.
+*/
+
+bool QtMmlWidget::setContent(const QString &text, QString *errorMsg,
+ int *errorLine, int *errorColumn)
+{
+ bool result = m_doc->setContent(text, errorMsg, errorLine, errorColumn);
+ if (result)
+ update();
+ return result;
+}
+
+/*! \internal */
+
+void QtMmlWidget::paintEvent(QPaintEvent *e)
+{
+ QFrame::paintEvent(e);
+ QPainter p(this);
+ if (e->rect().intersects(contentsRect()))
+ p.setClipRegion(e->region().intersect(contentsRect()));
+
+ QSize s = m_doc->size();
+ int x = (width() - s.width())/2;
+ int y = (height() - s.height())/2;
+ m_doc->paint(&p, QPoint(x, y));
+}
+
+/*! \internal */
+
+void QtMmlWidget::dump() const
+{
+ m_doc->dump();
+}
+
+// *******************************************************************
+// Static helper functions
+// *******************************************************************
+
+static QString entityDeclarations()
+{
+ QString result = "<!DOCTYPE math [\n";
+
+ const EntitySpec *ent = g_xml_entity_data;
+ for (; ent->name != 0; ++ent) {
+ result += "\t<!ENTITY " + QString(ent->name) + " \"" + ent->value + "\">\n";
+ }
+
+ result += "]>\n";
+
+ return result;
+}
+
+static int interpretSpacing(QString value, int em, int ex, bool *ok)
+{
+ if (ok != 0)
+ *ok = true;
+
+ if (value == "thin")
+ return 1;
+
+ if (value == "medium")
+ return 2;
+
+ if (value == "thick")
+ return 3;
+
+ struct HSpacingValue {
+ const char *name;
+ float factor;
+ };
+
+ static const HSpacingValue g_h_spacing_data[] =
+ {
+ { "veryverythinmathspace", (float) 0.0555556 },
+ { "verythinmathspace", (float) 0.111111 },
+ { "thinmathspace", (float) 0.166667 },
+ { "mediummathspace", (float) 0.222222 },
+ { "thickmathspace", (float) 0.277778 },
+ { "verythickmathspace", (float) 0.333333 },
+ { "veryverythickmathspace", (float) 0.388889 },
+ { 0, (float) 0 }
+ };
+
+ const HSpacingValue *v = g_h_spacing_data;
+ for (; v->name != 0; ++v) {
+ if (value == v->name)
+ return (int)(em*v->factor);
+ }
+
+ if (value.endsWith("em")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0)
+ return (int)(em*factor);
+ else {
+ qWarning("interpretSpacing(): could not parse \"%sem\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (value.endsWith("ex")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0)
+ return (int)(ex*factor);
+ else {
+ qWarning("interpretSpacing(): could not parse \"%sex\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (value.endsWith("cm")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0) {
+ Q_ASSERT(qApp->desktop() != 0);
+ QDesktopWidget *dw = qApp->desktop();
+ Q_ASSERT(dw->width() != 0);
+ Q_ASSERT(dw->widthMM() != 0);
+ return (int)(factor*10*dw->width()/dw->widthMM());
+ }
+ else {
+ qWarning("interpretSpacing(): could not parse \"%scm\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (value.endsWith("mm")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0) {
+ Q_ASSERT(qApp->desktop() != 0);
+ QDesktopWidget *dw = qApp->desktop();
+ Q_ASSERT(dw->width() != 0);
+ Q_ASSERT(dw->widthMM() != 0);
+ return (int)(factor*dw->width()/dw->widthMM());
+ }
+ else {
+ qWarning("interpretSpacing(): could not parse \"%smm\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (value.endsWith("in")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0) {
+ Q_ASSERT(qApp->desktop() != 0);
+ QDesktopWidget *dw = qApp->desktop();
+ Q_ASSERT(dw->width() != 0);
+ Q_ASSERT(dw->widthMM() != 0);
+ return (int)(factor*10*dw->width()/(2.54*dw->widthMM()));
+ }
+ else {
+ qWarning("interpretSpacing(): could not parse \"%sin\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ if (value.endsWith("px")) {
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ int i = (int) value.toFloat(&float_ok);
+ if (float_ok && i >= 0)
+ return i;
+ else {
+ qWarning("interpretSpacing(): could not parse \"%spx\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+ }
+
+ bool float_ok;
+ int i = (int)value.toFloat(&float_ok);
+ if (float_ok && i >= 0)
+ return i;
+
+ qWarning("interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+}
+
+static int interpretPercentSpacing(QString value, int base, bool *ok)
+{
+ if (!value.endsWith("%")) {
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+
+ value.truncate(value.length() - 1);
+ bool float_ok;
+ float factor = value.toFloat(&float_ok);
+ if (float_ok && factor >= 0) {
+ if (ok != 0)
+ *ok = true;
+ return (int)(base*factor/100.0);
+ }
+
+ qWarning("interpretPercentSpacing(): could not parse \"%s%%\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+}
+
+static int interpretPointSize(QString value, bool *ok)
+{
+ if (!value.endsWith("pt")) {
+ if (ok != 0)
+ *ok = false;
+ return 0;
+ }
+
+ value.truncate(value.length() - 2);
+ bool float_ok;
+ int pt_size = (int) value.toFloat(&float_ok);
+ if (float_ok && pt_size > 0) {
+ if (ok != 0)
+ *ok = true;
+ return pt_size;
+ }
+
+ qWarning("interpretPointSize(): could not parse \"%spt\"", value.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return 0;
+}
+
+static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type)
+{
+ const NodeSpec *spec = g_node_spec_data;
+ for (; spec->type != Mml::NoNode; ++spec) {
+ if (type == spec->type) return spec;
+ }
+ return 0;
+}
+
+static const NodeSpec *mmlFindNodeSpec(const QString &tag)
+{
+ const NodeSpec *spec = g_node_spec_data;
+ for (; spec->type != Mml::NoNode; ++spec) {
+ if (tag == spec->tag) return spec;
+ }
+ return 0;
+}
+
+static bool mmlCheckChildType(Mml::NodeType parent_type, Mml::NodeType child_type,
+ QString *error_str)
+{
+ if (parent_type == Mml::UnknownNode || child_type == Mml::UnknownNode)
+ return true;
+
+ const NodeSpec *child_spec = mmlFindNodeSpec(child_type);
+ const NodeSpec *parent_spec = mmlFindNodeSpec(parent_type);
+
+ Q_ASSERT(parent_spec != 0);
+ Q_ASSERT(child_spec != 0);
+
+ QString allowed_child_types(parent_spec->child_types);
+ // null list means any child type is valid
+ if (allowed_child_types.isNull())
+ return true;
+
+ QString child_type_str = QString(" ") + child_spec->type_str + " ";
+ if (!allowed_child_types.contains(child_type_str)) {
+ if (error_str != 0)
+ *error_str = QString("illegal child ")
+ + child_spec->type_str
+ + " for parent "
+ + parent_spec->type_str;
+ return false;
+ }
+
+ return true;
+}
+
+static bool mmlCheckAttributes(Mml::NodeType child_type, const MmlAttributeMap &attr,
+ QString *error_str)
+{
+ const NodeSpec *spec = mmlFindNodeSpec(child_type);
+ Q_ASSERT(spec != 0);
+
+ QString allowed_attr(spec->attributes);
+ // empty list means any attr is valid
+ if (allowed_attr.isEmpty())
+ return true;
+
+ MmlAttributeMap::const_iterator it = attr.begin(), end = attr.end();
+ for (; it != end; ++it) {
+ QString name = it.key();
+
+ if (name.indexOf(':') != -1)
+ continue;
+
+ QString padded_name = " " + name + " ";
+ if (!allowed_attr.contains(padded_name)) {
+ if (error_str != 0)
+ *error_str = QString("illegal attribute ")
+ + name
+ + " in "
+ + spec->type_str;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int attributeIndex(const QString &name)
+{
+ for (unsigned i = 0; i < g_oper_spec_rows; ++i) {
+ if (name == g_oper_spec_names[i])
+ return i;
+ }
+ return -1;
+}
+
+static QString decodeEntityValue(QString literal)
+{
+ QString result;
+
+ while (!literal.isEmpty()) {
+
+ if (!literal.startsWith("&#")) {
+ qWarning(("decodeEntityValue(): bad entity literal: \"" + literal + "\"").toLatin1().data());
+ return QString::null;
+ }
+
+ literal = literal.right(literal.length() - 2);
+
+ int i = literal.indexOf(';');
+ if (i == -1) {
+ qWarning(("decodeEntityValue(): bad entity literal: \"" + literal + "\"").toLatin1().data());
+ return QString::null;
+ }
+
+ QString char_code = literal.left(i);
+ literal = literal.right(literal.length() - i - 1);
+
+ if (char_code.isEmpty()) {
+ qWarning(("decodeEntityValue(): bad entity literal: \"" + literal + "\"").toLatin1().data());
+ return QString::null;
+ }
+
+ if (char_code.at(0) == 'x') {
+ char_code = char_code.right(char_code.length() - 1);
+ bool ok;
+ unsigned c = char_code.toUInt(&ok, 16);
+ if (!ok) {
+ qWarning(("decodeEntityValue(): bad entity literal: \"" + literal + "\"").toLatin1().data());
+ return QString::null;
+ }
+ result += QChar(c);
+ }
+ else {
+ bool ok;
+ unsigned c = char_code.toUInt(&ok, 10);
+ if (!ok) {
+ qWarning(("decodeEntityValue(): bad entity literal: \"" + literal + "\"").toLatin1().data());
+ return QString::null;
+ }
+ result += QChar(c);
+ }
+ }
+
+ return result;
+}
+
+static const EntitySpec *searchEntitySpecData(const QString &value, const EntitySpec *from = 0)
+{
+ const EntitySpec *ent = from;
+ if (ent == 0)
+ ent = g_xml_entity_data;
+ for (; ent->name != 0; ++ent) {
+ QString ent_value = decodeEntityValue(ent->value);
+ if (value == ent_value)
+ return ent;
+ }
+ return 0;
+}
+
+struct OperSpecSearchResult
+{
+ OperSpecSearchResult() { prefix_form = infix_form = postfix_form = 0; }
+
+ const OperSpec *prefix_form,
+ *infix_form,
+ *postfix_form;
+
+ const OperSpec *&getForm(Mml::FormType f);
+ bool haveForm(Mml::FormType f)
+ { return getForm(f) != 0; }
+ void addForm(const OperSpec *spec)
+ { getForm(spec->form) = spec; }
+};
+
+const OperSpec *&OperSpecSearchResult::getForm(Mml::FormType f)
+{
+ switch (f) {
+ case Mml::PrefixForm:
+ return prefix_form;
+ case Mml::InfixForm:
+ return infix_form;
+ case Mml::PostfixForm:
+ return postfix_form;
+ }
+ return postfix_form; // just to avoid warning
+}
+
+/*
+ Searches g_oper_spec_data and returns any instance of operator name. There may
+ be more instances, but since the list is sorted, they will be next to each other.
+*/
+static const OperSpec *searchOperSpecData(const QString &name)
+{
+ const char *name_latin1 = name.toLatin1().data();
+
+ // binary search
+ // establish invariant g_oper_spec_data[begin].name < name < g_oper_spec_data[end].name
+
+ int cmp = qstrcmp(name_latin1, g_oper_spec_data[0].name);
+ if (cmp < 0)
+ return 0;
+
+ if (cmp == 0)
+ return g_oper_spec_data;
+
+ uint begin = 0;
+ uint end = g_oper_spec_count;
+
+ // invariant holds
+ while (end - begin > 1) {
+ uint mid = (begin + end)/2;
+
+ const OperSpec *spec = g_oper_spec_data + mid;
+ int cmp = qstrcmp(name_latin1, spec->name);
+ if (cmp < 0)
+ end = mid;
+ else if (cmp > 0)
+ begin = mid;
+ else
+ return spec;
+ }
+
+ return 0;
+}
+
+/*
+ This searches g_oper_spec_data until at least one name in name_list is found with FormType form,
+ or until name_list is exhausted. The idea here is that if we don't find the operator in the
+ specified form, we still want to use some other available form of that operator.
+*/
+static OperSpecSearchResult _mmlFindOperSpec(const QStringList &name_list, Mml::FormType form)
+{
+ OperSpecSearchResult result;
+
+ QStringList::const_iterator it = name_list.begin();
+ for (; it != name_list.end(); ++it) {
+ const QString &name = *it;
+
+ const OperSpec *spec = searchOperSpecData(name);
+
+ if (spec == 0)
+ continue;
+
+ const char *name_latin1 = name.toLatin1().data();
+
+ // backtrack to the first instance of name
+ while (spec > g_oper_spec_data && qstrcmp((spec - 1)->name, name_latin1) == 0)
+ --spec;
+
+ // iterate over instances of name until the instances are exhausted or until we
+ // find an instance in the specified form.
+ do {
+ result.addForm(spec++);
+ if (result.haveForm(form))
+ break;
+ } while (qstrcmp(spec->name, name_latin1) == 0);
+
+ if (result.haveForm(form))
+ break;
+ }
+
+ return result;
+}
+
+/*
+ text is a string between <mo> and </mo>. It can be a character ('+'), an
+ entity reference ("∞") or a character reference ("∞"). Our
+ job is to find an operator spec in the operator dictionary (g_oper_spec_data)
+ that matches text. Things are further complicated by the fact, that many
+ operators come in several forms (prefix, infix, postfix).
+
+ If available, this function returns an operator spec matching text in the specified
+ form. If such operator is not available, returns an operator spec that matches
+ text, but of some other form in the preference order specified by the MathML spec.
+ If that's not available either, returns the default operator spec.
+*/
+static const OperSpec *mmlFindOperSpec(const QString &text, Mml::FormType form)
+{
+ QStringList name_list;
+ name_list.append(text);
+
+ // First, just try to find text in the operator dictionary.
+ OperSpecSearchResult result = _mmlFindOperSpec(name_list, form);
+
+ if (!result.haveForm(form)) {
+ // Try to find other names for the operator represented by text.
+
+ const EntitySpec *ent = 0;
+ for (;;) {
+ ent = searchEntitySpecData(text, ent);
+ if (ent == 0)
+ break;
+ name_list.append('&' + QString(ent->name) + ';');
+ ++ent;
+ }
+
+ result = _mmlFindOperSpec(name_list, form);
+ }
+
+ const OperSpec *spec = result.getForm(form);
+ if (spec != 0)
+ return spec;
+
+ spec = result.getForm(Mml::InfixForm);
+ if (spec != 0)
+ return spec;
+
+ spec = result.getForm(Mml::PostfixForm);
+ if (spec != 0)
+ return spec;
+
+ spec = result.getForm(Mml::PrefixForm);
+ if (spec != 0)
+ return spec;
+
+ return &g_oper_spec_defaults;
+}
+
+static QString mmlDictAttribute(const QString &name, const OperSpec *spec)
+{
+ int i = attributeIndex(name);
+ if (i == -1)
+ return QString::null;
+ else
+ return spec->attributes[i];
+}
+
+static uint interpretMathVariant(const QString &value, bool *ok)
+{
+ struct MathVariantValue {
+ const char *value;
+ uint mv;
+ };
+
+ static const MathVariantValue g_mv_data[] =
+ {
+ { "normal", Mml::NormalMV },
+ { "bold", Mml::BoldMV },
+ { "italic", Mml::ItalicMV },
+ { "bold-italic", Mml::BoldMV | Mml::ItalicMV },
+ { "double-struck", Mml::DoubleStruckMV },
+ { "bold-fraktur", Mml::BoldMV | Mml::FrakturMV },
+ { "script", Mml::ScriptMV },
+ { "bold-script", Mml::BoldMV | Mml::ScriptMV },
+ { "fraktur", Mml::FrakturMV },
+ { "sans-serif", Mml::SansSerifMV },
+ { "bold-sans-serif", Mml::BoldMV | Mml::SansSerifMV },
+ { "sans-serif-italic", Mml::SansSerifMV | Mml::ItalicMV },
+ { "sans-serif-bold-italic", Mml::SansSerifMV | Mml::ItalicMV | Mml::BoldMV },
+ { "monospace", Mml::MonospaceMV },
+ { 0, 0 }
+ };
+
+ const MathVariantValue *v = g_mv_data;
+ for (; v->value != 0; ++v) {
+ if (value == v->value) {
+ if (ok != 0)
+ *ok = true;
+ return v->mv;
+ }
+ }
+
+ if (ok != 0)
+ *ok = false;
+
+ qWarning("interpretMathVariant(): could not parse value: \"%s\"", value.toLatin1().data());
+
+ return Mml::NormalMV;
+}
+
+static Mml::FormType interpretForm(const QString &value, bool *ok)
+{
+ if (ok != 0)
+ *ok = true;
+
+ if (value == "prefix")
+ return Mml::PrefixForm;
+ if (value == "infix")
+ return Mml::InfixForm;
+ if (value == "postfix")
+ return Mml::PostfixForm;
+
+ if (ok != 0)
+ *ok = false;
+
+ qWarning("interpretForm(): could not parse value \"%s\"", value.toLatin1().data());
+ return Mml::InfixForm;
+}
+
+static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok)
+{
+ QString value = interpretListAttr(value_list, colnum, "center");
+
+ if (ok != 0)
+ *ok = true;
+
+ if (value == "left")
+ return Mml::ColAlignLeft;
+ if (value == "right")
+ return Mml::ColAlignRight;
+ if (value == "center")
+ return Mml::ColAlignCenter;
+
+ if (ok != 0)
+ *ok = false;
+
+ qWarning("interpretColAlign(): could not parse value \"%s\"", value.toLatin1().data());
+ return Mml::ColAlignCenter;
+}
+
+static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok)
+{
+ QString value = interpretListAttr(value_list, rownum, "axis");
+
+ if (ok != 0)
+ *ok = true;
+
+ if (value == "top")
+ return Mml::RowAlignTop;
+ if (value == "center")
+ return Mml::RowAlignCenter;
+ if (value == "bottom")
+ return Mml::RowAlignBottom;
+ if (value == "baseline")
+ return Mml::RowAlignBaseline;
+ if (value == "axis")
+ return Mml::RowAlignAxis;
+
+ if (ok != 0)
+ *ok = false;
+
+ qWarning("interpretRowAlign(): could not parse value \"%s\"", value.toLatin1().data());
+ return Mml::RowAlignAxis;
+}
+
+static QString interpretListAttr(const QString &value_list, int idx, const QString &def)
+{
+ QStringList l = value_list.split(' ');
+
+ if (l.count() == 0)
+ return def;
+
+ if (l.count() <= idx)
+ return l[l.count() - 1];
+ else
+ return l[idx];
+}
+
+static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok)
+{
+ if (ok != 0)
+ *ok = true;
+
+ QString value = interpretListAttr(value_list, idx, "none");
+
+ if (value == "none")
+ return Mml::FrameNone;
+ if (value == "solid")
+ return Mml::FrameSolid;
+ if (value == "dashed")
+ return Mml::FrameDashed;
+
+ if (ok != 0)
+ *ok = false;
+
+ qWarning("interpretFrameType(): could not parse value \"%s\"", value.toLatin1().data());
+ return Mml::FrameNone;
+}
+
+
+static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok)
+{
+ Mml::FrameSpacing fs;
+
+ QStringList l = value_list.split(' ');
+ if (l.count() != 2) {
+ qWarning("interpretFrameSpacing: could not parse value \"%s\"", value_list.toLatin1().data());
+ if (ok != 0)
+ *ok = false;
+ return Mml::FrameSpacing((int)(0.4*em), (int)(0.5*ex));
+ }
+
+ bool hor_ok, ver_ok;
+ fs.m_hor = interpretSpacing(l[0], em, ex, &hor_ok);
+ fs.m_ver = interpretSpacing(l[1], em, ex, &ver_ok);
+
+ if (ok != 0)
+ *ok = hor_ok && ver_ok;
+
+ return fs;
+}
+
+static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex)
+{
+ if (font_attr.contains("fontsize")) {
+ QString value = font_attr["fontsize"];
+
+ for (;;) {
+
+ bool ok;
+ int ptsize = interpretPointSize(value, &ok);
+ if (ok) {
+ fn.setPointSize(ptsize);
+ break;
+ }
+
+ ptsize = interpretPercentSpacing(value, fn.pointSize(), &ok);
+ if (ok) {
+ fn.setPointSize(ptsize);
+ break;
+ }
+
+ int size = interpretSpacing(value, em, ex, &ok);
+ if (ok) {
+ fn.setPixelSize(size);
+ break;
+ }
+
+ break;
+ }
+ }
+
+ if (font_attr.contains("fontweight")) {
+ QString value = font_attr["fontweight"];
+ if (value == "normal")
+ fn.setBold(false);
+ else if (value == "bold")
+ fn.setBold(true);
+ else
+ qWarning("interpretDepreciatedFontAttr(): could not parse fontweight \"%s\"", value.toLatin1().data());
+ }
+
+ if (font_attr.contains("fontstyle")) {
+ QString value = font_attr["fontstyle"];
+ if (value == "normal")
+ fn.setItalic(false);
+ else if (value == "italic")
+ fn.setItalic(true);
+ else
+ qWarning("interpretDepreciatedFontAttr(): could not parse fontstyle \"%s\"", value.toLatin1().data());
+ }
+
+ if (font_attr.contains("fontfamily")) {
+ QString value = font_attr["fontfamily"];
+ fn.setFamily(value);
+ }
+
+ return fn;
+}
+
+static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok)
+{
+ if (ok != 0)
+ *ok = true;
+
+ if (value == "small") {
+ fn.setPointSize((int)(fn.pointSize()*0.7));
+ return fn;
+ }
+
+ if (value == "normal")
+ return fn;
+
+ if (value == "big") {
+ fn.setPointSize((int)(fn.pointSize()*1.5));
+ return fn;
+ }
+
+ bool size_ok;
+
+ int ptsize = interpretPointSize(value, &size_ok);
+ if (size_ok) {
+ fn.setPointSize(ptsize);
+ return fn;
+ }
+
+ int size = interpretSpacing(value, em, ex, &size_ok);
+ if (size_ok) {
+ fn.setPixelSize(size);
+ return fn;
+ }
+
+ if (ok != 0)
+ *ok = false;
+ qWarning("interpretMathSize(): could not parse mathsize \"%s\"", value.toLatin1().data());
+ return fn;
+}
+
+/*!
+ \class QtMmlDocument
+
+ \brief The QtMmlDocument class renders mathematical formulas written in MathML 2.0.
+
+ This class provides a direct API to the rendering engine used by
+ QtMmlWidget. It can be used to paint MathML inside other widgets.
+
+ All methods work the same as the corresponding methods in QtMmlWidget.
+*/
+
+/*!
+ Constructs an empty MML document.
+*/
+QtMmlDocument::QtMmlDocument()
+{
+ m_doc = new MmlDocument;
+}
+
+/*!
+ Destroys the MML document.
+*/
+QtMmlDocument::~QtMmlDocument()
+{
+ delete m_doc;
+}
+
+/*!
+ Clears the contents of this MML document.
+*/
+void QtMmlDocument::clear()
+{
+ m_doc->clear();
+}
+
+/*!
+ Sets the MathML expression to be rendered. The expression is given
+ in the string \a text. If the expression is successfully parsed,
+ this method returns true; otherwise it returns false. If an error
+ occured \a errorMsg is set to a diagnostic message, while \a
+ errorLine and \a errorColumn contain the location of the error.
+ Any of \a errorMsg, \a errorLine and \a errorColumn may be 0,
+ in which case they are not set.
+
+ \a text should contain MathML 2.0 presentation markup elements enclosed
+ in a <math> element.
+*/
+bool QtMmlDocument::setContent(QString text, QString *errorMsg,
+ int *errorLine, int *errorColumn)
+{
+ return m_doc->setContent(text, errorMsg, errorLine, errorColumn);
+}
+
+/*!
+ Renders this MML document with the painter \a p at position \a pos.
+*/
+void QtMmlDocument::paint(QPainter *p, const QPoint &pos) const
+{
+ m_doc->paint(p, pos);
+}
+
+/*!
+ Returns the size of this MML document, as rendered, in pixels.
+*/
+QSize QtMmlDocument::size() const
+{
+ return m_doc->size();
+}
+
+/*!
+ Returns the name of the font used to render the font \a type.
+
+ \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
+*/
+QString QtMmlDocument::fontName(QtMmlWidget::MmlFont type) const
+{
+ return m_doc->fontName(type);
+}
+
+/*!
+ Sets the name of the font used to render the font \a type to \a name.
+
+ \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
+*/
+void QtMmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name)
+{
+ m_doc->setFontName(type, name);
+}
+
+/*!
+ Returns the point size of the font used to render expressions
+ whose scriptlevel is 0.
+
+ \sa setBaseFontPointSize() fontName() setFontName()
+*/
+int QtMmlDocument::baseFontPointSize() const
+{
+ return m_doc->baseFontPointSize();
+}
+
+/*!
+ Sets the point \a size of the font used to render expressions
+ whose scriptlevel is 0.
+
+ \sa baseFontPointSize() fontName() setFontName()
+*/
+void QtMmlDocument::setBaseFontPointSize(int size)
+{
+ m_doc->setBaseFontPointSize(size);
+}