diff --git a/pkg/harfbuzz/face.zig b/pkg/harfbuzz/face.zig new file mode 100644 index 000000000..2fd3605e8 --- /dev/null +++ b/pkg/harfbuzz/face.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const c = @import("c.zig"); + +/// A font face is an object that represents a single face from within a font family. +/// +/// More precisely, a font face represents a single face in a binary font file. +/// Font faces are typically built from a binary blob and a face index. +/// Font faces are used to create fonts. +pub const Face = struct { + handle: *c.hb_face_t, + + /// Decreases the reference count on a face object. When the reference + /// count reaches zero, the face is destroyed, freeing all memory. + pub fn destroy(self: *Face) void { + c.hb_face_destroy(self.handle); + } +}; diff --git a/pkg/harfbuzz/font.zig b/pkg/harfbuzz/font.zig new file mode 100644 index 000000000..259274446 --- /dev/null +++ b/pkg/harfbuzz/font.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const c = @import("c.zig"); +const Face = @import("face.zig").Face; +const Error = @import("errors.zig").Error; + +pub const Font = struct { + handle: *c.hb_font_t, + + /// Constructs a new font object from the specified face. + pub fn create(face: Face) Error!Font { + const handle = c.hb_font_create(face.handle) orelse return Error.HarfbuzzFailed; + return Font{ .handle = handle }; + } + + /// Decreases the reference count on the given font object. When the + /// reference count reaches zero, the font is destroyed, freeing all memory. + pub fn destroy(self: *Font) void { + c.hb_font_destroy(self.handle); + } +}; diff --git a/pkg/harfbuzz/freetype.zig b/pkg/harfbuzz/freetype.zig new file mode 100644 index 000000000..da33107ab --- /dev/null +++ b/pkg/harfbuzz/freetype.zig @@ -0,0 +1,73 @@ +const freetype = @import("freetype"); +const std = @import("std"); +const c = @import("c.zig"); +const Face = @import("face.zig").Face; +const Font = @import("font.zig").Font; +const Error = @import("errors.zig").Error; + +/// Creates an hb_face_t face object from the specified FT_Face. +/// +/// This is the preferred variant of the hb_ft_face_create* function +/// family, because it calls FT_Reference_Face() on ft_face , ensuring +/// that ft_face remains alive as long as the resulting hb_face_t face +/// object remains alive. Also calls FT_Done_Face() when the hb_face_t +/// face object is destroyed. +/// +/// Use this version unless you know you have good reasons not to. +pub fn createFace(face: freetype.c.FT_Face) Error!Face { + const handle = c.hb_ft_face_create_referenced( + @ptrCast(c.FT_Face, face), + ) orelse return Error.HarfbuzzFailed; + return Face{ .handle = handle }; +} + +/// Creates an hb_font_t font object from the specified FT_Face. +pub fn createFont(face: freetype.c.FT_Face) Error!Font { + const handle = c.hb_ft_font_create_referenced( + @ptrCast(c.FT_Face, face), + ) orelse return Error.HarfbuzzFailed; + return Font{ .handle = handle }; +} + +/// Configures the font-functions structure of the specified hb_font_t font +/// object to use FreeType font functions. +/// +/// In particular, you can use this function to configure an existing +/// hb_face_t face object for use with FreeType font functions even if that +/// hb_face_t face object was initially created with hb_face_create(), and +/// therefore was not initially configured to use FreeType font functions. +/// +/// An hb_face_t face object created with hb_ft_face_create() is preconfigured +/// for FreeType font functions and does not require this function to be used. +pub fn setFontFuncs(font: Font) void { + c.hb_ft_font_set_funcs(font.handle); +} + +test { + const testing = std.testing; + const testFont = @import("test.zig").fontRegular; + const ftc = freetype.c; + const ftok = ftc.FT_Err_Ok; + + var ft_lib: ftc.FT_Library = undefined; + if (ftc.FT_Init_FreeType(&ft_lib) != ftok) + return error.FreeTypeInitFailed; + defer _ = ftc.FT_Done_FreeType(ft_lib); + + var ft_face: ftc.FT_Face = undefined; + try testing.expect(ftc.FT_New_Memory_Face( + ft_lib, + testFont, + @intCast(c_long, testFont.len), + 0, + &ft_face, + ) == ftok); + defer _ = ftc.FT_Done_Face(ft_face); + + var face = try createFace(ft_face); + defer face.destroy(); + + var font = try createFont(ft_face); + defer font.destroy(); + setFontFuncs(font); +} diff --git a/pkg/harfbuzz/main.zig b/pkg/harfbuzz/main.zig index 6959edc2f..12894b16d 100644 --- a/pkg/harfbuzz/main.zig +++ b/pkg/harfbuzz/main.zig @@ -1,7 +1,10 @@ pub const c = @import("c.zig"); pub usingnamespace @import("blob.zig"); pub usingnamespace @import("errors.zig"); +pub usingnamespace @import("face.zig"); +pub usingnamespace @import("font.zig"); pub usingnamespace @import("version.zig"); +pub const Freetype = @import("freetype.zig"); test { @import("std").testing.refAllDecls(@This()); diff --git a/pkg/harfbuzz/res/FiraCode.ttf b/pkg/harfbuzz/res/FiraCode.ttf new file mode 100755 index 000000000..bd7368519 Binary files /dev/null and b/pkg/harfbuzz/res/FiraCode.ttf differ diff --git a/pkg/harfbuzz/test.zig b/pkg/harfbuzz/test.zig new file mode 100644 index 000000000..64837a1e1 --- /dev/null +++ b/pkg/harfbuzz/test.zig @@ -0,0 +1 @@ +pub const fontRegular = @embedFile("res/FiraCode.ttf");