From a29a0a1e5c499aea52ffd41c29c74289efcfe7ec Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 5 May 2024 21:01:44 -0700 Subject: [PATCH 1/4] pkg/macos: add more CoreText APIs --- pkg/macos/text/font.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/macos/text/font.zig b/pkg/macos/text/font.zig index c26a0f32f..32189f809 100644 --- a/pkg/macos/text/font.zig +++ b/pkg/macos/text/font.zig @@ -51,6 +51,10 @@ pub const Font = opaque { return @ptrCast(@constCast(c.CTFontCopyFeatures(@ptrCast(self)))); } + pub fn copyDefaultCascadeListForLanguages(self: *Font) *foundation.Array { + return @ptrCast(@constCast(c.CTFontCopyDefaultCascadeListForLanguages(@ptrCast(self), null))); + } + pub fn getGlyphCount(self: *Font) usize { return @intCast(c.CTFontGetGlyphCount(@ptrCast(self))); } From 1189817dd8f2d4c3026ad40a127d360d863297b4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 5 May 2024 21:38:19 -0700 Subject: [PATCH 2/4] pkg/macos: CTFontCreateForString --- pkg/macos/text/font.zig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/macos/text/font.zig b/pkg/macos/text/font.zig index 32189f809..e0c1f4891 100644 --- a/pkg/macos/text/font.zig +++ b/pkg/macos/text/font.zig @@ -18,6 +18,18 @@ pub const Font = opaque { ) orelse Allocator.Error.OutOfMemory; } + pub fn createForString( + self: *Font, + str: *foundation.String, + range: foundation.Range, + ) ?*Font { + return @ptrCast(@constCast(c.CTFontCreateForString( + @ptrCast(self), + @ptrCast(str), + @bitCast(range), + ))); + } + pub fn copyWithAttributes( self: *Font, size: f32, From 7c5d829274dd850b2f8464b701e82dc0af6baa87 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 7 May 2024 15:05:34 -0700 Subject: [PATCH 3/4] font: use CoreText API for CJK unified ideographs --- src/font/CodepointResolver.zig | 2 +- src/font/discovery.zig | 98 +++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/font/CodepointResolver.zig b/src/font/CodepointResolver.zig index 370d933cb..a0073f9ad 100644 --- a/src/font/CodepointResolver.zig +++ b/src/font/CodepointResolver.zig @@ -172,7 +172,7 @@ pub fn getIndex( if (self.discover) |disco| discover: { const load_opts = self.collection.load_options orelse break :discover; - var disco_it = disco.discover(alloc, .{ + var disco_it = disco.discoverFallback(alloc, &self.collection, .{ .codepoint = cp, .size = load_opts.size.points, .bold = style == .bold or style == .bold_italic, diff --git a/src/font/discovery.zig b/src/font/discovery.zig index 0259200c9..17e30ee61 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -5,6 +5,7 @@ const assert = std.debug.assert; const fontconfig = @import("fontconfig"); const macos = @import("macos"); const options = @import("main.zig").options; +const Collection = @import("main.zig").Collection; const DeferredFace = @import("main.zig").DeferredFace; const Variation = @import("main.zig").face.Variation; @@ -258,7 +259,11 @@ pub const Fontconfig = struct { /// Discover fonts from a descriptor. This returns an iterator that can /// be used to build up the deferred fonts. - pub fn discover(self: *const Fontconfig, alloc: Allocator, desc: Descriptor) !DiscoverIterator { + pub fn discover( + self: *const Fontconfig, + alloc: Allocator, + desc: Descriptor, + ) !DiscoverIterator { _ = alloc; // Build our pattern that we'll search for @@ -282,6 +287,16 @@ pub const Fontconfig = struct { }; } + pub fn discoverFallback( + self: *const CoreText, + alloc: Allocator, + collection: *Collection, + desc: Descriptor, + ) !DiscoverIterator { + _ = collection; + return try self.discover(alloc, desc); + } + pub const DiscoverIterator = struct { config: *fontconfig.Config, pattern: *fontconfig.Pattern, @@ -364,6 +379,87 @@ pub const CoreText = struct { }; } + pub fn discoverFallback( + self: *const CoreText, + alloc: Allocator, + collection: *Collection, + desc: Descriptor, + ) !DiscoverIterator { + // If we have a codepoint within the CJK unified ideographs block + // then we fallback to macOS to find a font that supports it because + // there isn't a better way manually with CoreText that I can find that + // properly takes into account system locale. + // + // References: + // - http://unicode.org/charts/PDF/U4E00.pdf + // - https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/fonts/LocaleInFonts.md#unified-han-ideographs + if (desc.codepoint >= 0x4E00 and + desc.codepoint <= 0x9FFF) + han: { + const han = try self.discoverCodepoint( + collection, + desc, + ) orelse break :han; + + // This is silly but our discover iterator needs a slice so + // we allocate here. This isn't a performance bottleneck but + // this is something we can optimize very easily... + const list = try alloc.alloc(*macos.text.FontDescriptor, 1); + errdefer alloc.free(list); + list[0] = han; + + return DiscoverIterator{ + .alloc = alloc, + .list = list, + .i = 0, + }; + } + + return try self.discover(alloc, desc); + } + + /// Discover a font for a specific codepoint using the CoreText + /// CTFontCreateForString API. + fn discoverCodepoint( + self: *const CoreText, + collection: *Collection, + desc: Descriptor, + ) !?*macos.text.FontDescriptor { + _ = self; + + assert(desc.codepoint > 0); + + // Get our original font. This is dependent on the requestd style + // from the descriptor. + const original = original: { + break :original try collection.getFace(.{ .style = .regular }); + }; + + // We need it in utf8 format + var buf: [4]u8 = undefined; + const len = try std.unicode.utf8Encode( + @intCast(desc.codepoint), + &buf, + ); + + // We need a CFString + const str = try macos.foundation.String.createWithBytes( + buf[0..len], + .utf8, + false, + ); + defer str.release(); + + // Get our font + const font = original.font.createForString( + str, + macos.foundation.Range.init(0, 1), + ) orelse return null; + defer font.release(); + + // Get the descriptor + return font.copyDescriptor(); + } fn copyMatchingDescriptors( alloc: Allocator, list: *macos.foundation.Array, From 84095025d5eedc666c2f06ed9af246055d299acf Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 7 May 2024 15:10:53 -0700 Subject: [PATCH 4/4] font: fontconfig has proper function --- src/font/discovery.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/font/discovery.zig b/src/font/discovery.zig index 17e30ee61..9cfbe4a05 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -288,7 +288,7 @@ pub const Fontconfig = struct { } pub fn discoverFallback( - self: *const CoreText, + self: *const Fontconfig, alloc: Allocator, collection: *Collection, desc: Descriptor,