font: more generic bearing adjustments

This generally adjusts the bearings of any glyph whose original advance
was narrower than the cell, which helps a lot with proportional fallback
glyphs so they aren't just left-aligned. This only applies to situations
where the glyph was originally narrower than the cell, so that we don't
mess up ligatures, and this centers the old advance width in the new one
rather than adjusting proportionally, because otherwise we can mess up
glyphs that are meant to align with others when placed vertically.
This commit is contained in:
Qwerasd
2025-07-06 16:32:22 -06:00
parent b10b0f06c3
commit d3aece21d8
2 changed files with 68 additions and 26 deletions

View File

@ -481,20 +481,42 @@ pub const Face = struct {
// This should be the distance from the left of
// the cell to the left of the glyph's bounding box.
const offset_x: i32 = offset_x: {
var result: i32 = @intFromFloat(@round(x));
// If our cell was resized then we adjust our glyph's
// position relative to the new center. This keeps glyphs
// centered in the cell whether it was made wider or narrower.
if (metrics.original_cell_width) |original_width| {
const before: i32 = @intCast(original_width);
const after: i32 = @intCast(metrics.cell_width);
// Increase the offset by half of the difference
// between the widths to keep things centered.
result += @divTrunc(after - before, 2);
// If the glyph's advance is narrower than the cell width then we
// center the advance of the glyph within the cell width. At first
// I implemented this to proportionally scale the center position
// of the glyph but that messes up glyphs that are meant to align
// vertically with others, so this is a compromise.
//
// This makes it so that when the `adjust-cell-width` config is
// used, or when a fallback font with a different advance width
// is used, we don't get weirdly aligned glyphs.
//
// We don't do this if the constraint has a horizontal alignment,
// since in that case the position was already calculated with the
// new cell width in mind.
if (opts.constraint.align_horizontal == .none) {
var advances: [glyphs.len]macos.graphics.Size = undefined;
_ = self.font.getAdvancesForGlyphs(.horizontal, &glyphs, &advances);
const advance = advances[0].width;
const new_advance =
cell_width * @as(f64, @floatFromInt(opts.cell_width orelse 1));
// If the original advance is greater than the cell width then
// it's possible that this is a ligature or other glyph that is
// intended to overflow the cell to one side or the other, and
// adjusting the bearings could mess that up, so we just leave
// it alone if that's the case.
//
// We also don't want to do anything if the advance is zero or
// less, since this is used for stuff like combining characters.
if (advance > new_advance or advance <= 0.0) {
break :offset_x @intFromFloat(@round(x));
}
break :offset_x @intFromFloat(
@round(x + (new_advance - advance) / 2),
);
} else {
break :offset_x @intFromFloat(@round(x));
}
break :offset_x result;
};
return .{

View File

@ -638,20 +638,40 @@ pub const Face = struct {
// This should be the distance from the left of
// the cell to the left of the glyph's bounding box.
const offset_x: i32 = offset_x: {
var result: i32 = @intFromFloat(@floor(x));
// If our cell was resized then we adjust our glyph's
// position relative to the new center. This keeps glyphs
// centered in the cell whether it was made wider or narrower.
if (metrics.original_cell_width) |original_width| {
const before: i32 = @intCast(original_width);
const after: i32 = @intCast(metrics.cell_width);
// Increase the offset by half of the difference
// between the widths to keep things centered.
result += @divTrunc(after - before, 2);
// If the glyph's advance is narrower than the cell width then we
// center the advance of the glyph within the cell width. At first
// I implemented this to proportionally scale the center position
// of the glyph but that messes up glyphs that are meant to align
// vertically with others, so this is a compromise.
//
// This makes it so that when the `adjust-cell-width` config is
// used, or when a fallback font with a different advance width
// is used, we don't get weirdly aligned glyphs.
//
// We don't do this if the constraint has a horizontal alignment,
// since in that case the position was already calculated with the
// new cell width in mind.
if (opts.constraint.align_horizontal == .none) {
const advance = f26dot6ToFloat(glyph.*.advance.x);
const new_advance =
cell_width * @as(f64, @floatFromInt(opts.cell_width orelse 1));
// If the original advance is greater than the cell width then
// it's possible that this is a ligature or other glyph that is
// intended to overflow the cell to one side or the other, and
// adjusting the bearings could mess that up, so we just leave
// it alone if that's the case.
//
// We also don't want to do anything if the advance is zero or
// less, since this is used for stuff like combining characters.
if (advance > new_advance or advance <= 0.0) {
break :offset_x @intFromFloat(@floor(x));
}
break :offset_x @intFromFloat(
@floor(x + (new_advance - advance) / 2),
);
} else {
break :offset_x @intFromFloat(@floor(x));
}
break :offset_x result;
};
return Glyph{