diff --git a/src/render/ttf.zig b/src/render/ttf.zig index fa0d571..5d53dfd 100644 --- a/src/render/ttf.zig +++ b/src/render/ttf.zig @@ -861,25 +861,41 @@ pub const TtfFont = struct { var cx = x; const baseline_y = y + self.ascent(); - for (text) |c| { - if (c == '\n') continue; - if (c == ' ') { - const metrics = self.getGlyphMetrics(c); + // UTF-8 iteration: decode codepoints instead of iterating bytes + var i: usize = 0; + while (i < text.len) { + // Get UTF-8 sequence length + const cp_len = std.unicode.utf8ByteSequenceLength(text[i]) catch { + i += 1; // Skip invalid byte + continue; + }; + if (i + cp_len > text.len) break; // Not enough bytes + + // Decode codepoint + const cp: u32 = std.unicode.utf8Decode(text[i..][0..cp_len]) catch { + i += cp_len; + continue; + }; + i += cp_len; + + if (cp == '\n') continue; + if (cp == ' ') { + const metrics = self.getGlyphMetrics(cp); cx += @intCast(metrics.advance); continue; } // Try to get cached glyph or rasterize - const cache_key = makeCacheKey(c, self.render_size); + const cache_key = makeCacheKey(cp, self.render_size); if (self.glyph_cache.get(cache_key)) |cached| { // Draw cached glyph self.drawGlyphBitmap(fb, cx, baseline_y, cached, color, clip); - const metrics = self.getGlyphMetrics(c); + const metrics = self.getGlyphMetrics(cp); cx += @intCast(metrics.advance); } else { // Rasterize and cache - const glyph_index = self.getGlyphIndex(c); + const glyph_index = self.getGlyphIndex(cp); if (self.getGlyphOutline(glyph_index)) |outline| { defer { var outline_copy = outline; @@ -895,9 +911,9 @@ pub const TtfFont = struct { .height = @intCast(bitmap.height), .bearing_x = @intCast(bitmap.bearing_x), .bearing_y = @intCast(bitmap.bearing_y), - .advance = self.getGlyphMetrics(c).advance, + .advance = self.getGlyphMetrics(cp).advance, }, - .codepoint = c, + .codepoint = cp, }; self.glyph_cache.put(cache_key, cached_glyph) catch {}; @@ -905,7 +921,7 @@ pub const TtfFont = struct { } } - const metrics = self.getGlyphMetrics(c); + const metrics = self.getGlyphMetrics(cp); cx += @intCast(metrics.advance); } }