#include "vega.h" typedef struct { uint32_t first, last; } Range; /* Zero-width: combining marks, Hangul vowel and final jamo, joiners, * variation selectors, format characters. */ static const Range zero[] = { {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, {0x0610, 0x061A}, {0x064B, 0x065F}, {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x1160, 0x11FF}, {0x1AB0, 0x1AFF}, {0x1DC0, 0x1DFF}, {0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2064}, {0x20D0, 0x20FF}, {0x302A, 0x302F}, {0x3099, 0x309A}, {0xD7B0, 0xD7FF}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0xFEFF, 0xFEFF}, {0x1F3FB, 0x1F3FF}, {0xE0000, 0xE0FFF}, }; /* East Asian Wide and Fullwidth, and emoji shown as pictures. */ static const Range wide[] = { {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x303E}, {0x3041, 0x33FF}, {0x3400, 0x4DBF}, {0x4E00, 0x9FFF}, {0xA000, 0xA4CF}, {0xA960, 0xA97F}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE6F}, {0xFF00, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x17000, 0x18CFF}, {0x1B000, 0x1B2FF}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F251}, {0x1F300, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F7E0, 0x1F7EB}, {0x1F90C, 0x1F9FF}, {0x1FA70, 0x1FAFF}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, }; static bool in(const Range *ranges, size_t count, uint32_t codepoint) { size_t low = 0, high = count; while (low < high) { size_t middle = (low + high) / 2; if (codepoint < ranges[middle].first) high = middle; else if (codepoint > ranges[middle].last) low = middle + 1; else return true; } return false; } unsigned vega_width(uint32_t codepoint) { if (in(zero, sizeof(zero) / sizeof(*zero), codepoint)) return 0; return in(wide, sizeof(wide) / sizeof(*wide), codepoint) ? 2 : 1; } uint32_t vega_visible(uint32_t codepoint) { if (codepoint < 0x20) return 0x2400 + codepoint; if (codepoint == 0x7F) return 0x2421; if (codepoint >= 0x80 && codepoint < 0xA0) return 0xFFFD; return codepoint; } size_t vega_decode(const char *text, size_t length, size_t at, uint32_t *codepoint) { const unsigned char *p = (const unsigned char *)text + at; size_t left = length - at, need = p[0] < 0x80 ? 1 : p[0] < 0xE0 ? 2 : p[0] < 0xF0 ? 3 : 4; if (need > left) { *codepoint = 0xFFFD; return left; } uint32_t value = need == 1 ? p[0] : need == 2 ? p[0] & 0x1Fu : need == 3 ? p[0] & 0x0Fu : p[0] & 0x07u; for (size_t i = 1; i < need; ++i) value = value << 6 | (p[i] & 0x3Fu); *codepoint = value; return need; } unsigned vega_text_width(const char *text, size_t length) { unsigned width = 0; for (size_t at = 0; at < length;) { uint32_t codepoint; at += vega_decode(text, length, at, &codepoint); width += vega_width(vega_visible(codepoint)); } return width; }