83974c9bfce8a9db72700eed838c07fb6c166e2c
[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, static_cast<Form>(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, Ref<RenderStyle>&& style)
1139     : RenderMathMLToken(element, WTF::move(style))
1140     , m_stretchHeightAboveBaseline(0)
1141     , m_stretchDepthBelowBaseline(0)
1142     , m_textContent(0)
1143     , m_isVertical(true)
1144 {
1145     updateTokenContent();
1146 }
1147
1148 RenderMathMLOperator::RenderMathMLOperator(Document& document, Ref<RenderStyle>&& style, const String& operatorString, MathMLOperatorDictionary::Form form, unsigned short flags)
1149     : RenderMathMLToken(document, WTF::move(style))
1150     , m_stretchHeightAboveBaseline(0)
1151     , m_stretchDepthBelowBaseline(0)
1152     , m_textContent(0)
1153     , m_isVertical(true)
1154     , m_operatorForm(form)
1155     , m_operatorFlags(flags)
1156 {
1157     updateTokenContent(operatorString);
1158 }
1159
1160 void RenderMathMLOperator::setOperatorFlagAndScheduleLayoutIfNeeded(MathMLOperatorDictionary::Flag flag, const AtomicString& attributeValue)
1161 {
1162     unsigned short oldOperatorFlags = m_operatorFlags;
1163
1164     setOperatorFlagFromAttributeValue(flag, attributeValue);
1165
1166     if (oldOperatorFlags != m_operatorFlags)
1167         setNeedsLayoutAndPrefWidthsRecalc();
1168 }
1169
1170 void RenderMathMLOperator::setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag flag, const QualifiedName& name)
1171 {
1172     setOperatorFlagFromAttributeValue(flag, element().fastGetAttribute(name));
1173 }
1174
1175 void RenderMathMLOperator::setOperatorFlagFromAttributeValue(MathMLOperatorDictionary::Flag flag, const AtomicString& attributeValue)
1176 {
1177     ASSERT(!isAnonymous());
1178
1179     if (attributeValue == "true")
1180         m_operatorFlags |= flag;
1181     else if (attributeValue == "false")
1182         m_operatorFlags &= ~flag;
1183     // We ignore absent or invalid attributes.
1184 }
1185
1186 void RenderMathMLOperator::setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry* entry)
1187 {
1188     // If this operator is anonymous, we preserve the Fence and Separator properties. This is to handle the case of RenderMathMLFenced.
1189     if (isAnonymous())
1190         m_operatorFlags = (m_operatorFlags & (MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator)) | entry->flags;
1191     else
1192         m_operatorFlags = entry->flags;
1193
1194     // Leading and trailing space is specified as multiple of 1/18em.
1195     m_leadingSpace = entry->lspace * style().font().size() / 18;
1196     m_trailingSpace = entry->rspace * style().font().size() / 18;
1197 }
1198
1199 void RenderMathMLOperator::setOperatorProperties()
1200 {
1201     // We determine the stretch direction (default is vertical).
1202     m_isVertical = !(tryBinarySearch<const UChar, UChar>(MathMLOperatorDictionary::horizontalOperators, WTF_ARRAY_LENGTH(MathMLOperatorDictionary::horizontalOperators), m_textContent, MathMLOperatorDictionary::ExtractKeyHorizontal));
1203
1204     // We determine the form of the operator.
1205     bool explicitForm = true;
1206     if (!isAnonymous()) {
1207         const AtomicString& form = element().fastGetAttribute(MathMLNames::formAttr);
1208         if (form == "prefix")
1209             m_operatorForm = MathMLOperatorDictionary::Prefix;
1210         else if (form == "infix")
1211             m_operatorForm = MathMLOperatorDictionary::Infix;
1212         else if (form == "postfix")
1213             m_operatorForm = MathMLOperatorDictionary::Postfix;
1214         else {
1215             // 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).
1216             explicitForm = false;
1217             if (!element().previousSibling() && element().nextSibling())
1218                 m_operatorForm = MathMLOperatorDictionary::Prefix;
1219             else if (element().previousSibling() && !element().nextSibling())
1220                 m_operatorForm = MathMLOperatorDictionary::Postfix;
1221             else
1222                 m_operatorForm = MathMLOperatorDictionary::Infix;
1223         }
1224     }
1225
1226     // We determine the default values of the operator properties.
1227
1228     // First we initialize with the default values for unknown operators.
1229     if (isAnonymous())
1230         m_operatorFlags &= MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator; // This resets all but the Fence and Separator properties.
1231     else
1232         m_operatorFlags = 0; // This resets all the operator properties.
1233     m_leadingSpace = 5 * style().font().size() / 18; // This sets leading space to "thickmathspace".
1234     m_trailingSpace = 5 * style().font().size() / 18; // This sets trailing space to "thickmathspace".
1235     m_minSize = style().font().size(); // This sets minsize to "1em".
1236     m_maxSize = intMaxForLayoutUnit; // This sets maxsize to "infinity".
1237
1238     if (m_textContent) {
1239         // Then we try to find the default values from the operator dictionary.
1240         if (const MathMLOperatorDictionary::Entry* entry = tryBinarySearch<const MathMLOperatorDictionary::Entry, MathMLOperatorDictionary::Key>(MathMLOperatorDictionary::dictionary, MATHML_OPDICT_SIZE, MathMLOperatorDictionary::Key(m_textContent, m_operatorForm), MathMLOperatorDictionary::ExtractKey))
1241             setOperatorPropertiesFromOpDictEntry(entry);
1242         else if (!explicitForm) {
1243             // 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.
1244             // 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.
1245             if (const MathMLOperatorDictionary::Entry* entry = tryBinarySearch<const MathMLOperatorDictionary::Entry, UChar>(MathMLOperatorDictionary::dictionary, MATHML_OPDICT_SIZE, m_textContent, MathMLOperatorDictionary::ExtractChar)) {
1246                 // 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.
1247                 if (entry != MathMLOperatorDictionary::dictionary && (entry-1)->character == m_textContent)
1248                     entry--;
1249                 m_operatorForm = static_cast<MathMLOperatorDictionary::Form>(entry->form); // We override the form previously determined.
1250                 setOperatorPropertiesFromOpDictEntry(entry);
1251             }
1252         }
1253     }
1254 #undef MATHML_OPDICT_SIZE
1255
1256     if (!isAnonymous()) {
1257         // Finally, we make the attribute values override the default.
1258
1259         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Fence, MathMLNames::fenceAttr);
1260         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Separator, MathMLNames::separatorAttr);
1261         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Stretchy, MathMLNames::stretchyAttr);
1262         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Symmetric, MathMLNames::symmetricAttr);
1263         setOperatorFlagFromAttribute(MathMLOperatorDictionary::LargeOp, MathMLNames::largeopAttr);
1264         setOperatorFlagFromAttribute(MathMLOperatorDictionary::MovableLimits, MathMLNames::movablelimitsAttr);
1265         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Accent, MathMLNames::accentAttr);
1266
1267         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).
1268         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).
1269
1270         parseMathMLLength(element().fastGetAttribute(MathMLNames::minsizeAttr), m_minSize, &style(), false);
1271         const AtomicString& maxsize = element().fastGetAttribute(MathMLNames::maxsizeAttr);
1272         if (maxsize != "infinity")
1273             parseMathMLLength(maxsize, m_maxSize, &style(), false);
1274     }
1275 }
1276
1277 bool RenderMathMLOperator::isChildAllowed(const RenderObject&, const RenderStyle&) const
1278 {
1279     return false;
1280 }
1281
1282 void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
1283 {
1284     if (!m_isVertical || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline))
1285         return;
1286
1287     m_stretchHeightAboveBaseline = heightAboveBaseline;
1288     m_stretchDepthBelowBaseline = depthBelowBaseline;
1289
1290     setOperatorProperties();
1291     if (hasOperatorFlag(MathMLOperatorDictionary::Symmetric)) {
1292         // We make the operator stretch symmetrically above and below the axis.
1293         // 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().
1294         LayoutUnit axis = style().fontMetrics().xHeight() / 2;
1295         LayoutUnit halfStretchSize = std::max(m_stretchHeightAboveBaseline - axis, m_stretchDepthBelowBaseline + axis);
1296         m_stretchHeightAboveBaseline = halfStretchSize + axis;
1297         m_stretchDepthBelowBaseline = halfStretchSize - axis;
1298     }
1299     // We try to honor the minsize/maxsize condition by increasing or decreasing both height and depth proportionately.
1300     // The MathML specification does not indicate what to do when maxsize < minsize, so we follow Gecko and make minsize take precedence.
1301     LayoutUnit size = stretchSize();
1302     float aspect = 1.0;
1303     if (size > 0) {
1304         if (size < m_minSize)
1305             aspect = float(m_minSize) / size;
1306         else if (m_maxSize < size)
1307             aspect = float(m_maxSize) / size;
1308     }
1309     m_stretchHeightAboveBaseline *= aspect;
1310     m_stretchDepthBelowBaseline *= aspect;
1311     updateStyle();
1312 }
1313
1314 void RenderMathMLOperator::stretchTo(LayoutUnit width)
1315 {
1316     if (m_isVertical || m_stretchWidth == width)
1317         return;
1318
1319     m_stretchWidth = width;
1320
1321     setOperatorProperties();
1322
1323     updateStyle();
1324 }
1325
1326 void RenderMathMLOperator::resetStretchSize()
1327 {
1328     if (m_isVertical) {
1329         m_stretchHeightAboveBaseline = 0;
1330         m_stretchDepthBelowBaseline = 0;
1331     } else
1332         m_stretchWidth = 0;
1333 }
1334
1335 FloatRect RenderMathMLOperator::boundsForGlyph(const GlyphData& data) const
1336 {
1337     return data.fontData->boundsForGlyph(data.glyph);
1338 }
1339
1340 float RenderMathMLOperator::heightForGlyph(const GlyphData& data) const
1341 {
1342     return boundsForGlyph(data).height();
1343 }
1344
1345 float RenderMathMLOperator::advanceForGlyph(const GlyphData& data) const
1346 {
1347     return data.fontData->widthForGlyph(data.glyph);
1348 }
1349
1350 void RenderMathMLOperator::computePreferredLogicalWidths()
1351 {
1352     ASSERT(preferredLogicalWidthsDirty());
1353
1354     setOperatorProperties();
1355     if (!shouldAllowStretching()) {
1356         RenderMathMLToken::computePreferredLogicalWidths();
1357         if (isInvisibleOperator()) {
1358             // In some fonts, glyphs for invisible operators have nonzero width. Consequently, we subtract that width here to avoid wide gaps.
1359             GlyphData data = style().font().glyphDataForCharacter(m_textContent, false);
1360             float glyphWidth = advanceForGlyph(data);
1361             ASSERT(glyphWidth <= m_minPreferredLogicalWidth);
1362             m_minPreferredLogicalWidth -= glyphWidth;
1363             m_maxPreferredLogicalWidth -= glyphWidth;
1364         }
1365         return;
1366     }
1367
1368     GlyphData data = style().font().glyphDataForCharacter(m_textContent, !style().isLeftToRightDirection());
1369     float maximumGlyphWidth = advanceForGlyph(data);
1370     if (!m_isVertical) {
1371         if (maximumGlyphWidth < stretchSize())
1372             maximumGlyphWidth = stretchSize();
1373         m_maxPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
1374         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
1375         return;
1376     }
1377     if (isLargeOperatorInDisplayStyle()) {
1378         // 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/).
1379         StretchyData largeOperator = getDisplayStyleLargeOperator(m_textContent);
1380         if (largeOperator.mode() == DrawSizeVariant)
1381             maximumGlyphWidth = boundsForGlyph(largeOperator.variant()).width();
1382     } else {
1383         // 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).
1384         findStretchyData(m_textContent, &maximumGlyphWidth);
1385     }
1386     m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
1387 }
1388
1389 void RenderMathMLOperator::rebuildTokenContent(const String& operatorString)
1390 {
1391     // We collapse the whitespace and replace the hyphens by minus signs.
1392     AtomicString textContent = operatorString.stripWhiteSpace().simplifyWhiteSpace().replace(hyphenMinus, minusSign).impl();
1393
1394     // We destroy the wrapper and rebuild it.
1395     // FIXME: Using this RenderText make the text inaccessible to the dumpAsText/selection code (https://bugs.webkit.org/show_bug.cgi?id=125597).
1396     if (firstChild())
1397         downcast<RenderElement>(*firstChild()).destroy();
1398     createWrapperIfNeeded();
1399     RenderPtr<RenderText> text = createRenderer<RenderText>(document(), textContent);
1400     downcast<RenderElement>(*firstChild()).addChild(text.leakPtr());
1401
1402     // We verify whether the operator text can be represented by a single UChar.
1403     // FIXME: This does not handle surrogate pairs (https://bugs.webkit.org/show_bug.cgi?id=122296).
1404     // FIXME: This does not handle <mo> operators with multiple characters (https://bugs.webkit.org/show_bug.cgi?id=124828).
1405     m_textContent = textContent.length() == 1 ? textContent[0] : 0;
1406     setOperatorProperties();
1407     updateStyle();
1408     setNeedsLayoutAndPrefWidthsRecalc();
1409 }
1410
1411 void RenderMathMLOperator::updateTokenContent(const String& operatorString)
1412 {
1413     ASSERT(isAnonymous());
1414     rebuildTokenContent(operatorString);
1415 }
1416
1417 void RenderMathMLOperator::updateTokenContent()
1418 {
1419     ASSERT(!isAnonymous());
1420     rebuildTokenContent(element().textContent());
1421 }
1422
1423 void RenderMathMLOperator::updateFromElement()
1424 {
1425     setOperatorProperties();
1426     RenderMathMLToken::updateFromElement();
1427 }
1428
1429 void RenderMathMLOperator::updateOperatorProperties()
1430 {
1431     setOperatorProperties();
1432     if (!isEmpty())
1433         updateStyle();
1434     setNeedsLayoutAndPrefWidthsRecalc();
1435 }
1436
1437 bool RenderMathMLOperator::shouldAllowStretching() const
1438 {
1439     return m_textContent && (hasOperatorFlag(MathMLOperatorDictionary::Stretchy) || isLargeOperatorInDisplayStyle());
1440 }
1441
1442 bool RenderMathMLOperator::getGlyphAssemblyFallBack(Vector<OpenTypeMathData::AssemblyPart> assemblyParts, StretchyData& stretchyData) const
1443 {
1444     GlyphData top;
1445     GlyphData extension;
1446     GlyphData bottom;
1447     GlyphData middle;
1448
1449     // 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.
1450     // FIXME: RenderMathMLOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
1451     // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
1452
1453     // We count the number of non extender pieces.
1454     int nonExtenderCount = 0;
1455     for (auto& part : assemblyParts) {
1456         if (!part.isExtender)
1457             nonExtenderCount++;
1458     }
1459     if (nonExtenderCount > 3)
1460         return false; // This is not supported: there are too many pieces.
1461
1462     // We now browse the list of pieces.
1463     // 1 = look for a left/bottom glyph
1464     // 2 = look for an extender between left/bottom and mid
1465     // 4 = look for a middle glyph
1466     // 5 = look for an extender between middle and right/top
1467     // 5 = look for a right/top glyph
1468     // 6 = no more piece expected
1469     unsigned state = 1;
1470
1471     extension.glyph = 0;
1472     middle.glyph = 0;
1473     for (auto& part : assemblyParts) {
1474         if ((state == 2 || state == 3) && nonExtenderCount < 3) {
1475             // We do not try to find a middle glyph.
1476             state += 2;
1477         }
1478         if (part.isExtender) {
1479             if (!extension.glyph)
1480                 extension.glyph = part.glyph;
1481             else if (extension.glyph != part.glyph)
1482                 return false; // This is not supported: the assembly has different extenders.
1483
1484             if (state == 1) {
1485                 // We ignore left/bottom piece and multiple successive extenders.
1486                 state = 2;
1487             } else if (state == 3) {
1488                 // We ignore middle piece and multiple successive extenders.
1489                 state = 4;
1490             } else if (state >= 5)
1491                 return false; // This is not supported: we got an unexpected extender.
1492             continue;
1493         }
1494
1495         if (state == 1) {
1496             // We copy the left/bottom part.
1497             bottom.glyph = part.glyph;
1498             state = 2;
1499             continue;
1500         }
1501
1502         if (state == 2 || state == 3) {
1503             // We copy the middle part.
1504             middle.glyph = part.glyph;
1505             state = 4;
1506             continue;
1507         }
1508
1509         if (state == 4 || state == 5) {
1510             // We copy the right/top part.
1511             top.glyph = part.glyph;
1512             state = 6;
1513         }
1514     }
1515
1516     if (!extension.glyph)
1517         return false; // This is not supported: we always assume that we have an extension glyph.
1518
1519     // If we don't have top/bottom glyphs, we use the extension glyph.
1520     if (!top.glyph)
1521         top.glyph = extension.glyph;
1522     if (!bottom.glyph)
1523         bottom.glyph = extension.glyph;
1524
1525     top.fontData = style().font().primaryFont();
1526     extension.fontData = top.fontData;
1527     bottom.fontData = top.fontData;
1528     if (middle.glyph)
1529         middle.fontData = top.fontData;
1530
1531     stretchyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1532
1533     return true;
1534 }
1535
1536 RenderMathMLOperator::StretchyData RenderMathMLOperator::getDisplayStyleLargeOperator(UChar character) const
1537 {
1538     StretchyData data;
1539
1540     ASSERT(m_isVertical && isLargeOperatorInDisplayStyle());
1541
1542     const auto& primaryFontData = style().font().primaryFont();
1543     GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
1544     if (!primaryFontData || !primaryFontData->mathData() || baseGlyph.fontData != primaryFontData)
1545         return data;
1546
1547     Vector<Glyph> sizeVariants;
1548     Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
1549
1550     // 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.
1551     float displayOperatorMinHeight = std::max(baseGlyph.fontData->boundsForGlyph(baseGlyph.glyph).height() * sqrtOfTwoFloat, primaryFontData->mathData()->getMathConstant(primaryFontData, OpenTypeMathData::DisplayOperatorMinHeight));
1552
1553     primaryFontData->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
1554
1555     // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
1556     for (auto& variant : sizeVariants) {
1557         GlyphData sizeVariant;
1558         sizeVariant.glyph = variant;
1559         sizeVariant.fontData = primaryFontData;
1560         data.setSizeVariantMode(sizeVariant);
1561         if (boundsForGlyph(sizeVariant).height() >= displayOperatorMinHeight)
1562             return data;
1563     }
1564     return data;
1565 }
1566
1567 RenderMathMLOperator::StretchyData RenderMathMLOperator::findStretchyData(UChar character, float* maximumGlyphWidth)
1568 {
1569     ASSERT(!maximumGlyphWidth || m_isVertical);
1570
1571     StretchyData data;
1572     StretchyData assemblyData;
1573
1574     const auto& primaryFontData = style().font().primaryFont();
1575     GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
1576     
1577     if (primaryFontData && primaryFontData->mathData() && baseGlyph.fontData == primaryFontData) {
1578         Vector<Glyph> sizeVariants;
1579         Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
1580         primaryFontData->mathData()->getMathVariants(baseGlyph.glyph, m_isVertical, sizeVariants, assemblyParts);
1581         // We verify the size variants.
1582         for (auto& variant : sizeVariants) {
1583             GlyphData sizeVariant;
1584             sizeVariant.glyph = variant;
1585             sizeVariant.fontData = primaryFontData;
1586             if (maximumGlyphWidth)
1587                 *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(sizeVariant));
1588             else {
1589                 data.setSizeVariantMode(sizeVariant);
1590                 float size = m_isVertical ? heightForGlyph(sizeVariant) : advanceForGlyph(sizeVariant);
1591                 if (size >= stretchSize()) 
1592                     return data;
1593             }
1594         }
1595
1596         // We verify if there is a construction.
1597         if (!getGlyphAssemblyFallBack(assemblyParts, assemblyData))
1598             return data;
1599     } else {
1600         if (!m_isVertical)
1601             return data;
1602
1603         // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
1604         const StretchyCharacter* stretchyCharacter = nullptr;
1605         const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
1606         for (unsigned index = 0; index < maxIndex; ++index) {
1607             if (stretchyCharacters[index].character == character) {
1608                 stretchyCharacter = &stretchyCharacters[index];
1609                 if (!style().isLeftToRightDirection() && index < leftRightPairsCount * 2) {
1610                     // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
1611                     index += index % 2 ? -1 : 1;
1612                 }
1613                 break;
1614             }
1615         }
1616
1617         // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
1618         if (!stretchyCharacter)
1619             return data;
1620
1621         // We convert the list of Unicode characters into a list of glyph data.
1622         GlyphData top = style().font().glyphDataForCharacter(stretchyCharacter->topChar, false);
1623         GlyphData extension = style().font().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
1624         GlyphData bottom = style().font().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
1625         GlyphData middle;
1626         if (stretchyCharacter->middleChar)
1627             middle = style().font().glyphDataForCharacter(stretchyCharacter->middleChar, false);
1628         assemblyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1629     }
1630
1631     ASSERT(assemblyData.mode() == DrawGlyphAssembly);
1632
1633     // If we are measuring the maximum width, verify each component.
1634     if (maximumGlyphWidth) {
1635         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.top()));
1636         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.extension()));
1637         if (assemblyData.middle().glyph)
1638             *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.middle()));
1639         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.bottom()));
1640         return assemblyData;
1641     }
1642
1643     // We ensure that the size is large enough to avoid glyph overlaps.
1644     float size;
1645     if (m_isVertical) {
1646         size = heightForGlyph(assemblyData.top()) + heightForGlyph(assemblyData.bottom());
1647         if (assemblyData.middle().glyph)
1648             size += heightForGlyph(assemblyData.middle());
1649     } else {
1650         size = advanceForGlyph(assemblyData.left()) + advanceForGlyph(assemblyData.right());
1651         if (assemblyData.middle().glyph)
1652             size += advanceForGlyph(assemblyData.middle());
1653     }
1654     if (size > stretchSize())
1655         return data;
1656
1657     return assemblyData;
1658 }
1659
1660 void RenderMathMLOperator::updateStyle()
1661 {
1662     FontCachePurgePreventer fontCachePurgePreventer;
1663
1664     ASSERT(firstChild());
1665     if (!firstChild())
1666         return;
1667
1668     m_stretchyData.setNormalMode();
1669     // We add spacing around the operator.
1670     // FIXME: The spacing should be added to the whole embellished operator (https://bugs.webkit.org/show_bug.cgi?id=124831).
1671     // FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing).
1672     const auto& wrapper = downcast<RenderElement>(firstChild());
1673     auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
1674     newStyle.get().setMarginStart(Length(m_leadingSpace, Fixed));
1675     newStyle.get().setMarginEnd(Length(m_trailingSpace, Fixed));
1676     wrapper->setStyle(WTF::move(newStyle));
1677     wrapper->setNeedsLayoutAndPrefWidthsRecalc();
1678
1679     if (!shouldAllowStretching())
1680         return;
1681
1682     if (m_isVertical && isLargeOperatorInDisplayStyle())
1683         m_stretchyData = getDisplayStyleLargeOperator(m_textContent);
1684     else {
1685         // We do not stretch if the base glyph is large enough.
1686         GlyphData baseGlyph = style().font().glyphDataForCharacter(m_textContent, !style().isLeftToRightDirection());
1687         float baseSize = m_isVertical ? heightForGlyph(baseGlyph) : advanceForGlyph(baseGlyph);
1688         if (stretchSize() <= baseSize)
1689             return;
1690         m_stretchyData = findStretchyData(m_textContent, nullptr);
1691     }
1692
1693     if (m_isVertical && m_stretchyData.mode() == DrawSizeVariant) {
1694         // We resize the operator to match the one of the size variant.
1695         if (isLargeOperatorInDisplayStyle()) {
1696             // The stretch size is actually not involved in the selection of the size variant in getDisplayStyleLargeOperator.
1697             // We simply use the height and depth of the selected size variant glyph.
1698             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1699             m_stretchHeightAboveBaseline = -glyphBounds.y();
1700             m_stretchDepthBelowBaseline = glyphBounds.maxY();
1701         } else {
1702             // We rescale the height and depth proportionately.
1703             float variantSize = heightForGlyph(m_stretchyData.variant());
1704             float size = stretchSize();
1705             float aspect = size > 0 ? variantSize / size : 1.0;
1706             m_stretchHeightAboveBaseline *= aspect;
1707             m_stretchDepthBelowBaseline *= aspect;
1708         }
1709     }
1710
1711     if (!m_isVertical) {
1712         if (m_stretchyData.mode() == DrawSizeVariant) {
1713             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1714             m_stretchHeightAboveBaseline = -glyphBounds.y();
1715             m_stretchDepthBelowBaseline = glyphBounds.maxY();
1716             m_stretchWidth = advanceForGlyph(m_stretchyData.variant());
1717         } else if (m_stretchyData.mode() == DrawGlyphAssembly) {
1718             FloatRect glyphBounds;
1719             m_stretchHeightAboveBaseline = 0;
1720             m_stretchDepthBelowBaseline = 0;
1721
1722             glyphBounds = boundsForGlyph(m_stretchyData.left());
1723             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1724             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1725
1726             glyphBounds = boundsForGlyph(m_stretchyData.right());
1727             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1728             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1729
1730             glyphBounds = boundsForGlyph(m_stretchyData.extension());
1731             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1732             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1733
1734             if (m_stretchyData.middle().glyph) {
1735                 glyphBounds = boundsForGlyph(m_stretchyData.middle());
1736                 m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
1737                 m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
1738             }
1739         }
1740     }
1741 }
1742
1743 int RenderMathMLOperator::firstLineBaseline() const
1744 {
1745     if (m_stretchyData.mode() != DrawNormal)
1746         return m_stretchHeightAboveBaseline;
1747     return RenderMathMLToken::firstLineBaseline();
1748 }
1749
1750 void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
1751 {
1752     if (m_stretchyData.mode() != DrawNormal)
1753         logicalHeight = m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline;
1754     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
1755 }
1756
1757 LayoutRect RenderMathMLOperator::paintGlyph(PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
1758 {
1759     FloatRect glyphBounds = boundsForGlyph(data);
1760
1761     LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
1762     glyphPaintRect.setY(origin.y() + glyphBounds.y());
1763
1764     // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
1765     // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
1766     // than full coverage. These edge pixels can introduce small seams between connected glyphs
1767     FloatRect clipBounds = info.rect;
1768     switch (trim) {
1769     case TrimTop:
1770         glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
1771         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
1772         break;
1773     case TrimBottom:
1774         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
1775         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
1776         break;
1777     case TrimTopAndBottom: {
1778         LayoutUnit temp = glyphPaintRect.y() + 1;
1779         glyphPaintRect.shiftYEdgeTo(temp.ceil());
1780         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
1781         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
1782         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
1783     }
1784         break;
1785     case TrimLeft:
1786         glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
1787         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
1788         break;
1789     case TrimRight:
1790         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
1791         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
1792         break;
1793     case TrimLeftAndRight: {
1794         LayoutUnit temp = glyphPaintRect.x() + 1;
1795         glyphPaintRect.shiftXEdgeTo(temp.ceil());
1796         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
1797         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
1798         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
1799     }
1800     }
1801
1802     // Clipping the enclosing IntRect avoids any potential issues at joined edges.
1803     GraphicsContextStateSaver stateSaver(*info.context);
1804     info.context->clip(clipBounds);
1805
1806     GlyphBuffer buffer;
1807     buffer.add(data.glyph, data.fontData, advanceForGlyph(data));
1808     info.context->drawGlyphs(style().font(), *data.fontData, buffer, 0, 1, origin);
1809
1810     return glyphPaintRect;
1811 }
1812
1813 void RenderMathMLOperator::fillWithVerticalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1814 {
1815     ASSERT(m_isVertical);
1816     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1817     ASSERT(m_stretchyData.extension().glyph);
1818     ASSERT(from.y() <= to.y());
1819
1820     // If there is no space for the extension glyph, we don't need to do anything.
1821     if (from.y() == to.y())
1822         return;
1823
1824     GraphicsContextStateSaver stateSaver(*info.context);
1825
1826     FloatRect glyphBounds = boundsForGlyph(m_stretchyData.extension());
1827
1828     // Clipping the extender region here allows us to draw the bottom extender glyph into the
1829     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
1830     LayoutRect clipBounds = info.rect;
1831     clipBounds.shiftYEdgeTo(from.y());
1832     clipBounds.shiftMaxYEdgeTo(to.y());
1833     info.context->clip(clipBounds);
1834
1835     // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
1836     float offsetToGlyphTop = glyphBounds.y() + 2;
1837     LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
1838     FloatRect lastPaintedGlyphRect(from, FloatSize());
1839
1840     while (lastPaintedGlyphRect.maxY() < to.y()) {
1841         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimTopAndBottom);
1842         glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
1843
1844         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
1845         // with trimming. In that case we just draw nothing.
1846         if (lastPaintedGlyphRect.isEmpty())
1847             break;
1848     }
1849 }
1850
1851 void RenderMathMLOperator::fillWithHorizontalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1852 {
1853     ASSERT(!m_isVertical);
1854     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1855     ASSERT(m_stretchyData.extension().glyph);
1856     ASSERT(from.x() <= to.x());
1857
1858     // If there is no space for the extension glyph, we don't need to do anything.
1859     if (from.x() == to.x())
1860         return;
1861
1862     GraphicsContextStateSaver stateSaver(*info.context);
1863
1864     // Clipping the extender region here allows us to draw the bottom extender glyph into the
1865     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
1866     LayoutRect clipBounds = info.rect;
1867     clipBounds.shiftXEdgeTo(from.x());
1868     clipBounds.shiftMaxXEdgeTo(to.x());
1869     info.context->clip(clipBounds);
1870
1871     // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
1872     float offsetToGlyphLeft = -2;
1873     LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, std::min(from.y(), to.y()) + m_stretchHeightAboveBaseline);
1874     FloatRect lastPaintedGlyphRect(from, FloatSize());
1875
1876     while (lastPaintedGlyphRect.maxX() < to.x()) {
1877         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimLeftAndRight);
1878         glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
1879
1880         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
1881         // with trimming. In that case we just draw nothing.
1882         if (lastPaintedGlyphRect.isEmpty())
1883             break;
1884     }
1885 }
1886
1887 void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
1888 {
1889     RenderMathMLToken::paint(info, paintOffset);
1890
1891     if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || m_stretchyData.mode() == DrawNormal)
1892         return;
1893
1894     GraphicsContextStateSaver stateSaver(*info.context);
1895     info.context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace());
1896
1897     if (m_stretchyData.mode() == DrawSizeVariant) {
1898         ASSERT(m_stretchyData.variant().glyph);
1899         GlyphBuffer buffer;
1900         buffer.add(m_stretchyData.variant().glyph, m_stretchyData.variant().fontData, advanceForGlyph(m_stretchyData.variant()));
1901         LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location());
1902         FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
1903         LayoutPoint operatorOrigin(operatorTopLeft.x(), operatorTopLeft.y() - glyphBounds.y());
1904         info.context->drawGlyphs(style().font(), *m_stretchyData.variant().fontData, buffer, 0, 1, operatorOrigin);
1905         return;
1906     }
1907
1908     if (m_isVertical)
1909         paintVerticalGlyphAssembly(info, paintOffset);
1910     else
1911         paintHorizontalGlyphAssembly(info, paintOffset);
1912 }
1913
1914 void RenderMathMLOperator::paintVerticalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1915 {
1916     ASSERT(m_isVertical);
1917     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1918     ASSERT(m_stretchyData.top().glyph);
1919     ASSERT(m_stretchyData.bottom().glyph);
1920
1921     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
1922     LayoutPoint operatorTopLeft = paintOffset + location();
1923     operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
1924     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
1925     FloatRect topGlyphBounds = boundsForGlyph(m_stretchyData.top());
1926     LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
1927     LayoutRect topGlyphPaintRect = paintGlyph(info, m_stretchyData.top(), topGlyphOrigin, TrimBottom);
1928
1929     FloatRect bottomGlyphBounds = boundsForGlyph(m_stretchyData.bottom());
1930     LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
1931     LayoutRect bottomGlyphPaintRect = paintGlyph(info, m_stretchyData.bottom(), bottomGlyphOrigin, TrimTop);
1932
1933     if (m_stretchyData.middle().glyph) {
1934         // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
1935         FloatRect middleGlyphBounds = boundsForGlyph(m_stretchyData.middle());
1936         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
1937         middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
1938         middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
1939
1940         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimTopAndBottom);
1941         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
1942         fillWithVerticalExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1943     } else
1944         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1945 }
1946
1947 void RenderMathMLOperator::paintHorizontalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1948 {
1949     ASSERT(!m_isVertical);
1950     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1951     ASSERT(m_stretchyData.left().glyph);
1952     ASSERT(m_stretchyData.right().glyph);
1953
1954     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
1955     LayoutPoint operatorTopLeft = paintOffset + location();
1956     operatorTopLeft.move(m_leadingSpace, 0);
1957     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
1958     LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
1959     LayoutRect leftGlyphPaintRect = paintGlyph(info, m_stretchyData.left(), leftGlyphOrigin, TrimRight);
1960
1961     FloatRect rightGlyphBounds = boundsForGlyph(m_stretchyData.right());
1962     LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + offsetWidth() - rightGlyphBounds.width(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
1963     LayoutRect rightGlyphPaintRect = paintGlyph(info, m_stretchyData.right(), rightGlyphOrigin, TrimLeft);
1964
1965     if (m_stretchyData.middle().glyph) {
1966         // Center the glyph origin between the start and end glyph paint extents.
1967         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), leftGlyphOrigin.y());
1968         middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
1969         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimLeftAndRight);
1970         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), middleGlyphPaintRect.minXMinYCorner());
1971         fillWithHorizontalExtensionGlyph(info, middleGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
1972     } else
1973         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
1974 }
1975
1976 void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
1977 {
1978     // We skip painting for invisible operators too to avoid some "missing character" glyph to appear if appropriate math fonts are not available.
1979     if (m_stretchyData.mode() != DrawNormal || isInvisibleOperator())
1980         return;
1981     RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
1982 }
1983
1984 LayoutUnit RenderMathMLOperator::trailingSpaceError()
1985 {
1986     const auto& primaryFontData = style().font().primaryFont();
1987     if (!primaryFontData || !primaryFontData->mathData())
1988         return 0;
1989
1990     // For OpenType MATH font, the layout is based on RenderMathOperator for which the preferred width is sometimes overestimated (bug https://bugs.webkit.org/show_bug.cgi?id=130326).
1991     // Hence we determine the error in the logical width with respect to the actual width of the glyph(s) used to paint the operator.
1992     LayoutUnit width = logicalWidth();
1993
1994     if (m_stretchyData.mode() == DrawNormal) {
1995         GlyphData data = style().font().glyphDataForCharacter(textContent(), !style().isLeftToRightDirection());
1996         return width - advanceForGlyph(data);
1997     }
1998
1999     if (m_stretchyData.mode() == DrawSizeVariant)
2000         return width - advanceForGlyph(m_stretchyData.variant());
2001
2002     float assemblyWidth = advanceForGlyph(m_stretchyData.top());
2003     assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.bottom()));
2004     assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.extension()));
2005     if (m_stretchyData.middle().glyph)
2006         assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.middle()));
2007     return width - assemblyWidth;
2008 }
2009
2010 }
2011
2012 #endif