From af5f035830394d083fab9c6da4445e048bbc7808 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sun, 20 Sep 2026 15:42:19 +0200 Subject: [PATCH] media: stbtt kerning table iterator --- packages/media/src/stbtt.zig | 34 +++++++++++++++++++ packages/media/src/stbtt/stb_truetype.c | 43 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/packages/media/src/stbtt.zig b/packages/media/src/stbtt.zig index 5dd0df3..40ad53d 100644 --- a/packages/media/src/stbtt.zig +++ b/packages/media/src/stbtt.zig @@ -100,6 +100,12 @@ const import = struct { padding: u8, }; + const stbtt_kerning_table_iterator = extern struct { + data: [*]u8, + k: c_int, + length: c_int, + }; + extern fn stbtt_GetNumberOfFonts(data: [*]const u8) c_int; extern fn stbtt_GetFontOffsetForIndex(index: c_int, data: [*]const u8) c_int; extern fn stbtt_InitFont(info: *stbtt_fontinfo, data: [*]const u8, offset: c_int) c_int; @@ -117,6 +123,8 @@ const import = struct { extern fn stbtt_UnitsPerEm(info: *const stbtt_fontinfo) c_int; extern fn stbtt_GetFontCapHeight(info: *const stbtt_fontinfo, capHeight: ?*c_int) c_int; + extern fn stbtt_KerningTableIterator(info: *const stbtt_fontinfo, it: *stbtt_kerning_table_iterator) void; + extern fn stbtt_KerningTableNext(it: *stbtt_kerning_table_iterator, entry: *stbtt_kerningentry) c_int; }; pub fn initFont(self: *Self, data: []const u8) !*Font { @@ -212,6 +220,13 @@ pub const Font = opaque { return null; } + pub fn kerningTableIterator(self: *const Font) KerningTableIterator { + const info = @as(*const import.stbtt_fontinfo, @ptrCast(@alignCast(self))); + var it: import.stbtt_kerning_table_iterator = undefined; + import.stbtt_KerningTableIterator(info, &it); + return .{ .it = it }; + } + test "refAllDecls" { std.testing.refAllDecls(@This()); } @@ -229,6 +244,25 @@ pub const GlyphMetrics = struct { left_side_bearing: i16, }; +pub const KerningTableEntry = struct { + glyph_prev: u16, + glyph_next: u16, + advance: i16, +}; + +pub const KerningTableIterator = struct { + it: import.stbtt_kerning_table_iterator, + + pub fn next(self: *KerningTableIterator) ?KerningTableEntry { + var entry: import.stbtt_kerningentry = undefined; + return if (import.stbtt_KerningTableNext(&self.it, &entry) != 0) .{ + .glyph_prev = @intCast(entry.glyph1), + .glyph_next = @intCast(entry.glyph2), + .advance = @intCast(entry.advance), + } else null; + } +}; + // --- MALLOC INTERFACE -------------------------------------------------------- export fn castle_media_stbtt_malloc(self: *Self, size: usize) callconv(.c) VoidPtr { diff --git a/packages/media/src/stbtt/stb_truetype.c b/packages/media/src/stbtt/stb_truetype.c index 45b3952..09f983f 100644 --- a/packages/media/src/stbtt/stb_truetype.c +++ b/packages/media/src/stbtt/stb_truetype.c @@ -22,3 +22,46 @@ STBTT_DEF int stbtt_GetFontCapHeight(const stbtt_fontinfo *info, int *capHeight) if (capHeight) *capHeight = ttSHORT(info->data+tab + 88); return 1; } + +typedef struct stbtt_kerning_table_iterator { + stbtt_uint8 *data; + int k; + int length; +} stbtt_kerning_table_iterator; + +STBTT_DEF void stbtt_KerningTableIterator(const stbtt_fontinfo *info, stbtt_kerning_table_iterator *it) +{ + stbtt_uint8 *data = info->data + info->kern; + + it->data = data; + it->k = 0; + it->length = 0; + + if (!info->kern) + return; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return; + + it->length = ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_KerningTableNext(stbtt_kerning_table_iterator *it, stbtt_kerningentry *entry) +{ + stbtt_uint8 *data = it->data; + int k = it->k; + int length = it->length; + + if (k < length) + { + entry->glyph1 = ttUSHORT(data+18+(k*6)); + entry->glyph2 = ttUSHORT(data+20+(k*6)); + entry->advance = ttSHORT(data+22+(k*6)); + + it->k += 1; + return 1; + } + + return 0; +}