From 7af90914971c02be9dc63cac281c8c10878d82d5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 28 Aug 2022 17:07:27 -0700 Subject: [PATCH] pkg/freetype: Library and Face --- pkg/freetype/Face.zig | 13 + pkg/freetype/Library.zig | 85 +++++ pkg/freetype/errors.zig | 294 ++++++++++++++++++ pkg/freetype/main.zig | 8 + .../res/FiraCode-Regular.ttf} | Bin pkg/freetype/test.zig | 1 + pkg/harfbuzz/freetype.zig | 2 +- pkg/harfbuzz/test.zig | 1 - src/main.zig | 1 + 9 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 pkg/freetype/Face.zig create mode 100644 pkg/freetype/Library.zig create mode 100644 pkg/freetype/errors.zig rename pkg/{harfbuzz/res/FiraCode.ttf => freetype/res/FiraCode-Regular.ttf} (100%) create mode 100644 pkg/freetype/test.zig delete mode 100644 pkg/harfbuzz/test.zig diff --git a/pkg/freetype/Face.zig b/pkg/freetype/Face.zig new file mode 100644 index 000000000..ffea2d9c3 --- /dev/null +++ b/pkg/freetype/Face.zig @@ -0,0 +1,13 @@ +const Face = @This(); + +const std = @import("std"); +const c = @import("c.zig"); +const errors = @import("errors.zig"); +const Error = errors.Error; +const intToError = errors.intToError; + +handle: c.FT_Face, + +pub fn deinit(self: Face) void { + _ = c.FT_Done_Face(self.handle); +} diff --git a/pkg/freetype/Library.zig b/pkg/freetype/Library.zig new file mode 100644 index 000000000..175d2258c --- /dev/null +++ b/pkg/freetype/Library.zig @@ -0,0 +1,85 @@ +const Library = @This(); + +const std = @import("std"); +const c = @import("c.zig"); +const Face = @import("Face.zig"); +const errors = @import("errors.zig"); +const Error = errors.Error; +const intToError = errors.intToError; + +handle: c.FT_Library, + +/// Initialize a new FreeType library object. The set of modules that are +/// registered by this function is determined at build time. +pub fn init() Error!Library { + var res = Library{ .handle = undefined }; + try intToError(c.FT_Init_FreeType(&res.handle)); + return res; +} + +/// Destroy a given FreeType library object and all of its children, +/// including resources, drivers, faces, sizes, etc. +pub fn deinit(self: Library) void { + _ = c.FT_Done_FreeType(self.handle); +} + +/// Return the version of the FreeType library being used. This is useful when +/// dynamically linking to the library, since one cannot use the macros +/// FREETYPE_MAJOR, FREETYPE_MINOR, and FREETYPE_PATCH. +pub fn version(self: Library) Version { + var v: Version = undefined; + c.FT_Library_Version(self.handle, &v.major, &v.minor, &v.patch); + return v; +} + +/// Call FT_Open_Face to open a font that has been loaded into memory. +pub fn initMemoryFace(self: Library, data: []const u8, index: i32) Error!Face { + var face: Face = undefined; + try intToError(c.FT_New_Memory_Face( + self.handle, + data.ptr, + @intCast(c_long, data.len), + index, + &face.handle, + )); + return face; +} + +pub const Version = struct { + major: i32, + minor: i32, + patch: i32, + + /// Convert the version to a string. The buffer should be able to + /// accomodate the size, recommended to be at least 8 chars wide. + /// The returned slice will be a slice of buf that contains the full + /// version string. + pub fn toString(self: Version, buf: []u8) ![]const u8 { + return try std.fmt.bufPrint(buf, "{d}.{d}.{d}", .{ + self.major, self.minor, self.patch, + }); + } +}; + +test "basics" { + const testing = std.testing; + + var lib = try init(); + defer lib.deinit(); + + const vsn = lib.version(); + try testing.expect(vsn.major > 1); + + var buf: [32]u8 = undefined; + _ = try vsn.toString(&buf); +} + +test "loading memory font" { + const font_data = @import("test.zig").font_regular; + + var lib = try init(); + defer lib.deinit(); + + var face = try lib.initMemoryFace(font_data, 0); + defer face.deinit(); +} diff --git a/pkg/freetype/errors.zig b/pkg/freetype/errors.zig new file mode 100644 index 000000000..b97f40407 --- /dev/null +++ b/pkg/freetype/errors.zig @@ -0,0 +1,294 @@ +const c = @import("c.zig"); + +// Thanks to Mach (https://github.com/hexops/mach) for this work, I didn't +// do this manually. I wrote the other Freetype bindings by hand but this +// one was... too tedius. + +pub const Error = error{ + CannotOpenResource, + UnknownFileFormat, + InvalidFileFormat, + InvalidVersion, + LowerModuleVersion, + InvalidArgument, + UnimplementedFeature, + InvalidTable, + InvalidOffset, + ArrayTooLarge, + MissingModule, + MissingProperty, + InvalidGlyphIndex, + InvalidCharacterCode, + InvalidGlyphFormat, + CannotRenderGlyph, + InvalidOutline, + InvalidComposite, + TooManyHints, + InvalidPixelSize, + InvalidHandle, + InvalidLibraryHandle, + InvalidDriverHandle, + InvalidFaceHandle, + InvalidSizeHandle, + InvalidSlotHandle, + InvalidCharMapHandle, + InvalidCacheHandle, + InvalidStreamHandle, + TooManyDrivers, + TooManyExtensions, + OutOfMemory, + UnlistedObject, + CannotOpenStream, + InvalidStreamSeek, + InvalidStreamSkip, + InvalidStreamRead, + InvalidStreamOperation, + InvalidFrameOperation, + NestedFrameAccess, + InvalidFrameRead, + RasterUninitialized, + RasterCorrupted, + RasterOverflow, + RasterNegativeHeight, + TooManyCaches, + InvalidOpcode, + TooFewArguments, + StackOverflow, + CodeOverflow, + BadArgument, + DivideByZero, + InvalidReference, + DebugOpCode, + ENDFInExecStream, + NestedDEFS, + InvalidCodeRange, + ExecutionTooLong, + TooManyFunctionDefs, + TooManyInstructionDefs, + TableMissing, + HorizHeaderMissing, + LocationsMissing, + NameTableMissing, + CMapTableMissing, + HmtxTableMissing, + PostTableMissing, + InvalidHorizMetrics, + InvalidCharMapFormat, + InvalidPPem, + InvalidVertMetrics, + CouldNotFindContext, + InvalidPostTableFormat, + InvalidPostTable, + Syntax, + StackUnderflow, + Ignore, + NoUnicodeGlyphName, + MissingStartfontField, + MissingFontField, + MissingSizeField, + MissingFontboundingboxField, + MissingCharsField, + MissingStartcharField, + MissingEncodingField, + MissingBbxField, + BbxTooBig, + CorruptedFontHeader, + CorruptedFontGlyphs, +}; + +pub fn intToError(err: c_int) Error!void { + return switch (err) { + c.FT_Err_Ok => {}, + c.FT_Err_Cannot_Open_Resource => Error.CannotOpenResource, + c.FT_Err_Unknown_File_Format => Error.UnknownFileFormat, + c.FT_Err_Invalid_File_Format => Error.InvalidFileFormat, + c.FT_Err_Invalid_Version => Error.InvalidVersion, + c.FT_Err_Lower_Module_Version => Error.LowerModuleVersion, + c.FT_Err_Invalid_Argument => Error.InvalidArgument, + c.FT_Err_Unimplemented_Feature => Error.UnimplementedFeature, + c.FT_Err_Invalid_Table => Error.InvalidTable, + c.FT_Err_Invalid_Offset => Error.InvalidOffset, + c.FT_Err_Array_Too_Large => Error.ArrayTooLarge, + c.FT_Err_Missing_Module => Error.MissingModule, + c.FT_Err_Missing_Property => Error.MissingProperty, + c.FT_Err_Invalid_Glyph_Index => Error.InvalidGlyphIndex, + c.FT_Err_Invalid_Character_Code => Error.InvalidCharacterCode, + c.FT_Err_Invalid_Glyph_Format => Error.InvalidGlyphFormat, + c.FT_Err_Cannot_Render_Glyph => Error.CannotRenderGlyph, + c.FT_Err_Invalid_Outline => Error.InvalidOutline, + c.FT_Err_Invalid_Composite => Error.InvalidComposite, + c.FT_Err_Too_Many_Hints => Error.TooManyHints, + c.FT_Err_Invalid_Pixel_Size => Error.InvalidPixelSize, + c.FT_Err_Invalid_Handle => Error.InvalidHandle, + c.FT_Err_Invalid_Library_Handle => Error.InvalidLibraryHandle, + c.FT_Err_Invalid_Driver_Handle => Error.InvalidDriverHandle, + c.FT_Err_Invalid_Face_Handle => Error.InvalidFaceHandle, + c.FT_Err_Invalid_Size_Handle => Error.InvalidSizeHandle, + c.FT_Err_Invalid_Slot_Handle => Error.InvalidSlotHandle, + c.FT_Err_Invalid_CharMap_Handle => Error.InvalidCharMapHandle, + c.FT_Err_Invalid_Cache_Handle => Error.InvalidCacheHandle, + c.FT_Err_Invalid_Stream_Handle => Error.InvalidStreamHandle, + c.FT_Err_Too_Many_Drivers => Error.TooManyDrivers, + c.FT_Err_Too_Many_Extensions => Error.TooManyExtensions, + c.FT_Err_Out_Of_Memory => Error.OutOfMemory, + c.FT_Err_Unlisted_Object => Error.UnlistedObject, + c.FT_Err_Cannot_Open_Stream => Error.CannotOpenStream, + c.FT_Err_Invalid_Stream_Seek => Error.InvalidStreamSeek, + c.FT_Err_Invalid_Stream_Skip => Error.InvalidStreamSkip, + c.FT_Err_Invalid_Stream_Read => Error.InvalidStreamRead, + c.FT_Err_Invalid_Stream_Operation => Error.InvalidStreamOperation, + c.FT_Err_Invalid_Frame_Operation => Error.InvalidFrameOperation, + c.FT_Err_Nested_Frame_Access => Error.NestedFrameAccess, + c.FT_Err_Invalid_Frame_Read => Error.InvalidFrameRead, + c.FT_Err_Raster_Uninitialized => Error.RasterUninitialized, + c.FT_Err_Raster_Corrupted => Error.RasterCorrupted, + c.FT_Err_Raster_Overflow => Error.RasterOverflow, + c.FT_Err_Raster_Negative_Height => Error.RasterNegativeHeight, + c.FT_Err_Too_Many_Caches => Error.TooManyCaches, + c.FT_Err_Invalid_Opcode => Error.InvalidOpcode, + c.FT_Err_Too_Few_Arguments => Error.TooFewArguments, + c.FT_Err_Stack_Overflow => Error.StackOverflow, + c.FT_Err_Code_Overflow => Error.CodeOverflow, + c.FT_Err_Bad_Argument => Error.BadArgument, + c.FT_Err_Divide_By_Zero => Error.DivideByZero, + c.FT_Err_Invalid_Reference => Error.InvalidReference, + c.FT_Err_Debug_OpCode => Error.DebugOpCode, + c.FT_Err_ENDF_In_Exec_Stream => Error.ENDFInExecStream, + c.FT_Err_Nested_DEFS => Error.NestedDEFS, + c.FT_Err_Invalid_CodeRange => Error.InvalidCodeRange, + c.FT_Err_Execution_Too_Long => Error.ExecutionTooLong, + c.FT_Err_Too_Many_Function_Defs => Error.TooManyFunctionDefs, + c.FT_Err_Too_Many_Instruction_Defs => Error.TooManyInstructionDefs, + c.FT_Err_Table_Missing => Error.TableMissing, + c.FT_Err_Horiz_Header_Missing => Error.HorizHeaderMissing, + c.FT_Err_Locations_Missing => Error.LocationsMissing, + c.FT_Err_Name_Table_Missing => Error.NameTableMissing, + c.FT_Err_CMap_Table_Missing => Error.CMapTableMissing, + c.FT_Err_Hmtx_Table_Missing => Error.HmtxTableMissing, + c.FT_Err_Post_Table_Missing => Error.PostTableMissing, + c.FT_Err_Invalid_Horiz_Metrics => Error.InvalidHorizMetrics, + c.FT_Err_Invalid_CharMap_Format => Error.InvalidCharMapFormat, + c.FT_Err_Invalid_PPem => Error.InvalidPPem, + c.FT_Err_Invalid_Vert_Metrics => Error.InvalidVertMetrics, + c.FT_Err_Could_Not_Find_Context => Error.CouldNotFindContext, + c.FT_Err_Invalid_Post_Table_Format => Error.InvalidPostTableFormat, + c.FT_Err_Invalid_Post_Table => Error.InvalidPostTable, + c.FT_Err_Syntax_Error => Error.Syntax, + c.FT_Err_Stack_Underflow => Error.StackUnderflow, + c.FT_Err_Ignore => Error.Ignore, + c.FT_Err_No_Unicode_Glyph_Name => Error.NoUnicodeGlyphName, + c.FT_Err_Missing_Startfont_Field => Error.MissingStartfontField, + c.FT_Err_Missing_Font_Field => Error.MissingFontField, + c.FT_Err_Missing_Size_Field => Error.MissingSizeField, + c.FT_Err_Missing_Fontboundingbox_Field => Error.MissingFontboundingboxField, + c.FT_Err_Missing_Chars_Field => Error.MissingCharsField, + c.FT_Err_Missing_Startchar_Field => Error.MissingStartcharField, + c.FT_Err_Missing_Encoding_Field => Error.MissingEncodingField, + c.FT_Err_Missing_Bbx_Field => Error.MissingBbxField, + c.FT_Err_Bbx_Too_Big => Error.BbxTooBig, + c.FT_Err_Corrupted_Font_Header => Error.CorruptedFontHeader, + c.FT_Err_Corrupted_Font_Glyphs => Error.CorruptedFontGlyphs, + else => unreachable, + }; +} + +pub fn errorToInt(err: Error) c_int { + return switch (err) { + Error.CannotOpenResource => c.FT_Err_Cannot_Open_Resource, + Error.UnknownFileFormat => c.FT_Err_Unknown_File_Format, + Error.InvalidFileFormat => c.FT_Err_Invalid_File_Format, + Error.InvalidVersion => c.FT_Err_Invalid_Version, + Error.LowerModuleVersion => c.FT_Err_Lower_Module_Version, + Error.InvalidArgument => c.FT_Err_Invalid_Argument, + Error.UnimplementedFeature => c.FT_Err_Unimplemented_Feature, + Error.InvalidTable => c.FT_Err_Invalid_Table, + Error.InvalidOffset => c.FT_Err_Invalid_Offset, + Error.ArrayTooLarge => c.FT_Err_Array_Too_Large, + Error.MissingModule => c.FT_Err_Missing_Module, + Error.MissingProperty => c.FT_Err_Missing_Property, + Error.InvalidGlyphIndex => c.FT_Err_Invalid_Glyph_Index, + Error.InvalidCharacterCode => c.FT_Err_Invalid_Character_Code, + Error.InvalidGlyphFormat => c.FT_Err_Invalid_Glyph_Format, + Error.CannotRenderGlyph => c.FT_Err_Cannot_Render_Glyph, + Error.InvalidOutline => c.FT_Err_Invalid_Outline, + Error.InvalidComposite => c.FT_Err_Invalid_Composite, + Error.TooManyHints => c.FT_Err_Too_Many_Hints, + Error.InvalidPixelSize => c.FT_Err_Invalid_Pixel_Size, + Error.InvalidHandle => c.FT_Err_Invalid_Handle, + Error.InvalidLibraryHandle => c.FT_Err_Invalid_Library_Handle, + Error.InvalidDriverHandle => c.FT_Err_Invalid_Driver_Handle, + Error.InvalidFaceHandle => c.FT_Err_Invalid_Face_Handle, + Error.InvalidSizeHandle => c.FT_Err_Invalid_Size_Handle, + Error.InvalidSlotHandle => c.FT_Err_Invalid_Slot_Handle, + Error.InvalidCharMapHandle => c.FT_Err_Invalid_CharMap_Handle, + Error.InvalidCacheHandle => c.FT_Err_Invalid_Cache_Handle, + Error.InvalidStreamHandle => c.FT_Err_Invalid_Stream_Handle, + Error.TooManyDrivers => c.FT_Err_Too_Many_Drivers, + Error.TooManyExtensions => c.FT_Err_Too_Many_Extensions, + Error.OutOfMemory => c.FT_Err_Out_Of_Memory, + Error.UnlistedObject => c.FT_Err_Unlisted_Object, + Error.CannotOpenStream => c.FT_Err_Cannot_Open_Stream, + Error.InvalidStreamSeek => c.FT_Err_Invalid_Stream_Seek, + Error.InvalidStreamSkip => c.FT_Err_Invalid_Stream_Skip, + Error.InvalidStreamRead => c.FT_Err_Invalid_Stream_Read, + Error.InvalidStreamOperation => c.FT_Err_Invalid_Stream_Operation, + Error.InvalidFrameOperation => c.FT_Err_Invalid_Frame_Operation, + Error.NestedFrameAccess => c.FT_Err_Nested_Frame_Access, + Error.InvalidFrameRead => c.FT_Err_Invalid_Frame_Read, + Error.RasterUninitialized => c.FT_Err_Raster_Uninitialized, + Error.RasterCorrupted => c.FT_Err_Raster_Corrupted, + Error.RasterOverflow => c.FT_Err_Raster_Overflow, + Error.RasterNegativeHeight => c.FT_Err_Raster_Negative_Height, + Error.TooManyCaches => c.FT_Err_Too_Many_Caches, + Error.InvalidOpcode => c.FT_Err_Invalid_Opcode, + Error.TooFewArguments => c.FT_Err_Too_Few_Arguments, + Error.StackOverflow => c.FT_Err_Stack_Overflow, + Error.CodeOverflow => c.FT_Err_Code_Overflow, + Error.BadArgument => c.FT_Err_Bad_Argument, + Error.DivideByZero => c.FT_Err_Divide_By_Zero, + Error.InvalidReference => c.FT_Err_Invalid_Reference, + Error.DebugOpCode => c.FT_Err_Debug_OpCode, + Error.ENDFInExecStream => c.FT_Err_ENDF_In_Exec_Stream, + Error.NestedDEFS => c.FT_Err_Nested_DEFS, + Error.InvalidCodeRange => c.FT_Err_Invalid_CodeRange, + Error.ExecutionTooLong => c.FT_Err_Execution_Too_Long, + Error.TooManyFunctionDefs => c.FT_Err_Too_Many_Function_Defs, + Error.TooManyInstructionDefs => c.FT_Err_Too_Many_Instruction_Defs, + Error.TableMissing => c.FT_Err_Table_Missing, + Error.HorizHeaderMissing => c.FT_Err_Horiz_Header_Missing, + Error.LocationsMissing => c.FT_Err_Locations_Missing, + Error.NameTableMissing => c.FT_Err_Name_Table_Missing, + Error.CMapTableMissing => c.FT_Err_CMap_Table_Missing, + Error.HmtxTableMissing => c.FT_Err_Hmtx_Table_Missing, + Error.PostTableMissing => c.FT_Err_Post_Table_Missing, + Error.InvalidHorizMetrics => c.FT_Err_Invalid_Horiz_Metrics, + Error.InvalidCharMapFormat => c.FT_Err_Invalid_CharMap_Format, + Error.InvalidPPem => c.FT_Err_Invalid_PPem, + Error.InvalidVertMetrics => c.FT_Err_Invalid_Vert_Metrics, + Error.CouldNotFindContext => c.FT_Err_Could_Not_Find_Context, + Error.InvalidPostTableFormat => c.FT_Err_Invalid_Post_Table_Format, + Error.InvalidPostTable => c.FT_Err_Invalid_Post_Table, + Error.Syntax => c.FT_Err_Syntax_Error, + Error.StackUnderflow => c.FT_Err_Stack_Underflow, + Error.Ignore => c.FT_Err_Ignore, + Error.NoUnicodeGlyphName => c.FT_Err_No_Unicode_Glyph_Name, + Error.MissingStartfontField => c.FT_Err_Missing_Startfont_Field, + Error.MissingFontField => c.FT_Err_Missing_Font_Field, + Error.MissingSizeField => c.FT_Err_Missing_Size_Field, + Error.MissingFontboundingboxField => c.FT_Err_Missing_Fontboundingbox_Field, + Error.MissingCharsField => c.FT_Err_Missing_Chars_Field, + Error.MissingStartcharField => c.FT_Err_Missing_Startchar_Field, + Error.MissingEncodingField => c.FT_Err_Missing_Encoding_Field, + Error.MissingBbxField => c.FT_Err_Missing_Bbx_Field, + Error.BbxTooBig => c.FT_Err_Bbx_Too_Big, + Error.CorruptedFontHeader => c.FT_Err_Corrupted_Font_Header, + Error.CorruptedFontGlyphs => c.FT_Err_Corrupted_Font_Glyphs, + }; +} + +test "error convertion" { + const expectError = @import("std").testing.expectError; + + try intToError(c.FT_Err_Ok); + try expectError(Error.OutOfMemory, intToError(c.FT_Err_Out_Of_Memory)); +} diff --git a/pkg/freetype/main.zig b/pkg/freetype/main.zig index e66cd7094..e94f8fd84 100644 --- a/pkg/freetype/main.zig +++ b/pkg/freetype/main.zig @@ -1 +1,9 @@ pub const c = @import("c.zig"); +pub const testing = @import("test.zig"); +pub const Face = @import("Face.zig"); +pub const Library = @import("Library.zig"); +pub usingnamespace @import("errors.zig"); + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/pkg/harfbuzz/res/FiraCode.ttf b/pkg/freetype/res/FiraCode-Regular.ttf similarity index 100% rename from pkg/harfbuzz/res/FiraCode.ttf rename to pkg/freetype/res/FiraCode-Regular.ttf diff --git a/pkg/freetype/test.zig b/pkg/freetype/test.zig new file mode 100644 index 000000000..866c6f2a4 --- /dev/null +++ b/pkg/freetype/test.zig @@ -0,0 +1 @@ +pub const font_regular = @embedFile("res/FiraCode-Regular.ttf"); diff --git a/pkg/harfbuzz/freetype.zig b/pkg/harfbuzz/freetype.zig index 4ae3666a5..6147fcb5e 100644 --- a/pkg/harfbuzz/freetype.zig +++ b/pkg/harfbuzz/freetype.zig @@ -48,7 +48,7 @@ pub fn setFontFuncs(font: Font) void { test { const testing = std.testing; - const testFont = @import("test.zig").fontRegular; + const testFont = freetype.testing.font_regular; const ftc = freetype.c; const ftok = ftc.FT_Err_Ok; diff --git a/pkg/harfbuzz/test.zig b/pkg/harfbuzz/test.zig deleted file mode 100644 index 64837a1e1..000000000 --- a/pkg/harfbuzz/test.zig +++ /dev/null @@ -1 +0,0 @@ -pub const fontRegular = @embedFile("res/FiraCode.ttf"); diff --git a/src/main.zig b/src/main.zig index e1230f8aa..188ff1fef 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const options = @import("build_options"); const std = @import("std"); const glfw = @import("glfw"); +const freetype = @import("freetype"); const harfbuzz = @import("harfbuzz"); const tracy = @import("tracy");