font: fallback search should search full discovery chain

Fixes #668

We were previously only checking the first font result in the search.
This also fixes our CoreText scoring algorithm to prioritize faces that
have the codepoint we're searching for.
This commit is contained in:
Mitchell Hashimoto
2023-10-13 12:25:27 -07:00
parent 00b7625694
commit 1411015038
3 changed files with 40 additions and 10 deletions

View File

@ -7,7 +7,7 @@ const text = @import("../text.zig");
const c = @import("c.zig"); const c = @import("c.zig");
pub const Font = opaque { pub const Font = opaque {
pub fn createWithFontDescriptor(desc: *text.FontDescriptor, size: f32) Allocator.Error!*Font { pub fn createWithFontDescriptor(desc: *const text.FontDescriptor, size: f32) Allocator.Error!*Font {
return @as( return @as(
?*Font, ?*Font,
@ptrFromInt(@intFromPtr(c.CTFontCreateWithFontDescriptor( @ptrFromInt(@intFromPtr(c.CTFontCreateWithFontDescriptor(

View File

@ -327,6 +327,7 @@ pub fn indexForCodepoint(
// If we are regular, try looking for a fallback using discovery. // If we are regular, try looking for a fallback using discovery.
if (style == .regular and font.Discover != void) { if (style == .regular and font.Discover != void) {
log.debug("searching for a fallback font for cp={x}", .{cp});
if (self.discover) |disco| discover: { if (self.discover) |disco| discover: {
var disco_it = disco.discover(self.alloc, .{ var disco_it = disco.discover(self.alloc, .{
.codepoint = cp, .codepoint = cp,
@ -337,11 +338,18 @@ pub fn indexForCodepoint(
}) catch break :discover; }) catch break :discover;
defer disco_it.deinit(); defer disco_it.deinit();
if (disco_it.next() catch break :discover) |face| { while (true) {
const face_ = disco_it.next() catch |err| {
log.warn("fallback search failed with error err={}", .{err});
break;
};
const face = face_ orelse break;
// Discovery is supposed to only return faces that have our // Discovery is supposed to only return faces that have our
// codepoint but we can't search presentation in discovery so // codepoint but we can't search presentation in discovery so
// we have to check it here. // we have to check it here.
if (face.hasCodepoint(cp, p)) { if (!face.hasCodepoint(cp, p)) continue;
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
log.info("found codepoint 0x{x} in fallback face={s}", .{ log.info("found codepoint 0x{x} in fallback face={s}", .{
cp, cp,
@ -349,7 +357,8 @@ pub fn indexForCodepoint(
}); });
return self.addFace(style, .{ .deferred = face }) catch break :discover; return self.addFace(style, .{ .deferred = face }) catch break :discover;
} }
}
log.debug("no fallback face found for cp={x}", .{cp});
} }
} }

View File

@ -399,6 +399,7 @@ pub const CoreText = struct {
style: Style = .unmatched, style: Style = .unmatched,
monospace: bool = false, monospace: bool = false,
codepoint: bool = false,
const Style = enum(u8) { unmatched = 0, match = 0xFF, _ }; const Style = enum(u8) { unmatched = 0, match = 0xFF, _ };
@ -410,6 +411,26 @@ pub const CoreText = struct {
fn score(desc: *const Descriptor, ct_desc: *const macos.text.FontDescriptor) Score { fn score(desc: *const Descriptor, ct_desc: *const macos.text.FontDescriptor) Score {
var score_acc: Score = .{}; var score_acc: Score = .{};
// If we're searching for a codepoint, prioritize fonts that
// have that codepoint.
if (desc.codepoint > 0) codepoint: {
const font = macos.text.Font.createWithFontDescriptor(ct_desc, 12) catch
break :codepoint;
defer font.release();
// Turn UTF-32 into UTF-16 for CT API
var unichars: [2]u16 = undefined;
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(
desc.codepoint,
&unichars,
);
const len: usize = if (pair) 2 else 1;
// Get our glyphs
var glyphs = [2]macos.graphics.Glyph{ 0, 0 };
score_acc.codepoint = font.getGlyphsForCharacters(unichars[0..len], glyphs[0..len]);
}
// Get our symbolic traits for the descriptor so we can compare // Get our symbolic traits for the descriptor so we can compare
// boolean attributes like bold, monospace, etc. // boolean attributes like bold, monospace, etc.
const symbolic_traits: macos.text.FontSymbolicTraits = traits: { const symbolic_traits: macos.text.FontSymbolicTraits = traits: {