1 /****************************************************************************
2 **
3 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of a Qt Solutions component.
8 **
9 ** Commercial Usage
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Solutions Commercial License Agreement provided
12 ** with the Software or, alternatively, in accordance with the terms
13 ** contained in a written agreement between you and Nokia.
14 **
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22 **
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this
26 ** package.
27 **
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
35 **
36 ** Please note Third Party Software included with Qt Solutions may impose
37 ** additional restrictions and it is the user's responsibility to ensure
38 ** that they have met the licensing requirements of the GPL, LGPL, or Qt
39 ** Solutions Commercial license and the relevant license of the Third
40 ** Party Software they are using.
41 **
42 ** If you are unsure which license is appropriate for your use, please
43 ** contact Nokia at qt-info@nokia.com.
44 **
45 ****************************************************************************/
46
47 #include <QtWidgets/QApplication>
48 #include <QtCore/QString>
49 #include <QtCore/QMap>
50 #include <QtWidgets/QDesktopWidget>
51 #include <QtGui/QPainter>
52 #include <QtGui/QPaintEvent>
53
54 #include "qtmmlwidget.h"
55
56 // *******************************************************************
57 // Declarations
58 // *******************************************************************
59
60 #define ROUND(a) (int)((a)+.5)
61
62 static bool g_draw_frames = false;
63 static const double g_mfrac_spacing = 0.1;
64 static const double g_mroot_base_margin = 0.1;
65 static const double g_script_size_multiplier = 0.7071; // sqrt(1/2)
66 static const int g_min_font_point_size = 8;
67 static const QChar g_radical_char = QChar(0x1A, 0x22);
68 static const unsigned g_oper_spec_rows = 9;
69
70 // use unnamed namespace
71 namespace
72 {
73
74 struct Mml
75 {
76 enum NodeType {
77 NoNode = 0, MiNode, MnNode, MfracNode, MrowNode, MsqrtNode,
78 MrootNode, MsupNode, MsubNode, MsubsupNode, MoNode,
79 MstyleNode, TextNode, MphantomNode, MfencedNode,
80 MtableNode, MtrNode, MtdNode, MoverNode, MunderNode,
81 MunderoverNode, MerrorNode, MtextNode, MpaddedNode,
82 MspaceNode, MalignMarkNode, UnknownNode
83 };
84
85 enum MathVariant {
86 NormalMV = 0x0000,
87 BoldMV = 0x0001,
88 ItalicMV = 0x0002,
89 DoubleStruckMV = 0x0004,
90 ScriptMV = 0x0008,
91 FrakturMV = 0x0010,
92 SansSerifMV = 0x0020,
93 MonospaceMV = 0x0040
94 };
95
96 enum FormType { PrefixForm, InfixForm, PostfixForm };
97 enum ColAlign { ColAlignLeft, ColAlignCenter, ColAlignRight };
98 enum RowAlign { RowAlignTop, RowAlignCenter, RowAlignBottom,
99 RowAlignAxis, RowAlignBaseline };
100 enum FrameType { FrameNone, FrameSolid, FrameDashed };
101
102 struct FrameSpacing {
FrameSpacing__anon1299d46a0111::Mml::FrameSpacing103 FrameSpacing(int hor = 0, int ver = 0)
104 : m_hor(hor), m_ver(ver) {}
105 int m_hor, m_ver;
106 };
107 };
108
109 struct OperSpec {
110 enum StretchDir { NoStretch, HStretch, VStretch, HVStretch };
111
112 const char *name;
113 Mml::FormType form;
114 const char *attributes[g_oper_spec_rows];
115 StretchDir stretch_dir;
116 };
117
118 struct NodeSpec
119 {
120 Mml::NodeType type;
121 const char *tag;
122 const char *type_str;
123 int child_spec;
124 const char *child_types;
125 const char *attributes;
126
127 enum ChildSpec {
128 ChildAny = -1, // any number of children allowed
129 ChildIgnore = -2, // do not build subexpression of children
130 ImplicitMrow = -3 // if more than one child, build mrow
131 };
132 };
133
134 struct EntitySpec
135 {
136 const char *name;
137 const char *value;
138 };
139
140 typedef QMap<QString, QString> MmlAttributeMap;
141 class MmlNode;
142 } // namespace
143
144 class MmlDocument : public Mml
145 {
146 public:
147 MmlDocument();
148 ~MmlDocument();
149 void clear();
150
151 bool setContent(QString text, QString *errorMsg = 0,
152 int *errorLine = 0, int *errorColumn = 0);
153 void paint(QPainter *p, const QPoint &pos) const;
154 void dump() const;
155 QSize size() const;
156 void layout();
157
158 QString fontName(QtMmlWidget::MmlFont type) const;
159 void setFontName(QtMmlWidget::MmlFont type, const QString &name);
160
baseFontPointSize() const161 int baseFontPointSize() const
162 { return m_base_font_point_size; }
setBaseFontPointSize(int size)163 void setBaseFontPointSize(int size)
164 { m_base_font_point_size = size; }
foregroundColor() const165 QColor foregroundColor() const
166 { return m_foreground_color; }
setForegroundColor(const QColor & color)167 void setForegroundColor(const QColor &color)
168 { m_foreground_color = color; }
backgroundColor() const169 QColor backgroundColor() const
170 { return m_background_color; }
setBackgroundColor(const QColor & color)171 void setBackgroundColor(const QColor &color)
172 { m_background_color = color; }
173
174 private:
175 void _dump(const MmlNode *node, QString &indent) const;
176 bool insertChild(MmlNode *parent, MmlNode *new_node, QString *errorMsg);
177
178 MmlNode *domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg);
179 MmlNode *createNode(NodeType type, const MmlAttributeMap &mml_attr,
180 const QString &mml_value, QString *errorMsg);
181 MmlNode *createImplicitMrowNode(const QDomNode &dom_node, bool *ok,
182 QString *errorMsg);
183
184 void insertOperator(MmlNode *node, const QString &text);
185
186 MmlNode *m_root_node;
187
188 QString m_normal_font_name;
189 QString m_fraktur_font_name;
190 QString m_sans_serif_font_name;
191 QString m_script_font_name;
192 QString m_monospace_font_name;
193 QString m_doublestruck_font_name;
194 int m_base_font_point_size;
195 QColor m_foreground_color;
196 QColor m_background_color;
197 };
198
199 namespace {
200 class MmlNode : public Mml
201 {
202 friend class ::MmlDocument;
203
204 public:
205 MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map);
206 virtual ~MmlNode();
207
208 // Mml stuff
nodeType() const209 NodeType nodeType() const
210 { return m_node_type; }
211
212 virtual QString toStr() const;
213
214 void setRelOrigin(const QPoint &rel_origin);
relOrigin() const215 QPoint relOrigin() const
216 { return m_rel_origin; }
217 void stretchTo(const QRect &rect);
isStretched() const218 bool isStretched() const
219 { return m_stretched; }
220 QPoint devicePoint(const QPoint &p) const;
221
myRect() const222 QRect myRect() const
223 { return m_my_rect; }
224 QRect parentRect() const;
225 virtual QRect deviceRect() const;
226 void updateMyRect();
setMyRect(const QRect & rect)227 virtual void setMyRect(const QRect &rect)
228 { m_my_rect = rect; }
229
230 virtual void stretch();
231 virtual void layout();
232 virtual void paint(QPainter *p);
233
234 int basePos() const;
235 int overlinePos() const;
236 int underlinePos() const;
237 int em() const;
238 int ex() const;
239
240 QString explicitAttribute(const QString &name, const QString &def = QString()) const;
241 QString inheritAttributeFromMrow(const QString &name, const QString &def = QString()) const;
242
243 virtual QFont font() const;
244 virtual QColor color() const;
245 virtual QColor background() const;
246 virtual int scriptlevel(const MmlNode *child = 0) const;
247
248
249 // Node stuff
document() const250 MmlDocument *document() const
251 { return m_document; }
parent() const252 MmlNode *parent() const
253 { return m_parent; }
firstChild() const254 MmlNode *firstChild() const
255 { return m_first_child; }
nextSibling() const256 MmlNode *nextSibling() const
257 { return m_next_sibling; }
previousSibling() const258 MmlNode *previousSibling() const
259 { return m_previous_sibling; }
260 MmlNode *lastSibling() const;
261 MmlNode *firstSibling() const;
isLastSibling() const262 bool isLastSibling() const
263 { return m_next_sibling == 0; }
isFirstSibling() const264 bool isFirstSibling() const
265 { return m_previous_sibling == 0; }
hasChildNodes() const266 bool hasChildNodes() const
267 { return m_first_child != 0; }
268
269 protected:
270 virtual void layoutSymbol();
271 virtual void paintSymbol(QPainter *p) const;
symbolRect() const272 virtual QRect symbolRect() const
273 { return QRect(0, 0, 0, 0); }
274
275 MmlNode *parentWithExplicitAttribute(const QString &name, NodeType type = NoNode);
276 int interpretSpacing(const QString &value, bool *ok) const;
277
278 private:
279 MmlAttributeMap m_attribute_map;
280 bool m_stretched;
281 QRect m_my_rect, m_parent_rect;
282 QPoint m_rel_origin;
283
284 NodeType m_node_type;
285 MmlDocument *m_document;
286
287 MmlNode *m_parent,
288 *m_first_child,
289 *m_next_sibling,
290 *m_previous_sibling;
291 };
292
293 class MmlTokenNode : public MmlNode
294 {
295 public:
MmlTokenNode(NodeType type,MmlDocument * document,const MmlAttributeMap & attribute_map)296 MmlTokenNode(NodeType type, MmlDocument *document,
297 const MmlAttributeMap &attribute_map)
298 : MmlNode(type, document, attribute_map) {}
299
300 QString text() const;
301 };
302
303 class MmlMphantomNode : public MmlNode
304 {
305 public:
MmlMphantomNode(MmlDocument * document,const MmlAttributeMap & attribute_map)306 MmlMphantomNode(MmlDocument *document,
307 const MmlAttributeMap &attribute_map)
308 : MmlNode(MphantomNode, document, attribute_map) {}
309
paint(QPainter *)310 virtual void paint(QPainter *) {}
311 };
312
313 class MmlUnknownNode : public MmlNode
314 {
315 public:
MmlUnknownNode(MmlDocument * document,const MmlAttributeMap & attribute_map)316 MmlUnknownNode(MmlDocument *document,
317 const MmlAttributeMap &attribute_map)
318 : MmlNode(UnknownNode, document, attribute_map) {}
319 };
320
321 class MmlMfencedNode : public MmlNode
322 {
323 public:
MmlMfencedNode(MmlDocument * document,const MmlAttributeMap & attribute_map)324 MmlMfencedNode(MmlDocument *document,
325 const MmlAttributeMap &attribute_map)
326 : MmlNode(MfencedNode, document, attribute_map) {}
327 };
328
329 class MmlMalignMarkNode : public MmlNode
330 {
331 public:
MmlMalignMarkNode(MmlDocument * document)332 MmlMalignMarkNode(MmlDocument *document)
333 : MmlNode(MalignMarkNode, document, MmlAttributeMap()) {}
334 };
335
336 class MmlMfracNode : public MmlNode
337 {
338 public:
MmlMfracNode(MmlDocument * document,const MmlAttributeMap & attribute_map)339 MmlMfracNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
340 : MmlNode(MfracNode, document, attribute_map) {}
341
342 MmlNode *numerator() const;
343 MmlNode *denominator() const;
344
345 protected:
346 virtual void layoutSymbol();
347 virtual void paintSymbol(QPainter *p) const;
348 virtual QRect symbolRect() const;
349 };
350
351 class MmlMrowNode : public MmlNode
352 {
353 public:
MmlMrowNode(MmlDocument * document,const MmlAttributeMap & attribute_map)354 MmlMrowNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
355 : MmlNode(MrowNode, document, attribute_map) {}
356 };
357
358 class MmlRootBaseNode : public MmlNode
359 {
360 public:
MmlRootBaseNode(NodeType type,MmlDocument * document,const MmlAttributeMap & attribute_map)361 MmlRootBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
362 : MmlNode(type, document, attribute_map) {}
363
364 MmlNode *base() const;
365 MmlNode *index() const;
366
367 virtual int scriptlevel(const MmlNode *child = 0) const;
368
369 protected:
370 virtual void layoutSymbol();
371 virtual void paintSymbol(QPainter *p) const;
372 virtual QRect symbolRect() const;
373 int tailWidth() const;
374 };
375
376 class MmlMrootNode : public MmlRootBaseNode
377 {
378 public:
MmlMrootNode(MmlDocument * document,const MmlAttributeMap & attribute_map)379 MmlMrootNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
380 : MmlRootBaseNode(MrootNode, document, attribute_map) {}
381 };
382
383 class MmlMsqrtNode : public MmlRootBaseNode
384 {
385 public:
MmlMsqrtNode(MmlDocument * document,const MmlAttributeMap & attribute_map)386 MmlMsqrtNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
387 : MmlRootBaseNode(MsqrtNode, document, attribute_map) {}
388
389 };
390
391
392 class MmlTextNode : public MmlNode
393 {
394 public:
395 MmlTextNode(const QString &text, MmlDocument *document);
396
397 virtual QString toStr() const;
text() const398 QString text() const
399 { return m_text; }
400
401 // TextNodes are not xml elements, so they can't have attributes of
402 // their own. Everything is taken from the parent.
font() const403 virtual QFont font() const
404 { return parent()->font(); }
scriptlevel(const MmlNode * =0) const405 virtual int scriptlevel(const MmlNode* = 0) const
406 { return parent()->scriptlevel(this); }
color() const407 virtual QColor color() const
408 { return parent()->color(); }
background() const409 virtual QColor background() const
410 { return parent()->background(); }
411
412 protected:
413 virtual void paintSymbol(QPainter *p) const;
414 virtual QRect symbolRect() const;
415
416 QString m_text;
417 };
418
419 class MmlMiNode : public MmlTokenNode
420 {
421 public:
MmlMiNode(MmlDocument * document,const MmlAttributeMap & attribute_map)422 MmlMiNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
423 : MmlTokenNode(MiNode, document, attribute_map) {}
424 };
425
426 class MmlMnNode : public MmlTokenNode
427 {
428 public:
MmlMnNode(MmlDocument * document,const MmlAttributeMap & attribute_map)429 MmlMnNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
430 : MmlTokenNode(MnNode, document, attribute_map) {}
431 };
432
433 class MmlSubsupBaseNode : public MmlNode
434 {
435 public:
MmlSubsupBaseNode(NodeType type,MmlDocument * document,const MmlAttributeMap & attribute_map)436 MmlSubsupBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
437 : MmlNode(type, document, attribute_map) {}
438
439 MmlNode *base() const;
440 MmlNode *sscript() const;
441
442 virtual int scriptlevel(const MmlNode *child = 0) const;
443 };
444
445 class MmlMsupNode : public MmlSubsupBaseNode
446 {
447 public:
MmlMsupNode(MmlDocument * document,const MmlAttributeMap & attribute_map)448 MmlMsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
449 : MmlSubsupBaseNode(MsupNode, document, attribute_map) {}
450
451 protected:
452 virtual void layoutSymbol();
453 };
454
455 class MmlMsubNode : public MmlSubsupBaseNode
456 {
457 public:
MmlMsubNode(MmlDocument * document,const MmlAttributeMap & attribute_map)458 MmlMsubNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
459 : MmlSubsupBaseNode(MsubNode, document, attribute_map) {}
460
461 protected:
462 virtual void layoutSymbol();
463 };
464
465 class MmlMsubsupNode : public MmlNode
466 {
467 public:
MmlMsubsupNode(MmlDocument * document,const MmlAttributeMap & attribute_map)468 MmlMsubsupNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
469 : MmlNode(MsubsupNode, document, attribute_map) {}
470
471 MmlNode *base() const;
472 MmlNode *superscript() const;
473 MmlNode *subscript() const;
474
475 virtual int scriptlevel(const MmlNode *child = 0) const;
476
477 protected:
478 virtual void layoutSymbol();
479 };
480
481 class MmlMoNode : public MmlTokenNode
482 {
483 public:
484 MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map);
485
486 QString dictionaryAttribute(const QString &name) const;
487 virtual void stretch();
488 virtual int lspace() const;
489 virtual int rspace() const;
490
491 virtual QString toStr() const;
492
493 protected:
494 virtual void layoutSymbol();
495 virtual QRect symbolRect() const;
496
497 virtual FormType form() const;
498
499 private:
500 const OperSpec *m_oper_spec;
501 };
502
503 class MmlMstyleNode : public MmlNode
504 {
505 public:
MmlMstyleNode(MmlDocument * document,const MmlAttributeMap & attribute_map)506 MmlMstyleNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
507 : MmlNode(MstyleNode, document, attribute_map) {}
508 };
509
510 class MmlTableBaseNode : public MmlNode
511 {
512 public:
MmlTableBaseNode(NodeType type,MmlDocument * document,const MmlAttributeMap & attribute_map)513 MmlTableBaseNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
514 : MmlNode(type, document, attribute_map) {}
515 };
516
517 class MmlMtableNode : public MmlTableBaseNode
518 {
519 public:
MmlMtableNode(MmlDocument * document,const MmlAttributeMap & attribute_map)520 MmlMtableNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
521 : MmlTableBaseNode(MtableNode, document, attribute_map) {}
522
523 int rowspacing() const;
524 int columnspacing() const;
525 int framespacing_hor() const;
526 int framespacing_ver() const;
527 FrameType frame() const;
528 FrameType columnlines(int idx) const;
529 FrameType rowlines(int idx) const;
530
531 protected:
532 virtual void layoutSymbol();
533 virtual QRect symbolRect() const;
534 virtual void paintSymbol(QPainter *p) const;
535
536 private:
537 struct CellSizeData
538 {
539 void init(const MmlNode *first_row);
540 QList<int> col_widths, row_heights;
numCols__anon1299d46a0211::MmlMtableNode::CellSizeData541 int numCols() const { return col_widths.count(); }
numRows__anon1299d46a0211::MmlMtableNode::CellSizeData542 int numRows() const { return row_heights.count(); }
543 uint colWidthSum() const;
544 uint rowHeightSum() const;
545 };
546
547 CellSizeData m_cell_size_data;
548 int m_content_width, m_content_height;
549 };
550
551 class MmlMtrNode : public MmlTableBaseNode
552 {
553 public:
MmlMtrNode(MmlDocument * document,const MmlAttributeMap & attribute_map)554 MmlMtrNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
555 : MmlTableBaseNode(MtrNode, document, attribute_map) {}
556 void layoutCells(const QList<int> &col_widths, int col_spc);
557 };
558
559 class MmlMtdNode : public MmlTableBaseNode
560 {
561 public:
MmlMtdNode(MmlDocument * document,const MmlAttributeMap & attribute_map)562 MmlMtdNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
563 : MmlTableBaseNode(MtdNode, document, attribute_map)
564 { m_scriptlevel_adjust = 0; }
565 virtual void setMyRect(const QRect &rect);
566
567 ColAlign columnalign();
568 RowAlign rowalign();
569 uint colNum();
570 uint rowNum();
571 virtual int scriptlevel(const MmlNode *child = 0) const;
572
573 private:
574 int m_scriptlevel_adjust; // added or subtracted to scriptlevel to
575 // make contents fit the cell
576 };
577
578 class MmlMoverNode : public MmlNode
579 {
580 public:
MmlMoverNode(MmlDocument * document,const MmlAttributeMap & attribute_map)581 MmlMoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
582 : MmlNode(MoverNode, document, attribute_map) {}
583 virtual int scriptlevel(const MmlNode *node = 0) const;
584
585 protected:
586 virtual void layoutSymbol();
587 };
588
589 class MmlMunderNode : public MmlNode
590 {
591 public:
MmlMunderNode(MmlDocument * document,const MmlAttributeMap & attribute_map)592 MmlMunderNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
593 : MmlNode(MunderNode, document, attribute_map) {}
594 virtual int scriptlevel(const MmlNode *node = 0) const;
595
596 protected:
597 virtual void layoutSymbol();
598 };
599
600 class MmlMunderoverNode : public MmlNode
601 {
602 public:
MmlMunderoverNode(MmlDocument * document,const MmlAttributeMap & attribute_map)603 MmlMunderoverNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
604 : MmlNode(MunderoverNode, document, attribute_map) {}
605 virtual int scriptlevel(const MmlNode *node = 0) const;
606
607 protected:
608 virtual void layoutSymbol();
609 };
610
611 class MmlMerrorNode : public MmlNode
612 {
613 public:
MmlMerrorNode(MmlDocument * document,const MmlAttributeMap & attribute_map)614 MmlMerrorNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
615 : MmlNode(MerrorNode, document, attribute_map) {}
616 };
617
618 class MmlMtextNode : public MmlNode
619 {
620 public:
MmlMtextNode(MmlDocument * document,const MmlAttributeMap & attribute_map)621 MmlMtextNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
622 : MmlNode(MtextNode, document, attribute_map) {}
623 };
624
625 class MmlMpaddedNode : public MmlNode
626 {
627 public:
MmlMpaddedNode(MmlDocument * document,const MmlAttributeMap & attribute_map)628 MmlMpaddedNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
629 : MmlNode(MpaddedNode, document, attribute_map) {}
630
631 public:
632 int lspace() const;
633 int width() const;
634 int height() const;
635 int depth() const;
636
637 protected:
638 int interpretSpacing(QString value, int base_value, bool *ok) const;
639 virtual void layoutSymbol();
640 virtual QRect symbolRect() const;
641 };
642
643 class MmlMspaceNode : public MmlNode
644 {
645 public:
MmlMspaceNode(MmlDocument * document,const MmlAttributeMap & attribute_map)646 MmlMspaceNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
647 : MmlNode(MspaceNode, document, attribute_map) {}
648 };
649 }
650
651 static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type);
652 static const NodeSpec *mmlFindNodeSpec(const QString &tag);
653 static bool mmlCheckChildType(Mml::NodeType parent_type,
654 Mml::NodeType child_type, QString *error_str);
655 static bool mmlCheckAttributes(Mml::NodeType child_type,
656 const MmlAttributeMap &attr, QString *error_str);
657 static QString mmlDictAttribute(const QString &name, const OperSpec *spec);
658 static const OperSpec *mmlFindOperSpec(const QString &name, Mml::FormType form);
659 static int interpretSpacing(QString name, int em, int ex, bool *ok);
660 static int interpretPercentSpacing(QString value, int base, bool *ok);
661 static uint interpretMathVariant(const QString &value, bool *ok);
662 static Mml::FormType interpretForm(const QString &value, bool *ok);
663 static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok);
664 static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok);
665 static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok);
666 static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok);
667 static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok);
668 static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex);
669 static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok);
670 static QString interpretListAttr(const QString &value_list, int idx, const QString &def);
671 static QString rectToStr(const QRect &rect);
672 static QString entityDeclarations();
673
674
675 #define MML_ATT_COMMON " class style id xref actiontype "
676 #define MML_ATT_FONTSIZE " fontsize fontweight fontstyle fontfamily color "
677 #define MML_ATT_MATHVARIANT " mathvariant mathsize mathcolor mathbackground "
678 #define MML_ATT_FONTINFO MML_ATT_FONTSIZE MML_ATT_MATHVARIANT
679 #define MML_ATT_OPINFO " form fence separator lspace rspace stretchy symmetric " \
680 " maxsize minsize largeop movablelimits accent "
681 #define MML_ATT_SIZEINFO " width height depth "
682 #define MML_ATT_TABLEINFO " align rowalign columnalign columnwidth groupalign " \
683 " alignmentscope side rowspacing columnspacing rowlines " \
684 " columnlines width frame framespacing equalrows " \
685 " equalcolumns displaystyle "
686 #define MML_ATT_MFRAC " bevelled numalign denomalign linethickness "
687 #define MML_ATT_MSTYLE MML_ATT_FONTINFO MML_ATT_OPINFO \
688 " scriptlevel lquote rquote linethickness displaystyle " \
689 " scriptsizemultiplier scriptminsize background " \
690 " veryverythinmathspace verythinmathspace thinmathspace " \
691 " mediummathspace thickmathspace verythickmathspace " \
692 " veryverythickmathspace open close separators " \
693 " subscriptshift superscriptshift accentunder tableinfo " \
694 " rowspan columnspan edge selection bevelled "
695 #define MML_ATT_MTABLE " align rowalign columnalign groupalign alignmentscope " \
696 " columnwidth width rowspacing columnspacing rowlines columnlines " \
697 " frame framespacing equalrows equalcolumns displaystyle side " \
698 " minlabelspacing "
699
700 static const NodeSpec g_node_spec_data[] = {
701
702 // type tag type_str child_spec child_types attributes ""=none, 0=any
703 // ----------------------- --------------- ------------------- ----------------------- ------------------------ ------------------------------------
704 { Mml::MiNode, "mi", "MiNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO },
705 { Mml::MnNode, "mn", "MnNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO },
706 { Mml::MfracNode, "mfrac", "MfracNode", 2, 0, MML_ATT_COMMON MML_ATT_MFRAC },
707 { Mml::MrowNode, "mrow", "MrowNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " display mode " },
708 { Mml::MsqrtNode, "msqrt", "MsqrtNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
709 { Mml::MrootNode, "mroot", "MrootNode", 2, 0, MML_ATT_COMMON },
710 { Mml::MsupNode, "msup", "MsupNode", 2, 0, MML_ATT_COMMON " subscriptshift " },
711 { Mml::MsubNode, "msub", "MsubNode", 2, 0, MML_ATT_COMMON " superscriptshift " },
712 { Mml::MsubsupNode, "msubsup", "MsubsupNode", 3, 0, MML_ATT_COMMON " subscriptshift superscriptshift " },
713 { Mml::MoNode, "mo", "MoNode", NodeSpec::ChildAny, " TextNode MalignMark ", MML_ATT_COMMON MML_ATT_FONTINFO MML_ATT_OPINFO },
714 { Mml::MstyleNode, "mstyle", "MstyleNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON MML_ATT_MSTYLE },
715 { Mml::MphantomNode, "mphantom", "MphantomNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
716 { Mml::MalignMarkNode, "malignmark", "MalignMarkNode", 0, 0, "" },
717 { Mml::MfencedNode, "mfenced", "MfencedNode", NodeSpec::ChildAny, 0, MML_ATT_COMMON " open close separators " },
718 { Mml::MtableNode, "mtable", "MtableNode", NodeSpec::ChildAny, " MtrNode ", MML_ATT_COMMON MML_ATT_MTABLE },
719 { Mml::MtrNode, "mtr", "MtrNode", NodeSpec::ChildAny, " MtdNode ", MML_ATT_COMMON " rowalign columnalign groupalign " },
720 { Mml::MtdNode, "mtd", "MtdNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " rowspan columnspan rowalign columnalign groupalign " },
721 { Mml::MoverNode, "mover", "MoverNode", 2, 0, MML_ATT_COMMON " accent " },
722 { Mml::MunderNode, "munder", "MunderNode", 2, 0, MML_ATT_COMMON " accentunder " },
723 { Mml::MunderoverNode, "munderover", "MunderoverNode", 3, 0, MML_ATT_COMMON " accentunder accent " },
724 { Mml::MerrorNode, "merror", "MerrorNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON },
725 { Mml::MtextNode, "mtext", "MtextNode", 1, " TextNode ", MML_ATT_COMMON " width height depth linebreak " },
726 { Mml::MpaddedNode, "mpadded", "MpaddedNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth lspace " },
727 { Mml::MspaceNode, "mspace", "MspaceNode", NodeSpec::ImplicitMrow, 0, MML_ATT_COMMON " width height depth linebreak " },
728 { Mml::TextNode, 0, "TextNode", NodeSpec::ChildIgnore, 0, "" },
729 { Mml::UnknownNode, 0, "UnknownNode", NodeSpec::ChildAny, 0, 0 },
730 { Mml::NoNode, 0, 0, 0, 0, 0 }
731 };
732
733 static const char *g_oper_spec_names[g_oper_spec_rows] = { "accent", "fence", "largeop", "lspace", "minsize", "movablelimits", "rspace", "separator", "stretchy" /* stretchdir */ };
734 static const OperSpec g_oper_spec_data[] = {
735
736 { "!!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!!"
737 { "!" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "!"
738 { "!=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "!="
739 { "⩓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩓"
740 { "⁡" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁡"
741 { "≔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≔"
742 { "∖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "∖"
743 { "∵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∵"
744 { "˘" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˘"
745 { "⋒" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋒"
746 { "ⅅ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // ⅅ"
747 { "¸" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¸"
748 { "·" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "·"
749 { "⊙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊙"
750 { "⊖" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊖"
751 { "⊕" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊕"
752 { "⊗" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊗"
753 { "∲" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true"}, OperSpec::VStretch }, // ∲"
754 { "”" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ”"
755 { "’" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "’"
756 { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷"
757 { "≡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≡"
758 { "∮" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "∮"
759 { "∐" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∐"
760 { "∳", Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // &CounterClockwiseContourInteg
761 { "⨯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⨯"
762 { "⋓" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋓"
763 { "≍" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≍"
764 { "∇" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∇"
765 { "´" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "´"
766 { "˙" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "˙"
767 { "˝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ˝"
768 { "`" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "`"
769 { "&DiacriticalLeftArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftArrow;"
770 { "&DiacriticalLeftRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightArrow;"
771 { "&DiacriticalLeftRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftRightVector;"
772 { "&DiacriticalLeftVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalLeftVector;"
773 { "&DiacriticalRightArrow;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightArrow;"
774 { "&DiacriticalRightVector;" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &DiacriticalRightVector;"
775 { "˜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "˜"
776 { "⋄" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋄"
777 { "ⅆ" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "ⅆ"
778 { "≐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≐"
779 { "∯" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ∯"
780 { "¨" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "¨"
781 { "⇓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇓"
782 { "⇐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇐"
783 { "⇔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // ⇔"
784 { "⫤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫤"
785 { "⟸" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟸"
786 { "⟺" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟺"
787 { "⟹" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⟹"
788 { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒"
789 { "⊨" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊨"
790 { "⇑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇑"
791 { "⇕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇕"
792 { "∥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∥"
793 { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↓"
794 { "⤓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤓"
795 { "⇵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇵"
796 { "̑" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "̑"
797 { "⥐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥐"
798 { "⥞" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥞"
799 { "↽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↽"
800 { "⥖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥖"
801 { "⥟" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥟"
802 { "⇁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇁"
803 { "⥗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥗"
804 { "⊤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊤"
805 { "↧" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↧"
806 { "∈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∈"
807 { "⩵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩵"
808 { "≂" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂"
809 { "⇌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇌"
810 { "∃" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∃"
811 { "∀" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∀"
812 { "≥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≥"
813 { "⋛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋛"
814 { "≧" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧"
815 { "⪢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪢"
816 { "≷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≷"
817 { "⩾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩾"
818 { "≳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≳"
819 { "ˇ" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::NoStretch }, // "ˇ"
820 { "^" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "^"
821 { "─" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::HStretch }, // "─"
822 { "≎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎"
823 { "≏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏"
824 { "⇒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇒"
825 { "∫" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "∫"
826 { "⋂" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋂"
827 { "⁣" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // "⁣"
828 { "⁢" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⁢"
829 { "⟨" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟨"
830 { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "←"
831 { "⇤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇤"
832 { "⇆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇆"
833 { "&LeftBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&LeftBracketingBar;"
834 { "⌈" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌈"
835 { "⟦" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⟦"
836 { "&LeftDoubleBracketingBar;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &LeftDoubleBracketingBar;"
837 { "⥡" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥡"
838 { "⇃" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇃"
839 { "⥙" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥙"
840 { "⌊" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌊"
841 { "↔" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↔"
842 { "⥎" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥎"
843 { "&LeftSkeleton;" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&LeftSkeleton;"
844 { "⊣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊣"
845 { "↤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↤"
846 { "⥚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥚"
847 { "⊲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊲"
848 { "⧏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏"
849 { "⊴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊴"
850 { "⥑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥑"
851 { "⥠" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥠"
852 { "↿" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↿"
853 { "⥘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥘"
854 { "↼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↼"
855 { "⥒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "⥒"
856 { "⋚" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋚"
857 { "≦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≦"
858 { "≶" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≶"
859 { "⪡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡"
860 { "⩽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽"
861 { "≲" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≲"
862 { "⟵" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟵"
863 { "⟷" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟷"
864 { "⟶" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // "⟶"
865 { "↙" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↙"
866 { "↘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↘"
867 { "∓" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∓"
868 { "≫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ≫"
869 { "≪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪"
870 { "⫬" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⫬"
871 { "≢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≢"
872 { "≭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≭"
873 { "∦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ∦"
874 { "∉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∉"
875 { "≠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≠"
876 { "≂̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≂̸"
877 { "∄" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∄"
878 { "≯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≯"
879 { "≱" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≱"
880 { "≧̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≧̸"
881 { "≫̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≫̸"
882 { "≹" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≹"
883 { "⩾̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⩾̸"
884 { "≵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≵"
885 { "≎̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≎̸"
886 { "≏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≏̸"
887 { "⋪" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋪"
888 { "⧏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧏̸"
889 { "⋬" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋬"
890 { "≮" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≮"
891 { "≰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≰"
892 { "&NotLessFullEqual;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotLessFullEqual;"
893 { "≸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≸"
894 { "≪̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≪̸"
895 { "⩽̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⩽̸"
896 { "≴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≴"
897 { "⪢̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⪢̸"
898 { "⪡̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪡̸"
899 { "⊀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊀"
900 { "⪯̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯̸"
901 { "⋠" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋠"
902 { "&NotPrecedesTilde;" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&NotPrecedesTilde;"
903 { "∌" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∌"
904 { "⋫" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋫"
905 { "⧐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐̸"
906 { "⋭" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋭"
907 { "⊏̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏̸"
908 { "⋢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋢"
909 { "⊐̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐̸"
910 { "⋣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋣"
911 { "⊂⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊂⃒"
912 { "⊈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊈"
913 { "⊁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊁"
914 { "⪰̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰̸"
915 { "⋡" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ⋡"
916 { "≿̸" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿̸"
917 { "⊃⃒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃⃒"
918 { "⊉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊉"
919 { "≁" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≁"
920 { "≄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≄"
921 { "≇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≇"
922 { "≉" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≉"
923 { "∤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∤"
924 { "“" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // “"
925 { "‘" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "‘"
926 { "⩔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::VStretch }, // "⩔"
927 { "‾" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "‾"
928 { "⏞" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏞"
929 { "⎴" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎴"
930 { "⏜" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⏜"
931 { "∂" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∂"
932 { "±" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "±"
933 { "≺" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≺"
934 { "⪯" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪯"
935 { "≼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≼"
936 { "≾" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≾"
937 { "∏" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∏"
938 { "∷" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∷"
939 { "∝" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∝"
940 { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋"
941 { "⇋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇋"
942 { "⥯" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HStretch }, // ⥯"
943 { "⟩" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟩"
944 { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "→"
945 { "⇥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇥"
946 { "⇄" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇄"
947 { "&RightBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "&RightBracketingBar;"
948 { "⌉" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌉"
949 { "⟧" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⟧"
950 { "&RightDoubleBracketingBar;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // &RightDoubleBracketingBar;"
951 { "⥝" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥝"
952 { "⇂" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⇂"
953 { "⥕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥕"
954 { "⌋" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "⌋"
955 { "&RightSkeleton;" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "&RightSkeleton;"
956 { "⊢" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊢"
957 { "↦" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "↦"
958 { "⥛" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥛"
959 { "⊳" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊳"
960 { "⧐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⧐"
961 { "⊵" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊵"
962 { "⥏" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥏"
963 { "⥜" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥜"
964 { "↾" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "↾"
965 { "⥔" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⥔"
966 { "⇀" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⇀"
967 { "⥓" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HStretch }, // "⥓"
968 { "⥰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⥰"
969 { "↓" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "↓"
970 { "←" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "←"
971 { "→" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::HStretch }, // "→"
972 { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::VStretch }, // "↑"
973 { "∘" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∘"
974 { "√" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "√"
975 { "□" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "□"
976 { "⊓" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊓"
977 { "⊏" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊏"
978 { "⊑" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊑"
979 { "⊐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊐"
980 { "⊒" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊒"
981 { "⊔" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, "true" }, OperSpec::HVStretch }, // "⊔"
982 { "⋆" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋆"
983 { "⋐" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋐"
984 { "⊆" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊆"
985 { "≻" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≻"
986 { "⪰" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⪰"
987 { "≽" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≽"
988 { "≿" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≿"
989 { "∋" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∋"
990 { "∑" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "∑"
991 { "⊃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊃"
992 { "⊇" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊇"
993 { "∴" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∴"
994 { "∼" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∼"
995 { "≃" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≃"
996 { "≅" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≅"
997 { "≈" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≈"
998 { "⃛" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "⃛"
999 { "_" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "_"
1000 { "⏟" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏟"
1001 { "⎵" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⎵"
1002 { "⏝" , Mml::PostfixForm, { "true", 0, 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::HStretch }, // "⏝"
1003 { "⋃" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⋃"
1004 { "⊎" , Mml::PrefixForm, { 0, 0, "true", "0em", 0, "true", "thinmathspace", 0, "true" }, OperSpec::HVStretch }, // "⊎"
1005 { "↑" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↑"
1006 { "⤒" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⤒"
1007 { "⇅" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⇅"
1008 { "↕" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↕"
1009 { "⥮" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "⥮"
1010 { "⊥" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "⊥"
1011 { "↥" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, "true" }, OperSpec::VStretch }, // "↥"
1012 { "↖" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↖"
1013 { "↗" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::HVStretch }, // "↗"
1014 { "⋁" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋁"
1015 { "∣" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "∣"
1016 { "|" , Mml::InfixForm, { 0, 0, 0, "0em", "0", 0, "0em", 0, "true" }, OperSpec::VStretch }, // "|"
1017 { "❘" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "❘"
1018 { "≀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "≀"
1019 { "⋀" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "⋀"
1020 { "&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&"
1021 { "&&" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "&&"
1022 { "≤" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "≤"
1023 { "<" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<"
1024 { "<=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "<="
1025 { "<>" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "<>"
1026 { "'" , Mml::PostfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "'"
1027 { "(" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "("
1028 { ")" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // ")"
1029 { "*" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "*"
1030 { "**" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "**"
1031 { "*=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "*="
1032 { "+" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+"
1033 { "+" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "+"
1034 { "++" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "++"
1035 { "+=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "+="
1036 { "," , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // ","
1037 { "-" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-"
1038 { "-" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "veryverythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "-"
1039 { "--" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "--"
1040 { "-=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "-="
1041 { "->" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "->"
1042 { "." , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "."
1043 { ".." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // ".."
1044 { "..." , Mml::PostfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "0em", 0, 0 }, OperSpec::NoStretch }, // "..."
1045 { "/" , Mml::InfixForm, { 0, 0, 0, "thinmathspace", 0, 0, "thinmathspace", 0, "true" }, OperSpec::VStretch }, // "/"
1046 { "//" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "//"
1047 { "/=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "/="
1048 { ":" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":"
1049 { ":=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ":="
1050 { ";" , Mml::InfixForm, { 0, 0, 0, "0em", 0, 0, "verythickmathspace", "true", 0 }, OperSpec::NoStretch }, // ";"
1051 { ";" , Mml::PostfixForm, { 0, 0, 0, "0em", 0, 0, "0em", "true", 0 }, OperSpec::NoStretch }, // ";"
1052 { "=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "="
1053 { "==" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // "=="
1054 { ">" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">"
1055 { ">=" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, 0 }, OperSpec::NoStretch }, // ">="
1056 { "?" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "?"
1057 { "@" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "@"
1058 { "[" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "["
1059 { "]" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "]"
1060 { "^" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "^"
1061 { "_" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "_"
1062 { "lim" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "lim"
1063 { "max" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "max"
1064 { "min" , Mml::PrefixForm, { 0, 0, 0, "0em", 0, "true", "thinmathspace", 0, 0 }, OperSpec::NoStretch }, // "min"
1065 { "{" , Mml::PrefixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "{"
1066 { "|" , Mml::InfixForm, { 0, 0, 0, "thickmathspace", 0, 0, "thickmathspace", 0, "true" }, OperSpec::VStretch }, // "|"
1067 { "||" , Mml::InfixForm, { 0, 0, 0, "mediummathspace", 0, 0, "mediummathspace", 0, 0 }, OperSpec::NoStretch }, // "||"
1068 { "}" , Mml::PostfixForm, { 0, "true", 0, "0em", 0, 0, "0em", 0, "true" }, OperSpec::VStretch }, // "}"
1069 { "~" , Mml::InfixForm, { 0, 0, 0, "verythinmathspace", 0, 0, "verythinmathspace", 0, 0 }, OperSpec::NoStretch }, // "~"
1070
1071 { 0 , Mml::InfixForm, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, OperSpec::NoStretch }
1072 };
1073
1074 static const OperSpec g_oper_spec_defaults =
1075 { 0 , Mml::InfixForm, { "false", "false", "false", "thickmathspace", "1", "false", "thickmathspace", "false", "false" }, OperSpec::NoStretch };
1076
1077 static const uint g_oper_spec_count = sizeof(g_oper_spec_data)/sizeof(OperSpec) - 1;
1078
1079 static const EntitySpec g_xml_entity_data[] = {
1080 { "angzarr", "⍼" },
1081 { "cirmid", "⫯" },
1082 { "cudarrl", "⤸" },
1083 { "cudarrr", "⤵" },
1084 { "cularr", "↶" },
1085 { "cularrp", "⤽" },
1086 { "curarr", "↷" },
1087 { "curarrm", "⤼" },
1088 { "Darr", "↡" },
1089 { "dArr", "⇓" },
1090 { "ddarr", "⇊" },
1091 { "DDotrahd", "⤑" },
1092 { "dfisht", "⥿" },
1093 { "dHar", "⥥" },
1094 { "dharl", "⇃" },
1095 { "dharr", "⇂" },
1096 { "duarr", "⇵" },
1097 { "duhar", "⥯" },
1098 { "dzigrarr", "⟿" },
1099 { "erarr", "⥱" },
1100 { "hArr", "⇔" },
1101 { "harr", "↔" },
1102 { "harrcir", "⥈" },
1103 { "harrw", "↭" },
1104 { "hoarr", "⇿" },
1105 { "imof", "⊷" },
1106 { "lAarr", "⇚" },
1107 { "Larr", "↞" },
1108 { "larrbfs", "⤟" },
1109 { "larrfs", "⤝" },
1110 { "larrhk", "↩" },
1111 { "larrlp", "↫" },
1112 { "larrpl", "⤹" },
1113 { "larrsim", "⥳" },
1114 { "larrtl", "↢" },
1115 { "lAtail", "⤛" },
1116 { "latail", "⤙" },
1117 { "lBarr", "⤎" },
1118 { "lbarr", "⤌" },
1119 { "ldca", "⤶" },
1120 { "ldrdhar", "⥧" },
1121 { "ldrushar", "⥋" },
1122 { "ldsh", "↲" },
1123 { "lfisht", "⥼" },
1124 { "lHar", "⥢" },
1125 { "lhard", "↽" },
1126 { "lharu", "↼" },
1127 { "lharul", "⥪" },
1128 { "llarr", "⇇" },
1129 { "llhard", "⥫" },
1130 { "loarr", "⇽" },
1131 { "lrarr", "⇆" },
1132 { "lrhar", "⇋" },
1133 { "lrhard", "⥭" },
1134 { "lsh", "↰" },
1135 { "lurdshar", "⥊" },
1136 { "luruhar", "⥦" },
1137 { "Map", "⤅" },
1138 { "map", "↦" },
1139 { "midcir", "⫰" },
1140 { "mumap", "⊸" },
1141 { "nearhk", "⤤" },
1142 { "neArr", "⇗" },
1143 { "nearr", "↗" },
1144 { "nesear", "⤨" },
1145 { "nhArr", "⇎" },
1146 { "nharr", "↮" },
1147 { "nlArr", "⇍" },
1148 { "nlarr", "↚" },
1149 { "nrArr", "⇏" },
1150 { "nrarr", "↛" },
1151 { "nrarrc", "⤳̸" },
1152 { "nrarrw", "↝̸" },
1153 { "nvHarr", "⤄" },
1154 { "nvlArr", "⤂" },
1155 { "nvrArr", "⤃" },
1156 { "nwarhk", "⤣" },
1157 { "nwArr", "⇖" },
1158 { "nwarr", "↖" },
1159 { "nwnear", "⤧" },
1160 { "olarr", "↺" },
1161 { "orarr", "↻" },
1162 { "origof", "⊶" },
1163 { "rAarr", "⇛" },
1164 { "Rarr", "↠" },
1165 { "rarrap", "⥵" },
1166 { "rarrbfs", "⤠" },
1167 { "rarrc", "⤳" },
1168 { "rarrfs", "⤞" },
1169 { "rarrhk", "↪" },
1170 { "rarrlp", "↬" },
1171 { "rarrpl", "⥅" },
1172 { "rarrsim", "⥴" },
1173 { "Rarrtl", "⤖" },
1174 { "rarrtl", "↣" },
1175 { "rarrw", "↝" },
1176 { "rAtail", "⤜" },
1177 { "ratail", "⤚" },
1178 { "RBarr", "⤐" },
1179 { "rBarr", "⤏" },
1180 { "rbarr", "⤍" },
1181 { "rdca", "⤷" },
1182 { "rdldhar", "⥩" },
1183 { "rdsh", "↳" },
1184 { "rfisht", "⥽" },
1185 { "rHar", "⥤" },
1186 { "rhard", "⇁" },
1187 { "rharu", "⇀" },
1188 { "rharul", "⥬" },
1189 { "rlarr", "⇄" },
1190 { "rlhar", "⇌" },
1191 { "roarr", "⇾" },
1192 { "rrarr", "⇉" },
1193 { "rsh", "↱" },
1194 { "ruluhar", "⥨" },
1195 { "searhk", "⤥" },
1196 { "seArr", "⇘" },
1197 { "searr", "↘" },
1198 { "seswar", "⤩" },
1199 { "simrarr", "⥲" },
1200 { "slarr", "←" },
1201 { "srarr", "→" },
1202 { "swarhk", "⤦" },
1203 { "swArr", "⇙" },
1204 { "swarr", "↙" },
1205 { "swnwar", "⤪" },
1206 { "Uarr", "↟" },
1207 { "uArr", "⇑" },
1208 { "Uarrocir", "⥉" },
1209 { "udarr", "⇅" },
1210 { "udhar", "⥮" },
1211 { "ufisht", "⥾" },
1212 { "uHar", "⥣" },
1213 { "uharl", "↿" },
1214 { "uharr", "↾" },
1215 { "uuarr", "⇈" },
1216 { "vArr", "⇕" },
1217 { "varr", "↕" },
1218 { "xhArr", "⟺" },
1219 { "xharr", "⟷" },
1220 { "xlArr", "⟸" },
1221 { "xlarr", "⟵" },
1222 { "xmap", "⟼" },
1223 { "xrArr", "⟹" },
1224 { "xrarr", "⟶" },
1225 { "zigrarr", "⇝" },
1226 { "ac", "∾" },
1227 { "acE", "∾̳" },
1228 { "amalg", "⨿" },
1229 { "barvee", "⊽" },
1230 { "Barwed", "⌆" },
1231 { "barwed", "⌅" },
1232 { "bsolb", "⧅" },
1233 { "Cap", "⋒" },
1234 { "capand", "⩄" },
1235 { "capbrcup", "⩉" },
1236 { "capcap", "⩋" },
1237 { "capcup", "⩇" },
1238 { "capdot", "⩀" },
1239 { "caps", "∩︀" },
1240 { "ccaps", "⩍" },
1241 { "ccups", "⩌" },
1242 { "ccupssm", "⩐" },
1243 { "coprod", "∐" },
1244 { "Cup", "⋓" },
1245 { "cupbrcap", "⩈" },
1246 { "cupcap", "⩆" },
1247 { "cupcup", "⩊" },
1248 { "cupdot", "⊍" },
1249 { "cupor", "⩅" },
1250 { "cups", "∪︀" },
1251 { "cuvee", "⋎" },
1252 { "cuwed", "⋏" },
1253 { "Dagger", "‡" },
1254 { "dagger", "†" },
1255 { "diam", "⋄" },
1256 { "divonx", "⋇" },
1257 { "eplus", "⩱" },
1258 { "hercon", "⊹" },
1259 { "intcal", "⊺" },
1260 { "iprod", "⨼" },
1261 { "loplus", "⨭" },
1262 { "lotimes", "⨴" },
1263 { "lthree", "⋋" },
1264 { "ltimes", "⋉" },
1265 { "midast", "*" },
1266 { "minusb", "⊟" },
1267 { "minusd", "∸" },
1268 { "minusdu", "⨪" },
1269 { "ncap", "⩃" },
1270 { "ncup", "⩂" },
1271 { "oast", "⊛" },
1272 { "ocir", "⊚" },
1273 { "odash", "⊝" },
1274 { "odiv", "⨸" },
1275 { "odot", "⊙" },
1276 { "odsold", "⦼" },
1277 { "ofcir", "⦿" },
1278 { "ogt", "⧁" },
1279 { "ohbar", "⦵" },
1280 { "olcir", "⦾" },
1281 { "olt", "⧀" },
1282 { "omid", "⦶" },
1283 { "ominus", "⊖" },
1284 { "opar", "⦷" },
1285 { "operp", "⦹" },
1286 { "oplus", "⊕" },
1287 { "osol", "⊘" },
1288 { "Otimes", "⨷" },
1289 { "otimes", "⊗" },
1290 { "otimesas", "⨶" },
1291 { "ovbar", "⌽" },
1292 { "plusacir", "⨣" },
1293 { "plusb", "⊞" },
1294 { "pluscir", "⨢" },
1295 { "plusdo", "∔" },
1296 { "plusdu", "⨥" },
1297 { "pluse", "⩲" },
1298 { "plussim", "⨦" },
1299 { "plustwo", "⨧" },
1300 { "prod", "∏" },
1301 { "race", "⧚" },
1302 { "roplus", "⨮" },
1303 { "rotimes", "⨵" },
1304 { "rthree", "⋌" },
1305 { "rtimes", "⋊" },
1306 { "sdot", "⋅" },
1307 { "sdotb", "⊡" },
1308 { "setmn", "∖" },
1309 { "simplus", "⨤" },
1310 { "smashp", "⨳" },
1311 { "solb", "⧄" },
1312 { "sqcap", "⊓" },
1313 { "sqcaps", "⊓︀" },
1314 { "sqcup", "⊔" },
1315 { "sqcups", "⊔︀" },
1316 { "ssetmn", "∖" },
1317 { "sstarf", "⋆" },
1318 { "subdot", "⪽" },
1319 { "sum", "∑" },
1320 { "supdot", "⪾" },
1321 { "timesb", "⊠" },
1322 { "timesbar", "⨱" },
1323 { "timesd", "⨰" },
1324 { "tridot", "◬" },
1325 { "triminus", "⨺" },
1326 { "triplus", "⨹" },
1327 { "trisb", "⧍" },
1328 { "tritime", "⨻" },
1329 { "uplus", "⊎" },
1330 { "veebar", "⊻" },
1331 { "wedbar", "⩟" },
1332 { "wreath", "≀" },
1333 { "xcap", "⋂" },
1334 { "xcirc", "◯" },
1335 { "xcup", "⋃" },
1336 { "xdtri", "▽" },
1337 { "xodot", "⨀" },
1338 { "xoplus", "⨁" },
1339 { "xotime", "⨂" },
1340 { "xsqcup", "⨆" },
1341 { "xuplus", "⨄" },
1342 { "xutri", "△" },
1343 { "xvee", "⋁" },
1344 { "xwedge", "⋀" },
1345 { "dlcorn", "⌞" },
1346 { "drcorn", "⌟" },
1347 { "gtlPar", "⦕" },
1348 { "langd", "⦑" },
1349 { "lbrke", "⦋" },
1350 { "lbrksld", "⦏" },
1351 { "lbrkslu", "⦍" },
1352 { "lceil", "⌈" },
1353 { "lfloor", "⌊" },
1354 { "lmoust", "⎰" },
1355 { "lparlt", "⦓" },
1356 { "ltrPar", "⦖" },
1357 { "rangd", "⦒" },
1358 { "rbrke", "⦌" },
1359 { "rbrksld", "⦎" },
1360 { "rbrkslu", "⦐" },
1361 { "rceil", "⌉" },
1362 { "rfloor", "⌋" },
1363 { "rmoust", "⎱" },
1364 { "rpargt", "⦔" },
1365 { "ulcorn", "⌜" },
1366 { "urcorn", "⌝" },
1367 { "gnap", "⪊" },
1368 { "gnE", "≩" },
1369 { "gne", "⪈" },
1370 { "gnsim", "⋧" },
1371 { "gvnE", "≩︀" },
1372 { "lnap", "⪉" },
1373 { "lnE", "≨" },
1374 { "lne", "⪇" },
1375 { "lnsim", "⋦" },
1376 { "lvnE", "≨︀" },
1377 { "nap", "≉" },
1378 { "napE", "⩰̸" },
1379 { "napid", "≋̸" },
1380 { "ncong", "≇" },
1381 { "ncongdot", "⩭̸" },
1382 { "nequiv", "≢" },
1383 { "ngE", "≧̸" },
1384 { "nge", "≱" },
1385 { "nges", "⩾̸" },
1386 { "nGg", "⋙̸" },
1387 { "ngsim", "≵" },
1388 { "nGt", "≫⃒" },
1389 { "ngt", "≯" },
1390 { "nGtv", "≫̸" },
1391 { "nlE", "≦̸" },
1392 { "nle", "≰" },
1393 { "nles", "⩽̸" },
1394 { "nLl", "⋘̸" },
1395 { "nlsim", "≴" },
1396 { "nLt", "≪⃒" },
1397 { "nlt", "≮" },
1398 { "nltri", "⋪" },
1399 { "nltrie", "⋬" },
1400 { "nLtv", "≪̸" },
1401 { "nmid", "∤" },
1402 { "npar", "∦" },
1403 { "npr", "⊀" },
1404 { "nprcue", "⋠" },
1405 { "npre", "⪯̸" },
1406 { "nrtri", "⋫" },
1407 { "nrtrie", "⋭" },
1408 { "nsc", "⊁" },
1409 { "nsccue", "⋡" },
1410 { "nsce", "⪰̸" },
1411 { "nsim", "≁" },
1412 { "nsime", "≄" },
1413 { "nsmid", "∤" },
1414 { "nspar", "∦" },
1415 { "nsqsube", "⋢" },
1416 { "nsqsupe", "⋣" },
1417 { "nsub", "⊄" },
1418 { "nsubE", "⫅̸" },
1419 { "nsube", "⊈" },
1420 { "nsup", "⊅" },
1421 { "nsupE", "⫆̸" },
1422 { "nsupe", "⊉" },
1423 { "ntgl", "≹" },
1424 { "ntlg", "≸" },
1425 { "nvap", "≍⃒" },
1426 { "nVDash", "⊯" },
1427 { "nVdash", "⊮" },
1428 { "nvDash", "⊭" },
1429 { "nvdash", "⊬" },
1430 { "nvge", "≥⃒" },
1431 { "nvgt", ">⃒" },
1432 { "nvle", "≤⃒" },
1433 { "nvlt", "<⃒" },
1434 { "nvltrie", "⊴⃒" },
1435 { "nvrtrie", "⊵⃒" },
1436 { "nvsim", "∼⃒" },
1437 { "parsim", "⫳" },
1438 { "prnap", "⪹" },
1439 { "prnE", "⪵" },
1440 { "prnsim", "⋨" },
1441 { "rnmid", "⫮" },
1442 { "scnap", "⪺" },
1443 { "scnE", "⪶" },
1444 { "scnsim", "⋩" },
1445 { "simne", "≆" },
1446 { "solbar", "⌿" },
1447 { "subnE", "⫋" },
1448 { "subne", "⊊" },
1449 { "supnE", "⫌" },
1450 { "supne", "⊋" },
1451 { "vnsub", "⊂⃒" },
1452 { "vnsup", "⊃⃒" },
1453 { "vsubnE", "⫋︀" },
1454 { "vsubne", "⊊︀" },
1455 { "vsupnE", "⫌︀" },
1456 { "vsupne", "⊋︀" },
1457 { "ang", "∠" },
1458 { "ange", "⦤" },
1459 { "angmsd", "∡" },
1460 { "angmsdaa", "⦨" },
1461 { "angmsdab", "⦩" },
1462 { "angmsdac", "⦪" },
1463 { "angmsdad", "⦫" },
1464 { "angmsdae", "⦬" },
1465 { "angmsdaf", "⦭" },
1466 { "angmsdag", "⦮" },
1467 { "angmsdah", "⦯" },
1468 { "angrtvb", "⊾" },
1469 { "angrtvbd", "⦝" },
1470 { "bbrk", "⎵" },
1471 { "bemptyv", "⦰" },
1472 { "beth", "ℶ" },
1473 { "boxbox", "⧉" },
1474 { "bprime", "‵" },
1475 { "bsemi", "⁏" },
1476 { "cemptyv", "⦲" },
1477 { "cirE", "⧃" },
1478 { "cirscir", "⧂" },
1479 { "comp", "∁" },
1480 { "daleth", "ℸ" },
1481 { "demptyv", "⦱" },
1482 { "ell", "ℓ" },
1483 { "empty", "∅" },
1484 { "emptyv", "∅" },
1485 { "gimel", "ℷ" },
1486 { "iiota", "℩" },
1487 { "image", "ℑ" },
1488 { "imath", "ı" },
1489 { "jmath", "j" },
1490 { "laemptyv", "⦴" },
1491 { "lltri", "◺" },
1492 { "lrtri", "⊿" },
1493 { "mho", "℧" },
1494 { "nang", "∠⃒" },
1495 { "nexist", "∄" },
1496 { "oS", "Ⓢ" },
1497 { "planck", "ℏ" },
1498 { "plankv", "ℏ" },
1499 { "raemptyv", "⦳" },
1500 { "range", "⦥" },
1501 { "real", "ℜ" },
1502 { "tbrk", "⎴" },
1503 { "ultri", "◸" },
1504 { "urtri", "◹" },
1505 { "vzigzag", "⦚" },
1506 { "weierp", "℘" },
1507 { "apE", "⩰" },
1508 { "ape", "≊" },
1509 { "apid", "≋" },
1510 { "asymp", "≈" },
1511 { "Barv", "⫧" },
1512 { "bcong", "≌" },
1513 { "bepsi", "϶" },
1514 { "bowtie", "⋈" },
1515 { "bsim", "∽" },
1516 { "bsime", "⋍" },
1517 { "bsolhsub", "\⊂" },
1518 { "bump", "≎" },
1519 { "bumpE", "⪮" },
1520 { "bumpe", "≏" },
1521 { "cire", "≗" },
1522 { "Colon", "∷" },
1523 { "Colone", "⩴" },
1524 { "colone", "≔" },
1525 { "congdot", "⩭" },
1526 { "csub", "⫏" },
1527 { "csube", "⫑" },
1528 { "csup", "⫐" },
1529 { "csupe", "⫒" },
1530 { "cuepr", "⋞" },
1531 { "cuesc", "⋟" },
1532 { "Dashv", "⫤" },
1533 { "dashv", "⊣" },
1534 { "easter", "⩮" },
1535 { "ecir", "≖" },
1536 { "ecolon", "≕" },
1537 { "eDDot", "⩷" },
1538 { "eDot", "≑" },
1539 { "efDot", "≒" },
1540 { "eg", "⪚" },
1541 { "egs", "⪖" },
1542 { "egsdot", "⪘" },
1543 { "el", "⪙" },
1544 { "els", "⪕" },
1545 { "elsdot", "⪗" },
1546 { "equest", "≟" },
1547 { "equivDD", "⩸" },
1548 { "erDot", "≓" },
1549 { "esdot", "≐" },
1550 { "Esim", "⩳" },
1551 { "esim", "≂" },
1552 { "fork", "⋔" },
1553 { "forkv", "⫙" },
1554 { "frown", "⌢" },
1555 { "gap", "⪆" },
1556 { "gE", "≧" },
1557 { "gEl", "⪌" },
1558 { "gel", "⋛" },
1559 { "ges", "⩾" },
1560 { "gescc", "⪩" },
1561 { "gesdot", "⪀" },
1562 { "gesdoto", "⪂" },
1563 { "gesdotol", "⪄" },
1564 { "gesl", "⋛︀" },
1565 { "gesles", "⪔" },
1566 { "Gg", "⋙" },
1567 { "gl", "≷" },
1568 { "gla", "⪥" },
1569 { "glE", "⪒" },
1570 { "glj", "⪤" },
1571 { "gsim", "≳" },
1572 { "gsime", "⪎" },
1573 { "gsiml", "⪐" },
1574 { "Gt", "≫" },
1575 { "gtcc", "⪧" },
1576 { "gtcir", "⩺" },
1577 { "gtdot", "⋗" },
1578 { "gtquest", "⩼" },
1579 { "gtrarr", "⥸" },
1580 { "homtht", "∻" },
1581 { "lap", "⪅" },
1582 { "lat", "⪫" },
1583 { "late", "⪭" },
1584 { "lates", "⪭︀" },
1585 { "lE", "≦" },
1586 { "lEg", "⪋" },
1587 { "leg", "⋚" },
1588 { "les", "⩽" },
1589 { "lescc", "⪨" },
1590 { "lesdot", "⩿" },
1591 { "lesdoto", "⪁" },
1592 { "lesdotor", "⪃" },
1593 { "lesg", "⋚︀" },
1594 { "lesges", "⪓" },
1595 { "lg", "≶" },
1596 { "lgE", "⪑" },
1597 { "Ll", "⋘" },
1598 { "lsim", "≲" },
1599 { "lsime", "⪍" },
1600 { "lsimg", "⪏" },
1601 { "Lt", "≪" },
1602 { "ltcc", "⪦" },
1603 { "ltcir", "⩹" },
1604 { "ltdot", "⋖" },
1605 { "ltlarr", "⥶" },
1606 { "ltquest", "⩻" },
1607 { "ltrie", "⊴" },
1608 { "mcomma", "⨩" },
1609 { "mDDot", "∺" },
1610 { "mid", "∣" },
1611 { "mlcp", "⫛" },
1612 { "models", "⊧" },
1613 { "mstpos", "∾" },
1614 { "Pr", "⪻" },
1615 { "pr", "≺" },
1616 { "prap", "⪷" },
1617 { "prcue", "≼" },
1618 { "prE", "⪳" },
1619 { "pre", "⪯" },
1620 { "prsim", "≾" },
1621 { "prurel", "⊰" },
1622 { "ratio", "∶" },
1623 { "rtrie", "⊵" },
1624 { "rtriltri", "⧎" },
1625 { "Sc", "⪼" },
1626 { "sc", "≻" },
1627 { "scap", "⪸" },
1628 { "sccue", "≽" },
1629 { "scE", "⪴" },
1630 { "sce", "⪰" },
1631 { "scsim", "≿" },
1632 { "sdote", "⩦" },
1633 { "simg", "⪞" },
1634 { "simgE", "⪠" },
1635 { "siml", "⪝" },
1636 { "simlE", "⪟" },
1637 { "smid", "∣" },
1638 { "smile", "⌣" },
1639 { "smt", "⪪" },
1640 { "smte", "⪬" },
1641 { "smtes", "⪬︀" },
1642 { "spar", "∥" },
1643 { "sqsub", "⊏" },
1644 { "sqsube", "⊑" },
1645 { "sqsup", "⊐" },
1646 { "sqsupe", "⊒" },
1647 { "Sub", "⋐" },
1648 { "subE", "⫅" },
1649 { "subedot", "⫃" },
1650 { "submult", "⫁" },
1651 { "subplus", "⪿" },
1652 { "subrarr", "⥹" },
1653 { "subsim", "⫇" },
1654 { "subsub", "⫕" },
1655 { "subsup", "⫓" },
1656 { "Sup", "⋑" },
1657 { "supdsub", "⫘" },
1658 { "supE", "⫆" },
1659 { "supedot", "⫄" },
1660 { "suphsol", "⊅" },
1661 { "suphsub", "⫗" },
1662 { "suplarr", "⥻" },
1663 { "supmult", "⫂" },
1664 { "supplus", "⫀" },
1665 { "supsim", "⫈" },
1666 { "supsub", "⫔" },
1667 { "supsup", "⫖" },
1668 { "thkap", "≈" },
1669 { "topfork", "⫚" },
1670 { "trie", "≜" },
1671 { "twixt", "≬" },
1672 { "Vbar", "⫫" },
1673 { "vBar", "⫨" },
1674 { "vBarv", "⫩" },
1675 { "VDash", "⊫" },
1676 { "Vdash", "⊩" },
1677 { "vDash", "⊨" },
1678 { "vdash", "⊢" },
1679 { "Vdashl", "⫦" },
1680 { "vltri", "⊲" },
1681 { "vprop", "∝" },
1682 { "vrtri", "⊳" },
1683 { "Vvdash", "⊪" },
1684 { "alpha", "α" },
1685 { "beta", "β" },
1686 { "chi", "χ" },
1687 { "Delta", "Δ" },
1688 { "delta", "δ" },
1689 { "epsi", "ε" },
1690 { "epsiv", "ɛ" },
1691 { "eta", "η" },
1692 { "Gamma", "Γ" },
1693 { "gamma", "γ" },
1694 { "Gammad", "Ϝ" },
1695 { "gammad", "ϝ" },
1696 { "iota", "ι" },
1697 { "kappa", "κ" },
1698 { "kappav", "ϰ" },
1699 { "Lambda", "Λ" },
1700 { "lambda", "λ" },
1701 { "mu", "μ" },
1702 { "nu", "ν" },
1703 { "Omega", "Ω" },
1704 { "omega", "ω" },
1705 { "Phi", "Φ" },
1706 { "phi", "ϕ" },
1707 { "phiv", "φ" },
1708 { "Pi", "Π" },
1709 { "pi", "π" },
1710 { "piv", "ϖ" },
1711 { "Psi", "Ψ" },
1712 { "psi", "ψ" },
1713 { "rho", "ρ" },
1714 { "rhov", "ϱ" },
1715 { "Sigma", "Σ" },
1716 { "sigma", "σ" },
1717 { "sigmav", "ς" },
1718 { "tau", "τ" },
1719 { "Theta", "Θ" },
1720 { "theta", "θ" },
1721 { "thetav", "ϑ" },
1722 { "Upsi", "ϒ" },
1723 { "upsi", "υ" },
1724 { "Xi", "Ξ" },
1725 { "xi", "ξ" },
1726 { "zeta", "ζ" },
1727 { "Cfr", "ℭ" },
1728 { "Hfr", "ℌ" },
1729 { "Ifr", "ℑ" },
1730 { "Rfr", "ℜ" },
1731 { "Zfr", "ℨ" },
1732 { "Copf", "ℂ" },
1733 { "Hopf", "ℍ" },
1734 { "Nopf", "ℕ" },
1735 { "Popf", "ℙ" },
1736 { "Qopf", "ℚ" },
1737 { "Ropf", "ℝ" },
1738 { "Zopf", "ℤ" },
1739 { "Bscr", "ℬ" },
1740 { "Escr", "ℰ" },
1741 { "escr", "ℯ" },
1742 { "Fscr", "ℱ" },
1743 { "gscr", "ℊ" },
1744 { "Hscr", "ℋ" },
1745 { "Iscr", "ℐ" },
1746 { "Lscr", "ℒ" },
1747 { "Mscr", "ℳ" },
1748 { "oscr", "ℴ" },
1749 { "pscr", "𝓅" },
1750 { "Rscr", "ℛ" },
1751 { "acd", "∿" },
1752 { "aleph", "ℵ" },
1753 { "And", "⩓" },
1754 { "and", "∧" },
1755 { "andand", "⩕" },
1756 { "andd", "⩜" },
1757 { "andslope", "⩘" },
1758 { "andv", "⩚" },
1759 { "angrt", "∟" },
1760 { "angsph", "∢" },
1761 { "angst", "Å" },
1762 { "ap", "≈" },
1763 { "apacir", "⩯" },
1764 { "awconint", "∳" },
1765 { "awint", "⨑" },
1766 { "becaus", "∵" },
1767 { "bernou", "ℬ" },
1768 { "bne", "=⃥" },
1769 { "bnequiv", "≡⃥" },
1770 { "bNot", "⫭" },
1771 { "bnot", "⌐" },
1772 { "bottom", "⊥" },
1773 { "cap", "∩" },
1774 { "Cconint", "∰" },
1775 { "cirfnint", "⨐" },
1776 { "compfn", "∘" },
1777 { "cong", "≅" },
1778 { "Conint", "∯" },
1779 { "conint", "∮" },
1780 { "ctdot", "⋯" },
1781 { "cup", "∪" },
1782 { "cwconint", "∲" },
1783 { "cwint", "∱" },
1784 { "cylcty", "⌭" },
1785 { "disin", "⋲" },
1786 { "Dot", "¨" },
1787 { "DotDot", "⃜" },
1788 { "dsol", "⧶" },
1789 { "dtdot", "⋱" },
1790 { "dwangle", "⦦" },
1791 { "epar", "⋕" },
1792 { "eparsl", "⧣" },
1793 { "equiv", "≡" },
1794 { "eqvparsl", "⧥" },
1795 { "exist", "∃" },
1796 { "fnof", "ƒ" },
1797 { "forall", "∀" },
1798 { "fpartint", "⨍" },
1799 { "ge", "≥" },
1800 { "hamilt", "ℋ" },
1801 { "iff", "⇔" },
1802 { "iinfin", "⧜" },
1803 { "infin", "∞" },
1804 { "Int", "∬" },
1805 { "int", "∫" },
1806 { "intlarhk", "⨗" },
1807 { "isin", "∈" },
1808 { "isindot", "⋵" },
1809 { "isinE", "⋹" },
1810 { "isins", "⋴" },
1811 { "isinsv", "⋳" },
1812 { "isinv", "∈" },
1813 { "lagran", "ℒ" },
1814 { "Lang", "《" },
1815 { "lang", "〈" },
1816 { "lArr", "⇐" },
1817 { "lbbrk", "〔" },
1818 { "le", "≤" },
1819 { "loang", "〘" },
1820 { "lobrk", "〚" },
1821 { "lopar", "⦅" },
1822 { "lowast", "∗" },
1823 { "minus", "−" },
1824 { "mnplus", "∓" },
1825 { "nabla", "∇" },
1826 { "ne", "≠" },
1827 { "nedot", "≐̸" },
1828 { "nhpar", "⫲" },
1829 { "ni", "∋" },
1830 { "nis", "⋼" },
1831 { "nisd", "⋺" },
1832 { "niv", "∋" },
1833 { "Not", "⫬" },
1834 { "notin", "∉" },
1835 { "notindot", "⋵̸" },
1836 { "notinva", "∉" },
1837 { "notinvb", "⋷" },
1838 { "notinvc", "⋶" },
1839 { "notni", "∌" },
1840 { "notniva", "∌" },
1841 { "notnivb", "⋾" },
1842 { "notnivc", "⋽" },
1843 { "nparsl", "⫽⃥" },
1844 { "npart", "∂̸" },
1845 { "npolint", "⨔" },
1846 { "nvinfin", "⧞" },
1847 { "olcross", "⦻" },
1848 { "Or", "⩔" },
1849 { "or", "∨" },
1850 { "ord", "⩝" },
1851 { "order", "ℴ" },
1852 { "oror", "⩖" },
1853 { "orslope", "⩗" },
1854 { "orv", "⩛" },
1855 { "par", "∥" },
1856 { "parsl", "⫽" },
1857 { "part", "∂" },
1858 { "permil", "‰" },
1859 { "perp", "⊥" },
1860 { "pertenk", "‱" },
1861 { "phmmat", "ℳ" },
1862 { "pointint", "⨕" },
1863 { "Prime", "″" },
1864 { "prime", "′" },
1865 { "profalar", "⌮" },
1866 { "profline", "⌒" },
1867 { "profsurf", "⌓" },
1868 { "prop", "∝" },
1869 { "qint", "⨌" },
1870 { "qprime", "⁗" },
1871 { "quatint", "⨖" },
1872 { "radic", "√" },
1873 { "Rang", "》" },
1874 { "rang", "〉" },
1875 { "rArr", "⇒" },
1876 { "rbbrk", "〕" },
1877 { "roang", "〙" },
1878 { "robrk", "〛" },
1879 { "ropar", "⦆" },
1880 { "rppolint", "⨒" },
1881 { "scpolint", "⨓" },
1882 { "sim", "∼" },
1883 { "simdot", "⩪" },
1884 { "sime", "≃" },
1885 { "smeparsl", "⧤" },
1886 { "square", "□" },
1887 { "squarf", "▪" },
1888 { "sub", "⊂" },
1889 { "sube", "⊆" },
1890 { "sup", "⊃" },
1891 { "supe", "⊇" },
1892 { "tdot", "⃛" },
1893 { "there4", "∴" },
1894 { "tint", "∭" },
1895 { "top", "⊤" },
1896 { "topbot", "⌶" },
1897 { "topcir", "⫱" },
1898 { "tprime", "‴" },
1899 { "utdot", "⋰" },
1900 { "uwangle", "⦧" },
1901 { "vangrt", "⦜" },
1902 { "veeeq", "≚" },
1903 { "Verbar", "‖" },
1904 { "wedgeq", "≙" },
1905 { "xnis", "⋻" },
1906 { "boxDL", "╗" },
1907 { "boxDl", "╖" },
1908 { "boxdL", "╕" },
1909 { "boxdl", "┐" },
1910 { "boxDR", "╔" },
1911 { "boxDr", "╓" },
1912 { "boxdR", "╒" },
1913 { "boxdr", "┌" },
1914 { "boxH", "═" },
1915 { "boxh", "─" },
1916 { "boxHD", "╦" },
1917 { "boxHd", "╤" },
1918 { "boxhD", "╥" },
1919 { "boxhd", "┬" },
1920 { "boxHU", "╩" },
1921 { "boxHu", "╧" },
1922 { "boxhU", "╨" },
1923 { "boxhu", "┴" },
1924 { "boxUL", "╝" },
1925 { "boxUl", "╜" },
1926 { "boxuL", "╛" },
1927 { "boxul", "┘" },
1928 { "boxUR", "╚" },
1929 { "boxUr", "╙" },
1930 { "boxuR", "╘" },
1931 { "boxur", "└" },
1932 { "boxV", "║" },
1933 { "boxv", "│" },
1934 { "boxVH", "╬" },
1935 { "boxVh", "╫" },
1936 { "boxvH", "╪" },
1937 { "boxvh", "┼" },
1938 { "boxVL", "╣" },
1939 { "boxVl", "╢" },
1940 { "boxvL", "╡" },
1941 { "boxvl", "┤" },
1942 { "boxVR", "╠" },
1943 { "boxVr", "╟" },
1944 { "boxvR", "╞" },
1945 { "boxvr", "├" },
1946 { "Acy", "А" },
1947 { "acy", "а" },
1948 { "Bcy", "Б" },
1949 { "bcy", "б" },
1950 { "CHcy", "Ч" },
1951 { "chcy", "ч" },
1952 { "Dcy", "Д" },
1953 { "dcy", "д" },
1954 { "Ecy", "Э" },
1955 { "ecy", "э" },
1956 { "Fcy", "Ф" },
1957 { "fcy", "ф" },
1958 { "Gcy", "Г" },
1959 { "gcy", "г" },
1960 { "HARDcy", "Ъ" },
1961 { "hardcy", "ъ" },
1962 { "Icy", "И" },
1963 { "icy", "и" },
1964 { "IEcy", "Е" },
1965 { "iecy", "е" },
1966 { "IOcy", "Ё" },
1967 { "iocy", "ё" },
1968 { "Jcy", "Й" },
1969 { "jcy", "й" },
1970 { "Kcy", "К" },
1971 { "kcy", "к" },
1972 { "KHcy", "Х" },
1973 { "khcy", "х" },
1974 { "Lcy", "Л" },
1975 { "lcy", "л" },
1976 { "Mcy", "М" },
1977 { "mcy", "м" },
1978 { "Ncy", "Н" },
1979 { "ncy", "н" },
1980 { "numero", "№" },
1981 { "Ocy", "О" },
1982 { "ocy", "о" },
1983 { "Pcy", "П" },
1984 { "pcy", "п" },
1985 { "Rcy", "Р" },
1986 { "rcy", "р" },
1987 { "Scy", "С" },
1988 { "scy", "с" },
1989 { "SHCHcy", "Щ" },
1990 { "shchcy", "щ" },
1991 { "SHcy", "Ш" },
1992 { "shcy", "ш" },
1993 { "SOFTcy", "Ь" },
1994 { "softcy", "ь" },
1995 { "Tcy", "Т" },
1996 { "tcy", "т" },
1997 { "TScy", "Ц" },
1998 { "tscy", "ц" },
1999 { "Ucy", "У" },
2000 { "ucy", "у" },
2001 { "Vcy", "В" },
2002 { "vcy", "в" },
2003 { "YAcy", "Я" },
2004 { "yacy", "я" },
2005 { "Ycy", "Ы" },
2006 { "ycy", "ы" },
2007 { "YUcy", "Ю" },
2008 { "yucy", "ю" },
2009 { "Zcy", "З" },
2010 { "zcy", "з" },
2011 { "ZHcy", "Ж" },
2012 { "zhcy", "ж" },
2013 { "DJcy", "Ђ" },
2014 { "djcy", "ђ" },
2015 { "DScy", "Ѕ" },
2016 { "dscy", "ѕ" },
2017 { "DZcy", "Џ" },
2018 { "dzcy", "џ" },
2019 { "GJcy", "Ѓ" },
2020 { "gjcy", "ѓ" },
2021 { "Iukcy", "І" },
2022 { "iukcy", "і" },
2023 { "Jsercy", "Ј" },
2024 { "jsercy", "ј" },
2025 { "Jukcy", "Є" },
2026 { "jukcy", "є" },
2027 { "KJcy", "Ќ" },
2028 { "kjcy", "ќ" },
2029 { "LJcy", "Љ" },
2030 { "ljcy", "љ" },
2031 { "NJcy", "Њ" },
2032 { "njcy", "њ" },
2033 { "TSHcy", "Ћ" },
2034 { "tshcy", "ћ" },
2035 { "Ubrcy", "Ў" },
2036 { "ubrcy", "ў" },
2037 { "YIcy", "Ї" },
2038 { "yicy", "ї" },
2039 { "acute", "´" },
2040 { "breve", "˘" },
2041 { "caron", "ˇ" },
2042 { "cedil", "¸" },
2043 { "circ", "ˆ" },
2044 { "dblac", "˝" },
2045 { "die", "¨" },
2046 { "dot", "˙" },
2047 { "grave", "`" },
2048 { "macr", "¯" },
2049 { "ogon", "˛" },
2050 { "ring", "˚" },
2051 { "tilde", "˜" },
2052 { "uml", "¨" },
2053 { "Aacute", "Á" },
2054 { "aacute", "á" },
2055 { "Acirc", "Â" },
2056 { "acirc", "â" },
2057 { "AElig", "Æ" },
2058 { "aelig", "æ" },
2059 { "Agrave", "À" },
2060 { "agrave", "à" },
2061 { "Aring", "Å" },
2062 { "aring", "å" },
2063 { "Atilde", "Ã" },
2064 { "atilde", "ã" },
2065 { "Auml", "Ä" },
2066 { "auml", "ä" },
2067 { "Ccedil", "Ç" },
2068 { "ccedil", "ç" },
2069 { "Eacute", "É" },
2070 { "eacute", "é" },
2071 { "Ecirc", "Ê" },
2072 { "ecirc", "ê" },
2073 { "Egrave", "È" },
2074 { "egrave", "è" },
2075 { "ETH", "Ð" },
2076 { "eth", "ð" },
2077 { "Euml", "Ë" },
2078 { "euml", "ë" },
2079 { "Iacute", "Í" },
2080 { "iacute", "í" },
2081 { "Icirc", "Î" },
2082 { "icirc", "î" },
2083 { "Igrave", "Ì" },
2084 { "igrave", "ì" },
2085 { "Iuml", "Ï" },
2086 { "iuml", "ï" },
2087 { "Ntilde", "Ñ" },
2088 { "ntilde", "ñ" },
2089 { "Oacute", "Ó" },
2090 { "oacute", "ó" },
2091 { "Ocirc", "Ô" },
2092 { "ocirc", "ô" },
2093 { "Ograve", "Ò" },
2094 { "ograve", "ò" },
2095 { "Oslash", "Ø" },
2096 { "oslash", "ø" },
2097 { "Otilde", "Õ" },
2098 { "otilde", "õ" },
2099 { "Ouml", "Ö" },
2100 { "ouml", "ö" },
2101 { "szlig", "ß" },
2102 { "THORN", "Þ" },
2103 { "thorn", "þ" },
2104 { "Uacute", "Ú" },
2105 { "uacute", "ú" },
2106 { "Ucirc", "Û" },
2107 { "ucirc", "û" },
2108 { "Ugrave", "Ù" },
2109 { "ugrave", "ù" },
2110 { "Uuml", "Ü" },
2111 { "uuml", "ü" },
2112 { "Yacute", "Ý" },
2113 { "yacute", "ý" },
2114 { "yuml", "ÿ" },
2115 { "Abreve", "Ă" },
2116 { "abreve", "ă" },
2117 { "Amacr", "Ā" },
2118 { "amacr", "ā" },
2119 { "Aogon", "Ą" },
2120 { "aogon", "ą" },
2121 { "Cacute", "Ć" },
2122 { "cacute", "ć" },
2123 { "Ccaron", "Č" },
2124 { "ccaron", "č" },
2125 { "Ccirc", "Ĉ" },
2126 { "ccirc", "ĉ" },
2127 { "Cdot", "Ċ" },
2128 { "cdot", "ċ" },
2129 { "Dcaron", "Ď" },
2130 { "dcaron", "ď" },
2131 { "Dstrok", "Đ" },
2132 { "dstrok", "đ" },
2133 { "Ecaron", "Ě" },
2134 { "ecaron", "ě" },
2135 { "Edot", "Ė" },
2136 { "edot", "ė" },
2137 { "Emacr", "Ē" },
2138 { "emacr", "ē" },
2139 { "ENG", "Ŋ" },
2140 { "eng", "ŋ" },
2141 { "Eogon", "Ę" },
2142 { "eogon", "ę" },
2143 { "gacute", "ǵ" },
2144 { "Gbreve", "Ğ" },
2145 { "gbreve", "ğ" },
2146 { "Gcedil", "Ģ" },
2147 { "Gcirc", "Ĝ" },
2148 { "gcirc", "ĝ" },
2149 { "Gdot", "Ġ" },
2150 { "gdot", "ġ" },
2151 { "Hcirc", "Ĥ" },
2152 { "hcirc", "ĥ" },
2153 { "Hstrok", "Ħ" },
2154 { "hstrok", "ħ" },
2155 { "Idot", "İ" },
2156 { "IJlig", "IJ" },
2157 { "ijlig", "ij" },
2158 { "Imacr", "Ī" },
2159 { "imacr", "ī" },
2160 { "inodot", "ı" },
2161 { "Iogon", "Į" },
2162 { "iogon", "į" },
2163 { "Itilde", "Ĩ" },
2164 { "itilde", "ĩ" },
2165 { "Jcirc", "Ĵ" },
2166 { "jcirc", "ĵ" },
2167 { "Kcedil", "Ķ" },
2168 { "kcedil", "ķ" },
2169 { "kgreen", "ĸ" },
2170 { "Lacute", "Ĺ" },
2171 { "lacute", "ĺ" },
2172 { "Lcaron", "Ľ" },
2173 { "lcaron", "ľ" },
2174 { "Lcedil", "Ļ" },
2175 { "lcedil", "ļ" },
2176 { "Lmidot", "Ŀ" },
2177 { "lmidot", "ŀ" },
2178 { "Lstrok", "Ł" },
2179 { "lstrok", "ł" },
2180 { "Nacute", "Ń" },
2181 { "nacute", "ń" },
2182 { "napos", "ʼn" },
2183 { "Ncaron", "Ň" },
2184 { "ncaron", "ň" },
2185 { "Ncedil", "Ņ" },
2186 { "ncedil", "ņ" },
2187 { "Odblac", "Ő" },
2188 { "odblac", "ő" },
2189 { "OElig", "Œ" },
2190 { "oelig", "œ" },
2191 { "Omacr", "Ō" },
2192 { "omacr", "ō" },
2193 { "Racute", "Ŕ" },
2194 { "racute", "ŕ" },
2195 { "Rcaron", "Ř" },
2196 { "rcaron", "ř" },
2197 { "Rcedil", "Ŗ" },
2198 { "rcedil", "ŗ" },
2199 { "Sacute", "Ś" },
2200 { "sacute", "ś" },
2201 { "Scaron", "Š" },
2202 { "scaron", "š" },
2203 { "Scedil", "Ş" },
2204 { "scedil", "ş" },
2205 { "Scirc", "Ŝ" },
2206 { "scirc", "ŝ" },
2207 { "Tcaron", "Ť" },
2208 { "tcaron", "ť" },
2209 { "Tcedil", "Ţ" },
2210 { "tcedil", "ţ" },
2211 { "Tstrok", "Ŧ" },
2212 { "tstrok", "ŧ" },
2213 { "Ubreve", "Ŭ" },
2214 { "ubreve", "ŭ" },
2215 { "Udblac", "Ű" },
2216 { "udblac", "ű" },
2217 { "Umacr", "Ū" },
2218 { "umacr", "ū" },
2219 { "Uogon", "Ų" },
2220 { "uogon", "ų" },
2221 { "Uring", "Ů" },
2222 { "uring", "ů" },
2223 { "Utilde", "Ũ" },
2224 { "utilde", "ũ" },
2225 { "Wcirc", "Ŵ" },
2226 { "wcirc", "ŵ" },
2227 { "Ycirc", "Ŷ" },
2228 { "ycirc", "ŷ" },
2229 { "Yuml", "Ÿ" },
2230 { "Zacute", "Ź" },
2231 { "zacute", "ź" },
2232 { "Zcaron", "Ž" },
2233 { "zcaron", "ž" },
2234 { "Zdot", "Ż" },
2235 { "zdot", "ż" },
2236 { "apos", "'" },
2237 { "ast", "*" },
2238 { "brvbar", "¦" },
2239 { "bsol", "\" },
2240 { "cent", "¢" },
2241 { "colon", ":" },
2242 { "comma", "," },
2243 { "commat", "@" },
2244 { "copy", "©" },
2245 { "curren", "¤" },
2246 { "darr", "↓" },
2247 { "deg", "°" },
2248 { "divide", "÷" },
2249 { "dollar", "$" },
2250 { "equals", "=" },
2251 { "excl", "!" },
2252 { "frac12", "½" },
2253 { "frac14", "¼" },
2254 { "frac18", "⅛" },
2255 { "frac34", "¾" },
2256 { "frac38", "⅜" },
2257 { "frac58", "⅝" },
2258 { "frac78", "⅞" },
2259 { "gt", ">" },
2260 { "half", "½" },
2261 { "horbar", "―" },
2262 { "hyphen", "‐" },
2263 { "iexcl", "¡" },
2264 { "iquest", "¿" },
2265 { "laquo", "«" },
2266 { "larr", "←" },
2267 { "lcub", "{" },
2268 { "ldquo", "“" },
2269 { "lowbar", "_" },
2270 { "lpar", "(" },
2271 { "lsqb", "[" },
2272 { "lsquo", "‘" },
2273 { "lt", "<" },
2274 { "micro", "µ" },
2275 { "middot", "·" },
2276 { "nbsp", " " },
2277 { "not", "¬" },
2278 { "num", "#" },
2279 { "ohm", "Ω" },
2280 { "ordf", "ª" },
2281 { "ordm", "º" },
2282 { "para", "¶" },
2283 { "percnt", "%" },
2284 { "period", "." },
2285 { "plus", "+" },
2286 { "plusmn", "±" },
2287 { "pound", "£" },
2288 { "quest", "?" },
2289 { "quot", """ },
2290 { "raquo", "»" },
2291 { "rarr", "→" },
2292 { "rcub", "}" },
2293 { "rdquo", "”" },
2294 { "reg", "®" },
2295 { "rpar", ")" },
2296 { "rsqb", "]" },
2297 { "rsquo", "’" },
2298 { "sect", "§" },
2299 { "semi", ";" },
2300 { "shy", "­" },
2301 { "sol", "/" },
2302 { "sung", "♪" },
2303 { "sup1", "¹" },
2304 { "sup2", "²" },
2305 { "sup3", "³" },
2306 { "times", "×" },
2307 { "trade", "™" },
2308 { "uarr", "↑" },
2309 { "verbar", "|" },
2310 { "yen", "¥" },
2311 { "blank", "␣" },
2312 { "blk12", "▒" },
2313 { "blk14", "░" },
2314 { "blk34", "▓" },
2315 { "block", "█" },
2316 { "bull", "•" },
2317 { "caret", "⁁" },
2318 { "check", "✓" },
2319 { "cir", "○" },
2320 { "clubs", "♣" },
2321 { "copysr", "℗" },
2322 { "cross", "✗" },
2323 { "Dagger", "‡" },
2324 { "dagger", "†" },
2325 { "dash", "‐" },
2326 { "diams", "♦" },
2327 { "dlcrop", "⌍" },
2328 { "drcrop", "⌌" },
2329 { "dtri", "▿" },
2330 { "dtrif", "▾" },
2331 { "emsp", " " },
2332 { "emsp13", " " },
2333 { "emsp14", " " },
2334 { "ensp", " " },
2335 { "female", "♀" },
2336 { "ffilig", "ffi" },
2337 { "fflig", "ff" },
2338 { "ffllig", "ffl" },
2339 { "filig", "fi" },
2340 { "flat", "♭" },
2341 { "fllig", "fl" },
2342 { "frac13", "⅓" },
2343 { "frac15", "⅕" },
2344 { "frac16", "⅙" },
2345 { "frac23", "⅔" },
2346 { "frac25", "⅖" },
2347 { "frac35", "⅗" },
2348 { "frac45", "⅘" },
2349 { "frac56", "⅚" },
2350 { "hairsp", " " },
2351 { "hearts", "♥" },
2352 { "hellip", "…" },
2353 { "hybull", "⁃" },
2354 { "incare", "℅" },
2355 { "ldquor", "„" },
2356 { "lhblk", "▄" },
2357 { "loz", "◊" },
2358 { "lozf", "⧫" },
2359 { "lsquor", "‚" },
2360 { "ltri", "◃" },
2361 { "ltrif", "◂" },
2362 { "male", "♂" },
2363 { "malt", "✠" },
2364 { "marker", "▮" },
2365 { "mdash", "—" },
2366 { "mldr", "…" },
2367 { "natur", "♮" },
2368 { "ndash", "–" },
2369 { "nldr", "‥" },
2370 { "numsp", " " },
2371 { "phone", "☎" },
2372 { "puncsp", " " },
2373 { "rdquor", "”" },
2374 { "rect", "▭" },
2375 { "rsquor", "’" },
2376 { "rtri", "▹" },
2377 { "rtrif", "▸" },
2378 { "rx", "℞" },
2379 { "sext", "✶" },
2380 { "sharp", "♯" },
2381 { "spades", "♠" },
2382 { "squ", "□" },
2383 { "squf", "▪" },
2384 { "star", "☆" },
2385 { "starf", "★" },
2386 { "target", "⌖" },
2387 { "telrec", "⌕" },
2388 { "thinsp", " " },
2389 { "uhblk", "▀" },
2390 { "ulcrop", "⌏" },
2391 { "urcrop", "⌎" },
2392 { "utri", "▵" },
2393 { "utrif", "▴" },
2394 { "vellip", "⋮" },
2395 { "af", "⁡" },
2396 { "asympeq", "≍" },
2397 { "Cross", "⨯" },
2398 { "DD", "ⅅ" },
2399 { "dd", "ⅆ" },
2400 { "DownArrowBar", "⤓" },
2401 { "DownBreve", "̑" },
2402 { "DownLeftRightVector", "⥐" },
2403 { "DownLeftTeeVector", "⥞" },
2404 { "DownLeftVectorBar", "⥖" },
2405 { "DownRightTeeVector", "⥟" },
2406 { "DownRightVectorBar", "⥗" },
2407 { "ee", "ⅇ" },
2408 { "EmptySmallSquare", "◻" },
2409 { "EmptyVerySmallSquare", "▫" },
2410 { "Equal", "⩵" },
2411 { "FilledSmallSquare", "◼" },
2412 { "FilledVerySmallSquare", "▪" },
2413 { "GreaterGreater", "⪢" },
2414 { "Hat", "^" },
2415 { "HorizontalLine", "─" },
2416 { "ic", "⁣" },
2417 { "ii", "ⅈ" },
2418 { "it", "⁢" },
2419 { "larrb", "⇤" },
2420 { "LeftDownTeeVector", "⥡" },
2421 { "LeftDownVectorBar", "⥙" },
2422 { "LeftRightVector", "⥎" },
2423 { "LeftTeeVector", "⥚" },
2424 { "LeftTriangleBar", "⧏" },
2425 { "LeftUpDownVector", "⥑" },
2426 { "LeftUpTeeVector", "⥠" },
2427 { "LeftUpVectorBar", "⥘" },
2428 { "LeftVectorBar", "⥒" },
2429 { "LessLess", "⪡" },
2430 { "mapstodown", "↧" },
2431 { "mapstoleft", "↤" },
2432 { "mapstoup", "↥" },
2433 { "MediumSpace", " " },
2434 { "nbump", "≎̸" },
2435 { "nbumpe", "≏̸" },
2436 { "nesim", "≂̸" },
2437 { "NewLine", "
" },
2438 { "NoBreak", "⁠" },
2439 { "NotCupCap", "≭" },
2440 { "NotHumpEqual", "≏̸" },
2441 { "NotLeftTriangleBar", "⧏̸" },
2442 { "NotNestedGreaterGreater", "⪢̸" },
2443 { "NotNestedLessLess", "⪡̸" },
2444 { "NotRightTriangleBar", "⧐̸" },
2445 { "NotSquareSubset", "⊏̸" },
2446 { "NotSquareSuperset", "⊐̸" },
2447 { "NotSucceedsTilde", "≿̸" },
2448 { "OverBar", "¯" },
2449 { "OverBrace", "︷" },
2450 { "OverBracket", "⎴" },
2451 { "OverParenthesis", "︵" },
2452 { "planckh", "ℎ" },
2453 { "Product", "∏" },
2454 { "rarrb", "⇥" },
2455 { "RightDownTeeVector", "⥝" },
2456 { "RightDownVectorBar", "⥕" },
2457 { "RightTeeVector", "⥛" },
2458 { "RightTriangleBar", "⧐" },
2459 { "RightUpDownVector", "⥏" },
2460 { "RightUpTeeVector", "⥜" },
2461 { "RightUpVectorBar", "⥔" },
2462 { "RightVectorBar", "⥓" },
2463 { "RoundImplies", "⥰" },
2464 { "RuleDelayed", "⧴" },
2465 { "Tab", "	" },
2466 { "ThickSpace", "   " },
2467 { "UnderBar", "̲" },
2468 { "UnderBrace", "︸" },
2469 { "UnderBracket", "⎵" },
2470 { "UnderParenthesis", "︶" },
2471 { "UpArrowBar", "⤒" },
2472 { "Upsilon", "Υ" },
2473 { "VerticalLine", "|" },
2474 { "VerticalSeparator", "❘" },
2475 { "ZeroWidthSpace", "​" },
2476 { "angle", "∠" },
2477 { "ApplyFunction", "⁡" },
2478 { "approx", "≈" },
2479 { "approxeq", "≊" },
2480 { "Assign", "≔" },
2481 { "backcong", "≌" },
2482 { "backepsilon", "϶" },
2483 { "backprime", "‵" },
2484 { "backsim", "∽" },
2485 { "backsimeq", "⋍" },
2486 { "Backslash", "∖" },
2487 { "barwedge", "⌅" },
2488 { "Because", "∵" },
2489 { "because", "∵" },
2490 { "Bernoullis", "ℬ" },
2491 { "between", "≬" },
2492 { "bigcap", "⋂" },
2493 { "bigcirc", "◯" },
2494 { "bigcup", "⋃" },
2495 { "bigodot", "⨀" },
2496 { "bigoplus", "⨁" },
2497 { "bigotimes", "⨂" },
2498 { "bigsqcup", "⨆" },
2499 { "bigstar", "★" },
2500 { "bigtriangledown", "▽" },
2501 { "bigtriangleup", "△" },
2502 { "biguplus", "⨄" },
2503 { "bigvee", "⋁" },
2504 { "bigwedge", "⋀" },
2505 { "bkarow", "⤍" },
2506 { "blacklozenge", "⧫" },
2507 { "blacksquare", "▪" },
2508 { "blacktriangle", "▴" },
2509 { "blacktriangledown", "▾" },
2510 { "blacktriangleleft", "◂" },
2511 { "blacktriangleright", "▸" },
2512 { "bot", "⊥" },
2513 { "boxminus", "⊟" },
2514 { "boxplus", "⊞" },
2515 { "boxtimes", "⊠" },
2516 { "Breve", "˘" },
2517 { "bullet", "•" },
2518 { "Bumpeq", "≎" },
2519 { "bumpeq", "≏" },
2520 { "CapitalDifferentialD", "ⅅ" },
2521 { "Cayleys", "ℭ" },
2522 { "Cedilla", "¸" },
2523 { "CenterDot", "·" },
2524 { "centerdot", "·" },
2525 { "checkmark", "✓" },
2526 { "circeq", "≗" },
2527 { "circlearrowleft", "↺" },
2528 { "circlearrowright", "↻" },
2529 { "circledast", "⊛" },
2530 { "circledcirc", "⊚" },
2531 { "circleddash", "⊝" },
2532 { "CircleDot", "⊙" },
2533 { "circledR", "®" },
2534 { "circledS", "Ⓢ" },
2535 { "CircleMinus", "⊖" },
2536 { "CirclePlus", "⊕" },
2537 { "CircleTimes", "⊗" },
2538 { "ClockwiseContourIntegral", "∲" },
2539 { "CloseCurlyDoubleQuote", "”" },
2540 { "CloseCurlyQuote", "’" },
2541 { "clubsuit", "♣" },
2542 { "coloneq", "≔" },
2543 { "complement", "∁" },
2544 { "complexes", "ℂ" },
2545 { "Congruent", "≡" },
2546 { "ContourIntegral", "∮" },
2547 { "Coproduct", "∐" },
2548 { "CounterClockwiseContourIntegral", "∳" },
2549 { "CupCap", "≍" },
2550 { "curlyeqprec", "⋞" },
2551 { "curlyeqsucc", "⋟" },
2552 { "curlyvee", "⋎" },
2553 { "curlywedge", "⋏" },
2554 { "curvearrowleft", "↶" },
2555 { "curvearrowright", "↷" },
2556 { "dbkarow", "⤏" },
2557 { "ddagger", "‡" },
2558 { "ddotseq", "⩷" },
2559 { "Del", "∇" },
2560 { "DiacriticalAcute", "´" },
2561 { "DiacriticalDot", "˙" },
2562 { "DiacriticalDoubleAcute", "˝" },
2563 { "DiacriticalGrave", "`" },
2564 { "DiacriticalTilde", "˜" },
2565 { "Diamond", "⋄" },
2566 { "diamond", "⋄" },
2567 { "diamondsuit", "♦" },
2568 { "DifferentialD", "ⅆ" },
2569 { "digamma", "ϝ" },
2570 { "div", "÷" },
2571 { "divideontimes", "⋇" },
2572 { "doteq", "≐" },
2573 { "doteqdot", "≑" },
2574 { "DotEqual", "≐" },
2575 { "dotminus", "∸" },
2576 { "dotplus", "∔" },
2577 { "dotsquare", "⊡" },
2578 { "doublebarwedge", "⌆" },
2579 { "DoubleContourIntegral", "∯" },
2580 { "DoubleDot", "¨" },
2581 { "DoubleDownArrow", "⇓" },
2582 { "DoubleLeftArrow", "⇐" },
2583 { "DoubleLeftRightArrow", "⇔" },
2584 { "DoubleLeftTee", "⫤" },
2585 { "DoubleLongLeftArrow", "⟸" },
2586 { "DoubleLongLeftRightArrow", "⟺" },
2587 { "DoubleLongRightArrow", "⟹" },
2588 { "DoubleRightArrow", "⇒" },
2589 { "DoubleRightTee", "⊨" },
2590 { "DoubleUpArrow", "⇑" },
2591 { "DoubleUpDownArrow", "⇕" },
2592 { "DoubleVerticalBar", "∥" },
2593 { "DownArrow", "↓" },
2594 { "Downarrow", "⇓" },
2595 { "downarrow", "↓" },
2596 { "DownArrowUpArrow", "⇵" },
2597 { "downdownarrows", "⇊" },
2598 { "downharpoonleft", "⇃" },
2599 { "downharpoonright", "⇂" },
2600 { "DownLeftVector", "↽" },
2601 { "DownRightVector", "⇁" },
2602 { "DownTee", "⊤" },
2603 { "DownTeeArrow", "↧" },
2604 { "drbkarow", "⤐" },
2605 { "Element", "∈" },
2606 { "emptyset", "∅" },
2607 { "eqcirc", "≖" },
2608 { "eqcolon", "≕" },
2609 { "eqsim", "≂" },
2610 { "eqslantgtr", "⪖" },
2611 { "eqslantless", "⪕" },
2612 { "EqualTilde", "≂" },
2613 { "Equilibrium", "⇌" },
2614 { "Exists", "∃" },
2615 { "expectation", "ℰ" },
2616 { "ExponentialE", "ⅇ" },
2617 { "exponentiale", "ⅇ" },
2618 { "fallingdotseq", "≒" },
2619 { "ForAll", "∀" },
2620 { "Fouriertrf", "ℱ" },
2621 { "geq", "≥" },
2622 { "geqq", "≧" },
2623 { "geqslant", "⩾" },
2624 { "gg", "≫" },
2625 { "ggg", "⋙" },
2626 { "gnapprox", "⪊" },
2627 { "gneq", "⪈" },
2628 { "gneqq", "≩" },
2629 { "GreaterEqual", "≥" },
2630 { "GreaterEqualLess", "⋛" },
2631 { "GreaterFullEqual", "≧" },
2632 { "GreaterLess", "≷" },
2633 { "GreaterSlantEqual", "⩾" },
2634 { "GreaterTilde", "≳" },
2635 { "gtrapprox", "⪆" },
2636 { "gtrdot", "⋗" },
2637 { "gtreqless", "⋛" },
2638 { "gtreqqless", "⪌" },
2639 { "gtrless", "≷" },
2640 { "gtrsim", "≳" },
2641 { "gvertneqq", "≩︀" },
2642 { "Hacek", "ˇ" },
2643 { "hbar", "ℏ" },
2644 { "heartsuit", "♥" },
2645 { "HilbertSpace", "ℋ" },
2646 { "hksearow", "⤥" },
2647 { "hkswarow", "⤦" },
2648 { "hookleftarrow", "↩" },
2649 { "hookrightarrow", "↪" },
2650 { "hslash", "ℏ" },
2651 { "HumpDownHump", "≎" },
2652 { "HumpEqual", "≏" },
2653 { "iiiint", "⨌" },
2654 { "iiint", "∭" },
2655 { "Im", "ℑ" },
2656 { "ImaginaryI", "ⅈ" },
2657 { "imagline", "ℐ" },
2658 { "imagpart", "ℑ" },
2659 { "Implies", "⇒" },
2660 { "in", "∈" },
2661 { "integers", "ℤ" },
2662 { "Integral", "∫" },
2663 { "intercal", "⊺" },
2664 { "Intersection", "⋂" },
2665 { "intprod", "⨼" },
2666 { "InvisibleComma", "⁣" },
2667 { "InvisibleTimes", "⁢" },
2668 { "langle", "〈" },
2669 { "Laplacetrf", "ℒ" },
2670 { "lbrace", "{" },
2671 { "lbrack", "[" },
2672 { "LeftAngleBracket", "〈" },
2673 { "LeftArrow", "←" },
2674 { "Leftarrow", "⇐" },
2675 { "leftarrow", "←" },
2676 { "LeftArrowBar", "⇤" },
2677 { "LeftArrowRightArrow", "⇆" },
2678 { "leftarrowtail", "↢" },
2679 { "LeftCeiling", "⌈" },
2680 { "LeftDoubleBracket", "〚" },
2681 { "LeftDownVector", "⇃" },
2682 { "LeftFloor", "⌊" },
2683 { "leftharpoondown", "↽" },
2684 { "leftharpoonup", "↼" },
2685 { "leftleftarrows", "⇇" },
2686 { "LeftRightArrow", "↔" },
2687 { "Leftrightarrow", "⇔" },
2688 { "leftrightarrow", "↔" },
2689 { "leftrightarrows", "⇆" },
2690 { "leftrightharpoons", "⇋" },
2691 { "leftrightsquigarrow", "↭" },
2692 { "LeftTee", "⊣" },
2693 { "LeftTeeArrow", "↤" },
2694 { "leftthreetimes", "⋋" },
2695 { "LeftTriangle", "⊲" },
2696 { "LeftTriangleEqual", "⊴" },
2697 { "LeftUpVector", "↿" },
2698 { "LeftVector", "↼" },
2699 { "leq", "≤" },
2700 { "leqq", "≦" },
2701 { "leqslant", "⩽" },
2702 { "lessapprox", "⪅" },
2703 { "lessdot", "⋖" },
2704 { "lesseqgtr", "⋚" },
2705 { "lesseqqgtr", "⪋" },
2706 { "LessEqualGreater", "⋚" },
2707 { "LessFullEqual", "≦" },
2708 { "LessGreater", "≶" },
2709 { "lessgtr", "≶" },
2710 { "lesssim", "≲" },
2711 { "LessSlantEqual", "⩽" },
2712 { "LessTilde", "≲" },
2713 { "ll", "≪" },
2714 { "llcorner", "⌞" },
2715 { "Lleftarrow", "⇚" },
2716 { "lmoustache", "⎰" },
2717 { "lnapprox", "⪉" },
2718 { "lneq", "⪇" },
2719 { "lneqq", "≨" },
2720 { "LongLeftArrow", "⟵" },
2721 { "Longleftarrow", "⟸" },
2722 { "longleftarrow", "⟵" },
2723 { "LongLeftRightArrow", "⟷" },
2724 { "Longleftrightarrow", "⟺" },
2725 { "longleftrightarrow", "⟷" },
2726 { "longmapsto", "⟼" },
2727 { "LongRightArrow", "⟶" },
2728 { "Longrightarrow", "⟹" },
2729 { "longrightarrow", "⟶" },
2730 { "looparrowleft", "↫" },
2731 { "looparrowright", "↬" },
2732 { "LowerLeftArrow", "↙" },
2733 { "LowerRightArrow", "↘" },
2734 { "lozenge", "◊" },
2735 { "lrcorner", "⌟" },
2736 { "Lsh", "↰" },
2737 { "lvertneqq", "≨︀" },
2738 { "maltese", "✠" },
2739 { "mapsto", "↦" },
2740 { "measuredangle", "∡" },
2741 { "Mellintrf", "ℳ" },
2742 { "MinusPlus", "∓" },
2743 { "mp", "∓" },
2744 { "multimap", "⊸" },
2745 { "napprox", "≉" },
2746 { "natural", "♮" },
2747 { "naturals", "ℕ" },
2748 { "nearrow", "↗" },
2749 { "NegativeMediumSpace", "​" },
2750 { "NegativeThickSpace", "​" },
2751 { "NegativeThinSpace", "​" },
2752 { "NegativeVeryThinSpace", "​" },
2753 { "NestedGreaterGreater", "≫" },
2754 { "NestedLessLess", "≪" },
2755 { "nexists", "∄" },
2756 { "ngeq", "≱" },
2757 { "ngeqq", "≧̸" },
2758 { "ngeqslant", "⩾̸" },
2759 { "ngtr", "≯" },
2760 { "nLeftarrow", "⇍" },
2761 { "nleftarrow", "↚" },
2762 { "nLeftrightarrow", "⇎" },
2763 { "nleftrightarrow", "↮" },
2764 { "nleq", "≰" },
2765 { "nleqq", "≦̸" },
2766 { "nleqslant", "⩽̸" },
2767 { "nless", "≮" },
2768 { "NonBreakingSpace", " " },
2769 { "NotCongruent", "≢" },
2770 { "NotDoubleVerticalBar", "∦" },
2771 { "NotElement", "∉" },
2772 { "NotEqual", "≠" },
2773 { "NotEqualTilde", "≂̸" },
2774 { "NotExists", "∄" },
2775 { "NotGreater", "≯" },
2776 { "NotGreaterEqual", "≱" },
2777 { "NotGreaterFullEqual", "≦̸" },
2778 { "NotGreaterGreater", "≫̸" },
2779 { "NotGreaterLess", "≹" },
2780 { "NotGreaterSlantEqual", "⩾̸" },
2781 { "NotGreaterTilde", "≵" },
2782 { "NotHumpDownHump", "≎̸" },
2783 { "NotLeftTriangle", "⋪" },
2784 { "NotLeftTriangleEqual", "⋬" },
2785 { "NotLess", "≮" },
2786 { "NotLessEqual", "≰" },
2787 { "NotLessGreater", "≸" },
2788 { "NotLessLess", "≪̸" },
2789 { "NotLessSlantEqual", "⩽̸" },
2790 { "NotLessTilde", "≴" },
2791 { "NotPrecedes", "⊀" },
2792 { "NotPrecedesEqual", "⪯̸" },
2793 { "NotPrecedesSlantEqual", "⋠" },
2794 { "NotReverseElement", "∌" },
2795 { "NotRightTriangle", "⋫" },
2796 { "NotRightTriangleEqual", "⋭" },
2797 { "NotSquareSubsetEqual", "⋢" },
2798 { "NotSquareSupersetEqual", "⋣" },
2799 { "NotSubset", "⊂⃒" },
2800 { "NotSubsetEqual", "⊈" },
2801 { "NotSucceeds", "⊁" },
2802 { "NotSucceedsEqual", "⪰̸" },
2803 { "NotSucceedsSlantEqual", "⋡" },
2804 { "NotSuperset", "⊃⃒" },
2805 { "NotSupersetEqual", "⊉" },
2806 { "NotTilde", "≁" },
2807 { "NotTildeEqual", "≄" },
2808 { "NotTildeFullEqual", "≇" },
2809 { "NotTildeTilde", "≉" },
2810 { "NotVerticalBar", "∤" },
2811 { "nparallel", "∦" },
2812 { "nprec", "⊀" },
2813 { "npreceq", "⪯̸" },
2814 { "nRightarrow", "⇏" },
2815 { "nrightarrow", "↛" },
2816 { "nshortmid", "∤" },
2817 { "nshortparallel", "∦" },
2818 { "nsimeq", "≄" },
2819 { "nsubset", "⊂⃒" },
2820 { "nsubseteq", "⊈" },
2821 { "nsubseteqq", "⫅̸" },
2822 { "nsucc", "⊁" },
2823 { "nsucceq", "⪰̸" },
2824 { "nsupset", "⊃⃒" },
2825 { "nsupseteq", "⊉" },
2826 { "nsupseteqq", "⫆̸" },
2827 { "ntriangleleft", "⋪" },
2828 { "ntrianglelefteq", "⋬" },
2829 { "ntriangleright", "⋫" },
2830 { "ntrianglerighteq", "⋭" },
2831 { "nwarrow", "↖" },
2832 { "oint", "∮" },
2833 { "OpenCurlyDoubleQuote", "“" },
2834 { "OpenCurlyQuote", "‘" },
2835 { "orderof", "ℴ" },
2836 { "parallel", "∥" },
2837 { "PartialD", "∂" },
2838 { "pitchfork", "⋔" },
2839 { "PlusMinus", "±" },
2840 { "pm", "±" },
2841 { "Poincareplane", "ℌ" },
2842 { "prec", "≺" },
2843 { "precapprox", "⪷" },
2844 { "preccurlyeq", "≼" },
2845 { "Precedes", "≺" },
2846 { "PrecedesEqual", "⪯" },
2847 { "PrecedesSlantEqual", "≼" },
2848 { "PrecedesTilde", "≾" },
2849 { "preceq", "⪯" },
2850 { "precnapprox", "⪹" },
2851 { "precneqq", "⪵" },
2852 { "precnsim", "⋨" },
2853 { "precsim", "≾" },
2854 { "primes", "ℙ" },
2855 { "Proportion", "∷" },
2856 { "Proportional", "∝" },
2857 { "propto", "∝" },
2858 { "quaternions", "ℍ" },
2859 { "questeq", "≟" },
2860 { "rangle", "〉" },
2861 { "rationals", "ℚ" },
2862 { "rbrace", "}" },
2863 { "rbrack", "]" },
2864 { "Re", "ℜ" },
2865 { "realine", "ℛ" },
2866 { "realpart", "ℜ" },
2867 { "reals", "ℝ" },
2868 { "ReverseElement", "∋" },
2869 { "ReverseEquilibrium", "⇋" },
2870 { "ReverseUpEquilibrium", "⥯" },
2871 { "RightAngleBracket", "〉" },
2872 { "RightArrow", "→" },
2873 { "Rightarrow", "⇒" },
2874 { "rightarrow", "→" },
2875 { "RightArrowBar", "⇥" },
2876 { "RightArrowLeftArrow", "⇄" },
2877 { "rightarrowtail", "↣" },
2878 { "RightCeiling", "⌉" },
2879 { "RightDoubleBracket", "〛" },
2880 { "RightDownVector", "⇂" },
2881 { "RightFloor", "⌋" },
2882 { "rightharpoondown", "⇁" },
2883 { "rightharpoonup", "⇀" },
2884 { "rightleftarrows", "⇄" },
2885 { "rightleftharpoons", "⇌" },
2886 { "rightrightarrows", "⇉" },
2887 { "rightsquigarrow", "↝" },
2888 { "RightTee", "⊢" },
2889 { "RightTeeArrow", "↦" },
2890 { "rightthreetimes", "⋌" },
2891 { "RightTriangle", "⊳" },
2892 { "RightTriangleEqual", "⊵" },
2893 { "RightUpVector", "↾" },
2894 { "RightVector", "⇀" },
2895 { "risingdotseq", "≓" },
2896 { "rmoustache", "⎱" },
2897 { "Rrightarrow", "⇛" },
2898 { "Rsh", "↱" },
2899 { "searrow", "↘" },
2900 { "setminus", "∖" },
2901 { "ShortDownArrow", "↓" },
2902 { "ShortLeftArrow", "←" },
2903 { "shortmid", "∣" },
2904 { "shortparallel", "∥" },
2905 { "ShortRightArrow", "→" },
2906 { "ShortUpArrow", "↑" },
2907 { "simeq", "≃" },
2908 { "SmallCircle", "∘" },
2909 { "smallsetminus", "∖" },
2910 { "spadesuit", "♠" },
2911 { "Sqrt", "√" },
2912 { "sqsubset", "⊏" },
2913 { "sqsubseteq", "⊑" },
2914 { "sqsupset", "⊐" },
2915 { "sqsupseteq", "⊒" },
2916 { "Square", "□" },
2917 { "SquareIntersection", "⊓" },
2918 { "SquareSubset", "⊏" },
2919 { "SquareSubsetEqual", "⊑" },
2920 { "SquareSuperset", "⊐" },
2921 { "SquareSupersetEqual", "⊒" },
2922 { "SquareUnion", "⊔" },
2923 { "Star", "⋆" },
2924 { "straightepsilon", "ε" },
2925 { "straightphi", "ϕ" },
2926 { "Subset", "⋐" },
2927 { "subset", "⊂" },
2928 { "subseteq", "⊆" },
2929 { "subseteqq", "⫅" },
2930 { "SubsetEqual", "⊆" },
2931 { "subsetneq", "⊊" },
2932 { "subsetneqq", "⫋" },
2933 { "succ", "≻" },
2934 { "succapprox", "⪸" },
2935 { "succcurlyeq", "≽" },
2936 { "Succeeds", "≻" },
2937 { "SucceedsEqual", "⪰" },
2938 { "SucceedsSlantEqual", "≽" },
2939 { "SucceedsTilde", "≿" },
2940 { "succeq", "⪰" },
2941 { "succnapprox", "⪺" },
2942 { "succneqq", "⪶" },
2943 { "succnsim", "⋩" },
2944 { "succsim", "≿" },
2945 { "SuchThat", "∋" },
2946 { "Sum", "∑" },
2947 { "Superset", "⊃" },
2948 { "SupersetEqual", "⊇" },
2949 { "Supset", "⋑" },
2950 { "supset", "⊃" },
2951 { "supseteq", "⊇" },
2952 { "supseteqq", "⫆" },
2953 { "supsetneq", "⊋" },
2954 { "supsetneqq", "⫌" },
2955 { "swarrow", "↙" },
2956 { "Therefore", "∴" },
2957 { "therefore", "∴" },
2958 { "thickapprox", "≈" },
2959 { "thicksim", "∼" },
2960 { "ThinSpace", " " },
2961 { "Tilde", "∼" },
2962 { "TildeEqual", "≃" },
2963 { "TildeFullEqual", "≅" },
2964 { "TildeTilde", "≈" },
2965 { "toea", "⤨" },
2966 { "tosa", "⤩" },
2967 { "triangle", "▵" },
2968 { "triangledown", "▿" },
2969 { "triangleleft", "◃" },
2970 { "trianglelefteq", "⊴" },
2971 { "triangleq", "≜" },
2972 { "triangleright", "▹" },
2973 { "trianglerighteq", "⊵" },
2974 { "TripleDot", "⃛" },
2975 { "twoheadleftarrow", "↞" },
2976 { "twoheadrightarrow", "↠" },
2977 { "ulcorner", "⌜" },
2978 { "Union", "⋃" },
2979 { "UnionPlus", "⊎" },
2980 { "UpArrow", "↑" },
2981 { "Uparrow", "⇑" },
2982 { "uparrow", "↑" },
2983 { "UpArrowDownArrow", "⇅" },
2984 { "UpDownArrow", "↕" },
2985 { "Updownarrow", "⇕" },
2986 { "updownarrow", "↕" },
2987 { "UpEquilibrium", "⥮" },
2988 { "upharpoonleft", "↿" },
2989 { "upharpoonright", "↾" },
2990 { "UpperLeftArrow", "↖" },
2991 { "UpperRightArrow", "↗" },
2992 { "upsilon", "υ" },
2993 { "UpTee", "⊥" },
2994 { "UpTeeArrow", "↥" },
2995 { "upuparrows", "⇈" },
2996 { "urcorner", "⌝" },
2997 { "varepsilon", "ɛ" },
2998 { "varkappa", "ϰ" },
2999 { "varnothing", "∅" },
3000 { "varphi", "φ" },
3001 { "varpi", "ϖ" },
3002 { "varpropto", "∝" },
3003 { "varrho", "ϱ" },
3004 { "varsigma", "ς" },
3005 { "varsubsetneq", "⊊︀" },
3006 { "varsubsetneqq", "⫋︀" },
3007 { "varsupsetneq", "⊋︀" },
3008 { "varsupsetneqq", "⫌︀" },
3009 { "vartheta", "ϑ" },
3010 { "vartriangleleft", "⊲" },
3011 { "vartriangleright", "⊳" },
3012 { "Vee", "⋁" },
3013 { "vee", "∨" },
3014 { "Vert", "‖" },
3015 { "vert", "|" },
3016 { "VerticalBar", "∣" },
3017 { "VerticalTilde", "≀" },
3018 { "VeryThinSpace", " " },
3019 { "Wedge", "⋀" },
3020 { "wedge", "∧" },
3021 { "wp", "℘" },
3022 { "wr", "≀" },
3023 { "zeetrf", "ℨ" },
3024 { 0, 0 }
3025 };
3026
3027 // *******************************************************************
3028 // MmlDocument
3029 // *******************************************************************
3030
fontName(QtMmlWidget::MmlFont type) const3031 QString MmlDocument::fontName(QtMmlWidget::MmlFont type) const
3032 {
3033 switch (type) {
3034 case QtMmlWidget::NormalFont:
3035 return m_normal_font_name;
3036 case QtMmlWidget::FrakturFont:
3037 return m_fraktur_font_name;
3038 case QtMmlWidget::SansSerifFont:
3039 return m_sans_serif_font_name;
3040 case QtMmlWidget::ScriptFont:
3041 return m_script_font_name;
3042 case QtMmlWidget::MonospaceFont:
3043 return m_monospace_font_name;
3044 case QtMmlWidget::DoublestruckFont:
3045 return m_doublestruck_font_name;
3046 };
3047
3048 return QString();
3049 }
3050
setFontName(QtMmlWidget::MmlFont type,const QString & name)3051 void MmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name)
3052 {
3053 switch (type) {
3054 case QtMmlWidget::NormalFont:
3055 m_normal_font_name = name;
3056 break;
3057 case QtMmlWidget::FrakturFont:
3058 m_fraktur_font_name = name;
3059 break;
3060 case QtMmlWidget::SansSerifFont:
3061 m_sans_serif_font_name = name;
3062 break;
3063 case QtMmlWidget::ScriptFont:
3064 m_script_font_name = name;
3065 break;
3066 case QtMmlWidget::MonospaceFont:
3067 m_monospace_font_name = name;
3068 break;
3069 case QtMmlWidget::DoublestruckFont:
3070 m_doublestruck_font_name = name;
3071 break;
3072 };
3073 }
3074
domToMmlNodeType(const QDomNode & dom_node)3075 Mml::NodeType domToMmlNodeType(const QDomNode &dom_node)
3076 {
3077 Mml::NodeType mml_type = Mml::NoNode;
3078
3079 switch (dom_node.nodeType()) {
3080 case QDomNode::ElementNode: {
3081 QString tag = dom_node.nodeName();
3082 const NodeSpec *spec = mmlFindNodeSpec(tag);
3083
3084 // treat urecognised tags as mrow
3085 if (spec == 0)
3086 mml_type = Mml::UnknownNode;
3087 else
3088 mml_type = spec->type;
3089
3090 break;
3091 }
3092 case QDomNode::TextNode:
3093 mml_type = Mml::TextNode;
3094 break;
3095
3096 case QDomNode::DocumentNode:
3097 mml_type = Mml::MrowNode;
3098 break;
3099
3100 case QDomNode::EntityReferenceNode:
3101 // qWarning("EntityReferenceNode: name=\"" + dom_node.nodeName() + "\" value=\"" + dom_node.nodeValue() + "\"");
3102 break;
3103
3104 case QDomNode::AttributeNode:
3105 case QDomNode::CDATASectionNode:
3106 case QDomNode::EntityNode:
3107 case QDomNode::ProcessingInstructionNode:
3108 case QDomNode::CommentNode:
3109 case QDomNode::DocumentTypeNode:
3110 case QDomNode::DocumentFragmentNode:
3111 case QDomNode::NotationNode:
3112 case QDomNode::BaseNode:
3113 case QDomNode::CharacterDataNode:
3114 break;
3115 }
3116
3117 return mml_type;
3118 }
3119
3120
MmlDocument()3121 MmlDocument::MmlDocument()
3122 {
3123 m_root_node = 0;
3124
3125 // Some defaults which happen to work on my computer,
3126 // but probably won't work on other's
3127 #if defined(Q_WS_WIN)
3128 m_normal_font_name = "Times New Roman";
3129 #else
3130 m_normal_font_name = "Century Schoolbook L";
3131 #endif
3132 m_fraktur_font_name = "Fraktur";
3133 m_sans_serif_font_name = "Luxi Sans";
3134 m_script_font_name = "Urw Chancery L";
3135 m_monospace_font_name = "Luxi Mono";
3136 m_doublestruck_font_name = "Doublestruck";
3137 m_base_font_point_size = 16;
3138 m_foreground_color = Qt::black;
3139 m_background_color = Qt::white;
3140 }
3141
~MmlDocument()3142 MmlDocument::~MmlDocument()
3143 {
3144 clear();
3145 }
3146
clear()3147 void MmlDocument::clear()
3148 {
3149 delete m_root_node;
3150 m_root_node = 0;
3151 }
3152
dump() const3153 void MmlDocument::dump() const
3154 {
3155 if (m_root_node == 0)
3156 return;
3157
3158 QString indent;
3159 _dump(m_root_node, indent);
3160 }
3161
_dump(const MmlNode * node,QString & indent) const3162 void MmlDocument::_dump(const MmlNode *node, QString &indent) const
3163 {
3164 if (node == 0) return;
3165
3166 qWarning("%s", (indent + node->toStr()).toLatin1().data());
3167
3168 indent += " ";
3169 const MmlNode *child = node->firstChild();
3170 for (; child != 0; child = child->nextSibling())
3171 _dump(child, indent);
3172 indent.truncate(indent.length() - 2);
3173 }
3174
setContent(QString text,QString * errorMsg,int * errorLine,int * errorColumn)3175 bool MmlDocument::setContent(QString text, QString *errorMsg,
3176 int *errorLine, int *errorColumn)
3177 {
3178 clear();
3179
3180 QString prefix = "<?xml version=\"2.0\"?>\n";
3181 prefix.append(entityDeclarations());
3182
3183 uint prefix_lines = 0;
3184 for (int i = 0; i < prefix.length(); ++i) {
3185 if (prefix.at(i) == '\n')
3186 ++prefix_lines;
3187 }
3188
3189 QDomDocument dom;
3190 if (!dom.setContent(prefix + text, false, errorMsg, errorLine, errorColumn)) {
3191 if (errorLine != 0)
3192 *errorLine -= prefix_lines;
3193 return false;
3194 }
3195
3196 // we don't have access to line info from now on
3197 if (errorLine != 0) *errorLine = -1;
3198 if (errorColumn != 0) *errorColumn = -1;
3199
3200 bool ok;
3201 MmlNode *root_node = domToMml(dom, &ok, errorMsg);
3202 if (!ok)
3203 return false;
3204
3205 if (root_node == 0) {
3206 if (errorMsg != 0)
3207 *errorMsg = "empty document";
3208 return false;
3209 }
3210
3211 insertChild(0, root_node, 0);
3212 layout();
3213
3214 /* QFile of("/tmp/dump.xml");
3215 of.open(IO_WriteOnly);
3216 QTextStream os(&of);
3217 os.setEncoding(QTextStream::UnicodeUTF8);
3218 os << dom.toString(); */
3219
3220 return true;
3221 }
3222
layout()3223 void MmlDocument::layout()
3224 {
3225 if (m_root_node == 0)
3226 return;
3227
3228 m_root_node->layout();
3229 m_root_node->stretch();
3230 // dump();
3231 }
3232
insertChild(MmlNode * parent,MmlNode * new_node,QString * errorMsg)3233 bool MmlDocument::insertChild(MmlNode *parent, MmlNode *new_node,
3234 QString *errorMsg)
3235 {
3236 if (new_node == 0)
3237 return true;
3238
3239 Q_ASSERT(new_node->parent() == 0
3240 && new_node->nextSibling() == 0
3241 && new_node->previousSibling() == 0);
3242
3243 if (parent != 0) {
3244 if (!mmlCheckChildType(parent->nodeType(), new_node->nodeType(), errorMsg))
3245 return false;
3246 }
3247
3248 if (parent == 0) {
3249 if (m_root_node == 0)
3250 m_root_node = new_node;
3251 else {
3252 MmlNode *n = m_root_node->lastSibling();
3253 n->m_next_sibling = new_node;
3254 new_node->m_previous_sibling = n;
3255 }
3256 }
3257 else {
3258 new_node->m_parent = parent;
3259 if (parent->hasChildNodes()) {
3260 MmlNode *n = parent->firstChild()->lastSibling();
3261 n->m_next_sibling = new_node;
3262 new_node->m_previous_sibling = n;
3263 }
3264 else parent->m_first_child = new_node;
3265 }
3266
3267 return true;
3268 }
3269
createNode(NodeType type,const MmlAttributeMap & mml_attr,const QString & mml_value,QString * errorMsg)3270 MmlNode *MmlDocument::createNode(NodeType type,
3271 const MmlAttributeMap &mml_attr,
3272 const QString &mml_value,
3273 QString *errorMsg)
3274 {
3275 Q_ASSERT(type != NoNode);
3276
3277 MmlNode *mml_node = 0;
3278
3279 if (!mmlCheckAttributes(type, mml_attr, errorMsg))
3280 return 0;
3281
3282 switch (type) {
3283 case MiNode:
3284 mml_node = new MmlMiNode(this, mml_attr);
3285 break;
3286 case MnNode:
3287 mml_node = new MmlMnNode(this, mml_attr);
3288 break;
3289 case MfracNode:
3290 mml_node = new MmlMfracNode(this, mml_attr);
3291 break;
3292 case MrowNode:
3293 mml_node = new MmlMrowNode(this, mml_attr);
3294 break;
3295 case MsqrtNode:
3296 mml_node = new MmlMsqrtNode(this, mml_attr);
3297 break;
3298 case MrootNode:
3299 mml_node = new MmlMrootNode(this, mml_attr);
3300 break;
3301 case MsupNode:
3302 mml_node = new MmlMsupNode(this, mml_attr);
3303 break;
3304 case MsubNode:
3305 mml_node = new MmlMsubNode(this, mml_attr);
3306 break;
3307 case MsubsupNode:
3308 mml_node = new MmlMsubsupNode(this, mml_attr);
3309 break;
3310 case MoNode:
3311 mml_node = new MmlMoNode(this, mml_attr);
3312 break;
3313 case MstyleNode:
3314 mml_node = new MmlMstyleNode(this, mml_attr);
3315 break;
3316 case TextNode:
3317 mml_node = new MmlTextNode(mml_value, this);
3318 break;
3319 case MphantomNode:
3320 mml_node = new MmlMphantomNode(this, mml_attr);
3321 break;
3322 case MfencedNode:
3323 mml_node = new MmlMfencedNode(this, mml_attr);
3324 break;
3325 case MtableNode:
3326 mml_node = new MmlMtableNode(this, mml_attr);
3327 break;
3328 case MtrNode:
3329 mml_node = new MmlMtrNode(this, mml_attr);
3330 break;
3331 case MtdNode:
3332 mml_node = new MmlMtdNode(this, mml_attr);
3333 break;
3334 case MoverNode:
3335 mml_node = new MmlMoverNode(this, mml_attr);
3336 break;
3337 case MunderNode:
3338 mml_node = new MmlMunderNode(this, mml_attr);
3339 break;
3340 case MunderoverNode:
3341 mml_node = new MmlMunderoverNode(this, mml_attr);
3342 break;
3343 case MalignMarkNode:
3344 mml_node = new MmlMalignMarkNode(this);
3345 break;
3346 case MerrorNode:
3347 mml_node = new MmlMerrorNode(this, mml_attr);
3348 break;
3349 case MtextNode:
3350 mml_node = new MmlMtextNode(this, mml_attr);
3351 break;
3352 case MpaddedNode:
3353 mml_node = new MmlMpaddedNode(this, mml_attr);
3354 break;
3355 case MspaceNode:
3356 mml_node = new MmlMspaceNode(this, mml_attr);
3357 break;
3358 case UnknownNode:
3359 mml_node = new MmlUnknownNode(this, mml_attr);
3360 break;
3361 case NoNode:
3362 mml_node = 0;
3363 break;
3364 }
3365
3366 return mml_node;
3367 }
3368
insertOperator(MmlNode * node,const QString & text)3369 void MmlDocument::insertOperator(MmlNode *node, const QString &text)
3370 {
3371 MmlNode *text_node = createNode(TextNode, MmlAttributeMap(), text, 0);
3372 MmlNode *mo_node = createNode(MoNode, MmlAttributeMap(), QString(), 0);
3373
3374 bool ok = insertChild(node, mo_node, 0);
3375 Q_ASSERT( ok );
3376 ok = insertChild(mo_node, text_node, 0);
3377 Q_ASSERT( ok );
3378 }
3379
domToMml(const QDomNode & dom_node,bool * ok,QString * errorMsg)3380 MmlNode *MmlDocument::domToMml(const QDomNode &dom_node, bool *ok, QString *errorMsg)
3381 {
3382 // create the node
3383
3384 Q_ASSERT(ok != 0);
3385
3386 NodeType mml_type = domToMmlNodeType(dom_node);
3387
3388 if (mml_type == NoNode) {
3389 *ok = true;
3390 return 0;
3391 }
3392
3393 QDomNamedNodeMap dom_attr = dom_node.attributes();
3394 MmlAttributeMap mml_attr;
3395 for (int i = 0; i < int(dom_attr.length()); ++i) {
3396 QDomNode attr_node = dom_attr.item(i);
3397 Q_ASSERT(!attr_node.nodeName().isNull());
3398 Q_ASSERT(!attr_node.nodeValue().isNull());
3399 mml_attr[attr_node.nodeName()] = attr_node.nodeValue();
3400 }
3401
3402 QString mml_value;
3403 if (mml_type == TextNode)
3404 mml_value = dom_node.nodeValue();
3405 MmlNode *mml_node = createNode(mml_type, mml_attr, mml_value, errorMsg);
3406 if (mml_node == 0) {
3407 *ok = false;
3408 return 0;
3409 }
3410
3411 // create the node's children according to the child_spec
3412
3413 const NodeSpec *spec = mmlFindNodeSpec(mml_type);
3414 QDomNodeList dom_child_list = dom_node.childNodes();
3415 int child_cnt = dom_child_list.count();
3416 MmlNode *mml_child = 0;
3417
3418 QString separator_list;
3419 if (mml_type == MfencedNode)
3420 separator_list = mml_node->explicitAttribute("separators", ",");
3421
3422 switch (spec->child_spec) {
3423 case NodeSpec::ChildIgnore:
3424 break;
3425
3426 case NodeSpec::ImplicitMrow:
3427
3428 if (child_cnt > 0) {
3429 mml_child = createImplicitMrowNode(dom_node, ok, errorMsg);
3430 if (!*ok) {
3431 delete mml_node;
3432 return 0;
3433 }
3434
3435 if (!insertChild(mml_node, mml_child, errorMsg)) {
3436 delete mml_node;
3437 delete mml_child;
3438 *ok = false;
3439 return 0;
3440 }
3441 }
3442
3443 break;
3444
3445 default:
3446 // exact ammount of children specified - check...
3447 if (spec->child_spec != child_cnt) {
3448 if (errorMsg != 0)
3449 *errorMsg = QString("element ")
3450 + spec->tag
3451 + " requires exactly "
3452 + QString::number(spec->child_spec)
3453 + " arguments, got "
3454 + QString::number(child_cnt);
3455 delete mml_node;
3456 *ok = false;
3457 return 0;
3458 }
3459
3460 // ...and continue just as in ChildAny
3461
3462 case NodeSpec::ChildAny:
3463 if (mml_type == MfencedNode)
3464 insertOperator(mml_node, mml_node->explicitAttribute("open", "("));
3465
3466 for (int i = 0; i < child_cnt; ++i) {
3467 QDomNode dom_child = dom_child_list.item(i);
3468
3469 MmlNode *mml_child = domToMml(dom_child, ok, errorMsg);
3470 if (!*ok) {
3471 delete mml_node;
3472 return 0;
3473 }
3474
3475 if (mml_type == MtableNode && mml_child->nodeType() != MtrNode) {
3476 MmlNode *mtr_node = createNode(MtrNode, MmlAttributeMap(), QString(), 0);
3477 insertChild(mml_node, mtr_node, 0);
3478 if (!insertChild(mtr_node, mml_child, errorMsg)) {
3479 delete mml_node;
3480 delete mml_child;
3481 *ok = false;
3482 return 0;
3483 }
3484 }
3485 else if (mml_type == MtrNode && mml_child->nodeType() != MtdNode) {
3486 MmlNode *mtd_node = createNode(MtdNode, MmlAttributeMap(), QString(), 0);
3487 insertChild(mml_node, mtd_node, 0);
3488 if (!insertChild(mtd_node, mml_child, errorMsg)) {
3489 delete mml_node;
3490 delete mml_child;
3491 *ok = false;
3492 return 0;
3493 }
3494 }
3495 else {
3496 if (!insertChild(mml_node, mml_child, errorMsg)) {
3497 delete mml_node;
3498 delete mml_child;
3499 *ok = false;
3500 return 0;
3501 }
3502 }
3503
3504 if (i < child_cnt - 1 && mml_type == MfencedNode && !separator_list.isEmpty()) {
3505 QChar separator;
3506 if (i >= (int)separator_list.length())
3507 separator = separator_list.at(separator_list.length() - 1);
3508 else
3509 separator = separator_list[i];
3510 insertOperator(mml_node, QString(separator));
3511 }
3512 }
3513
3514 if (mml_type == MfencedNode)
3515 insertOperator(mml_node, mml_node->explicitAttribute("close", ")"));
3516
3517 break;
3518 }
3519
3520 *ok = true;
3521 return mml_node;
3522 }
3523
createImplicitMrowNode(const QDomNode & dom_node,bool * ok,QString * errorMsg)3524 MmlNode *MmlDocument::createImplicitMrowNode(const QDomNode &dom_node, bool *ok,
3525 QString *errorMsg)
3526 {
3527 QDomNodeList dom_child_list = dom_node.childNodes();
3528 int child_cnt = dom_child_list.count();
3529
3530 if (child_cnt == 0) {
3531 *ok = true;
3532 return 0;
3533 }
3534
3535 if (child_cnt == 1)
3536 return domToMml(dom_child_list.item(0), ok, errorMsg);
3537
3538 MmlNode *mml_node = createNode(MrowNode, MmlAttributeMap(),
3539 QString(), errorMsg);
3540 Q_ASSERT(mml_node != 0); // there is no reason in heaven or hell for this to fail
3541
3542 for (int i = 0; i < child_cnt; ++i) {
3543 QDomNode dom_child = dom_child_list.item(i);
3544
3545 MmlNode *mml_child = domToMml(dom_child, ok, errorMsg);
3546 if (!*ok) {
3547 delete mml_node;
3548 return 0;
3549 }
3550
3551 if (!insertChild(mml_node, mml_child, errorMsg)) {
3552 delete mml_node;
3553 delete mml_child;
3554 *ok = false;
3555 return 0;
3556 }
3557 }
3558
3559 return mml_node;
3560 }
3561
paint(QPainter * p,const QPoint & pos) const3562 void MmlDocument::paint(QPainter *p, const QPoint &pos) const
3563 {
3564 if (m_root_node == 0)
3565 return;
3566
3567 /* p->save();
3568 p->setPen(Qt::blue);
3569 p->drawLine(pos.x() - 5, pos.y(), pos.x() + 5, pos.y());
3570 p->drawLine(pos.x(), pos.y() - 5, pos.x(), pos.y() + 5);
3571 p->restore(); */
3572
3573 QRect mr = m_root_node->myRect();
3574 m_root_node->setRelOrigin(pos - mr.topLeft());
3575 m_root_node->paint(p);
3576 }
3577
size() const3578 QSize MmlDocument::size() const
3579 {
3580 if (m_root_node == 0)
3581 return QSize(0, 0);
3582 return m_root_node->deviceRect().size();
3583 }
3584
3585
3586
3587
3588 // *******************************************************************
3589 // MmlNode
3590 // *******************************************************************
3591
3592
MmlNode(NodeType type,MmlDocument * document,const MmlAttributeMap & attribute_map)3593 MmlNode::MmlNode(NodeType type, MmlDocument *document, const MmlAttributeMap &attribute_map)
3594 {
3595 m_parent = 0;
3596 m_first_child = 0;
3597 m_next_sibling = 0;
3598 m_previous_sibling = 0;
3599
3600 m_node_type = type;
3601 m_document = document;
3602 m_attribute_map = attribute_map;
3603
3604 m_my_rect = m_parent_rect = QRect(0, 0, 0, 0);
3605 m_rel_origin = QPoint(0, 0);
3606 m_stretched = false;
3607 }
3608
~MmlNode()3609 MmlNode::~MmlNode()
3610 {
3611 MmlNode *n = firstChild();
3612 while (n != 0) {
3613 MmlNode *tmp = n->nextSibling();
3614 delete n;
3615 n = tmp;
3616 }
3617 }
3618
rectToStr(const QRect & rect)3619 static QString rectToStr(const QRect &rect)
3620 {
3621 return QString("[(%1, %2), %3x%4]")
3622 .arg(rect.x())
3623 .arg(rect.y())
3624 .arg(rect.width())
3625 .arg(rect.height());
3626 }
3627
toStr() const3628 QString MmlNode::toStr() const
3629 {
3630 const NodeSpec *spec = mmlFindNodeSpec(nodeType());
3631 Q_ASSERT(spec != 0);
3632
3633 return QString("%1 %2 mr=%3 pr=%4 dr=%5 ro=(%7, %8) str=%9")
3634 .arg(spec->type_str)
3635 .arg((unsigned long)this, 0, 16)
3636 .arg(rectToStr(myRect()))
3637 .arg(rectToStr(parentRect()))
3638 .arg(rectToStr(deviceRect()))
3639 .arg(m_rel_origin.x())
3640 .arg(m_rel_origin.y())
3641 .arg((int)isStretched());
3642 }
3643
interpretSpacing(const QString & value,bool * ok) const3644 int MmlNode::interpretSpacing(const QString &value, bool *ok) const
3645 {
3646 return ::interpretSpacing(value, em(), ex(), ok);
3647 }
3648
basePos() const3649 int MmlNode::basePos() const
3650 {
3651 QFontMetrics fm(font());
3652 return fm.strikeOutPos();
3653 }
3654
underlinePos() const3655 int MmlNode::underlinePos() const
3656 {
3657 QFontMetrics fm(font());
3658 return basePos() + fm.underlinePos();
3659 }
overlinePos() const3660 int MmlNode::overlinePos() const
3661 {
3662 QFontMetrics fm(font());
3663 return basePos() - fm.overlinePos();
3664 }
3665
lastSibling() const3666 MmlNode *MmlNode::lastSibling() const
3667 {
3668 const MmlNode *n = this;
3669 while (!n->isLastSibling())
3670 n = n->nextSibling();
3671 return const_cast<MmlNode*>(n);
3672 }
3673
firstSibling() const3674 MmlNode *MmlNode::firstSibling() const
3675 {
3676 const MmlNode *n = this;
3677 while (!n->isFirstSibling())
3678 n = n->previousSibling();
3679 return const_cast<MmlNode*>(n);
3680 }
3681
em() const3682 int MmlNode::em() const
3683 {
3684 return QFontMetrics(font()).boundingRect('m').width();
3685 }
3686
ex() const3687 int MmlNode::ex() const
3688 {
3689 return QFontMetrics(font()).boundingRect('x').height();
3690 }
3691
scriptlevel(const MmlNode *) const3692 int MmlNode::scriptlevel(const MmlNode *) const
3693 {
3694 int parent_sl;
3695 const MmlNode *p = parent();
3696 if (p == 0)
3697 parent_sl = 0;
3698 else
3699 parent_sl = p->scriptlevel(this);
3700
3701 QString expl_sl_str = explicitAttribute("scriptlevel");
3702 if (expl_sl_str.isNull())
3703 return parent_sl;
3704
3705 if (expl_sl_str.startsWith("+") || expl_sl_str.startsWith("-")) {
3706 bool ok;
3707 int expl_sl = expl_sl_str.toInt(&ok);
3708 if (ok) {
3709 return parent_sl + expl_sl;
3710 }
3711 else {
3712 qWarning("MmlNode::scriptlevel(): bad value %s", expl_sl_str.toLatin1().data());
3713 return parent_sl;
3714 }
3715 }
3716
3717 bool ok;
3718 int expl_sl = expl_sl_str.toInt(&ok);
3719 if (ok)
3720 return expl_sl;
3721
3722
3723 if (expl_sl_str == "+")
3724 return parent_sl + 1;
3725 else if (expl_sl_str == "-")
3726 return parent_sl - 1;
3727 else {
3728 qWarning("MmlNode::scriptlevel(): could not parse value: \"%s\"", expl_sl_str.toLatin1().data());
3729 return parent_sl;
3730 }
3731 }
3732
devicePoint(const QPoint & p) const3733 QPoint MmlNode::devicePoint(const QPoint &p) const
3734 {
3735 QRect mr = myRect();
3736 QRect dr = deviceRect();
3737
3738 if (isStretched())
3739 return dr.topLeft() + QPoint((p.x() - mr.left())*dr.width()/mr.width(),
3740 (p.y() - mr.top())*dr.height()/mr.height());
3741 else
3742 return dr.topLeft() + p - mr.topLeft();
3743 }
3744
inheritAttributeFromMrow(const QString & name,const QString & def) const3745 QString MmlNode::inheritAttributeFromMrow(const QString &name,
3746 const QString &def) const
3747 {
3748 const MmlNode *p = this;
3749 for (; p != 0; p = p->parent()) {
3750 if (p == this || p->nodeType() == MstyleNode) {
3751 QString value = p->explicitAttribute(name);
3752 if (!value.isNull())
3753 return value;
3754 }
3755 }
3756
3757 return def;
3758 }
3759
color() const3760 QColor MmlNode::color() const
3761 {
3762 // If we are child of <merror> return red
3763 const MmlNode *p = this;
3764 for (; p != 0; p = p->parent()) {
3765 if (p->nodeType() == MerrorNode)
3766 return QColor("red");
3767 }
3768
3769 QString value_str = inheritAttributeFromMrow("mathcolor");
3770 if (value_str.isNull())
3771 value_str = inheritAttributeFromMrow("color");
3772 if (value_str.isNull())
3773 return QColor();
3774
3775 return QColor(value_str);
3776 }
3777
background() const3778 QColor MmlNode::background() const
3779 {
3780 QString value_str = inheritAttributeFromMrow("mathbackground");
3781 if (value_str.isNull())
3782 value_str = inheritAttributeFromMrow("background");
3783 if (value_str.isNull())
3784 return QColor();
3785
3786 return QColor(value_str);
3787 }
3788
updateFontAttr(MmlAttributeMap & font_attr,const MmlNode * n,const QString & name,const QString & preferred_name=QString ())3789 static void updateFontAttr(MmlAttributeMap &font_attr, const MmlNode *n,
3790 const QString &name, const QString &preferred_name = QString())
3791 {
3792 if (font_attr.contains(preferred_name) || font_attr.contains(name))
3793 return;
3794 QString value = n->explicitAttribute(name);
3795 if (!value.isNull())
3796 font_attr[name] = value;
3797 }
3798
collectFontAttributes(const MmlNode * node)3799 static MmlAttributeMap collectFontAttributes(const MmlNode *node)
3800 {
3801 MmlAttributeMap font_attr;
3802
3803 for (const MmlNode *n = node; n != 0; n = n->parent()) {
3804 if (n == node || n->nodeType() == Mml::MstyleNode) {
3805 updateFontAttr(font_attr, n, "mathvariant");
3806 updateFontAttr(font_attr, n, "mathsize");
3807
3808 // depreciated attributes
3809 updateFontAttr(font_attr, n, "fontsize", "mathsize");
3810 updateFontAttr(font_attr, n, "fontweight", "mathvariant");
3811 updateFontAttr(font_attr, n, "fontstyle", "mathvariant");
3812 updateFontAttr(font_attr, n, "fontfamily", "mathvariant");
3813 }
3814 }
3815
3816 return font_attr;
3817 }
3818
font() const3819 QFont MmlNode::font() const
3820 {
3821 QFont fn(document()->fontName(QtMmlWidget::NormalFont),
3822 document()->baseFontPointSize());
3823
3824 int ps = fn.pointSize();
3825 int sl = scriptlevel();
3826 if (sl >= 0) {
3827 for (int i = 0; i < sl; ++i)
3828 ps = (int)(ps*g_script_size_multiplier);
3829 }
3830 else {
3831 for (int i = 0; i > sl; --i)
3832 ps = (int)(ps/g_script_size_multiplier);
3833 }
3834 if (ps < g_min_font_point_size)
3835 ps = g_min_font_point_size;
3836 fn.setPointSize(ps);
3837
3838 int em = QFontMetrics(fn).boundingRect('m').width();
3839 int ex = QFontMetrics(fn).boundingRect('x').height();
3840
3841 MmlAttributeMap font_attr = collectFontAttributes(this);
3842
3843 if (font_attr.contains("mathvariant")) {
3844 QString value = font_attr["mathvariant"];
3845
3846 bool ok;
3847 uint mv = interpretMathVariant(value, &ok);
3848
3849 if (ok) {
3850 if (mv & ScriptMV)
3851 fn.setFamily(document()->fontName(QtMmlWidget::ScriptFont));
3852
3853 if (mv & FrakturMV)
3854 fn.setFamily(document()->fontName(QtMmlWidget::FrakturFont));
3855
3856 if (mv & SansSerifMV)
3857 fn.setFamily(document()->fontName(QtMmlWidget::SansSerifFont));
3858
3859 if (mv & MonospaceMV)
3860 fn.setFamily(document()->fontName(QtMmlWidget::MonospaceFont));
3861
3862 if (mv & DoubleStruckMV)
3863 fn.setFamily(document()->fontName(QtMmlWidget::DoublestruckFont));
3864
3865 if (mv & BoldMV)
3866 fn.setBold(true);
3867
3868 if (mv & ItalicMV)
3869 fn.setItalic(true);
3870 }
3871 }
3872
3873 if (font_attr.contains("mathsize")) {
3874 QString value = font_attr["mathsize"];
3875 fn = interpretMathSize(value, fn, em, ex, 0);
3876 }
3877
3878 fn = interpretDepreciatedFontAttr(font_attr, fn, em, ex);
3879
3880 if (nodeType() == MiNode
3881 && !font_attr.contains("mathvariant")
3882 && !font_attr.contains("fontstyle")) {
3883 const MmlMiNode *mi_node = (const MmlMiNode*) this;
3884 if (mi_node->text().length() == 1)
3885 fn.setItalic(true);
3886 }
3887
3888 if (nodeType() == MoNode) {
3889 fn.setItalic(false);
3890 fn.setBold(false);
3891 }
3892
3893 return fn;
3894 }
3895
explicitAttribute(const QString & name,const QString & def) const3896 QString MmlNode::explicitAttribute(const QString &name, const QString &def) const
3897 {
3898 MmlAttributeMap::const_iterator it = m_attribute_map.find(name);
3899 if (it != m_attribute_map.end())
3900 return *it;
3901 return def;
3902 }
3903
3904
parentRect() const3905 QRect MmlNode::parentRect() const
3906 {
3907 if (isStretched())
3908 return m_parent_rect;
3909
3910 QRect mr = myRect();
3911 QPoint ro = relOrigin();
3912
3913 return QRect(ro + mr.topLeft(), mr.size());
3914 }
3915
3916
stretchTo(const QRect & rect)3917 void MmlNode::stretchTo(const QRect &rect)
3918 {
3919 m_parent_rect = rect;
3920 m_stretched = true;
3921 }
3922
setRelOrigin(const QPoint & rel_origin)3923 void MmlNode::setRelOrigin(const QPoint &rel_origin)
3924 {
3925 m_rel_origin = rel_origin + QPoint(-myRect().left(), 0);
3926 m_stretched = false;
3927 }
3928
updateMyRect()3929 void MmlNode::updateMyRect()
3930 {
3931 m_my_rect = symbolRect();
3932 MmlNode *child = firstChild();
3933 for (; child != 0; child = child->nextSibling())
3934 m_my_rect |= child->parentRect();
3935 }
3936
layout()3937 void MmlNode::layout()
3938 {
3939 m_parent_rect = QRect(0, 0, 0, 0);
3940 m_stretched = false;
3941 m_rel_origin = QPoint(0, 0);
3942
3943 MmlNode *child = firstChild();
3944 for (; child != 0; child = child->nextSibling())
3945 child->layout();
3946
3947 layoutSymbol();
3948
3949 updateMyRect();
3950
3951 if (parent() == 0)
3952 m_rel_origin = QPoint(0, 0);
3953 }
3954
3955
deviceRect() const3956 QRect MmlNode::deviceRect() const
3957 {
3958 if (parent() == 0)
3959 return QRect(relOrigin() + myRect().topLeft(), myRect().size());
3960
3961 /* if (!isStretched()) {
3962 QRect pdr = parent()->deviceRect();
3963 QRect pmr = parent()->myRect();
3964 QRect pr = parentRect();
3965 QRect mr = myRect();
3966 return QRect(pdr.left() + pr.left() - pmr.left(),
3967 pdr.top() + pr.top() - pmr.top(),
3968 mr.width(), mr.height());
3969 }
3970 */
3971 QRect pdr = parent()->deviceRect();
3972 QRect pr = parentRect();
3973 QRect pmr = parent()->myRect();
3974
3975 float scale_w = 0;
3976 if (pmr.width() != 0)
3977 scale_w = (float)pdr.width()/pmr.width();
3978 float scale_h = 0;
3979 if (pmr.height() != 0)
3980 scale_h = (float)pdr.height()/pmr.height();
3981
3982 return QRect(pdr.left() + ROUND((pr.left() - pmr.left())*scale_w),
3983 pdr.top() + ROUND((pr.top() - pmr.top())*scale_h),
3984 ROUND((pr.width()*scale_w)),
3985 ROUND((pr.height()*scale_h)));
3986 }
3987
layoutSymbol()3988 void MmlNode::layoutSymbol()
3989 {
3990 // default behaves like an mrow
3991
3992 // now lay them out in a neat row, aligning their origins to my origin
3993 int w = 0;
3994 MmlNode *child = firstChild();
3995 for (; child != 0; child = child->nextSibling()) {
3996 child->setRelOrigin(QPoint(w, 0));
3997 w += child->parentRect().width() + 1;
3998 }
3999 }
4000
paint(QPainter * p)4001 void MmlNode::paint(QPainter *p)
4002 {
4003 if (!myRect().isValid())
4004 return;
4005 p->save();
4006 p->setViewport(deviceRect());
4007 p->setWindow(myRect());
4008
4009
4010 QColor fg = color();
4011 QColor bg = background();
4012 if (bg.isValid())
4013 p->fillRect(myRect(), bg);
4014 if (fg.isValid())
4015 p->setPen(color());
4016
4017 MmlNode *child = firstChild();
4018 for (; child != 0; child = child->nextSibling())
4019 child->paint(p);
4020
4021 paintSymbol(p);
4022
4023 p->restore();
4024 }
4025
paintSymbol(QPainter * p) const4026 void MmlNode::paintSymbol(QPainter *p) const
4027 {
4028 if (g_draw_frames && myRect().isValid()) {
4029 p->save();
4030 p->setPen(Qt::red);
4031 p->drawRect(m_my_rect);
4032 QPen pen = p->pen();
4033 pen.setStyle(Qt::DotLine);
4034 p->setPen(pen);
4035 p->drawLine(myRect().left(), 0, myRect().right(), 0);
4036 p->restore();
4037 }
4038 }
4039
stretch()4040 void MmlNode::stretch()
4041 {
4042 MmlNode *child = firstChild();
4043 for (; child != 0; child = child->nextSibling())
4044 child->stretch();
4045 }
4046
text() const4047 QString MmlTokenNode::text() const
4048 {
4049 QString result;
4050
4051 const MmlNode *child = firstChild();
4052 for (; child != 0; child = child->nextSibling()) {
4053 if (child->nodeType() != TextNode) continue;
4054 if (!result.isEmpty())
4055 result += ' ';
4056 result += ((MmlTextNode*)child)->text();
4057 }
4058
4059 return result;
4060 }
4061
numerator() const4062 MmlNode *MmlMfracNode::numerator() const
4063 {
4064 MmlNode *node = firstChild();
4065 Q_ASSERT(node != 0);
4066 return node;
4067 }
4068
denominator() const4069 MmlNode *MmlMfracNode::denominator() const
4070 {
4071 MmlNode *node = numerator()->nextSibling();
4072 Q_ASSERT(node != 0);
4073 return node;
4074 }
4075
symbolRect() const4076 QRect MmlMfracNode::symbolRect() const
4077 {
4078 int num_width = numerator()->myRect().width();
4079 int denom_width = denominator()->myRect().width();
4080 int my_width = qMax(num_width, denom_width) + 4;
4081
4082 return QRect(-my_width/2, 0, my_width, 1);
4083 }
4084
layoutSymbol()4085 void MmlMfracNode::layoutSymbol()
4086 {
4087 MmlNode *num = numerator();
4088 MmlNode *denom = denominator();
4089
4090 QRect num_rect = num->myRect();
4091 QRect denom_rect = denom->myRect();
4092
4093 int spacing = (int)(g_mfrac_spacing*(num_rect.height() + denom_rect.height()));
4094
4095 num->setRelOrigin(QPoint(-num_rect.width()/2, - spacing - num_rect.bottom()));
4096 denom->setRelOrigin(QPoint(-denom_rect.width()/2, spacing - denom_rect.top()));
4097 }
4098
zeroLineThickness(const QString & s)4099 static bool zeroLineThickness(const QString &s)
4100 {
4101 if (s.length() == 0 || !s[0].isDigit())
4102 return false;
4103
4104 for (int i = 0; i < s.length(); ++i) {
4105 QChar c = s.at(i);
4106 if (c.isDigit() && c != '0')
4107 return false;
4108 }
4109 return true;
4110 }
4111
paintSymbol(QPainter * p) const4112 void MmlMfracNode::paintSymbol(QPainter *p) const
4113 {
4114 QString linethickness_str = inheritAttributeFromMrow("linethickness", "1");
4115
4116 /* InterpretSpacing returns an int, which might be 0 even if the thickness
4117 is > 0, though very very small. That's ok, because the painter then paints
4118 a line of thickness 1. However, we have to run this check if the line
4119 thickness really is zero */
4120 if (!zeroLineThickness(linethickness_str)) {
4121 bool ok;
4122 int linethickness = interpretSpacing(linethickness_str, &ok);
4123 if (!ok)
4124 linethickness = 1;
4125
4126 p->save();
4127 QPen pen = p->pen();
4128 pen.setWidth(linethickness);
4129 p->setPen(pen);
4130 QSize s = myRect().size();
4131 p->drawLine(-s.width()/2, 0, s.width()/2, 0);
4132 p->restore();
4133 }
4134 }
4135
base() const4136 MmlNode *MmlRootBaseNode::base() const
4137 {
4138 MmlNode *node = firstChild();
4139 // Q_ASSERT(node != 0);
4140 return node;
4141 }
4142
index() const4143 MmlNode *MmlRootBaseNode::index() const
4144 {
4145 MmlNode *b = base();
4146 if (b == 0)
4147 return 0;
4148 return b->nextSibling();
4149 }
4150
scriptlevel(const MmlNode * child) const4151 int MmlRootBaseNode::scriptlevel(const MmlNode *child) const
4152 {
4153 int sl = MmlNode::scriptlevel();
4154
4155 MmlNode *i = index();
4156 if (child != 0 && child == i)
4157 return sl + 1;
4158 else
4159 return sl;
4160 }
4161
4162
symbolRect() const4163 QRect MmlRootBaseNode::symbolRect() const
4164 {
4165 MmlNode *b = base();
4166 QRect base_rect;
4167 if (b == 0)
4168 base_rect = QRect(0, 0, 1, 1);
4169 else
4170 base_rect = base()->myRect();
4171
4172 int margin = (int)(g_mroot_base_margin*base_rect.height());
4173 int tw = tailWidth();
4174
4175 return QRect(-tw, base_rect.top() - margin, tw,
4176 base_rect.height() + 2*margin);
4177 }
4178
tailWidth() const4179 int MmlRootBaseNode::tailWidth() const
4180 {
4181 QFontMetrics fm(font());
4182 return fm.boundingRect(g_radical_char).width();
4183 }
4184
layoutSymbol()4185 void MmlRootBaseNode::layoutSymbol()
4186 {
4187 MmlNode *b = base();
4188 QSize base_size;
4189 if (b != 0) {
4190 b->setRelOrigin(QPoint(0, 0));
4191 base_size = base()->myRect().size();
4192 } else
4193 base_size = QSize(1, 1);
4194
4195 MmlNode *i = index();
4196 if (i != 0) {
4197 int tw = tailWidth();
4198
4199 QRect i_rect = i->myRect();
4200 i->setRelOrigin(QPoint(-tw/2 - i_rect.width(),
4201 -i_rect.bottom() - 4));
4202 }
4203 }
4204
paintSymbol(QPainter * p) const4205 void MmlRootBaseNode::paintSymbol(QPainter *p) const
4206 {
4207 QFont fn = font();
4208
4209 p->save();
4210
4211 QRect sr = symbolRect();
4212
4213 QRect r = sr;
4214 r.moveTopLeft(devicePoint(sr.topLeft()));
4215 p->setViewport(r);
4216 p->setWindow(QFontMetrics(fn).boundingRect(g_radical_char));
4217 p->setFont(font());
4218 p->drawText(0, 0, QString(g_radical_char));
4219
4220 p->restore();
4221
4222 p->drawLine(sr.right(), sr.top(), myRect().right(), sr.top());
4223 }
4224
MmlTextNode(const QString & text,MmlDocument * document)4225 MmlTextNode::MmlTextNode(const QString &text, MmlDocument *document)
4226 : MmlNode(TextNode, document, MmlAttributeMap())
4227 {
4228 m_text = text;
4229 // Trim whitespace from ends, but keep nbsp and thinsp
4230 m_text.remove(QRegExp("^[^\\S\\x00a0\\x2009]+"));
4231 m_text.remove(QRegExp("[^\\S\\x00a0\\x2009]+$"));
4232
4233 if (m_text == QString(QChar(0x62, 0x20)) // ⁢
4234 || m_text == QString(QChar(0x63, 0x20)) // ⁣
4235 || m_text == QString(QChar(0x61, 0x20))) // ⁡
4236 m_text = "";
4237 }
4238
toStr() const4239 QString MmlTextNode::toStr() const
4240 {
4241 return MmlNode::toStr() + ", text=\"" + m_text + "\"";
4242 }
4243
paintSymbol(QPainter * p) const4244 void MmlTextNode::paintSymbol(QPainter *p) const
4245 {
4246 MmlNode::paintSymbol(p);
4247
4248 QFont fn = font();
4249
4250 QFontInfo fi(fn);
4251 // qWarning("MmlTextNode::paintSymbol(): requested: %s, used: %s, size=%d, italic=%d, bold=%d, text=\"%s\" sl=%d",
4252 // fn.family().latin1(), fi.family().latin1(), fi.pointSize(), (int)fi.italic(), (int)fi.bold(), m_text.latin1(), scriptlevel());
4253
4254 QFontMetrics fm(fn);
4255
4256 p->save();
4257 p->setFont(fn);
4258
4259 p->drawText(0, fm.strikeOutPos(), m_text);
4260 p->restore();
4261 }
4262
symbolRect() const4263 QRect MmlTextNode::symbolRect() const
4264 {
4265 QFontMetrics fm(font());
4266
4267 QRect br = fm.tightBoundingRect(m_text);
4268 br.translate(0, fm.strikeOutPos());
4269
4270 return br;
4271 }
4272
base() const4273 MmlNode *MmlSubsupBaseNode::base() const
4274 {
4275 MmlNode *b = firstChild();
4276 Q_ASSERT(b != 0);
4277 return b;
4278 }
4279
sscript() const4280 MmlNode *MmlSubsupBaseNode::sscript() const
4281 {
4282 MmlNode *s = base()->nextSibling();
4283 Q_ASSERT(s != 0);
4284 return s;
4285 }
4286
scriptlevel(const MmlNode * child) const4287 int MmlSubsupBaseNode::scriptlevel(const MmlNode *child) const
4288 {
4289 int sl = MmlNode::scriptlevel();
4290
4291 MmlNode *s = sscript();
4292 if (child != 0 && child == s)
4293 return sl + 1;
4294 else
4295 return sl;
4296 }
4297
layoutSymbol()4298 void MmlMsupNode::layoutSymbol()
4299 {
4300 MmlNode *b = base();
4301 MmlNode *s = sscript();
4302
4303 b->setRelOrigin(QPoint(-b->myRect().width(), 0));
4304 s->setRelOrigin(QPoint(0, b->myRect().top()));
4305 }
4306
layoutSymbol()4307 void MmlMsubNode::layoutSymbol()
4308 {
4309 MmlNode *b = base();
4310 MmlNode *s = sscript();
4311
4312 b->setRelOrigin(QPoint(-b->myRect().width(), 0));
4313 s->setRelOrigin(QPoint(0, b->myRect().bottom()));
4314 }
4315
base() const4316 MmlNode *MmlMsubsupNode::base() const
4317 {
4318 MmlNode *b = firstChild();
4319 Q_ASSERT(b != 0);
4320 return b;
4321 }
4322
subscript() const4323 MmlNode *MmlMsubsupNode::subscript() const
4324 {
4325 MmlNode *sub = base()->nextSibling();
4326 Q_ASSERT(sub != 0);
4327 return sub;
4328 }
4329
superscript() const4330 MmlNode *MmlMsubsupNode::superscript() const
4331 {
4332 MmlNode *sup = subscript()->nextSibling();
4333 Q_ASSERT(sup != 0);
4334 return sup;
4335 }
4336
layoutSymbol()4337 void MmlMsubsupNode::layoutSymbol()
4338 {
4339 MmlNode *b = base();
4340 MmlNode *sub = subscript();
4341 MmlNode *sup = superscript();
4342
4343 b->setRelOrigin(QPoint(-b->myRect().width(), 0));
4344 sub->setRelOrigin(QPoint(0, b->myRect().bottom()));
4345 sup->setRelOrigin(QPoint(0, b->myRect().top()));
4346 }
4347
scriptlevel(const MmlNode * child) const4348 int MmlMsubsupNode::scriptlevel(const MmlNode *child) const
4349 {
4350 int sl = MmlNode::scriptlevel();
4351
4352 MmlNode *sub = subscript();
4353 MmlNode *sup = superscript();
4354
4355 if (child != 0 && (child == sup || child == sub))
4356 return sl + 1;
4357 else
4358 return sl;
4359 }
4360
toStr() const4361 QString MmlMoNode::toStr() const
4362 {
4363 return MmlNode::toStr() + QString(" form=%1").arg((int)form());
4364 }
4365
layoutSymbol()4366 void MmlMoNode::layoutSymbol()
4367 {
4368 MmlNode *child = firstChild();
4369 if (child == 0)
4370 return;
4371
4372 child->setRelOrigin(QPoint(0, 0));
4373
4374 if (m_oper_spec == 0)
4375 m_oper_spec = mmlFindOperSpec(text(), form());
4376 }
4377
MmlMoNode(MmlDocument * document,const MmlAttributeMap & attribute_map)4378 MmlMoNode::MmlMoNode(MmlDocument *document, const MmlAttributeMap &attribute_map)
4379 : MmlTokenNode(MoNode, document, attribute_map)
4380 {
4381 m_oper_spec = 0;
4382 }
4383
dictionaryAttribute(const QString & name) const4384 QString MmlMoNode::dictionaryAttribute(const QString &name) const
4385 {
4386 const MmlNode *p = this;
4387 for (; p != 0; p = p->parent()) {
4388 if (p == this || p->nodeType() == MstyleNode) {
4389 QString expl_attr = p->explicitAttribute(name);
4390 if (!expl_attr.isNull())
4391 return expl_attr;
4392 }
4393 }
4394
4395 return mmlDictAttribute(name, m_oper_spec);
4396 }
4397
form() const4398 Mml::FormType MmlMoNode::form() const
4399 {
4400 QString value_str = inheritAttributeFromMrow("form");
4401 if (!value_str.isNull()) {
4402 bool ok;
4403 FormType value = interpretForm(value_str, &ok);
4404 if (ok)
4405 return value;
4406 else
4407 qWarning("Could not convert %s to form", value_str.toLatin1().data());
4408
4409 }
4410
4411 // Default heuristic.
4412 if (firstSibling() == (MmlNode*)this && lastSibling() != (MmlNode*)this)
4413 return PrefixForm;
4414 else if (lastSibling() == (MmlNode*)this && firstSibling() != (MmlNode*)this)
4415 return PostfixForm;
4416 else return InfixForm;
4417
4418 }
4419
stretch()4420 void MmlMoNode::stretch()
4421 {
4422 if (parent() == 0)
4423 return;
4424
4425 if (m_oper_spec == 0)
4426 return;
4427
4428 if (m_oper_spec->stretch_dir == OperSpec::HStretch
4429 && parent()->nodeType() == MrowNode
4430 && (nextSibling() != 0 || previousSibling() != 0))
4431 return;
4432
4433 QRect pmr = parent()->myRect();
4434 QRect pr = parentRect();
4435
4436 switch (m_oper_spec->stretch_dir) {
4437 case OperSpec::VStretch:
4438 stretchTo(QRect(pr.left(), pmr.top(), pr.width(), pmr.height()));
4439 break;
4440 case OperSpec::HStretch:
4441 stretchTo(QRect(pmr.left(), pr.top(), pmr.width(), pr.height()));
4442 break;
4443 case OperSpec::HVStretch:
4444 stretchTo(pmr);
4445 break;
4446 case OperSpec::NoStretch:
4447 break;
4448 }
4449 }
4450
lspace() const4451 int MmlMoNode::lspace() const
4452 {
4453 Q_ASSERT(m_oper_spec != 0);
4454 if (parent() == 0
4455 || (parent()->nodeType() != MrowNode
4456 && parent()->nodeType() != MfencedNode
4457 && parent()->nodeType() != UnknownNode)
4458 || (previousSibling() == 0 && nextSibling() == 0))
4459 return 0;
4460 else
4461 return interpretSpacing(dictionaryAttribute("lspace"), 0);
4462 }
4463
rspace() const4464 int MmlMoNode::rspace() const
4465 {
4466 Q_ASSERT(m_oper_spec != 0);
4467 if (parent() == 0
4468 || (parent()->nodeType() != MrowNode
4469 && parent()->nodeType() != MfencedNode
4470 && parent()->nodeType() != UnknownNode)
4471 || (previousSibling() == 0 && nextSibling() == 0))
4472 return 0;
4473 else
4474 return interpretSpacing(dictionaryAttribute("rspace"), 0);
4475 }
4476
symbolRect() const4477 QRect MmlMoNode::symbolRect() const
4478 {
4479 const MmlNode *child = firstChild();
4480
4481 if (child == 0)
4482 return QRect(0, 0, 0, 0);
4483
4484 QRect cmr = child->myRect();
4485
4486 return QRect(-lspace(), cmr.top(),
4487 cmr.width() + lspace() + rspace(), cmr.height());
4488 }
4489
rowspacing() const4490 int MmlMtableNode::rowspacing() const
4491 {
4492 QString value = explicitAttribute("rowspacing");
4493 if (value.isNull())
4494 return ex();
4495 bool ok;
4496 int r = interpretSpacing(value, &ok);
4497
4498 if (ok)
4499 return r;
4500 else
4501 return ex();
4502 }
4503
columnspacing() const4504 int MmlMtableNode::columnspacing() const
4505 {
4506 QString value = explicitAttribute("columnspacing");
4507 if (value.isNull())
4508 return (int)(0.8*em());
4509 bool ok;
4510 int r = interpretSpacing(value, &ok);
4511
4512 if (ok)
4513 return r;
4514 else
4515 return (int)(0.8*em());
4516 }
4517
colWidthSum() const4518 uint MmlMtableNode::CellSizeData::colWidthSum() const
4519 {
4520 uint w = 0;
4521 for (int i = 0; i < col_widths.count(); ++i)
4522 w += col_widths[i];
4523 return w;
4524 }
4525
rowHeightSum() const4526 uint MmlMtableNode::CellSizeData::rowHeightSum() const
4527 {
4528 uint h = 0;
4529 for (int i = 0; i < row_heights.count(); ++i)
4530 h += row_heights[i];
4531 return h;
4532 }
4533
init(const MmlNode * first_row)4534 void MmlMtableNode::CellSizeData::init(const MmlNode *first_row)
4535 {
4536 col_widths.clear();
4537 row_heights.clear();
4538
4539 const MmlNode *mtr = first_row;
4540 for (; mtr != 0; mtr = mtr->nextSibling()) {
4541
4542 Q_ASSERT(mtr->nodeType() == MtrNode);
4543
4544 int col_cnt = 0;
4545 const MmlNode *mtd = mtr->firstChild();
4546 for (; mtd != 0; mtd = mtd->nextSibling(), ++col_cnt) {
4547
4548 Q_ASSERT(mtd->nodeType() == MtdNode);
4549
4550 QRect mtdmr = mtd->myRect();
4551
4552 if (col_cnt == col_widths.count())
4553 col_widths.append(mtdmr.width());
4554 else
4555 col_widths[col_cnt] = qMax(col_widths[col_cnt], mtdmr.width());
4556 }
4557
4558 row_heights.append(mtr->myRect().height());
4559 }
4560 }
4561
layoutSymbol()4562 void MmlMtableNode::layoutSymbol()
4563 {
4564 // Obtain natural widths of columns
4565 m_cell_size_data.init(firstChild());
4566
4567 int col_spc = columnspacing();
4568 int row_spc = rowspacing();
4569 int frame_spc_hor = framespacing_hor();
4570 QString columnwidth_attr = explicitAttribute("columnwidth", "auto");
4571
4572 // Is table width set by user? If so, set col_width_sum and never ever change it.
4573 int col_width_sum = m_cell_size_data.colWidthSum();
4574 bool width_set_by_user = false;
4575 QString width_str = explicitAttribute("width", "auto");
4576 if (width_str != "auto") {
4577 bool ok;
4578
4579 int w = interpretSpacing(width_str, &ok);
4580 if (ok) {
4581 col_width_sum = w
4582 - col_spc*(m_cell_size_data.numCols() - 1)
4583 - frame_spc_hor*2;
4584 width_set_by_user = true;
4585 }
4586 }
4587
4588 // Find out what kind of columns we are dealing with and set the widths of
4589 // statically sized columns.
4590 int fixed_width_sum = 0; // sum of widths of statically sized set columns
4591 int auto_width_sum = 0; // sum of natural widths of auto sized columns
4592 int relative_width_sum = 0; // sum of natural widths of relatively sized columns
4593 double relative_fraction_sum = 0; // total fraction of width taken by relatively
4594 // sized columns
4595 int i;
4596 for (i = 0; i < m_cell_size_data.numCols(); ++i) {
4597 QString value = interpretListAttr(columnwidth_attr, i, "auto");
4598
4599 // Is it an auto sized column?
4600 if (value == "auto" || value == "fit") {
4601 auto_width_sum += m_cell_size_data.col_widths[i];
4602 continue;
4603 }
4604
4605 // Is it a statically sized column?
4606 bool ok;
4607 int w = interpretSpacing(value, &ok);
4608 if (ok) {
4609 // Yup, sets its width to the user specified value
4610 m_cell_size_data.col_widths[i] = w;
4611 fixed_width_sum += w;
4612 continue;
4613 }
4614
4615 // Is it a relatively sized column?
4616 if (value.endsWith("%")) {
4617 value.truncate(value.length() - 1);
4618 double factor = value.toFloat(&ok);
4619 if (ok && !value.isEmpty()) {
4620 factor /= 100.0;
4621 relative_width_sum += m_cell_size_data.col_widths[i];
4622 relative_fraction_sum += factor;
4623 if (!width_set_by_user) {
4624 // If the table width was not set by the user, we are free to increase
4625 // it so that the width of this column will be >= than its natural width
4626 int min_col_width_sum = ROUND(m_cell_size_data.col_widths[i]/factor);
4627 if (min_col_width_sum > col_width_sum)
4628 col_width_sum = min_col_width_sum;
4629 }
4630 continue;
4631 }
4632 else
4633 qWarning("MmlMtableNode::layoutSymbol(): could not parse value %s%%", value.toLatin1().data());
4634 }
4635
4636 // Relatively sized column, but we failed to parse the factor. Treat is like an auto
4637 // column.
4638 auto_width_sum += m_cell_size_data.col_widths[i];
4639 }
4640
4641 // Work out how much space remains for the auto olumns, after allocating
4642 // the statically sized and the relatively sized columns.
4643 int required_auto_width_sum = col_width_sum
4644 - ROUND(relative_fraction_sum*col_width_sum)
4645 - fixed_width_sum;
4646
4647 if (!width_set_by_user && required_auto_width_sum < auto_width_sum) {
4648 if (relative_fraction_sum < 1)
4649 col_width_sum = ROUND((fixed_width_sum + auto_width_sum)/(1 - relative_fraction_sum));
4650 else
4651 col_width_sum = fixed_width_sum + auto_width_sum + relative_width_sum;
4652 required_auto_width_sum = auto_width_sum;
4653 }
4654
4655 // Ratio by which we have to shring/grow all auto sized columns to make it all fit
4656 double auto_width_scale = 1;
4657 if (auto_width_sum > 0)
4658 auto_width_scale = (float)required_auto_width_sum/auto_width_sum;
4659
4660 // Set correct sizes for the auto sized and the relatively sized columns.
4661 for (i = 0; i < m_cell_size_data.numCols(); ++i) {
4662 QString value = interpretListAttr(columnwidth_attr, i, "auto");
4663
4664 // Is it a relatively sized column?
4665 if (value.endsWith("%")) {
4666 bool ok;
4667 int w = interpretPercentSpacing(value, col_width_sum, &ok);
4668 if (ok)
4669 m_cell_size_data.col_widths[i] = w;
4670 else
4671 // We're treating parsing errors here as auto sized columns
4672 m_cell_size_data.col_widths[i]
4673 = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]);
4674 }
4675 // Is it an auto sized column?
4676 else if (value == "auto") {
4677 m_cell_size_data.col_widths[i]
4678 = ROUND(auto_width_scale*m_cell_size_data.col_widths[i]);
4679 }
4680 }
4681
4682 QString s;
4683 QList<int> &col_widths = m_cell_size_data.col_widths;
4684 for (i = 0; i < col_widths.count(); ++i) {
4685 s += QString("[w=%1 %2%%]")
4686 .arg(col_widths[i])
4687 .arg(100*col_widths[i]/m_cell_size_data.colWidthSum());
4688 }
4689 // qWarning(s);
4690
4691 m_content_width = m_cell_size_data.colWidthSum()
4692 + col_spc*(m_cell_size_data.numCols() - 1);
4693 m_content_height = m_cell_size_data.rowHeightSum()
4694 + row_spc*(m_cell_size_data.numRows() - 1);
4695
4696 int bottom = -m_content_height/2;
4697 MmlNode *child = firstChild();
4698 for (; child != 0; child = child->nextSibling()) {
4699 Q_ASSERT(child->nodeType() == MtrNode);
4700 MmlMtrNode *row = (MmlMtrNode*) child;
4701
4702 row->layoutCells(m_cell_size_data.col_widths, col_spc);
4703 QRect rmr = row->myRect();
4704 row->setRelOrigin(QPoint(0, bottom - rmr.top()));
4705 bottom += rmr.height() + row_spc;
4706 }
4707 }
4708
symbolRect() const4709 QRect MmlMtableNode::symbolRect() const
4710 {
4711 int frame_spc_hor = framespacing_hor();
4712 int frame_spc_ver = framespacing_ver();
4713
4714 return QRect(-frame_spc_hor,
4715 -m_content_height/2 - frame_spc_ver,
4716 m_content_width + 2*frame_spc_hor,
4717 m_content_height + 2*frame_spc_ver);
4718 }
4719
frame() const4720 Mml::FrameType MmlMtableNode::frame() const
4721 {
4722 QString value = explicitAttribute("frame", "none");
4723 return interpretFrameType(value, 0, 0);
4724 }
4725
columnlines(int idx) const4726 Mml::FrameType MmlMtableNode::columnlines(int idx) const
4727 {
4728 QString value = explicitAttribute("columnlines", "none");
4729 return interpretFrameType(value, idx, 0);
4730 }
4731
rowlines(int idx) const4732 Mml::FrameType MmlMtableNode::rowlines(int idx) const
4733 {
4734 QString value = explicitAttribute("rowlines", "none");
4735 return interpretFrameType(value, idx, 0);
4736 }
4737
paintSymbol(QPainter * p) const4738 void MmlMtableNode::paintSymbol(QPainter *p) const
4739 {
4740 FrameType f = frame();
4741 if (f != FrameNone) {
4742 p->save();
4743
4744 QPen pen = p->pen();
4745 if (f == FrameDashed)
4746 pen.setStyle(Qt::DashLine);
4747 else
4748 pen.setStyle(Qt::SolidLine);
4749 p->setPen(pen);
4750 p->drawRect(myRect());
4751
4752 p->restore();
4753 }
4754
4755 p->save();
4756
4757 int col_spc = columnspacing();
4758 int row_spc = rowspacing();
4759
4760 QPen pen = p->pen();
4761 int col_offset = 0;
4762 int i;
4763 for (i = 0; i < m_cell_size_data.numCols() - 1; ++i) {
4764 FrameType f = columnlines(i);
4765 col_offset += m_cell_size_data.col_widths[i];
4766
4767 if (f != FrameNone) {
4768 if (f == FrameDashed)
4769 pen.setStyle(Qt::DashLine);
4770 else if (f == FrameSolid)
4771 pen.setStyle(Qt::SolidLine);
4772
4773 p->setPen(pen);
4774 int x = col_offset + col_spc/2;
4775 p->drawLine(x, -m_content_height/2, x, m_content_height/2 );
4776 }
4777 col_offset += col_spc;
4778 }
4779
4780 int row_offset = 0;
4781 for (i = 0; i < m_cell_size_data.numRows() - 1; ++i) {
4782 FrameType f = rowlines(i);
4783 row_offset += m_cell_size_data.row_heights[i];
4784
4785 if (f != FrameNone) {
4786 if (f == FrameDashed)
4787 pen.setStyle(Qt::DashLine);
4788 else if (f == FrameSolid)
4789 pen.setStyle(Qt::SolidLine);
4790
4791 p->setPen(pen);
4792 int y = row_offset + row_spc/2 - m_content_height/2;
4793 p->drawLine(0, y, m_content_width, y);
4794 }
4795 row_offset += row_spc;
4796 }
4797
4798 p->restore();
4799 }
4800
framespacing_ver() const4801 int MmlMtableNode::framespacing_ver() const
4802 {
4803 if (frame() == FrameNone)
4804 return (int)(0.2*em());
4805
4806 QString value = explicitAttribute("framespacing", "0.4em 0.5ex");
4807
4808 bool ok;
4809 FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok);
4810 if (ok)
4811 return fs.m_ver;
4812 else
4813 return (int)(0.5*ex());
4814 }
4815
framespacing_hor() const4816 int MmlMtableNode::framespacing_hor() const
4817 {
4818 if (frame() == FrameNone)
4819 return (int)(0.2*em());
4820
4821 QString value = explicitAttribute("framespacing", "0.4em 0.5ex");
4822
4823 bool ok;
4824 FrameSpacing fs = interpretFrameSpacing(value, em(), ex(), &ok);
4825 if (ok)
4826 return fs.m_hor;
4827 else
4828 return (int)(0.4*em());
4829 }
4830
layoutCells(const QList<int> & col_widths,int col_spc)4831 void MmlMtrNode::layoutCells(const QList<int> &col_widths,
4832 int col_spc)
4833 {
4834 QRect mr = myRect();
4835
4836 MmlNode *child = firstChild();
4837 int col_offset = 0;
4838 uint colnum = 0;
4839 for (; child != 0; child = child->nextSibling(), ++colnum) {
4840 Q_ASSERT(child->nodeType() == MtdNode);
4841 MmlMtdNode *mtd = (MmlMtdNode*) child;
4842
4843 QRect r = QRect(0, mr.top(), col_widths[colnum], mr.height());
4844 mtd->setMyRect(r);
4845 mtd->setRelOrigin(QPoint(col_offset, 0));
4846 col_offset += col_widths[colnum] + col_spc;
4847 }
4848
4849 updateMyRect();
4850 }
4851
scriptlevel(const MmlNode * child) const4852 int MmlMtdNode::scriptlevel(const MmlNode *child) const
4853 {
4854 int sl = MmlNode::scriptlevel();
4855 if (child != 0 && child == firstChild())
4856 return sl + m_scriptlevel_adjust;
4857 else
4858 return sl;
4859 }
4860
setMyRect(const QRect & rect)4861 void MmlMtdNode::setMyRect(const QRect &rect)
4862 {
4863 MmlNode::setMyRect(rect);
4864 MmlNode *child = firstChild();
4865 if (child == 0)
4866 return;
4867
4868 if (rect.width() < child->myRect().width()) {
4869 while (rect.width() < child->myRect().width()
4870 && child->font().pointSize() > g_min_font_point_size) {
4871
4872 // qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d",
4873 // rect.width(), child->myRect().width(), m_scriptlevel_adjust);
4874
4875 ++m_scriptlevel_adjust;
4876 child->layout();
4877 }
4878
4879 // qWarning("MmlMtdNode::setMyRect(): rect.width()=%d, child()->myRect().width=%d sl=%d",
4880 // rect.width(), child->myRect().width(), m_scriptlevel_adjust);
4881 }
4882
4883 QRect mr = myRect();
4884 QRect cmr = child->myRect();
4885
4886 QPoint child_rel_origin;
4887
4888 switch (columnalign()) {
4889 case ColAlignLeft:
4890 child_rel_origin.setX(0);
4891 break;
4892 case ColAlignCenter:
4893 child_rel_origin.setX(mr.left() + (mr.width() - cmr.width())/2);
4894 break;
4895 case ColAlignRight:
4896 child_rel_origin.setX(mr.right() - cmr.width());
4897 break;
4898 }
4899
4900 switch (rowalign()) {
4901 case RowAlignTop:
4902 child_rel_origin.setY(mr.top() - cmr.top());
4903 break;
4904 case RowAlignCenter:
4905 case RowAlignBaseline:
4906 child_rel_origin.setY(mr.top() -cmr.top() + (mr.height() - cmr.height())/2);
4907 break;
4908 case RowAlignBottom:
4909 child_rel_origin.setY(mr.bottom() - cmr.bottom());
4910 break;
4911 case RowAlignAxis:
4912 child_rel_origin.setY(0);
4913 break;
4914 }
4915
4916 child->setRelOrigin(child_rel_origin);
4917 }
4918
colNum()4919 uint MmlMtdNode::colNum()
4920 {
4921 MmlNode *syb = previousSibling();
4922
4923 uint i = 0;
4924 for (; syb != 0; syb = syb->previousSibling())
4925 ++i;
4926
4927 return i;
4928 }
4929
rowNum()4930 uint MmlMtdNode::rowNum()
4931 {
4932 MmlNode *row = parent()->previousSibling();
4933
4934 uint i = 0;
4935 for (; row != 0; row = row->previousSibling())
4936 ++i;
4937
4938 return i;
4939 }
4940
columnalign()4941 MmlMtdNode::ColAlign MmlMtdNode::columnalign()
4942 {
4943 QString val = explicitAttribute("columnalign");
4944 if (!val.isNull())
4945 return interpretColAlign(val, 0, 0);
4946
4947 MmlNode *node = parent(); // <mtr>
4948 if (node == 0)
4949 return ColAlignCenter;
4950
4951 uint colnum = colNum();
4952 val = node->explicitAttribute("columnalign");
4953 if (!val.isNull())
4954 return interpretColAlign(val, colnum, 0);
4955
4956 node = node->parent(); // <mtable>
4957 if (node == 0)
4958 return ColAlignCenter;
4959
4960 val = node->explicitAttribute("columnalign");
4961 if (!val.isNull())
4962 return interpretColAlign(val, colnum, 0);
4963
4964 return ColAlignCenter;
4965 }
4966
rowalign()4967 MmlMtdNode::RowAlign MmlMtdNode::rowalign()
4968 {
4969 QString val = explicitAttribute("rowalign");
4970 if (!val.isNull())
4971 return interpretRowAlign(val, 0, 0);
4972
4973 MmlNode *node = parent(); // <mtr>
4974 if (node == 0)
4975 return RowAlignAxis;
4976
4977 uint rownum = rowNum();
4978 val = node->explicitAttribute("rowalign");
4979 if (!val.isNull())
4980 return interpretRowAlign(val, rownum, 0);
4981
4982 node = node->parent(); // <mtable>
4983 if (node == 0)
4984 return RowAlignAxis;
4985
4986 val = node->explicitAttribute("rowalign");
4987 if (!val.isNull())
4988 return interpretRowAlign(val, rownum, 0);
4989
4990 return RowAlignAxis;
4991 }
4992
layoutSymbol()4993 void MmlMoverNode::layoutSymbol()
4994 {
4995 MmlNode *base = firstChild();
4996 Q_ASSERT(base != 0);
4997 MmlNode *over = base->nextSibling();
4998 Q_ASSERT(over != 0);
4999
5000 QRect base_rect = base->myRect();
5001 QRect over_rect = over->myRect();
5002
5003 int spacing = (int)(g_mfrac_spacing*(over_rect.height()
5004 + base_rect.height()));
5005
5006 base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
5007 over->setRelOrigin(QPoint(-over_rect.width()/2,
5008 base_rect.top() - spacing - over_rect.bottom()));
5009 }
5010
scriptlevel(const MmlNode * node) const5011 int MmlMoverNode::scriptlevel(const MmlNode *node) const
5012 {
5013 MmlNode *base = firstChild();
5014 Q_ASSERT(base != 0);
5015 MmlNode *over = base->nextSibling();
5016 Q_ASSERT(over != 0);
5017
5018 int sl = MmlNode::scriptlevel();
5019 if (node != 0 && node == over)
5020 return sl + 1;
5021 else
5022 return sl;
5023 }
5024
layoutSymbol()5025 void MmlMunderNode::layoutSymbol()
5026 {
5027 MmlNode *base = firstChild();
5028 Q_ASSERT(base != 0);
5029 MmlNode *under = base->nextSibling();
5030 Q_ASSERT(under != 0);
5031
5032 QRect base_rect = base->myRect();
5033 QRect under_rect = under->myRect();
5034
5035 int spacing = (int)(g_mfrac_spacing*(under_rect.height() + base_rect.height()));
5036
5037 base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
5038 under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top()));
5039 }
5040
scriptlevel(const MmlNode * node) const5041 int MmlMunderNode::scriptlevel(const MmlNode *node) const
5042 {
5043 MmlNode *base = firstChild();
5044 Q_ASSERT(base != 0);
5045 MmlNode *under = base->nextSibling();
5046 Q_ASSERT(under != 0);
5047
5048 int sl = MmlNode::scriptlevel();
5049 if (node != 0 && node == under)
5050 return sl + 1;
5051 else
5052 return sl;
5053 }
5054
layoutSymbol()5055 void MmlMunderoverNode::layoutSymbol()
5056 {
5057 MmlNode *base = firstChild();
5058 Q_ASSERT(base != 0);
5059 MmlNode *under = base->nextSibling();
5060 Q_ASSERT(under != 0);
5061 MmlNode *over = under->nextSibling();
5062 Q_ASSERT(over != 0);
5063
5064 QRect base_rect = base->myRect();
5065 QRect under_rect = under->myRect();
5066 QRect over_rect = over->myRect();
5067
5068 int spacing = (int)(g_mfrac_spacing*( base_rect.height()
5069 + under_rect.height()
5070 + over_rect.height()) );
5071
5072 base->setRelOrigin(QPoint(-base_rect.width()/2, 0));
5073 under->setRelOrigin(QPoint(-under_rect.width()/2, base_rect.bottom() + spacing - under_rect.top()));
5074 over->setRelOrigin(QPoint(-over_rect.width()/2, base_rect.top() - spacing - under_rect.bottom()));
5075 }
5076
scriptlevel(const MmlNode * node) const5077 int MmlMunderoverNode::scriptlevel(const MmlNode *node) const
5078 {
5079 MmlNode *base = firstChild();
5080 Q_ASSERT(base != 0);
5081 MmlNode *under = base->nextSibling();
5082 Q_ASSERT(under != 0);
5083 MmlNode *over = under->nextSibling();
5084 Q_ASSERT(over != 0);
5085
5086 int sl = MmlNode::scriptlevel();
5087 if (node != 0 && (node == under || node == over))
5088 return sl + 1;
5089 else
5090 return sl;
5091 }
5092
interpretSpacing(QString value,int base_value,bool * ok) const5093 int MmlMpaddedNode::interpretSpacing(QString value, int base_value, bool *ok) const
5094 {
5095 if (ok != 0)
5096 *ok = false;
5097
5098 value.replace(' ', "");
5099
5100 QString sign, factor_str, pseudo_unit;
5101 bool percent = false;
5102
5103 // extract the sign
5104 int idx = 0;
5105 if (idx < value.length() && (value.at(idx) == '+' || value.at(idx) == '-'))
5106 sign = value.at(idx++);
5107
5108 // extract the factor
5109 while (idx < value.length() && (value.at(idx).isDigit() || value.at(idx) == '.'))
5110 factor_str.append(value.at(idx++));
5111
5112 // extract the % sign
5113 if (idx < value.length() && value.at(idx) == '%') {
5114 percent = true;
5115 ++idx;
5116 }
5117
5118 // extract the pseudo-unit
5119 pseudo_unit = value.mid(idx);
5120
5121 bool float_ok;
5122 double factor = factor_str.toFloat(&float_ok);
5123 if (!float_ok || factor < 0) {
5124 qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
5125 return 0;
5126 }
5127
5128 if (percent)
5129 factor /= 100.0;
5130
5131 QRect cr;
5132 if (firstChild() == 0)
5133 cr = QRect(0, 0, 0, 0);
5134 else
5135 cr = firstChild()->myRect();
5136
5137 int unit_size;
5138
5139 if (pseudo_unit.isEmpty())
5140 unit_size = base_value;
5141 else if (pseudo_unit == "width")
5142 unit_size = cr.width();
5143 else if (pseudo_unit == "height")
5144 unit_size = -cr.top();
5145 else if (pseudo_unit == "depth")
5146 unit_size = cr.bottom();
5147 else {
5148 bool unit_ok;
5149 unit_size = MmlNode::interpretSpacing("1" + pseudo_unit, &unit_ok);
5150 if (!unit_ok) {
5151 qWarning("MmlMpaddedNode::interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
5152 return 0;
5153 }
5154 }
5155
5156 if (ok != 0)
5157 *ok = true;
5158
5159 if (sign.isNull())
5160 return (int)(factor*unit_size);
5161 else if (sign == "+")
5162 return base_value + (int)(factor*unit_size);
5163 else // sign == "-"
5164 return base_value - (int)(factor*unit_size);
5165 }
5166
lspace() const5167 int MmlMpaddedNode::lspace() const
5168 {
5169 QString value = explicitAttribute("lspace");
5170
5171 if (value.isNull())
5172 return 0;
5173
5174 bool ok;
5175 int lspace = interpretSpacing(value, 0, &ok);
5176
5177 if (ok)
5178 return lspace;
5179
5180 return 0;
5181 }
5182
width() const5183 int MmlMpaddedNode::width() const
5184 {
5185 int child_width = 0;
5186 if (firstChild() != 0)
5187 child_width = firstChild()->myRect().width();
5188
5189 QString value = explicitAttribute("width");
5190 if (value.isNull())
5191 return child_width;
5192
5193 bool ok;
5194 int w = interpretSpacing(value, child_width, &ok);
5195 if (ok)
5196 return w;
5197
5198 return child_width;
5199 }
5200
height() const5201 int MmlMpaddedNode::height() const
5202 {
5203 QRect cr;
5204 if (firstChild() == 0)
5205 cr = QRect(0, 0, 0, 0);
5206 else
5207 cr = firstChild()->myRect();
5208
5209 QString value = explicitAttribute("height");
5210 if (value.isNull())
5211 return -cr.top();
5212
5213 bool ok;
5214 int h = interpretSpacing(value, -cr.top(), &ok);
5215 if (ok)
5216 return h;
5217
5218 return -cr.top();
5219 }
5220
depth() const5221 int MmlMpaddedNode::depth() const
5222 {
5223 QRect cr;
5224 if (firstChild() == 0)
5225 cr = QRect(0, 0, 0, 0);
5226 else
5227 cr = firstChild()->myRect();
5228
5229 QString value = explicitAttribute("depth");
5230 if (value.isNull())
5231 return cr.bottom();
5232
5233 bool ok;
5234 int h = interpretSpacing(value, cr.bottom(), &ok);
5235 if (ok)
5236 return h;
5237
5238 return cr.bottom();
5239 }
5240
layoutSymbol()5241 void MmlMpaddedNode::layoutSymbol()
5242 {
5243 MmlNode *child = firstChild();
5244 if (child == 0)
5245 return;
5246
5247 child->setRelOrigin(QPoint(0, 0));
5248 }
5249
5250
symbolRect() const5251 QRect MmlMpaddedNode::symbolRect() const
5252 {
5253 return QRect(-lspace(), -height(), lspace() + width(), height() + depth());
5254 }
5255
5256 // *******************************************************************
5257 // QtMmlWidget
5258 // *******************************************************************
5259
5260 /*!
5261 \class QtMmlWidget
5262
5263 \brief The QtMmlWidget class renders mathematical formulas written in MathML 2.0.
5264
5265 QtMmlWidget implements the Presentation Markup subset of the
5266 MathML 2.0 specification, with a few \link overview.html exceptions\endlink.
5267
5268 \code
5269 QtMmlWidget *mmlWidget = new QtMmlWidget(this);
5270 QString errorMsg;
5271 int errorLine;
5272 int errorColumn;
5273 bool ok = mmlWidget->setContent(mmlText, &errorMsg, &errorLine, &errorColumn);
5274 if (!ok) {
5275 qWarning("MathML error: %s, Line: %d, Column: %d",
5276 errorMsg.latin1(), errorLine, errorColumn);
5277 }
5278 \endcode
5279
5280 */
5281
5282
5283 /*!
5284 \enum QtMmlWidget::MmlFont
5285
5286 This ennumerated type is used in fontName() and setFontName() to
5287 specify a font type.
5288
5289 \value NormalFont The default font type, used to render
5290 expressions for which no mathvariant or fontfamily is specified,
5291 or for which the "normal" mathvariant is specified.
5292
5293 \value FrakturFont The font type used to render expressions for
5294 which the "fraktur" mathvariant is specified.
5295
5296 \value SansSerifFont The font type used to render expressions
5297 for which the "sans-serif" mathvariant is specified.
5298
5299 \value ScriptFont The font type used to render expressions
5300 for which the "script" mathvariant is specified.
5301
5302 \value MonospaceFont The font type used to render expressions
5303 for which the "monospace" mathvariant is specified.
5304
5305 \value DoublestruckFont The font type used to render expressions
5306 for which the "doublestruck" mathvariant is specified.
5307
5308 \sa setFontName() fontName() setBaseFontPointSize() baseFontPointSize()
5309 */
5310
5311 /*!
5312 Constructs a QtMmlWidget object. The \a parent
5313 parameter is passed to QFrame's constructor.
5314 */
5315
QtMmlWidget(QWidget * parent)5316 QtMmlWidget::QtMmlWidget(QWidget *parent)
5317 : QFrame(parent)
5318 {
5319 m_doc = new MmlDocument;
5320 }
5321
5322 /*!
5323 Destructs a QtMmlWidget object.
5324 */
5325
~QtMmlWidget()5326 QtMmlWidget::~QtMmlWidget()
5327 {
5328 delete m_doc;
5329 }
5330
5331 /*!
5332 Returns the name of the font used to render the font \a type.
5333
5334 \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
5335 */
5336
fontName(MmlFont type) const5337 QString QtMmlWidget::fontName(MmlFont type) const
5338 {
5339 return m_doc->fontName(type);
5340 }
5341
5342 /*!
5343 Sets the name of the font used to render the font \a type to \a name.
5344
5345 \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
5346 */
5347
setFontName(MmlFont type,const QString & name)5348 void QtMmlWidget::setFontName(MmlFont type, const QString &name)
5349 {
5350 m_doc->setFontName(type, name);
5351 m_doc->layout();
5352 update();
5353 }
5354
5355 /*!
5356 If \a b is true, draws a red bounding rectangle around each
5357 expression; if \a b is false, no such rectangle is drawn.
5358 This is mostly useful for debugging MathML expressions.
5359
5360 \sa drawFrames()
5361 */
5362
setDrawFrames(bool b)5363 void QtMmlWidget::setDrawFrames(bool b)
5364 {
5365 g_draw_frames = b;
5366 update();
5367 }
5368
5369 /*!
5370 Returns true if each expression should be drawn with a red
5371 bounding rectangle; otherwise returns false.
5372 This is mostly useful for debugging MathML expressions.
5373
5374 \sa setDrawFrames()
5375 */
5376
drawFrames() const5377 bool QtMmlWidget::drawFrames() const
5378 {
5379 return g_draw_frames;
5380 }
5381
5382 /*!
5383 Clears the contents of this widget.
5384 */
clear()5385 void QtMmlWidget::clear()
5386 {
5387 m_doc->clear();
5388 }
5389
5390 /*!
5391 Returns the point size of the font used to render expressions
5392 whose scriptlevel is 0.
5393
5394 \sa setBaseFontPointSize() fontName() setFontName()
5395 */
5396
baseFontPointSize() const5397 int QtMmlWidget::baseFontPointSize() const
5398 {
5399 return m_doc->baseFontPointSize();
5400 }
5401
5402 /*!
5403 Sets the point \a size of the font used to render expressions
5404 whose scriptlevel is 0.
5405
5406 \sa baseFontPointSize() fontName() setFontName()
5407 */
5408
setBaseFontPointSize(int size)5409 void QtMmlWidget::setBaseFontPointSize(int size)
5410 {
5411 if (size < g_min_font_point_size)
5412 return;
5413
5414 m_doc->setBaseFontPointSize(size);
5415 m_doc->layout();
5416 update();
5417 }
5418
5419 /*!
5420 Returns the size of the formula in pixels.
5421 */
5422
sizeHint() const5423 QSize QtMmlWidget::sizeHint() const
5424 {
5425 QSize size = m_doc->size();
5426 if (size == QSize(0, 0))
5427 return QSize(100, 50);
5428 return m_doc->size();
5429 }
5430
5431 /*!
5432 Sets the MathML expression to be rendered. The expression is given
5433 in the string \a text. If the expression is successfully parsed,
5434 this method returns true; otherwise it returns false. If an error
5435 occured \a errorMsg is set to a diagnostic message, while \a
5436 errorLine and \a errorColumn contain the location of the error.
5437 Any of \a errorMsg, \a errorLine and \a errorColumn may be 0,
5438 in which case they are not set.
5439
5440 \a text should contain MathML 2.0 presentation markup elements enclosed
5441 in a <math> element.
5442 */
5443
setContent(const QString & text,QString * errorMsg,int * errorLine,int * errorColumn)5444 bool QtMmlWidget::setContent(const QString &text, QString *errorMsg,
5445 int *errorLine, int *errorColumn)
5446 {
5447 bool result = m_doc->setContent(text, errorMsg, errorLine, errorColumn);
5448 if (result)
5449 update();
5450 return result;
5451 }
5452
5453 /*! \internal */
5454
paintEvent(QPaintEvent * e)5455 void QtMmlWidget::paintEvent(QPaintEvent *e)
5456 {
5457 QFrame::paintEvent(e);
5458 QPainter p(this);
5459 if (e->rect().intersects(contentsRect()))
5460 p.setClipRegion(e->region().intersected(contentsRect()));
5461
5462 QSize s = m_doc->size();
5463 int x = (width() - s.width())/2;
5464 int y = (height() - s.height())/2;
5465 m_doc->paint(&p, QPoint(x, y));
5466 }
5467
5468 /*! \internal */
5469
dump() const5470 void QtMmlWidget::dump() const
5471 {
5472 m_doc->dump();
5473 }
5474
5475 // *******************************************************************
5476 // Static helper functions
5477 // *******************************************************************
5478
entityDeclarations()5479 static QString entityDeclarations()
5480 {
5481 QString result = "<!DOCTYPE math [\n";
5482
5483 const EntitySpec *ent = g_xml_entity_data;
5484 for (; ent->name != 0; ++ent) {
5485 result += "\t<!ENTITY " + QString(ent->name) + " \"" + ent->value + "\">\n";
5486 }
5487
5488 result += "]>\n";
5489
5490 return result;
5491 }
5492
interpretSpacing(QString value,int em,int ex,bool * ok)5493 static int interpretSpacing(QString value, int em, int ex, bool *ok)
5494 {
5495 if (ok != 0)
5496 *ok = true;
5497
5498 if (value == "thin")
5499 return 1;
5500
5501 if (value == "medium")
5502 return 2;
5503
5504 if (value == "thick")
5505 return 3;
5506
5507 struct HSpacingValue {
5508 const char *name;
5509 float factor;
5510 };
5511
5512 static const HSpacingValue g_h_spacing_data[] =
5513 {
5514 { "veryverythinmathspace", (float) 0.0555556 },
5515 { "verythinmathspace", (float) 0.111111 },
5516 { "thinmathspace", (float) 0.166667 },
5517 { "mediummathspace", (float) 0.222222 },
5518 { "thickmathspace", (float) 0.277778 },
5519 { "verythickmathspace", (float) 0.333333 },
5520 { "veryverythickmathspace", (float) 0.388889 },
5521 { 0, (float) 0 }
5522 };
5523
5524 const HSpacingValue *v = g_h_spacing_data;
5525 for (; v->name != 0; ++v) {
5526 if (value == v->name)
5527 return (int)(em*v->factor);
5528 }
5529
5530 if (value.endsWith("em")) {
5531 value.truncate(value.length() - 2);
5532 bool float_ok;
5533 float factor = value.toFloat(&float_ok);
5534 if (float_ok && factor >= 0)
5535 return (int)(em*factor);
5536 else {
5537 qWarning("interpretSpacing(): could not parse \"%sem\"", value.toLatin1().data());
5538 if (ok != 0)
5539 *ok = false;
5540 return 0;
5541 }
5542 }
5543
5544 if (value.endsWith("ex")) {
5545 value.truncate(value.length() - 2);
5546 bool float_ok;
5547 float factor = value.toFloat(&float_ok);
5548 if (float_ok && factor >= 0)
5549 return (int)(ex*factor);
5550 else {
5551 qWarning("interpretSpacing(): could not parse \"%sex\"", value.toLatin1().data());
5552 if (ok != 0)
5553 *ok = false;
5554 return 0;
5555 }
5556 }
5557
5558 if (value.endsWith("cm")) {
5559 value.truncate(value.length() - 2);
5560 bool float_ok;
5561 float factor = value.toFloat(&float_ok);
5562 if (float_ok && factor >= 0) {
5563 Q_ASSERT(qApp->desktop() != 0);
5564 QDesktopWidget *dw = qApp->desktop();
5565 Q_ASSERT(dw->width() != 0);
5566 Q_ASSERT(dw->widthMM() != 0);
5567 return (int)(factor*10*dw->width()/dw->widthMM());
5568 }
5569 else {
5570 qWarning("interpretSpacing(): could not parse \"%scm\"", value.toLatin1().data());
5571 if (ok != 0)
5572 *ok = false;
5573 return 0;
5574 }
5575 }
5576
5577 if (value.endsWith("mm")) {
5578 value.truncate(value.length() - 2);
5579 bool float_ok;
5580 float factor = value.toFloat(&float_ok);
5581 if (float_ok && factor >= 0) {
5582 Q_ASSERT(qApp->desktop() != 0);
5583 QDesktopWidget *dw = qApp->desktop();
5584 Q_ASSERT(dw->width() != 0);
5585 Q_ASSERT(dw->widthMM() != 0);
5586 return (int)(factor*dw->width()/dw->widthMM());
5587 }
5588 else {
5589 qWarning("interpretSpacing(): could not parse \"%smm\"", value.toLatin1().data());
5590 if (ok != 0)
5591 *ok = false;
5592 return 0;
5593 }
5594 }
5595
5596 if (value.endsWith("in")) {
5597 value.truncate(value.length() - 2);
5598 bool float_ok;
5599 float factor = value.toFloat(&float_ok);
5600 if (float_ok && factor >= 0) {
5601 Q_ASSERT(qApp->desktop() != 0);
5602 QDesktopWidget *dw = qApp->desktop();
5603 Q_ASSERT(dw->width() != 0);
5604 Q_ASSERT(dw->widthMM() != 0);
5605 return (int)(factor*10*dw->width()/(2.54*dw->widthMM()));
5606 }
5607 else {
5608 qWarning("interpretSpacing(): could not parse \"%sin\"", value.toLatin1().data());
5609 if (ok != 0)
5610 *ok = false;
5611 return 0;
5612 }
5613 }
5614
5615 if (value.endsWith("px")) {
5616 value.truncate(value.length() - 2);
5617 bool float_ok;
5618 int i = (int) value.toFloat(&float_ok);
5619 if (float_ok && i >= 0)
5620 return i;
5621 else {
5622 qWarning("interpretSpacing(): could not parse \"%spx\"", value.toLatin1().data());
5623 if (ok != 0)
5624 *ok = false;
5625 return 0;
5626 }
5627 }
5628
5629 bool float_ok;
5630 int i = (int)value.toFloat(&float_ok);
5631 if (float_ok && i >= 0)
5632 return i;
5633
5634 qWarning("interpretSpacing(): could not parse \"%s\"", value.toLatin1().data());
5635 if (ok != 0)
5636 *ok = false;
5637 return 0;
5638 }
5639
interpretPercentSpacing(QString value,int base,bool * ok)5640 static int interpretPercentSpacing(QString value, int base, bool *ok)
5641 {
5642 if (!value.endsWith("%")) {
5643 if (ok != 0)
5644 *ok = false;
5645 return 0;
5646 }
5647
5648 value.truncate(value.length() - 1);
5649 bool float_ok;
5650 float factor = value.toFloat(&float_ok);
5651 if (float_ok && factor >= 0) {
5652 if (ok != 0)
5653 *ok = true;
5654 return (int)(base*factor/100.0);
5655 }
5656
5657 qWarning("interpretPercentSpacing(): could not parse \"%s%%\"", value.toLatin1().data());
5658 if (ok != 0)
5659 *ok = false;
5660 return 0;
5661 }
5662
interpretPointSize(QString value,bool * ok)5663 static int interpretPointSize(QString value, bool *ok)
5664 {
5665 if (!value.endsWith("pt")) {
5666 if (ok != 0)
5667 *ok = false;
5668 return 0;
5669 }
5670
5671 value.truncate(value.length() - 2);
5672 bool float_ok;
5673 int pt_size = (int) value.toFloat(&float_ok);
5674 if (float_ok && pt_size > 0) {
5675 if (ok != 0)
5676 *ok = true;
5677 return pt_size;
5678 }
5679
5680 qWarning("interpretPointSize(): could not parse \"%spt\"", value.toLatin1().data());
5681 if (ok != 0)
5682 *ok = false;
5683 return 0;
5684 }
5685
mmlFindNodeSpec(Mml::NodeType type)5686 static const NodeSpec *mmlFindNodeSpec(Mml::NodeType type)
5687 {
5688 const NodeSpec *spec = g_node_spec_data;
5689 for (; spec->type != Mml::NoNode; ++spec) {
5690 if (type == spec->type) return spec;
5691 }
5692 return 0;
5693 }
5694
mmlFindNodeSpec(const QString & tag)5695 static const NodeSpec *mmlFindNodeSpec(const QString &tag)
5696 {
5697 const NodeSpec *spec = g_node_spec_data;
5698 for (; spec->type != Mml::NoNode; ++spec) {
5699 if (tag == spec->tag) return spec;
5700 }
5701 return 0;
5702 }
5703
mmlCheckChildType(Mml::NodeType parent_type,Mml::NodeType child_type,QString * error_str)5704 static bool mmlCheckChildType(Mml::NodeType parent_type, Mml::NodeType child_type,
5705 QString *error_str)
5706 {
5707 if (parent_type == Mml::UnknownNode || child_type == Mml::UnknownNode)
5708 return true;
5709
5710 const NodeSpec *child_spec = mmlFindNodeSpec(child_type);
5711 const NodeSpec *parent_spec = mmlFindNodeSpec(parent_type);
5712
5713 Q_ASSERT(parent_spec != 0);
5714 Q_ASSERT(child_spec != 0);
5715
5716 QString allowed_child_types(parent_spec->child_types);
5717 // null list means any child type is valid
5718 if (allowed_child_types.isNull())
5719 return true;
5720
5721 QString child_type_str = QString(" ") + child_spec->type_str + " ";
5722 if (!allowed_child_types.contains(child_type_str)) {
5723 if (error_str != 0)
5724 *error_str = QString("illegal child ")
5725 + child_spec->type_str
5726 + " for parent "
5727 + parent_spec->type_str;
5728 return false;
5729 }
5730
5731 return true;
5732 }
5733
mmlCheckAttributes(Mml::NodeType child_type,const MmlAttributeMap & attr,QString * error_str)5734 static bool mmlCheckAttributes(Mml::NodeType child_type, const MmlAttributeMap &attr,
5735 QString *error_str)
5736 {
5737 const NodeSpec *spec = mmlFindNodeSpec(child_type);
5738 Q_ASSERT(spec != 0);
5739
5740 QString allowed_attr(spec->attributes);
5741 // empty list means any attr is valid
5742 if (allowed_attr.isEmpty())
5743 return true;
5744
5745 MmlAttributeMap::const_iterator it = attr.begin(), end = attr.end();
5746 for (; it != end; ++it) {
5747 QString name = it.key();
5748
5749 if (name.indexOf(':') != -1)
5750 continue;
5751
5752 QString padded_name = " " + name + " ";
5753 if (!allowed_attr.contains(padded_name)) {
5754 if (error_str != 0)
5755 *error_str = QString("illegal attribute ")
5756 + name
5757 + " in "
5758 + spec->type_str;
5759 return false;
5760 }
5761 }
5762
5763 return true;
5764 }
5765
attributeIndex(const QString & name)5766 static int attributeIndex(const QString &name)
5767 {
5768 for (unsigned i = 0; i < g_oper_spec_rows; ++i) {
5769 if (name == g_oper_spec_names[i])
5770 return i;
5771 }
5772 return -1;
5773 }
5774
decodeEntityValue(QString literal)5775 static QString decodeEntityValue(QString literal)
5776 {
5777 QString result;
5778
5779 while (!literal.isEmpty()) {
5780
5781 if (!literal.startsWith("&#")) {
5782 qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data());
5783 return QString();
5784 }
5785
5786 literal = literal.right(literal.length() - 2);
5787
5788 int i = literal.indexOf(';');
5789 if (i == -1) {
5790 qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data());
5791 return QString();
5792 }
5793
5794 QString char_code = literal.left(i);
5795 literal = literal.right(literal.length() - i - 1);
5796
5797 if (char_code.isEmpty()) {
5798 qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data());
5799 return QString();
5800 }
5801
5802 if (char_code.at(0) == 'x') {
5803 char_code = char_code.right(char_code.length() - 1);
5804 bool ok;
5805 unsigned c = char_code.toUInt(&ok, 16);
5806 if (!ok) {
5807 qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data());
5808 return QString();
5809 }
5810 result += QChar(c);
5811 }
5812 else {
5813 bool ok;
5814 unsigned c = char_code.toUInt(&ok, 10);
5815 if (!ok) {
5816 qWarning("decodeEntityValue(): bad entity literal: \"%s\"", literal.toLatin1().data());
5817 return QString();
5818 }
5819 result += QChar(c);
5820 }
5821 }
5822
5823 return result;
5824 }
5825
searchEntitySpecData(const QString & value,const EntitySpec * from=0)5826 static const EntitySpec *searchEntitySpecData(const QString &value, const EntitySpec *from = 0)
5827 {
5828 const EntitySpec *ent = from;
5829 if (ent == 0)
5830 ent = g_xml_entity_data;
5831 for (; ent->name != 0; ++ent) {
5832 QString ent_value = decodeEntityValue(ent->value);
5833 if (value == ent_value)
5834 return ent;
5835 }
5836 return 0;
5837 }
5838
5839 struct OperSpecSearchResult
5840 {
OperSpecSearchResultOperSpecSearchResult5841 OperSpecSearchResult() { prefix_form = infix_form = postfix_form = 0; }
5842
5843 const OperSpec *prefix_form,
5844 *infix_form,
5845 *postfix_form;
5846
5847 const OperSpec *&getForm(Mml::FormType f);
haveFormOperSpecSearchResult5848 bool haveForm(Mml::FormType f)
5849 { return getForm(f) != 0; }
addFormOperSpecSearchResult5850 void addForm(const OperSpec *spec)
5851 { getForm(spec->form) = spec; }
5852 };
5853
getForm(Mml::FormType f)5854 const OperSpec *&OperSpecSearchResult::getForm(Mml::FormType f)
5855 {
5856 switch (f) {
5857 case Mml::PrefixForm:
5858 return prefix_form;
5859 case Mml::InfixForm:
5860 return infix_form;
5861 case Mml::PostfixForm:
5862 return postfix_form;
5863 }
5864 return postfix_form; // just to avoid warning
5865 }
5866
5867 /*
5868 Searches g_oper_spec_data and returns any instance of operator name. There may
5869 be more instances, but since the list is sorted, they will be next to each other.
5870 */
searchOperSpecData(const QString & name)5871 static const OperSpec *searchOperSpecData(const QString &name)
5872 {
5873 const char *name_latin1 = name.toLatin1().data();
5874
5875 // binary search
5876 // establish invariant g_oper_spec_data[begin].name < name < g_oper_spec_data[end].name
5877
5878 int cmp = qstrcmp(name_latin1, g_oper_spec_data[0].name);
5879 if (cmp < 0)
5880 return 0;
5881
5882 if (cmp == 0)
5883 return g_oper_spec_data;
5884
5885 uint begin = 0;
5886 uint end = g_oper_spec_count;
5887
5888 // invariant holds
5889 while (end - begin > 1) {
5890 uint mid = (begin + end)/2;
5891
5892 const OperSpec *spec = g_oper_spec_data + mid;
5893 int cmp = qstrcmp(name_latin1, spec->name);
5894 if (cmp < 0)
5895 end = mid;
5896 else if (cmp > 0)
5897 begin = mid;
5898 else
5899 return spec;
5900 }
5901
5902 return 0;
5903 }
5904
5905 /*
5906 This searches g_oper_spec_data until at least one name in name_list is found with FormType form,
5907 or until name_list is exhausted. The idea here is that if we don't find the operator in the
5908 specified form, we still want to use some other available form of that operator.
5909 */
_mmlFindOperSpec(const QStringList & name_list,Mml::FormType form)5910 static OperSpecSearchResult _mmlFindOperSpec(const QStringList &name_list, Mml::FormType form)
5911 {
5912 OperSpecSearchResult result;
5913
5914 QStringList::const_iterator it = name_list.begin();
5915 for (; it != name_list.end(); ++it) {
5916 const QString &name = *it;
5917
5918 const OperSpec *spec = searchOperSpecData(name);
5919
5920 if (spec == 0)
5921 continue;
5922
5923 const char *name_latin1 = name.toLatin1().data();
5924
5925 // backtrack to the first instance of name
5926 while (spec > g_oper_spec_data && qstrcmp((spec - 1)->name, name_latin1) == 0)
5927 --spec;
5928
5929 // iterate over instances of name until the instances are exhausted or until we
5930 // find an instance in the specified form.
5931 do {
5932 result.addForm(spec++);
5933 if (result.haveForm(form))
5934 break;
5935 } while (qstrcmp(spec->name, name_latin1) == 0);
5936
5937 if (result.haveForm(form))
5938 break;
5939 }
5940
5941 return result;
5942 }
5943
5944 /*
5945 text is a string between <mo> and </mo>. It can be a character ('+'), an
5946 entity reference ("∞") or a character reference ("∞"). Our
5947 job is to find an operator spec in the operator dictionary (g_oper_spec_data)
5948 that matches text. Things are further complicated by the fact, that many
5949 operators come in several forms (prefix, infix, postfix).
5950
5951 If available, this function returns an operator spec matching text in the specified
5952 form. If such operator is not available, returns an operator spec that matches
5953 text, but of some other form in the preference order specified by the MathML spec.
5954 If that's not available either, returns the default operator spec.
5955 */
mmlFindOperSpec(const QString & text,Mml::FormType form)5956 static const OperSpec *mmlFindOperSpec(const QString &text, Mml::FormType form)
5957 {
5958 QStringList name_list;
5959 name_list.append(text);
5960
5961 // First, just try to find text in the operator dictionary.
5962 OperSpecSearchResult result = _mmlFindOperSpec(name_list, form);
5963
5964 if (!result.haveForm(form)) {
5965 // Try to find other names for the operator represented by text.
5966
5967 const EntitySpec *ent = 0;
5968 for (;;) {
5969 ent = searchEntitySpecData(text, ent);
5970 if (ent == 0)
5971 break;
5972 name_list.append('&' + QString(ent->name) + ';');
5973 ++ent;
5974 }
5975
5976 result = _mmlFindOperSpec(name_list, form);
5977 }
5978
5979 const OperSpec *spec = result.getForm(form);
5980 if (spec != 0)
5981 return spec;
5982
5983 spec = result.getForm(Mml::InfixForm);
5984 if (spec != 0)
5985 return spec;
5986
5987 spec = result.getForm(Mml::PostfixForm);
5988 if (spec != 0)
5989 return spec;
5990
5991 spec = result.getForm(Mml::PrefixForm);
5992 if (spec != 0)
5993 return spec;
5994
5995 return &g_oper_spec_defaults;
5996 }
5997
mmlDictAttribute(const QString & name,const OperSpec * spec)5998 static QString mmlDictAttribute(const QString &name, const OperSpec *spec)
5999 {
6000 int i = attributeIndex(name);
6001 if (i == -1)
6002 return QString();
6003 else
6004 return spec->attributes[i];
6005 }
6006
interpretMathVariant(const QString & value,bool * ok)6007 static uint interpretMathVariant(const QString &value, bool *ok)
6008 {
6009 struct MathVariantValue {
6010 const char *value;
6011 uint mv;
6012 };
6013
6014 static const MathVariantValue g_mv_data[] =
6015 {
6016 { "normal", Mml::NormalMV },
6017 { "bold", Mml::BoldMV },
6018 { "italic", Mml::ItalicMV },
6019 { "bold-italic", Mml::BoldMV | Mml::ItalicMV },
6020 { "double-struck", Mml::DoubleStruckMV },
6021 { "bold-fraktur", Mml::BoldMV | Mml::FrakturMV },
6022 { "script", Mml::ScriptMV },
6023 { "bold-script", Mml::BoldMV | Mml::ScriptMV },
6024 { "fraktur", Mml::FrakturMV },
6025 { "sans-serif", Mml::SansSerifMV },
6026 { "bold-sans-serif", Mml::BoldMV | Mml::SansSerifMV },
6027 { "sans-serif-italic", Mml::SansSerifMV | Mml::ItalicMV },
6028 { "sans-serif-bold-italic", Mml::SansSerifMV | Mml::ItalicMV | Mml::BoldMV },
6029 { "monospace", Mml::MonospaceMV },
6030 { 0, 0 }
6031 };
6032
6033 const MathVariantValue *v = g_mv_data;
6034 for (; v->value != 0; ++v) {
6035 if (value == v->value) {
6036 if (ok != 0)
6037 *ok = true;
6038 return v->mv;
6039 }
6040 }
6041
6042 if (ok != 0)
6043 *ok = false;
6044
6045 qWarning("interpretMathVariant(): could not parse value: \"%s\"", value.toLatin1().data());
6046
6047 return Mml::NormalMV;
6048 }
6049
interpretForm(const QString & value,bool * ok)6050 static Mml::FormType interpretForm(const QString &value, bool *ok)
6051 {
6052 if (ok != 0)
6053 *ok = true;
6054
6055 if (value == "prefix")
6056 return Mml::PrefixForm;
6057 if (value == "infix")
6058 return Mml::InfixForm;
6059 if (value == "postfix")
6060 return Mml::PostfixForm;
6061
6062 if (ok != 0)
6063 *ok = false;
6064
6065 qWarning("interpretForm(): could not parse value \"%s\"", value.toLatin1().data());
6066 return Mml::InfixForm;
6067 }
6068
interpretColAlign(const QString & value_list,uint colnum,bool * ok)6069 static Mml::ColAlign interpretColAlign(const QString &value_list, uint colnum, bool *ok)
6070 {
6071 QString value = interpretListAttr(value_list, colnum, "center");
6072
6073 if (ok != 0)
6074 *ok = true;
6075
6076 if (value == "left")
6077 return Mml::ColAlignLeft;
6078 if (value == "right")
6079 return Mml::ColAlignRight;
6080 if (value == "center")
6081 return Mml::ColAlignCenter;
6082
6083 if (ok != 0)
6084 *ok = false;
6085
6086 qWarning("interpretColAlign(): could not parse value \"%s\"", value.toLatin1().data());
6087 return Mml::ColAlignCenter;
6088 }
6089
interpretRowAlign(const QString & value_list,uint rownum,bool * ok)6090 static Mml::RowAlign interpretRowAlign(const QString &value_list, uint rownum, bool *ok)
6091 {
6092 QString value = interpretListAttr(value_list, rownum, "axis");
6093
6094 if (ok != 0)
6095 *ok = true;
6096
6097 if (value == "top")
6098 return Mml::RowAlignTop;
6099 if (value == "center")
6100 return Mml::RowAlignCenter;
6101 if (value == "bottom")
6102 return Mml::RowAlignBottom;
6103 if (value == "baseline")
6104 return Mml::RowAlignBaseline;
6105 if (value == "axis")
6106 return Mml::RowAlignAxis;
6107
6108 if (ok != 0)
6109 *ok = false;
6110
6111 qWarning("interpretRowAlign(): could not parse value \"%s\"", value.toLatin1().data());
6112 return Mml::RowAlignAxis;
6113 }
6114
interpretListAttr(const QString & value_list,int idx,const QString & def)6115 static QString interpretListAttr(const QString &value_list, int idx, const QString &def)
6116 {
6117 QStringList l = value_list.split(' ');
6118
6119 if (l.count() == 0)
6120 return def;
6121
6122 if (l.count() <= idx)
6123 return l[l.count() - 1];
6124 else
6125 return l[idx];
6126 }
6127
interpretFrameType(const QString & value_list,uint idx,bool * ok)6128 static Mml::FrameType interpretFrameType(const QString &value_list, uint idx, bool *ok)
6129 {
6130 if (ok != 0)
6131 *ok = true;
6132
6133 QString value = interpretListAttr(value_list, idx, "none");
6134
6135 if (value == "none")
6136 return Mml::FrameNone;
6137 if (value == "solid")
6138 return Mml::FrameSolid;
6139 if (value == "dashed")
6140 return Mml::FrameDashed;
6141
6142 if (ok != 0)
6143 *ok = false;
6144
6145 qWarning("interpretFrameType(): could not parse value \"%s\"", value.toLatin1().data());
6146 return Mml::FrameNone;
6147 }
6148
6149
interpretFrameSpacing(const QString & value_list,int em,int ex,bool * ok)6150 static Mml::FrameSpacing interpretFrameSpacing(const QString &value_list, int em, int ex, bool *ok)
6151 {
6152 Mml::FrameSpacing fs;
6153
6154 QStringList l = value_list.split(' ');
6155 if (l.count() != 2) {
6156 qWarning("interpretFrameSpacing: could not parse value \"%s\"", value_list.toLatin1().data());
6157 if (ok != 0)
6158 *ok = false;
6159 return Mml::FrameSpacing((int)(0.4*em), (int)(0.5*ex));
6160 }
6161
6162 bool hor_ok, ver_ok;
6163 fs.m_hor = interpretSpacing(l[0], em, ex, &hor_ok);
6164 fs.m_ver = interpretSpacing(l[1], em, ex, &ver_ok);
6165
6166 if (ok != 0)
6167 *ok = hor_ok && ver_ok;
6168
6169 return fs;
6170 }
6171
interpretDepreciatedFontAttr(const MmlAttributeMap & font_attr,QFont & fn,int em,int ex)6172 static QFont interpretDepreciatedFontAttr(const MmlAttributeMap &font_attr, QFont &fn, int em, int ex)
6173 {
6174 if (font_attr.contains("fontsize")) {
6175 QString value = font_attr["fontsize"];
6176
6177 for (;;) {
6178
6179 bool ok;
6180 int ptsize = interpretPointSize(value, &ok);
6181 if (ok) {
6182 fn.setPointSize(ptsize);
6183 break;
6184 }
6185
6186 ptsize = interpretPercentSpacing(value, fn.pointSize(), &ok);
6187 if (ok) {
6188 fn.setPointSize(ptsize);
6189 break;
6190 }
6191
6192 int size = interpretSpacing(value, em, ex, &ok);
6193 if (ok) {
6194 fn.setPixelSize(size);
6195 break;
6196 }
6197
6198 break;
6199 }
6200 }
6201
6202 if (font_attr.contains("fontweight")) {
6203 QString value = font_attr["fontweight"];
6204 if (value == "normal")
6205 fn.setBold(false);
6206 else if (value == "bold")
6207 fn.setBold(true);
6208 else
6209 qWarning("interpretDepreciatedFontAttr(): could not parse fontweight \"%s\"", value.toLatin1().data());
6210 }
6211
6212 if (font_attr.contains("fontstyle")) {
6213 QString value = font_attr["fontstyle"];
6214 if (value == "normal")
6215 fn.setItalic(false);
6216 else if (value == "italic")
6217 fn.setItalic(true);
6218 else
6219 qWarning("interpretDepreciatedFontAttr(): could not parse fontstyle \"%s\"", value.toLatin1().data());
6220 }
6221
6222 if (font_attr.contains("fontfamily")) {
6223 QString value = font_attr["fontfamily"];
6224 fn.setFamily(value);
6225 }
6226
6227 return fn;
6228 }
6229
interpretMathSize(QString value,QFont & fn,int em,int ex,bool * ok)6230 static QFont interpretMathSize(QString value, QFont &fn, int em, int ex, bool *ok)
6231 {
6232 if (ok != 0)
6233 *ok = true;
6234
6235 if (value == "small") {
6236 fn.setPointSize((int)(fn.pointSize()*0.7));
6237 return fn;
6238 }
6239
6240 if (value == "normal")
6241 return fn;
6242
6243 if (value == "big") {
6244 fn.setPointSize((int)(fn.pointSize()*1.5));
6245 return fn;
6246 }
6247
6248 bool size_ok;
6249
6250 int ptsize = interpretPointSize(value, &size_ok);
6251 if (size_ok) {
6252 fn.setPointSize(ptsize);
6253 return fn;
6254 }
6255
6256 int size = interpretSpacing(value, em, ex, &size_ok);
6257 if (size_ok) {
6258 fn.setPixelSize(size);
6259 return fn;
6260 }
6261
6262 if (ok != 0)
6263 *ok = false;
6264 qWarning("interpretMathSize(): could not parse mathsize \"%s\"", value.toLatin1().data());
6265 return fn;
6266 }
6267
6268 /*!
6269 \class QtMmlDocument
6270
6271 \brief The QtMmlDocument class renders mathematical formulas written in MathML 2.0.
6272
6273 This class provides a direct API to the rendering engine used by
6274 QtMmlWidget. It can be used to paint MathML inside other widgets.
6275
6276 All methods work the same as the corresponding methods in QtMmlWidget.
6277 */
6278
6279 /*!
6280 Constructs an empty MML document.
6281 */
QtMmlDocument()6282 QtMmlDocument::QtMmlDocument()
6283 {
6284 m_doc = new MmlDocument;
6285 }
6286
6287 /*!
6288 Destroys the MML document.
6289 */
~QtMmlDocument()6290 QtMmlDocument::~QtMmlDocument()
6291 {
6292 delete m_doc;
6293 }
6294
6295 /*!
6296 Clears the contents of this MML document.
6297 */
clear()6298 void QtMmlDocument::clear()
6299 {
6300 m_doc->clear();
6301 }
6302
6303 /*!
6304 Sets the MathML expression to be rendered. The expression is given
6305 in the string \a text. If the expression is successfully parsed,
6306 this method returns true; otherwise it returns false. If an error
6307 occured \a errorMsg is set to a diagnostic message, while \a
6308 errorLine and \a errorColumn contain the location of the error.
6309 Any of \a errorMsg, \a errorLine and \a errorColumn may be 0,
6310 in which case they are not set.
6311
6312 \a text should contain MathML 2.0 presentation markup elements enclosed
6313 in a <math> element.
6314 */
setContent(QString text,QString * errorMsg,int * errorLine,int * errorColumn)6315 bool QtMmlDocument::setContent(QString text, QString *errorMsg,
6316 int *errorLine, int *errorColumn)
6317 {
6318 return m_doc->setContent(text, errorMsg, errorLine, errorColumn);
6319 }
6320
6321 /*!
6322 Renders this MML document with the painter \a p at position \a pos.
6323 */
paint(QPainter * p,const QPoint & pos) const6324 void QtMmlDocument::paint(QPainter *p, const QPoint &pos) const
6325 {
6326 m_doc->paint(p, pos);
6327 }
6328
6329 /*!
6330 Returns the size of this MML document, as rendered, in pixels.
6331 */
size() const6332 QSize QtMmlDocument::size() const
6333 {
6334 return m_doc->size();
6335 }
6336
6337 /*!
6338 Returns the name of the font used to render the font \a type.
6339
6340 \sa setFontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
6341 */
fontName(QtMmlWidget::MmlFont type) const6342 QString QtMmlDocument::fontName(QtMmlWidget::MmlFont type) const
6343 {
6344 return m_doc->fontName(type);
6345 }
6346
6347 /*!
6348 Sets the name of the font used to render the font \a type to \a name.
6349
6350 \sa fontName() setBaseFontPointSize() baseFontPointSize() QtMmlWidget::MmlFont
6351 */
setFontName(QtMmlWidget::MmlFont type,const QString & name)6352 void QtMmlDocument::setFontName(QtMmlWidget::MmlFont type, const QString &name)
6353 {
6354 m_doc->setFontName(type, name);
6355 m_doc->layout();
6356 }
6357
6358 /*!
6359 Returns the point size of the font used to render expressions
6360 whose scriptlevel is 0.
6361
6362 \sa setBaseFontPointSize() fontName() setFontName()
6363 */
baseFontPointSize() const6364 int QtMmlDocument::baseFontPointSize() const
6365 {
6366 return m_doc->baseFontPointSize();
6367 }
6368
6369 /*!
6370 Sets the point \a size of the font used to render expressions
6371 whose scriptlevel is 0.
6372
6373 \sa baseFontPointSize() fontName() setFontName()
6374 */
setBaseFontPointSize(int size)6375 void QtMmlDocument::setBaseFontPointSize(int size)
6376 {
6377 m_doc->setBaseFontPointSize(size);
6378 m_doc->layout();
6379 }
6380