// Unicode character names, which no engine API gives: by rule where Unicode has one, else from UnicodeData.txt // WORDS REFS a character of REFS stands for the word at its place in WORDS · NAMES ALIASES runs, one a line: the first // code point in hex, then | between names · table runs into a Map · names alias short those Maps, built at first use // list words, with _ a space and - nothing · CYRILLIC GREEK DIGITS JAMO letter names · is a pattern on a code point // hex hex digits, four or more · index a variation selector's number, or 0 · ABBREVIATIONS runs of abbreviations export {nameOf, aliasOf, abbreviationOf}; const WORDS = "COMBINING|LATIN SMALL LETTER|CAPITAL LETTER|CYRILLIC SMALL LETTER|WITH|BELOW|QUOTATION MARK|DOUBLE|LATIN|CYRILLIC|CIRCUMFLEX|DIAERESIS|EMOJI MODIFIER FITZPATRICK|SIGN|ABOVE|GRAVE|CHARACTER|ACUTE|SEPARATOR|SPACE|VERTICAL LINE|RIGHT|DEVICE CONTROL|ACCENT|PUNCTUATION|INVERTED|MARK|TILDE|SINGLE|INTERLINEAR ANNOTATION|EXCLAMATION|SQUARE BRACKET|LEFT|LEFT-TO-RIGHT|RIGHT-TO-LEFT|TABULATION|REVERSED|LINE|ARROWHEAD|INFORMATION|QUESTION|VULGAR FRACTION|START OF".split("|"); const REFS = "abcdefghijklmnopqrstuvwxyz!#%&*+/:;<=>?@^_~"; // Unicode: copied for ASCII, Latin-1, combining marks, some Cyrillic, general punctuation and invisibles; never renamed const NAMES = `20 t|*!|g|NUMBERn|DOLLARn|PERCENTn|AMPERSAND|APOSTROPHE|/PARENTHESIS|vPARENTHESIS|ASTERISK|PLUSn|COMMA|HYPHEN-MINUS|FULL STOP|SOLIDUS 3A COLON|SEMICOLON|LESS-THANn|EQUALSn|GREATER-THANn|^!|COMMERCIAL AT 5B /+|REVERSE SOLIDUS|v+|kx|LOW>|px 7B /CURLY BRACKET|u|vCURLY BRACKET|# A0 NO-BREAKt|z*!|CENTn|POUNDn|CURRENCYn|YENn|BROKEN BAR|SECTIONn|l|COPYRIGHTn|FEMININE ORDINAL INDICATOR|LEFT-POINTINGhANGLEg|NOTn|SOFT HYPHEN|REGISTEREDn|MACRON|DEGREEn|PLUS-MINUSn|SUPERSCRIPT TWO|SUPERSCRIPT THREE|rx|MICROn|PILCROWn|MIDDLE DOT|CEDILLA|SUPERSCRIPT ONE|MASCULINE ORDINAL INDICATOR|RIGHT-POINTINGhANGLEg|_ONE QUARTER|_ONE HALF|_THREE QUARTERS|z^!|icAep|icAer|icAek|icAe#|icAel|icAeRINGo|icAE|icCeCEDILLA|icEep|icEer|icEek|icEel|icIep|icIer|icIek|icIel|icETH|icNe#|icOep|icOer|icOek|icOe#|icOel|MULTIPLICATIONn|icOeSTROKE|icUep|icUer|icUek|icUel|icYer|icTHORN|bSHARP S|bAep|bAer|bAek|bAe#|bAel|bAeRINGo|bAE|bCeCEDILLA|bEep|bEer|bEek|bEel|bIep|bIer|bIek|bIel|bETH|bNe#|bOep|bOer|bOek|bOe#|bOel|DIVISIONn|bOeSTROKE|bUep|bUer|bUek|bUel|bYer|bTHORN|bYel 300 apx|arx|akx|a#|aMACRON|aOVERLINE|aBREVE|aDOTo|al|aHOOKo|aRINGo|ahrx|aCARON|auo|ahuo|ahpx|aCANDRABINDU|azBREVE|aTURNED COMMAo|aCOMMAo|a=COMMAo|aCOMMAov|apxf|arxf|a/TACKf|avTACKf|a/ANGLEo|aHORN|a/HALF RINGf|aUP TACKf|aDOWN TACKf|aPLUSnf|aMINUSnf|aPALATALIZED HOOKf|aRETROFLEX HOOKf|aDOTf|alf|aRINGf|aCOMMAf|aCEDILLA|aOGONEK|auf|aBRIDGEf|azhARCHf|aCARONf|akxf|aBREVEf|azBREVEf|a#f|aMACRONf|aLOW>|ahLOW>|a#OVERLAY|aSHORT STROKE OVERLAY|aLONG STROKE OVERLAY|aSHORT SOLIDUS OVERLAY|aLONG SOLIDUS OVERLAY|avHALF RINGf|azBRIDGEf|aSQUAREf|aSEAGULLf|aXo|aVERTICAL#|ahOVERLINE|apTONE!|arTONE!|aGREEK PERISPOMENI|aGREEK KORONIS|aGREEK DIALYTIKA TONOS|aGREEK YPOGEGRAMMENI|aBRIDGEo|aEQUALSnf|ahuf|a/ANGLEf|aNOT#o|aHOMOTHETICo|aALMOST EQUAL TOo|a/vARROWf|aUPWARDS ARROWf|aGRAPHEME JOINER|av?o|a/HALF RINGo|aFERMATA|aXf|a/?f|av?f|av?AND UP?f|avHALF RINGo|aDOTov|aASTERISKf|ahRINGf|aZIGZAGo|ahBREVEf|ahBREVE|ahMACRON|ahMACRONf|ah#|ahzBREVE|ahRIGHTWARDS ARROWf|abA|abE|abI|abO|abU|abC|abD|abH|abM|abR|abT|abV|abX 400 jcIEep|jcIO|jcDJE|jcGJE|jcUKRAINIAN IE|jcDZE|jcBYELORUSSIAN-UKRAINIAN I|jcYI|jcJE|jcLJE|jcNJE|jcTSHE|jcKJE|jcIep|jcSHORT U|jcDZHE 450 dIEep|dIO|dDJE|dGJE|dUKRAINIAN IE|dDZE|dBYELORUSSIAN-UKRAINIAN I|dYI|dJE|dLJE|dNJE|dTSHE|dKJE|dIep|dSHORT U|dDZHE 490 jcGHEeUPTURN|dGHEeUPTURN 61C ARABIC LETTER! 115F HANGUL CHOSEONG FILLER|HANGUL JUNGSEONG FILLER 180E MONGOLIAN VOWELs 2000 EN QUAD|EM QUAD|ENt|EMt|THREE-PER-EMt|FOUR-PER-EMt|SIX-PER-EMt|FIGUREt|yt|THINt|HAIRt|ZERO WIDTHt|ZERO WIDTH NON-JOINER|ZERO WIDTH JOINER|:!|;!|HYPHEN|NON-BREAKING HYPHEN|FIGURE DASH|EN DASH|EM DASH|HORIZONTAL BAR|hu|hLOW>|/%g|v%g|%LOW-9g|%HIGH-REVERSED-9g|/hg|vhg|hLOW-9g|hHIGH-REVERSED-9g|DAGGER|hDAGGER|BULLET|TRIANGULAR BULLET|ONE DOT LEADER|TWO DOT LEADER|HORIZONTAL ELLIPSIS|HYPHENATION POINT|>s|PARAGRAPHs|:EMBEDDING|;EMBEDDING|POP DIRECTIONAL FORMATTING|:OVERRIDE|;OVERRIDE|NARROW NO-BREAKt|PER MILLEn|PER TEN THOUSANDn|PRIME|hPRIME|TRIPLE PRIME|=PRIME|=hPRIME|=TRIPLE PRIME|CARET|%LEFT-POINTING ANGLEg|%RIGHT-POINTING ANGLEg|REFERENCE!|h*!|INTERROBANG|OVERLINE|UNDERTIE|qTIE|CARET INSERTION POINT|ASTERISM|HYPHEN BULLET|FRACTION SLASH|/+eQUILL|v+eQUILL|h^!|^*!|*^!|TIRONIANnET|=PILCROWn|BLACK LEFTWARDS BULLET|BLACK RIGHTWARDS BULLET|LOW ASTERISK|=SEMICOLON|CLOSE UP|TWO ASTERISKS ALIGNED VERTICALLY|COMMERCIAL MINUSn|SWUNG DASH|zUNDERTIE|FLOWERy!|THREE DOTy|QUADRUPLE PRIME|FOUR DOTy|FIVE DOTy|TWO DOTy|FOUR DOT!|DOTTED CROSS|TRICOLON|VERTICAL FOUR DOTS|MEDIUM MATHEMATICALt|WORD JOINER|FUNCTION APPLICATION|INVISIBLE TIMES|INVISIBLEs|INVISIBLE PLUS||:ISOLATE|;ISOLATE|FIRST STRONG ISOLATE|POP DIRECTIONAL ISOLATE|INHIBIT SYMMETRIC SWAPPING|ACTIVATE SYMMETRIC SWAPPING|INHIBIT ARABIC FORM SHAPING|ACTIVATE ARABIC FORM SHAPING|NATIONAL DIGIT SHAPES|NOMINAL DIGIT SHAPES 20AC EUROn 20E3 aENCLOSING KEYCAP 2122 TRADE!n 2126 OHMn 212A KELVINn|ANGSTROMn 25CC DOTTED CIRCLE 2800 BRAILLE PATTERN BLANK 3000 IDEOGRAPHICt 3164 HANGUL FILLER FB01 iSMALL LIGATURE FI|iSMALL LIGATURE FL FEFF ZERO WIDTH NO-BREAKt FFA0 HALFWIDTH HANGUL FILLER FFF9 &ANCHOR|&s|&TERMINATOR|OBJECT REPLACEMENTq|REPLACEMENTq 1F3F4 WAVING BLACK FLAG 1F3FB mTYPE-1-2|mTYPE-3|mTYPE-4|mTYPE-5|mTYPE-6 E0001 LANGUAGE TAG E007F CANCEL TAG`; const ALIASES = `0 NULL|~HEADING|~TEXT|END OF TEXT|END OF TRANSMISSION|ENQUIRY|ACKNOWLEDGE|BELL|BACKSPACE|q<|>FEED|><|FORM FEED|CARRIAGE RETURN|SHIFT OUT|SHIFT IN|DATA LINK ESCAPE|wONE|wTWO|wTHREE|wFOUR|NEGATIVE ACKNOWLEDGE|SYNCHRONOUS IDLE|END OF TRANSMISSION BLOCK|CANCEL|END OF MEDIUM|SUBSTITUTE|ESCAPE|@sFOUR|@sTHREE|@sTWO|@sONE 7F DELETE|PADDINGq|HIGH OCTET PRESET|BREAK PERMITTED HERE|NO BREAK HERE|INDEX|NEXT>|~SELECTED AREA|END OF SELECTED AREA|qFORWARD|PARTIAL>BACKWARD|REVERSE>FEED|%SHIFT TWO|%SHIFT THREE|wSTRING|PRIVATE USE ONE|PRIVATE USE TWO|SET TRANSMIT STATE|CANCELq|MESSAGE WAITING|~GUARDED AREA|END OF GUARDED AREA|~STRING|%GRAPHICqINTRODUCER|%qINTRODUCER|CONTROL SEQUENCE INTRODUCER|STRING TERMINATOR|OPERATING SYSTEM COMMAND|PRIVACY MESSAGE|APPLICATION PROGRAM COMMAND`; function table(s) { const o = new Map(); for (const x of s.split("\n")) { const [start] = x.split(" ", 1), at = parseInt(start, 16); x.slice(start.length + 1).split("|").forEach((v, i) => { if (v) o.set(at + i, v.match(/[A-Z0-9-]+|[^ ]/g).map(k => WORDS[REFS.indexOf(k)] ?? k).join(" ")) }) } return o } let names, alias, short; const list = s => s.split(" ").map(k => k.replace("_", " ").replace("-", "")); const CYRILLIC = list("A BE VE GHE DE IE ZHE ZE I SHORT_I KA EL EM EN O PE ER ES TE U EF HA TSE CHE SHA SHCHA HARD_SIGN YERU SOFT_SIGN E YU YA"); const GREEK = list("ALPHA BETA GAMMA DELTA EPSILON ZETA ETA THETA IOTA KAPPA LAMDA MU NU XI OMICRON PI RHO FINAL_SIGMA SIGMA TAU UPSILON PHI CHI PSI OMEGA"); const DIGITS = list("ZERO ONE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE"); // Unicode §3.12: a syllable's index over AC00 is (L × 21 + V) × 28 + T, and its name joins these jamo short names const JAMO = ["G GG N D DD R M B BB S SS - J JJ C K T P H", "A AE YA YAE EO E YEO YE O WA WAE OE YO U WEO WE WI YU EU YI I", "- G GG GS N NJ NH D L LG LM LB LS LT LP LH M B BS S SS NG J C K T P H"].map(list); const is = (v, c) => v.test(String.fromCodePoint(c)); const hex = c => c.toString(16).toUpperCase().padStart(4, "0"); const index = c => c >= 0xfe00 && c <= 0xfe0f ? c - 0xfdff : c >= 0xe0100 && c <= 0xe01ef ? c - 0xe00ef : 0; function nameOf(cp) { const letter = (s, q, a, i) => `${s} ${q ? "SMALL" : "CAPITAL"} LETTER ${a[i]}`; if (cp >= 0x30 && cp <= 0x39) return "DIGIT " + DIGITS[cp-0x30]; if (cp >= 0x41 && cp <= 0x5a) return "LATIN CAPITAL LETTER " + String.fromCharCode(cp); if (cp >= 0x61 && cp <= 0x7a) return "LATIN SMALL LETTER " + String.fromCharCode(cp - 32); if (cp >= 0x410 && cp <= 0x44f) return letter("CYRILLIC", cp >= 0x430, CYRILLIC, (cp - 0x410) % 32); if ((cp >= 0x391 && cp <= 0x3a9 && cp !== 0x3a2) || (cp >= 0x3b1 && cp <= 0x3c9)) return letter("GREEK", cp >= 0x3b1, GREEK, (cp - 0x391) % 32); if (cp >= 0xac00 && cp <= 0xd7a3) { const i = cp - 0xac00; return "HANGUL SYLLABLE " + JAMO[0][Math.floor(i/588)] + JAMO[1][Math.floor(i/28)%21] + JAMO[2][i%28] } if (((cp >= 0xf900 && cp <= 0xfaff) || (cp >= 0x2f800 && cp <= 0x2fa1f)) && !is(/\p{Cn}/u, cp)) return "CJK COMPATIBILITY IDEOGRAPH-" + hex(cp); if (cp > 0x33ff && is(/\p{Unified_Ideograph}/u, cp)) return "CJK UNIFIED IDEOGRAPH-" + hex(cp); if (cp >= 0x1f1e6 && cp <= 0x1f1ff) return "REGIONAL INDICATOR SYMBOL LETTER " + String.fromCharCode(cp - 0x1f1a5); if (cp >= 0xe0020 && cp <= 0xe007e) return "TAG " + nameOf(cp - 0xe0000); if (index(cp)) return "VARIATION SELECTOR-" + index(cp); return (names ??= table(NAMES)).get(cp) ?? null } // Unicode: a control character has no name, only a formal alias function aliasOf(cp) { return (alias ??= table(ALIASES)).get(cp) ?? null } const ABBREVIATIONS = [ [0, "NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US SP"], [0x7f, "DEL PAD HOP BPH NBH IND NEL SSA ESA HTS HTJ VTS PLD PLU RI SS2 SS3 DCS PU1 PU2 STS CCH MW SPA EPA SOS SGC SCI CSI ST OSC PM APC NBSP"], [0xad, "SHY"], [0x34f, "CGJ"], [0x61c, "ALM"], [0x115f, "HCF HJF"], [0x180b, "FVS1 FVS2 FVS3 MVS FVS4"], [0x2000, "NQSP MQSP ENSP EMSP 3/MSP 4/MSP 6/MSP FSP PSP THSP HSP ZWSP ZWNJ ZWJ LRM RLM"], [0x2028, "LSEP PSEP LRE RLE PDF LRO RLO NNBSP"], [0x205f, "MMSP WJ"], [0x2066, "LRI RLI FSI PDI"], [0x3000, "IDSP"], [0x3164, "HF"], [0xfeff, "BOM"], [0xffa0, "HWHF"], [0xe0001, "LANG_TAG"], [0xe007f, "CANCEL_TAG"]]; function abbreviationOf(cp) { if (cp >= 0xe0020 && cp < 0xe007f) return "TAG " + (cp === 0xe0020 ? "SP" : String.fromCharCode(cp - 0xe0000)); if (index(cp)) return "VS" + index(cp); return (short ??= new Map(ABBREVIATIONS.flatMap(([i, s]) => list(s).map((k, j) => [i + j, k])))).get(cp) ?? null }