34788b022e97a50508fb2dff8affd793b337a9d4
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLOperator.cpp
1 /*
2  * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2010 Fran├žois Sausset (sausset@gmail.com). All rights reserved.
4  * Copyright (C) 2013 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #define _USE_MATH_DEFINES 1
29 #include "config.h"
30
31 #if ENABLE(MATHML)
32
33 #include "RenderMathMLOperator.h"
34
35 #include "FontCache.h"
36 #include "FontSelector.h"
37 #include "MathMLNames.h"
38 #include "PaintInfo.h"
39 #include "RenderBlockFlow.h"
40 #include "RenderText.h"
41 #include "ScaleTransformOperation.h"
42 #include "TransformOperations.h"
43 #include <wtf/MathExtras.h>
44 #include <wtf/unicode/CharacterNames.h>
45
46 namespace WebCore {
47     
48 using namespace MathMLNames;
49
50 // FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297).
51 struct StretchyCharacter {
52     UChar character;
53     UChar topChar;
54     UChar extensionChar;
55     UChar bottomChar;
56     UChar middleChar;
57 };
58 // The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
59 static const short leftRightPairsCount = 5;
60 static const StretchyCharacter stretchyCharacters[14] = {
61     { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
62     { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
63     { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
64     { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
65     { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
66     { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
67     { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0    }, // left ceiling
68     { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0    }, // right ceiling
69     { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0    }, // left floor
70     { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0    }, // right floor
71     { 0x7c  , 0x7c,   0x7c,   0x7c,   0x0    }, // vertical bar
72     { 0x2016, 0x2016, 0x2016, 0x2016, 0x0    }, // double vertical line
73     { 0x2225, 0x2225, 0x2225, 0x2225, 0x0    }, // parallel to
74     { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
75 };
76
77 namespace MathMLOperatorDictionary {
78
79 typedef std::pair<UChar, Form> Key;
80 inline Key ExtractKey(const Entry* entry) { return Key(entry->character, entry->form); }
81 inline UChar ExtractChar(const Entry* entry) { return entry->character; }
82
83 // This table has been automatically generated from the Operator Dictionary of the MathML3 specification (appendix C).
84 // Some people use the binary operator "U+2225 PARALLEL TO" as an opening and closing delimiter, so we add the corresponding stretchy prefix and postfix forms.
85 #define MATHML_OPDICT_SIZE 1041
86 static const Entry dictionary[MATHML_OPDICT_SIZE] = {
87     { 0x21, Postfix, 1, 0, 0}, // EXCLAMATION MARK
88     { 0x25, Infix, 3, 3, 0}, // PERCENT SIGN
89     { 0x26, Postfix, 0, 0, 0}, // AMPERSAND
90     { 0x27, Postfix, 0, 0, Accent}, // APOSTROPHE
91     { 0x28, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT PARENTHESIS
92     { 0x29, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT PARENTHESIS
93     { 0x2A, Infix, 3, 3, 0}, // ASTERISK
94     { 0x2B, Infix, 4, 4, 0}, // PLUS SIGN
95     { 0x2B, Prefix, 0, 1, 0}, // PLUS SIGN
96     { 0x2C, Infix, 0, 3, Separator}, // COMMA
97     { 0x2D, Infix, 4, 4, 0}, // HYPHEN-MINUS
98     { 0x2D, Prefix, 0, 1, 0}, // HYPHEN-MINUS
99     { 0x2E, Infix, 3, 3, 0}, // FULL STOP
100     { 0x2F, Infix, 1, 1, 0}, // SOLIDUS
101     { 0x3A, Infix, 1, 2, 0}, // COLON
102     { 0x3B, Infix, 0, 3, Separator}, // SEMICOLON
103     { 0x3C, Infix, 5, 5, 0}, // LESS-THAN SIGN
104     { 0x3D, Infix, 5, 5, 0}, // EQUALS SIGN
105     { 0x3E, Infix, 5, 5, 0}, // GREATER-THAN SIGN
106     { 0x3F, Infix, 1, 1, 0}, // QUESTION MARK
107     { 0x40, Infix, 1, 1, 0}, // COMMERCIAL AT
108     { 0x5B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET
109     { 0x5C, Infix, 0, 0, 0}, // REVERSE SOLIDUS
110     { 0x5D, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET
111     { 0x5E, Postfix, 0, 0, Accent | Stretchy}, // CIRCUMFLEX ACCENT
112     { 0x5E, Infix, 1, 1, 0}, // CIRCUMFLEX ACCENT
113     { 0x5F, Postfix, 0, 0, Accent | Stretchy}, // LOW LINE
114     { 0x5F, Infix, 1, 1, 0}, // LOW LINE
115     { 0x60, Postfix, 0, 0, Accent}, // GRAVE ACCENT
116     { 0x7B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT CURLY BRACKET
117     { 0x7C, Infix, 2, 2, Stretchy | Symmetric | Fence}, // VERTICAL LINE
118     { 0x7C, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // VERTICAL LINE
119     { 0x7C, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // VERTICAL LINE
120     { 0x7D, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT CURLY BRACKET
121     { 0x7E, Postfix, 0, 0, Accent | Stretchy}, // TILDE
122     { 0xA8, Postfix, 0, 0, Accent}, // DIAERESIS
123     { 0xAC, Prefix, 2, 1, 0}, // NOT SIGN
124     { 0xAF, Postfix, 0, 0, Accent | Stretchy}, // MACRON
125     { 0xB0, Postfix, 0, 0, 0}, // DEGREE SIGN
126     { 0xB1, Infix, 4, 4, 0}, // PLUS-MINUS SIGN
127     { 0xB1, Prefix, 0, 1, 0}, // PLUS-MINUS SIGN
128     { 0xB4, Postfix, 0, 0, Accent}, // ACUTE ACCENT
129     { 0xB7, Infix, 4, 4, 0}, // MIDDLE DOT
130     { 0xB8, Postfix, 0, 0, Accent}, // CEDILLA
131     { 0xD7, Infix, 4, 4, 0}, // MULTIPLICATION SIGN
132     { 0xF7, Infix, 4, 4, 0}, // DIVISION SIGN
133     { 0x2C6, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER CIRCUMFLEX ACCENT
134     { 0x2C7, Postfix, 0, 0, Accent | Stretchy}, // CARON
135     { 0x2C9, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER MACRON
136     { 0x2CA, Postfix, 0, 0, Accent}, // MODIFIER LETTER ACUTE ACCENT
137     { 0x2CB, Postfix, 0, 0, Accent}, // MODIFIER LETTER GRAVE ACCENT
138     { 0x2CD, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER LOW MACRON
139     { 0x2D8, Postfix, 0, 0, Accent}, // BREVE
140     { 0x2D9, Postfix, 0, 0, Accent}, // DOT ABOVE
141     { 0x2DA, Postfix, 0, 0, Accent}, // RING ABOVE
142     { 0x2DC, Postfix, 0, 0, Accent | Stretchy}, // SMALL TILDE
143     { 0x2DD, Postfix, 0, 0, Accent}, // DOUBLE ACUTE ACCENT
144     { 0x2F7, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER LOW TILDE
145     { 0x302, Postfix, 0, 0, Accent | Stretchy}, // COMBINING CIRCUMFLEX ACCENT
146     { 0x311, Postfix, 0, 0, Accent}, // COMBINING INVERTED BREVE
147     { 0x3F6, Infix, 5, 5, 0}, // GREEK REVERSED LUNATE EPSILON SYMBOL
148     { 0x2016, Prefix, 0, 0, Fence | Stretchy}, // DOUBLE VERTICAL LINE
149     { 0x2016, Postfix, 0, 0, Fence | Stretchy}, // DOUBLE VERTICAL LINE
150     { 0x2018, Prefix, 0, 0, Fence}, // LEFT SINGLE QUOTATION MARK
151     { 0x2019, Postfix, 0, 0, Fence}, // RIGHT SINGLE QUOTATION MARK
152     { 0x201C, Prefix, 0, 0, Fence}, // LEFT DOUBLE QUOTATION MARK
153     { 0x201D, Postfix, 0, 0, Fence}, // RIGHT DOUBLE QUOTATION MARK
154     { 0x2022, Infix, 4, 4, 0}, // BULLET
155     { 0x2026, Infix, 0, 0, 0}, // HORIZONTAL ELLIPSIS
156     { 0x2032, Postfix, 0, 2, 0}, // PRIME
157     { 0x203E, Postfix, 0, 0, Accent | Stretchy}, // OVERLINE
158     { 0x2044, Infix, 4, 4, Stretchy}, // FRACTION SLASH
159     { 0x2061, Infix, 0, 0, 0}, // FUNCTION APPLICATION
160     { 0x2062, Infix, 0, 0, 0}, // INVISIBLE TIMES
161     { 0x2063, Infix, 0, 0, Separator}, // INVISIBLE SEPARATOR
162     { 0x2064, Infix, 0, 0, 0}, // INVISIBLE PLUS
163     { 0x20DB, Postfix, 0, 0, Accent}, // COMBINING THREE DOTS ABOVE
164     { 0x20DC, Postfix, 0, 0, Accent}, // COMBINING FOUR DOTS ABOVE
165     { 0x2145, Prefix, 2, 1, 0}, // DOUBLE-STRUCK ITALIC CAPITAL D
166     { 0x2146, Prefix, 2, 0, 0}, // DOUBLE-STRUCK ITALIC SMALL D
167     { 0x2190, Infix, 5, 5, Accent | Stretchy}, // LEFTWARDS ARROW
168     { 0x2191, Infix, 5, 5, Stretchy}, // UPWARDS ARROW
169     { 0x2192, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW
170     { 0x2193, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW
171     { 0x2194, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT ARROW
172     { 0x2195, Infix, 5, 5, Stretchy}, // UP DOWN ARROW
173     { 0x2196, Infix, 5, 5, Stretchy}, // NORTH WEST ARROW
174     { 0x2197, Infix, 5, 5, Stretchy}, // NORTH EAST ARROW
175     { 0x2198, Infix, 5, 5, Stretchy}, // SOUTH EAST ARROW
176     { 0x2199, Infix, 5, 5, Stretchy}, // SOUTH WEST ARROW
177     { 0x219A, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH STROKE
178     { 0x219B, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH STROKE
179     { 0x219C, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS WAVE ARROW
180     { 0x219D, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WAVE ARROW
181     { 0x219E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TWO HEADED ARROW
182     { 0x219F, Infix, 5, 5, Stretchy | Accent}, // UPWARDS TWO HEADED ARROW
183     { 0x21A0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TWO HEADED ARROW
184     { 0x21A1, Infix, 5, 5, Stretchy}, // DOWNWARDS TWO HEADED ARROW
185     { 0x21A2, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH TAIL
186     { 0x21A3, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH TAIL
187     { 0x21A4, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW FROM BAR
188     { 0x21A5, Infix, 5, 5, Stretchy}, // UPWARDS ARROW FROM BAR
189     { 0x21A6, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW FROM BAR
190     { 0x21A7, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW FROM BAR
191     { 0x21A8, Infix, 5, 5, Stretchy}, // UP DOWN ARROW WITH BASE
192     { 0x21A9, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH HOOK
193     { 0x21AA, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH HOOK
194     { 0x21AB, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH LOOP
195     { 0x21AC, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH LOOP
196     { 0x21AD, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT WAVE ARROW
197     { 0x21AE, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH STROKE
198     { 0x21AF, Infix, 5, 5, Stretchy}, // DOWNWARDS ZIGZAG ARROW
199     { 0x21B0, Infix, 5, 5, Stretchy}, // UPWARDS ARROW WITH TIP LEFTWARDS
200     { 0x21B1, Infix, 5, 5, Stretchy}, // UPWARDS ARROW WITH TIP RIGHTWARDS
201     { 0x21B2, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH TIP LEFTWARDS
202     { 0x21B3, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH TIP RIGHTWARDS
203     { 0x21B4, Infix, 5, 5, Stretchy}, // RIGHTWARDS ARROW WITH CORNER DOWNWARDS
204     { 0x21B5, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH CORNER LEFTWARDS
205     { 0x21B6, Infix, 5, 5, Accent}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
206     { 0x21B7, Infix, 5, 5, Accent}, // CLOCKWISE TOP SEMICIRCLE ARROW
207     { 0x21B8, Infix, 5, 5, 0}, // NORTH WEST ARROW TO LONG BAR
208     { 0x21B9, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR
209     { 0x21BA, Infix, 5, 5, 0}, // ANTICLOCKWISE OPEN CIRCLE ARROW
210     { 0x21BB, Infix, 5, 5, 0}, // CLOCKWISE OPEN CIRCLE ARROW
211     { 0x21BC, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UPWARDS
212     { 0x21BD, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
213     { 0x21BE, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
214     { 0x21BF, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFTWARDS
215     { 0x21C0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
216     { 0x21C1, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
217     { 0x21C2, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
218     { 0x21C3, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
219     { 0x21C4, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
220     { 0x21C5, Infix, 5, 5, Stretchy}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
221     { 0x21C6, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
222     { 0x21C7, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS PAIRED ARROWS
223     { 0x21C8, Infix, 5, 5, Stretchy}, // UPWARDS PAIRED ARROWS
224     { 0x21C9, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS PAIRED ARROWS
225     { 0x21CA, Infix, 5, 5, Stretchy}, // DOWNWARDS PAIRED ARROWS
226     { 0x21CB, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
227     { 0x21CC, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
228     { 0x21CD, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW WITH STROKE
229     { 0x21CE, Infix, 5, 5, Accent}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
230     { 0x21CF, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
231     { 0x21D0, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DOUBLE ARROW
232     { 0x21D1, Infix, 5, 5, Stretchy}, // UPWARDS DOUBLE ARROW
233     { 0x21D2, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DOUBLE ARROW
234     { 0x21D3, Infix, 5, 5, Stretchy}, // DOWNWARDS DOUBLE ARROW
235     { 0x21D4, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT DOUBLE ARROW
236     { 0x21D5, Infix, 5, 5, Stretchy}, // UP DOWN DOUBLE ARROW
237     { 0x21D6, Infix, 5, 5, Stretchy}, // NORTH WEST DOUBLE ARROW
238     { 0x21D7, Infix, 5, 5, Stretchy}, // NORTH EAST DOUBLE ARROW
239     { 0x21D8, Infix, 5, 5, Stretchy}, // SOUTH EAST DOUBLE ARROW
240     { 0x21D9, Infix, 5, 5, Stretchy}, // SOUTH WEST DOUBLE ARROW
241     { 0x21DA, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TRIPLE ARROW
242     { 0x21DB, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TRIPLE ARROW
243     { 0x21DC, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS SQUIGGLE ARROW
244     { 0x21DD, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS SQUIGGLE ARROW
245     { 0x21DE, Infix, 5, 5, 0}, // UPWARDS ARROW WITH DOUBLE STROKE
246     { 0x21DF, Infix, 5, 5, 0}, // DOWNWARDS ARROW WITH DOUBLE STROKE
247     { 0x21E0, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DASHED ARROW
248     { 0x21E1, Infix, 5, 5, Stretchy}, // UPWARDS DASHED ARROW
249     { 0x21E2, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DASHED ARROW
250     { 0x21E3, Infix, 5, 5, Stretchy}, // DOWNWARDS DASHED ARROW
251     { 0x21E4, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW TO BAR
252     { 0x21E5, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW TO BAR
253     { 0x21E6, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS WHITE ARROW
254     { 0x21E7, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW
255     { 0x21E8, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WHITE ARROW
256     { 0x21E9, Infix, 5, 5, Stretchy}, // DOWNWARDS WHITE ARROW
257     { 0x21EA, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW FROM BAR
258     { 0x21EB, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL
259     { 0x21EC, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR
260     { 0x21ED, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR
261     { 0x21EE, Infix, 5, 5, Stretchy}, // UPWARDS WHITE DOUBLE ARROW
262     { 0x21EF, Infix, 5, 5, Stretchy}, // UPWARDS WHITE DOUBLE ARROW ON PEDESTAL
263     { 0x21F0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WHITE ARROW FROM WALL
264     { 0x21F1, Infix, 5, 5, 0}, // NORTH WEST ARROW TO CORNER
265     { 0x21F2, Infix, 5, 5, 0}, // SOUTH EAST ARROW TO CORNER
266     { 0x21F3, Infix, 5, 5, Stretchy}, // UP DOWN WHITE ARROW
267     { 0x21F4, Infix, 5, 5, Accent}, // RIGHT ARROW WITH SMALL CIRCLE
268     { 0x21F5, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
269     { 0x21F6, Infix, 5, 5, Stretchy | Accent}, // THREE RIGHTWARDS ARROWS
270     { 0x21F7, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH VERTICAL STROKE
271     { 0x21F8, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH VERTICAL STROKE
272     { 0x21F9, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH VERTICAL STROKE
273     { 0x21FA, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE
274     { 0x21FB, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE
275     { 0x21FC, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE
276     { 0x21FD, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS OPEN-HEADED ARROW
277     { 0x21FE, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS OPEN-HEADED ARROW
278     { 0x21FF, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT OPEN-HEADED ARROW
279     { 0x2200, Prefix, 2, 1, 0}, // FOR ALL
280     { 0x2201, Infix, 1, 2, 0}, // COMPLEMENT
281     { 0x2202, Prefix, 2, 1, 0}, // PARTIAL DIFFERENTIAL
282     { 0x2203, Prefix, 2, 1, 0}, // THERE EXISTS
283     { 0x2204, Prefix, 2, 1, 0}, // THERE DOES NOT EXIST
284     { 0x2206, Infix, 3, 3, 0}, // INCREMENT
285     { 0x2207, Prefix, 2, 1, 0}, // NABLA
286     { 0x2208, Infix, 5, 5, 0}, // ELEMENT OF
287     { 0x2209, Infix, 5, 5, 0}, // NOT AN ELEMENT OF
288     { 0x220A, Infix, 5, 5, 0}, // SMALL ELEMENT OF
289     { 0x220B, Infix, 5, 5, 0}, // CONTAINS AS MEMBER
290     { 0x220C, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS MEMBER
291     { 0x220D, Infix, 5, 5, 0}, // SMALL CONTAINS AS MEMBER
292     { 0x220E, Infix, 3, 3, 0}, // END OF PROOF
293     { 0x220F, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY PRODUCT
294     { 0x2210, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY COPRODUCT
295     { 0x2211, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SUMMATION
296     { 0x2212, Infix, 4, 4, 0}, // MINUS SIGN
297     { 0x2212, Prefix, 0, 1, 0}, // MINUS SIGN
298     { 0x2213, Infix, 4, 4, 0}, // MINUS-OR-PLUS SIGN
299     { 0x2213, Prefix, 0, 1, 0}, // MINUS-OR-PLUS SIGN
300     { 0x2214, Infix, 4, 4, 0}, // DOT PLUS
301     { 0x2215, Infix, 4, 4, Stretchy}, // DIVISION SLASH
302     { 0x2216, Infix, 4, 4, 0}, // SET MINUS
303     { 0x2217, Infix, 4, 4, 0}, // ASTERISK OPERATOR
304     { 0x2218, Infix, 4, 4, 0}, // RING OPERATOR
305     { 0x2219, Infix, 4, 4, 0}, // BULLET OPERATOR
306     { 0x221A, Prefix, 1, 1, Stretchy}, // SQUARE ROOT
307     { 0x221B, Prefix, 1, 1, 0}, // CUBE ROOT
308     { 0x221C, Prefix, 1, 1, 0}, // FOURTH ROOT
309     { 0x221D, Infix, 5, 5, 0}, // PROPORTIONAL TO
310     { 0x221F, Infix, 5, 5, 0}, // RIGHT ANGLE
311     { 0x2220, Prefix, 0, 0, 0}, // ANGLE
312     { 0x2221, Prefix, 0, 0, 0}, // MEASURED ANGLE
313     { 0x2222, Prefix, 0, 0, 0}, // SPHERICAL ANGLE
314     { 0x2223, Infix, 5, 5, 0}, // DIVIDES
315     { 0x2224, Infix, 5, 5, 0}, // DOES NOT DIVIDE
316     { 0x2225, Infix, 5, 5, 0}, // PARALLEL TO
317     { 0x2225, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // PARALLEL TO
318     { 0x2225, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // PARALLEL TO
319     { 0x2226, Infix, 5, 5, 0}, // NOT PARALLEL TO
320     { 0x2227, Infix, 4, 4, 0}, // LOGICAL AND
321     { 0x2228, Infix, 4, 4, 0}, // LOGICAL OR
322     { 0x2229, Infix, 4, 4, 0}, // INTERSECTION
323     { 0x222A, Infix, 4, 4, 0}, // UNION
324     { 0x222B, Prefix, 0, 1, Symmetric | LargeOp}, // INTEGRAL
325     { 0x222C, Prefix, 0, 1, Symmetric | LargeOp}, // DOUBLE INTEGRAL
326     { 0x222D, Prefix, 0, 1, Symmetric | LargeOp}, // TRIPLE INTEGRAL
327     { 0x222E, Prefix, 0, 1, Symmetric | LargeOp}, // CONTOUR INTEGRAL
328     { 0x222F, Prefix, 0, 1, Symmetric | LargeOp}, // SURFACE INTEGRAL
329     { 0x2230, Prefix, 0, 1, Symmetric | LargeOp}, // VOLUME INTEGRAL
330     { 0x2231, Prefix, 0, 1, Symmetric | LargeOp}, // CLOCKWISE INTEGRAL
331     { 0x2232, Prefix, 0, 1, Symmetric | LargeOp}, // CLOCKWISE CONTOUR INTEGRAL
332     { 0x2233, Prefix, 0, 1, Symmetric | LargeOp}, // ANTICLOCKWISE CONTOUR INTEGRAL
333     { 0x2234, Infix, 5, 5, 0}, // THEREFORE
334     { 0x2235, Infix, 5, 5, 0}, // BECAUSE
335     { 0x2236, Infix, 5, 5, 0}, // RATIO
336     { 0x2237, Infix, 5, 5, 0}, // PROPORTION
337     { 0x2238, Infix, 4, 4, 0}, // DOT MINUS
338     { 0x2239, Infix, 5, 5, 0}, // EXCESS
339     { 0x223A, Infix, 4, 4, 0}, // GEOMETRIC PROPORTION
340     { 0x223B, Infix, 5, 5, 0}, // HOMOTHETIC
341     { 0x223C, Infix, 5, 5, 0}, // TILDE OPERATOR
342     { 0x223D, Infix, 5, 5, 0}, // REVERSED TILDE
343     { 0x223E, Infix, 5, 5, 0}, // INVERTED LAZY S
344     { 0x223F, Infix, 3, 3, 0}, // SINE WAVE
345     { 0x2240, Infix, 4, 4, 0}, // WREATH PRODUCT
346     { 0x2241, Infix, 5, 5, 0}, // NOT TILDE
347     { 0x2242, Infix, 5, 5, 0}, // MINUS TILDE
348     { 0x2243, Infix, 5, 5, 0}, // ASYMPTOTICALLY EQUAL TO
349     { 0x2244, Infix, 5, 5, 0}, // NOT ASYMPTOTICALLY EQUAL TO
350     { 0x2245, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL TO
351     { 0x2246, Infix, 5, 5, 0}, // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
352     { 0x2247, Infix, 5, 5, 0}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
353     { 0x2248, Infix, 5, 5, 0}, // ALMOST EQUAL TO
354     { 0x2249, Infix, 5, 5, 0}, // NOT ALMOST EQUAL TO
355     { 0x224A, Infix, 5, 5, 0}, // ALMOST EQUAL OR EQUAL TO
356     { 0x224B, Infix, 5, 5, 0}, // TRIPLE TILDE
357     { 0x224C, Infix, 5, 5, 0}, // ALL EQUAL TO
358     { 0x224D, Infix, 5, 5, 0}, // EQUIVALENT TO
359     { 0x224E, Infix, 5, 5, 0}, // GEOMETRICALLY EQUIVALENT TO
360     { 0x224F, Infix, 5, 5, 0}, // DIFFERENCE BETWEEN
361     { 0x2250, Infix, 5, 5, 0}, // APPROACHES THE LIMIT
362     { 0x2251, Infix, 5, 5, 0}, // GEOMETRICALLY EQUAL TO
363     { 0x2252, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
364     { 0x2253, Infix, 5, 5, 0}, // IMAGE OF OR APPROXIMATELY EQUAL TO
365     { 0x2254, Infix, 5, 5, 0}, // COLON EQUALS
366     { 0x2255, Infix, 5, 5, 0}, // EQUALS COLON
367     { 0x2256, Infix, 5, 5, 0}, // RING IN EQUAL TO
368     { 0x2257, Infix, 5, 5, 0}, // RING EQUAL TO
369     { 0x2258, Infix, 5, 5, 0}, // CORRESPONDS TO
370     { 0x2259, Infix, 5, 5, 0}, // ESTIMATES
371     { 0x225A, Infix, 5, 5, 0}, // EQUIANGULAR TO
372     { 0x225C, Infix, 5, 5, 0}, // DELTA EQUAL TO
373     { 0x225D, Infix, 5, 5, 0}, // EQUAL TO BY DEFINITION
374     { 0x225E, Infix, 5, 5, 0}, // MEASURED BY
375     { 0x225F, Infix, 5, 5, 0}, // QUESTIONED EQUAL TO
376     { 0x2260, Infix, 5, 5, 0}, // NOT EQUAL TO
377     { 0x2261, Infix, 5, 5, 0}, // IDENTICAL TO
378     { 0x2262, Infix, 5, 5, 0}, // NOT IDENTICAL TO
379     { 0x2263, Infix, 5, 5, 0}, // STRICTLY EQUIVALENT TO
380     { 0x2264, Infix, 5, 5, 0}, // LESS-THAN OR EQUAL TO
381     { 0x2265, Infix, 5, 5, 0}, // GREATER-THAN OR EQUAL TO
382     { 0x2266, Infix, 5, 5, 0}, // LESS-THAN OVER EQUAL TO
383     { 0x2267, Infix, 5, 5, 0}, // GREATER-THAN OVER EQUAL TO
384     { 0x2268, Infix, 5, 5, 0}, // LESS-THAN BUT NOT EQUAL TO
385     { 0x2269, Infix, 5, 5, 0}, // GREATER-THAN BUT NOT EQUAL TO
386     { 0x226A, Infix, 5, 5, 0}, // MUCH LESS-THAN
387     { 0x226B, Infix, 5, 5, 0}, // MUCH GREATER-THAN
388     { 0x226C, Infix, 5, 5, 0}, // BETWEEN
389     { 0x226D, Infix, 5, 5, 0}, // NOT EQUIVALENT TO
390     { 0x226E, Infix, 5, 5, 0}, // NOT LESS-THAN
391     { 0x226F, Infix, 5, 5, 0}, // NOT GREATER-THAN
392     { 0x2270, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR EQUAL TO
393     { 0x2271, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR EQUAL TO
394     { 0x2272, Infix, 5, 5, 0}, // LESS-THAN OR EQUIVALENT TO
395     { 0x2273, Infix, 5, 5, 0}, // GREATER-THAN OR EQUIVALENT TO
396     { 0x2274, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR EQUIVALENT TO
397     { 0x2275, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
398     { 0x2276, Infix, 5, 5, 0}, // LESS-THAN OR GREATER-THAN
399     { 0x2277, Infix, 5, 5, 0}, // GREATER-THAN OR LESS-THAN
400     { 0x2278, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR GREATER-THAN
401     { 0x2279, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR LESS-THAN
402     { 0x227A, Infix, 5, 5, 0}, // PRECEDES
403     { 0x227B, Infix, 5, 5, 0}, // SUCCEEDS
404     { 0x227C, Infix, 5, 5, 0}, // PRECEDES OR EQUAL TO
405     { 0x227D, Infix, 5, 5, 0}, // SUCCEEDS OR EQUAL TO
406     { 0x227E, Infix, 5, 5, 0}, // PRECEDES OR EQUIVALENT TO
407     { 0x227F, Infix, 5, 5, 0}, // SUCCEEDS OR EQUIVALENT TO
408     { 0x2280, Infix, 5, 5, 0}, // DOES NOT PRECEDE
409     { 0x2281, Infix, 5, 5, 0}, // DOES NOT SUCCEED
410     { 0x2282, Infix, 5, 5, 0}, // SUBSET OF
411     { 0x2283, Infix, 5, 5, 0}, // SUPERSET OF
412     { 0x2284, Infix, 5, 5, 0}, // NOT A SUBSET OF
413     { 0x2285, Infix, 5, 5, 0}, // NOT A SUPERSET OF
414     { 0x2286, Infix, 5, 5, 0}, // SUBSET OF OR EQUAL TO
415     { 0x2287, Infix, 5, 5, 0}, // SUPERSET OF OR EQUAL TO
416     { 0x2288, Infix, 5, 5, 0}, // NEITHER A SUBSET OF NOR EQUAL TO
417     { 0x2289, Infix, 5, 5, 0}, // NEITHER A SUPERSET OF NOR EQUAL TO
418     { 0x228A, Infix, 5, 5, 0}, // SUBSET OF WITH NOT EQUAL TO
419     { 0x228B, Infix, 5, 5, 0}, // SUPERSET OF WITH NOT EQUAL TO
420     { 0x228C, Infix, 4, 4, 0}, // MULTISET
421     { 0x228D, Infix, 4, 4, 0}, // MULTISET MULTIPLICATION
422     { 0x228E, Infix, 4, 4, 0}, // MULTISET UNION
423     { 0x228F, Infix, 5, 5, 0}, // SQUARE IMAGE OF
424     { 0x2290, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF
425     { 0x2291, Infix, 5, 5, 0}, // SQUARE IMAGE OF OR EQUAL TO
426     { 0x2292, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF OR EQUAL TO
427     { 0x2293, Infix, 4, 4, 0}, // SQUARE CAP
428     { 0x2294, Infix, 4, 4, 0}, // SQUARE CUP
429     { 0x2295, Infix, 4, 4, 0}, // CIRCLED PLUS
430     { 0x2296, Infix, 4, 4, 0}, // CIRCLED MINUS
431     { 0x2297, Infix, 4, 4, 0}, // CIRCLED TIMES
432     { 0x2298, Infix, 4, 4, 0}, // CIRCLED DIVISION SLASH
433     { 0x2299, Infix, 4, 4, 0}, // CIRCLED DOT OPERATOR
434     { 0x229A, Infix, 4, 4, 0}, // CIRCLED RING OPERATOR
435     { 0x229B, Infix, 4, 4, 0}, // CIRCLED ASTERISK OPERATOR
436     { 0x229C, Infix, 4, 4, 0}, // CIRCLED EQUALS
437     { 0x229D, Infix, 4, 4, 0}, // CIRCLED DASH
438     { 0x229E, Infix, 4, 4, 0}, // SQUARED PLUS
439     { 0x229F, Infix, 4, 4, 0}, // SQUARED MINUS
440     { 0x22A0, Infix, 4, 4, 0}, // SQUARED TIMES
441     { 0x22A1, Infix, 4, 4, 0}, // SQUARED DOT OPERATOR
442     { 0x22A2, Infix, 5, 5, 0}, // RIGHT TACK
443     { 0x22A3, Infix, 5, 5, 0}, // LEFT TACK
444     { 0x22A4, Infix, 5, 5, 0}, // DOWN TACK
445     { 0x22A5, Infix, 5, 5, 0}, // UP TACK
446     { 0x22A6, Infix, 5, 5, 0}, // ASSERTION
447     { 0x22A7, Infix, 5, 5, 0}, // MODELS
448     { 0x22A8, Infix, 5, 5, 0}, // TRUE
449     { 0x22A9, Infix, 5, 5, 0}, // FORCES
450     { 0x22AA, Infix, 5, 5, 0}, // TRIPLE VERTICAL BAR RIGHT TURNSTILE
451     { 0x22AB, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
452     { 0x22AC, Infix, 5, 5, 0}, // DOES NOT PROVE
453     { 0x22AD, Infix, 5, 5, 0}, // NOT TRUE
454     { 0x22AE, Infix, 5, 5, 0}, // DOES NOT FORCE
455     { 0x22AF, Infix, 5, 5, 0}, // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
456     { 0x22B0, Infix, 5, 5, 0}, // PRECEDES UNDER RELATION
457     { 0x22B1, Infix, 5, 5, 0}, // SUCCEEDS UNDER RELATION
458     { 0x22B2, Infix, 5, 5, 0}, // NORMAL SUBGROUP OF
459     { 0x22B3, Infix, 5, 5, 0}, // CONTAINS AS NORMAL SUBGROUP
460     { 0x22B4, Infix, 5, 5, 0}, // NORMAL SUBGROUP OF OR EQUAL TO
461     { 0x22B5, Infix, 5, 5, 0}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
462     { 0x22B6, Infix, 5, 5, 0}, // ORIGINAL OF
463     { 0x22B7, Infix, 5, 5, 0}, // IMAGE OF
464     { 0x22B8, Infix, 5, 5, 0}, // MULTIMAP
465     { 0x22B9, Infix, 5, 5, 0}, // HERMITIAN CONJUGATE MATRIX
466     { 0x22BA, Infix, 4, 4, 0}, // INTERCALATE
467     { 0x22BB, Infix, 4, 4, 0}, // XOR
468     { 0x22BC, Infix, 4, 4, 0}, // NAND
469     { 0x22BD, Infix, 4, 4, 0}, // NOR
470     { 0x22BE, Infix, 3, 3, 0}, // RIGHT ANGLE WITH ARC
471     { 0x22BF, Infix, 3, 3, 0}, // RIGHT TRIANGLE
472     { 0x22C0, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY LOGICAL AND
473     { 0x22C1, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY LOGICAL OR
474     { 0x22C2, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY INTERSECTION
475     { 0x22C3, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION
476     { 0x22C4, Infix, 4, 4, 0}, // DIAMOND OPERATOR
477     { 0x22C5, Infix, 4, 4, 0}, // DOT OPERATOR
478     { 0x22C6, Infix, 4, 4, 0}, // STAR OPERATOR
479     { 0x22C7, Infix, 4, 4, 0}, // DIVISION TIMES
480     { 0x22C8, Infix, 5, 5, 0}, // BOWTIE
481     { 0x22C9, Infix, 4, 4, 0}, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
482     { 0x22CA, Infix, 4, 4, 0}, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
483     { 0x22CB, Infix, 4, 4, 0}, // LEFT SEMIDIRECT PRODUCT
484     { 0x22CC, Infix, 4, 4, 0}, // RIGHT SEMIDIRECT PRODUCT
485     { 0x22CD, Infix, 5, 5, 0}, // REVERSED TILDE EQUALS
486     { 0x22CE, Infix, 4, 4, 0}, // CURLY LOGICAL OR
487     { 0x22CF, Infix, 4, 4, 0}, // CURLY LOGICAL AND
488     { 0x22D0, Infix, 5, 5, 0}, // DOUBLE SUBSET
489     { 0x22D1, Infix, 5, 5, 0}, // DOUBLE SUPERSET
490     { 0x22D2, Infix, 4, 4, 0}, // DOUBLE INTERSECTION
491     { 0x22D3, Infix, 4, 4, 0}, // DOUBLE UNION
492     { 0x22D4, Infix, 5, 5, 0}, // PITCHFORK
493     { 0x22D5, Infix, 5, 5, 0}, // EQUAL AND PARALLEL TO
494     { 0x22D6, Infix, 5, 5, 0}, // LESS-THAN WITH DOT
495     { 0x22D7, Infix, 5, 5, 0}, // GREATER-THAN WITH DOT
496     { 0x22D8, Infix, 5, 5, 0}, // VERY MUCH LESS-THAN
497     { 0x22D9, Infix, 5, 5, 0}, // VERY MUCH GREATER-THAN
498     { 0x22DA, Infix, 5, 5, 0}, // LESS-THAN EQUAL TO OR GREATER-THAN
499     { 0x22DB, Infix, 5, 5, 0}, // GREATER-THAN EQUAL TO OR LESS-THAN
500     { 0x22DC, Infix, 5, 5, 0}, // EQUAL TO OR LESS-THAN
501     { 0x22DD, Infix, 5, 5, 0}, // EQUAL TO OR GREATER-THAN
502     { 0x22DE, Infix, 5, 5, 0}, // EQUAL TO OR PRECEDES
503     { 0x22DF, Infix, 5, 5, 0}, // EQUAL TO OR SUCCEEDS
504     { 0x22E0, Infix, 5, 5, 0}, // DOES NOT PRECEDE OR EQUAL
505     { 0x22E1, Infix, 5, 5, 0}, // DOES NOT SUCCEED OR EQUAL
506     { 0x22E2, Infix, 5, 5, 0}, // NOT SQUARE IMAGE OF OR EQUAL TO
507     { 0x22E3, Infix, 5, 5, 0}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
508     { 0x22E4, Infix, 5, 5, 0}, // SQUARE IMAGE OF OR NOT EQUAL TO
509     { 0x22E5, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF OR NOT EQUAL TO
510     { 0x22E6, Infix, 5, 5, 0}, // LESS-THAN BUT NOT EQUIVALENT TO
511     { 0x22E7, Infix, 5, 5, 0}, // GREATER-THAN BUT NOT EQUIVALENT TO
512     { 0x22E8, Infix, 5, 5, 0}, // PRECEDES BUT NOT EQUIVALENT TO
513     { 0x22E9, Infix, 5, 5, 0}, // SUCCEEDS BUT NOT EQUIVALENT TO
514     { 0x22EA, Infix, 5, 5, 0}, // NOT NORMAL SUBGROUP OF
515     { 0x22EB, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
516     { 0x22EC, Infix, 5, 5, 0}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
517     { 0x22ED, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
518     { 0x22EE, Infix, 5, 5, 0}, // VERTICAL ELLIPSIS
519     { 0x22EF, Infix, 0, 0, 0}, // MIDLINE HORIZONTAL ELLIPSIS
520     { 0x22F0, Infix, 5, 5, 0}, // UP RIGHT DIAGONAL ELLIPSIS
521     { 0x22F1, Infix, 5, 5, 0}, // DOWN RIGHT DIAGONAL ELLIPSIS
522     { 0x22F2, Infix, 5, 5, 0}, // ELEMENT OF WITH LONG HORIZONTAL STROKE
523     { 0x22F3, Infix, 5, 5, 0}, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
524     { 0x22F4, Infix, 5, 5, 0}, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
525     { 0x22F5, Infix, 5, 5, 0}, // ELEMENT OF WITH DOT ABOVE
526     { 0x22F6, Infix, 5, 5, 0}, // ELEMENT OF WITH OVERBAR
527     { 0x22F7, Infix, 5, 5, 0}, // SMALL ELEMENT OF WITH OVERBAR
528     { 0x22F8, Infix, 5, 5, 0}, // ELEMENT OF WITH UNDERBAR
529     { 0x22F9, Infix, 5, 5, 0}, // ELEMENT OF WITH TWO HORIZONTAL STROKES
530     { 0x22FA, Infix, 5, 5, 0}, // CONTAINS WITH LONG HORIZONTAL STROKE
531     { 0x22FB, Infix, 5, 5, 0}, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
532     { 0x22FC, Infix, 5, 5, 0}, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
533     { 0x22FD, Infix, 5, 5, 0}, // CONTAINS WITH OVERBAR
534     { 0x22FE, Infix, 5, 5, 0}, // SMALL CONTAINS WITH OVERBAR
535     { 0x22FF, Infix, 5, 5, 0}, // Z NOTATION BAG MEMBERSHIP
536     { 0x2308, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT CEILING
537     { 0x2309, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT CEILING
538     { 0x230A, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT FLOOR
539     { 0x230B, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT FLOOR
540     { 0x23B4, Postfix, 0, 0, Accent | Stretchy}, // TOP SQUARE BRACKET
541     { 0x23B5, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM SQUARE BRACKET
542     { 0x23DC, Postfix, 0, 0, Accent | Stretchy}, // TOP PARENTHESIS
543     { 0x23DD, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM PARENTHESIS
544     { 0x23DE, Postfix, 0, 0, Accent | Stretchy}, // TOP CURLY BRACKET
545     { 0x23DF, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM CURLY BRACKET
546     { 0x23E0, Postfix, 0, 0, Accent | Stretchy}, // TOP TORTOISE SHELL BRACKET
547     { 0x23E1, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM TORTOISE SHELL BRACKET
548     { 0x25A0, Infix, 3, 3, 0}, // BLACK SQUARE
549     { 0x25A1, Infix, 3, 3, 0}, // WHITE SQUARE
550     { 0x25AA, Infix, 3, 3, 0}, // BLACK SMALL SQUARE
551     { 0x25AB, Infix, 3, 3, 0}, // WHITE SMALL SQUARE
552     { 0x25AD, Infix, 3, 3, 0}, // WHITE RECTANGLE
553     { 0x25AE, Infix, 3, 3, 0}, // BLACK VERTICAL RECTANGLE
554     { 0x25AF, Infix, 3, 3, 0}, // WHITE VERTICAL RECTANGLE
555     { 0x25B0, Infix, 3, 3, 0}, // BLACK PARALLELOGRAM
556     { 0x25B1, Infix, 3, 3, 0}, // WHITE PARALLELOGRAM
557     { 0x25B2, Infix, 4, 4, 0}, // BLACK UP-POINTING TRIANGLE
558     { 0x25B3, Infix, 4, 4, 0}, // WHITE UP-POINTING TRIANGLE
559     { 0x25B4, Infix, 4, 4, 0}, // BLACK UP-POINTING SMALL TRIANGLE
560     { 0x25B5, Infix, 4, 4, 0}, // WHITE UP-POINTING SMALL TRIANGLE
561     { 0x25B6, Infix, 4, 4, 0}, // BLACK RIGHT-POINTING TRIANGLE
562     { 0x25B7, Infix, 4, 4, 0}, // WHITE RIGHT-POINTING TRIANGLE
563     { 0x25B8, Infix, 4, 4, 0}, // BLACK RIGHT-POINTING SMALL TRIANGLE
564     { 0x25B9, Infix, 4, 4, 0}, // WHITE RIGHT-POINTING SMALL TRIANGLE
565     { 0x25BC, Infix, 4, 4, 0}, // BLACK DOWN-POINTING TRIANGLE
566     { 0x25BD, Infix, 4, 4, 0}, // WHITE DOWN-POINTING TRIANGLE
567     { 0x25BE, Infix, 4, 4, 0}, // BLACK DOWN-POINTING SMALL TRIANGLE
568     { 0x25BF, Infix, 4, 4, 0}, // WHITE DOWN-POINTING SMALL TRIANGLE
569     { 0x25C0, Infix, 4, 4, 0}, // BLACK LEFT-POINTING TRIANGLE
570     { 0x25C1, Infix, 4, 4, 0}, // WHITE LEFT-POINTING TRIANGLE
571     { 0x25C2, Infix, 4, 4, 0}, // BLACK LEFT-POINTING SMALL TRIANGLE
572     { 0x25C3, Infix, 4, 4, 0}, // WHITE LEFT-POINTING SMALL TRIANGLE
573     { 0x25C4, Infix, 4, 4, 0}, // BLACK LEFT-POINTING POINTER
574     { 0x25C5, Infix, 4, 4, 0}, // WHITE LEFT-POINTING POINTER
575     { 0x25C6, Infix, 4, 4, 0}, // BLACK DIAMOND
576     { 0x25C7, Infix, 4, 4, 0}, // WHITE DIAMOND
577     { 0x25C8, Infix, 4, 4, 0}, // WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND
578     { 0x25C9, Infix, 4, 4, 0}, // FISHEYE
579     { 0x25CC, Infix, 4, 4, 0}, // DOTTED CIRCLE
580     { 0x25CD, Infix, 4, 4, 0}, // CIRCLE WITH VERTICAL FILL
581     { 0x25CE, Infix, 4, 4, 0}, // BULLSEYE
582     { 0x25CF, Infix, 4, 4, 0}, // BLACK CIRCLE
583     { 0x25D6, Infix, 4, 4, 0}, // LEFT HALF BLACK CIRCLE
584     { 0x25D7, Infix, 4, 4, 0}, // RIGHT HALF BLACK CIRCLE
585     { 0x25E6, Infix, 4, 4, 0}, // WHITE BULLET
586     { 0x266D, Postfix, 0, 2, 0}, // MUSIC FLAT SIGN
587     { 0x266E, Postfix, 0, 2, 0}, // MUSIC NATURAL SIGN
588     { 0x266F, Postfix, 0, 2, 0}, // MUSIC SHARP SIGN
589     { 0x2758, Infix, 5, 5, 0}, // LIGHT VERTICAL BAR
590     { 0x2772, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
591     { 0x2773, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
592     { 0x27E6, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
593     { 0x27E7, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
594     { 0x27E8, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT ANGLE BRACKET
595     { 0x27E9, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT ANGLE BRACKET
596     { 0x27EA, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
597     { 0x27EB, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
598     { 0x27EC, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
599     { 0x27ED, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
600     { 0x27EE, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT FLATTENED PARENTHESIS
601     { 0x27EF, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT FLATTENED PARENTHESIS
602     { 0x27F0, Infix, 5, 5, Stretchy}, // UPWARDS QUADRUPLE ARROW
603     { 0x27F1, Infix, 5, 5, Stretchy}, // DOWNWARDS QUADRUPLE ARROW
604     { 0x27F5, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS ARROW
605     { 0x27F6, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS ARROW
606     { 0x27F7, Infix, 5, 5, Stretchy | Accent}, // LONG LEFT RIGHT ARROW
607     { 0x27F8, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS DOUBLE ARROW
608     { 0x27F9, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS DOUBLE ARROW
609     { 0x27FA, Infix, 5, 5, Stretchy | Accent}, // LONG LEFT RIGHT DOUBLE ARROW
610     { 0x27FB, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS ARROW FROM BAR
611     { 0x27FC, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS ARROW FROM BAR
612     { 0x27FD, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS DOUBLE ARROW FROM BAR
613     { 0x27FE, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS DOUBLE ARROW FROM BAR
614     { 0x27FF, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS SQUIGGLE ARROW
615     { 0x2900, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE
616     { 0x2901, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE
617     { 0x2902, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE
618     { 0x2903, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE
619     { 0x2904, Infix, 5, 5, Accent}, // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE
620     { 0x2905, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW FROM BAR
621     { 0x2906, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW FROM BAR
622     { 0x2907, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW FROM BAR
623     { 0x2908, Infix, 5, 5, 0}, // DOWNWARDS ARROW WITH HORIZONTAL STROKE
624     { 0x2909, Infix, 5, 5, 0}, // UPWARDS ARROW WITH HORIZONTAL STROKE
625     { 0x290A, Infix, 5, 5, Stretchy}, // UPWARDS TRIPLE ARROW
626     { 0x290B, Infix, 5, 5, Stretchy}, // DOWNWARDS TRIPLE ARROW
627     { 0x290C, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DOUBLE DASH ARROW
628     { 0x290D, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DOUBLE DASH ARROW
629     { 0x290E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TRIPLE DASH ARROW
630     { 0x290F, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TRIPLE DASH ARROW
631     { 0x2910, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
632     { 0x2911, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH DOTTED STEM
633     { 0x2912, Infix, 5, 5, Stretchy}, // UPWARDS ARROW TO BAR
634     { 0x2913, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW TO BAR
635     { 0x2914, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE
636     { 0x2915, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE
637     { 0x2916, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL
638     { 0x2917, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE
639     { 0x2918, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE
640     { 0x2919, Infix, 5, 5, Accent}, // LEFTWARDS ARROW-TAIL
641     { 0x291A, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW-TAIL
642     { 0x291B, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW-TAIL
643     { 0x291C, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW-TAIL
644     { 0x291D, Infix, 5, 5, Accent}, // LEFTWARDS ARROW TO BLACK DIAMOND
645     { 0x291E, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW TO BLACK DIAMOND
646     { 0x291F, Infix, 5, 5, Accent}, // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND
647     { 0x2920, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND
648     { 0x2921, Infix, 5, 5, Stretchy}, // NORTH WEST AND SOUTH EAST ARROW
649     { 0x2922, Infix, 5, 5, Stretchy}, // NORTH EAST AND SOUTH WEST ARROW
650     { 0x2923, Infix, 5, 5, 0}, // NORTH WEST ARROW WITH HOOK
651     { 0x2924, Infix, 5, 5, 0}, // NORTH EAST ARROW WITH HOOK
652     { 0x2925, Infix, 5, 5, 0}, // SOUTH EAST ARROW WITH HOOK
653     { 0x2926, Infix, 5, 5, 0}, // SOUTH WEST ARROW WITH HOOK
654     { 0x2927, Infix, 5, 5, 0}, // NORTH WEST ARROW AND NORTH EAST ARROW
655     { 0x2928, Infix, 5, 5, 0}, // NORTH EAST ARROW AND SOUTH EAST ARROW
656     { 0x2929, Infix, 5, 5, 0}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
657     { 0x292A, Infix, 5, 5, 0}, // SOUTH WEST ARROW AND NORTH WEST ARROW
658     { 0x292B, Infix, 5, 5, 0}, // RISING DIAGONAL CROSSING FALLING DIAGONAL
659     { 0x292C, Infix, 5, 5, 0}, // FALLING DIAGONAL CROSSING RISING DIAGONAL
660     { 0x292D, Infix, 5, 5, 0}, // SOUTH EAST ARROW CROSSING NORTH EAST ARROW
661     { 0x292E, Infix, 5, 5, 0}, // NORTH EAST ARROW CROSSING SOUTH EAST ARROW
662     { 0x292F, Infix, 5, 5, 0}, // FALLING DIAGONAL CROSSING NORTH EAST ARROW
663     { 0x2930, Infix, 5, 5, 0}, // RISING DIAGONAL CROSSING SOUTH EAST ARROW
664     { 0x2931, Infix, 5, 5, 0}, // NORTH EAST ARROW CROSSING NORTH WEST ARROW
665     { 0x2932, Infix, 5, 5, 0}, // NORTH WEST ARROW CROSSING NORTH EAST ARROW
666     { 0x2933, Infix, 5, 5, Accent}, // WAVE ARROW POINTING DIRECTLY RIGHT
667     { 0x2934, Infix, 5, 5, 0}, // ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS
668     { 0x2935, Infix, 5, 5, 0}, // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS
669     { 0x2936, Infix, 5, 5, 0}, // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
670     { 0x2937, Infix, 5, 5, 0}, // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS
671     { 0x2938, Infix, 5, 5, 0}, // RIGHT-SIDE ARC CLOCKWISE ARROW
672     { 0x2939, Infix, 5, 5, 0}, // LEFT-SIDE ARC ANTICLOCKWISE ARROW
673     { 0x293A, Infix, 5, 5, Accent}, // TOP ARC ANTICLOCKWISE ARROW
674     { 0x293B, Infix, 5, 5, Accent}, // BOTTOM ARC ANTICLOCKWISE ARROW
675     { 0x293C, Infix, 5, 5, Accent}, // TOP ARC CLOCKWISE ARROW WITH MINUS
676     { 0x293D, Infix, 5, 5, Accent}, // TOP ARC ANTICLOCKWISE ARROW WITH PLUS
677     { 0x293E, Infix, 5, 5, 0}, // LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW
678     { 0x293F, Infix, 5, 5, 0}, // LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW
679     { 0x2940, Infix, 5, 5, 0}, // ANTICLOCKWISE CLOSED CIRCLE ARROW
680     { 0x2941, Infix, 5, 5, 0}, // CLOCKWISE CLOSED CIRCLE ARROW
681     { 0x2942, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW
682     { 0x2943, Infix, 5, 5, Accent}, // LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW
683     { 0x2944, Infix, 5, 5, Accent}, // SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW
684     { 0x2945, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH PLUS BELOW
685     { 0x2946, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH PLUS BELOW
686     { 0x2947, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW THROUGH X
687     { 0x2948, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW THROUGH SMALL CIRCLE
688     { 0x2949, Infix, 5, 5, 0}, // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE
689     { 0x294A, Infix, 5, 5, Accent}, // LEFT BARB UP RIGHT BARB DOWN HARPOON
690     { 0x294B, Infix, 5, 5, Accent}, // LEFT BARB DOWN RIGHT BARB UP HARPOON
691     { 0x294C, Infix, 5, 5, 0}, // UP BARB RIGHT DOWN BARB LEFT HARPOON
692     { 0x294D, Infix, 5, 5, 0}, // UP BARB LEFT DOWN BARB RIGHT HARPOON
693     { 0x294E, Infix, 5, 5, Stretchy | Accent}, // LEFT BARB UP RIGHT BARB UP HARPOON
694     { 0x294F, Infix, 5, 5, Stretchy}, // UP BARB RIGHT DOWN BARB RIGHT HARPOON
695     { 0x2950, Infix, 5, 5, Stretchy | Accent}, // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
696     { 0x2951, Infix, 5, 5, Stretchy}, // UP BARB LEFT DOWN BARB LEFT HARPOON
697     { 0x2952, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UP TO BAR
698     { 0x2953, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UP TO BAR
699     { 0x2954, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHT TO BAR
700     { 0x2955, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
701     { 0x2956, Infix, 5, 5, Stretchy}, // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
702     { 0x2957, Infix, 5, 5, Stretchy}, // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
703     { 0x2958, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT TO BAR
704     { 0x2959, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
705     { 0x295A, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UP FROM BAR
706     { 0x295B, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
707     { 0x295C, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
708     { 0x295D, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
709     { 0x295E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
710     { 0x295F, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
711     { 0x2960, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT FROM BAR
712     { 0x2961, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
713     { 0x2962, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
714     { 0x2963, Infix, 5, 5, 0}, // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
715     { 0x2964, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
716     { 0x2965, Infix, 5, 5, 0}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
717     { 0x2966, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP
718     { 0x2967, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
719     { 0x2968, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP
720     { 0x2969, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN
721     { 0x296A, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
722     { 0x296B, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
723     { 0x296C, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
724     { 0x296D, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
725     { 0x296E, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
726     { 0x296F, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
727     { 0x2970, Infix, 5, 5, Accent}, // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
728     { 0x2971, Infix, 5, 5, Accent}, // EQUALS SIGN ABOVE RIGHTWARDS ARROW
729     { 0x2972, Infix, 5, 5, Accent}, // TILDE OPERATOR ABOVE RIGHTWARDS ARROW
730     { 0x2973, Infix, 5, 5, Accent}, // LEFTWARDS ARROW ABOVE TILDE OPERATOR
731     { 0x2974, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE TILDE OPERATOR
732     { 0x2975, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO
733     { 0x2976, Infix, 5, 5, Accent}, // LESS-THAN ABOVE LEFTWARDS ARROW
734     { 0x2977, Infix, 5, 5, Accent}, // LEFTWARDS ARROW THROUGH LESS-THAN
735     { 0x2978, Infix, 5, 5, Accent}, // GREATER-THAN ABOVE RIGHTWARDS ARROW
736     { 0x2979, Infix, 5, 5, Accent}, // SUBSET ABOVE RIGHTWARDS ARROW
737     { 0x297A, Infix, 5, 5, Accent}, // LEFTWARDS ARROW THROUGH SUBSET
738     { 0x297B, Infix, 5, 5, Accent}, // SUPERSET ABOVE LEFTWARDS ARROW
739     { 0x297C, Infix, 5, 5, Accent}, // LEFT FISH TAIL
740     { 0x297D, Infix, 5, 5, Accent}, // RIGHT FISH TAIL
741     { 0x297E, Infix, 5, 5, 0}, // UP FISH TAIL
742     { 0x297F, Infix, 5, 5, 0}, // DOWN FISH TAIL
743     { 0x2980, Prefix, 0, 0, Fence | Stretchy}, // TRIPLE VERTICAL BAR DELIMITER
744     { 0x2980, Postfix, 0, 0, Fence | Stretchy}, // TRIPLE VERTICAL BAR DELIMITER
745     { 0x2981, Infix, 3, 3, 0}, // Z NOTATION SPOT
746     { 0x2982, Infix, 3, 3, 0}, // Z NOTATION TYPE COLON
747     { 0x2983, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT WHITE CURLY BRACKET
748     { 0x2984, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT WHITE CURLY BRACKET
749     { 0x2985, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT WHITE PARENTHESIS
750     { 0x2986, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT WHITE PARENTHESIS
751     { 0x2987, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION LEFT IMAGE BRACKET
752     { 0x2988, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION RIGHT IMAGE BRACKET
753     { 0x2989, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION LEFT BINDING BRACKET
754     { 0x298A, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION RIGHT BINDING BRACKET
755     { 0x298B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH UNDERBAR
756     { 0x298C, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH UNDERBAR
757     { 0x298D, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
758     { 0x298E, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
759     { 0x298F, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
760     { 0x2990, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
761     { 0x2991, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT ANGLE BRACKET WITH DOT
762     { 0x2992, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT ANGLE BRACKET WITH DOT
763     { 0x2993, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT ARC LESS-THAN BRACKET
764     { 0x2994, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT ARC GREATER-THAN BRACKET
765     { 0x2995, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // DOUBLE LEFT ARC GREATER-THAN BRACKET
766     { 0x2996, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // DOUBLE RIGHT ARC LESS-THAN BRACKET
767     { 0x2997, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT BLACK TORTOISE SHELL BRACKET
768     { 0x2998, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT BLACK TORTOISE SHELL BRACKET
769     { 0x2999, Infix, 3, 3, 0}, // DOTTED FENCE
770     { 0x299A, Infix, 3, 3, 0}, // VERTICAL ZIGZAG LINE
771     { 0x299B, Infix, 3, 3, 0}, // MEASURED ANGLE OPENING LEFT
772     { 0x299C, Infix, 3, 3, 0}, // RIGHT ANGLE VARIANT WITH SQUARE
773     { 0x299D, Infix, 3, 3, 0}, // MEASURED RIGHT ANGLE WITH DOT
774     { 0x299E, Infix, 3, 3, 0}, // ANGLE WITH S INSIDE
775     { 0x299F, Infix, 3, 3, 0}, // ACUTE ANGLE
776     { 0x29A0, Infix, 3, 3, 0}, // SPHERICAL ANGLE OPENING LEFT
777     { 0x29A1, Infix, 3, 3, 0}, // SPHERICAL ANGLE OPENING UP
778     { 0x29A2, Infix, 3, 3, 0}, // TURNED ANGLE
779     { 0x29A3, Infix, 3, 3, 0}, // REVERSED ANGLE
780     { 0x29A4, Infix, 3, 3, 0}, // ANGLE WITH UNDERBAR
781     { 0x29A5, Infix, 3, 3, 0}, // REVERSED ANGLE WITH UNDERBAR
782     { 0x29A6, Infix, 3, 3, 0}, // OBLIQUE ANGLE OPENING UP
783     { 0x29A7, Infix, 3, 3, 0}, // OBLIQUE ANGLE OPENING DOWN
784     { 0x29A8, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
785     { 0x29A9, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
786     { 0x29AA, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
787     { 0x29AB, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
788     { 0x29AC, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
789     { 0x29AD, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
790     { 0x29AE, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
791     { 0x29AF, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
792     { 0x29B0, Infix, 3, 3, 0}, // REVERSED EMPTY SET
793     { 0x29B1, Infix, 3, 3, 0}, // EMPTY SET WITH OVERBAR
794     { 0x29B2, Infix, 3, 3, 0}, // EMPTY SET WITH SMALL CIRCLE ABOVE
795     { 0x29B3, Infix, 3, 3, 0}, // EMPTY SET WITH RIGHT ARROW ABOVE
796     { 0x29B4, Infix, 3, 3, 0}, // EMPTY SET WITH LEFT ARROW ABOVE
797     { 0x29B5, Infix, 3, 3, 0}, // CIRCLE WITH HORIZONTAL BAR
798     { 0x29B6, Infix, 4, 4, 0}, // CIRCLED VERTICAL BAR
799     { 0x29B7, Infix, 4, 4, 0}, // CIRCLED PARALLEL
800     { 0x29B8, Infix, 4, 4, 0}, // CIRCLED REVERSE SOLIDUS
801     { 0x29B9, Infix, 4, 4, 0}, // CIRCLED PERPENDICULAR
802     { 0x29BA, Infix, 4, 4, 0}, // CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR
803     { 0x29BB, Infix, 4, 4, 0}, // CIRCLE WITH SUPERIMPOSED X
804     { 0x29BC, Infix, 4, 4, 0}, // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN
805     { 0x29BD, Infix, 4, 4, 0}, // UP ARROW THROUGH CIRCLE
806     { 0x29BE, Infix, 4, 4, 0}, // CIRCLED WHITE BULLET
807     { 0x29BF, Infix, 4, 4, 0}, // CIRCLED BULLET
808     { 0x29C0, Infix, 5, 5, 0}, // CIRCLED LESS-THAN
809     { 0x29C1, Infix, 5, 5, 0}, // CIRCLED GREATER-THAN
810     { 0x29C2, Infix, 3, 3, 0}, // CIRCLE WITH SMALL CIRCLE TO THE RIGHT
811     { 0x29C3, Infix, 3, 3, 0}, // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
812     { 0x29C4, Infix, 4, 4, 0}, // SQUARED RISING DIAGONAL SLASH
813     { 0x29C5, Infix, 4, 4, 0}, // SQUARED FALLING DIAGONAL SLASH
814     { 0x29C6, Infix, 4, 4, 0}, // SQUARED ASTERISK
815     { 0x29C7, Infix, 4, 4, 0}, // SQUARED SMALL CIRCLE
816     { 0x29C8, Infix, 4, 4, 0}, // SQUARED SQUARE
817     { 0x29C9, Infix, 3, 3, 0}, // TWO JOINED SQUARES
818     { 0x29CA, Infix, 3, 3, 0}, // TRIANGLE WITH DOT ABOVE
819     { 0x29CB, Infix, 3, 3, 0}, // TRIANGLE WITH UNDERBAR
820     { 0x29CC, Infix, 3, 3, 0}, // S IN TRIANGLE
821     { 0x29CD, Infix, 3, 3, 0}, // TRIANGLE WITH SERIFS AT BOTTOM
822     { 0x29CE, Infix, 5, 5, 0}, // RIGHT TRIANGLE ABOVE LEFT TRIANGLE
823     { 0x29CF, Infix, 5, 5, 0}, // LEFT TRIANGLE BESIDE VERTICAL BAR
824     { 0x29D0, Infix, 5, 5, 0}, // VERTICAL BAR BESIDE RIGHT TRIANGLE
825     { 0x29D1, Infix, 5, 5, 0}, // BOWTIE WITH LEFT HALF BLACK
826     { 0x29D2, Infix, 5, 5, 0}, // BOWTIE WITH RIGHT HALF BLACK
827     { 0x29D3, Infix, 5, 5, 0}, // BLACK BOWTIE
828     { 0x29D4, Infix, 5, 5, 0}, // TIMES WITH LEFT HALF BLACK
829     { 0x29D5, Infix, 5, 5, 0}, // TIMES WITH RIGHT HALF BLACK
830     { 0x29D6, Infix, 4, 4, 0}, // WHITE HOURGLASS
831     { 0x29D7, Infix, 4, 4, 0}, // BLACK HOURGLASS
832     { 0x29D8, Infix, 3, 3, 0}, // LEFT WIGGLY FENCE
833     { 0x29D9, Infix, 3, 3, 0}, // RIGHT WIGGLY FENCE
834     { 0x29DB, Infix, 3, 3, 0}, // RIGHT DOUBLE WIGGLY FENCE
835     { 0x29DC, Infix, 3, 3, 0}, // INCOMPLETE INFINITY
836     { 0x29DD, Infix, 3, 3, 0}, // TIE OVER INFINITY
837     { 0x29DE, Infix, 5, 5, 0}, // INFINITY NEGATED WITH VERTICAL BAR
838     { 0x29DF, Infix, 3, 3, 0}, // DOUBLE-ENDED MULTIMAP
839     { 0x29E0, Infix, 3, 3, 0}, // SQUARE WITH CONTOURED OUTLINE
840     { 0x29E1, Infix, 5, 5, 0}, // INCREASES AS
841     { 0x29E2, Infix, 4, 4, 0}, // SHUFFLE PRODUCT
842     { 0x29E3, Infix, 5, 5, 0}, // EQUALS SIGN AND SLANTED PARALLEL
843     { 0x29E4, Infix, 5, 5, 0}, // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
844     { 0x29E5, Infix, 5, 5, 0}, // IDENTICAL TO AND SLANTED PARALLEL
845     { 0x29E6, Infix, 5, 5, 0}, // GLEICH STARK
846     { 0x29E7, Infix, 3, 3, 0}, // THERMODYNAMIC
847     { 0x29E8, Infix, 3, 3, 0}, // DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK
848     { 0x29E9, Infix, 3, 3, 0}, // DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK
849     { 0x29EA, Infix, 3, 3, 0}, // BLACK DIAMOND WITH DOWN ARROW
850     { 0x29EB, Infix, 3, 3, 0}, // BLACK LOZENGE
851     { 0x29EC, Infix, 3, 3, 0}, // WHITE CIRCLE WITH DOWN ARROW
852     { 0x29ED, Infix, 3, 3, 0}, // BLACK CIRCLE WITH DOWN ARROW
853     { 0x29EE, Infix, 3, 3, 0}, // ERROR-BARRED WHITE SQUARE
854     { 0x29EF, Infix, 3, 3, 0}, // ERROR-BARRED BLACK SQUARE
855     { 0x29F0, Infix, 3, 3, 0}, // ERROR-BARRED WHITE DIAMOND
856     { 0x29F1, Infix, 3, 3, 0}, // ERROR-BARRED BLACK DIAMOND
857     { 0x29F2, Infix, 3, 3, 0}, // ERROR-BARRED WHITE CIRCLE
858     { 0x29F3, Infix, 3, 3, 0}, // ERROR-BARRED BLACK CIRCLE
859     { 0x29F4, Infix, 5, 5, 0}, // RULE-DELAYED
860     { 0x29F5, Infix, 4, 4, 0}, // REVERSE SOLIDUS OPERATOR
861     { 0x29F6, Infix, 4, 4, 0}, // SOLIDUS WITH OVERBAR
862     { 0x29F7, Infix, 4, 4, 0}, // REVERSE SOLIDUS WITH HORIZONTAL STROKE
863     { 0x29F8, Infix, 3, 3, 0}, // BIG SOLIDUS
864     { 0x29F9, Infix, 3, 3, 0}, // BIG REVERSE SOLIDUS
865     { 0x29FA, Infix, 3, 3, 0}, // DOUBLE PLUS
866     { 0x29FB, Infix, 3, 3, 0}, // TRIPLE PLUS
867     { 0x29FC, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT-POINTING CURVED ANGLE BRACKET
868     { 0x29FD, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT-POINTING CURVED ANGLE BRACKET
869     { 0x29FE, Infix, 4, 4, 0}, // TINY
870     { 0x29FF, Infix, 4, 4, 0}, // MINY
871     { 0x2A00, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED DOT OPERATOR
872     { 0x2A01, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED PLUS OPERATOR
873     { 0x2A02, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED TIMES OPERATOR
874     { 0x2A03, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION OPERATOR WITH DOT
875     { 0x2A04, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION OPERATOR WITH PLUS
876     { 0x2A05, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SQUARE INTERSECTION OPERATOR
877     { 0x2A06, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SQUARE UNION OPERATOR
878     { 0x2A07, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // TWO LOGICAL AND OPERATOR
879     { 0x2A08, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // TWO LOGICAL OR OPERATOR
880     { 0x2A09, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY TIMES OPERATOR
881     { 0x2A0A, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // MODULO TWO SUM
882     { 0x2A0B, Prefix, 1, 2, Symmetric | LargeOp}, // SUMMATION WITH INTEGRAL
883     { 0x2A0C, Prefix, 0, 1, Symmetric | LargeOp}, // QUADRUPLE INTEGRAL OPERATOR
884     { 0x2A0D, Prefix, 1, 2, Symmetric | LargeOp}, // FINITE PART INTEGRAL
885     { 0x2A0E, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH DOUBLE STROKE
886     { 0x2A0F, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL AVERAGE WITH SLASH
887     { 0x2A10, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // CIRCULATION FUNCTION
888     { 0x2A11, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // ANTICLOCKWISE INTEGRATION
889     { 0x2A12, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
890     { 0x2A13, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
891     { 0x2A14, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION NOT INCLUDING THE POLE
892     { 0x2A15, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL AROUND A POINT OPERATOR
893     { 0x2A16, Prefix, 1, 2, Symmetric | LargeOp}, // QUATERNION INTEGRAL OPERATOR
894     { 0x2A17, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
895     { 0x2A18, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH TIMES SIGN
896     { 0x2A19, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH INTERSECTION
897     { 0x2A1A, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH UNION
898     { 0x2A1B, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH OVERBAR
899     { 0x2A1C, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH UNDERBAR
900     { 0x2A1D, Infix, 3, 3, 0}, // JOIN
901     { 0x2A1E, Infix, 3, 3, 0}, // LARGE LEFT TRIANGLE OPERATOR
902     { 0x2A1F, Infix, 3, 3, 0}, // Z NOTATION SCHEMA COMPOSITION
903     { 0x2A20, Infix, 3, 3, 0}, // Z NOTATION SCHEMA PIPING
904     { 0x2A21, Infix, 3, 3, 0}, // Z NOTATION SCHEMA PROJECTION
905     { 0x2A22, Infix, 4, 4, 0}, // PLUS SIGN WITH SMALL CIRCLE ABOVE
906     { 0x2A23, Infix, 4, 4, 0}, // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE
907     { 0x2A24, Infix, 4, 4, 0}, // PLUS SIGN WITH TILDE ABOVE
908     { 0x2A25, Infix, 4, 4, 0}, // PLUS SIGN WITH DOT BELOW
909     { 0x2A26, Infix, 4, 4, 0}, // PLUS SIGN WITH TILDE BELOW
910     { 0x2A27, Infix, 4, 4, 0}, // PLUS SIGN WITH SUBSCRIPT TWO
911     { 0x2A28, Infix, 4, 4, 0}, // PLUS SIGN WITH BLACK TRIANGLE
912     { 0x2A29, Infix, 4, 4, 0}, // MINUS SIGN WITH COMMA ABOVE
913     { 0x2A2A, Infix, 4, 4, 0}, // MINUS SIGN WITH DOT BELOW
914     { 0x2A2B, Infix, 4, 4, 0}, // MINUS SIGN WITH FALLING DOTS
915     { 0x2A2C, Infix, 4, 4, 0}, // MINUS SIGN WITH RISING DOTS
916     { 0x2A2D, Infix, 4, 4, 0}, // PLUS SIGN IN LEFT HALF CIRCLE
917     { 0x2A2E, Infix, 4, 4, 0}, // PLUS SIGN IN RIGHT HALF CIRCLE
918     { 0x2A2F, Infix, 4, 4, 0}, // VECTOR OR CROSS PRODUCT
919     { 0x2A30, Infix, 4, 4, 0}, // MULTIPLICATION SIGN WITH DOT ABOVE
920     { 0x2A31, Infix, 4, 4, 0}, // MULTIPLICATION SIGN WITH UNDERBAR
921     { 0x2A32, Infix, 4, 4, 0}, // SEMIDIRECT PRODUCT WITH BOTTOM CLOSED
922     { 0x2A33, Infix, 4, 4, 0}, // SMASH PRODUCT
923     { 0x2A34, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
924     { 0x2A35, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
925     { 0x2A36, Infix, 4, 4, 0}, // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT
926     { 0x2A37, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN DOUBLE CIRCLE
927     { 0x2A38, Infix, 4, 4, 0}, // CIRCLED DIVISION SIGN
928     { 0x2A39, Infix, 4, 4, 0}, // PLUS SIGN IN TRIANGLE
929     { 0x2A3A, Infix, 4, 4, 0}, // MINUS SIGN IN TRIANGLE
930     { 0x2A3B, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN TRIANGLE
931     { 0x2A3C, Infix, 4, 4, 0}, // INTERIOR PRODUCT
932     { 0x2A3D, Infix, 4, 4, 0}, // RIGHTHAND INTERIOR PRODUCT
933     { 0x2A3E, Infix, 4, 4, 0}, // Z NOTATION RELATIONAL COMPOSITION
934     { 0x2A3F, Infix, 4, 4, 0}, // AMALGAMATION OR COPRODUCT
935     { 0x2A40, Infix, 4, 4, 0}, // INTERSECTION WITH DOT
936     { 0x2A41, Infix, 4, 4, 0}, // UNION WITH MINUS SIGN
937     { 0x2A42, Infix, 4, 4, 0}, // UNION WITH OVERBAR
938     { 0x2A43, Infix, 4, 4, 0}, // INTERSECTION WITH OVERBAR
939     { 0x2A44, Infix, 4, 4, 0}, // INTERSECTION WITH LOGICAL AND
940     { 0x2A45, Infix, 4, 4, 0}, // UNION WITH LOGICAL OR
941     { 0x2A46, Infix, 4, 4, 0}, // UNION ABOVE INTERSECTION
942     { 0x2A47, Infix, 4, 4, 0}, // INTERSECTION ABOVE UNION
943     { 0x2A48, Infix, 4, 4, 0}, // UNION ABOVE BAR ABOVE INTERSECTION
944     { 0x2A49, Infix, 4, 4, 0}, // INTERSECTION ABOVE BAR ABOVE UNION
945     { 0x2A4A, Infix, 4, 4, 0}, // UNION BESIDE AND JOINED WITH UNION
946     { 0x2A4B, Infix, 4, 4, 0}, // INTERSECTION BESIDE AND JOINED WITH INTERSECTION
947     { 0x2A4C, Infix, 4, 4, 0}, // CLOSED UNION WITH SERIFS
948     { 0x2A4D, Infix, 4, 4, 0}, // CLOSED INTERSECTION WITH SERIFS
949     { 0x2A4E, Infix, 4, 4, 0}, // DOUBLE SQUARE INTERSECTION
950     { 0x2A4F, Infix, 4, 4, 0}, // DOUBLE SQUARE UNION
951     { 0x2A50, Infix, 4, 4, 0}, // CLOSED UNION WITH SERIFS AND SMASH PRODUCT
952     { 0x2A51, Infix, 4, 4, 0}, // LOGICAL AND WITH DOT ABOVE
953     { 0x2A52, Infix, 4, 4, 0}, // LOGICAL OR WITH DOT ABOVE
954     { 0x2A53, Infix, 4, 4, 0}, // DOUBLE LOGICAL AND
955     { 0x2A54, Infix, 4, 4, 0}, // DOUBLE LOGICAL OR
956     { 0x2A55, Infix, 4, 4, 0}, // TWO INTERSECTING LOGICAL AND
957     { 0x2A56, Infix, 4, 4, 0}, // TWO INTERSECTING LOGICAL OR
958     { 0x2A57, Infix, 4, 4, 0}, // SLOPING LARGE OR
959     { 0x2A58, Infix, 4, 4, 0}, // SLOPING LARGE AND
960     { 0x2A59, Infix, 5, 5, 0}, // LOGICAL OR OVERLAPPING LOGICAL AND
961     { 0x2A5A, Infix, 4, 4, 0}, // LOGICAL AND WITH MIDDLE STEM
962     { 0x2A5B, Infix, 4, 4, 0}, // LOGICAL OR WITH MIDDLE STEM
963     { 0x2A5C, Infix, 4, 4, 0}, // LOGICAL AND WITH HORIZONTAL DASH
964     { 0x2A5D, Infix, 4, 4, 0}, // LOGICAL OR WITH HORIZONTAL DASH
965     { 0x2A5E, Infix, 4, 4, 0}, // LOGICAL AND WITH DOUBLE OVERBAR
966     { 0x2A5F, Infix, 4, 4, 0}, // LOGICAL AND WITH UNDERBAR
967     { 0x2A60, Infix, 4, 4, 0}, // LOGICAL AND WITH DOUBLE UNDERBAR
968     { 0x2A61, Infix, 4, 4, 0}, // SMALL VEE WITH UNDERBAR
969     { 0x2A62, Infix, 4, 4, 0}, // LOGICAL OR WITH DOUBLE OVERBAR
970     { 0x2A63, Infix, 4, 4, 0}, // LOGICAL OR WITH DOUBLE UNDERBAR
971     { 0x2A64, Infix, 4, 4, 0}, // Z NOTATION DOMAIN ANTIRESTRICTION
972     { 0x2A65, Infix, 4, 4, 0}, // Z NOTATION RANGE ANTIRESTRICTION
973     { 0x2A66, Infix, 5, 5, 0}, // EQUALS SIGN WITH DOT BELOW
974     { 0x2A67, Infix, 5, 5, 0}, // IDENTICAL WITH DOT ABOVE
975     { 0x2A68, Infix, 5, 5, 0}, // TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE
976     { 0x2A69, Infix, 5, 5, 0}, // TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE
977     { 0x2A6A, Infix, 5, 5, 0}, // TILDE OPERATOR WITH DOT ABOVE
978     { 0x2A6B, Infix, 5, 5, 0}, // TILDE OPERATOR WITH RISING DOTS
979     { 0x2A6C, Infix, 5, 5, 0}, // SIMILAR MINUS SIMILAR
980     { 0x2A6D, Infix, 5, 5, 0}, // CONGRUENT WITH DOT ABOVE
981     { 0x2A6E, Infix, 5, 5, 0}, // EQUALS WITH ASTERISK
982     { 0x2A6F, Infix, 5, 5, 0}, // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
983     { 0x2A70, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL OR EQUAL TO
984     { 0x2A71, Infix, 4, 4, 0}, // EQUALS SIGN ABOVE PLUS SIGN
985     { 0x2A72, Infix, 4, 4, 0}, // PLUS SIGN ABOVE EQUALS SIGN
986     { 0x2A73, Infix, 5, 5, 0}, // EQUALS SIGN ABOVE TILDE OPERATOR
987     { 0x2A74, Infix, 5, 5, 0}, // DOUBLE COLON EQUAL
988     { 0x2A75, Infix, 5, 5, 0}, // TWO CONSECUTIVE EQUALS SIGNS
989     { 0x2A76, Infix, 5, 5, 0}, // THREE CONSECUTIVE EQUALS SIGNS
990     { 0x2A77, Infix, 5, 5, 0}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
991     { 0x2A78, Infix, 5, 5, 0}, // EQUIVALENT WITH FOUR DOTS ABOVE
992     { 0x2A79, Infix, 5, 5, 0}, // LESS-THAN WITH CIRCLE INSIDE
993     { 0x2A7A, Infix, 5, 5, 0}, // GREATER-THAN WITH CIRCLE INSIDE
994     { 0x2A7B, Infix, 5, 5, 0}, // LESS-THAN WITH QUESTION MARK ABOVE
995     { 0x2A7C, Infix, 5, 5, 0}, // GREATER-THAN WITH QUESTION MARK ABOVE
996     { 0x2A7D, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO
997     { 0x2A7E, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO
998     { 0x2A7F, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
999     { 0x2A80, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
1000     { 0x2A81, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
1001     { 0x2A82, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
1002     { 0x2A83, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
1003     { 0x2A84, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
1004     { 0x2A85, Infix, 5, 5, 0}, // LESS-THAN OR APPROXIMATE
1005     { 0x2A86, Infix, 5, 5, 0}, // GREATER-THAN OR APPROXIMATE
1006     { 0x2A87, Infix, 5, 5, 0}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
1007     { 0x2A88, Infix, 5, 5, 0}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
1008     { 0x2A89, Infix, 5, 5, 0}, // LESS-THAN AND NOT APPROXIMATE
1009     { 0x2A8A, Infix, 5, 5, 0}, // GREATER-THAN AND NOT APPROXIMATE
1010     { 0x2A8B, Infix, 5, 5, 0}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
1011     { 0x2A8C, Infix, 5, 5, 0}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
1012     { 0x2A8D, Infix, 5, 5, 0}, // LESS-THAN ABOVE SIMILAR OR EQUAL
1013     { 0x2A8E, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SIMILAR OR EQUAL
1014     { 0x2A8F, Infix, 5, 5, 0}, // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
1015     { 0x2A90, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
1016     { 0x2A91, Infix, 5, 5, 0}, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
1017     { 0x2A92, Infix, 5, 5, 0}, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
1018     { 0x2A93, Infix, 5, 5, 0}, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
1019     { 0x2A94, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
1020     { 0x2A95, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR LESS-THAN
1021     { 0x2A96, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR GREATER-THAN
1022     { 0x2A97, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
1023     { 0x2A98, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
1024     { 0x2A99, Infix, 5, 5, 0}, // DOUBLE-LINE EQUAL TO OR LESS-THAN
1025     { 0x2A9A, Infix, 5, 5, 0}, // DOUBLE-LINE EQUAL TO OR GREATER-THAN
1026     { 0x2A9B, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
1027     { 0x2A9C, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
1028     { 0x2A9D, Infix, 5, 5, 0}, // SIMILAR OR LESS-THAN
1029     { 0x2A9E, Infix, 5, 5, 0}, // SIMILAR OR GREATER-THAN
1030     { 0x2A9F, Infix, 5, 5, 0}, // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
1031     { 0x2AA0, Infix, 5, 5, 0}, // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
1032     { 0x2AA1, Infix, 5, 5, 0}, // DOUBLE NESTED LESS-THAN
1033     { 0x2AA2, Infix, 5, 5, 0}, // DOUBLE NESTED GREATER-THAN
1034     { 0x2AA3, Infix, 5, 5, 0}, // DOUBLE NESTED LESS-THAN WITH UNDERBAR
1035     { 0x2AA4, Infix, 5, 5, 0}, // GREATER-THAN OVERLAPPING LESS-THAN
1036     { 0x2AA5, Infix, 5, 5, 0}, // GREATER-THAN BESIDE LESS-THAN
1037     { 0x2AA6, Infix, 5, 5, 0}, // LESS-THAN CLOSED BY CURVE
1038     { 0x2AA7, Infix, 5, 5, 0}, // GREATER-THAN CLOSED BY CURVE
1039     { 0x2AA8, Infix, 5, 5, 0}, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
1040     { 0x2AA9, Infix, 5, 5, 0}, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
1041     { 0x2AAA, Infix, 5, 5, 0}, // SMALLER THAN
1042     { 0x2AAB, Infix, 5, 5, 0}, // LARGER THAN
1043     { 0x2AAC, Infix, 5, 5, 0}, // SMALLER THAN OR EQUAL TO
1044     { 0x2AAD, Infix, 5, 5, 0}, // LARGER THAN OR EQUAL TO
1045     { 0x2AAE, Infix, 5, 5, 0}, // EQUALS SIGN WITH BUMPY ABOVE
1046     { 0x2AAF, Infix, 5, 5, 0}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
1047     { 0x2AB0, Infix, 5, 5, 0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
1048     { 0x2AB1, Infix, 5, 5, 0}, // PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO
1049     { 0x2AB2, Infix, 5, 5, 0}, // SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO
1050     { 0x2AB3, Infix, 5, 5, 0}, // PRECEDES ABOVE EQUALS SIGN
1051     { 0x2AB4, Infix, 5, 5, 0}, // SUCCEEDS ABOVE EQUALS SIGN
1052     { 0x2AB5, Infix, 5, 5, 0}, // PRECEDES ABOVE NOT EQUAL TO
1053     { 0x2AB6, Infix, 5, 5, 0}, // SUCCEEDS ABOVE NOT EQUAL TO
1054     { 0x2AB7, Infix, 5, 5, 0}, // PRECEDES ABOVE ALMOST EQUAL TO
1055     { 0x2AB8, Infix, 5, 5, 0}, // SUCCEEDS ABOVE ALMOST EQUAL TO
1056     { 0x2AB9, Infix, 5, 5, 0}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
1057     { 0x2ABA, Infix, 5, 5, 0}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
1058     { 0x2ABB, Infix, 5, 5, 0}, // DOUBLE PRECEDES
1059     { 0x2ABC, Infix, 5, 5, 0}, // DOUBLE SUCCEEDS
1060     { 0x2ABD, Infix, 5, 5, 0}, // SUBSET WITH DOT
1061     { 0x2ABE, Infix, 5, 5, 0}, // SUPERSET WITH DOT
1062     { 0x2ABF, Infix, 5, 5, 0}, // SUBSET WITH PLUS SIGN BELOW
1063     { 0x2AC0, Infix, 5, 5, 0}, // SUPERSET WITH PLUS SIGN BELOW
1064     { 0x2AC1, Infix, 5, 5, 0}, // SUBSET WITH MULTIPLICATION SIGN BELOW
1065     { 0x2AC2, Infix, 5, 5, 0}, // SUPERSET WITH MULTIPLICATION SIGN BELOW
1066     { 0x2AC3, Infix, 5, 5, 0}, // SUBSET OF OR EQUAL TO WITH DOT ABOVE
1067     { 0x2AC4, Infix, 5, 5, 0}, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
1068     { 0x2AC5, Infix, 5, 5, 0}, // SUBSET OF ABOVE EQUALS SIGN
1069     { 0x2AC6, Infix, 5, 5, 0}, // SUPERSET OF ABOVE EQUALS SIGN
1070     { 0x2AC7, Infix, 5, 5, 0}, // SUBSET OF ABOVE TILDE OPERATOR
1071     { 0x2AC8, Infix, 5, 5, 0}, // SUPERSET OF ABOVE TILDE OPERATOR
1072     { 0x2AC9, Infix, 5, 5, 0}, // SUBSET OF ABOVE ALMOST EQUAL TO
1073     { 0x2ACA, Infix, 5, 5, 0}, // SUPERSET OF ABOVE ALMOST EQUAL TO
1074     { 0x2ACB, Infix, 5, 5, 0}, // SUBSET OF ABOVE NOT EQUAL TO
1075     { 0x2ACC, Infix, 5, 5, 0}, // SUPERSET OF ABOVE NOT EQUAL TO
1076     { 0x2ACD, Infix, 5, 5, 0}, // SQUARE LEFT OPEN BOX OPERATOR
1077     { 0x2ACE, Infix, 5, 5, 0}, // SQUARE RIGHT OPEN BOX OPERATOR
1078     { 0x2ACF, Infix, 5, 5, 0}, // CLOSED SUBSET
1079     { 0x2AD0, Infix, 5, 5, 0}, // CLOSED SUPERSET
1080     { 0x2AD1, Infix, 5, 5, 0}, // CLOSED SUBSET OR EQUAL TO
1081     { 0x2AD2, Infix, 5, 5, 0}, // CLOSED SUPERSET OR EQUAL TO
1082     { 0x2AD3, Infix, 5, 5, 0}, // SUBSET ABOVE SUPERSET
1083     { 0x2AD4, Infix, 5, 5, 0}, // SUPERSET ABOVE SUBSET
1084     { 0x2AD5, Infix, 5, 5, 0}, // SUBSET ABOVE SUBSET
1085     { 0x2AD6, Infix, 5, 5, 0}, // SUPERSET ABOVE SUPERSET
1086     { 0x2AD7, Infix, 5, 5, 0}, // SUPERSET BESIDE SUBSET
1087     { 0x2AD8, Infix, 5, 5, 0}, // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET
1088     { 0x2AD9, Infix, 5, 5, 0}, // ELEMENT OF OPENING DOWNWARDS
1089     { 0x2ADA, Infix, 5, 5, 0}, // PITCHFORK WITH TEE TOP
1090     { 0x2ADB, Infix, 5, 5, 0}, // TRANSVERSAL INTERSECTION
1091     { 0x2ADD, Infix, 5, 5, 0}, // NONFORKING
1092     { 0x2ADE, Infix, 5, 5, 0}, // SHORT LEFT TACK
1093     { 0x2ADF, Infix, 5, 5, 0}, // SHORT DOWN TACK
1094     { 0x2AE0, Infix, 5, 5, 0}, // SHORT UP TACK
1095     { 0x2AE1, Infix, 5, 5, 0}, // PERPENDICULAR WITH S
1096     { 0x2AE2, Infix, 5, 5, 0}, // VERTICAL BAR TRIPLE RIGHT TURNSTILE
1097     { 0x2AE3, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR LEFT TURNSTILE
1098     { 0x2AE4, Infix, 5, 5, 0}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
1099     { 0x2AE5, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
1100     { 0x2AE6, Infix, 5, 5, 0}, // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
1101     { 0x2AE7, Infix, 5, 5, 0}, // SHORT DOWN TACK WITH OVERBAR
1102     { 0x2AE8, Infix, 5, 5, 0}, // SHORT UP TACK WITH UNDERBAR
1103     { 0x2AE9, Infix, 5, 5, 0}, // SHORT UP TACK ABOVE SHORT DOWN TACK
1104     { 0x2AEA, Infix, 5, 5, 0}, // DOUBLE DOWN TACK
1105     { 0x2AEB, Infix, 5, 5, 0}, // DOUBLE UP TACK
1106     { 0x2AEC, Infix, 5, 5, 0}, // DOUBLE STROKE NOT SIGN
1107     { 0x2AED, Infix, 5, 5, 0}, // REVERSED DOUBLE STROKE NOT SIGN
1108     { 0x2AEE, Infix, 5, 5, 0}, // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
1109     { 0x2AEF, Infix, 5, 5, 0}, // VERTICAL LINE WITH CIRCLE ABOVE
1110     { 0x2AF0, Infix, 5, 5, 0}, // VERTICAL LINE WITH CIRCLE BELOW
1111     { 0x2AF1, Infix, 5, 5, 0}, // DOWN TACK WITH CIRCLE BELOW
1112     { 0x2AF2, Infix, 5, 5, 0}, // PARALLEL WITH HORIZONTAL STROKE
1113     { 0x2AF3, Infix, 5, 5, 0}, // PARALLEL WITH TILDE OPERATOR
1114     { 0x2AF4, Infix, 4, 4, 0}, // TRIPLE VERTICAL BAR BINARY RELATION
1115     { 0x2AF5, Infix, 4, 4, 0}, // TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE
1116     { 0x2AF6, Infix, 4, 4, 0}, // TRIPLE COLON OPERATOR
1117     { 0x2AF7, Infix, 5, 5, 0}, // TRIPLE NESTED LESS-THAN
1118     { 0x2AF8, Infix, 5, 5, 0}, // TRIPLE NESTED GREATER-THAN
1119     { 0x2AF9, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
1120     { 0x2AFA, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
1121     { 0x2AFB, Infix, 4, 4, 0}, // TRIPLE SOLIDUS BINARY RELATION
1122     { 0x2AFC, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LARGE TRIPLE VERTICAL BAR OPERATOR
1123     { 0x2AFD, Infix, 4, 4, 0}, // DOUBLE SOLIDUS OPERATOR
1124     { 0x2AFE, Infix, 3, 3, 0}, // WHITE VERTICAL BAR
1125     { 0x2AFF, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY WHITE VERTICAL BAR
1126     { 0x2B45, Infix, 5, 5, Stretchy}, // LEFTWARDS QUADRUPLE ARROW
1127     { 0x2B46, Infix, 5, 5, Stretchy} // RIGHTWARDS QUADRUPLE ARROW
1128 };
1129
1130 // A list of operators that stretch in the horizontal direction. This has been generated from Mozilla's MathML operator dictionary.
1131 inline UChar ExtractKeyHorizontal(const UChar* entry) { return *entry; }
1132 static const UChar horizontalOperators[] = {
1133     0x003D, 0x005E, 0x005F, 0x007E, 0x00AF, 0x02C6, 0x02C7, 0x02C9, 0x02CD, 0x02DC, 0x02F7, 0x0302, 0x0332, 0x203E, 0x20D0, 0x20D1, 0x20D6, 0x20D7, 0x20E1, 0x2190, 0x2192, 0x2194, 0x2198, 0x2199, 0x219C, 0x219D, 0x219E, 0x21A0, 0x21A2, 0x21A3, 0x21A4, 0x21A6, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21B4, 0x21B9, 0x21BC, 0x21BD, 0x21C0, 0x21C1, 0x21C4, 0x21C6, 0x21C7, 0x21C9, 0x21CB, 0x21CC, 0x21D0, 0x21D2, 0x21D4, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21E0, 0x21E2, 0x21E4, 0x21E5, 0x21E6, 0x21E8, 0x21F0, 0x21F6, 0x21FD, 0x21FE, 0x21FF, 0x23B4, 0x23B5, 0x23DC, 0x23DD, 0x23DE, 0x23DF, 0x23E0, 0x23E1, 0x2500, 0x27F5, 0x27F6, 0x27F7, 0x27F8, 0x27F9, 0x27FA, 0x27FB, 0x27FC, 0x27FD, 0x27FE, 0x27FF, 0x290C, 0x290D, 0x290E, 0x290F, 0x2910, 0x294E, 0x2950, 0x2952, 0x2953, 0x2956, 0x2957, 0x295A, 0x295B, 0x295E, 0x295F, 0x2B45, 0x2B46, 0xFE35, 0xFE36, 0xFE37, 0xFE38
1134 };
1135
1136 }
1137
1138 RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, PassRef<RenderStyle> style)
1139     : RenderMathMLToken(element, std::move(style))
1140     , m_stretchHeightAboveBaseline(0)
1141     , m_stretchDepthBelowBaseline(0)
1142     , m_operator(0)
1143     , m_isVertical(true)
1144 {
1145     updateTokenContent();
1146 }
1147
1148 RenderMathMLOperator::RenderMathMLOperator(Document& document, PassRef<RenderStyle> style, const String& operatorString, MathMLOperatorDictionary::Form form, MathMLOperatorDictionary::Flag flag)
1149     : RenderMathMLToken(document, std::move(style))
1150     , m_stretchHeightAboveBaseline(0)
1151     , m_stretchDepthBelowBaseline(0)
1152     , m_operator(0)
1153     , m_isVertical(true)
1154     , m_operatorForm(form)
1155     , m_operatorFlags(flag)
1156 {
1157     updateTokenContent(operatorString);
1158 }
1159
1160 void RenderMathMLOperator::setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag flag, const QualifiedName& name)
1161 {
1162     ASSERT(!isFencedOperator());
1163     const AtomicString& attributeValue = element().fastGetAttribute(name);
1164     if (attributeValue == "true")
1165         m_operatorFlags |= flag;
1166     else if (attributeValue == "false")
1167         m_operatorFlags &= ~flag;
1168     // We ignore absent or invalid attributes.
1169 }
1170
1171 void RenderMathMLOperator::setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry* entry)
1172 {
1173     // If this operator has been created by RenderMathMLFenced, we preserve the Fence and Separator properties.
1174     if (isFencedOperator())
1175         m_operatorFlags = (m_operatorFlags & (MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator)) | entry->flags;
1176     else
1177         m_operatorFlags = entry->flags;
1178
1179     // Leading and trailing space is specified as multiple of 1/18em.
1180     m_leadingSpace = entry->lspace * style().font().size() / 18;
1181     m_trailingSpace = entry->rspace * style().font().size() / 18;
1182 }
1183
1184 void RenderMathMLOperator::SetOperatorProperties()
1185 {
1186     // We determine the stretch direction (default is vertical).
1187     m_isVertical = !(tryBinarySearch<const UChar, UChar>(MathMLOperatorDictionary::horizontalOperators, WTF_ARRAY_LENGTH(MathMLOperatorDictionary::horizontalOperators), m_operator, MathMLOperatorDictionary::ExtractKeyHorizontal));
1188
1189     // We determine the form of the operator.
1190     bool explicitForm = true;
1191     if (!isFencedOperator()) {
1192         const AtomicString& form = element().fastGetAttribute(MathMLNames::formAttr);
1193         if (form == "prefix")
1194             m_operatorForm = MathMLOperatorDictionary::Prefix;
1195         else if (form == "infix")
1196             m_operatorForm = MathMLOperatorDictionary::Infix;
1197         else if (form == "postfix")
1198             m_operatorForm = MathMLOperatorDictionary::Postfix;
1199         else {
1200             // FIXME: We should use more advanced heuristics indicated in the specification to determine the operator form (https://bugs.webkit.org/show_bug.cgi?id=124829).
1201             explicitForm = false;
1202             if (!element().previousSibling() && element().nextSibling())
1203                 m_operatorForm = MathMLOperatorDictionary::Prefix;
1204             else if (element().previousSibling() && !element().nextSibling())
1205                 m_operatorForm = MathMLOperatorDictionary::Postfix;
1206             else
1207                 m_operatorForm = MathMLOperatorDictionary::Infix;
1208         }
1209     }
1210
1211     // We determine the default values of the operator properties.
1212
1213     // First we initialize with the default values for unknown operators.
1214     if (isFencedOperator())
1215         m_operatorFlags &= MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator; // This resets all but the Fence and Separator properties.
1216     else
1217         m_operatorFlags = 0; // This resets all the operator properties.
1218     m_leadingSpace = 5 * style().font().size() / 18; // This sets leading space to "thickmathspace".
1219     m_trailingSpace = 5 * style().font().size() / 18; // This sets trailing space to "thickmathspace".
1220     m_minSize = style().font().size(); // This sets minsize to "1em".
1221     m_maxSize = intMaxForLayoutUnit; // This sets maxsize to "infinity".
1222
1223     if (m_operator) {
1224         // Then we try to find the default values from the operator dictionary.
1225         if (const MathMLOperatorDictionary::Entry* entry = tryBinarySearch<const MathMLOperatorDictionary::Entry, MathMLOperatorDictionary::Key>(MathMLOperatorDictionary::dictionary, MATHML_OPDICT_SIZE, MathMLOperatorDictionary::Key(m_operator, m_operatorForm), MathMLOperatorDictionary::ExtractKey))
1226             setOperatorPropertiesFromOpDictEntry(entry);
1227         else if (!explicitForm) {
1228             // If we did not find the desired operator form and if it was not set explicitely, we use the first one in the following order: Infix, Prefix, Postfix.
1229             // This is to handle bad MathML markup without explicit <mrow> delimiters like "<mo>(</mo><mi>a</mi><mo>)</mo><mo>(</mo><mi>b</mi><mo>)</mo>" where the inner parenthesis should not be considered infix.
1230             if (const MathMLOperatorDictionary::Entry* entry = tryBinarySearch<const MathMLOperatorDictionary::Entry, UChar>(MathMLOperatorDictionary::dictionary, MATHML_OPDICT_SIZE, m_operator, MathMLOperatorDictionary::ExtractChar)) {
1231                 // If the previous entry is another form for that operator, we move to that entry. Note that it only remains at most two forms so we don't need to move any further.
1232                 if (entry != MathMLOperatorDictionary::dictionary && (entry-1)->character == m_operator)
1233                     entry--;
1234                 m_operatorForm = entry->form; // We override the form previously determined.
1235                 setOperatorPropertiesFromOpDictEntry(entry);
1236             }
1237         }
1238     }
1239 #undef MATHML_OPDICT_SIZE
1240
1241     if (!isFencedOperator()) {
1242         // Finally, we make the attribute values override the default.
1243
1244         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Fence, MathMLNames::fenceAttr);
1245         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Separator, MathMLNames::separatorAttr);
1246         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Stretchy, MathMLNames::stretchyAttr);
1247         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Symmetric, MathMLNames::symmetricAttr);
1248         setOperatorFlagFromAttribute(MathMLOperatorDictionary::LargeOp, MathMLNames::largeopAttr);
1249         setOperatorFlagFromAttribute(MathMLOperatorDictionary::MovableLimits, MathMLNames::movablelimitsAttr);
1250         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Accent, MathMLNames::accentAttr);
1251
1252         parseMathMLLength(element().fastGetAttribute(MathMLNames::lspaceAttr), m_leadingSpace, &style(), false); // FIXME: Negative leading space must be implemented (https://bugs.webkit.org/show_bug.cgi?id=124830).
1253         parseMathMLLength(element().fastGetAttribute(MathMLNames::rspaceAttr), m_trailingSpace, &style(), false); // FIXME: Negative trailing space must be implemented (https://bugs.webkit.org/show_bug.cgi?id=124830).
1254
1255         parseMathMLLength(element().fastGetAttribute(MathMLNames::minsizeAttr), m_minSize, &style(), false);
1256         const AtomicString& maxsize = element().fastGetAttribute(MathMLNames::maxsizeAttr);
1257         if (maxsize != "infinity")
1258             parseMathMLLength(maxsize, m_maxSize, &style(), false);
1259     }
1260 }
1261
1262 bool RenderMathMLOperator::isChildAllowed(const RenderObject&, const RenderStyle&) const
1263 {
1264     return false;
1265 }
1266
1267 void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
1268 {
1269     if (!m_isVertical || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline))
1270         return;
1271
1272     m_stretchHeightAboveBaseline = heightAboveBaseline;
1273     m_stretchDepthBelowBaseline = depthBelowBaseline;
1274
1275     SetOperatorProperties();
1276     if (hasOperatorFlag(MathMLOperatorDictionary::Symmetric)) {
1277         // We make the operator stretch symmetrically above and below the axis.
1278         // FIXME: We should read the axis from the MATH table (https://bugs.webkit.org/show_bug.cgi?id=122297). For now, we use the same value as in RenderMathMLFraction::firstLineBaseline().
1279         LayoutUnit axis = style().fontMetrics().xHeight() / 2;
1280         LayoutUnit halfStretchSize = std::max(m_stretchHeightAboveBaseline - axis, m_stretchDepthBelowBaseline + axis);
1281         m_stretchHeightAboveBaseline = halfStretchSize + axis;
1282         m_stretchDepthBelowBaseline = halfStretchSize - axis;
1283     }
1284     // We try to honor the minsize/maxsize condition by increasing or decreasing both height and depth proportionately.
1285     // The MathML specification does not indicate what to do when maxsize < minsize, so we follow Gecko and make minsize take precedence.
1286     LayoutUnit size = stretchSize();
1287     float aspect = 1.0;
1288     if (size > 0) {
1289         if (size < m_minSize)
1290             aspect = float(m_minSize) / size;
1291         else if (m_maxSize < size)
1292             aspect = float(m_maxSize) / size;
1293     }
1294     m_stretchHeightAboveBaseline *= aspect;
1295     m_stretchDepthBelowBaseline *= aspect;
1296     updateStyle();
1297 }
1298
1299 void RenderMathMLOperator::stretchTo(LayoutUnit width)
1300 {
1301     if (m_isVertical || m_stretchWidth == width)
1302         return;
1303
1304     m_stretchWidth = width;
1305
1306     SetOperatorProperties();
1307
1308     updateStyle();
1309 }
1310
1311 FloatRect RenderMathMLOperator::boundsForGlyph(const GlyphData& data) const
1312 {
1313     return data.fontData->boundsForGlyph(data.glyph);
1314 }
1315
1316 float RenderMathMLOperator::heightForGlyph(const GlyphData& data) const
1317 {
1318     return boundsForGlyph(data).height();
1319 }
1320
1321 float RenderMathMLOperator::advanceForGlyph(const GlyphData& data) const
1322 {
1323     return data.fontData->widthForGlyph(data.glyph);
1324 }
1325
1326 void RenderMathMLOperator::computePreferredLogicalWidths()
1327 {
1328     ASSERT(preferredLogicalWidthsDirty());
1329
1330     SetOperatorProperties();
1331     if (!shouldAllowStretching()) {
1332         RenderMathMLToken::computePreferredLogicalWidths();
1333         if (isInvisibleOperator()) {
1334             // In some fonts, glyphs for invisible operators have nonzero width. Consequently, we subtract that width here to avoid wide gaps.
1335             GlyphData data = style().font().glyphDataForCharacter(m_operator, false);
1336             float glyphWidth = advanceForGlyph(data);
1337             ASSERT(glyphWidth <= m_minPreferredLogicalWidth);
1338             m_minPreferredLogicalWidth -= glyphWidth;
1339             m_maxPreferredLogicalWidth -= glyphWidth;
1340         }
1341         return;
1342     }
1343
1344     GlyphData data = style().font().glyphDataForCharacter(m_operator, !style().isLeftToRightDirection());
1345     float maximumGlyphWidth = advanceForGlyph(data);
1346     if (!m_isVertical) {
1347         if (maximumGlyphWidth < stretchSize())
1348             maximumGlyphWidth = stretchSize();
1349         m_maxPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
1350         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
1351         return;
1352     }
1353     if (isLargeOperatorInDisplayStyle()) {
1354         // Large operators in STIX Word have incorrect advance width, causing misplacement of superscript, so we use the glyph bound instead (http://sourceforge.net/p/stixfonts/tracking/49/).
1355         StretchyData largeOperator = getDisplayStyleLargeOperator(m_operator);
1356         if (largeOperator.mode() == DrawSizeVariant)
1357             maximumGlyphWidth = boundsForGlyph(largeOperator.variant()).width();
1358     } else {
1359         // FIXME: some glyphs (e.g. the one for "FRACTION SLASH" in the STIX Math font or large operators) have a width that depends on the height, resulting in large gaps (https://bugs.webkit.org/show_bug.cgi?id=130326).
1360         findStretchyData(m_operator, &maximumGlyphWidth);
1361     }
1362     m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
1363 }
1364
1365 void RenderMathMLOperator::rebuildTokenContent(const String& operatorString)
1366 {
1367     // We collapse the whitespace and replace the hyphens by minus signs.
1368     AtomicString textContent = operatorString.stripWhiteSpace().simplifyWhiteSpace().replace(hyphenMinus, minusSign).impl();
1369
1370     // We destroy the wrapper and rebuild it.
1371     // FIXME: Using this RenderText make the text inaccessible to the dumpAsText/selection code (https://bugs.webkit.org/show_bug.cgi?id=125597).
1372     if (firstChild())
1373         toRenderElement(firstChild())->destroy();
1374     createWrapperIfNeeded();
1375     RenderPtr<RenderText> text = createRenderer<RenderText>(document(), textContent);
1376     toRenderElement(firstChild())->addChild(text.leakPtr());
1377
1378     // We verify whether the operator text can be represented by a single UChar.
1379     // FIXME: This does not handle surrogate pairs (https://bugs.webkit.org/show_bug.cgi?id=122296).
1380     // FIXME: This does not handle <mo> operators with multiple characters (https://bugs.webkit.org/show_bug.cgi?id=124828).
1381     m_operator = textContent.length() == 1 ? textContent[0] : 0;
1382     SetOperatorProperties();
1383     updateStyle();
1384     setNeedsLayoutAndPrefWidthsRecalc();
1385 }
1386
1387 void RenderMathMLOperator::updateTokenContent(const String& operatorString)
1388 {
1389     ASSERT(isFencedOperator());
1390     rebuildTokenContent(operatorString);
1391 }
1392
1393 void RenderMathMLOperator::updateTokenContent()
1394 {
1395     ASSERT(!isFencedOperator());
1396     rebuildTokenContent(element().textContent());
1397 }
1398
1399 void RenderMathMLOperator::updateFromElement()
1400 {
1401     SetOperatorProperties();
1402     RenderMathMLToken::updateFromElement();
1403 }
1404
1405 void RenderMathMLOperator::updateOperatorProperties()
1406 {
1407     SetOperatorProperties();
1408     if (!isEmpty())
1409         updateStyle();
1410     setNeedsLayoutAndPrefWidthsRecalc();
1411 }
1412
1413 bool RenderMathMLOperator::shouldAllowStretching() const
1414 {
1415     return m_operator && (hasOperatorFlag(MathMLOperatorDictionary::Stretchy) || isLargeOperatorInDisplayStyle());
1416 }
1417
1418 bool RenderMathMLOperator::getGlyphAssemblyFallBack(Vector<OpenTypeMathData::AssemblyPart> assemblyParts, StretchyData& stretchyData) const
1419 {
1420     GlyphData top;
1421     GlyphData extension;
1422     GlyphData bottom;
1423     GlyphData middle;
1424
1425     // The structure of the Open Type Math table is a bit more general than the one currently used by the RenderMathMLOperator code, so we try to fallback in a reasonable way.
1426     // FIXME: RenderMathMLOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
1427     // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
1428
1429     // We count the number of non extender pieces.
1430     int nonExtenderCount = 0;
1431     for (auto& part : assemblyParts) {
1432         if (!part.isExtender)
1433             nonExtenderCount++;
1434     }
1435     if (nonExtenderCount > 3)
1436         return false; // This is not supported: there are too many pieces.
1437
1438     // We now browse the list of pieces.
1439     // 1 = look for a left/bottom glyph
1440     // 2 = look for an extender between left/bottom and mid
1441     // 4 = look for a middle glyph
1442     // 5 = look for an extender between middle and right/top
1443     // 5 = look for a right/top glyph
1444     // 6 = no more piece expected
1445     unsigned state = 1;
1446
1447     extension.glyph = 0;
1448     middle.glyph = 0;
1449     for (auto& part : assemblyParts) {
1450         if ((state == 2 || state == 3) && nonExtenderCount < 3) {
1451             // We do not try to find a middle glyph.
1452             state += 2;
1453         }
1454         if (part.isExtender) {
1455             if (!extension.glyph)
1456                 extension.glyph = part.glyph;
1457             else if (extension.glyph != part.glyph)
1458                 return false; // This is not supported: the assembly has different extenders.
1459
1460             if (state == 1) {
1461                 // We ignore left/bottom piece and multiple successive extenders.
1462                 state = 2;
1463             } else if (state == 3) {
1464                 // We ignore middle piece and multiple successive extenders.
1465                 state = 4;
1466             } else if (state >= 5)
1467                 return false; // This is not supported: we got an unexpected extender.
1468             continue;
1469         }
1470
1471         if (state == 1) {
1472             // We copy the left/bottom part.
1473             bottom.glyph = part.glyph;
1474             state = 2;
1475             continue;
1476         }
1477
1478         if (state == 2 || state == 3) {
1479             // We copy the middle part.
1480             middle.glyph = part.glyph;
1481             state = 4;
1482             continue;
1483         }
1484
1485         if (state == 4 || state == 5) {
1486             // We copy the right/top part.
1487             top.glyph = part.glyph;
1488             state = 6;
1489         }
1490     }
1491
1492     if (!extension.glyph)
1493         return false; // This is not supported: we always assume that we have an extension glyph.
1494
1495     // If we don't have top/bottom glyphs, we use the extension glyph.
1496     if (!top.glyph)
1497         top.glyph = extension.glyph;
1498     if (!bottom.glyph)
1499         bottom.glyph = extension.glyph;
1500
1501     top.fontData = style().font().primaryFont();
1502     extension.fontData = top.fontData;
1503     bottom.fontData = top.fontData;
1504     if (middle.glyph)
1505         middle.fontData = top.fontData;
1506
1507     stretchyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1508
1509     return true;
1510 }
1511
1512 RenderMathMLOperator::StretchyData RenderMathMLOperator::getDisplayStyleLargeOperator(UChar character) const
1513 {
1514     StretchyData data;
1515
1516     ASSERT(m_isVertical && isLargeOperatorInDisplayStyle());
1517
1518     const auto& primaryFontData = style().font().primaryFont();
1519     GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
1520     if (!primaryFontData || !primaryFontData->mathData() || baseGlyph.fontData != primaryFontData)
1521         return data;
1522
1523     Vector<Glyph> sizeVariants;
1524     Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
1525
1526     // The value of displayOperatorMinHeight is sometimes too small, so we ensure that it is at least \sqrt{2} times the size of the base glyph.
1527     float displayOperatorMinHeight = std::max(baseGlyph.fontData->boundsForGlyph(baseGlyph.glyph).height() * float(M_SQRT2), primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::DisplayOperatorMinHeight));
1528
1529     primaryFontData->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
1530
1531     // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
1532     for (auto& variant : sizeVariants) {
1533         GlyphData sizeVariant;
1534         sizeVariant.glyph = variant;
1535         sizeVariant.fontData = primaryFontData;
1536         data.setSizeVariantMode(sizeVariant);
1537         if (boundsForGlyph(sizeVariant).height() >= displayOperatorMinHeight)
1538             return data;
1539     }
1540     return data;
1541 }
1542
1543 RenderMathMLOperator::StretchyData RenderMathMLOperator::findStretchyData(UChar character, float* maximumGlyphWidth)
1544 {
1545     ASSERT(!maximumGlyphWidth || m_isVertical);
1546
1547     StretchyData data;
1548     StretchyData assemblyData;
1549
1550     const auto& primaryFontData = style().font().primaryFont();
1551     GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
1552     
1553     if (primaryFontData && primaryFontData->mathData() && baseGlyph.fontData == primaryFontData) {
1554         Vector<Glyph> sizeVariants;
1555         Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
1556         primaryFontData->mathData()->getMathVariants(baseGlyph.glyph, m_isVertical, sizeVariants, assemblyParts);
1557         // We verify the size variants.
1558         for (auto& variant : sizeVariants) {
1559             GlyphData sizeVariant;
1560             sizeVariant.glyph = variant;
1561             sizeVariant.fontData = primaryFontData;
1562             if (maximumGlyphWidth)
1563                 *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(sizeVariant));
1564             else {
1565                 data.setSizeVariantMode(sizeVariant);
1566                 float size = m_isVertical ? heightForGlyph(sizeVariant) : advanceForGlyph(sizeVariant);
1567                 if (size >= stretchSize()) 
1568                     return data;
1569             }
1570         }
1571
1572         // We verify if there is a construction.
1573         if (!getGlyphAssemblyFallBack(assemblyParts, assemblyData))
1574             return data;
1575     } else {
1576         if (!m_isVertical)
1577             return data;
1578
1579         // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
1580         const StretchyCharacter* stretchyCharacter = nullptr;
1581         const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
1582         for (unsigned index = 0; index < maxIndex; ++index) {
1583             if (stretchyCharacters[index].character == character) {
1584                 stretchyCharacter = &stretchyCharacters[index];
1585                 if (!style().isLeftToRightDirection() && index < leftRightPairsCount * 2) {
1586                     // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
1587                     index += index % 2 ? -1 : 1;
1588                 }
1589                 break;
1590             }
1591         }
1592
1593         // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
1594         if (!stretchyCharacter)
1595             return data;
1596
1597         // We convert the list of Unicode characters into a list of glyph data.
1598         GlyphData top = style().font().glyphDataForCharacter(stretchyCharacter->topChar, false);
1599         GlyphData extension = style().font().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
1600         GlyphData bottom = style().font().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
1601         GlyphData middle;
1602         if (stretchyCharacter->middleChar)
1603             middle = style().font().glyphDataForCharacter(stretchyCharacter->middleChar, false);
1604         assemblyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1605     }
1606
1607     ASSERT(assemblyData.mode() == DrawGlyphAssembly);
1608
1609     // If we are measuring the maximum width, verify each component.
1610     if (maximumGlyphWidth) {
1611         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.top()));
1612         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.extension()));
1613         if (assemblyData.middle().glyph)
1614             *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.middle()));
1615         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.bottom()));
1616         return assemblyData;
1617     }
1618
1619     // We ensure that the size is large enough to avoid glyph overlaps.
1620     float size;
1621     if (m_isVertical) {
1622         size = heightForGlyph(assemblyData.top()) + heightForGlyph(assemblyData.bottom());
1623         if (assemblyData.middle().glyph)
1624             size += heightForGlyph(assemblyData.middle());
1625     } else {
1626         size = advanceForGlyph(assemblyData.left()) + advanceForGlyph(assemblyData.right());
1627         if (assemblyData.middle().glyph)
1628             size += advanceForGlyph(assemblyData.middle());
1629     }
1630     if (size > stretchSize())
1631         return data;
1632
1633     return assemblyData;
1634 }
1635
1636 void RenderMathMLOperator::updateStyle()
1637 {
1638     FontCachePurgePreventer fontCachePurgePreventer;
1639
1640     ASSERT(firstChild());
1641     if (!firstChild())
1642         return;
1643
1644     m_stretchyData.setNormalMode();
1645     // We add spacing around the operator.
1646     // FIXME: The spacing should be added to the whole embellished operator (https://bugs.webkit.org/show_bug.cgi?id=124831).
1647     // FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing).
1648     const auto& wrapper = toRenderElement(firstChild());
1649     auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
1650     newStyle.get().setMarginStart(Length(m_leadingSpace, Fixed));
1651     newStyle.get().setMarginEnd(Length(m_trailingSpace, Fixed));
1652     wrapper->setStyle(std::move(newStyle));
1653     wrapper->setNeedsLayoutAndPrefWidthsRecalc();
1654
1655     if (!shouldAllowStretching())
1656         return;
1657
1658     if (m_isVertical && isLargeOperatorInDisplayStyle())
1659         m_stretchyData = getDisplayStyleLargeOperator(m_operator);
1660     else {
1661         // We do not stretch if the base glyph is large enough.
1662         GlyphData baseGlyph = style().font().glyphDataForCharacter(m_operator, !style().isLeftToRightDirection());
1663         float baseSize = m_isVertical ? heightForGlyph(baseGlyph) : advanceForGlyph(baseGlyph);
1664         if (stretchSize() <= baseSize)
1665             return;
1666         m_stretchyData = findStretchyData(m_operator, nullptr);
1667     }
1668
1669     if (m_isVertical && m_stretchyData.mode() == DrawSizeVariant) {
1670         // We resize the operator to match the one of the size variant.
1671         if (isLargeOperatorInDisplayStyle()) {
1672             // The stretch size is actually not involved in the selection of the size variant in getDisplayStyleLargeOperator.
1673             // We simply use the height and depth of the selected size variant glyph.
1674             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1675             m_stretchHeightAboveBaseline = -glyphBounds.y();
1676             m_stretchDepthBelowBaseline = glyphBounds.maxY();
1677         } else {
1678             // We rescale the height and depth proportionately.
1679             float variantSize = heightForGlyph(m_stretchyData.variant());
1680             float size = stretchSize();
1681             float aspect = size > 0 ? variantSize / size : 1.0;
1682             m_stretchHeightAboveBaseline *= aspect;
1683             m_stretchDepthBelowBaseline *= aspect;
1684         }
1685     }
1686
1687     if (!m_isVertical) {
1688         if (m_stretchyData.mode() == DrawSizeVariant) {
1689             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1690             m_stretchHeightAboveBaseline = -glyphBounds.y();
1691             m_stretchDepthBelowBaseline = glyphBounds.maxY();
1692             m_stretchWidth = advanceForGlyph(m_stretchyData.variant());
1693         } else if (m_stretchyData.mode() == DrawGlyphAssembly) {
1694             FloatRect glyphBounds;
1695             m_stretchHeightAboveBaseline = 0;
1696             m_stretchDepthBelowBaseline = 0;
1697
1698             glyphBounds = boundsForGlyph(m_stretchyData.left());
1699             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1700             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1701
1702             glyphBounds = boundsForGlyph(m_stretchyData.right());
1703             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1704             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1705
1706             glyphBounds = boundsForGlyph(m_stretchyData.extension());
1707             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1708             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1709
1710             if (m_stretchyData.middle().glyph) {
1711                 glyphBounds = boundsForGlyph(m_stretchyData.middle());
1712                 m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1713                 m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1714             }
1715         }
1716     }
1717 }
1718
1719 int RenderMathMLOperator::firstLineBaseline() const
1720 {
1721     if (m_stretchyData.mode() != DrawNormal)
1722         return m_stretchHeightAboveBaseline;
1723     return RenderMathMLToken::firstLineBaseline();
1724 }
1725
1726 void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
1727 {
1728     if (m_stretchyData.mode() != DrawNormal)
1729         logicalHeight = m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline;
1730     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
1731 }
1732
1733 LayoutRect RenderMathMLOperator::paintGlyph(PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
1734 {
1735     FloatRect glyphBounds = boundsForGlyph(data);
1736
1737     LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
1738     glyphPaintRect.setY(origin.y() + glyphBounds.y());
1739
1740     // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
1741     // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
1742     // than full coverage. These edge pixels can introduce small seams between connected glyphs
1743     FloatRect clipBounds = info.rect;
1744     switch (trim) {
1745     case TrimTop:
1746         glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
1747         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
1748         break;
1749     case TrimBottom:
1750         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
1751         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
1752         break;
1753     case TrimTopAndBottom: {
1754         LayoutUnit temp = glyphPaintRect.y() + 1;
1755         glyphPaintRect.shiftYEdgeTo(temp.ceil());
1756         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
1757         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
1758         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
1759     }
1760         break;
1761     case TrimLeft:
1762         glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
1763         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
1764         break;
1765     case TrimRight:
1766         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
1767         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
1768         break;
1769     case TrimLeftAndRight: {
1770         LayoutUnit temp = glyphPaintRect.x() + 1;
1771         glyphPaintRect.shiftXEdgeTo(temp.ceil());
1772         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
1773         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
1774         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
1775     }
1776     }
1777
1778     // Clipping the enclosing IntRect avoids any potential issues at joined edges.
1779     GraphicsContextStateSaver stateSaver(*info.context);
1780     info.context->clip(clipBounds);
1781
1782     GlyphBuffer buffer;
1783     buffer.add(data.glyph, data.fontData, advanceForGlyph(data));
1784     info.context->drawGlyphs(style().font(), *data.fontData, buffer, 0, 1, origin);
1785
1786     return glyphPaintRect;
1787 }
1788
1789 void RenderMathMLOperator::fillWithVerticalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1790 {
1791     ASSERT(m_isVertical);
1792     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1793     ASSERT(m_stretchyData.extension().glyph);
1794     ASSERT(from.y() <= to.y());
1795
1796     // If there is no space for the extension glyph, we don't need to do anything.
1797     if (from.y() == to.y())
1798         return;
1799
1800     GraphicsContextStateSaver stateSaver(*info.context);
1801
1802     FloatRect glyphBounds = boundsForGlyph(m_stretchyData.extension());
1803
1804     // Clipping the extender region here allows us to draw the bottom extender glyph into the
1805     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
1806     LayoutRect clipBounds = info.rect;
1807     clipBounds.shiftYEdgeTo(from.y());
1808     clipBounds.shiftMaxYEdgeTo(to.y());
1809     info.context->clip(clipBounds);
1810
1811     // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
1812     float offsetToGlyphTop = glyphBounds.y() + 2;
1813     LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
1814     FloatRect lastPaintedGlyphRect(from, FloatSize());
1815
1816     while (lastPaintedGlyphRect.maxY() < to.y()) {
1817         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimTopAndBottom);
1818         glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
1819
1820         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
1821         // with trimming. In that case we just draw nothing.
1822         if (lastPaintedGlyphRect.isEmpty())
1823             break;
1824     }
1825 }
1826
1827 void RenderMathMLOperator::fillWithHorizontalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1828 {
1829     ASSERT(!m_isVertical);
1830     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1831     ASSERT(m_stretchyData.extension().glyph);
1832     ASSERT(from.x() <= to.x());
1833
1834     // If there is no space for the extension glyph, we don't need to do anything.
1835     if (from.x() == to.x())
1836         return;
1837
1838     GraphicsContextStateSaver stateSaver(*info.context);
1839
1840     // Clipping the extender region here allows us to draw the bottom extender glyph into the
1841     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
1842     LayoutRect clipBounds = info.rect;
1843     clipBounds.shiftXEdgeTo(from.x());
1844     clipBounds.shiftMaxXEdgeTo(to.x());
1845     info.context->clip(clipBounds);
1846
1847     // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
1848     float offsetToGlyphLeft = -2;
1849     LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, std::min(from.y(), to.y()) + m_stretchHeightAboveBaseline);
1850     FloatRect lastPaintedGlyphRect(from, FloatSize());
1851
1852     while (lastPaintedGlyphRect.maxX() < to.x()) {
1853         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimLeftAndRight);
1854         glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
1855
1856         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
1857         // with trimming. In that case we just draw nothing.
1858         if (lastPaintedGlyphRect.isEmpty())
1859             break;
1860     }
1861 }
1862
1863 void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
1864 {
1865     RenderMathMLToken::paint(info, paintOffset);
1866
1867     if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || m_stretchyData.mode() == DrawNormal)
1868         return;
1869
1870     GraphicsContextStateSaver stateSaver(*info.context);
1871     info.context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace());
1872
1873     if (m_stretchyData.mode() == DrawSizeVariant) {
1874         ASSERT(m_stretchyData.variant().glyph);
1875         GlyphBuffer buffer;
1876         buffer.add(m_stretchyData.variant().glyph, m_stretchyData.variant().fontData, advanceForGlyph(m_stretchyData.variant()));
1877         LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location());
1878         FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1879         LayoutPoint operatorOrigin(operatorTopLeft.x(), operatorTopLeft.y() - glyphBounds.y());
1880         info.context->drawGlyphs(style().font(), *m_stretchyData.variant().fontData, buffer, 0, 1, operatorOrigin);
1881         return;
1882     }
1883
1884     if (m_isVertical)
1885         paintVerticalGlyphAssembly(info, paintOffset);
1886     else
1887         paintHorizontalGlyphAssembly(info, paintOffset);
1888 }
1889
1890 void RenderMathMLOperator::paintVerticalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1891 {
1892     ASSERT(m_isVertical);
1893     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1894     ASSERT(m_stretchyData.top().glyph);
1895     ASSERT(m_stretchyData.bottom().glyph);
1896
1897     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
1898     LayoutPoint operatorTopLeft = paintOffset + location();
1899     operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
1900     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
1901     FloatRect topGlyphBounds = boundsForGlyph(m_stretchyData.top());
1902     LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
1903     LayoutRect topGlyphPaintRect = paintGlyph(info, m_stretchyData.top(), topGlyphOrigin, TrimBottom);
1904
1905     FloatRect bottomGlyphBounds = boundsForGlyph(m_stretchyData.bottom());
1906     LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
1907     LayoutRect bottomGlyphPaintRect = paintGlyph(info, m_stretchyData.bottom(), bottomGlyphOrigin, TrimTop);
1908
1909     if (m_stretchyData.middle().glyph) {
1910         // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
1911         FloatRect middleGlyphBounds = boundsForGlyph(m_stretchyData.middle());
1912         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
1913         middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
1914         middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
1915
1916         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimTopAndBottom);
1917         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
1918         fillWithVerticalExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1919     } else
1920         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1921 }
1922
1923 void RenderMathMLOperator::paintHorizontalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1924 {
1925     ASSERT(!m_isVertical);
1926     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1927     ASSERT(m_stretchyData.left().glyph);
1928     ASSERT(m_stretchyData.right().glyph);
1929
1930     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
1931     LayoutPoint operatorTopLeft = paintOffset + location();
1932     operatorTopLeft.move(m_leadingSpace, 0);
1933     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
1934     LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
1935     LayoutRect leftGlyphPaintRect = paintGlyph(info, m_stretchyData.left(), leftGlyphOrigin, TrimRight);
1936
1937     FloatRect rightGlyphBounds = boundsForGlyph(m_stretchyData.right());
1938     LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + offsetWidth() - rightGlyphBounds.width(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
1939     LayoutRect rightGlyphPaintRect = paintGlyph(info, m_stretchyData.right(), rightGlyphOrigin, TrimLeft);
1940
1941     if (m_stretchyData.middle().glyph) {
1942         // Center the glyph origin between the start and end glyph paint extents.
1943         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), leftGlyphOrigin.y());
1944         middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
1945         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimLeftAndRight);
1946         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), middleGlyphPaintRect.minXMinYCorner());
1947         fillWithHorizontalExtensionGlyph(info, middleGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
1948     } else
1949         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
1950 }
1951
1952 void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
1953 {
1954     // We skip painting for invisible operators too to avoid some "missing character" glyph to appear if appropriate math fonts are not available.
1955     if (m_stretchyData.mode() != DrawNormal || isInvisibleOperator())
1956         return;
1957     RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
1958 }
1959     
1960 }
1961
1962 #endif