From 8920f45fd88ccd6424cb6f46838ce11863316fa5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 28 May 2024 09:48:01 -0700 Subject: [PATCH] font/freetype: API to load font table --- pkg/freetype/face.zig | 27 +++++++++++++++++++++++++++ pkg/freetype/main.zig | 1 + pkg/freetype/tag.zig | 17 +++++++++++++++++ src/font/face/freetype.zig | 21 +++++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 pkg/freetype/tag.zig diff --git a/pkg/freetype/face.zig b/pkg/freetype/face.zig index 2bd4a33c7..7b72d1b94 100644 --- a/pkg/freetype/face.zig +++ b/pkg/freetype/face.zig @@ -1,7 +1,9 @@ const std = @import("std"); +const Allocator = std.mem.Allocator; const c = @import("c.zig"); const errors = @import("errors.zig"); const Library = @import("Library.zig"); +const Tag = @import("tag.zig").Tag; const Error = errors.Error; const intToError = errors.intToError; @@ -102,6 +104,31 @@ pub const Face = struct { return if (intToError(res)) |_| name else |err| err; } + /// Load any SFNT font table into client memory. + pub fn loadSfntTable( + self: Face, + alloc: Allocator, + tag: Tag, + ) (Allocator.Error || Error)!?[]u8 { + const tag_u64: u64 = @intCast(@as(u32, @bitCast(tag))); + + // Get the length of the table in bytes + var len: c_ulong = 0; + var res = c.FT_Load_Sfnt_Table(self.handle, tag_u64, 0, null, &len); + _ = intToError(res) catch |err| return err; + + // If our length is zero we don't have a table. + if (len == 0) return null; + + // Allocate a buffer to hold the table and load it + const buf = try alloc.alloc(u8, len); + errdefer alloc.free(buf); + res = c.FT_Load_Sfnt_Table(self.handle, tag_u64, 0, buf.ptr, &len); + _ = intToError(res) catch |err| return err; + + return buf; + } + /// Retrieve the font variation descriptor for a font. pub fn getMMVar(self: Face) Error!*c.FT_MM_Var { var result: *c.FT_MM_Var = undefined; diff --git a/pkg/freetype/main.zig b/pkg/freetype/main.zig index 4adfeeaf4..bfa5e6bc1 100644 --- a/pkg/freetype/main.zig +++ b/pkg/freetype/main.zig @@ -4,6 +4,7 @@ pub const Library = @import("Library.zig"); pub usingnamespace @import("computations.zig"); pub usingnamespace @import("errors.zig"); pub usingnamespace @import("face.zig"); +pub usingnamespace @import("tag.zig"); test { @import("std").testing.refAllDecls(@This()); diff --git a/pkg/freetype/tag.zig b/pkg/freetype/tag.zig new file mode 100644 index 000000000..32dab2a01 --- /dev/null +++ b/pkg/freetype/tag.zig @@ -0,0 +1,17 @@ +/// FT_Tag +pub const Tag = packed struct(u32) { + d: u8, + c: u8, + b: u8, + a: u8, + + pub fn init(v: *const [4]u8) Tag { + return .{ .a = v[0], .b = v[1], .c = v[2], .d = v[3] }; + } + + /// Converts the ID to a string. The return value is only valid + /// for the lifetime of the self pointer. + pub fn str(self: Tag) [4]u8 { + return .{ self.a, self.b, self.c, self.d }; + } +}; diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index 858f14fca..33eca002c 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -631,6 +631,11 @@ pub const Face = struct { const div = @as(f32, @floatFromInt(mul)) / 64; return @ceil(div); } + + /// Copy the font table data for the given tag. + pub fn copyTable(self: Face, alloc: Allocator, tag: *const [4]u8) !?[]u8 { + return try self.face.loadSfntTable(alloc, freetype.Tag.init(tag)); + } }; test { @@ -763,3 +768,19 @@ test "mono to rgba" { // glyph 3 is mono in Noto _ = try ft_font.renderGlyph(alloc, &atlas, 3, .{}); } + +test "svg font table" { + const alloc = testing.allocator; + const testFont = @import("../test.zig").fontJuliaMono; + + var lib = try font.Library.init(); + defer lib.deinit(); + + var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }); + defer face.deinit(); + + const table = (try face.copyTable(alloc, "SVG ")).?; + defer alloc.free(table); + + try testing.expectEqual(430, table.len); +}