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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
28 #define _USE_MATH_DEFINES 1
33 #include "RenderMathMLOperator.h"
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>
48 using namespace MathMLNames;
50 // FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297).
51 struct StretchyCharacter {
58 // The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
59 static const short leftRightPairsCount = 5;
60 static const StretchyCharacter stretchyCharacters[14] = {
61 { 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, // left parenthesis
62 { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, // right parenthesis
63 { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, // left square bracket
64 { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, // right square bracket
65 { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
66 { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
67 { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, // left ceiling
68 { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, // right ceiling
69 { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, // left floor
70 { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, // right floor
71 { 0x7c , 0x7c, 0x7c, 0x7c, 0x0 }, // vertical bar
72 { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, // double vertical line
73 { 0x2225, 0x2225, 0x2225, 0x2225, 0x0 }, // parallel to
74 { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } // integral sign
77 namespace MathMLOperatorDictionary {
79 typedef std::pair<UChar, Form> Key;
80 inline Key ExtractKey(const Entry* entry) { return Key(entry->character, entry->form); }
81 inline UChar ExtractChar(const Entry* entry) { return entry->character; }
83 // This table has been automatically generated from the Operator Dictionary of the MathML3 specification (appendix C).
84 // Some people use the binary operator "U+2225 PARALLEL TO" as an opening and closing delimiter, so we add the corresponding stretchy prefix and postfix forms.
85 #define MATHML_OPDICT_SIZE 1041
86 static const Entry dictionary[MATHML_OPDICT_SIZE] = {
87 { 0x21, Postfix, 1, 0, 0}, // EXCLAMATION MARK
88 { 0x25, Infix, 3, 3, 0}, // PERCENT SIGN
89 { 0x26, Postfix, 0, 0, 0}, // AMPERSAND
90 { 0x27, Postfix, 0, 0, Accent}, // APOSTROPHE
91 { 0x28, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT PARENTHESIS
92 { 0x29, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT PARENTHESIS
93 { 0x2A, Infix, 3, 3, 0}, // ASTERISK
94 { 0x2B, Infix, 4, 4, 0}, // PLUS SIGN
95 { 0x2B, Prefix, 0, 1, 0}, // PLUS SIGN
96 { 0x2C, Infix, 0, 3, Separator}, // COMMA
97 { 0x2D, Infix, 4, 4, 0}, // HYPHEN-MINUS
98 { 0x2D, Prefix, 0, 1, 0}, // HYPHEN-MINUS
99 { 0x2E, Infix, 3, 3, 0}, // FULL STOP
100 { 0x2F, Infix, 1, 1, 0}, // SOLIDUS
101 { 0x3A, Infix, 1, 2, 0}, // COLON
102 { 0x3B, Infix, 0, 3, Separator}, // SEMICOLON
103 { 0x3C, Infix, 5, 5, 0}, // LESS-THAN SIGN
104 { 0x3D, Infix, 5, 5, 0}, // EQUALS SIGN
105 { 0x3E, Infix, 5, 5, 0}, // GREATER-THAN SIGN
106 { 0x3F, Infix, 1, 1, 0}, // QUESTION MARK
107 { 0x40, Infix, 1, 1, 0}, // COMMERCIAL AT
108 { 0x5B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET
109 { 0x5C, Infix, 0, 0, 0}, // REVERSE SOLIDUS
110 { 0x5D, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET
111 { 0x5E, Postfix, 0, 0, Accent | Stretchy}, // CIRCUMFLEX ACCENT
112 { 0x5E, Infix, 1, 1, 0}, // CIRCUMFLEX ACCENT
113 { 0x5F, Postfix, 0, 0, Accent | Stretchy}, // LOW LINE
114 { 0x5F, Infix, 1, 1, 0}, // LOW LINE
115 { 0x60, Postfix, 0, 0, Accent}, // GRAVE ACCENT
116 { 0x7B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT CURLY BRACKET
117 { 0x7C, Infix, 2, 2, Stretchy | Symmetric | Fence}, // VERTICAL LINE
118 { 0x7C, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // VERTICAL LINE
119 { 0x7C, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // VERTICAL LINE
120 { 0x7D, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT CURLY BRACKET
121 { 0x7E, Postfix, 0, 0, Accent | Stretchy}, // TILDE
122 { 0xA8, Postfix, 0, 0, Accent}, // DIAERESIS
123 { 0xAC, Prefix, 2, 1, 0}, // NOT SIGN
124 { 0xAF, Postfix, 0, 0, Accent | Stretchy}, // MACRON
125 { 0xB0, Postfix, 0, 0, 0}, // DEGREE SIGN
126 { 0xB1, Infix, 4, 4, 0}, // PLUS-MINUS SIGN
127 { 0xB1, Prefix, 0, 1, 0}, // PLUS-MINUS SIGN
128 { 0xB4, Postfix, 0, 0, Accent}, // ACUTE ACCENT
129 { 0xB7, Infix, 4, 4, 0}, // MIDDLE DOT
130 { 0xB8, Postfix, 0, 0, Accent}, // CEDILLA
131 { 0xD7, Infix, 4, 4, 0}, // MULTIPLICATION SIGN
132 { 0xF7, Infix, 4, 4, 0}, // DIVISION SIGN
133 { 0x2C6, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER CIRCUMFLEX ACCENT
134 { 0x2C7, Postfix, 0, 0, Accent | Stretchy}, // CARON
135 { 0x2C9, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER MACRON
136 { 0x2CA, Postfix, 0, 0, Accent}, // MODIFIER LETTER ACUTE ACCENT
137 { 0x2CB, Postfix, 0, 0, Accent}, // MODIFIER LETTER GRAVE ACCENT
138 { 0x2CD, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER LOW MACRON
139 { 0x2D8, Postfix, 0, 0, Accent}, // BREVE
140 { 0x2D9, Postfix, 0, 0, Accent}, // DOT ABOVE
141 { 0x2DA, Postfix, 0, 0, Accent}, // RING ABOVE
142 { 0x2DC, Postfix, 0, 0, Accent | Stretchy}, // SMALL TILDE
143 { 0x2DD, Postfix, 0, 0, Accent}, // DOUBLE ACUTE ACCENT
144 { 0x2F7, Postfix, 0, 0, Accent | Stretchy}, // MODIFIER LETTER LOW TILDE
145 { 0x302, Postfix, 0, 0, Accent | Stretchy}, // COMBINING CIRCUMFLEX ACCENT
146 { 0x311, Postfix, 0, 0, Accent}, // COMBINING INVERTED BREVE
147 { 0x3F6, Infix, 5, 5, 0}, // GREEK REVERSED LUNATE EPSILON SYMBOL
148 { 0x2016, Prefix, 0, 0, Fence | Stretchy}, // DOUBLE VERTICAL LINE
149 { 0x2016, Postfix, 0, 0, Fence | Stretchy}, // DOUBLE VERTICAL LINE
150 { 0x2018, Prefix, 0, 0, Fence}, // LEFT SINGLE QUOTATION MARK
151 { 0x2019, Postfix, 0, 0, Fence}, // RIGHT SINGLE QUOTATION MARK
152 { 0x201C, Prefix, 0, 0, Fence}, // LEFT DOUBLE QUOTATION MARK
153 { 0x201D, Postfix, 0, 0, Fence}, // RIGHT DOUBLE QUOTATION MARK
154 { 0x2022, Infix, 4, 4, 0}, // BULLET
155 { 0x2026, Infix, 0, 0, 0}, // HORIZONTAL ELLIPSIS
156 { 0x2032, Postfix, 0, 2, 0}, // PRIME
157 { 0x203E, Postfix, 0, 0, Accent | Stretchy}, // OVERLINE
158 { 0x2044, Infix, 4, 4, Stretchy}, // FRACTION SLASH
159 { 0x2061, Infix, 0, 0, 0}, // FUNCTION APPLICATION
160 { 0x2062, Infix, 0, 0, 0}, // INVISIBLE TIMES
161 { 0x2063, Infix, 0, 0, Separator}, // INVISIBLE SEPARATOR
162 { 0x2064, Infix, 0, 0, 0}, // INVISIBLE PLUS
163 { 0x20DB, Postfix, 0, 0, Accent}, // COMBINING THREE DOTS ABOVE
164 { 0x20DC, Postfix, 0, 0, Accent}, // COMBINING FOUR DOTS ABOVE
165 { 0x2145, Prefix, 2, 1, 0}, // DOUBLE-STRUCK ITALIC CAPITAL D
166 { 0x2146, Prefix, 2, 0, 0}, // DOUBLE-STRUCK ITALIC SMALL D
167 { 0x2190, Infix, 5, 5, Accent | Stretchy}, // LEFTWARDS ARROW
168 { 0x2191, Infix, 5, 5, Stretchy}, // UPWARDS ARROW
169 { 0x2192, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW
170 { 0x2193, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW
171 { 0x2194, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT ARROW
172 { 0x2195, Infix, 5, 5, Stretchy}, // UP DOWN ARROW
173 { 0x2196, Infix, 5, 5, Stretchy}, // NORTH WEST ARROW
174 { 0x2197, Infix, 5, 5, Stretchy}, // NORTH EAST ARROW
175 { 0x2198, Infix, 5, 5, Stretchy}, // SOUTH EAST ARROW
176 { 0x2199, Infix, 5, 5, Stretchy}, // SOUTH WEST ARROW
177 { 0x219A, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH STROKE
178 { 0x219B, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH STROKE
179 { 0x219C, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS WAVE ARROW
180 { 0x219D, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WAVE ARROW
181 { 0x219E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TWO HEADED ARROW
182 { 0x219F, Infix, 5, 5, Stretchy | Accent}, // UPWARDS TWO HEADED ARROW
183 { 0x21A0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TWO HEADED ARROW
184 { 0x21A1, Infix, 5, 5, Stretchy}, // DOWNWARDS TWO HEADED ARROW
185 { 0x21A2, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH TAIL
186 { 0x21A3, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH TAIL
187 { 0x21A4, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW FROM BAR
188 { 0x21A5, Infix, 5, 5, Stretchy}, // UPWARDS ARROW FROM BAR
189 { 0x21A6, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW FROM BAR
190 { 0x21A7, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW FROM BAR
191 { 0x21A8, Infix, 5, 5, Stretchy}, // UP DOWN ARROW WITH BASE
192 { 0x21A9, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH HOOK
193 { 0x21AA, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH HOOK
194 { 0x21AB, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW WITH LOOP
195 { 0x21AC, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW WITH LOOP
196 { 0x21AD, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT WAVE ARROW
197 { 0x21AE, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH STROKE
198 { 0x21AF, Infix, 5, 5, Stretchy}, // DOWNWARDS ZIGZAG ARROW
199 { 0x21B0, Infix, 5, 5, Stretchy}, // UPWARDS ARROW WITH TIP LEFTWARDS
200 { 0x21B1, Infix, 5, 5, Stretchy}, // UPWARDS ARROW WITH TIP RIGHTWARDS
201 { 0x21B2, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH TIP LEFTWARDS
202 { 0x21B3, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH TIP RIGHTWARDS
203 { 0x21B4, Infix, 5, 5, Stretchy}, // RIGHTWARDS ARROW WITH CORNER DOWNWARDS
204 { 0x21B5, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW WITH CORNER LEFTWARDS
205 { 0x21B6, Infix, 5, 5, Accent}, // ANTICLOCKWISE TOP SEMICIRCLE ARROW
206 { 0x21B7, Infix, 5, 5, Accent}, // CLOCKWISE TOP SEMICIRCLE ARROW
207 { 0x21B8, Infix, 5, 5, 0}, // NORTH WEST ARROW TO LONG BAR
208 { 0x21B9, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR
209 { 0x21BA, Infix, 5, 5, 0}, // ANTICLOCKWISE OPEN CIRCLE ARROW
210 { 0x21BB, Infix, 5, 5, 0}, // CLOCKWISE OPEN CIRCLE ARROW
211 { 0x21BC, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UPWARDS
212 { 0x21BD, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB DOWNWARDS
213 { 0x21BE, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHTWARDS
214 { 0x21BF, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFTWARDS
215 { 0x21C0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UPWARDS
216 { 0x21C1, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
217 { 0x21C2, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
218 { 0x21C3, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFTWARDS
219 { 0x21C4, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
220 { 0x21C5, Infix, 5, 5, Stretchy}, // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
221 { 0x21C6, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
222 { 0x21C7, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS PAIRED ARROWS
223 { 0x21C8, Infix, 5, 5, Stretchy}, // UPWARDS PAIRED ARROWS
224 { 0x21C9, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS PAIRED ARROWS
225 { 0x21CA, Infix, 5, 5, Stretchy}, // DOWNWARDS PAIRED ARROWS
226 { 0x21CB, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
227 { 0x21CC, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
228 { 0x21CD, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW WITH STROKE
229 { 0x21CE, Infix, 5, 5, Accent}, // LEFT RIGHT DOUBLE ARROW WITH STROKE
230 { 0x21CF, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW WITH STROKE
231 { 0x21D0, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DOUBLE ARROW
232 { 0x21D1, Infix, 5, 5, Stretchy}, // UPWARDS DOUBLE ARROW
233 { 0x21D2, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DOUBLE ARROW
234 { 0x21D3, Infix, 5, 5, Stretchy}, // DOWNWARDS DOUBLE ARROW
235 { 0x21D4, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT DOUBLE ARROW
236 { 0x21D5, Infix, 5, 5, Stretchy}, // UP DOWN DOUBLE ARROW
237 { 0x21D6, Infix, 5, 5, Stretchy}, // NORTH WEST DOUBLE ARROW
238 { 0x21D7, Infix, 5, 5, Stretchy}, // NORTH EAST DOUBLE ARROW
239 { 0x21D8, Infix, 5, 5, Stretchy}, // SOUTH EAST DOUBLE ARROW
240 { 0x21D9, Infix, 5, 5, Stretchy}, // SOUTH WEST DOUBLE ARROW
241 { 0x21DA, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TRIPLE ARROW
242 { 0x21DB, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TRIPLE ARROW
243 { 0x21DC, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS SQUIGGLE ARROW
244 { 0x21DD, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS SQUIGGLE ARROW
245 { 0x21DE, Infix, 5, 5, 0}, // UPWARDS ARROW WITH DOUBLE STROKE
246 { 0x21DF, Infix, 5, 5, 0}, // DOWNWARDS ARROW WITH DOUBLE STROKE
247 { 0x21E0, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DASHED ARROW
248 { 0x21E1, Infix, 5, 5, Stretchy}, // UPWARDS DASHED ARROW
249 { 0x21E2, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DASHED ARROW
250 { 0x21E3, Infix, 5, 5, Stretchy}, // DOWNWARDS DASHED ARROW
251 { 0x21E4, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS ARROW TO BAR
252 { 0x21E5, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS ARROW TO BAR
253 { 0x21E6, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS WHITE ARROW
254 { 0x21E7, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW
255 { 0x21E8, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WHITE ARROW
256 { 0x21E9, Infix, 5, 5, Stretchy}, // DOWNWARDS WHITE ARROW
257 { 0x21EA, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW FROM BAR
258 { 0x21EB, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL
259 { 0x21EC, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR
260 { 0x21ED, Infix, 5, 5, Stretchy}, // UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR
261 { 0x21EE, Infix, 5, 5, Stretchy}, // UPWARDS WHITE DOUBLE ARROW
262 { 0x21EF, Infix, 5, 5, Stretchy}, // UPWARDS WHITE DOUBLE ARROW ON PEDESTAL
263 { 0x21F0, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS WHITE ARROW FROM WALL
264 { 0x21F1, Infix, 5, 5, 0}, // NORTH WEST ARROW TO CORNER
265 { 0x21F2, Infix, 5, 5, 0}, // SOUTH EAST ARROW TO CORNER
266 { 0x21F3, Infix, 5, 5, Stretchy}, // UP DOWN WHITE ARROW
267 { 0x21F4, Infix, 5, 5, Accent}, // RIGHT ARROW WITH SMALL CIRCLE
268 { 0x21F5, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
269 { 0x21F6, Infix, 5, 5, Stretchy | Accent}, // THREE RIGHTWARDS ARROWS
270 { 0x21F7, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH VERTICAL STROKE
271 { 0x21F8, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH VERTICAL STROKE
272 { 0x21F9, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH VERTICAL STROKE
273 { 0x21FA, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE
274 { 0x21FB, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE
275 { 0x21FC, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE
276 { 0x21FD, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS OPEN-HEADED ARROW
277 { 0x21FE, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS OPEN-HEADED ARROW
278 { 0x21FF, Infix, 5, 5, Stretchy | Accent}, // LEFT RIGHT OPEN-HEADED ARROW
279 { 0x2200, Prefix, 2, 1, 0}, // FOR ALL
280 { 0x2201, Infix, 1, 2, 0}, // COMPLEMENT
281 { 0x2202, Prefix, 2, 1, 0}, // PARTIAL DIFFERENTIAL
282 { 0x2203, Prefix, 2, 1, 0}, // THERE EXISTS
283 { 0x2204, Prefix, 2, 1, 0}, // THERE DOES NOT EXIST
284 { 0x2206, Infix, 3, 3, 0}, // INCREMENT
285 { 0x2207, Prefix, 2, 1, 0}, // NABLA
286 { 0x2208, Infix, 5, 5, 0}, // ELEMENT OF
287 { 0x2209, Infix, 5, 5, 0}, // NOT AN ELEMENT OF
288 { 0x220A, Infix, 5, 5, 0}, // SMALL ELEMENT OF
289 { 0x220B, Infix, 5, 5, 0}, // CONTAINS AS MEMBER
290 { 0x220C, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS MEMBER
291 { 0x220D, Infix, 5, 5, 0}, // SMALL CONTAINS AS MEMBER
292 { 0x220E, Infix, 3, 3, 0}, // END OF PROOF
293 { 0x220F, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY PRODUCT
294 { 0x2210, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY COPRODUCT
295 { 0x2211, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SUMMATION
296 { 0x2212, Infix, 4, 4, 0}, // MINUS SIGN
297 { 0x2212, Prefix, 0, 1, 0}, // MINUS SIGN
298 { 0x2213, Infix, 4, 4, 0}, // MINUS-OR-PLUS SIGN
299 { 0x2213, Prefix, 0, 1, 0}, // MINUS-OR-PLUS SIGN
300 { 0x2214, Infix, 4, 4, 0}, // DOT PLUS
301 { 0x2215, Infix, 4, 4, Stretchy}, // DIVISION SLASH
302 { 0x2216, Infix, 4, 4, 0}, // SET MINUS
303 { 0x2217, Infix, 4, 4, 0}, // ASTERISK OPERATOR
304 { 0x2218, Infix, 4, 4, 0}, // RING OPERATOR
305 { 0x2219, Infix, 4, 4, 0}, // BULLET OPERATOR
306 { 0x221A, Prefix, 1, 1, Stretchy}, // SQUARE ROOT
307 { 0x221B, Prefix, 1, 1, 0}, // CUBE ROOT
308 { 0x221C, Prefix, 1, 1, 0}, // FOURTH ROOT
309 { 0x221D, Infix, 5, 5, 0}, // PROPORTIONAL TO
310 { 0x221F, Infix, 5, 5, 0}, // RIGHT ANGLE
311 { 0x2220, Prefix, 0, 0, 0}, // ANGLE
312 { 0x2221, Prefix, 0, 0, 0}, // MEASURED ANGLE
313 { 0x2222, Prefix, 0, 0, 0}, // SPHERICAL ANGLE
314 { 0x2223, Infix, 5, 5, 0}, // DIVIDES
315 { 0x2224, Infix, 5, 5, 0}, // DOES NOT DIVIDE
316 { 0x2225, Infix, 5, 5, 0}, // PARALLEL TO
317 { 0x2225, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // PARALLEL TO
318 { 0x2225, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // PARALLEL TO
319 { 0x2226, Infix, 5, 5, 0}, // NOT PARALLEL TO
320 { 0x2227, Infix, 4, 4, 0}, // LOGICAL AND
321 { 0x2228, Infix, 4, 4, 0}, // LOGICAL OR
322 { 0x2229, Infix, 4, 4, 0}, // INTERSECTION
323 { 0x222A, Infix, 4, 4, 0}, // UNION
324 { 0x222B, Prefix, 0, 1, Symmetric | LargeOp}, // INTEGRAL
325 { 0x222C, Prefix, 0, 1, Symmetric | LargeOp}, // DOUBLE INTEGRAL
326 { 0x222D, Prefix, 0, 1, Symmetric | LargeOp}, // TRIPLE INTEGRAL
327 { 0x222E, Prefix, 0, 1, Symmetric | LargeOp}, // CONTOUR INTEGRAL
328 { 0x222F, Prefix, 0, 1, Symmetric | LargeOp}, // SURFACE INTEGRAL
329 { 0x2230, Prefix, 0, 1, Symmetric | LargeOp}, // VOLUME INTEGRAL
330 { 0x2231, Prefix, 0, 1, Symmetric | LargeOp}, // CLOCKWISE INTEGRAL
331 { 0x2232, Prefix, 0, 1, Symmetric | LargeOp}, // CLOCKWISE CONTOUR INTEGRAL
332 { 0x2233, Prefix, 0, 1, Symmetric | LargeOp}, // ANTICLOCKWISE CONTOUR INTEGRAL
333 { 0x2234, Infix, 5, 5, 0}, // THEREFORE
334 { 0x2235, Infix, 5, 5, 0}, // BECAUSE
335 { 0x2236, Infix, 5, 5, 0}, // RATIO
336 { 0x2237, Infix, 5, 5, 0}, // PROPORTION
337 { 0x2238, Infix, 4, 4, 0}, // DOT MINUS
338 { 0x2239, Infix, 5, 5, 0}, // EXCESS
339 { 0x223A, Infix, 4, 4, 0}, // GEOMETRIC PROPORTION
340 { 0x223B, Infix, 5, 5, 0}, // HOMOTHETIC
341 { 0x223C, Infix, 5, 5, 0}, // TILDE OPERATOR
342 { 0x223D, Infix, 5, 5, 0}, // REVERSED TILDE
343 { 0x223E, Infix, 5, 5, 0}, // INVERTED LAZY S
344 { 0x223F, Infix, 3, 3, 0}, // SINE WAVE
345 { 0x2240, Infix, 4, 4, 0}, // WREATH PRODUCT
346 { 0x2241, Infix, 5, 5, 0}, // NOT TILDE
347 { 0x2242, Infix, 5, 5, 0}, // MINUS TILDE
348 { 0x2243, Infix, 5, 5, 0}, // ASYMPTOTICALLY EQUAL TO
349 { 0x2244, Infix, 5, 5, 0}, // NOT ASYMPTOTICALLY EQUAL TO
350 { 0x2245, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL TO
351 { 0x2246, Infix, 5, 5, 0}, // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
352 { 0x2247, Infix, 5, 5, 0}, // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
353 { 0x2248, Infix, 5, 5, 0}, // ALMOST EQUAL TO
354 { 0x2249, Infix, 5, 5, 0}, // NOT ALMOST EQUAL TO
355 { 0x224A, Infix, 5, 5, 0}, // ALMOST EQUAL OR EQUAL TO
356 { 0x224B, Infix, 5, 5, 0}, // TRIPLE TILDE
357 { 0x224C, Infix, 5, 5, 0}, // ALL EQUAL TO
358 { 0x224D, Infix, 5, 5, 0}, // EQUIVALENT TO
359 { 0x224E, Infix, 5, 5, 0}, // GEOMETRICALLY EQUIVALENT TO
360 { 0x224F, Infix, 5, 5, 0}, // DIFFERENCE BETWEEN
361 { 0x2250, Infix, 5, 5, 0}, // APPROACHES THE LIMIT
362 { 0x2251, Infix, 5, 5, 0}, // GEOMETRICALLY EQUAL TO
363 { 0x2252, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL TO OR THE IMAGE OF
364 { 0x2253, Infix, 5, 5, 0}, // IMAGE OF OR APPROXIMATELY EQUAL TO
365 { 0x2254, Infix, 5, 5, 0}, // COLON EQUALS
366 { 0x2255, Infix, 5, 5, 0}, // EQUALS COLON
367 { 0x2256, Infix, 5, 5, 0}, // RING IN EQUAL TO
368 { 0x2257, Infix, 5, 5, 0}, // RING EQUAL TO
369 { 0x2258, Infix, 5, 5, 0}, // CORRESPONDS TO
370 { 0x2259, Infix, 5, 5, 0}, // ESTIMATES
371 { 0x225A, Infix, 5, 5, 0}, // EQUIANGULAR TO
372 { 0x225C, Infix, 5, 5, 0}, // DELTA EQUAL TO
373 { 0x225D, Infix, 5, 5, 0}, // EQUAL TO BY DEFINITION
374 { 0x225E, Infix, 5, 5, 0}, // MEASURED BY
375 { 0x225F, Infix, 5, 5, 0}, // QUESTIONED EQUAL TO
376 { 0x2260, Infix, 5, 5, 0}, // NOT EQUAL TO
377 { 0x2261, Infix, 5, 5, 0}, // IDENTICAL TO
378 { 0x2262, Infix, 5, 5, 0}, // NOT IDENTICAL TO
379 { 0x2263, Infix, 5, 5, 0}, // STRICTLY EQUIVALENT TO
380 { 0x2264, Infix, 5, 5, 0}, // LESS-THAN OR EQUAL TO
381 { 0x2265, Infix, 5, 5, 0}, // GREATER-THAN OR EQUAL TO
382 { 0x2266, Infix, 5, 5, 0}, // LESS-THAN OVER EQUAL TO
383 { 0x2267, Infix, 5, 5, 0}, // GREATER-THAN OVER EQUAL TO
384 { 0x2268, Infix, 5, 5, 0}, // LESS-THAN BUT NOT EQUAL TO
385 { 0x2269, Infix, 5, 5, 0}, // GREATER-THAN BUT NOT EQUAL TO
386 { 0x226A, Infix, 5, 5, 0}, // MUCH LESS-THAN
387 { 0x226B, Infix, 5, 5, 0}, // MUCH GREATER-THAN
388 { 0x226C, Infix, 5, 5, 0}, // BETWEEN
389 { 0x226D, Infix, 5, 5, 0}, // NOT EQUIVALENT TO
390 { 0x226E, Infix, 5, 5, 0}, // NOT LESS-THAN
391 { 0x226F, Infix, 5, 5, 0}, // NOT GREATER-THAN
392 { 0x2270, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR EQUAL TO
393 { 0x2271, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR EQUAL TO
394 { 0x2272, Infix, 5, 5, 0}, // LESS-THAN OR EQUIVALENT TO
395 { 0x2273, Infix, 5, 5, 0}, // GREATER-THAN OR EQUIVALENT TO
396 { 0x2274, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR EQUIVALENT TO
397 { 0x2275, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR EQUIVALENT TO
398 { 0x2276, Infix, 5, 5, 0}, // LESS-THAN OR GREATER-THAN
399 { 0x2277, Infix, 5, 5, 0}, // GREATER-THAN OR LESS-THAN
400 { 0x2278, Infix, 5, 5, 0}, // NEITHER LESS-THAN NOR GREATER-THAN
401 { 0x2279, Infix, 5, 5, 0}, // NEITHER GREATER-THAN NOR LESS-THAN
402 { 0x227A, Infix, 5, 5, 0}, // PRECEDES
403 { 0x227B, Infix, 5, 5, 0}, // SUCCEEDS
404 { 0x227C, Infix, 5, 5, 0}, // PRECEDES OR EQUAL TO
405 { 0x227D, Infix, 5, 5, 0}, // SUCCEEDS OR EQUAL TO
406 { 0x227E, Infix, 5, 5, 0}, // PRECEDES OR EQUIVALENT TO
407 { 0x227F, Infix, 5, 5, 0}, // SUCCEEDS OR EQUIVALENT TO
408 { 0x2280, Infix, 5, 5, 0}, // DOES NOT PRECEDE
409 { 0x2281, Infix, 5, 5, 0}, // DOES NOT SUCCEED
410 { 0x2282, Infix, 5, 5, 0}, // SUBSET OF
411 { 0x2283, Infix, 5, 5, 0}, // SUPERSET OF
412 { 0x2284, Infix, 5, 5, 0}, // NOT A SUBSET OF
413 { 0x2285, Infix, 5, 5, 0}, // NOT A SUPERSET OF
414 { 0x2286, Infix, 5, 5, 0}, // SUBSET OF OR EQUAL TO
415 { 0x2287, Infix, 5, 5, 0}, // SUPERSET OF OR EQUAL TO
416 { 0x2288, Infix, 5, 5, 0}, // NEITHER A SUBSET OF NOR EQUAL TO
417 { 0x2289, Infix, 5, 5, 0}, // NEITHER A SUPERSET OF NOR EQUAL TO
418 { 0x228A, Infix, 5, 5, 0}, // SUBSET OF WITH NOT EQUAL TO
419 { 0x228B, Infix, 5, 5, 0}, // SUPERSET OF WITH NOT EQUAL TO
420 { 0x228C, Infix, 4, 4, 0}, // MULTISET
421 { 0x228D, Infix, 4, 4, 0}, // MULTISET MULTIPLICATION
422 { 0x228E, Infix, 4, 4, 0}, // MULTISET UNION
423 { 0x228F, Infix, 5, 5, 0}, // SQUARE IMAGE OF
424 { 0x2290, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF
425 { 0x2291, Infix, 5, 5, 0}, // SQUARE IMAGE OF OR EQUAL TO
426 { 0x2292, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF OR EQUAL TO
427 { 0x2293, Infix, 4, 4, 0}, // SQUARE CAP
428 { 0x2294, Infix, 4, 4, 0}, // SQUARE CUP
429 { 0x2295, Infix, 4, 4, 0}, // CIRCLED PLUS
430 { 0x2296, Infix, 4, 4, 0}, // CIRCLED MINUS
431 { 0x2297, Infix, 4, 4, 0}, // CIRCLED TIMES
432 { 0x2298, Infix, 4, 4, 0}, // CIRCLED DIVISION SLASH
433 { 0x2299, Infix, 4, 4, 0}, // CIRCLED DOT OPERATOR
434 { 0x229A, Infix, 4, 4, 0}, // CIRCLED RING OPERATOR
435 { 0x229B, Infix, 4, 4, 0}, // CIRCLED ASTERISK OPERATOR
436 { 0x229C, Infix, 4, 4, 0}, // CIRCLED EQUALS
437 { 0x229D, Infix, 4, 4, 0}, // CIRCLED DASH
438 { 0x229E, Infix, 4, 4, 0}, // SQUARED PLUS
439 { 0x229F, Infix, 4, 4, 0}, // SQUARED MINUS
440 { 0x22A0, Infix, 4, 4, 0}, // SQUARED TIMES
441 { 0x22A1, Infix, 4, 4, 0}, // SQUARED DOT OPERATOR
442 { 0x22A2, Infix, 5, 5, 0}, // RIGHT TACK
443 { 0x22A3, Infix, 5, 5, 0}, // LEFT TACK
444 { 0x22A4, Infix, 5, 5, 0}, // DOWN TACK
445 { 0x22A5, Infix, 5, 5, 0}, // UP TACK
446 { 0x22A6, Infix, 5, 5, 0}, // ASSERTION
447 { 0x22A7, Infix, 5, 5, 0}, // MODELS
448 { 0x22A8, Infix, 5, 5, 0}, // TRUE
449 { 0x22A9, Infix, 5, 5, 0}, // FORCES
450 { 0x22AA, Infix, 5, 5, 0}, // TRIPLE VERTICAL BAR RIGHT TURNSTILE
451 { 0x22AB, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
452 { 0x22AC, Infix, 5, 5, 0}, // DOES NOT PROVE
453 { 0x22AD, Infix, 5, 5, 0}, // NOT TRUE
454 { 0x22AE, Infix, 5, 5, 0}, // DOES NOT FORCE
455 { 0x22AF, Infix, 5, 5, 0}, // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
456 { 0x22B0, Infix, 5, 5, 0}, // PRECEDES UNDER RELATION
457 { 0x22B1, Infix, 5, 5, 0}, // SUCCEEDS UNDER RELATION
458 { 0x22B2, Infix, 5, 5, 0}, // NORMAL SUBGROUP OF
459 { 0x22B3, Infix, 5, 5, 0}, // CONTAINS AS NORMAL SUBGROUP
460 { 0x22B4, Infix, 5, 5, 0}, // NORMAL SUBGROUP OF OR EQUAL TO
461 { 0x22B5, Infix, 5, 5, 0}, // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
462 { 0x22B6, Infix, 5, 5, 0}, // ORIGINAL OF
463 { 0x22B7, Infix, 5, 5, 0}, // IMAGE OF
464 { 0x22B8, Infix, 5, 5, 0}, // MULTIMAP
465 { 0x22B9, Infix, 5, 5, 0}, // HERMITIAN CONJUGATE MATRIX
466 { 0x22BA, Infix, 4, 4, 0}, // INTERCALATE
467 { 0x22BB, Infix, 4, 4, 0}, // XOR
468 { 0x22BC, Infix, 4, 4, 0}, // NAND
469 { 0x22BD, Infix, 4, 4, 0}, // NOR
470 { 0x22BE, Infix, 3, 3, 0}, // RIGHT ANGLE WITH ARC
471 { 0x22BF, Infix, 3, 3, 0}, // RIGHT TRIANGLE
472 { 0x22C0, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY LOGICAL AND
473 { 0x22C1, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY LOGICAL OR
474 { 0x22C2, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY INTERSECTION
475 { 0x22C3, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION
476 { 0x22C4, Infix, 4, 4, 0}, // DIAMOND OPERATOR
477 { 0x22C5, Infix, 4, 4, 0}, // DOT OPERATOR
478 { 0x22C6, Infix, 4, 4, 0}, // STAR OPERATOR
479 { 0x22C7, Infix, 4, 4, 0}, // DIVISION TIMES
480 { 0x22C8, Infix, 5, 5, 0}, // BOWTIE
481 { 0x22C9, Infix, 4, 4, 0}, // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
482 { 0x22CA, Infix, 4, 4, 0}, // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
483 { 0x22CB, Infix, 4, 4, 0}, // LEFT SEMIDIRECT PRODUCT
484 { 0x22CC, Infix, 4, 4, 0}, // RIGHT SEMIDIRECT PRODUCT
485 { 0x22CD, Infix, 5, 5, 0}, // REVERSED TILDE EQUALS
486 { 0x22CE, Infix, 4, 4, 0}, // CURLY LOGICAL OR
487 { 0x22CF, Infix, 4, 4, 0}, // CURLY LOGICAL AND
488 { 0x22D0, Infix, 5, 5, 0}, // DOUBLE SUBSET
489 { 0x22D1, Infix, 5, 5, 0}, // DOUBLE SUPERSET
490 { 0x22D2, Infix, 4, 4, 0}, // DOUBLE INTERSECTION
491 { 0x22D3, Infix, 4, 4, 0}, // DOUBLE UNION
492 { 0x22D4, Infix, 5, 5, 0}, // PITCHFORK
493 { 0x22D5, Infix, 5, 5, 0}, // EQUAL AND PARALLEL TO
494 { 0x22D6, Infix, 5, 5, 0}, // LESS-THAN WITH DOT
495 { 0x22D7, Infix, 5, 5, 0}, // GREATER-THAN WITH DOT
496 { 0x22D8, Infix, 5, 5, 0}, // VERY MUCH LESS-THAN
497 { 0x22D9, Infix, 5, 5, 0}, // VERY MUCH GREATER-THAN
498 { 0x22DA, Infix, 5, 5, 0}, // LESS-THAN EQUAL TO OR GREATER-THAN
499 { 0x22DB, Infix, 5, 5, 0}, // GREATER-THAN EQUAL TO OR LESS-THAN
500 { 0x22DC, Infix, 5, 5, 0}, // EQUAL TO OR LESS-THAN
501 { 0x22DD, Infix, 5, 5, 0}, // EQUAL TO OR GREATER-THAN
502 { 0x22DE, Infix, 5, 5, 0}, // EQUAL TO OR PRECEDES
503 { 0x22DF, Infix, 5, 5, 0}, // EQUAL TO OR SUCCEEDS
504 { 0x22E0, Infix, 5, 5, 0}, // DOES NOT PRECEDE OR EQUAL
505 { 0x22E1, Infix, 5, 5, 0}, // DOES NOT SUCCEED OR EQUAL
506 { 0x22E2, Infix, 5, 5, 0}, // NOT SQUARE IMAGE OF OR EQUAL TO
507 { 0x22E3, Infix, 5, 5, 0}, // NOT SQUARE ORIGINAL OF OR EQUAL TO
508 { 0x22E4, Infix, 5, 5, 0}, // SQUARE IMAGE OF OR NOT EQUAL TO
509 { 0x22E5, Infix, 5, 5, 0}, // SQUARE ORIGINAL OF OR NOT EQUAL TO
510 { 0x22E6, Infix, 5, 5, 0}, // LESS-THAN BUT NOT EQUIVALENT TO
511 { 0x22E7, Infix, 5, 5, 0}, // GREATER-THAN BUT NOT EQUIVALENT TO
512 { 0x22E8, Infix, 5, 5, 0}, // PRECEDES BUT NOT EQUIVALENT TO
513 { 0x22E9, Infix, 5, 5, 0}, // SUCCEEDS BUT NOT EQUIVALENT TO
514 { 0x22EA, Infix, 5, 5, 0}, // NOT NORMAL SUBGROUP OF
515 { 0x22EB, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS NORMAL SUBGROUP
516 { 0x22EC, Infix, 5, 5, 0}, // NOT NORMAL SUBGROUP OF OR EQUAL TO
517 { 0x22ED, Infix, 5, 5, 0}, // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
518 { 0x22EE, Infix, 5, 5, 0}, // VERTICAL ELLIPSIS
519 { 0x22EF, Infix, 0, 0, 0}, // MIDLINE HORIZONTAL ELLIPSIS
520 { 0x22F0, Infix, 5, 5, 0}, // UP RIGHT DIAGONAL ELLIPSIS
521 { 0x22F1, Infix, 5, 5, 0}, // DOWN RIGHT DIAGONAL ELLIPSIS
522 { 0x22F2, Infix, 5, 5, 0}, // ELEMENT OF WITH LONG HORIZONTAL STROKE
523 { 0x22F3, Infix, 5, 5, 0}, // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
524 { 0x22F4, Infix, 5, 5, 0}, // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
525 { 0x22F5, Infix, 5, 5, 0}, // ELEMENT OF WITH DOT ABOVE
526 { 0x22F6, Infix, 5, 5, 0}, // ELEMENT OF WITH OVERBAR
527 { 0x22F7, Infix, 5, 5, 0}, // SMALL ELEMENT OF WITH OVERBAR
528 { 0x22F8, Infix, 5, 5, 0}, // ELEMENT OF WITH UNDERBAR
529 { 0x22F9, Infix, 5, 5, 0}, // ELEMENT OF WITH TWO HORIZONTAL STROKES
530 { 0x22FA, Infix, 5, 5, 0}, // CONTAINS WITH LONG HORIZONTAL STROKE
531 { 0x22FB, Infix, 5, 5, 0}, // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
532 { 0x22FC, Infix, 5, 5, 0}, // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
533 { 0x22FD, Infix, 5, 5, 0}, // CONTAINS WITH OVERBAR
534 { 0x22FE, Infix, 5, 5, 0}, // SMALL CONTAINS WITH OVERBAR
535 { 0x22FF, Infix, 5, 5, 0}, // Z NOTATION BAG MEMBERSHIP
536 { 0x2308, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT CEILING
537 { 0x2309, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT CEILING
538 { 0x230A, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT FLOOR
539 { 0x230B, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT FLOOR
540 { 0x23B4, Postfix, 0, 0, Accent | Stretchy}, // TOP SQUARE BRACKET
541 { 0x23B5, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM SQUARE BRACKET
542 { 0x23DC, Postfix, 0, 0, Accent | Stretchy}, // TOP PARENTHESIS
543 { 0x23DD, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM PARENTHESIS
544 { 0x23DE, Postfix, 0, 0, Accent | Stretchy}, // TOP CURLY BRACKET
545 { 0x23DF, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM CURLY BRACKET
546 { 0x23E0, Postfix, 0, 0, Accent | Stretchy}, // TOP TORTOISE SHELL BRACKET
547 { 0x23E1, Postfix, 0, 0, Accent | Stretchy}, // BOTTOM TORTOISE SHELL BRACKET
548 { 0x25A0, Infix, 3, 3, 0}, // BLACK SQUARE
549 { 0x25A1, Infix, 3, 3, 0}, // WHITE SQUARE
550 { 0x25AA, Infix, 3, 3, 0}, // BLACK SMALL SQUARE
551 { 0x25AB, Infix, 3, 3, 0}, // WHITE SMALL SQUARE
552 { 0x25AD, Infix, 3, 3, 0}, // WHITE RECTANGLE
553 { 0x25AE, Infix, 3, 3, 0}, // BLACK VERTICAL RECTANGLE
554 { 0x25AF, Infix, 3, 3, 0}, // WHITE VERTICAL RECTANGLE
555 { 0x25B0, Infix, 3, 3, 0}, // BLACK PARALLELOGRAM
556 { 0x25B1, Infix, 3, 3, 0}, // WHITE PARALLELOGRAM
557 { 0x25B2, Infix, 4, 4, 0}, // BLACK UP-POINTING TRIANGLE
558 { 0x25B3, Infix, 4, 4, 0}, // WHITE UP-POINTING TRIANGLE
559 { 0x25B4, Infix, 4, 4, 0}, // BLACK UP-POINTING SMALL TRIANGLE
560 { 0x25B5, Infix, 4, 4, 0}, // WHITE UP-POINTING SMALL TRIANGLE
561 { 0x25B6, Infix, 4, 4, 0}, // BLACK RIGHT-POINTING TRIANGLE
562 { 0x25B7, Infix, 4, 4, 0}, // WHITE RIGHT-POINTING TRIANGLE
563 { 0x25B8, Infix, 4, 4, 0}, // BLACK RIGHT-POINTING SMALL TRIANGLE
564 { 0x25B9, Infix, 4, 4, 0}, // WHITE RIGHT-POINTING SMALL TRIANGLE
565 { 0x25BC, Infix, 4, 4, 0}, // BLACK DOWN-POINTING TRIANGLE
566 { 0x25BD, Infix, 4, 4, 0}, // WHITE DOWN-POINTING TRIANGLE
567 { 0x25BE, Infix, 4, 4, 0}, // BLACK DOWN-POINTING SMALL TRIANGLE
568 { 0x25BF, Infix, 4, 4, 0}, // WHITE DOWN-POINTING SMALL TRIANGLE
569 { 0x25C0, Infix, 4, 4, 0}, // BLACK LEFT-POINTING TRIANGLE
570 { 0x25C1, Infix, 4, 4, 0}, // WHITE LEFT-POINTING TRIANGLE
571 { 0x25C2, Infix, 4, 4, 0}, // BLACK LEFT-POINTING SMALL TRIANGLE
572 { 0x25C3, Infix, 4, 4, 0}, // WHITE LEFT-POINTING SMALL TRIANGLE
573 { 0x25C4, Infix, 4, 4, 0}, // BLACK LEFT-POINTING POINTER
574 { 0x25C5, Infix, 4, 4, 0}, // WHITE LEFT-POINTING POINTER
575 { 0x25C6, Infix, 4, 4, 0}, // BLACK DIAMOND
576 { 0x25C7, Infix, 4, 4, 0}, // WHITE DIAMOND
577 { 0x25C8, Infix, 4, 4, 0}, // WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND
578 { 0x25C9, Infix, 4, 4, 0}, // FISHEYE
579 { 0x25CC, Infix, 4, 4, 0}, // DOTTED CIRCLE
580 { 0x25CD, Infix, 4, 4, 0}, // CIRCLE WITH VERTICAL FILL
581 { 0x25CE, Infix, 4, 4, 0}, // BULLSEYE
582 { 0x25CF, Infix, 4, 4, 0}, // BLACK CIRCLE
583 { 0x25D6, Infix, 4, 4, 0}, // LEFT HALF BLACK CIRCLE
584 { 0x25D7, Infix, 4, 4, 0}, // RIGHT HALF BLACK CIRCLE
585 { 0x25E6, Infix, 4, 4, 0}, // WHITE BULLET
586 { 0x266D, Postfix, 0, 2, 0}, // MUSIC FLAT SIGN
587 { 0x266E, Postfix, 0, 2, 0}, // MUSIC NATURAL SIGN
588 { 0x266F, Postfix, 0, 2, 0}, // MUSIC SHARP SIGN
589 { 0x2758, Infix, 5, 5, 0}, // LIGHT VERTICAL BAR
590 { 0x2772, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
591 { 0x2773, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
592 { 0x27E6, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT WHITE SQUARE BRACKET
593 { 0x27E7, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
594 { 0x27E8, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT ANGLE BRACKET
595 { 0x27E9, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT ANGLE BRACKET
596 { 0x27EA, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
597 { 0x27EB, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
598 { 0x27EC, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
599 { 0x27ED, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
600 { 0x27EE, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL LEFT FLATTENED PARENTHESIS
601 { 0x27EF, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // MATHEMATICAL RIGHT FLATTENED PARENTHESIS
602 { 0x27F0, Infix, 5, 5, Stretchy}, // UPWARDS QUADRUPLE ARROW
603 { 0x27F1, Infix, 5, 5, Stretchy}, // DOWNWARDS QUADRUPLE ARROW
604 { 0x27F5, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS ARROW
605 { 0x27F6, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS ARROW
606 { 0x27F7, Infix, 5, 5, Stretchy | Accent}, // LONG LEFT RIGHT ARROW
607 { 0x27F8, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS DOUBLE ARROW
608 { 0x27F9, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS DOUBLE ARROW
609 { 0x27FA, Infix, 5, 5, Stretchy | Accent}, // LONG LEFT RIGHT DOUBLE ARROW
610 { 0x27FB, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS ARROW FROM BAR
611 { 0x27FC, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS ARROW FROM BAR
612 { 0x27FD, Infix, 5, 5, Stretchy | Accent}, // LONG LEFTWARDS DOUBLE ARROW FROM BAR
613 { 0x27FE, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS DOUBLE ARROW FROM BAR
614 { 0x27FF, Infix, 5, 5, Stretchy | Accent}, // LONG RIGHTWARDS SQUIGGLE ARROW
615 { 0x2900, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE
616 { 0x2901, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE
617 { 0x2902, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE
618 { 0x2903, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE
619 { 0x2904, Infix, 5, 5, Accent}, // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE
620 { 0x2905, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW FROM BAR
621 { 0x2906, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW FROM BAR
622 { 0x2907, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW FROM BAR
623 { 0x2908, Infix, 5, 5, 0}, // DOWNWARDS ARROW WITH HORIZONTAL STROKE
624 { 0x2909, Infix, 5, 5, 0}, // UPWARDS ARROW WITH HORIZONTAL STROKE
625 { 0x290A, Infix, 5, 5, Stretchy}, // UPWARDS TRIPLE ARROW
626 { 0x290B, Infix, 5, 5, Stretchy}, // DOWNWARDS TRIPLE ARROW
627 { 0x290C, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS DOUBLE DASH ARROW
628 { 0x290D, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS DOUBLE DASH ARROW
629 { 0x290E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS TRIPLE DASH ARROW
630 { 0x290F, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TRIPLE DASH ARROW
631 { 0x2910, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
632 { 0x2911, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH DOTTED STEM
633 { 0x2912, Infix, 5, 5, Stretchy}, // UPWARDS ARROW TO BAR
634 { 0x2913, Infix, 5, 5, Stretchy}, // DOWNWARDS ARROW TO BAR
635 { 0x2914, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE
636 { 0x2915, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE
637 { 0x2916, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL
638 { 0x2917, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE
639 { 0x2918, Infix, 5, 5, Accent}, // RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE
640 { 0x2919, Infix, 5, 5, Accent}, // LEFTWARDS ARROW-TAIL
641 { 0x291A, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW-TAIL
642 { 0x291B, Infix, 5, 5, Accent}, // LEFTWARDS DOUBLE ARROW-TAIL
643 { 0x291C, Infix, 5, 5, Accent}, // RIGHTWARDS DOUBLE ARROW-TAIL
644 { 0x291D, Infix, 5, 5, Accent}, // LEFTWARDS ARROW TO BLACK DIAMOND
645 { 0x291E, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW TO BLACK DIAMOND
646 { 0x291F, Infix, 5, 5, Accent}, // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND
647 { 0x2920, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND
648 { 0x2921, Infix, 5, 5, Stretchy}, // NORTH WEST AND SOUTH EAST ARROW
649 { 0x2922, Infix, 5, 5, Stretchy}, // NORTH EAST AND SOUTH WEST ARROW
650 { 0x2923, Infix, 5, 5, 0}, // NORTH WEST ARROW WITH HOOK
651 { 0x2924, Infix, 5, 5, 0}, // NORTH EAST ARROW WITH HOOK
652 { 0x2925, Infix, 5, 5, 0}, // SOUTH EAST ARROW WITH HOOK
653 { 0x2926, Infix, 5, 5, 0}, // SOUTH WEST ARROW WITH HOOK
654 { 0x2927, Infix, 5, 5, 0}, // NORTH WEST ARROW AND NORTH EAST ARROW
655 { 0x2928, Infix, 5, 5, 0}, // NORTH EAST ARROW AND SOUTH EAST ARROW
656 { 0x2929, Infix, 5, 5, 0}, // SOUTH EAST ARROW AND SOUTH WEST ARROW
657 { 0x292A, Infix, 5, 5, 0}, // SOUTH WEST ARROW AND NORTH WEST ARROW
658 { 0x292B, Infix, 5, 5, 0}, // RISING DIAGONAL CROSSING FALLING DIAGONAL
659 { 0x292C, Infix, 5, 5, 0}, // FALLING DIAGONAL CROSSING RISING DIAGONAL
660 { 0x292D, Infix, 5, 5, 0}, // SOUTH EAST ARROW CROSSING NORTH EAST ARROW
661 { 0x292E, Infix, 5, 5, 0}, // NORTH EAST ARROW CROSSING SOUTH EAST ARROW
662 { 0x292F, Infix, 5, 5, 0}, // FALLING DIAGONAL CROSSING NORTH EAST ARROW
663 { 0x2930, Infix, 5, 5, 0}, // RISING DIAGONAL CROSSING SOUTH EAST ARROW
664 { 0x2931, Infix, 5, 5, 0}, // NORTH EAST ARROW CROSSING NORTH WEST ARROW
665 { 0x2932, Infix, 5, 5, 0}, // NORTH WEST ARROW CROSSING NORTH EAST ARROW
666 { 0x2933, Infix, 5, 5, Accent}, // WAVE ARROW POINTING DIRECTLY RIGHT
667 { 0x2934, Infix, 5, 5, 0}, // ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS
668 { 0x2935, Infix, 5, 5, 0}, // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS
669 { 0x2936, Infix, 5, 5, 0}, // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
670 { 0x2937, Infix, 5, 5, 0}, // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS
671 { 0x2938, Infix, 5, 5, 0}, // RIGHT-SIDE ARC CLOCKWISE ARROW
672 { 0x2939, Infix, 5, 5, 0}, // LEFT-SIDE ARC ANTICLOCKWISE ARROW
673 { 0x293A, Infix, 5, 5, Accent}, // TOP ARC ANTICLOCKWISE ARROW
674 { 0x293B, Infix, 5, 5, Accent}, // BOTTOM ARC ANTICLOCKWISE ARROW
675 { 0x293C, Infix, 5, 5, Accent}, // TOP ARC CLOCKWISE ARROW WITH MINUS
676 { 0x293D, Infix, 5, 5, Accent}, // TOP ARC ANTICLOCKWISE ARROW WITH PLUS
677 { 0x293E, Infix, 5, 5, 0}, // LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW
678 { 0x293F, Infix, 5, 5, 0}, // LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW
679 { 0x2940, Infix, 5, 5, 0}, // ANTICLOCKWISE CLOSED CIRCLE ARROW
680 { 0x2941, Infix, 5, 5, 0}, // CLOCKWISE CLOSED CIRCLE ARROW
681 { 0x2942, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW
682 { 0x2943, Infix, 5, 5, Accent}, // LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW
683 { 0x2944, Infix, 5, 5, Accent}, // SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW
684 { 0x2945, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW WITH PLUS BELOW
685 { 0x2946, Infix, 5, 5, Accent}, // LEFTWARDS ARROW WITH PLUS BELOW
686 { 0x2947, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW THROUGH X
687 { 0x2948, Infix, 5, 5, Accent}, // LEFT RIGHT ARROW THROUGH SMALL CIRCLE
688 { 0x2949, Infix, 5, 5, 0}, // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE
689 { 0x294A, Infix, 5, 5, Accent}, // LEFT BARB UP RIGHT BARB DOWN HARPOON
690 { 0x294B, Infix, 5, 5, Accent}, // LEFT BARB DOWN RIGHT BARB UP HARPOON
691 { 0x294C, Infix, 5, 5, 0}, // UP BARB RIGHT DOWN BARB LEFT HARPOON
692 { 0x294D, Infix, 5, 5, 0}, // UP BARB LEFT DOWN BARB RIGHT HARPOON
693 { 0x294E, Infix, 5, 5, Stretchy | Accent}, // LEFT BARB UP RIGHT BARB UP HARPOON
694 { 0x294F, Infix, 5, 5, Stretchy}, // UP BARB RIGHT DOWN BARB RIGHT HARPOON
695 { 0x2950, Infix, 5, 5, Stretchy | Accent}, // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
696 { 0x2951, Infix, 5, 5, Stretchy}, // UP BARB LEFT DOWN BARB LEFT HARPOON
697 { 0x2952, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UP TO BAR
698 { 0x2953, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UP TO BAR
699 { 0x2954, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHT TO BAR
700 { 0x2955, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
701 { 0x2956, Infix, 5, 5, Stretchy}, // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
702 { 0x2957, Infix, 5, 5, Stretchy}, // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
703 { 0x2958, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT TO BAR
704 { 0x2959, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
705 { 0x295A, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB UP FROM BAR
706 { 0x295B, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
707 { 0x295C, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
708 { 0x295D, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
709 { 0x295E, Infix, 5, 5, Stretchy | Accent}, // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
710 { 0x295F, Infix, 5, 5, Stretchy | Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
711 { 0x2960, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT FROM BAR
712 { 0x2961, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
713 { 0x2962, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
714 { 0x2963, Infix, 5, 5, 0}, // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
715 { 0x2964, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
716 { 0x2965, Infix, 5, 5, 0}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
717 { 0x2966, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP
718 { 0x2967, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
719 { 0x2968, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP
720 { 0x2969, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN
721 { 0x296A, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
722 { 0x296B, Infix, 5, 5, Accent}, // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
723 { 0x296C, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
724 { 0x296D, Infix, 5, 5, Accent}, // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
725 { 0x296E, Infix, 5, 5, Stretchy}, // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
726 { 0x296F, Infix, 5, 5, Stretchy}, // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
727 { 0x2970, Infix, 5, 5, Accent}, // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
728 { 0x2971, Infix, 5, 5, Accent}, // EQUALS SIGN ABOVE RIGHTWARDS ARROW
729 { 0x2972, Infix, 5, 5, Accent}, // TILDE OPERATOR ABOVE RIGHTWARDS ARROW
730 { 0x2973, Infix, 5, 5, Accent}, // LEFTWARDS ARROW ABOVE TILDE OPERATOR
731 { 0x2974, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE TILDE OPERATOR
732 { 0x2975, Infix, 5, 5, Accent}, // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO
733 { 0x2976, Infix, 5, 5, Accent}, // LESS-THAN ABOVE LEFTWARDS ARROW
734 { 0x2977, Infix, 5, 5, Accent}, // LEFTWARDS ARROW THROUGH LESS-THAN
735 { 0x2978, Infix, 5, 5, Accent}, // GREATER-THAN ABOVE RIGHTWARDS ARROW
736 { 0x2979, Infix, 5, 5, Accent}, // SUBSET ABOVE RIGHTWARDS ARROW
737 { 0x297A, Infix, 5, 5, Accent}, // LEFTWARDS ARROW THROUGH SUBSET
738 { 0x297B, Infix, 5, 5, Accent}, // SUPERSET ABOVE LEFTWARDS ARROW
739 { 0x297C, Infix, 5, 5, Accent}, // LEFT FISH TAIL
740 { 0x297D, Infix, 5, 5, Accent}, // RIGHT FISH TAIL
741 { 0x297E, Infix, 5, 5, 0}, // UP FISH TAIL
742 { 0x297F, Infix, 5, 5, 0}, // DOWN FISH TAIL
743 { 0x2980, Prefix, 0, 0, Fence | Stretchy}, // TRIPLE VERTICAL BAR DELIMITER
744 { 0x2980, Postfix, 0, 0, Fence | Stretchy}, // TRIPLE VERTICAL BAR DELIMITER
745 { 0x2981, Infix, 3, 3, 0}, // Z NOTATION SPOT
746 { 0x2982, Infix, 3, 3, 0}, // Z NOTATION TYPE COLON
747 { 0x2983, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT WHITE CURLY BRACKET
748 { 0x2984, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT WHITE CURLY BRACKET
749 { 0x2985, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT WHITE PARENTHESIS
750 { 0x2986, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT WHITE PARENTHESIS
751 { 0x2987, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION LEFT IMAGE BRACKET
752 { 0x2988, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION RIGHT IMAGE BRACKET
753 { 0x2989, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION LEFT BINDING BRACKET
754 { 0x298A, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // Z NOTATION RIGHT BINDING BRACKET
755 { 0x298B, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH UNDERBAR
756 { 0x298C, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH UNDERBAR
757 { 0x298D, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
758 { 0x298E, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
759 { 0x298F, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
760 { 0x2990, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
761 { 0x2991, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT ANGLE BRACKET WITH DOT
762 { 0x2992, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT ANGLE BRACKET WITH DOT
763 { 0x2993, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT ARC LESS-THAN BRACKET
764 { 0x2994, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT ARC GREATER-THAN BRACKET
765 { 0x2995, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // DOUBLE LEFT ARC GREATER-THAN BRACKET
766 { 0x2996, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // DOUBLE RIGHT ARC LESS-THAN BRACKET
767 { 0x2997, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT BLACK TORTOISE SHELL BRACKET
768 { 0x2998, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT BLACK TORTOISE SHELL BRACKET
769 { 0x2999, Infix, 3, 3, 0}, // DOTTED FENCE
770 { 0x299A, Infix, 3, 3, 0}, // VERTICAL ZIGZAG LINE
771 { 0x299B, Infix, 3, 3, 0}, // MEASURED ANGLE OPENING LEFT
772 { 0x299C, Infix, 3, 3, 0}, // RIGHT ANGLE VARIANT WITH SQUARE
773 { 0x299D, Infix, 3, 3, 0}, // MEASURED RIGHT ANGLE WITH DOT
774 { 0x299E, Infix, 3, 3, 0}, // ANGLE WITH S INSIDE
775 { 0x299F, Infix, 3, 3, 0}, // ACUTE ANGLE
776 { 0x29A0, Infix, 3, 3, 0}, // SPHERICAL ANGLE OPENING LEFT
777 { 0x29A1, Infix, 3, 3, 0}, // SPHERICAL ANGLE OPENING UP
778 { 0x29A2, Infix, 3, 3, 0}, // TURNED ANGLE
779 { 0x29A3, Infix, 3, 3, 0}, // REVERSED ANGLE
780 { 0x29A4, Infix, 3, 3, 0}, // ANGLE WITH UNDERBAR
781 { 0x29A5, Infix, 3, 3, 0}, // REVERSED ANGLE WITH UNDERBAR
782 { 0x29A6, Infix, 3, 3, 0}, // OBLIQUE ANGLE OPENING UP
783 { 0x29A7, Infix, 3, 3, 0}, // OBLIQUE ANGLE OPENING DOWN
784 { 0x29A8, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
785 { 0x29A9, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
786 { 0x29AA, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
787 { 0x29AB, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
788 { 0x29AC, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
789 { 0x29AD, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
790 { 0x29AE, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
791 { 0x29AF, Infix, 3, 3, 0}, // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
792 { 0x29B0, Infix, 3, 3, 0}, // REVERSED EMPTY SET
793 { 0x29B1, Infix, 3, 3, 0}, // EMPTY SET WITH OVERBAR
794 { 0x29B2, Infix, 3, 3, 0}, // EMPTY SET WITH SMALL CIRCLE ABOVE
795 { 0x29B3, Infix, 3, 3, 0}, // EMPTY SET WITH RIGHT ARROW ABOVE
796 { 0x29B4, Infix, 3, 3, 0}, // EMPTY SET WITH LEFT ARROW ABOVE
797 { 0x29B5, Infix, 3, 3, 0}, // CIRCLE WITH HORIZONTAL BAR
798 { 0x29B6, Infix, 4, 4, 0}, // CIRCLED VERTICAL BAR
799 { 0x29B7, Infix, 4, 4, 0}, // CIRCLED PARALLEL
800 { 0x29B8, Infix, 4, 4, 0}, // CIRCLED REVERSE SOLIDUS
801 { 0x29B9, Infix, 4, 4, 0}, // CIRCLED PERPENDICULAR
802 { 0x29BA, Infix, 4, 4, 0}, // CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR
803 { 0x29BB, Infix, 4, 4, 0}, // CIRCLE WITH SUPERIMPOSED X
804 { 0x29BC, Infix, 4, 4, 0}, // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN
805 { 0x29BD, Infix, 4, 4, 0}, // UP ARROW THROUGH CIRCLE
806 { 0x29BE, Infix, 4, 4, 0}, // CIRCLED WHITE BULLET
807 { 0x29BF, Infix, 4, 4, 0}, // CIRCLED BULLET
808 { 0x29C0, Infix, 5, 5, 0}, // CIRCLED LESS-THAN
809 { 0x29C1, Infix, 5, 5, 0}, // CIRCLED GREATER-THAN
810 { 0x29C2, Infix, 3, 3, 0}, // CIRCLE WITH SMALL CIRCLE TO THE RIGHT
811 { 0x29C3, Infix, 3, 3, 0}, // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
812 { 0x29C4, Infix, 4, 4, 0}, // SQUARED RISING DIAGONAL SLASH
813 { 0x29C5, Infix, 4, 4, 0}, // SQUARED FALLING DIAGONAL SLASH
814 { 0x29C6, Infix, 4, 4, 0}, // SQUARED ASTERISK
815 { 0x29C7, Infix, 4, 4, 0}, // SQUARED SMALL CIRCLE
816 { 0x29C8, Infix, 4, 4, 0}, // SQUARED SQUARE
817 { 0x29C9, Infix, 3, 3, 0}, // TWO JOINED SQUARES
818 { 0x29CA, Infix, 3, 3, 0}, // TRIANGLE WITH DOT ABOVE
819 { 0x29CB, Infix, 3, 3, 0}, // TRIANGLE WITH UNDERBAR
820 { 0x29CC, Infix, 3, 3, 0}, // S IN TRIANGLE
821 { 0x29CD, Infix, 3, 3, 0}, // TRIANGLE WITH SERIFS AT BOTTOM
822 { 0x29CE, Infix, 5, 5, 0}, // RIGHT TRIANGLE ABOVE LEFT TRIANGLE
823 { 0x29CF, Infix, 5, 5, 0}, // LEFT TRIANGLE BESIDE VERTICAL BAR
824 { 0x29D0, Infix, 5, 5, 0}, // VERTICAL BAR BESIDE RIGHT TRIANGLE
825 { 0x29D1, Infix, 5, 5, 0}, // BOWTIE WITH LEFT HALF BLACK
826 { 0x29D2, Infix, 5, 5, 0}, // BOWTIE WITH RIGHT HALF BLACK
827 { 0x29D3, Infix, 5, 5, 0}, // BLACK BOWTIE
828 { 0x29D4, Infix, 5, 5, 0}, // TIMES WITH LEFT HALF BLACK
829 { 0x29D5, Infix, 5, 5, 0}, // TIMES WITH RIGHT HALF BLACK
830 { 0x29D6, Infix, 4, 4, 0}, // WHITE HOURGLASS
831 { 0x29D7, Infix, 4, 4, 0}, // BLACK HOURGLASS
832 { 0x29D8, Infix, 3, 3, 0}, // LEFT WIGGLY FENCE
833 { 0x29D9, Infix, 3, 3, 0}, // RIGHT WIGGLY FENCE
834 { 0x29DB, Infix, 3, 3, 0}, // RIGHT DOUBLE WIGGLY FENCE
835 { 0x29DC, Infix, 3, 3, 0}, // INCOMPLETE INFINITY
836 { 0x29DD, Infix, 3, 3, 0}, // TIE OVER INFINITY
837 { 0x29DE, Infix, 5, 5, 0}, // INFINITY NEGATED WITH VERTICAL BAR
838 { 0x29DF, Infix, 3, 3, 0}, // DOUBLE-ENDED MULTIMAP
839 { 0x29E0, Infix, 3, 3, 0}, // SQUARE WITH CONTOURED OUTLINE
840 { 0x29E1, Infix, 5, 5, 0}, // INCREASES AS
841 { 0x29E2, Infix, 4, 4, 0}, // SHUFFLE PRODUCT
842 { 0x29E3, Infix, 5, 5, 0}, // EQUALS SIGN AND SLANTED PARALLEL
843 { 0x29E4, Infix, 5, 5, 0}, // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
844 { 0x29E5, Infix, 5, 5, 0}, // IDENTICAL TO AND SLANTED PARALLEL
845 { 0x29E6, Infix, 5, 5, 0}, // GLEICH STARK
846 { 0x29E7, Infix, 3, 3, 0}, // THERMODYNAMIC
847 { 0x29E8, Infix, 3, 3, 0}, // DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK
848 { 0x29E9, Infix, 3, 3, 0}, // DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK
849 { 0x29EA, Infix, 3, 3, 0}, // BLACK DIAMOND WITH DOWN ARROW
850 { 0x29EB, Infix, 3, 3, 0}, // BLACK LOZENGE
851 { 0x29EC, Infix, 3, 3, 0}, // WHITE CIRCLE WITH DOWN ARROW
852 { 0x29ED, Infix, 3, 3, 0}, // BLACK CIRCLE WITH DOWN ARROW
853 { 0x29EE, Infix, 3, 3, 0}, // ERROR-BARRED WHITE SQUARE
854 { 0x29EF, Infix, 3, 3, 0}, // ERROR-BARRED BLACK SQUARE
855 { 0x29F0, Infix, 3, 3, 0}, // ERROR-BARRED WHITE DIAMOND
856 { 0x29F1, Infix, 3, 3, 0}, // ERROR-BARRED BLACK DIAMOND
857 { 0x29F2, Infix, 3, 3, 0}, // ERROR-BARRED WHITE CIRCLE
858 { 0x29F3, Infix, 3, 3, 0}, // ERROR-BARRED BLACK CIRCLE
859 { 0x29F4, Infix, 5, 5, 0}, // RULE-DELAYED
860 { 0x29F5, Infix, 4, 4, 0}, // REVERSE SOLIDUS OPERATOR
861 { 0x29F6, Infix, 4, 4, 0}, // SOLIDUS WITH OVERBAR
862 { 0x29F7, Infix, 4, 4, 0}, // REVERSE SOLIDUS WITH HORIZONTAL STROKE
863 { 0x29F8, Infix, 3, 3, 0}, // BIG SOLIDUS
864 { 0x29F9, Infix, 3, 3, 0}, // BIG REVERSE SOLIDUS
865 { 0x29FA, Infix, 3, 3, 0}, // DOUBLE PLUS
866 { 0x29FB, Infix, 3, 3, 0}, // TRIPLE PLUS
867 { 0x29FC, Prefix, 0, 0, Symmetric | Fence | Stretchy}, // LEFT-POINTING CURVED ANGLE BRACKET
868 { 0x29FD, Postfix, 0, 0, Symmetric | Fence | Stretchy}, // RIGHT-POINTING CURVED ANGLE BRACKET
869 { 0x29FE, Infix, 4, 4, 0}, // TINY
870 { 0x29FF, Infix, 4, 4, 0}, // MINY
871 { 0x2A00, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED DOT OPERATOR
872 { 0x2A01, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED PLUS OPERATOR
873 { 0x2A02, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY CIRCLED TIMES OPERATOR
874 { 0x2A03, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION OPERATOR WITH DOT
875 { 0x2A04, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY UNION OPERATOR WITH PLUS
876 { 0x2A05, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SQUARE INTERSECTION OPERATOR
877 { 0x2A06, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY SQUARE UNION OPERATOR
878 { 0x2A07, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // TWO LOGICAL AND OPERATOR
879 { 0x2A08, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // TWO LOGICAL OR OPERATOR
880 { 0x2A09, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY TIMES OPERATOR
881 { 0x2A0A, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // MODULO TWO SUM
882 { 0x2A0B, Prefix, 1, 2, Symmetric | LargeOp}, // SUMMATION WITH INTEGRAL
883 { 0x2A0C, Prefix, 0, 1, Symmetric | LargeOp}, // QUADRUPLE INTEGRAL OPERATOR
884 { 0x2A0D, Prefix, 1, 2, Symmetric | LargeOp}, // FINITE PART INTEGRAL
885 { 0x2A0E, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH DOUBLE STROKE
886 { 0x2A0F, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL AVERAGE WITH SLASH
887 { 0x2A10, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // CIRCULATION FUNCTION
888 { 0x2A11, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // ANTICLOCKWISE INTEGRATION
889 { 0x2A12, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
890 { 0x2A13, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
891 { 0x2A14, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LINE INTEGRATION NOT INCLUDING THE POLE
892 { 0x2A15, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL AROUND A POINT OPERATOR
893 { 0x2A16, Prefix, 1, 2, Symmetric | LargeOp}, // QUATERNION INTEGRAL OPERATOR
894 { 0x2A17, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
895 { 0x2A18, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH TIMES SIGN
896 { 0x2A19, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH INTERSECTION
897 { 0x2A1A, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH UNION
898 { 0x2A1B, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH OVERBAR
899 { 0x2A1C, Prefix, 1, 2, Symmetric | LargeOp}, // INTEGRAL WITH UNDERBAR
900 { 0x2A1D, Infix, 3, 3, 0}, // JOIN
901 { 0x2A1E, Infix, 3, 3, 0}, // LARGE LEFT TRIANGLE OPERATOR
902 { 0x2A1F, Infix, 3, 3, 0}, // Z NOTATION SCHEMA COMPOSITION
903 { 0x2A20, Infix, 3, 3, 0}, // Z NOTATION SCHEMA PIPING
904 { 0x2A21, Infix, 3, 3, 0}, // Z NOTATION SCHEMA PROJECTION
905 { 0x2A22, Infix, 4, 4, 0}, // PLUS SIGN WITH SMALL CIRCLE ABOVE
906 { 0x2A23, Infix, 4, 4, 0}, // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE
907 { 0x2A24, Infix, 4, 4, 0}, // PLUS SIGN WITH TILDE ABOVE
908 { 0x2A25, Infix, 4, 4, 0}, // PLUS SIGN WITH DOT BELOW
909 { 0x2A26, Infix, 4, 4, 0}, // PLUS SIGN WITH TILDE BELOW
910 { 0x2A27, Infix, 4, 4, 0}, // PLUS SIGN WITH SUBSCRIPT TWO
911 { 0x2A28, Infix, 4, 4, 0}, // PLUS SIGN WITH BLACK TRIANGLE
912 { 0x2A29, Infix, 4, 4, 0}, // MINUS SIGN WITH COMMA ABOVE
913 { 0x2A2A, Infix, 4, 4, 0}, // MINUS SIGN WITH DOT BELOW
914 { 0x2A2B, Infix, 4, 4, 0}, // MINUS SIGN WITH FALLING DOTS
915 { 0x2A2C, Infix, 4, 4, 0}, // MINUS SIGN WITH RISING DOTS
916 { 0x2A2D, Infix, 4, 4, 0}, // PLUS SIGN IN LEFT HALF CIRCLE
917 { 0x2A2E, Infix, 4, 4, 0}, // PLUS SIGN IN RIGHT HALF CIRCLE
918 { 0x2A2F, Infix, 4, 4, 0}, // VECTOR OR CROSS PRODUCT
919 { 0x2A30, Infix, 4, 4, 0}, // MULTIPLICATION SIGN WITH DOT ABOVE
920 { 0x2A31, Infix, 4, 4, 0}, // MULTIPLICATION SIGN WITH UNDERBAR
921 { 0x2A32, Infix, 4, 4, 0}, // SEMIDIRECT PRODUCT WITH BOTTOM CLOSED
922 { 0x2A33, Infix, 4, 4, 0}, // SMASH PRODUCT
923 { 0x2A34, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
924 { 0x2A35, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
925 { 0x2A36, Infix, 4, 4, 0}, // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT
926 { 0x2A37, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN DOUBLE CIRCLE
927 { 0x2A38, Infix, 4, 4, 0}, // CIRCLED DIVISION SIGN
928 { 0x2A39, Infix, 4, 4, 0}, // PLUS SIGN IN TRIANGLE
929 { 0x2A3A, Infix, 4, 4, 0}, // MINUS SIGN IN TRIANGLE
930 { 0x2A3B, Infix, 4, 4, 0}, // MULTIPLICATION SIGN IN TRIANGLE
931 { 0x2A3C, Infix, 4, 4, 0}, // INTERIOR PRODUCT
932 { 0x2A3D, Infix, 4, 4, 0}, // RIGHTHAND INTERIOR PRODUCT
933 { 0x2A3E, Infix, 4, 4, 0}, // Z NOTATION RELATIONAL COMPOSITION
934 { 0x2A3F, Infix, 4, 4, 0}, // AMALGAMATION OR COPRODUCT
935 { 0x2A40, Infix, 4, 4, 0}, // INTERSECTION WITH DOT
936 { 0x2A41, Infix, 4, 4, 0}, // UNION WITH MINUS SIGN
937 { 0x2A42, Infix, 4, 4, 0}, // UNION WITH OVERBAR
938 { 0x2A43, Infix, 4, 4, 0}, // INTERSECTION WITH OVERBAR
939 { 0x2A44, Infix, 4, 4, 0}, // INTERSECTION WITH LOGICAL AND
940 { 0x2A45, Infix, 4, 4, 0}, // UNION WITH LOGICAL OR
941 { 0x2A46, Infix, 4, 4, 0}, // UNION ABOVE INTERSECTION
942 { 0x2A47, Infix, 4, 4, 0}, // INTERSECTION ABOVE UNION
943 { 0x2A48, Infix, 4, 4, 0}, // UNION ABOVE BAR ABOVE INTERSECTION
944 { 0x2A49, Infix, 4, 4, 0}, // INTERSECTION ABOVE BAR ABOVE UNION
945 { 0x2A4A, Infix, 4, 4, 0}, // UNION BESIDE AND JOINED WITH UNION
946 { 0x2A4B, Infix, 4, 4, 0}, // INTERSECTION BESIDE AND JOINED WITH INTERSECTION
947 { 0x2A4C, Infix, 4, 4, 0}, // CLOSED UNION WITH SERIFS
948 { 0x2A4D, Infix, 4, 4, 0}, // CLOSED INTERSECTION WITH SERIFS
949 { 0x2A4E, Infix, 4, 4, 0}, // DOUBLE SQUARE INTERSECTION
950 { 0x2A4F, Infix, 4, 4, 0}, // DOUBLE SQUARE UNION
951 { 0x2A50, Infix, 4, 4, 0}, // CLOSED UNION WITH SERIFS AND SMASH PRODUCT
952 { 0x2A51, Infix, 4, 4, 0}, // LOGICAL AND WITH DOT ABOVE
953 { 0x2A52, Infix, 4, 4, 0}, // LOGICAL OR WITH DOT ABOVE
954 { 0x2A53, Infix, 4, 4, 0}, // DOUBLE LOGICAL AND
955 { 0x2A54, Infix, 4, 4, 0}, // DOUBLE LOGICAL OR
956 { 0x2A55, Infix, 4, 4, 0}, // TWO INTERSECTING LOGICAL AND
957 { 0x2A56, Infix, 4, 4, 0}, // TWO INTERSECTING LOGICAL OR
958 { 0x2A57, Infix, 4, 4, 0}, // SLOPING LARGE OR
959 { 0x2A58, Infix, 4, 4, 0}, // SLOPING LARGE AND
960 { 0x2A59, Infix, 5, 5, 0}, // LOGICAL OR OVERLAPPING LOGICAL AND
961 { 0x2A5A, Infix, 4, 4, 0}, // LOGICAL AND WITH MIDDLE STEM
962 { 0x2A5B, Infix, 4, 4, 0}, // LOGICAL OR WITH MIDDLE STEM
963 { 0x2A5C, Infix, 4, 4, 0}, // LOGICAL AND WITH HORIZONTAL DASH
964 { 0x2A5D, Infix, 4, 4, 0}, // LOGICAL OR WITH HORIZONTAL DASH
965 { 0x2A5E, Infix, 4, 4, 0}, // LOGICAL AND WITH DOUBLE OVERBAR
966 { 0x2A5F, Infix, 4, 4, 0}, // LOGICAL AND WITH UNDERBAR
967 { 0x2A60, Infix, 4, 4, 0}, // LOGICAL AND WITH DOUBLE UNDERBAR
968 { 0x2A61, Infix, 4, 4, 0}, // SMALL VEE WITH UNDERBAR
969 { 0x2A62, Infix, 4, 4, 0}, // LOGICAL OR WITH DOUBLE OVERBAR
970 { 0x2A63, Infix, 4, 4, 0}, // LOGICAL OR WITH DOUBLE UNDERBAR
971 { 0x2A64, Infix, 4, 4, 0}, // Z NOTATION DOMAIN ANTIRESTRICTION
972 { 0x2A65, Infix, 4, 4, 0}, // Z NOTATION RANGE ANTIRESTRICTION
973 { 0x2A66, Infix, 5, 5, 0}, // EQUALS SIGN WITH DOT BELOW
974 { 0x2A67, Infix, 5, 5, 0}, // IDENTICAL WITH DOT ABOVE
975 { 0x2A68, Infix, 5, 5, 0}, // TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE
976 { 0x2A69, Infix, 5, 5, 0}, // TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE
977 { 0x2A6A, Infix, 5, 5, 0}, // TILDE OPERATOR WITH DOT ABOVE
978 { 0x2A6B, Infix, 5, 5, 0}, // TILDE OPERATOR WITH RISING DOTS
979 { 0x2A6C, Infix, 5, 5, 0}, // SIMILAR MINUS SIMILAR
980 { 0x2A6D, Infix, 5, 5, 0}, // CONGRUENT WITH DOT ABOVE
981 { 0x2A6E, Infix, 5, 5, 0}, // EQUALS WITH ASTERISK
982 { 0x2A6F, Infix, 5, 5, 0}, // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
983 { 0x2A70, Infix, 5, 5, 0}, // APPROXIMATELY EQUAL OR EQUAL TO
984 { 0x2A71, Infix, 4, 4, 0}, // EQUALS SIGN ABOVE PLUS SIGN
985 { 0x2A72, Infix, 4, 4, 0}, // PLUS SIGN ABOVE EQUALS SIGN
986 { 0x2A73, Infix, 5, 5, 0}, // EQUALS SIGN ABOVE TILDE OPERATOR
987 { 0x2A74, Infix, 5, 5, 0}, // DOUBLE COLON EQUAL
988 { 0x2A75, Infix, 5, 5, 0}, // TWO CONSECUTIVE EQUALS SIGNS
989 { 0x2A76, Infix, 5, 5, 0}, // THREE CONSECUTIVE EQUALS SIGNS
990 { 0x2A77, Infix, 5, 5, 0}, // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
991 { 0x2A78, Infix, 5, 5, 0}, // EQUIVALENT WITH FOUR DOTS ABOVE
992 { 0x2A79, Infix, 5, 5, 0}, // LESS-THAN WITH CIRCLE INSIDE
993 { 0x2A7A, Infix, 5, 5, 0}, // GREATER-THAN WITH CIRCLE INSIDE
994 { 0x2A7B, Infix, 5, 5, 0}, // LESS-THAN WITH QUESTION MARK ABOVE
995 { 0x2A7C, Infix, 5, 5, 0}, // GREATER-THAN WITH QUESTION MARK ABOVE
996 { 0x2A7D, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO
997 { 0x2A7E, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO
998 { 0x2A7F, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
999 { 0x2A80, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
1000 { 0x2A81, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
1001 { 0x2A82, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
1002 { 0x2A83, Infix, 5, 5, 0}, // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
1003 { 0x2A84, Infix, 5, 5, 0}, // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
1004 { 0x2A85, Infix, 5, 5, 0}, // LESS-THAN OR APPROXIMATE
1005 { 0x2A86, Infix, 5, 5, 0}, // GREATER-THAN OR APPROXIMATE
1006 { 0x2A87, Infix, 5, 5, 0}, // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
1007 { 0x2A88, Infix, 5, 5, 0}, // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
1008 { 0x2A89, Infix, 5, 5, 0}, // LESS-THAN AND NOT APPROXIMATE
1009 { 0x2A8A, Infix, 5, 5, 0}, // GREATER-THAN AND NOT APPROXIMATE
1010 { 0x2A8B, Infix, 5, 5, 0}, // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
1011 { 0x2A8C, Infix, 5, 5, 0}, // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
1012 { 0x2A8D, Infix, 5, 5, 0}, // LESS-THAN ABOVE SIMILAR OR EQUAL
1013 { 0x2A8E, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SIMILAR OR EQUAL
1014 { 0x2A8F, Infix, 5, 5, 0}, // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
1015 { 0x2A90, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
1016 { 0x2A91, Infix, 5, 5, 0}, // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
1017 { 0x2A92, Infix, 5, 5, 0}, // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
1018 { 0x2A93, Infix, 5, 5, 0}, // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
1019 { 0x2A94, Infix, 5, 5, 0}, // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
1020 { 0x2A95, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR LESS-THAN
1021 { 0x2A96, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR GREATER-THAN
1022 { 0x2A97, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
1023 { 0x2A98, Infix, 5, 5, 0}, // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
1024 { 0x2A99, Infix, 5, 5, 0}, // DOUBLE-LINE EQUAL TO OR LESS-THAN
1025 { 0x2A9A, Infix, 5, 5, 0}, // DOUBLE-LINE EQUAL TO OR GREATER-THAN
1026 { 0x2A9B, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
1027 { 0x2A9C, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
1028 { 0x2A9D, Infix, 5, 5, 0}, // SIMILAR OR LESS-THAN
1029 { 0x2A9E, Infix, 5, 5, 0}, // SIMILAR OR GREATER-THAN
1030 { 0x2A9F, Infix, 5, 5, 0}, // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
1031 { 0x2AA0, Infix, 5, 5, 0}, // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
1032 { 0x2AA1, Infix, 5, 5, 0}, // DOUBLE NESTED LESS-THAN
1033 { 0x2AA2, Infix, 5, 5, 0}, // DOUBLE NESTED GREATER-THAN
1034 { 0x2AA3, Infix, 5, 5, 0}, // DOUBLE NESTED LESS-THAN WITH UNDERBAR
1035 { 0x2AA4, Infix, 5, 5, 0}, // GREATER-THAN OVERLAPPING LESS-THAN
1036 { 0x2AA5, Infix, 5, 5, 0}, // GREATER-THAN BESIDE LESS-THAN
1037 { 0x2AA6, Infix, 5, 5, 0}, // LESS-THAN CLOSED BY CURVE
1038 { 0x2AA7, Infix, 5, 5, 0}, // GREATER-THAN CLOSED BY CURVE
1039 { 0x2AA8, Infix, 5, 5, 0}, // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
1040 { 0x2AA9, Infix, 5, 5, 0}, // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
1041 { 0x2AAA, Infix, 5, 5, 0}, // SMALLER THAN
1042 { 0x2AAB, Infix, 5, 5, 0}, // LARGER THAN
1043 { 0x2AAC, Infix, 5, 5, 0}, // SMALLER THAN OR EQUAL TO
1044 { 0x2AAD, Infix, 5, 5, 0}, // LARGER THAN OR EQUAL TO
1045 { 0x2AAE, Infix, 5, 5, 0}, // EQUALS SIGN WITH BUMPY ABOVE
1046 { 0x2AAF, Infix, 5, 5, 0}, // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
1047 { 0x2AB0, Infix, 5, 5, 0}, // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
1048 { 0x2AB1, Infix, 5, 5, 0}, // PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO
1049 { 0x2AB2, Infix, 5, 5, 0}, // SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO
1050 { 0x2AB3, Infix, 5, 5, 0}, // PRECEDES ABOVE EQUALS SIGN
1051 { 0x2AB4, Infix, 5, 5, 0}, // SUCCEEDS ABOVE EQUALS SIGN
1052 { 0x2AB5, Infix, 5, 5, 0}, // PRECEDES ABOVE NOT EQUAL TO
1053 { 0x2AB6, Infix, 5, 5, 0}, // SUCCEEDS ABOVE NOT EQUAL TO
1054 { 0x2AB7, Infix, 5, 5, 0}, // PRECEDES ABOVE ALMOST EQUAL TO
1055 { 0x2AB8, Infix, 5, 5, 0}, // SUCCEEDS ABOVE ALMOST EQUAL TO
1056 { 0x2AB9, Infix, 5, 5, 0}, // PRECEDES ABOVE NOT ALMOST EQUAL TO
1057 { 0x2ABA, Infix, 5, 5, 0}, // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
1058 { 0x2ABB, Infix, 5, 5, 0}, // DOUBLE PRECEDES
1059 { 0x2ABC, Infix, 5, 5, 0}, // DOUBLE SUCCEEDS
1060 { 0x2ABD, Infix, 5, 5, 0}, // SUBSET WITH DOT
1061 { 0x2ABE, Infix, 5, 5, 0}, // SUPERSET WITH DOT
1062 { 0x2ABF, Infix, 5, 5, 0}, // SUBSET WITH PLUS SIGN BELOW
1063 { 0x2AC0, Infix, 5, 5, 0}, // SUPERSET WITH PLUS SIGN BELOW
1064 { 0x2AC1, Infix, 5, 5, 0}, // SUBSET WITH MULTIPLICATION SIGN BELOW
1065 { 0x2AC2, Infix, 5, 5, 0}, // SUPERSET WITH MULTIPLICATION SIGN BELOW
1066 { 0x2AC3, Infix, 5, 5, 0}, // SUBSET OF OR EQUAL TO WITH DOT ABOVE
1067 { 0x2AC4, Infix, 5, 5, 0}, // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
1068 { 0x2AC5, Infix, 5, 5, 0}, // SUBSET OF ABOVE EQUALS SIGN
1069 { 0x2AC6, Infix, 5, 5, 0}, // SUPERSET OF ABOVE EQUALS SIGN
1070 { 0x2AC7, Infix, 5, 5, 0}, // SUBSET OF ABOVE TILDE OPERATOR
1071 { 0x2AC8, Infix, 5, 5, 0}, // SUPERSET OF ABOVE TILDE OPERATOR
1072 { 0x2AC9, Infix, 5, 5, 0}, // SUBSET OF ABOVE ALMOST EQUAL TO
1073 { 0x2ACA, Infix, 5, 5, 0}, // SUPERSET OF ABOVE ALMOST EQUAL TO
1074 { 0x2ACB, Infix, 5, 5, 0}, // SUBSET OF ABOVE NOT EQUAL TO
1075 { 0x2ACC, Infix, 5, 5, 0}, // SUPERSET OF ABOVE NOT EQUAL TO
1076 { 0x2ACD, Infix, 5, 5, 0}, // SQUARE LEFT OPEN BOX OPERATOR
1077 { 0x2ACE, Infix, 5, 5, 0}, // SQUARE RIGHT OPEN BOX OPERATOR
1078 { 0x2ACF, Infix, 5, 5, 0}, // CLOSED SUBSET
1079 { 0x2AD0, Infix, 5, 5, 0}, // CLOSED SUPERSET
1080 { 0x2AD1, Infix, 5, 5, 0}, // CLOSED SUBSET OR EQUAL TO
1081 { 0x2AD2, Infix, 5, 5, 0}, // CLOSED SUPERSET OR EQUAL TO
1082 { 0x2AD3, Infix, 5, 5, 0}, // SUBSET ABOVE SUPERSET
1083 { 0x2AD4, Infix, 5, 5, 0}, // SUPERSET ABOVE SUBSET
1084 { 0x2AD5, Infix, 5, 5, 0}, // SUBSET ABOVE SUBSET
1085 { 0x2AD6, Infix, 5, 5, 0}, // SUPERSET ABOVE SUPERSET
1086 { 0x2AD7, Infix, 5, 5, 0}, // SUPERSET BESIDE SUBSET
1087 { 0x2AD8, Infix, 5, 5, 0}, // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET
1088 { 0x2AD9, Infix, 5, 5, 0}, // ELEMENT OF OPENING DOWNWARDS
1089 { 0x2ADA, Infix, 5, 5, 0}, // PITCHFORK WITH TEE TOP
1090 { 0x2ADB, Infix, 5, 5, 0}, // TRANSVERSAL INTERSECTION
1091 { 0x2ADD, Infix, 5, 5, 0}, // NONFORKING
1092 { 0x2ADE, Infix, 5, 5, 0}, // SHORT LEFT TACK
1093 { 0x2ADF, Infix, 5, 5, 0}, // SHORT DOWN TACK
1094 { 0x2AE0, Infix, 5, 5, 0}, // SHORT UP TACK
1095 { 0x2AE1, Infix, 5, 5, 0}, // PERPENDICULAR WITH S
1096 { 0x2AE2, Infix, 5, 5, 0}, // VERTICAL BAR TRIPLE RIGHT TURNSTILE
1097 { 0x2AE3, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR LEFT TURNSTILE
1098 { 0x2AE4, Infix, 5, 5, 0}, // VERTICAL BAR DOUBLE LEFT TURNSTILE
1099 { 0x2AE5, Infix, 5, 5, 0}, // DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
1100 { 0x2AE6, Infix, 5, 5, 0}, // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
1101 { 0x2AE7, Infix, 5, 5, 0}, // SHORT DOWN TACK WITH OVERBAR
1102 { 0x2AE8, Infix, 5, 5, 0}, // SHORT UP TACK WITH UNDERBAR
1103 { 0x2AE9, Infix, 5, 5, 0}, // SHORT UP TACK ABOVE SHORT DOWN TACK
1104 { 0x2AEA, Infix, 5, 5, 0}, // DOUBLE DOWN TACK
1105 { 0x2AEB, Infix, 5, 5, 0}, // DOUBLE UP TACK
1106 { 0x2AEC, Infix, 5, 5, 0}, // DOUBLE STROKE NOT SIGN
1107 { 0x2AED, Infix, 5, 5, 0}, // REVERSED DOUBLE STROKE NOT SIGN
1108 { 0x2AEE, Infix, 5, 5, 0}, // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
1109 { 0x2AEF, Infix, 5, 5, 0}, // VERTICAL LINE WITH CIRCLE ABOVE
1110 { 0x2AF0, Infix, 5, 5, 0}, // VERTICAL LINE WITH CIRCLE BELOW
1111 { 0x2AF1, Infix, 5, 5, 0}, // DOWN TACK WITH CIRCLE BELOW
1112 { 0x2AF2, Infix, 5, 5, 0}, // PARALLEL WITH HORIZONTAL STROKE
1113 { 0x2AF3, Infix, 5, 5, 0}, // PARALLEL WITH TILDE OPERATOR
1114 { 0x2AF4, Infix, 4, 4, 0}, // TRIPLE VERTICAL BAR BINARY RELATION
1115 { 0x2AF5, Infix, 4, 4, 0}, // TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE
1116 { 0x2AF6, Infix, 4, 4, 0}, // TRIPLE COLON OPERATOR
1117 { 0x2AF7, Infix, 5, 5, 0}, // TRIPLE NESTED LESS-THAN
1118 { 0x2AF8, Infix, 5, 5, 0}, // TRIPLE NESTED GREATER-THAN
1119 { 0x2AF9, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
1120 { 0x2AFA, Infix, 5, 5, 0}, // DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
1121 { 0x2AFB, Infix, 4, 4, 0}, // TRIPLE SOLIDUS BINARY RELATION
1122 { 0x2AFC, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // LARGE TRIPLE VERTICAL BAR OPERATOR
1123 { 0x2AFD, Infix, 4, 4, 0}, // DOUBLE SOLIDUS OPERATOR
1124 { 0x2AFE, Infix, 3, 3, 0}, // WHITE VERTICAL BAR
1125 { 0x2AFF, Prefix, 1, 2, Symmetric | LargeOp | MovableLimits}, // N-ARY WHITE VERTICAL BAR
1126 { 0x2B45, Infix, 5, 5, Stretchy}, // LEFTWARDS QUADRUPLE ARROW
1127 { 0x2B46, Infix, 5, 5, Stretchy} // RIGHTWARDS QUADRUPLE ARROW
1130 // A list of operators that stretch in the horizontal direction. This has been generated from Mozilla's MathML operator dictionary.
1131 inline UChar ExtractKeyHorizontal(const UChar* entry) { return *entry; }
1132 static const UChar horizontalOperators[] = {
1133 0x003D, 0x005E, 0x005F, 0x007E, 0x00AF, 0x02C6, 0x02C7, 0x02C9, 0x02CD, 0x02DC, 0x02F7, 0x0302, 0x0332, 0x203E, 0x20D0, 0x20D1, 0x20D6, 0x20D7, 0x20E1, 0x2190, 0x2192, 0x2194, 0x2198, 0x2199, 0x219C, 0x219D, 0x219E, 0x21A0, 0x21A2, 0x21A3, 0x21A4, 0x21A6, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21B4, 0x21B9, 0x21BC, 0x21BD, 0x21C0, 0x21C1, 0x21C4, 0x21C6, 0x21C7, 0x21C9, 0x21CB, 0x21CC, 0x21D0, 0x21D2, 0x21D4, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21E0, 0x21E2, 0x21E4, 0x21E5, 0x21E6, 0x21E8, 0x21F0, 0x21F6, 0x21FD, 0x21FE, 0x21FF, 0x23B4, 0x23B5, 0x23DC, 0x23DD, 0x23DE, 0x23DF, 0x23E0, 0x23E1, 0x2500, 0x27F5, 0x27F6, 0x27F7, 0x27F8, 0x27F9, 0x27FA, 0x27FB, 0x27FC, 0x27FD, 0x27FE, 0x27FF, 0x290C, 0x290D, 0x290E, 0x290F, 0x2910, 0x294E, 0x2950, 0x2952, 0x2953, 0x2956, 0x2957, 0x295A, 0x295B, 0x295E, 0x295F, 0x2B45, 0x2B46, 0xFE35, 0xFE36, 0xFE37, 0xFE38
1138 RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, PassRef<RenderStyle> style)
1139 : RenderMathMLToken(element, std::move(style))
1140 , m_stretchHeightAboveBaseline(0)
1141 , m_stretchDepthBelowBaseline(0)
1143 , m_isVertical(true)
1145 updateTokenContent();
1148 RenderMathMLOperator::RenderMathMLOperator(Document& document, PassRef<RenderStyle> style, const String& operatorString, MathMLOperatorDictionary::Form form, MathMLOperatorDictionary::Flag flag)
1149 : RenderMathMLToken(document, std::move(style))
1150 , m_stretchHeightAboveBaseline(0)
1151 , m_stretchDepthBelowBaseline(0)
1153 , m_isVertical(true)
1154 , m_operatorForm(form)
1155 , m_operatorFlags(flag)
1157 updateTokenContent(operatorString);
1160 void RenderMathMLOperator::setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag flag, const QualifiedName& name)
1162 ASSERT(!isFencedOperator());
1163 const AtomicString& attributeValue = element().fastGetAttribute(name);
1164 if (attributeValue == "true")
1165 m_operatorFlags |= flag;
1166 else if (attributeValue == "false")
1167 m_operatorFlags &= ~flag;
1168 // We ignore absent or invalid attributes.
1171 void RenderMathMLOperator::setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry* entry)
1173 // If this operator has been created by RenderMathMLFenced, we preserve the Fence and Separator properties.
1174 if (isFencedOperator())
1175 m_operatorFlags = (m_operatorFlags & (MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator)) | entry->flags;
1177 m_operatorFlags = entry->flags;
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;
1184 void RenderMathMLOperator::SetOperatorProperties()
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));
1189 // We determine the form of the operator.
1190 bool explicitForm = true;
1191 if (!isFencedOperator()) {
1192 const AtomicString& form = element().fastGetAttribute(MathMLNames::formAttr);
1193 if (form == "prefix")
1194 m_operatorForm = MathMLOperatorDictionary::Prefix;
1195 else if (form == "infix")
1196 m_operatorForm = MathMLOperatorDictionary::Infix;
1197 else if (form == "postfix")
1198 m_operatorForm = MathMLOperatorDictionary::Postfix;
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;
1207 m_operatorForm = MathMLOperatorDictionary::Infix;
1211 // We determine the default values of the operator properties.
1213 // First we initialize with the default values for unknown operators.
1214 if (isFencedOperator())
1215 m_operatorFlags &= MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator; // This resets all but the Fence and Separator properties.
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".
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)
1234 m_operatorForm = entry->form; // We override the form previously determined.
1235 setOperatorPropertiesFromOpDictEntry(entry);
1239 #undef MATHML_OPDICT_SIZE
1241 if (!isFencedOperator()) {
1242 // Finally, we make the attribute values override the default.
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);
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).
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);
1262 bool RenderMathMLOperator::isChildAllowed(const RenderObject&, const RenderStyle&) const
1267 void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
1269 if (!m_isVertical || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline))
1272 m_stretchHeightAboveBaseline = heightAboveBaseline;
1273 m_stretchDepthBelowBaseline = depthBelowBaseline;
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;
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();
1289 if (size < m_minSize)
1290 aspect = float(m_minSize) / size;
1291 else if (m_maxSize < size)
1292 aspect = float(m_maxSize) / size;
1294 m_stretchHeightAboveBaseline *= aspect;
1295 m_stretchDepthBelowBaseline *= aspect;
1299 void RenderMathMLOperator::stretchTo(LayoutUnit width)
1301 if (m_isVertical || m_stretchWidth == width)
1304 m_stretchWidth = width;
1306 SetOperatorProperties();
1311 FloatRect RenderMathMLOperator::boundsForGlyph(const GlyphData& data) const
1313 return data.fontData->boundsForGlyph(data.glyph);
1316 float RenderMathMLOperator::heightForGlyph(const GlyphData& data) const
1318 return boundsForGlyph(data).height();
1321 float RenderMathMLOperator::advanceForGlyph(const GlyphData& data) const
1323 return data.fontData->widthForGlyph(data.glyph);
1326 void RenderMathMLOperator::computePreferredLogicalWidths()
1328 ASSERT(preferredLogicalWidthsDirty());
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;
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;
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();
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);
1362 m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
1365 void RenderMathMLOperator::rebuildTokenContent(const String& operatorString)
1367 // We collapse the whitespace and replace the hyphens by minus signs.
1368 AtomicString textContent = operatorString.stripWhiteSpace().simplifyWhiteSpace().replace(hyphenMinus, minusSign).impl();
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).
1373 toRenderElement(firstChild())->destroy();
1374 createWrapperIfNeeded();
1375 RenderPtr<RenderText> text = createRenderer<RenderText>(document(), textContent);
1376 toRenderElement(firstChild())->addChild(text.leakPtr());
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();
1384 setNeedsLayoutAndPrefWidthsRecalc();
1387 void RenderMathMLOperator::updateTokenContent(const String& operatorString)
1389 ASSERT(isFencedOperator());
1390 rebuildTokenContent(operatorString);
1393 void RenderMathMLOperator::updateTokenContent()
1395 ASSERT(!isFencedOperator());
1396 rebuildTokenContent(element().textContent());
1399 void RenderMathMLOperator::updateFromElement()
1401 SetOperatorProperties();
1402 RenderMathMLToken::updateFromElement();
1405 void RenderMathMLOperator::updateOperatorProperties()
1407 SetOperatorProperties();
1410 setNeedsLayoutAndPrefWidthsRecalc();
1413 bool RenderMathMLOperator::shouldAllowStretching() const
1415 return m_operator && (hasOperatorFlag(MathMLOperatorDictionary::Stretchy) || isLargeOperatorInDisplayStyle());
1418 bool RenderMathMLOperator::getGlyphAssemblyFallBack(Vector<OpenTypeMathData::AssemblyPart> assemblyParts, StretchyData& stretchyData) const
1421 GlyphData extension;
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
1429 // We count the number of non extender pieces.
1430 int nonExtenderCount = 0;
1431 for (auto& part : assemblyParts) {
1432 if (!part.isExtender)
1435 if (nonExtenderCount > 3)
1436 return false; // This is not supported: there are too many pieces.
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
1447 extension.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.
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.
1461 // We ignore left/bottom piece and multiple successive extenders.
1463 } else if (state == 3) {
1464 // We ignore middle piece and multiple successive extenders.
1466 } else if (state >= 5)
1467 return false; // This is not supported: we got an unexpected extender.
1472 // We copy the left/bottom part.
1473 bottom.glyph = part.glyph;
1478 if (state == 2 || state == 3) {
1479 // We copy the middle part.
1480 middle.glyph = part.glyph;
1485 if (state == 4 || state == 5) {
1486 // We copy the right/top part.
1487 top.glyph = part.glyph;
1492 if (!extension.glyph)
1493 return false; // This is not supported: we always assume that we have an extension glyph.
1495 // If we don't have top/bottom glyphs, we use the extension glyph.
1497 top.glyph = extension.glyph;
1499 bottom.glyph = extension.glyph;
1501 top.fontData = style().font().primaryFont();
1502 extension.fontData = top.fontData;
1503 bottom.fontData = top.fontData;
1505 middle.fontData = top.fontData;
1507 stretchyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1512 RenderMathMLOperator::StretchyData RenderMathMLOperator::getDisplayStyleLargeOperator(UChar character) const
1516 ASSERT(m_isVertical && isLargeOperatorInDisplayStyle());
1518 const auto& primaryFontData = style().font().primaryFont();
1519 GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
1520 if (!primaryFontData || !primaryFontData->mathData() || baseGlyph.fontData != primaryFontData)
1523 Vector<Glyph> sizeVariants;
1524 Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
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));
1529 primaryFontData->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
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)
1543 RenderMathMLOperator::StretchyData RenderMathMLOperator::findStretchyData(UChar character, float* maximumGlyphWidth)
1545 ASSERT(!maximumGlyphWidth || m_isVertical);
1548 StretchyData assemblyData;
1550 const auto& primaryFontData = style().font().primaryFont();
1551 GlyphData baseGlyph = style().font().glyphDataForCharacter(character, !style().isLeftToRightDirection());
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));
1565 data.setSizeVariantMode(sizeVariant);
1566 float size = m_isVertical ? heightForGlyph(sizeVariant) : advanceForGlyph(sizeVariant);
1567 if (size >= stretchSize())
1572 // We verify if there is a construction.
1573 if (!getGlyphAssemblyFallBack(assemblyParts, assemblyData))
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;
1593 // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
1594 if (!stretchyCharacter)
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);
1602 if (stretchyCharacter->middleChar)
1603 middle = style().font().glyphDataForCharacter(stretchyCharacter->middleChar, false);
1604 assemblyData.setGlyphAssemblyMode(top, extension, bottom, middle);
1607 ASSERT(assemblyData.mode() == DrawGlyphAssembly);
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;
1619 // We ensure that the size is large enough to avoid glyph overlaps.
1622 size = heightForGlyph(assemblyData.top()) + heightForGlyph(assemblyData.bottom());
1623 if (assemblyData.middle().glyph)
1624 size += heightForGlyph(assemblyData.middle());
1626 size = advanceForGlyph(assemblyData.left()) + advanceForGlyph(assemblyData.right());
1627 if (assemblyData.middle().glyph)
1628 size += advanceForGlyph(assemblyData.middle());
1630 if (size > stretchSize())
1633 return assemblyData;
1636 void RenderMathMLOperator::updateStyle()
1638 FontCachePurgePreventer fontCachePurgePreventer;
1640 ASSERT(firstChild());
1644 m_stretchyData.setNormalMode();
1645 // We add spacing around the operator.
1646 // FIXME: The spacing should be added to the whole embellished operator (https://bugs.webkit.org/show_bug.cgi?id=124831).
1647 // FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing).
1648 const auto& wrapper = toRenderElement(firstChild());
1649 auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
1650 newStyle.get().setMarginStart(Length(m_leadingSpace, Fixed));
1651 newStyle.get().setMarginEnd(Length(m_trailingSpace, Fixed));
1652 wrapper->setStyle(std::move(newStyle));
1653 wrapper->setNeedsLayoutAndPrefWidthsRecalc();
1655 if (!shouldAllowStretching())
1658 if (m_isVertical && isLargeOperatorInDisplayStyle())
1659 m_stretchyData = getDisplayStyleLargeOperator(m_operator);
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)
1666 m_stretchyData = findStretchyData(m_operator, nullptr);
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();
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;
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;
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());
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());
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());
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());
1719 int RenderMathMLOperator::firstLineBaseline() const
1721 if (m_stretchyData.mode() != DrawNormal)
1722 return m_stretchHeightAboveBaseline;
1723 return RenderMathMLToken::firstLineBaseline();
1726 void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
1728 if (m_stretchyData.mode() != DrawNormal)
1729 logicalHeight = m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline;
1730 RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
1733 LayoutRect RenderMathMLOperator::paintGlyph(PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
1735 FloatRect glyphBounds = boundsForGlyph(data);
1737 LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
1738 glyphPaintRect.setY(origin.y() + glyphBounds.y());
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;
1746 glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
1747 clipBounds.shiftYEdgeTo(glyphPaintRect.y());
1750 glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
1751 clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
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());
1762 glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
1763 clipBounds.shiftXEdgeTo(glyphPaintRect.x());
1766 glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
1767 clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
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());
1778 // Clipping the enclosing IntRect avoids any potential issues at joined edges.
1779 GraphicsContextStateSaver stateSaver(*info.context);
1780 info.context->clip(clipBounds);
1783 buffer.add(data.glyph, data.fontData, advanceForGlyph(data));
1784 info.context->drawGlyphs(style().font(), *data.fontData, buffer, 0, 1, origin);
1786 return glyphPaintRect;
1789 void RenderMathMLOperator::fillWithVerticalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1791 ASSERT(m_isVertical);
1792 ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1793 ASSERT(m_stretchyData.extension().glyph);
1794 ASSERT(from.y() <= to.y());
1796 // If there is no space for the extension glyph, we don't need to do anything.
1797 if (from.y() == to.y())
1800 GraphicsContextStateSaver stateSaver(*info.context);
1802 FloatRect glyphBounds = boundsForGlyph(m_stretchyData.extension());
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);
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());
1816 while (lastPaintedGlyphRect.maxY() < to.y()) {
1817 lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimTopAndBottom);
1818 glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
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())
1827 void RenderMathMLOperator::fillWithHorizontalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
1829 ASSERT(!m_isVertical);
1830 ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1831 ASSERT(m_stretchyData.extension().glyph);
1832 ASSERT(from.x() <= to.x());
1834 // If there is no space for the extension glyph, we don't need to do anything.
1835 if (from.x() == to.x())
1838 GraphicsContextStateSaver stateSaver(*info.context);
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);
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());
1852 while (lastPaintedGlyphRect.maxX() < to.x()) {
1853 lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimLeftAndRight);
1854 glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
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())
1863 void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
1865 RenderMathMLToken::paint(info, paintOffset);
1867 if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || m_stretchyData.mode() == DrawNormal)
1870 GraphicsContextStateSaver stateSaver(*info.context);
1871 info.context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace());
1873 if (m_stretchyData.mode() == DrawSizeVariant) {
1874 ASSERT(m_stretchyData.variant().glyph);
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);
1885 paintVerticalGlyphAssembly(info, paintOffset);
1887 paintHorizontalGlyphAssembly(info, paintOffset);
1890 void RenderMathMLOperator::paintVerticalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1892 ASSERT(m_isVertical);
1893 ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1894 ASSERT(m_stretchyData.top().glyph);
1895 ASSERT(m_stretchyData.bottom().glyph);
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);
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);
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));
1916 LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimTopAndBottom);
1917 fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
1918 fillWithVerticalExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1920 fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
1923 void RenderMathMLOperator::paintHorizontalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
1925 ASSERT(!m_isVertical);
1926 ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
1927 ASSERT(m_stretchyData.left().glyph);
1928 ASSERT(m_stretchyData.right().glyph);
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);
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);
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());
1949 fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
1952 void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
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())
1957 RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);