mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
font discovery builds up a set of deferred faces
This commit is contained in:
@ -17,18 +17,23 @@ const Presentation = @import("main.zig").Presentation;
|
||||
face: ?Face = null,
|
||||
|
||||
/// Fontconfig
|
||||
fc: if (options.fontconfig) Fontconfig else void = undefined,
|
||||
fc: if (options.fontconfig) ?Fontconfig else void = if (options.fontconfig) null else {},
|
||||
|
||||
/// Fontconfig specific data. This is only present if building with fontconfig.
|
||||
pub const Fontconfig = struct {
|
||||
pattern: *fontconfig.Pattern,
|
||||
charset: *fontconfig.CharSet,
|
||||
langset: *fontconfig.LangSet,
|
||||
charset: *const fontconfig.CharSet,
|
||||
langset: *const fontconfig.LangSet,
|
||||
|
||||
pub fn deinit(self: *Fontconfig) void {
|
||||
self.pattern.destroy();
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(self: *DeferredFace) void {
|
||||
if (self.face) |*face| face.deinit();
|
||||
|
||||
if (options.fontconfig) if (self.fc) |*fc| fc.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@ -54,21 +59,23 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
|
||||
// If we are using fontconfig, use the fontconfig metadata to
|
||||
// avoid loading the face.
|
||||
if (options.fontconfig) {
|
||||
// Check if char exists
|
||||
if (!self.fc.charset.hasChar(cp)) return false;
|
||||
if (self.fc) |fc| {
|
||||
// Check if char exists
|
||||
if (!fc.charset.hasChar(cp)) return false;
|
||||
|
||||
// If we have a presentation, check it matches
|
||||
if (p) |desired| {
|
||||
const emoji_lang = "und-zsye";
|
||||
const actual: Presentation = if (self.fc.langset.hasLang(emoji_lang))
|
||||
.emoji
|
||||
else
|
||||
.text;
|
||||
// If we have a presentation, check it matches
|
||||
if (p) |desired| {
|
||||
const emoji_lang = "und-zsye";
|
||||
const actual: Presentation = if (fc.langset.hasLang(emoji_lang))
|
||||
.emoji
|
||||
else
|
||||
.text;
|
||||
|
||||
return desired == actual;
|
||||
return desired == actual;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is unreachable because discovery mechanisms terminate, and
|
||||
|
@ -1,9 +1,14 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const fontconfig = @import("fontconfig");
|
||||
const DeferredFace = @import("main.zig").DeferredFace;
|
||||
|
||||
const log = std.log.named(.discovery);
|
||||
|
||||
pub const Error = error{
|
||||
FontConfigFailed,
|
||||
};
|
||||
|
||||
/// Descriptor is used to search for fonts. The only required field
|
||||
/// is "family". The rest are ignored unless they're set to a non-zero value.
|
||||
pub const Descriptor = struct {
|
||||
@ -53,25 +58,78 @@ pub const Fontconfig = struct {
|
||||
pub fn init() Fontconfig {
|
||||
// safe to call multiple times and concurrently
|
||||
_ = fontconfig.init();
|
||||
return .{ .fc_config = fontconfig.initLoadConfig() };
|
||||
return .{ .fc_config = fontconfig.initLoadConfigAndFonts() };
|
||||
}
|
||||
|
||||
pub fn discover(self: *Fontconfig, desc: Descriptor) void {
|
||||
/// 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 {
|
||||
// Build our pattern that we'll search for
|
||||
const pat = desc.toFcPattern();
|
||||
defer pat.destroy();
|
||||
errdefer pat.destroy();
|
||||
assert(self.fc_config.substituteWithPat(pat, .pattern));
|
||||
pat.defaultSubstitute();
|
||||
|
||||
// Search
|
||||
const res = self.fc_config.fontSort(pat, true, null);
|
||||
defer res.fs.destroy();
|
||||
if (res.result != .match) return Error.FontConfigFailed;
|
||||
errdefer res.fs.destroy();
|
||||
|
||||
return DiscoverIterator{
|
||||
.config = self.fc_config,
|
||||
.pattern = pat,
|
||||
.set = res.fs,
|
||||
.fonts = res.fs.fonts(),
|
||||
.i = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub const DiscoverIterator = struct {
|
||||
config: *fontconfig.Config,
|
||||
pattern: *fontconfig.Pattern,
|
||||
set: *fontconfig.FontSet,
|
||||
fonts: []*fontconfig.Pattern,
|
||||
i: usize,
|
||||
|
||||
pub fn deinit(self: *DiscoverIterator) void {
|
||||
self.set.destroy();
|
||||
self.pattern.destroy();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn next(self: *DiscoverIterator) fontconfig.Error!?DeferredFace {
|
||||
if (self.i >= self.fonts.len) return null;
|
||||
|
||||
// Get the copied pattern from our fontset that has the
|
||||
// attributes configured for rendering.
|
||||
const font_pattern = try self.config.fontRenderPrepare(
|
||||
self.pattern,
|
||||
self.fonts[self.i],
|
||||
);
|
||||
errdefer font_pattern.destroy();
|
||||
|
||||
// Increment after we return
|
||||
defer self.i += 1;
|
||||
|
||||
return DeferredFace{
|
||||
.face = null,
|
||||
.fc = .{
|
||||
.pattern = font_pattern,
|
||||
.charset = (try font_pattern.get(.charset, 0)).char_set,
|
||||
.langset = (try font_pattern.get(.lang, 0)).lang_set,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
test {
|
||||
defer fontconfig.fini();
|
||||
var fc = Fontconfig.init();
|
||||
const testing = std.testing;
|
||||
|
||||
fc.discover(.{ .family = "monospace" });
|
||||
var fc = Fontconfig.init();
|
||||
var it = try fc.discover(.{ .family = "monospace" });
|
||||
defer it.deinit();
|
||||
while (try it.next()) |face| {
|
||||
try testing.expect(!face.loaded());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user