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(); var font_lib = try font.Library.init();
errdefer font_lib.deinit(); errdefer font_lib.deinit();
var font_group = try font.GroupCache.init(alloc, group: { 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); 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( try group.addFace(
alloc, alloc,
.regular, .regular,

View File

@ -32,11 +32,6 @@ pub const Fontconfig = struct {
charset: *const fontconfig.CharSet, charset: *const fontconfig.CharSet,
langset: *const fontconfig.LangSet, 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 { pub fn deinit(self: *Fontconfig) void {
self.pattern.destroy(); self.pattern.destroy();
self.* = undefined; self.* = undefined;
@ -60,13 +55,28 @@ pub inline fn loaded(self: DeferredFace) bool {
return self.face != null; 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. /// 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 // No-op if we already loaded
if (self.face != null) return; if (self.face != null) return;
if (options.fontconfig) { if (options.fontconfig) {
try self.loadFontconfig(lib); try self.loadFontconfig(lib, size);
return; return;
} }
@ -75,7 +85,11 @@ pub fn load(self: *DeferredFace, lib: Library) !void {
unreachable; unreachable;
} }
fn loadFontconfig(self: *DeferredFace, lib: Library) !void { fn loadFontconfig(
self: *DeferredFace,
lib: Library,
size: Face.DesiredSize,
) !void {
assert(self.face == null); assert(self.face == null);
const fc = self.fc.?; 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 filename = (try fc.pattern.get(.file, 0)).string;
const face_index = (try fc.pattern.get(.index, 0)).integer; const face_index = (try fc.pattern.get(.index, 0)).integer;
self.face = try Face.initFile(lib, filename, face_index, .{ self.face = try Face.initFile(lib, filename, face_index, size);
.points = fc.req_size,
});
} }
/// Returns true if this face can satisfy the given codepoint and /// Returns true if this face can satisfy the given codepoint and
@ -165,8 +177,12 @@ test "fontconfig" {
defer def.deinit(); defer def.deinit();
try testing.expect(!def.loaded()); 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 // 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.hasCodepoint(' ', null));
try testing.expect(def.face.?.glyphIndex(' ') != 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. /// The library being used for all the faces.
lib: Library, 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. /// The available faces we have. This shouldn't be modified manually.
/// Instead, use the functions available on Group. /// Instead, use the functions available on Group.
faces: StyleArray, faces: StyleArray,
pub fn init(alloc: Allocator, lib: Library) !Group { pub fn init(
var result = Group{ .lib = lib, .faces = undefined }; 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. // Initialize all our styles to initially sized lists.
var i: usize = 0; 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. /// Return the Face represented by a given FontIndex.
pub fn faceFromIndex(self: Group, index: FontIndex) !Face { pub fn faceFromIndex(self: Group, index: FontIndex) !Face {
const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; 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.?; return deferred.face.?;
} }
@ -154,7 +161,7 @@ pub fn renderGlyph(
glyph_index: u32, glyph_index: u32,
) !Glyph { ) !Glyph {
const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; 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); return try face.face.?.renderGlyph(alloc, atlas, glyph_index);
} }
@ -171,7 +178,7 @@ test {
var lib = try Library.init(); var lib = try Library.init();
defer lib.deinit(); defer lib.deinit();
var group = try init(alloc, lib); var group = try init(alloc, lib, .{ .points = 12 });
defer group.deinit(alloc); defer group.deinit(alloc);
try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 }))); 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 // Initialize the group with the deferred face
var lib = try Library.init(); var lib = try Library.init();
defer lib.deinit(); defer lib.deinit();
var group = try init(alloc, lib); var group = try init(alloc, lib, .{ .points = 12 });
defer group.deinit(alloc); defer group.deinit(alloc);
try group.addFace(alloc, .regular, (try it.next()).?); try group.addFace(alloc, .regular, (try it.next()).?);

View File

@ -218,7 +218,11 @@ test {
var lib = try Library.init(); var lib = try Library.init();
defer lib.deinit(); 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); defer cache.deinit(alloc);
// Setup group // Setup group

View File

@ -596,7 +596,11 @@ fn testShaper(alloc: Allocator) !TestShaper {
var cache_ptr = try alloc.create(GroupCache); var cache_ptr = try alloc.create(GroupCache);
errdefer alloc.destroy(cache_ptr); 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); errdefer cache_ptr.*.deinit(alloc);
// Setup group // Setup group

View File

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