mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
font: rework font init to use a struct with modifiersets everywhere
This commit is contained in:
@ -224,6 +224,13 @@ pub fn init(
|
||||
var group = try font.Group.init(alloc, font_lib, font_size);
|
||||
errdefer group.deinit();
|
||||
|
||||
// Setup our font metric modifiers if we have any.
|
||||
group.metric_modifiers = set: {
|
||||
var set: font.face.Metrics.ModifierSet = .{};
|
||||
errdefer set.deinit(alloc);
|
||||
break :set null;
|
||||
};
|
||||
|
||||
// If we have codepoint mappings, set those.
|
||||
if (config.@"font-codepoint-map".map.list.len > 0) {
|
||||
group.codepoint_map = config.@"font-codepoint-map".map;
|
||||
@ -306,11 +313,11 @@ pub fn init(
|
||||
// Our built-in font will be used as a backup
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try font.Face.init(font_lib, face_ttf, font_size) },
|
||||
.{ .loaded = try font.Face.init(font_lib, face_ttf, group.faceOptions()) },
|
||||
);
|
||||
_ = try group.addFace(
|
||||
.bold,
|
||||
.{ .loaded = try font.Face.init(font_lib, face_bold_ttf, font_size) },
|
||||
.{ .loaded = try font.Face.init(font_lib, face_bold_ttf, group.faceOptions()) },
|
||||
);
|
||||
|
||||
// Auto-italicize if we have to.
|
||||
@ -321,11 +328,11 @@ pub fn init(
|
||||
if (builtin.os.tag != .macos or font.Discover == void) {
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try font.Face.init(font_lib, face_emoji_ttf, font_size) },
|
||||
.{ .loaded = try font.Face.init(font_lib, face_emoji_ttf, group.faceOptions()) },
|
||||
);
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try font.Face.init(font_lib, face_emoji_text_ttf, font_size) },
|
||||
.{ .loaded = try font.Face.init(font_lib, face_emoji_text_ttf, group.faceOptions()) },
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ const cli = @import("../cli.zig");
|
||||
const Key = @import("key.zig").Key;
|
||||
const KeyValue = @import("key.zig").Value;
|
||||
const ErrorList = @import("ErrorList.zig");
|
||||
const MetricModifier = fontpkg.face.Metrics.Modifier;
|
||||
|
||||
const log = std.log.scoped(.config);
|
||||
|
||||
@ -122,6 +123,22 @@ const c = @cImport({
|
||||
/// currently on macOS.
|
||||
@"font-thicken": bool = false,
|
||||
|
||||
/// All of the configurations behavior adjust various metrics determined
|
||||
/// by the font. The values can be integers (1, -1, etc.) or a percentage
|
||||
/// (20%, -15%, etc.). In each case, the values represent the amount to
|
||||
/// change the original value.
|
||||
///
|
||||
/// For example, a value of "1" increases the value by 1; it does not set
|
||||
/// it to literally 1. A value of "20%" increases the value by 20%. And so
|
||||
/// on.
|
||||
///
|
||||
/// There is little to no validation on these values so the wrong values
|
||||
/// (i.e. "-100%") can cause the terminal to be unusable. Use with caution
|
||||
/// and reason.
|
||||
@"adjust-cell-width": ?MetricModifier = null,
|
||||
@"adjust-cell-height": ?MetricModifier = null,
|
||||
@"adjust-font-baseline": ?MetricModifier = null,
|
||||
|
||||
/// Background color for the window.
|
||||
background: Color = .{ .r = 0x28, .g = 0x2C, .b = 0x34 },
|
||||
|
||||
|
@ -192,7 +192,8 @@ fn loadCoreText(
|
||||
) !Face {
|
||||
_ = lib;
|
||||
const ct = self.ct.?;
|
||||
return try Face.initFontCopy(ct.font, size);
|
||||
// TODO: make options
|
||||
return try Face.initFontCopy(ct.font, .{ .size = size });
|
||||
}
|
||||
|
||||
fn loadCoreTextFreetype(
|
||||
|
@ -71,6 +71,10 @@ lib: Library,
|
||||
/// The desired font size. All fonts in a group must share the same size.
|
||||
size: font.face.DesiredSize,
|
||||
|
||||
/// Metric modifiers to apply to loaded fonts. The Group takes ownership
|
||||
/// over the memory and will use the associated allocator to free it.
|
||||
metric_modifiers: ?font.face.Metrics.ModifierSet = null,
|
||||
|
||||
/// The available faces we have. This shouldn't be modified manually.
|
||||
/// Instead, use the functions available on Group.
|
||||
faces: StyleArray,
|
||||
@ -139,9 +143,20 @@ pub fn deinit(self: *Group) void {
|
||||
}
|
||||
}
|
||||
|
||||
if (self.metric_modifiers) |*v| v.deinit(self.alloc);
|
||||
|
||||
self.descriptor_cache.deinit(self.alloc);
|
||||
}
|
||||
|
||||
/// Returns the options for initializing a face based on the options associated
|
||||
/// with this font group.
|
||||
pub fn faceOptions(self: *const Group) font.face.Options {
|
||||
return .{
|
||||
.size = self.size,
|
||||
.metric_modifiers = if (self.metric_modifiers) |*v| v else null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Add a face to the list for the given style. This face will be added as
|
||||
/// next in priority if others exist already, i.e. it'll be the _last_ to be
|
||||
/// searched for a glyph in that list.
|
||||
@ -205,7 +220,7 @@ pub fn setSize(self: *Group, size: font.face.DesiredSize) !void {
|
||||
while (it.next()) |entry| {
|
||||
for (entry.value.items) |*elem| switch (elem.*) {
|
||||
.deferred => continue,
|
||||
.loaded => |*f| try f.setSize(size),
|
||||
.loaded => |*f| try f.setSize(.{ .size = size }),
|
||||
};
|
||||
}
|
||||
|
||||
@ -623,13 +638,22 @@ test {
|
||||
var group = try init(alloc, lib, .{ .points = 12 });
|
||||
defer group.deinit();
|
||||
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }) },
|
||||
);
|
||||
|
||||
if (font.options.backend != .coretext) {
|
||||
// Coretext doesn't support Noto's format
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testEmoji, .{ .points = 12 }) });
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testEmoji, .{ .size = .{ .points = 12 } }) },
|
||||
);
|
||||
}
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testEmojiText, .{ .points = 12 }) });
|
||||
_ = try group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testEmojiText, .{ .size = .{ .points = 12 } }) },
|
||||
);
|
||||
|
||||
// Should find all visible ASCII
|
||||
var i: u32 = 32;
|
||||
@ -694,9 +718,10 @@ test "disabled font style" {
|
||||
group.styles.set(.bold, false);
|
||||
|
||||
// Same font but we can test the style in the index
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
_ = try group.addFace(.bold, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
_ = try group.addFace(.italic, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
const opts: font.face.Options = .{ .size = .{ .points = 12 } };
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, opts) });
|
||||
_ = try group.addFace(.bold, .{ .loaded = try Face.init(lib, testFont, opts) });
|
||||
_ = try group.addFace(.italic, .{ .loaded = try Face.init(lib, testFont, opts) });
|
||||
|
||||
// Regular should work fine
|
||||
{
|
||||
@ -731,16 +756,17 @@ test "face count limit" {
|
||||
var lib = try Library.init();
|
||||
defer lib.deinit();
|
||||
|
||||
var group = try init(alloc, lib, .{ .points = 12 });
|
||||
const opts: font.face.Options = .{ .size = .{ .points = 12 } };
|
||||
var group = try init(alloc, lib, opts.size);
|
||||
defer group.deinit();
|
||||
|
||||
for (0..FontIndex.Special.start - 1) |_| {
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, opts) });
|
||||
}
|
||||
|
||||
try testing.expectError(error.GroupFull, group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) },
|
||||
.{ .loaded = try Face.init(lib, testFont, opts) },
|
||||
));
|
||||
}
|
||||
|
||||
@ -790,7 +816,11 @@ test "resize" {
|
||||
var group = try init(alloc, lib, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer group.deinit();
|
||||
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 }) });
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12, .xdpi = 96, .ydpi = 96 } },
|
||||
) });
|
||||
|
||||
// Load a letter
|
||||
{
|
||||
@ -881,7 +911,11 @@ test "faceFromIndex returns pointer" {
|
||||
var group = try init(alloc, lib, .{ .points = 12, .xdpi = 96, .ydpi = 96 });
|
||||
defer group.deinit();
|
||||
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 }) });
|
||||
_ = try group.addFace(.regular, .{ .loaded = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12, .xdpi = 96, .ydpi = 96 } },
|
||||
) });
|
||||
|
||||
{
|
||||
const idx = group.indexForCodepoint('A', .regular, null).?;
|
||||
|
@ -184,7 +184,7 @@ test {
|
||||
// Setup group
|
||||
_ = try cache.group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) },
|
||||
.{ .loaded = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } }) },
|
||||
);
|
||||
var group = cache.group;
|
||||
|
||||
@ -340,7 +340,11 @@ test "resize" {
|
||||
// Setup group
|
||||
_ = try cache.group.addFace(
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(lib, testFont, .{ .points = 12, .xdpi = 96, .ydpi = 96 }) },
|
||||
.{ .loaded = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12, .xdpi = 96, .ydpi = 96 } },
|
||||
) },
|
||||
);
|
||||
|
||||
// Load a letter
|
||||
|
@ -22,6 +22,12 @@ pub const Face = switch (options.backend) {
|
||||
/// using whatever platform method you can.
|
||||
pub const default_dpi = if (builtin.os.tag == .macos) 72 else 96;
|
||||
|
||||
/// Options for initializing a font face.
|
||||
pub const Options = struct {
|
||||
size: DesiredSize,
|
||||
metric_modifiers: ?*const Metrics.ModifierSet = null,
|
||||
};
|
||||
|
||||
/// The desired size for loading a font.
|
||||
pub const DesiredSize = struct {
|
||||
// Desired size in points
|
||||
|
@ -36,7 +36,7 @@ pub const Face = struct {
|
||||
};
|
||||
|
||||
/// Initialize a CoreText-based font from a TTF/TTC in memory.
|
||||
pub fn init(lib: font.Library, source: [:0]const u8, size: font.face.DesiredSize) !Face {
|
||||
pub fn init(lib: font.Library, source: [:0]const u8, opts: font.face.Options) !Face {
|
||||
_ = lib;
|
||||
|
||||
const data = try macos.foundation.Data.createWithBytesNoCopy(source);
|
||||
@ -51,18 +51,22 @@ pub const Face = struct {
|
||||
const ct_font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||
defer ct_font.release();
|
||||
|
||||
return try initFontCopy(ct_font, size);
|
||||
return try initFontCopy(ct_font, opts);
|
||||
}
|
||||
|
||||
/// Initialize a CoreText-based face from another initialized font face
|
||||
/// but with a new size. This is often how CoreText fonts are initialized
|
||||
/// because the font is loaded at a default size during discovery, and then
|
||||
/// adjusted to the final size for final load.
|
||||
pub fn initFontCopy(base: *macos.text.Font, size: font.face.DesiredSize) !Face {
|
||||
pub fn initFontCopy(base: *macos.text.Font, opts: font.face.Options) !Face {
|
||||
// Create a copy. The copyWithAttributes docs say the size is in points,
|
||||
// but we need to scale the points by the DPI and to do that we use our
|
||||
// function called "pixels".
|
||||
const ct_font = try base.copyWithAttributes(@floatFromInt(size.pixels()), null, null);
|
||||
const ct_font = try base.copyWithAttributes(
|
||||
@floatFromInt(opts.size.pixels()),
|
||||
null,
|
||||
null,
|
||||
);
|
||||
errdefer ct_font.release();
|
||||
|
||||
return try initFont(ct_font);
|
||||
@ -161,9 +165,9 @@ pub const Face = struct {
|
||||
|
||||
/// Resize the font in-place. If this succeeds, the caller is responsible
|
||||
/// for clearing any glyph caches, font atlas data, etc.
|
||||
pub fn setSize(self: *Face, size: font.face.DesiredSize) !void {
|
||||
pub fn setSize(self: *Face, opts: font.face.Options) !void {
|
||||
// We just create a copy and replace ourself
|
||||
const face = try initFontCopy(self.font, size);
|
||||
const face = try initFontCopy(self.font, opts);
|
||||
self.deinit();
|
||||
self.* = face;
|
||||
}
|
||||
@ -514,7 +518,7 @@ test {
|
||||
const ct_font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||
defer ct_font.release();
|
||||
|
||||
var face = try Face.initFontCopy(ct_font, .{ .points = 12 });
|
||||
var face = try Face.initFontCopy(ct_font, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
|
||||
try testing.expectEqual(font.Presentation.text, face.presentation);
|
||||
@ -537,7 +541,7 @@ test "emoji" {
|
||||
const ct_font = try macos.text.Font.createWithFontDescriptor(desc, 12);
|
||||
defer ct_font.release();
|
||||
|
||||
var face = try Face.initFontCopy(ct_font, .{ .points = 18 });
|
||||
var face = try Face.initFontCopy(ct_font, .{ .size = .{ .points = 18 } });
|
||||
defer face.deinit();
|
||||
|
||||
// Presentation
|
||||
@ -558,7 +562,7 @@ test "in-memory" {
|
||||
var lib = try font.Library.init();
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
|
||||
try testing.expectEqual(font.Presentation.text, face.presentation);
|
||||
@ -582,7 +586,7 @@ test "variable" {
|
||||
var lib = try font.Library.init();
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
|
||||
try testing.expectEqual(font.Presentation.text, face.presentation);
|
||||
@ -606,7 +610,7 @@ test "variable set variation" {
|
||||
var lib = try font.Library.init();
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .points = 12 });
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
|
||||
try testing.expectEqual(font.Presentation.text, face.presentation);
|
||||
|
@ -899,11 +899,19 @@ fn testShaper(alloc: Allocator) !TestShaper {
|
||||
errdefer cache_ptr.*.deinit(alloc);
|
||||
|
||||
// Setup group
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(lib, testFont, .{ .points = 12 }) });
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12 } },
|
||||
) });
|
||||
|
||||
if (font.options.backend != .coretext) {
|
||||
// Coretext doesn't support Noto's format
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(lib, testEmoji, .{ .points = 12 }) });
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(
|
||||
lib,
|
||||
testEmoji,
|
||||
.{ .size = .{ .points = 12 } },
|
||||
) });
|
||||
} else {
|
||||
// On CoreText we want to load Apple Emoji, we should have it.
|
||||
var disco = font.Discover.init();
|
||||
@ -918,7 +926,11 @@ fn testShaper(alloc: Allocator) !TestShaper {
|
||||
errdefer face.deinit();
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .deferred = face });
|
||||
}
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(lib, testEmojiText, .{ .points = 12 }) });
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(
|
||||
lib,
|
||||
testEmojiText,
|
||||
.{ .size = .{ .points = 12 } },
|
||||
) });
|
||||
|
||||
var shaper = try Shaper.init(alloc, .{});
|
||||
errdefer shaper.deinit();
|
||||
|
Reference in New Issue
Block a user