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