diff --git a/pkg/macos/text/font.zig b/pkg/macos/text/font.zig index 4a23d7202..6ba10f058 100644 --- a/pkg/macos/text/font.zig +++ b/pkg/macos/text/font.zig @@ -89,6 +89,12 @@ pub const Font = opaque { @ptrToInt(c.CTFontCopyDisplayName(@ptrCast(c.CTFontRef, self))), ); } + + pub fn getSymbolicTraits(self: *Font) text.FontSymbolicTraits { + return @bitCast(text.FontSymbolicTraits, c.CTFontGetSymbolicTraits( + @ptrCast(c.CTFontRef, self), + )); + } }; pub const FontOrientation = enum(c_uint) { @@ -108,6 +114,12 @@ test { const font = try Font.createWithFontDescriptor(desc, 12); defer font.release(); + // Traits + { + const traits = font.getSymbolicTraits(); + try testing.expect(!traits.color_glyphs); + } + var glyphs = [1]graphics.Glyph{0}; try testing.expect(font.getGlyphsForCharacters( &[_]u16{'A'}, diff --git a/src/font/DeferredFace.zig b/src/font/DeferredFace.zig index 19585be57..a3131d558 100644 --- a/src/font/DeferredFace.zig +++ b/src/font/DeferredFace.zig @@ -111,7 +111,13 @@ pub fn load( }, .coretext => { - try self.loadCoreText(lib, size); + // It is possible to use CoreText with Freetype so we support + // both here. + switch (font.Face) { + font.face.freetype.Face => try self.loadCoreTextFreetype(lib, size), + else => unreachable, + } + return; }, @@ -136,7 +142,7 @@ fn loadFontconfig( self.face = try Face.initFile(lib, filename, face_index, size); } -fn loadCoreText( +fn loadCoreTextFreetype( self: *DeferredFace, lib: Library, size: font.face.DesiredSize, diff --git a/src/font/face.zig b/src/font/face.zig index e8e679894..f3b4c47d9 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const options = @import("main.zig").options; -const freetype = @import("face/freetype.zig"); -const coretext = @import("face/coretext.zig"); +pub const freetype = @import("face/freetype.zig"); +pub const coretext = @import("face/coretext.zig"); /// Face implementation for the compile options. pub const Face = switch (options.backend) { diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 8bbcae356..3c73ce80f 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const macos = @import("macos"); const harfbuzz = @import("harfbuzz"); const font = @import("../main.zig"); @@ -9,6 +10,9 @@ pub const Face = struct { /// Harfbuzz font corresponding to this face. hb_font: harfbuzz.Font, + /// The presentation for this font. + presentation: font.Presentation, + /// Initialize a CoreText-based face from another initialized font face /// but with a new size. This is often how CoreText fonts are initialized /// because the font is loaded at a default size during discovery, and then @@ -21,9 +25,12 @@ pub const Face = struct { const hb_font = try harfbuzz.coretext.createFont(ct_font); errdefer hb_font.destroy(); + const traits = ct_font.getSymbolicTraits(); + return Face{ .font = ct_font, .hb_font = hb_font, + .presentation = if (traits.color_glyphs) .emoji else .text, }; } @@ -35,6 +42,8 @@ pub const Face = struct { }; test { + const testing = std.testing; + const name = try macos.foundation.String.createWithBytes("Monaco", .utf8, false); defer name.release(); const desc = try macos.text.FontDescriptor.createWithNameAndSize(name, 12); @@ -44,4 +53,22 @@ test { var face = try Face.initFontCopy(ct_font, .{ .points = 18 }); defer face.deinit(); + + try testing.expectEqual(font.Presentation.text, face.presentation); +} + +test "emoji" { + const testing = std.testing; + + const name = try macos.foundation.String.createWithBytes("Apple Color Emoji", .utf8, false); + defer name.release(); + const desc = try macos.text.FontDescriptor.createWithNameAndSize(name, 12); + defer desc.release(); + const ct_font = try macos.text.Font.createWithFontDescriptor(desc, 12); + defer ct_font.release(); + + var face = try Face.initFontCopy(ct_font, .{ .points = 18 }); + defer face.deinit(); + + try testing.expectEqual(font.Presentation.emoji, face.presentation); }