Merge remote-tracking branch 'upstream/main' into jacob/uucode

pull/8757/head
Jacob Sandlund 2025-09-18 08:46:08 -04:00
commit ec5e1e504d
3 changed files with 60 additions and 10 deletions

View File

@ -117,6 +117,16 @@ pub const FaceMetrics = struct {
/// lowercase x glyph.
ex_height: ?f64 = null,
/// The measured height of the bounding box containing all printable
/// ASCII characters. This can be different from ascent - descent for
/// two reasons: non-letter symbols like @ and $ often exceed the
/// the ascender and descender lines; and fonts often bake the line
/// gap into the ascent and descent metrics (as per, e.g., the Google
/// Fonts guidelines: https://simoncozens.github.io/gf-docs/metrics.html).
///
/// Positive value in px
ascii_height: ?f64 = null,
/// The width of the character "" (CJK water ideograph, U+6C34),
/// if present. This is used for font size adjustment, to normalize
/// the width of CJK fonts mixed with latin fonts.
@ -144,11 +154,20 @@ pub const FaceMetrics = struct {
return 0.75 * self.capHeight();
}
/// Convenience function for getting the ASCII height. If we
/// couldn't measure this, we use 1.5 * cap_height as our
/// estimator, based on measurements across programming fonts.
pub inline fn asciiHeight(self: FaceMetrics) f64 {
if (self.ascii_height) |value| if (value > 0) return value;
return 1.5 * self.capHeight();
}
/// Convenience function for getting the ideograph width. If this is
/// not defined in the font, we estimate it as two cell widths.
/// not defined in the font, we estimate it as the minimum of the
/// ascii height and two cell widths.
pub inline fn icWidth(self: FaceMetrics) f64 {
if (self.ic_width) |value| if (value > 0) return value;
return 2 * self.cell_width;
return @min(self.asciiHeight(), 2 * self.cell_width);
}
/// Convenience function for getting the underline thickness. If

View File

@ -775,7 +775,10 @@ pub const Face = struct {
// Cell width is calculated by calculating the widest width of the
// visible ASCII characters. Usually 'M' is widest but we just take
// whatever is widest.
const cell_width: f64 = cell_width: {
//
// ASCII height is calculated as the height of the overall bounding
// box of the same characters.
const cell_width: f64, const ascii_height: f64 = measurements: {
// Build a comptime array of all the ASCII chars
const unichars = comptime unichars: {
const len = 127 - 32;
@ -803,7 +806,10 @@ pub const Face = struct {
max = @max(advances[i].width, max);
}
break :cell_width max;
// Get the overall bounding rect for the glyphs
const rect = ct_font.getBoundingRectsForGlyphs(.horizontal, &glyphs, null);
break :measurements .{ max, rect.size.height };
};
// Measure "" (CJK water ideograph, U+6C34) for our ic width.
@ -864,6 +870,7 @@ pub const Face = struct {
.cap_height = cap_height,
.ex_height = ex_height,
.ascii_height = ascii_height,
.ic_width = ic_width,
};
}

View File

@ -960,13 +960,19 @@ pub const Face = struct {
// visible ASCII characters. Usually 'M' is widest but we just take
// whatever is widest.
//
// ASCII height is calculated as the height of the overall bounding
// box of the same characters.
//
// If we fail to load any visible ASCII we just use max_advance from
// the metrics provided by FreeType.
const cell_width: f64 = cell_width: {
// the metrics provided by FreeType, and set ascii_height to null as
// it's optional.
const cell_width: f64, const ascii_height: ?f64 = measurements: {
self.ft_mutex.lock();
defer self.ft_mutex.unlock();
var max: f64 = 0.0;
var top: f64 = 0.0;
var bottom: f64 = 0.0;
var c: u8 = ' ';
while (c < 127) : (c += 1) {
if (face.getCharIndex(c)) |glyph_index| {
@ -974,20 +980,37 @@ pub const Face = struct {
.render = false,
.no_svg = true,
})) {
const glyph = face.handle.*.glyph;
max = @max(
f26dot6ToF64(face.handle.*.glyph.*.advance.x),
f26dot6ToF64(glyph.*.advance.x),
max,
);
top = @max(
f26dot6ToF64(glyph.*.metrics.horiBearingY),
top,
);
bottom = @min(
f26dot6ToF64(glyph.*.metrics.horiBearingY - glyph.*.metrics.height),
bottom,
);
} else |_| {}
}
}
// If we couldn't get any widths, just use FreeType's max_advance.
// If we couldn't get valid measurements, just use
// FreeType's max_advance and null, respectively.
if (max == 0.0) {
break :cell_width f26dot6ToF64(size_metrics.max_advance);
max = f26dot6ToF64(size_metrics.max_advance);
}
const rect_height: ?f64 = rect_height: {
const estimate = top - bottom;
if (estimate <= 0.0) {
break :rect_height null;
}
break :rect_height estimate;
};
break :cell_width max;
break :measurements .{ max, rect_height };
};
// We use the cap and ex heights specified by the font if they're
@ -1089,6 +1112,7 @@ pub const Face = struct {
.cap_height = cap_height,
.ex_height = ex_height,
.ascii_height = ascii_height,
.ic_width = ic_width,
};
}