runeWidth function
Returns the display width of a single Unicode code point.
Control characters, combining marks, and zero-width characters return 0. CJK, fullwidth, and emoji characters return 2. All others return 1.
Implementation
int runeWidth(int rune) {
// Control characters and null
if (rune < 32 || (rune >= 0x7F && rune < 0xA0)) {
return 0;
}
// Combining characters (zero width)
if ((rune >= 0x0300 && rune <= 0x036F) || // Combining Diacritical Marks
(rune >= 0x1AB0 &&
rune <= 0x1AFF) || // Combining Diacritical Marks Extended
(rune >= 0x1DC0 &&
rune <= 0x1DFF) || // Combining Diacritical Marks Supplement
(rune >= 0x20D0 &&
rune <= 0x20FF) || // Combining Diacritical Marks for Symbols
(rune >= 0xFE20 && rune <= 0xFE2F)) {
// Combining Half Marks
return 0;
}
// Zero-width (very small subset).
if (rune == 0x200B || // ZWSP
rune == 0x200C || // ZWNJ
rune == 0x200D || // ZWJ
rune == 0xFEFF || // BOM
(rune >= 0xFE00 && rune <= 0xFE0F) || // Variation Selectors (VS1-VS16)
(rune >= 0xE0100 && rune <= 0xE01EF)) {
// Variation Selectors Supplement (VS17-VS256)
return 0;
}
// Wide characters (CJK + emoji range subset).
if ((rune >= 0x1100 && rune <= 0x115F) || // Hangul Jamo
(rune >= 0x2E80 && rune <= 0x9FFF) || // CJK
(rune >= 0xAC00 && rune <= 0xD7A3) || // Hangul Syllables
(rune >= 0xF900 && rune <= 0xFAFF) || // CJK Compatibility
(rune >= 0xFE10 && rune <= 0xFE1F) || // Vertical Forms
(rune >= 0xFE30 && rune <= 0xFE6F) || // CJK Compatibility Forms
(rune >= 0xFF00 && rune <= 0xFF60) || // Fullwidth ASCII
(rune >= 0xFFE0 && rune <= 0xFFE6) || // Fullwidth symbols
(rune >= 0x20000 && rune <= 0x3FFFF)) {
return 2;
}
// Emoji / symbol characters that terminals render as wide (2 cells).
// These are below U+2E80 (the CJK range start) but have emoji presentation
// in most terminals.
if (_isEmojiPresentation(rune)) {
return emojiPresentationWidth;
}
// Emoji ranges in Supplementary Multilingual Plane (U+1Fxxx).
// Covers all Emoji_Presentation=Yes code points above U+FFFF.
// Source: Unicode Emoji Data 15.1 — emoji-data.txt
if ((rune >= 0x1F000 && rune <= 0x1F02F) || // Mahjong Tiles, Dominos
(rune >= 0x1F0A0 && rune <= 0x1F0FF) || // Playing Cards
(rune >= 0x1F100 &&
rune <= 0x1F1FF) || // Enclosed Alphanumerics + Regional Indicators
(rune >= 0x1F200 && rune <= 0x1F2FF) || // Enclosed Ideographic Supplement
(rune >= 0x1F300 &&
rune <= 0x1F9FF) || // Misc Symbols/Pictographs, Emoticons, etc.
(rune >= 0x1FA00 &&
rune <= 0x1FAFF) || // Symbols and Pictographs Extended-A
(rune >= 0x1F7E0 && rune <= 0x1F7FF)) {
// Geometric Shapes Extended (colored circles/squares)
return emojiPresentationWidth;
}
return 1;
}