From c92ddd6cc22fe68095d12cd84d9c4e5033eb6891 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Sat, 17 May 2025 14:14:51 +0200 Subject: [PATCH] ttf: handle metrics for composite glyphs To enable glyph-to-character mapping, it is necessary to include information in GlyphMetrics indicating which indices of the original text each glyph corresponds to. Since characters encoded with multiple utf8 bytes still result in a single glyph, both the starting index in the utf8 string and the byte length must be stored. This allows accurate mapping between glyphs and their original text representation. --- inc/thorvg.h | 2 ++ src/loaders/ttf/tvgTtfLoader.cpp | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/inc/thorvg.h b/inc/thorvg.h index ed951dd9..bb0ca31c 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -297,6 +297,8 @@ struct GlyphMetrics float yOffset; float minw; float minh; + //for glyph<->text mapping: + uint32_t length; }; diff --git a/src/loaders/ttf/tvgTtfLoader.cpp b/src/loaders/ttf/tvgTtfLoader.cpp index c7627e36..c63b6064 100644 --- a/src/loaders/ttf/tvgTtfLoader.cpp +++ b/src/loaders/ttf/tvgTtfLoader.cpp @@ -198,6 +198,35 @@ static uint32_t* _codepoints(const char* text, size_t n) } +static uint32_t* _codepointsLength(const char* text, size_t n) { + auto length = tvg::malloc(sizeof(uint32_t) * (n + 1)); + + size_t i = 0; + auto p = text; + while(*p) { + if (!(*p & 0x80U)) { + length[i++] = 1; + ++p; + } else if ((*p & 0xe0U) == 0xc0U) { + length[i++] = 2; + p += 2; + } else if ((*p & 0xf0U) == 0xe0U) { + length[i++] = 3; + p += 3; + } else if ((*p & 0xf8U) == 0xf0U) { + length[i++] = 4; + p += 4; + } else { + tvg::free(length); + return nullptr; + } + } + length[i] = 0; + + return length; +} + + static void _scale(GlyphMetrics& gmetric, float scale) { gmetric.kerning.x *= scale; @@ -333,6 +362,8 @@ bool TtfLoader::metrics(char* text, float fontSize, GlyphMetrics** metrics, uint auto n = strlen(text); auto code = _codepoints(text, n); if (!code) return false; + auto length = _codepointsLength(text, n); + if (!length) return false; GlyphMetrics gmetrics; auto lglyph = INVALID_GLYPH, rglyph = INVALID_GLYPH; @@ -345,6 +376,7 @@ bool TtfLoader::metrics(char* text, float fontSize, GlyphMetrics** metrics, uint if ((rglyph = reader.glyph(code[idx], gmetrics, nullptr)) != INVALID_GLYPH) { if (lglyph != INVALID_GLYPH) reader.kerning(lglyph, rglyph, gmetrics.kerning); _scale(gmetrics, scale); + gmetrics.length = length[idx]; out.push(gmetrics); lglyph = rglyph; } @@ -352,6 +384,7 @@ bool TtfLoader::metrics(char* text, float fontSize, GlyphMetrics** metrics, uint } tvg::free(code); + tvg::free(length); *metrics = out.data; out.data = nullptr;