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