From: Aleix Pol Gonzalez Date: Sat, 14 Mar 2009 17:34:51 +0000 (+0000) Subject: Using the recently freed QtMmlWidget solution in the Dictionary as we were using... X-Git-Tag: v4.5.80~9^2~4 X-Git-Url: https://git.rmz.fi/?a=commitdiff_plain;h=373a55aa2296de7cd43703ccb584f382d662d7cc;p=libqmvoc.git Using the recently freed QtMmlWidget solution in the Dictionary as we were using KFormula's widget back when it was available on KDELibs. svn path=/trunk/KDE/kdeedu/kalgebra/src/qtmmlwidget/; revision=939347 --- 373a55aa2296de7cd43703ccb584f382d662d7cc diff --git a/kalgebra/qtmmlwidget/QtMmlDocument b/kalgebra/qtmmlwidget/QtMmlDocument new file mode 100644 index 0000000..ef69b7f --- /dev/null +++ b/kalgebra/qtmmlwidget/QtMmlDocument @@ -0,0 +1 @@ +#include "qtmmlwidget.h" diff --git a/kalgebra/qtmmlwidget/QtMmlWidget b/kalgebra/qtmmlwidget/QtMmlWidget new file mode 100644 index 0000000..ef69b7f --- /dev/null +++ b/kalgebra/qtmmlwidget/QtMmlWidget @@ -0,0 +1 @@ +#include "qtmmlwidget.h" diff --git a/kalgebra/qtmmlwidget/qtmmlwidget.cpp b/kalgebra/qtmmlwidget/qtmmlwidget.cpp new file mode 100644 index 0000000..92256ff --- /dev/null +++ b/kalgebra/qtmmlwidget/qtmmlwidget.cpp @@ -0,0 +1,6366 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +#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 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 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 &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 = "\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(n); +} + +MmlNode *MmlNode::firstSibling() const +{ + const MmlNode *n = this; + while (!n->isFirstSibling()) + n = n->previousSibling(); + return const_cast(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 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 &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 &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(); // + if (node == 0) + return ColAlignCenter; + + uint colnum = colNum(); + val = node->explicitAttribute("columnalign"); + if (!val.isNull()) + return interpretColAlign(val, colnum, 0); + + node = node->parent(); // + 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(); // + if (node == 0) + return RowAlignAxis; + + uint rownum = rowNum(); + val = node->explicitAttribute("rowalign"); + if (!val.isNull()) + return interpretRowAlign(val, rownum, 0); + + node = node->parent(); // + 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 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 = "name != 0; ++ent) { + result += "\tname) + " \"" + 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 and . 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 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); +} diff --git a/kalgebra/qtmmlwidget/qtmmlwidget.h b/kalgebra/qtmmlwidget/qtmmlwidget.h new file mode 100644 index 0000000..bcf0537 --- /dev/null +++ b/kalgebra/qtmmlwidget/qtmmlwidget.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#ifndef QTMMLWIDGET_H +#define QTMMLWIDGET_H + +#include +#include + +class MmlDocument; + +#if defined(Q_WS_WIN) +# if !defined(QT_QTMMLWIDGET_EXPORT) && !defined(QT_QTMMLWIDGET_IMPORT) +# define QT_QTMMLWIDGET_EXPORT +# elif defined(QT_QTMMLWIDGET_IMPORT) +# if defined(QT_QTMMLWIDGET_EXPORT) +# undef QT_QTMMLWIDGET_EXPORT +# endif +# define QT_QTMMLWIDGET_EXPORT __declspec(dllimport) +# elif defined(QT_QTMMLWIDGET_EXPORT) +# undef QT_QTMMLWIDGET_EXPORT +# define QT_QTMMLWIDGET_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTMMLWIDGET_EXPORT +#endif + +class QT_QTMMLWIDGET_EXPORT QtMmlWidget : public QFrame +{ + public: + enum MmlFont { NormalFont, FrakturFont, SansSerifFont, ScriptFont, + MonospaceFont, DoublestruckFont }; + + QtMmlWidget(QWidget *parent = 0); + ~QtMmlWidget(); + + QString fontName(MmlFont type) const; + void setFontName(MmlFont type, const QString &name); + int baseFontPointSize() const; + void setBaseFontPointSize(int size); + + bool setContent(const QString &text, QString *errorMsg = 0, + int *errorLine = 0, int *errorColumn = 0); + void dump() const; + virtual QSize sizeHint() const; + + void setDrawFrames(bool b); + bool drawFrames() const; + + void clear(); + + protected: + virtual void paintEvent(QPaintEvent *e); + + private: + MmlDocument *m_doc; +}; + + +class QT_QTMMLWIDGET_EXPORT QtMmlDocument +{ +public: + QtMmlDocument(); + ~QtMmlDocument(); + void clear(); + + bool setContent(QString text, QString *errorMsg = 0, + int *errorLine = 0, int *errorColumn = 0); + void paint(QPainter *p, const QPoint &pos) const; + QSize size() const; + + QString fontName(QtMmlWidget::MmlFont type) const; + void setFontName(QtMmlWidget::MmlFont type, const QString &name); + + int baseFontPointSize() const; + void setBaseFontPointSize(int size); +private: + MmlDocument *m_doc; +}; + +#endif