// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #include namespace plot::render { // The decoder lives with the interface, not with a font, because it is what keeps // "what a string is worth" (`TextBackend::measure`) and "what a string draws" // (`TextBatch::add_text`) the same thing: both walk a label with this function, so // neither can disagree with the other about where a codepoint starts or how many // bytes a malformed sequence costs. u32 next_codepoint(const char*& p) noexcept { const auto b0 = static_cast(static_cast(p[0])); if (b0 < 0x80u) { ++p; return b0; } u32 codepoint = 0; u32 extra = 0; if ((b0 & 0xE0u) == 0xC0u) { codepoint = b0 & 0x1Fu; extra = 1u; } else if ((b0 & 0xF0u) == 0xE0u) { codepoint = b0 & 0x0Fu; extra = 2u; } else if ((b0 & 0xF8u) == 0xF0u) { codepoint = b0 & 0x07u; extra = 3u; } else { ++p; return kInvalidCodepoint; } for (u32 i = 1; i <= extra; ++i) { const auto b = static_cast(static_cast(p[i])); if ((b & 0xC0u) != 0x80u) { // truncated: stop before the terminator ++p; return kInvalidCodepoint; } codepoint = (codepoint << 6) | (b & 0x3Fu); } p += extra + 1u; constexpr u32 kMinForLength[4] = {0u, 0x80u, 0x800u, 0x10000u}; if (codepoint < kMinForLength[extra] || codepoint > 0x10FFFFu || (codepoint >= 0xD800u && codepoint <= 0xDFFFu)) { return kInvalidCodepoint; // overlong, out of range or a surrogate } return codepoint; } } // namespace plot::render