Skip to content

Commit

Permalink
Merge pull request #2501 from ghostty-org/push-zzksspuskpmx
Browse files Browse the repository at this point in the history
font/coretext: use CTFontCreateForString for final codepoint fallback
  • Loading branch information
mitchellh authored Oct 26, 2024
2 parents 734c8ce + 1aa932f commit ffc4c6e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
4 changes: 4 additions & 0 deletions pkg/macos/text/font.zig
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ pub const Font = opaque {
return @ptrFromInt(@intFromPtr(c.CTFontCopyDisplayName(@ptrCast(self))));
}

pub fn copyPostScriptName(self: *Font) *foundation.String {
return @ptrFromInt(@intFromPtr(c.CTFontCopyPostScriptName(@ptrCast(self))));
}

pub fn getSymbolicTraits(self: *Font) text.FontSymbolicTraits {
return @bitCast(c.CTFontGetSymbolicTraits(@ptrCast(self)));
}
Expand Down
56 changes: 54 additions & 2 deletions src/font/discovery.zig
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,30 @@ pub const CoreText = struct {
};
}

return try self.discover(alloc, desc);
const it = try self.discover(alloc, desc);

// If our normal discovery doesn't find anything and we have a specific
// codepoint, then fallback to using CTFontCreateForString to find a
// matching font CoreText wants to use. See:
// https://github.com/ghostty-org/ghostty/issues/2499
if (it.list.len == 0 and desc.codepoint > 0) codepoint: {
const ct_desc = try self.discoverCodepoint(
collection,
desc,
) orelse break :codepoint;

const list = try alloc.alloc(*macos.text.FontDescriptor, 1);
errdefer alloc.free(list);
list[0] = ct_desc;

return DiscoverIterator{
.alloc = alloc,
.list = list,
.i = 0,
};
}

return it;
}

/// Discover a font for a specific codepoint using the CoreText
Expand Down Expand Up @@ -491,16 +514,45 @@ pub const CoreText = struct {
);
defer str.release();

// Get our range length for CTFontCreateForString. It looks like
// the range uses UTF-16 codepoints and not UTF-32 codepoints.
const range_len: usize = range_len: {
var unichars: [2]u16 = undefined;
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(
desc.codepoint,
&unichars,
);
break :range_len if (pair) 2 else 1;
};

// Get our font
const font = original.font.createForString(
str,
macos.foundation.Range.init(0, 1),
macos.foundation.Range.init(0, range_len),
) orelse return null;
defer font.release();

// Do not allow the last resort font to go through. This is the
// last font used by CoreText if it can't find anything else and
// only contains replacement characters.
last_resort: {
const name_str = font.copyPostScriptName();
defer name_str.release();

// If the name doesn't fit in our buffer, then it can't
// be the last resort font so we break out.
var name_buf: [64]u8 = undefined;
const name: []const u8 = name_str.cstring(&name_buf, .utf8) orelse
break :last_resort;

// If the name is "LastResort" then we don't want to use it.
if (std.mem.eql(u8, "LastResort", name)) return null;
}

// Get the descriptor
return font.copyDescriptor();
}

fn copyMatchingDescriptors(
alloc: Allocator,
list: *macos.foundation.Array,
Expand Down

0 comments on commit ffc4c6e

Please sign in to comment.