font/freetype: convert encoding of font names (#8204)

Freetype encodes some font names internally in formats other than UTF-8.
This only affects debug logs but it was annoying me so I fixed it. There
may be other encodings that might need to be dealt with but I took care
of the one that I ran across.
pull/8209/head
Mitchell Hashimoto 2025-08-10 19:55:35 -07:00 committed by GitHub
commit edd73fa0e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 40 additions and 5 deletions

View File

@ -156,23 +156,58 @@ pub const Face = struct {
/// but sometimes allocation isn't required and a static string is
/// returned.
pub fn name(self: *const Face, buf: []u8) Allocator.Error![]const u8 {
// We don't use this today but its possible the table below
// returns UTF-16 in which case we'd want to use this for conversion.
_ = buf;
const count = self.face.getSfntNameCount();
// We look for the font family entry.
for (0..count) |i| {
const entry = self.face.getSfntName(i) catch continue;
if (entry.name_id == freetype.c.TT_NAME_ID_FONT_FAMILY) {
return entry.string[0..entry.string_len];
const string = entry.string[0..entry.string_len];
// There are other encodings that are something other than UTF-8
// but this is one we've seen "in the wild" so far.
if (entry.platform_id == freetype.c.TT_PLATFORM_MICROSOFT and entry.encoding_id == freetype.c.TT_MS_ID_UNICODE_CS) skip: {
if (string.len % 2 != 0) break :skip;
if (string.len > 1024) break :skip;
var tmp: [512]u16 = undefined;
const max = string.len / 2;
for (@as([]const u16, @alignCast(@ptrCast(string))), 0..) |c, j| tmp[j] = @byteSwap(c);
const len = std.unicode.utf16LeToUtf8(buf, tmp[0..max]) catch return string;
return buf[0..len];
}
return string;
}
}
return "";
}
test "face name" {
const embedded = @import("../embedded.zig");
var lib: Library = try .init(testing.allocator);
defer lib.deinit();
{
var face: Face = try .init(lib, embedded.variable, .{ .size = .{ .points = 14 } });
defer face.deinit();
var buf: [1024]u8 = undefined;
const actual = try face.name(&buf);
try testing.expectEqualStrings("JetBrains Mono", actual);
}
{
var face: Face = try .init(lib, embedded.inconsolata, .{ .size = .{ .points = 14 } });
defer face.deinit();
var buf: [1024]u8 = undefined;
const actual = try face.name(&buf);
try testing.expectEqualStrings("Inconsolata", actual);
}
}
/// Return a new face that is the same as this but also has synthetic
/// bold applied.
pub fn syntheticBold(self: *const Face, opts: font.face.Options) !Face {