From 5f0daa23b9d9329ba053f9a328d6254191dc9e49 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 24 Aug 2024 20:51:26 -0700 Subject: [PATCH] font/freetype: synthetic italic --- pkg/freetype/face.zig | 19 +++++++++++++++++++ src/font/face/freetype.zig | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/freetype/face.zig b/pkg/freetype/face.zig index 2724aba40..367d409e5 100644 --- a/pkg/freetype/face.zig +++ b/pkg/freetype/face.zig @@ -14,6 +14,11 @@ pub const Face = struct { _ = c.FT_Done_Face(self.handle); } + /// Increment the counter of the face. + pub fn ref(self: Face) void { + _ = c.FT_Reference_Face(self.handle); + } + /// A macro that returns true whenever a face object contains some /// embedded bitmaps. See the available_sizes field of the FT_FaceRec structure. pub fn hasFixedSizes(self: Face) bool { @@ -170,6 +175,20 @@ pub const Face = struct { ); return intToError(res); } + + /// Set the transformation that is applied to glyph images when they are + /// loaded into a glyph slot through FT_Load_Glyph. + pub fn setTransform( + self: Face, + matrix: ?*const c.FT_Matrix, + delta: ?*const c.FT_Vector, + ) void { + c.FT_Set_Transform( + self.handle, + @constCast(@ptrCast(matrix)), + @constCast(@ptrCast(delta)), + ); + } }; /// An enumeration to specify indices of SFNT tables loaded and parsed by diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index a182f1657..d1e375647 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -37,6 +37,17 @@ pub const Face = struct { /// Set quirks.disableDefaultFontFeatures quirks_disable_default_font_features: bool = false, + /// Set to true to apply a synthetic italic to the face. + synthetic_italic: bool = false, + + /// The matrix applied to a regular font to create a synthetic italic. + const italic_matrix: freetype.c.FT_Matrix = .{ + .xx = 0x10000, + .xy = 0x044ED, // approx. tan(15) + .yx = 0, + .yy = 0x10000, + }; + /// Initialize a new font face with the given source in-memory. pub fn initFile(lib: Library, path: [:0]const u8, index: i32, opts: font.face.Options) !Face { const face = try lib.lib.initFace(path, index); @@ -119,6 +130,24 @@ pub const Face = struct { return ""; } + /// Return a new face that is the same as this but has a transformation + /// matrix applied to italicize it. + pub fn syntheticItalic(self: *const Face, opts: font.face.Options) !Face { + // Increase face ref count + self.face.ref(); + errdefer self.face.deinit(); + + var f = try initFace( + .{ .lib = self.lib }, + self.face, + opts, + ); + errdefer f.deinit(); + f.synthetic_italic = true; + + return f; + } + /// Resize the font in-place. If this succeeds, the caller is responsible /// for clearing any glyph caches, font atlas data, etc. pub fn setSize(self: *Face, opts: font.face.Options) !void { @@ -246,6 +275,12 @@ pub const Face = struct { ) !Glyph { const metrics = opts.grid_metrics orelse self.metrics; + // If we have synthetic italic, then we apply a transformation matrix. + // We have to undo this because synthetic italic works by increasing + // the ref count of the base face. + if (self.synthetic_italic) self.face.setTransform(&italic_matrix, null); + defer if (self.synthetic_italic) self.face.setTransform(null, null); + // If our glyph has color, we want to render the color try self.face.loadGlyph(glyph_index, .{ .render = true, @@ -265,7 +300,7 @@ pub const Face = struct { // This bitmap is blank. I've seen it happen in a font, I don't know why. // If it is empty, we just return a valid glyph struct that does nothing. - if (bitmap_ft.rows == 0) return Glyph{ + if (bitmap_ft.rows == 0) return .{ .width = 0, .height = 0, .offset_x = 0,