convert src/font to use new pkg/freetype

This commit is contained in:
Mitchell Hashimoto
2022-08-28 22:22:16 -07:00
parent 28e9619361
commit f1abca51f7
8 changed files with 109 additions and 118 deletions

View File

@ -106,6 +106,7 @@ pub const LoadFlags = packed struct {
monochrome: bool = false, monochrome: bool = false,
linear_design: bool = false, linear_design: bool = false,
no_autohint: bool = false, no_autohint: bool = false,
_padding1: u1 = 0,
target_normal: bool = false, target_normal: bool = false,
target_light: bool = false, target_light: bool = false,
target_mono: bool = false, target_mono: bool = false,
@ -114,7 +115,7 @@ pub const LoadFlags = packed struct {
color: bool = false, color: bool = false,
compute_metrics: bool = false, compute_metrics: bool = false,
bitmap_metrics_only: bool = false, bitmap_metrics_only: bool = false,
_padding: u10 = 0, _padding2: u9 = 0,
test { test {
// This must always be an i32 size so we can bitcast directly. // This must always be an i32 size so we can bitcast directly.
@ -124,11 +125,12 @@ pub const LoadFlags = packed struct {
test "bitcast" { test "bitcast" {
const testing = std.testing; const testing = std.testing;
const cval: i32 = c.FT_LOAD_RENDER | c.FT_LOAD_PEDANTIC; const cval: i32 = c.FT_LOAD_RENDER | c.FT_LOAD_PEDANTIC | c.FT_LOAD_COLOR;
const flags = @bitCast(LoadFlags, cval); const flags = @bitCast(LoadFlags, cval);
try testing.expect(!flags.no_hinting); try testing.expect(!flags.no_hinting);
try testing.expect(flags.render); try testing.expect(flags.render);
try testing.expect(flags.pedantic); try testing.expect(flags.pedantic);
try testing.expect(flags.color);
} }
}; };

View File

@ -44,6 +44,7 @@ texture: gl.Texture,
texture_color: gl.Texture, texture_color: gl.Texture,
/// The font atlas. /// The font atlas.
font_lib: font.Library,
font_set: font.FallbackSet, font_set: font.FallbackSet,
/// Whether the cursor is visible or not. This is used to control cursor /// Whether the cursor is visible or not. This is used to control cursor
@ -151,9 +152,12 @@ pub fn init(
try font_set.families.ensureTotalCapacity(alloc, 2); try font_set.families.ensureTotalCapacity(alloc, 2);
errdefer font_set.deinit(alloc); errdefer font_set.deinit(alloc);
var font_lib = try font.Library.init();
errdefer font_lib.deinit();
// Regular text // Regular text
font_set.families.appendAssumeCapacity(fam: { font_set.families.appendAssumeCapacity(fam: {
var fam = try font.Family.init(atlas); var fam = font.Family.init(font_lib, atlas);
errdefer fam.deinit(alloc); errdefer fam.deinit(alloc);
try fam.loadFaceFromMemory(.regular, face_ttf, font_size); try fam.loadFaceFromMemory(.regular, face_ttf, font_size);
try fam.loadFaceFromMemory(.bold, face_bold_ttf, font_size); try fam.loadFaceFromMemory(.bold, face_bold_ttf, font_size);
@ -162,7 +166,7 @@ pub fn init(
// Emoji // Emoji
font_set.families.appendAssumeCapacity(fam: { font_set.families.appendAssumeCapacity(fam: {
var fam_emoji = try font.Family.init(atlas_color); var fam_emoji = font.Family.init(font_lib, atlas_color);
errdefer fam_emoji.deinit(alloc); errdefer fam_emoji.deinit(alloc);
try fam_emoji.loadFaceFromMemory(.regular, face_emoji_ttf, font_size); try fam_emoji.loadFaceFromMemory(.regular, face_emoji_ttf, font_size);
break :fam fam_emoji; break :fam fam_emoji;
@ -189,11 +193,11 @@ pub fn init(
const fam = &font_set.families.items[0]; const fam = &font_set.families.items[0];
// This is the height reported by the font face // This is the height reported by the font face
const face_height: i32 = fam.regular.?.unitsToPxY(fam.regular.?.ft_face.*.height); const face_height: i32 = fam.regular.?.unitsToPxY(fam.regular.?.face.?.handle.*.height);
// Determine the height of the underscore char // Determine the height of the underscore char
const glyph = font_set.families.items[0].getGlyph('_', .regular).?; const glyph = font_set.families.items[0].getGlyph('_', .regular).?;
var res: i32 = fam.regular.?.unitsToPxY(fam.regular.?.ft_face.*.ascender); var res: i32 = fam.regular.?.unitsToPxY(fam.regular.?.face.?.handle.*.ascender);
res -= glyph.offset_y; res -= glyph.offset_y;
res += @intCast(i32, glyph.height); res += @intCast(i32, glyph.height);
@ -207,7 +211,7 @@ pub fn init(
const fam = &font_set.families.items[0]; const fam = &font_set.families.items[0];
break :cell_baseline cell_height - @intToFloat( break :cell_baseline cell_height - @intToFloat(
f32, f32,
fam.regular.?.unitsToPxY(fam.regular.?.ft_face.*.ascender), fam.regular.?.unitsToPxY(fam.regular.?.face.?.handle.*.ascender),
); );
}; };
log.debug("cell dimensions w={d} h={d} baseline={d}", .{ cell_width, cell_height, cell_baseline }); log.debug("cell dimensions w={d} h={d} baseline={d}", .{ cell_width, cell_height, cell_baseline });
@ -331,6 +335,7 @@ pub fn init(
.vbo = vbo, .vbo = vbo,
.texture = tex, .texture = tex,
.texture_color = tex_color, .texture_color = tex_color,
.font_lib = font_lib,
.font_set = font_set, .font_set = font_set,
.cursor_visible = true, .cursor_visible = true,
.cursor_style = .box, .cursor_style = .box,
@ -345,6 +350,7 @@ pub fn deinit(self: *Grid) void {
family.deinit(self.alloc); family.deinit(self.alloc);
} }
self.font_set.deinit(self.alloc); self.font_set.deinit(self.alloc);
self.font_lib.deinit();
self.texture.destroy(); self.texture.destroy();
self.texture_color.destroy(); self.texture_color.destroy();

View File

@ -240,8 +240,8 @@ pub fn create(alloc: Allocator, loop: libuv.Loop, config: *const Config) !*Windo
const window_size = try window.getSize(); const window_size = try window.getSize();
var grid = try Grid.init(alloc, .{ var grid = try Grid.init(alloc, .{
.points = config.@"font-size", .points = config.@"font-size",
.xdpi = @floatToInt(u32, x_dpi), .xdpi = @floatToInt(u16, x_dpi),
.ydpi = @floatToInt(u32, y_dpi), .ydpi = @floatToInt(u16, y_dpi),
}); });
try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height }); try grid.setScreenSize(.{ .width = window_size.width, .height = window_size.height });
grid.background = .{ grid.background = .{

View File

@ -7,53 +7,49 @@ const Face = @This();
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const freetype = @import("freetype");
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ftc = @import("freetype").c;
const Atlas = @import("../Atlas.zig"); const Atlas = @import("../Atlas.zig");
const Glyph = @import("main.zig").Glyph; const Glyph = @import("main.zig").Glyph;
const Library = @import("main.zig").Library;
const ftok = ftc.FT_Err_Ok;
const log = std.log.scoped(.font_face); const log = std.log.scoped(.font_face);
/// The FreeType library /// The core library
ft_library: ftc.FT_Library, library: Library,
/// Our font face. /// Our font face.
ft_face: ftc.FT_Face = null, face: ?freetype.Face = null,
/// If a DPI can't be calculated, this DPI is used. This is probably /// If a DPI can't be calculated, this DPI is used. This is probably
/// wrong on modern devices so it is highly recommended you get the DPI /// wrong on modern devices so it is highly recommended you get the DPI
/// using whatever platform method you can. /// using whatever platform method you can.
pub const default_dpi = if (builtin.os.tag == .macos) 72 else 96; pub const default_dpi = if (builtin.os.tag == .macos) 72 else 96;
pub fn init(lib: ftc.FT_Library) !Face { pub fn init(lib: Library) !Face {
return Face{ return Face{
.ft_library = lib, .library = lib,
}; };
} }
pub fn deinit(self: *Face) void { pub fn deinit(self: *Face) void {
if (self.ft_face != null) { if (self.face) |face| face.deinit();
if (ftc.FT_Done_Face(self.ft_face) != ftok)
log.err("failed to clean up font face", .{});
}
self.* = undefined; self.* = undefined;
} }
/// The desired size for loading a font. /// The desired size for loading a font.
pub const DesiredSize = struct { pub const DesiredSize = struct {
// Desired size in points // Desired size in points
points: u32, points: u16,
// The DPI of the screen so we can convert points to pixels. // The DPI of the screen so we can convert points to pixels.
xdpi: u32 = default_dpi, xdpi: u16 = default_dpi,
ydpi: u32 = default_dpi, ydpi: u16 = default_dpi,
// Converts points to pixels // Converts points to pixels
pub fn pixels(self: DesiredSize) u32 { pub fn pixels(self: DesiredSize) u16 {
// 1 point = 1/72 inch // 1 point = 1/72 inch
return (self.points * self.ydpi) / 72; return (self.points * self.ydpi) / 72;
} }
@ -65,86 +61,66 @@ pub fn loadFaceFromMemory(
source: [:0]const u8, source: [:0]const u8,
size: DesiredSize, size: DesiredSize,
) !void { ) !void {
assert(self.ft_face == null); assert(self.face == null);
if (ftc.FT_New_Memory_Face( const face = try self.library.lib.initMemoryFace(source, 0);
self.ft_library, errdefer face.deinit();
source.ptr,
@intCast(c_long, source.len),
0,
&self.ft_face,
) != ftok) return error.FaceLoadFailed;
errdefer {
_ = ftc.FT_Done_Face(self.ft_face);
self.ft_face = null;
}
if (ftc.FT_Select_Charmap(self.ft_face, ftc.FT_ENCODING_UNICODE) != ftok) try face.selectCharmap(.unicode);
return error.FaceLoadFailed;
// If we have fixed sizes, we just have to try to pick the one closest // If we have fixed sizes, we just have to try to pick the one closest
// to what the user requested. Otherwise, we can choose an arbitrary // to what the user requested. Otherwise, we can choose an arbitrary
// pixel size. // pixel size.
if (!ftc.FT_HAS_FIXED_SIZES(self.ft_face)) { if (!face.hasFixedSizes()) {
const size_26dot6 = size.points << 6; // mult by 64 const size_26dot6 = @intCast(i32, size.points << 6); // mult by 64
if (ftc.FT_Set_Char_Size(self.ft_face, 0, size_26dot6, size.xdpi, size.ydpi) != ftok) try face.setCharSize(0, size_26dot6, size.xdpi, size.ydpi);
return error.FaceLoadFailed; } else try selectSizeNearest(face, size.pixels());
} else try self.selectSizeNearest(size.pixels());
// Success, persist
self.face = face;
} }
/// Selects the fixed size in the loaded face that is closest to the /// Selects the fixed size in the loaded face that is closest to the
/// requested pixel size. /// requested pixel size.
fn selectSizeNearest(self: *Face, size: u32) !void { fn selectSizeNearest(face: freetype.Face, size: u32) !void {
var i: usize = 0; var i: i32 = 0;
var best_i: usize = 0; var best_i: i32 = 0;
var best_diff: i32 = 0; var best_diff: i32 = 0;
while (i < self.ft_face.*.num_fixed_sizes) : (i += 1) { while (i < face.handle.*.num_fixed_sizes) : (i += 1) {
const diff = @intCast(i32, size) - @intCast(i32, self.ft_face.*.available_sizes[i].width); const width = face.handle.*.available_sizes[@intCast(usize, i)].width;
const diff = @intCast(i32, size) - @intCast(i32, width);
if (i == 0 or diff < best_diff) { if (i == 0 or diff < best_diff) {
best_diff = diff; best_diff = diff;
best_i = i; best_i = i;
} }
} }
if (ftc.FT_Select_Size(self.ft_face, @intCast(c_int, best_i)) != ftok) try face.selectSize(best_i);
return error.FaceSelectSizeFailed;
} }
/// Load a glyph for this face. The codepoint can be either a u8 or /// Load a glyph for this face. The codepoint can be either a u8 or
/// []const u8 depending on if you know it is ASCII or must be UTF-8 decoded. /// []const u8 depending on if you know it is ASCII or must be UTF-8 decoded.
pub fn loadGlyph(self: Face, alloc: Allocator, atlas: *Atlas, cp: u32) !Glyph { pub fn loadGlyph(self: Face, alloc: Allocator, atlas: *Atlas, cp: u32) !Glyph {
assert(self.ft_face != null); const face = self.face.?;
// We need a UTF32 codepoint for freetype // We need a UTF32 codepoint for freetype
const glyph_index = glyph_index: { const glyph_index = face.getCharIndex(cp) orelse return error.GlyphNotFound;
//log.warn("glyph load: {x}", .{cp});
const idx = ftc.FT_Get_Char_Index(self.ft_face, cp);
if (idx > 0) break :glyph_index idx;
// Unknown glyph.
//log.warn("glyph not found: {x}", .{cp});
return error.GlyphNotFound;
};
//log.warn("glyph index: {}", .{glyph_index}); //log.warn("glyph index: {}", .{glyph_index});
// If our glyph has color, we want to render the color // If our glyph has color, we want to render the color
var load_flags: c_int = ftc.FT_LOAD_RENDER; try face.loadGlyph(glyph_index, .{
if (ftc.FT_HAS_COLOR(self.ft_face)) load_flags |= @intCast(c_int, ftc.FT_LOAD_COLOR); .render = true,
.color = face.hasColor(),
});
if (ftc.FT_Load_Glyph( const glyph = face.handle.*.glyph;
self.ft_face,
glyph_index,
load_flags,
) != ftok) return error.LoadGlyphFailed;
const glyph = self.ft_face.*.glyph;
const bitmap = glyph.*.bitmap; const bitmap = glyph.*.bitmap;
// Ensure we know how to work with the font format. And assure that // Ensure we know how to work with the font format. And assure that
// or color depth is as expected on the texture atlas. // or color depth is as expected on the texture atlas.
const format: Atlas.Format = switch (bitmap.pixel_mode) { const format: Atlas.Format = switch (bitmap.pixel_mode) {
ftc.FT_PIXEL_MODE_GRAY => .greyscale, freetype.c.FT_PIXEL_MODE_GRAY => .greyscale,
ftc.FT_PIXEL_MODE_BGRA => .rgba, freetype.c.FT_PIXEL_MODE_BGRA => .rgba,
else => { else => {
log.warn("pixel mode={}", .{bitmap.pixel_mode}); log.warn("pixel mode={}", .{bitmap.pixel_mode});
@panic("unsupported pixel mode"); @panic("unsupported pixel mode");
@ -204,27 +180,28 @@ pub fn loadGlyph(self: Face, alloc: Allocator, atlas: *Atlas, cp: u32) !Glyph {
/// Convert 16.6 pixel format to pixels based on the scale factor of the /// Convert 16.6 pixel format to pixels based on the scale factor of the
/// current font size. /// current font size.
pub fn unitsToPxY(self: Face, units: i32) i32 { pub fn unitsToPxY(self: Face, units: i32) i32 {
return @intCast(i32, ftc.FT_MulFix(units, self.ft_face.*.size.*.metrics.y_scale) >> 6); return @intCast(i32, freetype.mulFix(
units,
@intCast(i32, self.face.?.handle.*.size.*.metrics.y_scale),
) >> 6);
} }
/// Convert 26.6 pixel format to f32 /// Convert 26.6 pixel format to f32
fn f26dot6ToFloat(v: ftc.FT_F26Dot6) f32 { fn f26dot6ToFloat(v: freetype.c.FT_F26Dot6) f32 {
return @intToFloat(f32, v >> 6); return @intToFloat(f32, v >> 6);
} }
test { test {
const testFont = @import("test.zig").fontRegular; const testFont = @import("test.zig").fontRegular;
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);
const alloc = testing.allocator; const alloc = testing.allocator;
var lib = try Library.init();
defer lib.deinit();
var atlas = try Atlas.init(alloc, 512, .greyscale); var atlas = try Atlas.init(alloc, 512, .greyscale);
defer atlas.deinit(alloc); defer atlas.deinit(alloc);
var font = try init(ft_lib); var font = try init(lib);
defer font.deinit(); defer font.deinit();
try font.loadFaceFromMemory(testFont, .{ .points = 12 }); try font.loadFaceFromMemory(testFont, .{ .points = 12 });
@ -237,18 +214,16 @@ test {
} }
test "color emoji" { test "color emoji" {
const alloc = testing.allocator;
const testFont = @import("test.zig").fontEmoji; const testFont = @import("test.zig").fontEmoji;
var ft_lib: ftc.FT_Library = undefined; var lib = try Library.init();
if (ftc.FT_Init_FreeType(&ft_lib) != ftok) defer lib.deinit();
return error.FreeTypeInitFailed;
defer _ = ftc.FT_Done_FreeType(ft_lib);
const alloc = testing.allocator;
var atlas = try Atlas.init(alloc, 512, .rgba); var atlas = try Atlas.init(alloc, 512, .rgba);
defer atlas.deinit(alloc); defer atlas.deinit(alloc);
var font = try init(ft_lib); var font = try init(lib);
defer font.deinit(); defer font.deinit();
try font.loadFaceFromMemory(testFont, .{ .points = 12 }); try font.loadFaceFromMemory(testFont, .{ .points = 12 });

View File

@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator;
const Atlas = @import("../Atlas.zig"); const Atlas = @import("../Atlas.zig");
const Family = @import("main.zig").Family; const Family = @import("main.zig").Family;
const Library = @import("main.zig").Library;
const Glyph = @import("main.zig").Glyph; const Glyph = @import("main.zig").Glyph;
const Style = @import("main.zig").Style; const Style = @import("main.zig").Style;
const codepoint = @import("main.zig").codepoint; const codepoint = @import("main.zig").codepoint;
@ -123,14 +124,17 @@ test {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var lib = try Library.init();
defer lib.deinit();
var set: FallbackSet = .{}; var set: FallbackSet = .{};
try set.families.append(alloc, fam: { try set.families.append(alloc, fam: {
var fam = try Family.init(try Atlas.init(alloc, 512, .greyscale)); var fam = Family.init(lib, try Atlas.init(alloc, 512, .greyscale));
try fam.loadFaceFromMemory(.regular, fontRegular, .{ .points = 48 }); try fam.loadFaceFromMemory(.regular, fontRegular, .{ .points = 48 });
break :fam fam; break :fam fam;
}); });
try set.families.append(alloc, fam: { try set.families.append(alloc, fam: {
var fam = try Family.init(try Atlas.init(alloc, 512, .rgba)); var fam = Family.init(lib, try Atlas.init(alloc, 512, .rgba));
try fam.loadFaceFromMemory(.regular, fontEmoji, .{ .points = 48 }); try fam.loadFaceFromMemory(.regular, fontEmoji, .{ .points = 48 });
break :fam fam; break :fam fam;
}); });

View File

@ -5,27 +5,22 @@ const Family = @This();
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Atlas = @import("../Atlas.zig"); const Atlas = @import("../Atlas.zig");
const ftc = @import("freetype").c;
const ftok = ftc.FT_Err_Ok;
const Face = @import("main.zig").Face; const Face = @import("main.zig").Face;
const Glyph = @import("main.zig").Glyph; const Glyph = @import("main.zig").Glyph;
const Style = @import("main.zig").Style; const Style = @import("main.zig").Style;
const testFont = @import("test.zig").fontRegular; const testFont = @import("test.zig").fontRegular;
const codepoint = @import("main.zig").codepoint; const codepoint = @import("main.zig").codepoint;
const Library = @import("main.zig").Library;
const log = std.log.scoped(.font_family); const log = std.log.scoped(.font_family);
// NOTE(mitchellh): I think eventually atlas and the freetype lib fields
// move even higher up into another struct that manages sets of font families
// in order to support fallback and so on.
/// The texture atlas where all the font glyphs are rendered. /// The texture atlas where all the font glyphs are rendered.
/// This is NOT owned by the Family, deinitialization must /// This is NOT owned by the Family, deinitialization must
/// be manually done. /// be manually done.
atlas: Atlas, atlas: Atlas,
/// The FreeType library, initialized by this init func. /// The library shared state.
ft_library: ftc.FT_Library, lib: Library,
/// The glyphs that are loaded into the atlas, keyed by codepoint. /// The glyphs that are loaded into the atlas, keyed by codepoint.
glyphs: std.AutoHashMapUnmanaged(GlyphKey, Glyph) = .{}, glyphs: std.AutoHashMapUnmanaged(GlyphKey, Glyph) = .{},
@ -41,16 +36,11 @@ const GlyphKey = struct {
codepoint: u32, codepoint: u32,
}; };
pub fn init(atlas: Atlas) !Family { pub fn init(lib: Library, atlas: Atlas) Family {
var res = Family{ return .{
.lib = lib,
.atlas = atlas, .atlas = atlas,
.ft_library = undefined,
}; };
if (ftc.FT_Init_FreeType(&res.ft_library) != ftok)
return error.FreeTypeInitFailed;
return res;
} }
pub fn deinit(self: *Family, alloc: Allocator) void { pub fn deinit(self: *Family, alloc: Allocator) void {
@ -59,9 +49,6 @@ pub fn deinit(self: *Family, alloc: Allocator) void {
if (self.regular) |*face| face.deinit(); if (self.regular) |*face| face.deinit();
if (self.bold) |*face| face.deinit(); if (self.bold) |*face| face.deinit();
if (ftc.FT_Done_FreeType(self.ft_library) != ftok)
log.err("failed to clean up FreeType", .{});
self.* = undefined; self.* = undefined;
} }
@ -74,7 +61,7 @@ pub fn loadFaceFromMemory(
source: [:0]const u8, source: [:0]const u8,
size: Face.DesiredSize, size: Face.DesiredSize,
) !void { ) !void {
var face = try Face.init(self.ft_library); var face = try Face.init(self.lib);
errdefer face.deinit(); errdefer face.deinit();
try face.loadFaceFromMemory(source, size); try face.loadFaceFromMemory(source, size);
@ -145,7 +132,11 @@ pub fn addGlyph(self: *Family, alloc: Allocator, v: anytype, style: Style) !*Gly
test { test {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var fam = try init(try Atlas.init(alloc, 512, .greyscale));
var lib = try Library.init();
defer lib.deinit();
var fam = init(lib, try Atlas.init(alloc, 512, .greyscale));
defer fam.deinit(alloc); defer fam.deinit(alloc);
defer fam.atlas.deinit(alloc); defer fam.atlas.deinit(alloc);
try fam.loadFaceFromMemory(.regular, testFont, .{ .points = 12 }); try fam.loadFaceFromMemory(.regular, testFont, .{ .points = 12 });

19
src/font/Library.zig Normal file
View File

@ -0,0 +1,19 @@
//! A library represents the shared state that the underlying font
//! library implementation(s) require per-process.
//!
//! In the future, this will be abstracted so that the underlying text
//! engine might not be Freetype and may be something like Core Text,
//! but the API will remain the same.
const Library = @This();
const freetype = @import("freetype");
lib: freetype.Library,
pub fn init() freetype.Error!Library {
return Library{ .lib = try freetype.Library.init() };
}
pub fn deinit(self: *Library) void {
self.lib.deinit();
}

View File

@ -4,10 +4,7 @@ pub const Face = @import("Face.zig");
pub const Family = @import("Family.zig"); pub const Family = @import("Family.zig");
pub const Glyph = @import("Glyph.zig"); pub const Glyph = @import("Glyph.zig");
pub const FallbackSet = @import("FallbackSet.zig"); pub const FallbackSet = @import("FallbackSet.zig");
pub const Library = @import("Library.zig");
/// Embedded fonts (for now)
pub const fontRegular = @import("test.zig").fontRegular;
pub const fontBold = @import("test.zig").fontBold;
/// The styles that a family can take. /// The styles that a family can take.
pub const Style = enum { pub const Style = enum {
@ -29,8 +26,5 @@ pub fn codepoint(v: anytype) u32 {
} }
test { test {
_ = Face; @import("std").testing.refAllDecls(@This());
_ = Family;
_ = Glyph;
_ = FallbackSet;
} }