config: font-synthetic-style to enable/disable synthetic styles

This adds a new configuration "font-synthetic-style" to enable or
disable synthetic styles. This is different from "font-style-*" which
specifies a named style or disables a style completely.

Instead, "font-synthetic-style" will disable only the creation of
synthetic styles in the case a font does not support a given style.
This is useful for users who want to obviously know when a font doesn't
support a given style or a user who wants to explicitly only use the
styles that were designed by the font designer.

The default value is to enable all synthetic styles.
This commit is contained in:
Mitchell Hashimoto
2024-08-26 20:46:14 -07:00
parent 80327402b8
commit bdcc21942d
4 changed files with 70 additions and 4 deletions

View File

@ -14,6 +14,7 @@ pub const formatEntry = formatter.formatEntry;
pub const ClipboardAccess = Config.ClipboardAccess;
pub const CopyOnSelect = Config.CopyOnSelect;
pub const CustomShaderAnimation = Config.CustomShaderAnimation;
pub const FontSyntheticStyle = Config.FontSyntheticStyle;
pub const FontStyle = Config.FontStyle;
pub const Keybinds = Config.Keybinds;
pub const MouseShiftCapture = Config.MouseShiftCapture;

View File

@ -107,6 +107,38 @@ const c = @cImport({
@"font-style-italic": FontStyle = .{ .default = {} },
@"font-style-bold-italic": FontStyle = .{ .default = {} },
/// Control whether Ghostty should synthesize a style if the requested style is
/// not available in the specified font-family.
///
/// Ghostty can synthesize bold, italic, and bold italic styles if the font
/// does not have a specific style. For bold, this is done by drawing an
/// outline around the glyph of varying thickness. For italic, this is done by
/// applying a slant to the glyph. For bold italic, both of these are applied.
///
/// Synthetic styles are not perfect and will generally not look as good
/// as a font that has the style natively. However, they are useful to
/// provide styled text when the font does not have the style.
///
/// Set this to "false" or "true" to disable or enable synthetic styles
/// completely. You can disable specific styles using "no-bold", "no-italic",
/// and "no-bold-italic". You can disable multiple styles by separating them
/// with a comma. For example, "no-bold,no-italic".
///
/// Available style keys are: `bold`, `italic`, `bold-italic`.
///
/// If synthetic styles are disabled, then the regular style will be used
/// instead if the requested style is not available. If the font has the
/// requested style, then the font will be used as-is since the style is
/// not synthetic.
///
/// Warning! An easy mistake is to disable `bold` or `italic` but not
/// `bold-italic`. Disabling only `bold` or `italic` will NOT disable either
/// in the `bold-italic` style. If you want to disable `bold-italic`, you must
/// explicitly disable it. You cannot partially disable `bold-italic`.
///
/// By default, synthetic styles are enabled.
@"font-synthetic-style": FontSyntheticStyle = .{},
/// Apply a font feature. This can be repeated multiple times to enable multiple
/// font features. You can NOT set multiple font features with a single value
/// (yet).
@ -3882,6 +3914,13 @@ pub const FontStyle = union(enum) {
}
};
/// See `font-synthetic-style` for documentation.
pub const FontSyntheticStyle = packed struct {
bold: bool = true,
italic: bool = true,
@"bold-italic": bool = true,
};
/// See "link" for documentation.
pub const RepeatableLink = struct {
const Self = @This();

View File

@ -18,6 +18,7 @@ const Collection = @This();
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const config = @import("../config.zig");
const font = @import("main.zig");
const options = font.options;
const DeferredFace = font.DeferredFace;
@ -207,7 +208,11 @@ pub const CompleteError = Allocator.Error || error{
/// This requires that a regular font face is already loaded.
/// This is asserted. If a font style is missing, we will synthesize
/// it if possible. Otherwise, we will use the regular font style.
pub fn completeStyles(self: *Collection, alloc: Allocator) CompleteError!void {
pub fn completeStyles(
self: *Collection,
alloc: Allocator,
synthetic_config: config.FontSyntheticStyle,
) CompleteError!void {
// If every style has at least one entry then we're done!
// This is the most common case.
empty: {
@ -262,6 +267,12 @@ pub fn completeStyles(self: *Collection, alloc: Allocator) CompleteError!void {
const italic_list = self.faces.getPtr(.italic);
const have_italic = italic_list.count() > 0;
if (!have_italic) italic: {
if (!synthetic_config.italic) {
log.info("italic style not available and synthetic italic disabled", .{});
try italic_list.append(alloc, .{ .alias = regular_entry });
break :italic;
}
const synthetic = self.syntheticItalic(regular_entry) catch |err| {
log.warn("failed to create synthetic italic, italic style will not be available err={}", .{err});
try italic_list.append(alloc, .{ .alias = regular_entry });
@ -276,6 +287,12 @@ pub fn completeStyles(self: *Collection, alloc: Allocator) CompleteError!void {
const bold_list = self.faces.getPtr(.bold);
const have_bold = bold_list.count() > 0;
if (!have_bold) bold: {
if (!synthetic_config.bold) {
log.info("bold style not available and synthetic bold disabled", .{});
try bold_list.append(alloc, .{ .alias = regular_entry });
break :bold;
}
const synthetic = self.syntheticBold(regular_entry) catch |err| {
log.warn("failed to create synthetic bold, bold style will not be available err={}", .{err});
try bold_list.append(alloc, .{ .alias = regular_entry });
@ -290,6 +307,12 @@ pub fn completeStyles(self: *Collection, alloc: Allocator) CompleteError!void {
// of the italic font. If we can't do that, we'll use the italic font.
const bold_italic_list = self.faces.getPtr(.bold_italic);
if (bold_italic_list.count() == 0) bold_italic: {
if (!synthetic_config.@"bold-italic") {
log.info("bold italic style not available and synthetic bold italic disabled", .{});
try bold_italic_list.append(alloc, .{ .alias = regular_entry });
break :bold_italic;
}
// Prefer to synthesize on top of the face we already had. If we
// have bold then we try to synthesize italic on top of bold.
if (have_bold) {
@ -759,7 +782,7 @@ test completeStyles {
try testing.expect(c.getIndex('A', .bold, .{ .any = {} }) == null);
try testing.expect(c.getIndex('A', .italic, .{ .any = {} }) == null);
try testing.expect(c.getIndex('A', .bold_italic, .{ .any = {} }) == null);
try c.completeStyles(alloc);
try c.completeStyles(alloc, .{});
try testing.expect(c.getIndex('A', .bold, .{ .any = {} }) != null);
try testing.expect(c.getIndex('A', .italic, .{ .any = {} }) != null);
try testing.expect(c.getIndex('A', .bold_italic, .{ .any = {} }) != null);

View File

@ -128,7 +128,7 @@ pub fn ref(
// Build our collection. This is the expensive operation that
// involves finding fonts, loading them (maybe, some are deferred),
// etc.
var c = try self.collection(&key, font_size);
var c = try self.collection(&key, font_size, config);
errdefer c.deinit(self.alloc);
// Setup our enabled/disabled styles
@ -156,6 +156,7 @@ fn collection(
self: *SharedGridSet,
key: *const Key,
size: DesiredSize,
config: *const DerivedConfig,
) !Collection {
// A quick note on memory management:
// - font_lib is owned by the SharedGridSet
@ -300,7 +301,7 @@ fn collection(
// Complete our styles to ensure we have something to satisfy every
// possible style request.
try c.completeStyles(self.alloc);
try c.completeStyles(self.alloc, config.@"font-synthetic-style");
return c;
}
@ -386,6 +387,7 @@ pub const DerivedConfig = struct {
@"font-variation-italic": configpkg.RepeatableFontVariation,
@"font-variation-bold-italic": configpkg.RepeatableFontVariation,
@"font-codepoint-map": configpkg.RepeatableCodepointMap,
@"font-synthetic-style": configpkg.FontSyntheticStyle,
@"adjust-cell-width": ?Metrics.Modifier,
@"adjust-cell-height": ?Metrics.Modifier,
@"adjust-font-baseline": ?Metrics.Modifier,
@ -416,6 +418,7 @@ pub const DerivedConfig = struct {
.@"font-variation-italic" = try config.@"font-variation-italic".clone(alloc),
.@"font-variation-bold-italic" = try config.@"font-variation-bold-italic".clone(alloc),
.@"font-codepoint-map" = try config.@"font-codepoint-map".clone(alloc),
.@"font-synthetic-style" = config.@"font-synthetic-style",
.@"adjust-cell-width" = config.@"adjust-cell-width",
.@"adjust-cell-height" = config.@"adjust-cell-height",
.@"adjust-font-baseline" = config.@"adjust-font-baseline",