diff --git a/pkg/macos/text/font.zig b/pkg/macos/text/font.zig index ca4405594..4a23d7202 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 copyWithAttributes(self: *Font, size: f32, attrs: ?*text.FontDescriptor) Allocator.Error!*Font { + return @intToPtr( + ?*Font, + @ptrToInt(c.CTFontCreateCopyWithAttributes( + @ptrCast(c.CTFontRef, self), + size, + null, + @ptrCast(c.CTFontDescriptorRef, attrs), + )), + ) orelse Allocator.Error.OutOfMemory; + } + pub fn release(self: *Font) void { c.CFRelease(self); } @@ -129,3 +141,16 @@ test { ); } } + +test "copy" { + const name = try foundation.String.createWithBytes("Monaco", .utf8, false); + defer name.release(); + const desc = try text.FontDescriptor.createWithNameAndSize(name, 12); + defer desc.release(); + + const font = try Font.createWithFontDescriptor(desc, 12); + defer font.release(); + + const f2 = try font.copyWithAttributes(14, null); + defer f2.release(); +} diff --git a/src/font/face.zig b/src/font/face.zig index 835d2bc7a..e8e679894 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -31,3 +31,9 @@ pub const DesiredSize = struct { return (self.points * self.ydpi) / 72; } }; + +pub const Foo = if (options.backend == .coretext) coretext.Face else void; + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 7dad78b90..8bbcae356 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -1 +1,47 @@ -// One day! +const macos = @import("macos"); +const harfbuzz = @import("harfbuzz"); +const font = @import("../main.zig"); + +pub const Face = struct { + /// Our font face + font: *macos.text.Font, + + /// Harfbuzz font corresponding to this face. + hb_font: harfbuzz.Font, + + /// 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 + /// adjusted to the final size for final load. + pub fn initFontCopy(base: *macos.text.Font, size: font.face.DesiredSize) !Face { + // Create a copy + const ct_font = try base.copyWithAttributes(@intToFloat(f32, size.points), null); + errdefer ct_font.release(); + + const hb_font = try harfbuzz.coretext.createFont(ct_font); + errdefer hb_font.destroy(); + + return Face{ + .font = ct_font, + .hb_font = hb_font, + }; + } + + pub fn deinit(self: *Face) void { + self.font.release(); + self.hb_font.destroy(); + self.* = undefined; + } +}; + +test { + const name = try macos.foundation.String.createWithBytes("Monaco", .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(); +} diff --git a/src/font/main.zig b/src/font/main.zig index 95f773c16..69e6a9431 100644 --- a/src/font/main.zig +++ b/src/font/main.zig @@ -34,6 +34,15 @@ pub const Backend = enum { /// CoreText for both font discovery and rendering (macOS). coretext, + + /// Helper that just returns true if we should be using freetype. This + /// is used for tests. + pub fn freetype(self: Backend) bool { + return switch (self) { + .freetype, .fontconfig_freetype => true, + .coretext => false, + }; + } }; /// The styles that a family can take.