search for fonts on startup

This commit is contained in:
Mitchell Hashimoto
2022-09-29 11:57:29 -07:00
parent fdbf40d3ee
commit b6a4fff6d8
6 changed files with 88 additions and 26 deletions

View File

@ -160,10 +160,36 @@ pub fn init(
var font_lib = try font.Library.init();
errdefer font_lib.deinit();
var font_group = try font.GroupCache.init(alloc, group: {
var group = try font.Group.init(alloc, font_lib);
var group = try font.Group.init(alloc, font_lib, font_size);
errdefer group.deinit(alloc);
// Our regular font
// Search for fonts
{
var disco = font.Discover.init();
defer disco.deinit();
{
var disco_it = try disco.discover(.{
.family = "Fira Code",
.size = font_size.points,
});
defer disco_it.deinit();
if (try disco_it.next()) |face|
try group.addFace(alloc, .regular, face);
}
{
var disco_it = try disco.discover(.{
.family = "Fira Code",
.size = font_size.points,
.bold = true,
});
defer disco_it.deinit();
if (try disco_it.next()) |face|
try group.addFace(alloc, .bold, face);
}
}
// Our built-in font will be used as a backup
try group.addFace(
alloc,
.regular,

View File

@ -32,11 +32,6 @@ pub const Fontconfig = struct {
charset: *const fontconfig.CharSet,
langset: *const fontconfig.LangSet,
/// The requested size in points for this font. This is used for loading.
/// This can't be derived from pattern because the requested size may
/// differ from the size the font advertises supported.
req_size: u16,
pub fn deinit(self: *Fontconfig) void {
self.pattern.destroy();
self.* = undefined;
@ -60,13 +55,28 @@ pub inline fn loaded(self: DeferredFace) bool {
return self.face != null;
}
/// Returns the name of this face. The memory is always owned by the
/// face so it doesn't have to be freed.
pub fn name(self: DeferredFace) ![:0]const u8 {
if (options.fontconfig) {
if (self.fc) |fc|
return (try fc.pattern.get(.fullname, 0)).string;
}
return "TODO: built-in font names";
}
/// Load the deferred font face. This does nothing if the face is loaded.
pub fn load(self: *DeferredFace, lib: Library) !void {
pub fn load(
self: *DeferredFace,
lib: Library,
size: Face.DesiredSize,
) !void {
// No-op if we already loaded
if (self.face != null) return;
if (options.fontconfig) {
try self.loadFontconfig(lib);
try self.loadFontconfig(lib, size);
return;
}
@ -75,7 +85,11 @@ pub fn load(self: *DeferredFace, lib: Library) !void {
unreachable;
}
fn loadFontconfig(self: *DeferredFace, lib: Library) !void {
fn loadFontconfig(
self: *DeferredFace,
lib: Library,
size: Face.DesiredSize,
) !void {
assert(self.face == null);
const fc = self.fc.?;
@ -83,9 +97,7 @@ fn loadFontconfig(self: *DeferredFace, lib: Library) !void {
const filename = (try fc.pattern.get(.file, 0)).string;
const face_index = (try fc.pattern.get(.index, 0)).integer;
self.face = try Face.initFile(lib, filename, face_index, .{
.points = fc.req_size,
});
self.face = try Face.initFile(lib, filename, face_index, size);
}
/// Returns true if this face can satisfy the given codepoint and
@ -165,8 +177,12 @@ test "fontconfig" {
defer def.deinit();
try testing.expect(!def.loaded());
// Verify we can get the name
const n = try def.name();
try testing.expect(n.len > 0);
// Load it and verify it works
try def.load(lib);
try def.load(lib, .{ .points = 12 });
try testing.expect(def.hasCodepoint(' ', null));
try testing.expect(def.face.?.glyphIndex(' ') != null);
}

View File

@ -31,12 +31,19 @@ const StyleArray = std.EnumArray(Style, std.ArrayListUnmanaged(DeferredFace));
/// The library being used for all the faces.
lib: Library,
/// The desired font size. All fonts in a group must share the same size.
size: Face.DesiredSize,
/// The available faces we have. This shouldn't be modified manually.
/// Instead, use the functions available on Group.
faces: StyleArray,
pub fn init(alloc: Allocator, lib: Library) !Group {
var result = Group{ .lib = lib, .faces = undefined };
pub fn init(
alloc: Allocator,
lib: Library,
size: Face.DesiredSize,
) !Group {
var result = Group{ .lib = lib, .size = size, .faces = undefined };
// Initialize all our styles to initially sized lists.
var i: usize = 0;
@ -131,7 +138,7 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation)
/// Return the Face represented by a given FontIndex.
pub fn faceFromIndex(self: Group, index: FontIndex) !Face {
const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
try deferred.load(self.lib);
try deferred.load(self.lib, self.size);
return deferred.face.?;
}
@ -154,7 +161,7 @@ pub fn renderGlyph(
glyph_index: u32,
) !Glyph {
const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
try face.load(self.lib);
try face.load(self.lib, self.size);
return try face.face.?.renderGlyph(alloc, atlas, glyph_index);
}
@ -171,7 +178,7 @@ test {
var lib = try Library.init();
defer lib.deinit();
var group = try init(alloc, lib);
var group = try init(alloc, lib, .{ .points = 12 });
defer group.deinit(alloc);
try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })));
@ -231,7 +238,7 @@ test {
// Initialize the group with the deferred face
var lib = try Library.init();
defer lib.deinit();
var group = try init(alloc, lib);
var group = try init(alloc, lib, .{ .points = 12 });
defer group.deinit(alloc);
try group.addFace(alloc, .regular, (try it.next()).?);

View File

@ -218,7 +218,11 @@ test {
var lib = try Library.init();
defer lib.deinit();
var cache = try init(alloc, try Group.init(alloc, lib));
var cache = try init(alloc, try Group.init(
alloc,
lib,
.{ .points = 12 },
));
defer cache.deinit(alloc);
// Setup group

View File

@ -596,7 +596,11 @@ fn testShaper(alloc: Allocator) !TestShaper {
var cache_ptr = try alloc.create(GroupCache);
errdefer alloc.destroy(cache_ptr);
cache_ptr.* = try GroupCache.init(alloc, try Group.init(alloc, lib));
cache_ptr.* = try GroupCache.init(alloc, try Group.init(
alloc,
lib,
.{ .points = 12 },
));
errdefer cache_ptr.*.deinit(alloc);
// Setup group

View File

@ -39,7 +39,11 @@ pub const Descriptor = struct {
pub fn toFcPattern(self: Descriptor) *fontconfig.Pattern {
const pat = fontconfig.Pattern.create();
assert(pat.add(.family, .{ .string = self.family }, false));
if (self.size > 0) assert(pat.add(.size, .{ .integer = self.size }, false));
if (self.size > 0) assert(pat.add(
.size,
.{ .integer = self.size },
false,
));
if (self.bold) assert(pat.add(
.weight,
.{ .integer = @enumToInt(fontconfig.Weight.bold) },
@ -64,6 +68,10 @@ pub const Fontconfig = struct {
return .{ .fc_config = fontconfig.initLoadConfigAndFonts() };
}
pub fn deinit(self: *Fontconfig) void {
_ = self;
}
/// Discover fonts from a descriptor. This returns an iterator that can
/// be used to build up the deferred fonts.
pub fn discover(self: *Fontconfig, desc: Descriptor) !DiscoverIterator {
@ -84,7 +92,6 @@ pub const Fontconfig = struct {
.set = res.fs,
.fonts = res.fs.fonts(),
.i = 0,
.req_size = @floatToInt(u16, (try pat.get(.size, 0)).double),
};
}
@ -94,7 +101,6 @@ pub const Fontconfig = struct {
set: *fontconfig.FontSet,
fonts: []*fontconfig.Pattern,
i: usize,
req_size: u16,
pub fn deinit(self: *DiscoverIterator) void {
self.set.destroy();
@ -122,7 +128,6 @@ pub const Fontconfig = struct {
.pattern = font_pattern,
.charset = (try font_pattern.get(.charset, 0)).char_set,
.langset = (try font_pattern.get(.lang, 0)).lang_set,
.req_size = self.req_size,
},
};
}