1 /*
2 blahtex: a TeX to MathML converter designed with MediaWiki in mind
3 blahtexml: an extension of blahtex with XML processing in mind
4 http://gva.noekeon.org/blahtexml
5 
6 Copyright (c) 2006, David Harvey
7 All rights reserved.
8 
9 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10 
11     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
13     * Neither the names of the authors nor the names of their affiliation may be used to endorse or promote products derived from this software without specific prior written permission.
14 
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 */
17 
18 #include <stdexcept>
19 #include "ParseTree.h"
20 
21 using namespace std;
22 
23 namespace blahtex
24 {
25 
26 pair<wstring, wchar_t> lowercaseGreekArray[] =
27 {
28     make_pair(L"\\alpha",      L'\U000003B1'),
29     make_pair(L"\\beta",       L'\U000003B2'),
30     make_pair(L"\\gamma",      L'\U000003B3'),
31     make_pair(L"\\delta",      L'\U000003B4'),
32     make_pair(L"\\epsilon",    L'\U000003F5'),  // straightepsilon
33     make_pair(L"\\varepsilon", L'\U000003B5'),  // varepsilon
34     make_pair(L"\\zeta",       L'\U000003B6'),
35     make_pair(L"\\eta",        L'\U000003B7'),
36     make_pair(L"\\theta",      L'\U000003B8'),
37     make_pair(L"\\vartheta",   L'\U000003D1'),
38     make_pair(L"\\iota",       L'\U000003B9'),
39     make_pair(L"\\kappa",      L'\U000003BA'),
40     make_pair(L"\\varkappa",   L'\U000003F0'),
41     make_pair(L"\\lambda",     L'\U000003BB'),
42     make_pair(L"\\mu",         L'\U000003BC'),
43     make_pair(L"\\nu",         L'\U000003BD'),
44     make_pair(L"\\pi",         L'\U000003C0'),
45     make_pair(L"\\varpi",      L'\U000003D6'),
46     make_pair(L"\\rho",        L'\U000003C1'),
47     make_pair(L"\\varrho",     L'\U000003F1'),
48     make_pair(L"\\sigma",      L'\U000003C3'),
49     make_pair(L"\\varsigma",   L'\U000003C2'),
50     make_pair(L"\\tau",        L'\U000003C4'),
51     make_pair(L"\\upsilon",    L'\U000003C5'),
52     make_pair(L"\\phi",        L'\U000003D5'),  // straightphi
53     make_pair(L"\\varphi",     L'\U000003C6'),
54     make_pair(L"\\chi",        L'\U000003C7'),
55     make_pair(L"\\psi",        L'\U000003C8'),
56     make_pair(L"\\omega",      L'\U000003C9'),
57     make_pair(L"\\xi",         L'\U000003BE'),
58     make_pair(L"\\digamma",    L'\U000003DD')
59 };
60 wishful_hash_map<wstring, wchar_t> lowercaseGreekTable(
61     lowercaseGreekArray,
62     END_ARRAY(lowercaseGreekArray)
63 );
64 
65 
66 pair<wstring, wchar_t> uppercaseGreekArray[] =
67 {
68     make_pair(L"\\Gamma",     L'\U00000393'),
69     make_pair(L"\\Delta",     L'\U00000394'),
70     make_pair(L"\\Theta",     L'\U00000398'),
71     make_pair(L"\\Lambda",    L'\U0000039B'),
72     make_pair(L"\\Pi",        L'\U000003A0'),
73     make_pair(L"\\Sigma",     L'\U000003A3'),
74     make_pair(L"\\Upsilon",   L'\U000003A5'),
75     make_pair(L"\\Phi",       L'\U000003A6'),
76     make_pair(L"\\Psi",       L'\U000003A8'),
77     make_pair(L"\\Omega",     L'\U000003A9'),
78     make_pair(L"\\Xi",        L'\U0000039E')
79 };
80 wishful_hash_map<wstring, wchar_t> uppercaseGreekTable(
81     uppercaseGreekArray,
82     END_ARRAY(uppercaseGreekArray)
83 );
84 
85 
86 pair<wstring, int> spaceArray[] =
87 {
88     make_pair(L"\\!",       -3),
89     make_pair(L"\\,",       3),
90     make_pair(L"\\>",       4),
91     make_pair(L"\\;",       5),
92     make_pair(L"\\quad",    18),
93     make_pair(L"\\qquad",   36),
94     // These last two aren't quite right, but hopefully they're close
95     // enough. TeX's rules are too complicated for me to care :-)
96     make_pair(L"~",         6),
97     make_pair(L"\\ ",       6)
98 };
99 wishful_hash_map<wstring, int> spaceTable(
100     spaceArray,
101     END_ARRAY(spaceArray)
102 );
103 
104 
105 struct OperatorInfo
106 {
107     wstring mText;
108     LayoutTree::Node::Flavour mFlavour;
109     LayoutTree::Node::Limits mLimits;
110 
OperatorInfoblahtex::OperatorInfo111     OperatorInfo(
112         const wstring& text,
113         LayoutTree::Node::Flavour flavour,
114         LayoutTree::Node::Limits limits =
115             LayoutTree::Node::cLimitsDisplayLimits
116     ) :
117         mText(text),
118         mFlavour(flavour),
119         mLimits(limits)
120     { }
121 };
122 
123 // Here is a list of all commands that get translated as operators,
124 // together with their MathML translation and flavour.
125 pair<wstring, OperatorInfo> operatorArray[] =
126 {
127     make_pair(L"(",                      OperatorInfo(L"(", LayoutTree::Node::cFlavourOpen)),
128     make_pair(L")",                      OperatorInfo(L")", LayoutTree::Node::cFlavourClose)),
129     make_pair(L"[",                      OperatorInfo(L"[", LayoutTree::Node::cFlavourOpen)),
130     make_pair(L"]",                      OperatorInfo(L"]", LayoutTree::Node::cFlavourClose)),
131     make_pair(L"<",                      OperatorInfo(L"<", LayoutTree::Node::cFlavourRel)),
132     make_pair(L">",                      OperatorInfo(L">", LayoutTree::Node::cFlavourRel)),
133     make_pair(L"+",                      OperatorInfo(L"+", LayoutTree::Node::cFlavourBin)),
134     make_pair(L"-",                      OperatorInfo(L"-", LayoutTree::Node::cFlavourBin)),
135     make_pair(L"=",                      OperatorInfo(L"=", LayoutTree::Node::cFlavourRel)),
136     make_pair(L"|",                      OperatorInfo(L"|", LayoutTree::Node::cFlavourOrd)),
137     make_pair(L";",                      OperatorInfo(L";", LayoutTree::Node::cFlavourPunct)),
138     make_pair(L":",                      OperatorInfo(L":", LayoutTree::Node::cFlavourRel)),
139     make_pair(L",",                      OperatorInfo(L",", LayoutTree::Node::cFlavourPunct)),
140     make_pair(L".",                      OperatorInfo(L".", LayoutTree::Node::cFlavourOrd)),
141     make_pair(L"/",                      OperatorInfo(L"/", LayoutTree::Node::cFlavourOrd)),
142     make_pair(L"?",                      OperatorInfo(L"?", LayoutTree::Node::cFlavourClose)),
143     make_pair(L"!",                      OperatorInfo(L"!", LayoutTree::Node::cFlavourClose)),
144     make_pair(L"@",                      OperatorInfo(L"@", LayoutTree::Node::cFlavourOrd)),
145     make_pair(L"*",                      OperatorInfo(L"*", LayoutTree::Node::cFlavourBin)),
146     make_pair(L"\\_",                    OperatorInfo(L"_", LayoutTree::Node::cFlavourOrd)),
147     make_pair(L"\\&",                    OperatorInfo(L"&", LayoutTree::Node::cFlavourOrd)),
148     make_pair(L"\\$",                    OperatorInfo(L"$", LayoutTree::Node::cFlavourOrd)),
149     make_pair(L"\\#",                    OperatorInfo(L"#", LayoutTree::Node::cFlavourOrd)),
150     make_pair(L"\\%",                    OperatorInfo(L"%", LayoutTree::Node::cFlavourOrd)),
151     make_pair(L"\\{",                    OperatorInfo(L"{", LayoutTree::Node::cFlavourOpen)),
152     make_pair(L"\\}",                    OperatorInfo(L"}", LayoutTree::Node::cFlavourClose)),
153     make_pair(L"\\ast",                  OperatorInfo(L"*", LayoutTree::Node::cFlavourBin)),
154     make_pair(L"\\lbrace",               OperatorInfo(L"{", LayoutTree::Node::cFlavourOpen)),
155     make_pair(L"\\rbrace",               OperatorInfo(L"}", LayoutTree::Node::cFlavourClose)),
156     make_pair(L"\\vert",                 OperatorInfo(L"|", LayoutTree::Node::cFlavourOrd)),
157     make_pair(L"\\lvert",                OperatorInfo(L"|", LayoutTree::Node::cFlavourOpen)),
158     make_pair(L"\\rvert",                OperatorInfo(L"|", LayoutTree::Node::cFlavourClose)),
159     make_pair(L"\\lbrack",               OperatorInfo(L"[", LayoutTree::Node::cFlavourOpen)),
160     make_pair(L"\\rbrack",               OperatorInfo(L"]", LayoutTree::Node::cFlavourClose)),
161     make_pair(L"\\Vert",                 OperatorInfo(L"\U00002225", LayoutTree::Node::cFlavourOrd)),
162     make_pair(L"\\lVert",                OperatorInfo(L"\U00002225", LayoutTree::Node::cFlavourOpen)),
163     make_pair(L"\\rVert",                OperatorInfo(L"\U00002225", LayoutTree::Node::cFlavourClose)),
164     make_pair(L"\\lfloor",               OperatorInfo(L"\U0000230A", LayoutTree::Node::cFlavourOpen)),
165     make_pair(L"\\rfloor",               OperatorInfo(L"\U0000230B", LayoutTree::Node::cFlavourClose)),
166     make_pair(L"\\lceil",                OperatorInfo(L"\U00002308", LayoutTree::Node::cFlavourOpen)),
167     make_pair(L"\\rceil",                OperatorInfo(L"\U00002309", LayoutTree::Node::cFlavourClose)),
168     make_pair(L"\\langle",               OperatorInfo(L"\U00002329", LayoutTree::Node::cFlavourOpen)),
169     make_pair(L"\\rangle",               OperatorInfo(L"\U0000232A", LayoutTree::Node::cFlavourClose)),
170     make_pair(L"\\forall",               OperatorInfo(L"\U00002200", LayoutTree::Node::cFlavourOrd)),
171     make_pair(L"\\exists",               OperatorInfo(L"\U00002203", LayoutTree::Node::cFlavourOrd)),
172     make_pair(L"\\leftarrow",            OperatorInfo(L"\U00002190", LayoutTree::Node::cFlavourRel)),
173     make_pair(L"\\rightarrow",           OperatorInfo(L"\U00002192", LayoutTree::Node::cFlavourRel)),
174 
175     // FIX: The first version below has the correct MathML characters.
176     // They seem to be missing in the fonts currently shipped with
177     // Firefox, so we just map them to their short counterparts (second
178     // version) for the moment.
179     // FIX: perhaps it's possible to do this with the "stretchy" attribute
180     // instead?
181 #if 0
182     make_pair(L"\\longleftarrow",        OperatorInfo(L"\U000027F5", LayoutTree::Node::cFlavourRel)),
183     make_pair(L"\\longrightarrow",       OperatorInfo(L"\U000027F6", LayoutTree::Node::cFlavourRel)),
184     make_pair(L"\\Longleftarrow",        OperatorInfo(L"\U000027F8", LayoutTree::Node::cFlavourRel)),
185     make_pair(L"\\Longrightarrow",       OperatorInfo(L"\U000027F9", LayoutTree::Node::cFlavourRel)),
186     make_pair(L"\\longmapsto",           OperatorInfo(L"\U000027FC", LayoutTree::Node::cFlavourRel)),
187     make_pair(L"\\longleftrightarrow",   OperatorInfo(L"\U000027F7", LayoutTree::Node::cFlavourRel)),
188     make_pair(L"\\Longleftrightarrow",   OperatorInfo(L"\U000027FA", LayoutTree::Node::cFlavourRel)),
189 #else
190     make_pair(L"\\longleftarrow",        OperatorInfo(L"\U00002190", LayoutTree::Node::cFlavourRel)),
191     make_pair(L"\\longrightarrow",       OperatorInfo(L"\U00002192", LayoutTree::Node::cFlavourRel)),
192     make_pair(L"\\Longleftarrow",        OperatorInfo(L"\U000021D0", LayoutTree::Node::cFlavourRel)),
193     make_pair(L"\\Longrightarrow",       OperatorInfo(L"\U000021D2", LayoutTree::Node::cFlavourRel)),
194     make_pair(L"\\longmapsto",           OperatorInfo(L"\U000021A6", LayoutTree::Node::cFlavourRel)),
195     make_pair(L"\\longleftrightarrow",   OperatorInfo(L"\U00002194", LayoutTree::Node::cFlavourRel)),
196     make_pair(L"\\Longleftrightarrow",   OperatorInfo(L"\U000021D4", LayoutTree::Node::cFlavourRel)),
197 #endif
198 
199     make_pair(L"\\Leftarrow",            OperatorInfo(L"\U000021D0", LayoutTree::Node::cFlavourRel)),
200     make_pair(L"\\Rightarrow",           OperatorInfo(L"\U000021D2", LayoutTree::Node::cFlavourRel)),
201     make_pair(L"\\mapsto",               OperatorInfo(L"\U000021A6", LayoutTree::Node::cFlavourRel)),
202     make_pair(L"\\leftrightarrow",       OperatorInfo(L"\U00002194", LayoutTree::Node::cFlavourRel)),
203     make_pair(L"\\Leftrightarrow",       OperatorInfo(L"\U000021D4", LayoutTree::Node::cFlavourRel)),
204     make_pair(L"\\uparrow",              OperatorInfo(L"\U00002191", LayoutTree::Node::cFlavourRel)),
205     make_pair(L"\\Uparrow",              OperatorInfo(L"\U000021D1", LayoutTree::Node::cFlavourRel)),
206     make_pair(L"\\downarrow",            OperatorInfo(L"\U00002193", LayoutTree::Node::cFlavourRel)),
207     make_pair(L"\\Downarrow",            OperatorInfo(L"\U000021D3", LayoutTree::Node::cFlavourRel)),
208     make_pair(L"\\updownarrow",          OperatorInfo(L"\U00002195", LayoutTree::Node::cFlavourRel)),
209     make_pair(L"\\Updownarrow",          OperatorInfo(L"\U000021D5", LayoutTree::Node::cFlavourRel)),
210     make_pair(L"\\searrow",              OperatorInfo(L"\U00002198", LayoutTree::Node::cFlavourRel)),
211     make_pair(L"\\nearrow",              OperatorInfo(L"\U00002197", LayoutTree::Node::cFlavourRel)),
212     make_pair(L"\\swarrow",              OperatorInfo(L"\U00002199", LayoutTree::Node::cFlavourRel)),
213     make_pair(L"\\nwarrow",              OperatorInfo(L"\U00002196", LayoutTree::Node::cFlavourRel)),
214     make_pair(L"\\hookrightarrow",       OperatorInfo(L"\U000021AA", LayoutTree::Node::cFlavourRel)),
215     make_pair(L"\\hookleftarrow",        OperatorInfo(L"\U000021A9", LayoutTree::Node::cFlavourRel)),
216     make_pair(L"\\upharpoonright",       OperatorInfo(L"\U000021BE", LayoutTree::Node::cFlavourRel)),
217     make_pair(L"\\upharpoonleft",        OperatorInfo(L"\U000021BF", LayoutTree::Node::cFlavourRel)),
218     make_pair(L"\\downharpoonright",     OperatorInfo(L"\U000021C2", LayoutTree::Node::cFlavourRel)),
219     make_pair(L"\\downharpoonleft",      OperatorInfo(L"\U000021C3", LayoutTree::Node::cFlavourRel)),
220     make_pair(L"\\rightharpoonup",       OperatorInfo(L"\U000021C0", LayoutTree::Node::cFlavourRel)),
221     make_pair(L"\\rightharpoondown",     OperatorInfo(L"\U000021C1", LayoutTree::Node::cFlavourRel)),
222     make_pair(L"\\leftharpoonup",        OperatorInfo(L"\U000021BC", LayoutTree::Node::cFlavourRel)),
223     make_pair(L"\\leftharpoondown",      OperatorInfo(L"\U000021BD", LayoutTree::Node::cFlavourRel)),
224     make_pair(L"\\nleftarrow",           OperatorInfo(L"\U0000219A", LayoutTree::Node::cFlavourRel)),
225     make_pair(L"\\nrightarrow",          OperatorInfo(L"\U0000219B", LayoutTree::Node::cFlavourRel)),
226     make_pair(L"\\supset",               OperatorInfo(L"\U00002283", LayoutTree::Node::cFlavourRel)),
227     make_pair(L"\\subset",               OperatorInfo(L"\U00002282", LayoutTree::Node::cFlavourRel)),
228     make_pair(L"\\supseteq",             OperatorInfo(L"\U00002287", LayoutTree::Node::cFlavourRel)),
229     make_pair(L"\\subseteq",             OperatorInfo(L"\U00002286", LayoutTree::Node::cFlavourRel)),
230     make_pair(L"\\sqsupset",             OperatorInfo(L"\U00002290", LayoutTree::Node::cFlavourRel)),
231     make_pair(L"\\sqsubset",             OperatorInfo(L"\U0000228F", LayoutTree::Node::cFlavourRel)),
232     make_pair(L"\\sqsupseteq",           OperatorInfo(L"\U00002292", LayoutTree::Node::cFlavourRel)),
233     make_pair(L"\\sqsubseteq",           OperatorInfo(L"\U00002291", LayoutTree::Node::cFlavourRel)),
234     make_pair(L"\\supsetneq",            OperatorInfo(L"\U0000228B", LayoutTree::Node::cFlavourRel)),
235     make_pair(L"\\subsetneq",            OperatorInfo(L"\U0000228A", LayoutTree::Node::cFlavourRel)),
236     make_pair(L"\\in",                   OperatorInfo(L"\U00002208", LayoutTree::Node::cFlavourRel)),
237     make_pair(L"\\ni",                   OperatorInfo(L"\U0000220B", LayoutTree::Node::cFlavourRel)),
238     make_pair(L"\\notin",                OperatorInfo(L"\U00002209", LayoutTree::Node::cFlavourRel)),
239     make_pair(L"\\mid",                  OperatorInfo(L"|",          LayoutTree::Node::cFlavourRel)),
240     make_pair(L"\\sim",                  OperatorInfo(L"\U0000223C", LayoutTree::Node::cFlavourRel)),
241     make_pair(L"\\simeq",                OperatorInfo(L"\U00002243", LayoutTree::Node::cFlavourRel)),
242     make_pair(L"\\approx",               OperatorInfo(L"\U00002248", LayoutTree::Node::cFlavourRel)),
243     make_pair(L"\\propto",               OperatorInfo(L"\U0000221D", LayoutTree::Node::cFlavourRel)),
244     make_pair(L"\\equiv",                OperatorInfo(L"\U00002261", LayoutTree::Node::cFlavourRel)),
245     make_pair(L"\\cong",                 OperatorInfo(L"\U00002245", LayoutTree::Node::cFlavourRel)),
246     make_pair(L"\\neq",                  OperatorInfo(L"\U00002260", LayoutTree::Node::cFlavourRel)),
247     make_pair(L"\\ll",                   OperatorInfo(L"\U0000226A", LayoutTree::Node::cFlavourRel)),
248     make_pair(L"\\gg",                   OperatorInfo(L"\U0000226B", LayoutTree::Node::cFlavourRel)),
249     make_pair(L"\\geq",                  OperatorInfo(L"\U00002265", LayoutTree::Node::cFlavourRel)),
250     make_pair(L"\\leq",                  OperatorInfo(L"\U00002264", LayoutTree::Node::cFlavourRel)),
251     make_pair(L"\\triangleleft",         OperatorInfo(L"\U000025C3", LayoutTree::Node::cFlavourBin)),
252     make_pair(L"\\triangleright",        OperatorInfo(L"\U000025B9", LayoutTree::Node::cFlavourBin)),
253     make_pair(L"\\models",               OperatorInfo(L"\U000022A7", LayoutTree::Node::cFlavourRel)),
254     make_pair(L"\\vdash",                OperatorInfo(L"\U000022A2", LayoutTree::Node::cFlavourRel)),
255     make_pair(L"\\Vdash",                OperatorInfo(L"\U000022A9", LayoutTree::Node::cFlavourRel)),
256     make_pair(L"\\vDash",                OperatorInfo(L"\U000022A8", LayoutTree::Node::cFlavourRel)),
257     make_pair(L"\\lesssim",              OperatorInfo(L"\U00002272", LayoutTree::Node::cFlavourRel)),
258     make_pair(L"\\nless",                OperatorInfo(L"\U0000226E", LayoutTree::Node::cFlavourRel)),
259     make_pair(L"\\ngeq",                 OperatorInfo(L"\U00002271", LayoutTree::Node::cFlavourRel)),
260     make_pair(L"\\nleq",                 OperatorInfo(L"\U00002270", LayoutTree::Node::cFlavourRel)),
261 
262     // FIX: the fonts shipped with Firefox 1.5 don't know about
263     // 0x2a2f (&Cross;). So I'm mapping it to 0xd7 (&times;) for now.
264 #if 0
265     make_pair(L"\\times",                OperatorInfo(L"\U00002A2F", LayoutTree::Node::cFlavourBin)),
266 #else
267     make_pair(L"\\times",                OperatorInfo(L"\U000000D7", LayoutTree::Node::cFlavourBin)),
268 #endif
269 
270     make_pair(L"\\div",                  OperatorInfo(L"\U000000F7", LayoutTree::Node::cFlavourBin)),
271     make_pair(L"\\wedge",                OperatorInfo(L"\U00002227", LayoutTree::Node::cFlavourBin)),
272     make_pair(L"\\vee",                  OperatorInfo(L"\U00002228", LayoutTree::Node::cFlavourBin)),
273     make_pair(L"\\oplus",                OperatorInfo(L"\U00002295", LayoutTree::Node::cFlavourBin)),
274     make_pair(L"\\otimes",               OperatorInfo(L"\U00002297", LayoutTree::Node::cFlavourBin)),
275     make_pair(L"\\cap",                  OperatorInfo(L"\U00002229", LayoutTree::Node::cFlavourBin)),
276     make_pair(L"\\cup",                  OperatorInfo(L"\U0000222A", LayoutTree::Node::cFlavourBin)),
277     make_pair(L"\\sqcap",                OperatorInfo(L"\U00002293", LayoutTree::Node::cFlavourBin)),
278     make_pair(L"\\sqcup",                OperatorInfo(L"\U00002294", LayoutTree::Node::cFlavourBin)),
279     make_pair(L"\\smile",                OperatorInfo(L"\U00002323", LayoutTree::Node::cFlavourRel)),
280     make_pair(L"\\frown",                OperatorInfo(L"\U00002322", LayoutTree::Node::cFlavourRel)),
281     // FIX: how to make these smiles/frowns smaller?
282     make_pair(L"\\smallsmile",           OperatorInfo(L"\U00002323", LayoutTree::Node::cFlavourRel)),
283     make_pair(L"\\smallfrown",           OperatorInfo(L"\U00002322", LayoutTree::Node::cFlavourRel)),
284     make_pair(L"\\setminus",             OperatorInfo(L"\U00002216", LayoutTree::Node::cFlavourBin)),
285     // FIX: how to make smallsetminus smaller?
286     make_pair(L"\\smallsetminus",        OperatorInfo(L"\U00002216", LayoutTree::Node::cFlavourBin)),
287     make_pair(L"\\star",                 OperatorInfo(L"\U000022C6", LayoutTree::Node::cFlavourBin)),
288     make_pair(L"\\triangle",             OperatorInfo(L"\U000025B3", LayoutTree::Node::cFlavourOrd)),
289     make_pair(L"\\wr",                   OperatorInfo(L"\U00002240", LayoutTree::Node::cFlavourBin)),
290     make_pair(L"\\circ",                 OperatorInfo(L"\U00002218", LayoutTree::Node::cFlavourBin)),
291     make_pair(L"\\lnot",                 OperatorInfo(L"\U000000AC", LayoutTree::Node::cFlavourOrd)),
292     make_pair(L"\\nabla",                OperatorInfo(L"\U00002207", LayoutTree::Node::cFlavourOrd)),
293     make_pair(L"\\prime",                OperatorInfo(L"\U00002032", LayoutTree::Node::cFlavourOrd)),
294     make_pair(L"\\backslash",            OperatorInfo(L"\U00002216", LayoutTree::Node::cFlavourOrd)),
295     make_pair(L"\\pm",                   OperatorInfo(L"\U000000B1", LayoutTree::Node::cFlavourBin)),
296     make_pair(L"\\mp",                   OperatorInfo(L"\U00002213", LayoutTree::Node::cFlavourBin)),
297     make_pair(L"\\angle",                OperatorInfo(L"\U00002220", LayoutTree::Node::cFlavourOrd)),
298     make_pair(L"\\nmid",                 OperatorInfo(L"\U00002224", LayoutTree::Node::cFlavourRel)),
299     make_pair(L"\\square",               OperatorInfo(L"\U000025A1", LayoutTree::Node::cFlavourOrd)),
300     make_pair(L"\\Box",                  OperatorInfo(L"\U000025A1", LayoutTree::Node::cFlavourOrd)),
301     make_pair(L"\\checkmark",            OperatorInfo(L"\U00002713", LayoutTree::Node::cFlavourOrd)),
302     make_pair(L"\\complement",           OperatorInfo(L"\U00002201", LayoutTree::Node::cFlavourOrd)),
303     make_pair(L"\\flat",                 OperatorInfo(L"\U0000266D", LayoutTree::Node::cFlavourOrd)),
304     make_pair(L"\\sharp",                OperatorInfo(L"\U0000266F", LayoutTree::Node::cFlavourOrd)),
305     make_pair(L"\\natural",              OperatorInfo(L"\U0000266E", LayoutTree::Node::cFlavourOrd)),
306     make_pair(L"\\bullet",               OperatorInfo(L"\U00002022", LayoutTree::Node::cFlavourBin)),
307     make_pair(L"\\dagger",               OperatorInfo(L"\U00002020", LayoutTree::Node::cFlavourBin)),
308     make_pair(L"\\ddagger",              OperatorInfo(L"\U00002021", LayoutTree::Node::cFlavourBin)),
309     make_pair(L"\\clubsuit",             OperatorInfo(L"\U00002663", LayoutTree::Node::cFlavourOrd)),
310     make_pair(L"\\spadesuit",            OperatorInfo(L"\U00002660", LayoutTree::Node::cFlavourOrd)),
311     make_pair(L"\\heartsuit",            OperatorInfo(L"\U00002665", LayoutTree::Node::cFlavourOrd)),
312     make_pair(L"\\diamondsuit",          OperatorInfo(L"\U00002666", LayoutTree::Node::cFlavourOrd)),
313     make_pair(L"\\top",                  OperatorInfo(L"\U000022A4", LayoutTree::Node::cFlavourOrd)),
314     make_pair(L"\\bot",                  OperatorInfo(L"\U000022A5", LayoutTree::Node::cFlavourOrd)),
315     make_pair(L"\\perp",                 OperatorInfo(L"\U000022A5", LayoutTree::Node::cFlavourRel)),
316     make_pair(L"\\cdot",                 OperatorInfo(L"\U000022C5", LayoutTree::Node::cFlavourBin)),
317     make_pair(L"\\vdots",                OperatorInfo(L"\U000022EE", LayoutTree::Node::cFlavourOrd)),
318     make_pair(L"\\ddots",                OperatorInfo(L"\U000022F1", LayoutTree::Node::cFlavourInner)),
319     make_pair(L"\\cdots",                OperatorInfo(L"\U000022EF", LayoutTree::Node::cFlavourInner)),
320     make_pair(L"\\ldots",                OperatorInfo(L"\U00002026", LayoutTree::Node::cFlavourInner)),
321     // FIX: these next two aren't right. The amsmath package does tricky
322     // things so that the dots change their vertical position depending
323     // on the surrounding operators. We chicken out and just map them
324     // to the same as \cdots and \ldots respectively.
325     make_pair(L"\\dotsb",                OperatorInfo(L"\U000022EF", LayoutTree::Node::cFlavourInner)),
326     make_pair(L"\\dots",                 OperatorInfo(L"\U00002026", LayoutTree::Node::cFlavourInner)),
327     make_pair(L"\\sum",                  OperatorInfo(L"\U00002211", LayoutTree::Node::cFlavourOp)),
328     make_pair(L"\\prod",                 OperatorInfo(L"\U0000220F", LayoutTree::Node::cFlavourOp)),
329     make_pair(L"\\int",                  OperatorInfo(L"\U0000222B", LayoutTree::Node::cFlavourOp, LayoutTree::Node::cLimitsNoLimits)),
330     make_pair(L"\\iint",                 OperatorInfo(L"\U0000222C", LayoutTree::Node::cFlavourOp, LayoutTree::Node::cLimitsNoLimits)),
331     make_pair(L"\\iiint",                OperatorInfo(L"\U0000222D", LayoutTree::Node::cFlavourOp, LayoutTree::Node::cLimitsNoLimits)),
332     make_pair(L"\\iiiint",               OperatorInfo(L"\U00002A0C", LayoutTree::Node::cFlavourOp, LayoutTree::Node::cLimitsNoLimits)),
333     make_pair(L"\\oint",                 OperatorInfo(L"\U0000222E", LayoutTree::Node::cFlavourOp, LayoutTree::Node::cLimitsNoLimits)),
334     make_pair(L"\\bigcap",               OperatorInfo(L"\U000022C2", LayoutTree::Node::cFlavourOp)),
335     make_pair(L"\\bigodot",              OperatorInfo(L"\U00002A00", LayoutTree::Node::cFlavourOp)),
336     make_pair(L"\\bigcup",               OperatorInfo(L"\U000022C3", LayoutTree::Node::cFlavourOp)),
337     make_pair(L"\\bigotimes",            OperatorInfo(L"\U00002A02", LayoutTree::Node::cFlavourOp)),
338     make_pair(L"\\coprod",               OperatorInfo(L"\U00002210", LayoutTree::Node::cFlavourOp)),
339     make_pair(L"\\bigsqcup",             OperatorInfo(L"\U00002A06", LayoutTree::Node::cFlavourOp)),
340     make_pair(L"\\bigoplus",             OperatorInfo(L"\U00002A01", LayoutTree::Node::cFlavourOp)),
341     make_pair(L"\\bigvee",               OperatorInfo(L"\U000022C1", LayoutTree::Node::cFlavourOp)),
342     make_pair(L"\\biguplus",             OperatorInfo(L"\U00002A04", LayoutTree::Node::cFlavourOp)),
343     make_pair(L"\\bigwedge",             OperatorInfo(L"\U000022C0", LayoutTree::Node::cFlavourOp)),
344     make_pair(L"\\ulcorner",             OperatorInfo(L"\U0000231C", LayoutTree::Node::cFlavourOrd)),
345     make_pair(L"\\urcorner",             OperatorInfo(L"\U0000231D", LayoutTree::Node::cFlavourOrd)),
346     make_pair(L"\\llcorner",             OperatorInfo(L"\U0000231E", LayoutTree::Node::cFlavourOrd)),
347     make_pair(L"\\lrcorner",             OperatorInfo(L"\U0000231F", LayoutTree::Node::cFlavourOrd)),
348     make_pair(L"\\dashrightarrow",       OperatorInfo(L"\U0000290F", LayoutTree::Node::cFlavourRel)),
349     make_pair(L"\\dashleftarrow",        OperatorInfo(L"\U0000290E", LayoutTree::Node::cFlavourRel)),
350     make_pair(L"\\backprime",            OperatorInfo(L"\U00002035", LayoutTree::Node::cFlavourOrd)),
351     make_pair(L"\\vartriangle",          OperatorInfo(L"\U000025B5", LayoutTree::Node::cFlavourRel)),
352     make_pair(L"\\blacktriangle",        OperatorInfo(L"\U000025B4", LayoutTree::Node::cFlavourOrd)),
353     make_pair(L"\\triangledown",         OperatorInfo(L"\U000025BF", LayoutTree::Node::cFlavourOrd)),
354     make_pair(L"\\blacktriangledown",    OperatorInfo(L"\U000025BE", LayoutTree::Node::cFlavourOrd)),
355     make_pair(L"\\blacksquare",          OperatorInfo(L"\U000025FC", LayoutTree::Node::cFlavourOrd)),
356     make_pair(L"\\lozenge",              OperatorInfo(L"\U000025CA", LayoutTree::Node::cFlavourOrd)),
357     make_pair(L"\\blacklozenge",         OperatorInfo(L"\U000029EB", LayoutTree::Node::cFlavourOrd)),
358     make_pair(L"\\bigstar",              OperatorInfo(L"\U00002605", LayoutTree::Node::cFlavourOrd)),
359     make_pair(L"\\sphericalangle",       OperatorInfo(L"\U00002222", LayoutTree::Node::cFlavourOrd)),
360     make_pair(L"\\measuredangle",        OperatorInfo(L"\U00002221", LayoutTree::Node::cFlavourOrd)),
361     make_pair(L"\\dotplus",              OperatorInfo(L"\U00002214", LayoutTree::Node::cFlavourOrd)),
362     make_pair(L"\\ltimes",               OperatorInfo(L"\U000022C9", LayoutTree::Node::cFlavourBin)),
363     make_pair(L"\\rtimes",               OperatorInfo(L"\U000022CA", LayoutTree::Node::cFlavourBin)),
364     make_pair(L"\\Cap",                  OperatorInfo(L"\U000022D2", LayoutTree::Node::cFlavourBin)),
365     make_pair(L"\\leftthreetimes",       OperatorInfo(L"\U000022CB", LayoutTree::Node::cFlavourBin)),
366     make_pair(L"\\rightthreetimes",      OperatorInfo(L"\U000022CC", LayoutTree::Node::cFlavourBin)),
367     make_pair(L"\\Cup",                  OperatorInfo(L"\U000022D3", LayoutTree::Node::cFlavourBin)),
368     make_pair(L"\\barwedge",             OperatorInfo(L"\U00002305", LayoutTree::Node::cFlavourBin)),
369     make_pair(L"\\curlywedge",           OperatorInfo(L"\U000022CF", LayoutTree::Node::cFlavourBin)),
370     make_pair(L"\\veebar",               OperatorInfo(L"\U000022BB", LayoutTree::Node::cFlavourBin)),
371     make_pair(L"\\curlyvee",             OperatorInfo(L"\U000022CE", LayoutTree::Node::cFlavourBin)),
372     make_pair(L"\\doublebarwedge",       OperatorInfo(L"\U00002306", LayoutTree::Node::cFlavourBin)),
373     make_pair(L"\\boxminus",             OperatorInfo(L"\U0000229F", LayoutTree::Node::cFlavourBin)),
374     make_pair(L"\\circleddash",          OperatorInfo(L"\U0000229D", LayoutTree::Node::cFlavourBin)),
375     make_pair(L"\\boxtimes",             OperatorInfo(L"\U000022A0", LayoutTree::Node::cFlavourBin)),
376     make_pair(L"\\circledast",           OperatorInfo(L"\U0000229B", LayoutTree::Node::cFlavourBin)),
377     make_pair(L"\\boxdot",               OperatorInfo(L"\U000022A1", LayoutTree::Node::cFlavourBin)),
378     make_pair(L"\\circledcirc",          OperatorInfo(L"\U0000229A", LayoutTree::Node::cFlavourBin)),
379     make_pair(L"\\boxplus",              OperatorInfo(L"\U0000229E", LayoutTree::Node::cFlavourBin)),
380     make_pair(L"\\centerdot",            OperatorInfo(L"\U000022C5", LayoutTree::Node::cFlavourBin)),
381     make_pair(L"\\divideontimes",        OperatorInfo(L"\U000022C7", LayoutTree::Node::cFlavourBin)),
382     make_pair(L"\\intercal",             OperatorInfo(L"\U000022BA", LayoutTree::Node::cFlavourBin)),
383     make_pair(L"\\leqq",                 OperatorInfo(L"\U00002266", LayoutTree::Node::cFlavourRel)),
384     make_pair(L"\\geqq",                 OperatorInfo(L"\U00002267", LayoutTree::Node::cFlavourRel)),
385     make_pair(L"\\leqslant",             OperatorInfo(L"\U00002A7D", LayoutTree::Node::cFlavourRel)),
386     make_pair(L"\\geqslant",             OperatorInfo(L"\U00002A7E", LayoutTree::Node::cFlavourRel)),
387     make_pair(L"\\eqslantless",          OperatorInfo(L"\U00002A95", LayoutTree::Node::cFlavourRel)),
388     make_pair(L"\\eqslantgtr",           OperatorInfo(L"\U00002A96", LayoutTree::Node::cFlavourRel)),
389     make_pair(L"\\gtrsim",               OperatorInfo(L"\U00002273", LayoutTree::Node::cFlavourRel)),
390     make_pair(L"\\lessapprox",           OperatorInfo(L"\U00002A85", LayoutTree::Node::cFlavourRel)),
391     make_pair(L"\\gtrapprox",            OperatorInfo(L"\U00002A86", LayoutTree::Node::cFlavourRel)),
392     make_pair(L"\\approxeq",             OperatorInfo(L"\U0000224A", LayoutTree::Node::cFlavourRel)),
393     make_pair(L"\\eqsim",                OperatorInfo(L"\U00002242", LayoutTree::Node::cFlavourRel)),
394     make_pair(L"\\lessdot",              OperatorInfo(L"\U000022D6", LayoutTree::Node::cFlavourBin)),
395     make_pair(L"\\gtrdot",               OperatorInfo(L"\U000022D7", LayoutTree::Node::cFlavourBin)),
396     make_pair(L"\\lll",                  OperatorInfo(L"\U000022D8", LayoutTree::Node::cFlavourRel)),
397     make_pair(L"\\ggg",                  OperatorInfo(L"\U000022D9", LayoutTree::Node::cFlavourRel)),
398     make_pair(L"\\lessgtr",              OperatorInfo(L"\U00002276", LayoutTree::Node::cFlavourRel)),
399     make_pair(L"\\gtrless",              OperatorInfo(L"\U00002277", LayoutTree::Node::cFlavourRel)),
400     make_pair(L"\\lesseqgtr",            OperatorInfo(L"\U000022DA", LayoutTree::Node::cFlavourRel)),
401     make_pair(L"\\gtreqless",            OperatorInfo(L"\U000022DB", LayoutTree::Node::cFlavourRel)),
402     make_pair(L"\\lesseqqgtr",           OperatorInfo(L"\U00002A8B", LayoutTree::Node::cFlavourRel)),
403     make_pair(L"\\gtreqqless",           OperatorInfo(L"\U00002A8C", LayoutTree::Node::cFlavourRel)),
404     make_pair(L"\\doteqdot",             OperatorInfo(L"\U00002251", LayoutTree::Node::cFlavourRel)),
405     make_pair(L"\\eqcirc",               OperatorInfo(L"\U00002256", LayoutTree::Node::cFlavourRel)),
406     make_pair(L"\\risingdotseq",         OperatorInfo(L"\U00002253", LayoutTree::Node::cFlavourRel)),
407     make_pair(L"\\circeq",               OperatorInfo(L"\U00002257", LayoutTree::Node::cFlavourRel)),
408     make_pair(L"\\fallingdotseq",        OperatorInfo(L"\U00002252", LayoutTree::Node::cFlavourRel)),
409     make_pair(L"\\triangleq",            OperatorInfo(L"\U0000225C", LayoutTree::Node::cFlavourRel)),
410     make_pair(L"\\backsim",              OperatorInfo(L"\U0000223D", LayoutTree::Node::cFlavourRel)),
411     make_pair(L"\\thicksim",             OperatorInfo(L"\U0000223C", LayoutTree::Node::cFlavourRel)),
412     make_pair(L"\\backsimeq",            OperatorInfo(L"\U000022CD", LayoutTree::Node::cFlavourRel)),
413     make_pair(L"\\thickapprox",          OperatorInfo(L"\U00002248", LayoutTree::Node::cFlavourRel)),
414     make_pair(L"\\subseteqq",            OperatorInfo(L"\U00002AC5", LayoutTree::Node::cFlavourRel)),
415     make_pair(L"\\supseteqq",            OperatorInfo(L"\U00002AC6", LayoutTree::Node::cFlavourRel)),
416     make_pair(L"\\Subset",               OperatorInfo(L"\U000022D0", LayoutTree::Node::cFlavourRel)),
417     make_pair(L"\\Supset",               OperatorInfo(L"\U000022D1", LayoutTree::Node::cFlavourRel)),
418     make_pair(L"\\preccurlyeq",          OperatorInfo(L"\U0000227C", LayoutTree::Node::cFlavourRel)),
419     make_pair(L"\\succcurlyeq",          OperatorInfo(L"\U0000227D", LayoutTree::Node::cFlavourRel)),
420     make_pair(L"\\curlyeqprec",          OperatorInfo(L"\U000022DE", LayoutTree::Node::cFlavourRel)),
421     make_pair(L"\\curlyeqsucc",          OperatorInfo(L"\U000022DF", LayoutTree::Node::cFlavourRel)),
422     make_pair(L"\\precsim",              OperatorInfo(L"\U0000227E", LayoutTree::Node::cFlavourRel)),
423     make_pair(L"\\succsim",              OperatorInfo(L"\U0000227F", LayoutTree::Node::cFlavourRel)),
424     make_pair(L"\\precapprox",           OperatorInfo(L"\U00002AB7", LayoutTree::Node::cFlavourRel)),
425     make_pair(L"\\succapprox",           OperatorInfo(L"\U00002AB8", LayoutTree::Node::cFlavourRel)),
426     make_pair(L"\\Vvdash",               OperatorInfo(L"\U000022AA", LayoutTree::Node::cFlavourRel)),
427     make_pair(L"\\shortmid",             OperatorInfo(L"\U00002223", LayoutTree::Node::cFlavourRel)),
428     make_pair(L"\\shortparallel",        OperatorInfo(L"\U00002225", LayoutTree::Node::cFlavourRel)),
429     make_pair(L"\\bumpeq",               OperatorInfo(L"\U0000224F", LayoutTree::Node::cFlavourRel)),
430     make_pair(L"\\between",              OperatorInfo(L"\U0000226C", LayoutTree::Node::cFlavourRel)),
431     make_pair(L"\\Bumpeq",               OperatorInfo(L"\U0000224E", LayoutTree::Node::cFlavourRel)),
432     make_pair(L"\\varpropto",            OperatorInfo(L"\U0000221D", LayoutTree::Node::cFlavourRel)),
433     make_pair(L"\\backepsilon",          OperatorInfo(L"\U000003F6", LayoutTree::Node::cFlavourRel)),
434     make_pair(L"\\blacktriangleleft",    OperatorInfo(L"\U000025C0", LayoutTree::Node::cFlavourRel)),
435     make_pair(L"\\blacktriangleright",   OperatorInfo(L"\U000025B6", LayoutTree::Node::cFlavourRel)),
436     make_pair(L"\\therefore",            OperatorInfo(L"\U00002234", LayoutTree::Node::cFlavourRel)),
437     make_pair(L"\\because",              OperatorInfo(L"\U00002235", LayoutTree::Node::cFlavourRel)),
438     make_pair(L"\\ngtr",                 OperatorInfo(L"\U0000226F", LayoutTree::Node::cFlavourRel)),
439     make_pair(L"\\nleqslant",            OperatorInfo(L"\U00002A7D\U00000338", LayoutTree::Node::cFlavourRel)),
440     make_pair(L"\\ngeqslant",            OperatorInfo(L"\U00002A7E\U00000338", LayoutTree::Node::cFlavourRel)),
441     make_pair(L"\\nleqq",                OperatorInfo(L"\U00002266\U00000338", LayoutTree::Node::cFlavourRel)),
442     make_pair(L"\\ngeqq",                OperatorInfo(L"\U00002267\U00000338", LayoutTree::Node::cFlavourRel)),
443     make_pair(L"\\lneqq",                OperatorInfo(L"\U00002268", LayoutTree::Node::cFlavourRel)),
444     make_pair(L"\\gneqq",                OperatorInfo(L"\U00002269", LayoutTree::Node::cFlavourRel)),
445     make_pair(L"\\lvertneqq",            OperatorInfo(L"\U00002268\U0000FE00", LayoutTree::Node::cFlavourRel)),
446     make_pair(L"\\gvertneqq",            OperatorInfo(L"\U00002269\U0000FE00", LayoutTree::Node::cFlavourRel)),
447     make_pair(L"\\lnsim",                OperatorInfo(L"\U000022E6", LayoutTree::Node::cFlavourRel)),
448     make_pair(L"\\gnsim",                OperatorInfo(L"\U000022E7", LayoutTree::Node::cFlavourRel)),
449     make_pair(L"\\lnapprox",             OperatorInfo(L"\U00002A89", LayoutTree::Node::cFlavourRel)),
450     make_pair(L"\\gnapprox",             OperatorInfo(L"\U00002A8A", LayoutTree::Node::cFlavourRel)),
451     make_pair(L"\\nprec",                OperatorInfo(L"\U00002280", LayoutTree::Node::cFlavourRel)),
452     make_pair(L"\\nsucc",                OperatorInfo(L"\U00002281", LayoutTree::Node::cFlavourRel)),
453     make_pair(L"\\npreceq",              OperatorInfo(L"\U00002AAF\U00000338", LayoutTree::Node::cFlavourRel)),
454     make_pair(L"\\nsucceq",              OperatorInfo(L"\U00002AB0\U00000338", LayoutTree::Node::cFlavourRel)),
455     make_pair(L"\\precneqq",             OperatorInfo(L"\U00002AB5", LayoutTree::Node::cFlavourRel)),
456     make_pair(L"\\succneqq",             OperatorInfo(L"\U00002AB6", LayoutTree::Node::cFlavourRel)),
457     make_pair(L"\\precnsim",             OperatorInfo(L"\U000022E8", LayoutTree::Node::cFlavourRel)),
458     make_pair(L"\\succnsim",             OperatorInfo(L"\U000022E9", LayoutTree::Node::cFlavourRel)),
459     make_pair(L"\\precnapprox",          OperatorInfo(L"\U00002AB9", LayoutTree::Node::cFlavourRel)),
460     make_pair(L"\\succnapprox",          OperatorInfo(L"\U00002ABA", LayoutTree::Node::cFlavourRel)),
461     make_pair(L"\\nsim",                 OperatorInfo(L"\U00002241", LayoutTree::Node::cFlavourRel)),
462     make_pair(L"\\ncong",                OperatorInfo(L"\U00002247", LayoutTree::Node::cFlavourRel)),
463     make_pair(L"\\nshortmid",            OperatorInfo(L"\U00002224", LayoutTree::Node::cFlavourRel)),
464     make_pair(L"\\nshortparallel",       OperatorInfo(L"\U00002226", LayoutTree::Node::cFlavourRel)),
465     make_pair(L"\\nmid",                 OperatorInfo(L"\U00002224", LayoutTree::Node::cFlavourRel)),
466     make_pair(L"\\nparallel",            OperatorInfo(L"\U00002226", LayoutTree::Node::cFlavourRel)),
467     make_pair(L"\\nvdash",               OperatorInfo(L"\U000022AC", LayoutTree::Node::cFlavourRel)),
468     make_pair(L"\\nvDash",               OperatorInfo(L"\U000022AD", LayoutTree::Node::cFlavourRel)),
469     make_pair(L"\\nVdash",               OperatorInfo(L"\U000022AE", LayoutTree::Node::cFlavourRel)),
470     make_pair(L"\\nVDash",               OperatorInfo(L"\U000022AF", LayoutTree::Node::cFlavourRel)),
471     make_pair(L"\\ntriangleleft",        OperatorInfo(L"\U000022EA", LayoutTree::Node::cFlavourRel)),
472     make_pair(L"\\ntriangleright",       OperatorInfo(L"\U000022EB", LayoutTree::Node::cFlavourRel)),
473     make_pair(L"\\ntrianglelefteq",      OperatorInfo(L"\U000022EC", LayoutTree::Node::cFlavourRel)),
474     make_pair(L"\\ntrianglerighteq",     OperatorInfo(L"\U000022ED", LayoutTree::Node::cFlavourRel)),
475     make_pair(L"\\nsubseteq",            OperatorInfo(L"\U00002288", LayoutTree::Node::cFlavourRel)),
476     make_pair(L"\\nsupseteq",            OperatorInfo(L"\U00002289", LayoutTree::Node::cFlavourRel)),
477     make_pair(L"\\nsubseteqq",           OperatorInfo(L"\U00002AC5\U00000338", LayoutTree::Node::cFlavourRel)),
478     make_pair(L"\\nsupseteqq",           OperatorInfo(L"\U00002AC6\U00000338", LayoutTree::Node::cFlavourRel)),
479     make_pair(L"\\subsetneq",            OperatorInfo(L"\U0000228A", LayoutTree::Node::cFlavourRel)),
480     make_pair(L"\\supsetneq",            OperatorInfo(L"\U0000228B", LayoutTree::Node::cFlavourRel)),
481     make_pair(L"\\varsubsetneq",         OperatorInfo(L"\U0000228A\U0000FE00", LayoutTree::Node::cFlavourRel)),
482     make_pair(L"\\varsupsetneq",         OperatorInfo(L"\U0000228B\U0000FE00", LayoutTree::Node::cFlavourRel)),
483     make_pair(L"\\subsetneqq",           OperatorInfo(L"\U00002ACB", LayoutTree::Node::cFlavourRel)),
484     make_pair(L"\\supsetneqq",           OperatorInfo(L"\U00002ACC", LayoutTree::Node::cFlavourRel)),
485     make_pair(L"\\varsubsetneqq",        OperatorInfo(L"\U00002ACB\U0000FE00", LayoutTree::Node::cFlavourRel)),
486     make_pair(L"\\varsupsetneqq",        OperatorInfo(L"\U00002ACC\U0000FE00", LayoutTree::Node::cFlavourRel)),
487     make_pair(L"\\leftleftarrows",       OperatorInfo(L"\U000021C7", LayoutTree::Node::cFlavourRel)),
488     make_pair(L"\\rightrightarrows",     OperatorInfo(L"\U000021C9", LayoutTree::Node::cFlavourRel)),
489     make_pair(L"\\leftrightarrows",      OperatorInfo(L"\U000021C6", LayoutTree::Node::cFlavourRel)),
490     make_pair(L"\\rightleftarrows",      OperatorInfo(L"\U000021C4", LayoutTree::Node::cFlavourRel)),
491     make_pair(L"\\Lleftarrow",           OperatorInfo(L"\U000021DA", LayoutTree::Node::cFlavourRel)),
492     make_pair(L"\\Rrightarrow",          OperatorInfo(L"\U000021DB", LayoutTree::Node::cFlavourRel)),
493     make_pair(L"\\twoheadleftarrow",     OperatorInfo(L"\U0000219E", LayoutTree::Node::cFlavourRel)),
494     make_pair(L"\\twoheadrightarrow",    OperatorInfo(L"\U000021A0", LayoutTree::Node::cFlavourRel)),
495     make_pair(L"\\leftarrowtail",        OperatorInfo(L"\U000021A2", LayoutTree::Node::cFlavourRel)),
496     make_pair(L"\\rightarrowtail",       OperatorInfo(L"\U000021A3", LayoutTree::Node::cFlavourRel)),
497     make_pair(L"\\looparrowleft",        OperatorInfo(L"\U000021AB", LayoutTree::Node::cFlavourRel)),
498     make_pair(L"\\looparrowright",       OperatorInfo(L"\U000021AC", LayoutTree::Node::cFlavourRel)),
499     make_pair(L"\\leftrightharpoons",    OperatorInfo(L"\U000021CB", LayoutTree::Node::cFlavourRel)),
500     make_pair(L"\\rightleftharpoons",    OperatorInfo(L"\U000021CC", LayoutTree::Node::cFlavourRel)),
501     make_pair(L"\\curvearrowleft",       OperatorInfo(L"\U000021B6", LayoutTree::Node::cFlavourRel)),
502     make_pair(L"\\curvearrowright",      OperatorInfo(L"\U000021B7", LayoutTree::Node::cFlavourRel)),
503     make_pair(L"\\circlearrowleft",      OperatorInfo(L"\U000021BA", LayoutTree::Node::cFlavourRel)),
504     make_pair(L"\\circlearrowright",     OperatorInfo(L"\U000021BB", LayoutTree::Node::cFlavourRel)),
505     make_pair(L"\\Lsh",                  OperatorInfo(L"\U000021B0", LayoutTree::Node::cFlavourRel)),
506     make_pair(L"\\Rsh",                  OperatorInfo(L"\U000021B1", LayoutTree::Node::cFlavourRel)),
507     make_pair(L"\\upuparrows",           OperatorInfo(L"\U000021C8", LayoutTree::Node::cFlavourRel)),
508     make_pair(L"\\downdownarrows",       OperatorInfo(L"\U000021CA", LayoutTree::Node::cFlavourRel)),
509     make_pair(L"\\multimap",             OperatorInfo(L"\U000022B8", LayoutTree::Node::cFlavourRel)),
510     make_pair(L"\\rightsquigarrow",      OperatorInfo(L"\U0000219D", LayoutTree::Node::cFlavourRel)),
511     make_pair(L"\\leftrightsquigarrow",  OperatorInfo(L"\U000021AD", LayoutTree::Node::cFlavourRel)),
512     make_pair(L"\\nLeftarrow",           OperatorInfo(L"\U000021CD", LayoutTree::Node::cFlavourRel)),
513     make_pair(L"\\nRightarrow",          OperatorInfo(L"\U000021CF", LayoutTree::Node::cFlavourRel)),
514     make_pair(L"\\nleftrightarrow",      OperatorInfo(L"\U000021AE", LayoutTree::Node::cFlavourRel)),
515     make_pair(L"\\nLeftrightarrow",      OperatorInfo(L"\U000021CE", LayoutTree::Node::cFlavourRel)),
516     make_pair(L"\\pitchfork",            OperatorInfo(L"\U000022D4", LayoutTree::Node::cFlavourRel)),
517     make_pair(L"\\nexists",              OperatorInfo(L"\U00002204", LayoutTree::Node::cFlavourOrd)),
518     make_pair(L"\\lhd",                  OperatorInfo(L"\U000022B2", LayoutTree::Node::cFlavourBin)),
519     make_pair(L"\\rhd",                  OperatorInfo(L"\U000022B3", LayoutTree::Node::cFlavourBin)),
520     make_pair(L"\\unlhd",                OperatorInfo(L"\U000022B4", LayoutTree::Node::cFlavourBin)),
521     make_pair(L"\\unrhd",                OperatorInfo(L"\U000022B5", LayoutTree::Node::cFlavourBin)),
522     make_pair(L"\\leadsto",              OperatorInfo(L"\U000021DD", LayoutTree::Node::cFlavourRel)),
523     make_pair(L"\\uplus",                OperatorInfo(L"\U0000228E", LayoutTree::Node::cFlavourBin)),
524     make_pair(L"\\diamond",              OperatorInfo(L"\U000022C4", LayoutTree::Node::cFlavourBin)),
525     make_pair(L"\\bigtriangleup",        OperatorInfo(L"\U000025B3", LayoutTree::Node::cFlavourBin)),
526     make_pair(L"\\bigtriangledown",      OperatorInfo(L"\U000025BD", LayoutTree::Node::cFlavourBin)),
527     make_pair(L"\\ominus",               OperatorInfo(L"\U00002296", LayoutTree::Node::cFlavourBin)),
528     make_pair(L"\\oslash",               OperatorInfo(L"\U00002298", LayoutTree::Node::cFlavourBin)),
529     make_pair(L"\\odot",                 OperatorInfo(L"\U00002299", LayoutTree::Node::cFlavourBin)),
530     make_pair(L"\\bigcirc",              OperatorInfo(L"\U000025EF", LayoutTree::Node::cFlavourBin)),
531     make_pair(L"\\amalg",                OperatorInfo(L"\U00002A3F", LayoutTree::Node::cFlavourBin)),
532     make_pair(L"\\prec",                 OperatorInfo(L"\U0000227A", LayoutTree::Node::cFlavourRel)),
533     make_pair(L"\\succ",                 OperatorInfo(L"\U0000227B", LayoutTree::Node::cFlavourRel)),
534     make_pair(L"\\preceq",               OperatorInfo(L"\U00002AAF", LayoutTree::Node::cFlavourRel)),
535     make_pair(L"\\succeq",               OperatorInfo(L"\U00002AB0", LayoutTree::Node::cFlavourRel)),
536     make_pair(L"\\dashv",                OperatorInfo(L"\U000022A3", LayoutTree::Node::cFlavourRel)),
537     make_pair(L"\\asymp",                OperatorInfo(L"\U00002248", LayoutTree::Node::cFlavourRel)),
538     make_pair(L"\\doteq",                OperatorInfo(L"\U00002250", LayoutTree::Node::cFlavourRel)),
539     make_pair(L"\\parallel",             OperatorInfo(L"\U00002225", LayoutTree::Node::cFlavourRel)),
540     make_pair(L"\\bowtie",               OperatorInfo(L"\U000022C8", LayoutTree::Node::cFlavourRel)),
541     make_pair(L"\\surd",                 OperatorInfo(L"\U0000221A", LayoutTree::Node::cFlavourOrd)),
542 
543     make_pair(L"\\lim",                  OperatorInfo(L"lim",        LayoutTree::Node::cFlavourOp)),
544     make_pair(L"\\sup",                  OperatorInfo(L"sup",        LayoutTree::Node::cFlavourOp)),
545     make_pair(L"\\inf",                  OperatorInfo(L"inf",        LayoutTree::Node::cFlavourOp)),
546     make_pair(L"\\min",                  OperatorInfo(L"min",        LayoutTree::Node::cFlavourOp)),
547     make_pair(L"\\max",                  OperatorInfo(L"max",        LayoutTree::Node::cFlavourOp)),
548     make_pair(L"\\gcd",                  OperatorInfo(L"gcd",        LayoutTree::Node::cFlavourOp)),
549     make_pair(L"\\det",                  OperatorInfo(L"det",        LayoutTree::Node::cFlavourOp)),
550     make_pair(L"\\Pr",                   OperatorInfo(L"Pr",         LayoutTree::Node::cFlavourOp)),
551     // FIX: the space between the words in these operators is maybe a tiny bit too big.
552     make_pair(L"\\limsup",               OperatorInfo(L"lim sup",    LayoutTree::Node::cFlavourOp)),
553     make_pair(L"\\liminf",               OperatorInfo(L"lim inf",    LayoutTree::Node::cFlavourOp)),
554     make_pair(L"\\injlim",               OperatorInfo(L"inj lim",    LayoutTree::Node::cFlavourOp)),
555     make_pair(L"\\projlim",              OperatorInfo(L"proj lim",   LayoutTree::Node::cFlavourOp)),
556 
557     // The translation of \not is special: we record it as a SymbolOperator
558     // in the layout tree, but it gets special handling later.
559     make_pair(L"\\not",                  OperatorInfo(L"NOT", LayoutTree::Node::cFlavourRel))
560 };
561 wishful_hash_map<wstring, OperatorInfo> operatorTable(
562     operatorArray,
563     END_ARRAY(operatorArray)
564 );
565 
566 
567 struct IdentifierInfo
568 {
569     bool mIsItalicDefault;
570     wstring mText;
571     LayoutTree::Node::Flavour mFlavour;
572 
IdentifierInfoblahtex::IdentifierInfo573     IdentifierInfo(
574         bool isItalicDefault,
575         const wstring& text,
576         LayoutTree::Node::Flavour flavour
577     ) :
578         mIsItalicDefault(isItalicDefault),
579         mText(text),
580         mFlavour(flavour)
581     { }
582 };
583 
584 // A list of all commands that get translated as identifiers,
585 // their MathML translations, flavour, and whether they should be
586 // rendered in italic font.
587 pair<wstring, IdentifierInfo> identifierArray[] =
588 {
589     make_pair(L"\\ker",         IdentifierInfo(false, L"ker",        LayoutTree::Node::cFlavourOp)),
590     make_pair(L"\\deg",         IdentifierInfo(false, L"deg",        LayoutTree::Node::cFlavourOp)),
591     make_pair(L"\\hom",         IdentifierInfo(false, L"hom",        LayoutTree::Node::cFlavourOp)),
592     make_pair(L"\\dim",         IdentifierInfo(false, L"dim",        LayoutTree::Node::cFlavourOp)),
593     make_pair(L"\\arg",         IdentifierInfo(false, L"arg",        LayoutTree::Node::cFlavourOp)),
594     make_pair(L"\\sin",         IdentifierInfo(false, L"sin",        LayoutTree::Node::cFlavourOp)),
595     make_pair(L"\\cos",         IdentifierInfo(false, L"cos",        LayoutTree::Node::cFlavourOp)),
596     make_pair(L"\\sec",         IdentifierInfo(false, L"sec",        LayoutTree::Node::cFlavourOp)),
597     make_pair(L"\\csc",         IdentifierInfo(false, L"csc",        LayoutTree::Node::cFlavourOp)),
598     make_pair(L"\\tan",         IdentifierInfo(false, L"tan",        LayoutTree::Node::cFlavourOp)),
599     make_pair(L"\\cot",         IdentifierInfo(false, L"cot",        LayoutTree::Node::cFlavourOp)),
600     make_pair(L"\\arcsin",      IdentifierInfo(false, L"arcsin",     LayoutTree::Node::cFlavourOp)),
601     make_pair(L"\\arccos",      IdentifierInfo(false, L"arccos",     LayoutTree::Node::cFlavourOp)),
602     make_pair(L"\\arctan",      IdentifierInfo(false, L"arctan",     LayoutTree::Node::cFlavourOp)),
603     make_pair(L"\\sinh",        IdentifierInfo(false, L"sinh",       LayoutTree::Node::cFlavourOp)),
604     make_pair(L"\\cosh",        IdentifierInfo(false, L"cosh",       LayoutTree::Node::cFlavourOp)),
605     make_pair(L"\\tanh",        IdentifierInfo(false, L"tanh",       LayoutTree::Node::cFlavourOp)),
606     make_pair(L"\\coth",        IdentifierInfo(false, L"coth",       LayoutTree::Node::cFlavourOp)),
607     make_pair(L"\\log",         IdentifierInfo(false, L"log",        LayoutTree::Node::cFlavourOp)),
608     make_pair(L"\\lg",          IdentifierInfo(false, L"lg",         LayoutTree::Node::cFlavourOp)),
609     make_pair(L"\\ln",          IdentifierInfo(false, L"ln",         LayoutTree::Node::cFlavourOp)),
610     make_pair(L"\\exp",         IdentifierInfo(false, L"exp",        LayoutTree::Node::cFlavourOp)),
611     make_pair(L"\\aleph",       IdentifierInfo(false, L"\U00002135", LayoutTree::Node::cFlavourOrd)),
612     make_pair(L"\\beth",        IdentifierInfo(false, L"\U00002136", LayoutTree::Node::cFlavourOrd)),
613     make_pair(L"\\gimel",       IdentifierInfo(false, L"\U00002137", LayoutTree::Node::cFlavourOrd)),
614     make_pair(L"\\daleth",      IdentifierInfo(false, L"\U00002138", LayoutTree::Node::cFlavourOrd)),
615     make_pair(L"\\wp",          IdentifierInfo(true,  L"\U00002118", LayoutTree::Node::cFlavourOrd)),
616     make_pair(L"\\ell",         IdentifierInfo(true,  L"\U00002113", LayoutTree::Node::cFlavourOrd)),
617     make_pair(L"\\P",           IdentifierInfo(true,  L"\U000000B6", LayoutTree::Node::cFlavourOrd)),
618     make_pair(L"\\imath",       IdentifierInfo(true,  L"\U00000131", LayoutTree::Node::cFlavourOrd)),
619     make_pair(L"\\Finv",        IdentifierInfo(false, L"\U00002132", LayoutTree::Node::cFlavourOrd)),
620     make_pair(L"\\Game",        IdentifierInfo(false, L"\U00002141", LayoutTree::Node::cFlavourOrd)),
621     make_pair(L"\\partial",     IdentifierInfo(false, L"\U00002202", LayoutTree::Node::cFlavourOrd)),
622     make_pair(L"\\Re",          IdentifierInfo(false, L"\U0000211C", LayoutTree::Node::cFlavourOrd)),
623     make_pair(L"\\Im",          IdentifierInfo(false, L"\U00002111", LayoutTree::Node::cFlavourOrd)),
624     make_pair(L"\\infty",       IdentifierInfo(false, L"\U0000221E", LayoutTree::Node::cFlavourOrd)),
625     make_pair(L"\\hbar",        IdentifierInfo(false, L"\U00000127", LayoutTree::Node::cFlavourOrd)),
626     make_pair(L"\\emptyset",    IdentifierInfo(false, L"\U00002205", LayoutTree::Node::cFlavourOrd)),
627     make_pair(L"\\varnothing",  IdentifierInfo(false, L"\U000000D8", LayoutTree::Node::cFlavourOrd)),
628     make_pair(L"\\S",           IdentifierInfo(false, L"\U000000A7", LayoutTree::Node::cFlavourOrd)),
629     make_pair(L"\\AA",          IdentifierInfo(false, L"\U000000C5", LayoutTree::Node::cFlavourOrd)),
630     make_pair(L"\\eth",         IdentifierInfo(false, L"\U000000F0", LayoutTree::Node::cFlavourOrd)),
631     make_pair(L"\\hslash",      IdentifierInfo(false, L"\U0000210F", LayoutTree::Node::cFlavourOrd)),
632     make_pair(L"\\mho",         IdentifierInfo(false, L"\U00002127", LayoutTree::Node::cFlavourOrd)),
633     make_pair(L"\\circledR",    IdentifierInfo(false, L"\U000000AE", LayoutTree::Node::cFlavourOrd)),
634     make_pair(L"\\yen",         IdentifierInfo(false, L"\U000000A5", LayoutTree::Node::cFlavourOrd)),
635     make_pair(L"\\maltese",     IdentifierInfo(false, L"\U00002720", LayoutTree::Node::cFlavourOrd)),
636     make_pair(L"\\circledS",    IdentifierInfo(false, L"\U000024C8", LayoutTree::Node::cFlavourOrd)),
637     // FIX: these two needs special testing since they're plane-1:
638     // FIX: need to update mediawiki to recognise these entities
639     make_pair(L"\\Bbbk",        IdentifierInfo(false, L"\U0001D55C", LayoutTree::Node::cFlavourOrd)),
640     make_pair(L"\\jmath",       IdentifierInfo(true,  L"\U0001D6A5", LayoutTree::Node::cFlavourOrd))
641 };
642 wishful_hash_map<wstring, IdentifierInfo> identifierTable(
643     identifierArray,
644     END_ARRAY(identifierArray)
645 );
646 
647 
648 namespace ParseTree
649 {
650 
BuildLayoutTree(const TexProcessingState & state) const651 auto_ptr<LayoutTree::Node> MathSymbol::BuildLayoutTree(
652     const TexProcessingState& state
653 ) const
654 {
655     // First check for certain easy-to-handle single character commands,
656     // like letters or numerals.
657     if (mCommand.size() == 1)
658     {
659         bool good = false;
660         bool isNumber = false;
661         // fancyFontsIllegal is set for characters which can't be
662         // displayed in frak, cal or bb fonts.
663         bool fancyFontsIllegal = false;
664         TexMathFont::Family defaultFamily = TexMathFont::cFamilyIt;
665         TexMathFont font = state.mMathFont;
666 
667         if (mCommand[0] >= L'A' && mCommand[0] <= L'Z')
668             good = true;
669 
670         else if (mCommand[0] >= L'a' && mCommand[0] <= L'z')
671         {
672             fancyFontsIllegal = true;
673             good = true;
674         }
675 
676         else if (mCommand[0] >= L'0' && mCommand[0] <= L'9')
677         {
678             fancyFontsIllegal = true;
679             defaultFamily = TexMathFont::cFamilyRm;
680             good = isNumber = true;
681         }
682 
683         if (good)
684         {
685             if (font.mFamily == TexMathFont::cFamilyDefault)
686                 font.mFamily = defaultFamily;
687 
688             if (fancyFontsIllegal &&
689                 font.mFamily == TexMathFont::cFamilyCal
690             )
691                 throw Exception(
692                     L"UnavailableSymbolFontCombination", mCommand, L"cal"
693                 );
694 
695             if (fancyFontsIllegal &&
696                 font.mFamily == TexMathFont::cFamilyBb
697             )
698                 throw Exception(
699                     L"UnavailableSymbolFontCombination", mCommand, L"bb"
700                 );
701 
702             if (isNumber)
703                 return auto_ptr<LayoutTree::Node>(
704                     new LayoutTree::SymbolNumber(
705                         mCommand,
706                         font.GetMathmlApproximation(),
707                         state.mStyle,
708                         LayoutTree::Node::cFlavourOrd,
709                         LayoutTree::Node::cLimitsDisplayLimits,
710                         state.mColour
711                     )
712                 );
713             else
714                 return auto_ptr<LayoutTree::Node>(
715                     new LayoutTree::SymbolIdentifier(
716                         mCommand,
717                         font.GetMathmlApproximation(),
718                         state.mStyle,
719                         LayoutTree::Node::cFlavourOrd,
720                         LayoutTree::Node::cLimitsDisplayLimits,
721                         state.mColour
722                     )
723                 );
724         }
725 
726         // Non-ascii characters
727         if (mCommand[0] > 0x7F)
728             throw logic_error(
729                 "Unexpected non-ASCII character "
730                 "in MathSymbol::BuildLayoutTree"
731             );
732     }
733 
734     wishful_hash_map<wstring, wchar_t>::const_iterator
735         lowercaseGreekLookup = lowercaseGreekTable.find(mCommand);
736 
737     if (lowercaseGreekLookup != lowercaseGreekTable.end())
738     {
739         return auto_ptr<LayoutTree::Node>(
740             new LayoutTree::SymbolIdentifier(
741                 wstring(1, lowercaseGreekLookup->second),
742                 // lowercase greek is only affected by the boldsymbol
743                 // status, not the family.
744                 state.mMathFont.mIsBoldsymbol
745                     ? cMathmlFontBoldItalic : cMathmlFontItalic,
746                 state.mStyle,
747                 LayoutTree::Node::cFlavourOrd,
748                 LayoutTree::Node::cLimitsDisplayLimits,
749                 state.mColour
750             )
751         );
752     }
753 
754     wishful_hash_map<wstring, wchar_t>::const_iterator
755         uppercaseGreekLookup = uppercaseGreekTable.find(mCommand);
756 
757     if (uppercaseGreekLookup != uppercaseGreekTable.end())
758     {
759         TexMathFont font = state.mMathFont;
760         if (font.mFamily == TexMathFont::cFamilyCal)
761             throw Exception(
762                 L"UnavailableSymbolFontCombination", mCommand, L"cal"
763             );
764 
765         if (font.mFamily == TexMathFont::cFamilyBb)
766             throw Exception(
767                 L"UnavailableSymbolFontCombination", mCommand, L"bb"
768             );
769 
770         if (font.mFamily == TexMathFont::cFamilyFrak)
771             throw Exception(
772                 L"UnavailableSymbolFontCombination", mCommand, L"frak"
773             );
774 
775         if (font.mFamily == TexMathFont::cFamilyDefault)
776             font.mFamily = TexMathFont::cFamilyRm;
777 
778         return auto_ptr<LayoutTree::Node>(
779             new LayoutTree::SymbolIdentifier(
780                 wstring(1, uppercaseGreekLookup->second),
781                 font.GetMathmlApproximation(),
782                 state.mStyle,
783                 LayoutTree::Node::cFlavourOrd,
784                 LayoutTree::Node::cLimitsDisplayLimits,
785                 state.mColour
786             )
787         );
788     }
789 
790     wishful_hash_map<wstring, int>::const_iterator
791         spaceLookup = spaceTable.find(mCommand);
792 
793     if (spaceLookup != spaceTable.end())
794     {
795         return auto_ptr<LayoutTree::Node>(
796             new LayoutTree::Space(
797                 spaceLookup->second,
798                 true      // true = indicates a user-requested space
799             )
800         );
801     }
802 
803     wishful_hash_map<wstring, OperatorInfo>::const_iterator
804         operatorLookup = operatorTable.find(mCommand);
805 
806     if (operatorLookup != operatorTable.end())
807     {
808         return auto_ptr<LayoutTree::Node>(
809             new LayoutTree::SymbolOperator(
810                 false, L"",     // not stretchy
811                 false,          // not an accent
812                 operatorLookup->second.mText,
813                 // operators are only affected by the boldsymbol status,
814                 // not the family.
815                 state.mMathFont.mIsBoldsymbol
816                     ? cMathmlFontBold : cMathmlFontNormal,
817                 state.mStyle,
818                 operatorLookup->second.mFlavour,
819                 operatorLookup->second.mLimits,
820                 state.mColour
821             )
822         );
823     }
824 
825     wishful_hash_map<wstring, IdentifierInfo>::const_iterator
826         identifierLookup = identifierTable.find(mCommand);
827 
828     if (identifierLookup != identifierTable.end())
829     {
830         TexMathFont font = state.mMathFont;
831         font.mFamily =
832             identifierLookup->second.mIsItalicDefault
833                 ? TexMathFont::cFamilyIt : TexMathFont::cFamilyRm;
834 
835         return auto_ptr<LayoutTree::Node>(
836             new LayoutTree::SymbolIdentifier(
837                 identifierLookup->second.mText,
838                 font.GetMathmlApproximation(),
839                 state.mStyle,
840                 identifierLookup->second.mFlavour,
841                 // For all the "\sin"-like functions:
842                 (
843                     identifierLookup->second.mFlavour ==
844                     LayoutTree::Node::cFlavourOp
845                 )
846                     ? LayoutTree::Node::cLimitsNoLimits
847                     : LayoutTree::Node::cLimitsDisplayLimits,
848                 state.mColour
849             )
850         );
851     }
852 
853     if (mCommand == L"\\And")
854     {
855         auto_ptr<LayoutTree::Row> row(
856             new LayoutTree::Row(state.mStyle, state.mColour)
857         );
858         row->mFlavour = LayoutTree::Node::cFlavourRel;
859         row->mChildren.push_back(new LayoutTree::Space(5, true));
860         row->mChildren.push_back(
861             new LayoutTree::SymbolOperator(
862                 false,
863                 L"",
864                 false,
865                 L"&",
866                 state.mMathFont.mIsBoldsymbol
867                     ? cMathmlFontBold : cMathmlFontNormal,
868                 state.mStyle,
869                 LayoutTree::Node::cFlavourOrd,
870                 LayoutTree::Node::cLimitsDisplayLimits,
871                 state.mColour
872             )
873         );
874         row->mChildren.push_back(new LayoutTree::Space(5, true));
875         return static_cast<auto_ptr<LayoutTree::Node> >(row);
876     }
877 
878     if (mCommand == L"\\iff")
879     {
880         auto_ptr<LayoutTree::Row> row(
881             new LayoutTree::Row(state.mStyle, state.mColour)
882         );
883         row->mFlavour = LayoutTree::Node::cFlavourRel;
884         row->mChildren.push_back(new LayoutTree::Space(5, true));
885         // FIX: I would like to make this stretchy and set a particular
886         // size, but for some reason firefox doesn't stretch things
887         // horizontally like this. It DOES do it if the element is in a
888         // <mover> or <munder> etc, but not when it's just by itself.
889         // Very strange. This is mozilla bug 320303.
890         row->mChildren.push_back(
891             new LayoutTree::SymbolOperator(
892                 false,
893                 L"",
894                 false,
895                 L"\U000021D4",
896                 state.mMathFont.mIsBoldsymbol
897                     ? cMathmlFontBold : cMathmlFontNormal,
898                 state.mStyle,
899                 LayoutTree::Node::cFlavourOrd,
900                 LayoutTree::Node::cLimitsDisplayLimits,
901                 state.mColour
902             )
903         );
904         row->mChildren.push_back(new LayoutTree::Space(5, true));
905         return static_cast<auto_ptr<LayoutTree::Node> >(row);
906     }
907 
908     if (mCommand == L"\\colon")
909     {
910         // FIX: this spacing stuff isn't quite right, but it will hopefully
911         // do. The amsmath package does all kinds of interesting things with
912         // \colon's spacing.
913         auto_ptr<LayoutTree::Row> row(
914             new LayoutTree::Row(state.mStyle, state.mColour)
915         );
916         row->mChildren.push_back(new LayoutTree::Space(2, true));
917         row->mChildren.push_back(
918             new LayoutTree::SymbolOperator(
919                 false,
920                 L"",
921                 false,
922                 L":",
923                 state.mMathFont.mIsBoldsymbol
924                     ? cMathmlFontBold : cMathmlFontNormal,
925                 state.mStyle,
926                 LayoutTree::Node::cFlavourOrd,
927                 LayoutTree::Node::cLimitsDisplayLimits,
928                 state.mColour
929             )
930         );
931         row->mChildren.push_back(new LayoutTree::Space(6, true));
932         return static_cast<auto_ptr<LayoutTree::Node> >(row);
933     }
934 
935     if (mCommand == L"\\bmod")
936     {
937         auto_ptr<LayoutTree::Row> row(
938             new LayoutTree::Row(state.mStyle, state.mColour)
939         );
940         row->mFlavour = LayoutTree::Node::cFlavourBin;
941         row->mChildren.push_back(new LayoutTree::Space(1, true));
942         row->mChildren.push_back(
943             new LayoutTree::SymbolOperator(
944                 false,
945                 L"",
946                 false,
947                 L"mod",
948                 state.mMathFont.mIsBoldsymbol
949                     ? cMathmlFontBold : cMathmlFontNormal,
950                 state.mStyle,
951                 LayoutTree::Node::cFlavourOrd,
952                 LayoutTree::Node::cLimitsDisplayLimits,
953                 state.mColour
954             )
955         );
956         row->mChildren.push_back(new LayoutTree::Space(1, true));
957         return static_cast<auto_ptr<LayoutTree::Node> >(row);
958     }
959 
960     if (mCommand == L"\\mod")
961     {
962         auto_ptr<LayoutTree::Row> row(
963             new LayoutTree::Row(state.mStyle, state.mColour)
964         );
965         row->mChildren.push_back(new LayoutTree::Space(18, true));
966         row->mChildren.push_back(
967             new LayoutTree::SymbolOperator(
968                 false,
969                 L"",
970                 false,
971                 L"mod",
972                 state.mMathFont.mIsBoldsymbol
973                     ? cMathmlFontBold : cMathmlFontNormal,
974                 state.mStyle,
975                 LayoutTree::Node::cFlavourOrd,
976                 LayoutTree::Node::cLimitsDisplayLimits,
977                 state.mColour
978             )
979         );
980         row->mChildren.push_back(new LayoutTree::Space(6, true));
981         return static_cast<auto_ptr<LayoutTree::Node> >(row);
982     }
983 
984     if (mCommand == L"\\varinjlim" || mCommand == L"\\varprojlim" ||
985         mCommand == L"\\varlimsup" || mCommand == L"\\varliminf")
986     {
987         MathmlFont font =
988             state.mMathFont.mIsBoldsymbol
989                 ? cMathmlFontBold : cMathmlFontNormal;
990 
991         auto_ptr<LayoutTree::Node> base(
992             new LayoutTree::SymbolOperator(
993                 false,
994                 L"",
995                 false,
996                 L"lim",
997                 font,
998                 state.mStyle,
999                 LayoutTree::Node::cFlavourOp,
1000                 LayoutTree::Node::cLimitsLimits,
1001                 state.mColour
1002             )
1003         );
1004 
1005         auto_ptr<LayoutTree::Scripts> node(
1006             new LayoutTree::Scripts(
1007                 state.mStyle,
1008                 LayoutTree::Node::cFlavourOp,
1009                 LayoutTree::Node::cLimitsDisplayLimits,
1010                 state.mColour,
1011                 false,
1012                 base,
1013                 auto_ptr<LayoutTree::Node>(),
1014                 auto_ptr<LayoutTree::Node>()
1015             )
1016         );
1017 
1018         if (mCommand == L"\\varinjlim")
1019             node->mLower = auto_ptr<LayoutTree::Node>(
1020                 new LayoutTree::SymbolOperator(
1021                     false,
1022                     L"",
1023                     true,
1024                     L"\U00002192",
1025                     font,
1026                     state.mStyle,
1027                     LayoutTree::Node::cFlavourOrd,
1028                     LayoutTree::Node::cLimitsDisplayLimits,
1029                     state.mColour
1030                 )
1031             );
1032 
1033         else if (mCommand == L"\\varprojlim")
1034             node->mLower = auto_ptr<LayoutTree::Node>(
1035                 new LayoutTree::SymbolOperator(
1036                     false,
1037                     L"",
1038                     true,
1039                     L"\U00002190",
1040                     font,
1041                     state.mStyle,
1042                     LayoutTree::Node::cFlavourOrd,
1043                     LayoutTree::Node::cLimitsDisplayLimits,
1044                     state.mColour
1045                 )
1046             );
1047 
1048         else if (mCommand == L"\\varliminf")
1049             node->mLower = auto_ptr<LayoutTree::Node>(
1050                 new LayoutTree::SymbolOperator(
1051                     true,
1052                     L"",
1053                     true,
1054                     L"\U000000AF",
1055                     font,
1056                     state.mStyle,
1057                     LayoutTree::Node::cFlavourOrd,
1058                     LayoutTree::Node::cLimitsDisplayLimits,
1059                     state.mColour
1060                 )
1061             );
1062 
1063         else if (mCommand == L"\\varlimsup")
1064             node->mUpper = auto_ptr<LayoutTree::Node>(
1065                 new LayoutTree::SymbolOperator(
1066                     true,
1067                     L"",
1068                     true,
1069                     L"\U000000AF",
1070                     font,
1071                     state.mStyle,
1072                     LayoutTree::Node::cFlavourOrd,
1073                     LayoutTree::Node::cLimitsDisplayLimits,
1074                     state.mColour
1075                 )
1076             );
1077 
1078         return static_cast<auto_ptr<LayoutTree::Node> >(node);
1079     }
1080 
1081     throw logic_error("Unexpected command in MathSymbol::BuildLayoutTree");
1082 }
1083 
1084 }
1085 }
1086 
1087 // end of file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1088