mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-23 04:06:13 +03:00
Make normalization reference metric customizable per font
This commit is contained in:
@ -124,8 +124,7 @@ pub const AdjustSizeError = font.Face.GetMetricsError;
|
|||||||
|
|
||||||
// Calculate a size for the provided face that will match it with the primary
|
// Calculate a size for the provided face that will match it with the primary
|
||||||
// font, metrically, to improve consistency with fallback fonts. Right now we
|
// font, metrically, to improve consistency with fallback fonts. Right now we
|
||||||
// match the font based on the ex height, or the ideograph width if the font
|
// match the font based on the a preferred metric specified per fallback font.
|
||||||
// has ideographs in it.
|
|
||||||
//
|
//
|
||||||
// This returns null if load options is null or if self.load_options is null.
|
// This returns null if load options is null or if self.load_options is null.
|
||||||
//
|
//
|
||||||
@ -134,7 +133,7 @@ pub const AdjustSizeError = font.Face.GetMetricsError;
|
|||||||
//
|
//
|
||||||
// TODO: In the future, provide config options that allow the user to select
|
// TODO: In the future, provide config options that allow the user to select
|
||||||
// which metric should be matched for fallback fonts, instead of hard
|
// which metric should be matched for fallback fonts, instead of hard
|
||||||
// coding it as ex height.
|
// coding it in the font init code.
|
||||||
pub fn adjustedSize(
|
pub fn adjustedSize(
|
||||||
self: *Collection,
|
self: *Collection,
|
||||||
face: *Face,
|
face: *Face,
|
||||||
@ -151,26 +150,26 @@ pub fn adjustedSize(
|
|||||||
const primary_metrics = try primary_face.getMetrics();
|
const primary_metrics = try primary_face.getMetrics();
|
||||||
const face_metrics = try face.getMetrics();
|
const face_metrics = try face.getMetrics();
|
||||||
|
|
||||||
// We use the ex height to match our font sizes, so that the height of
|
// The preferred metric to normalize by is specified by
|
||||||
// lower-case letters matches between all fonts in the fallback chain.
|
// face.reference_metric, however we don't want to normalize by a
|
||||||
//
|
// metric not explicitly defined in `face`, so if needed we fall
|
||||||
// We estimate ex height as 0.75 * cap height if it's not specifically
|
// back through the other possible reference metrics in the order
|
||||||
// provided, and we estimate cap height as 0.75 * ascent in the same case.
|
// shown in the switch statement below. If the reference metric is
|
||||||
//
|
// not defined in the primary font, we use the default estimate from
|
||||||
// If the fallback font has an ic_width we prefer that, for normalization
|
// the face metrics.
|
||||||
// of CJK font sizes when mixed with latin fonts.
|
const line_height_scale = primary_metrics.lineHeight() / face_metrics.lineHeight();
|
||||||
//
|
const scale: f64 = normalize_by: switch (face.reference_metric) {
|
||||||
// We estimate the ic_width as twice the cell width if it isn't provided.
|
.ic_width => if (face_metrics.ic_width) |value| primary_metrics.icWidth() / value else continue :normalize_by .ex_height,
|
||||||
const primary_ex = primary_metrics.exHeight();
|
.ex_height => if (face_metrics.ex_height) |value| primary_metrics.exHeight() / value else continue :normalize_by .cap_height,
|
||||||
const primary_ic = primary_metrics.icWidth();
|
.cap_height => if (face_metrics.cap_height) |value| primary_metrics.capHeight() / value else continue :normalize_by .line_height,
|
||||||
|
.line_height => line_height_scale,
|
||||||
const face_ex = face_metrics.exHeight();
|
.em_size => 1.0,
|
||||||
const face_ic = face_metrics.icWidth();
|
};
|
||||||
|
|
||||||
// If the line height of the scaled font would be larger than
|
// If the line height of the scaled font would be larger than
|
||||||
// the line height of the primary font, we don't want that, so
|
// the line height of the primary font, we don't want that, so
|
||||||
// we take the minimum between matching the ic/ex and the line
|
// we take the minimum between matching the reference metric
|
||||||
// height.
|
// and keeping the line heights within some margin.
|
||||||
//
|
//
|
||||||
// NOTE: We actually allow the line height to be up to 1.2
|
// NOTE: We actually allow the line height to be up to 1.2
|
||||||
// times the primary line height because empirically
|
// times the primary line height because empirically
|
||||||
@ -178,19 +177,13 @@ pub fn adjustedSize(
|
|||||||
//
|
//
|
||||||
// TODO: We should probably provide a config option that lets
|
// TODO: We should probably provide a config option that lets
|
||||||
// the user pick what metric to use for size adjustment.
|
// the user pick what metric to use for size adjustment.
|
||||||
const scale = @min(
|
const capped_scale = @min(scale, 1.2 * line_height_scale);
|
||||||
1.2 * primary_metrics.lineHeight() / face_metrics.lineHeight(),
|
|
||||||
if (face_metrics.ic_width != null)
|
|
||||||
primary_ic / face_ic
|
|
||||||
else
|
|
||||||
primary_ex / face_ex,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make a copy of our load options, set the size to the size of
|
// Make a copy of our load options, set the size to the size of
|
||||||
// the provided face, and then multiply that by our scaling factor.
|
// the provided face, and then multiply that by our scaling factor.
|
||||||
var opts = load_options;
|
var opts = load_options;
|
||||||
opts.size = face.size;
|
opts.size = face.size;
|
||||||
opts.size.points *= @as(f32, @floatCast(scale));
|
opts.size.points *= @as(f32, @floatCast(capped_scale));
|
||||||
|
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,12 @@ pub const freetype_load_flags_default: FreetypeLoadFlags = if (FreetypeLoadFlags
|
|||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
size: DesiredSize,
|
size: DesiredSize,
|
||||||
freetype_load_flags: FreetypeLoadFlags = freetype_load_flags_default,
|
freetype_load_flags: FreetypeLoadFlags = freetype_load_flags_default,
|
||||||
|
|
||||||
|
// reference_metric defaults to ic_width to ensure appropriate
|
||||||
|
// normalization of CJK font sizes when mixed with latin fonts. See
|
||||||
|
// the implementation of Collections.adjustedSize() for fallback
|
||||||
|
// rules when the font does not define the specified metric.
|
||||||
|
reference_metric: ReferenceMetric = .ic_width,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The desired size for loading a font.
|
/// The desired size for loading a font.
|
||||||
@ -57,6 +63,20 @@ pub const DesiredSize = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ReferenceMetric = enum {
|
||||||
|
// The font's ideograph width
|
||||||
|
ic_width,
|
||||||
|
// The font's ex height
|
||||||
|
ex_height,
|
||||||
|
// The font's cap height
|
||||||
|
cap_height,
|
||||||
|
// The font's line height
|
||||||
|
line_height,
|
||||||
|
// The font's em size (i.e., what point sizes like 12 refer to).
|
||||||
|
// Usually equivalent to line height, but that's just convention.
|
||||||
|
em_size,
|
||||||
|
};
|
||||||
|
|
||||||
/// A font variation setting. The best documentation for this I know of
|
/// A font variation setting. The best documentation for this I know of
|
||||||
/// is actually the CSS font-variation-settings property on MDN:
|
/// is actually the CSS font-variation-settings property on MDN:
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings
|
/// https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings
|
||||||
|
@ -34,6 +34,10 @@ pub const Face = struct {
|
|||||||
/// The current size this font is set to.
|
/// The current size this font is set to.
|
||||||
size: font.face.DesiredSize,
|
size: font.face.DesiredSize,
|
||||||
|
|
||||||
|
// The preferred font metric to use when normalizing the size of a
|
||||||
|
// fallback font to the primary font.
|
||||||
|
reference_metric: font.face.ReferenceMetric,
|
||||||
|
|
||||||
/// True if our build is using Harfbuzz. If we're not, we can avoid
|
/// True if our build is using Harfbuzz. If we're not, we can avoid
|
||||||
/// some Harfbuzz-specific code paths.
|
/// some Harfbuzz-specific code paths.
|
||||||
const harfbuzz_shaper = font.options.backend.hasHarfbuzz();
|
const harfbuzz_shaper = font.options.backend.hasHarfbuzz();
|
||||||
@ -110,6 +114,7 @@ pub const Face = struct {
|
|||||||
.hb_font = hb_font,
|
.hb_font = hb_font,
|
||||||
.color = color,
|
.color = color,
|
||||||
.size = opts.size,
|
.size = opts.size,
|
||||||
|
.reference_metric = opts.reference_metric,
|
||||||
};
|
};
|
||||||
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
||||||
|
|
||||||
|
@ -62,6 +62,10 @@ pub const Face = struct {
|
|||||||
/// The current size this font is set to.
|
/// The current size this font is set to.
|
||||||
size: font.face.DesiredSize,
|
size: font.face.DesiredSize,
|
||||||
|
|
||||||
|
// The preferred font metric to use when normalizing the size of a
|
||||||
|
// fallback font to the primary font.
|
||||||
|
reference_metric: font.face.ReferenceMetric,
|
||||||
|
|
||||||
/// Initialize a new font face with the given source in-memory.
|
/// Initialize a new font face with the given source in-memory.
|
||||||
pub fn initFile(
|
pub fn initFile(
|
||||||
lib: Library,
|
lib: Library,
|
||||||
@ -111,6 +115,7 @@ pub const Face = struct {
|
|||||||
.ft_mutex = ft_mutex,
|
.ft_mutex = ft_mutex,
|
||||||
.load_flags = opts.freetype_load_flags,
|
.load_flags = opts.freetype_load_flags,
|
||||||
.size = opts.size,
|
.size = opts.size,
|
||||||
|
.reference_metric = opts.reference_metric,
|
||||||
};
|
};
|
||||||
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user