Nerd Font Constraint Fixes (#7834)

Fixes #7820, and while fixing that I noticed that we need to respect the
cell width constraints since certain glyphs should not expand to 2
cells; before fixing that the heavy bracket would align differently
depending on if it had whitespace after it, which was obviously wrong
and looked terrible.
This commit is contained in:
Mitchell Hashimoto
2025-07-06 12:06:13 -07:00
committed by GitHub
5 changed files with 85 additions and 26 deletions

View File

@ -147,6 +147,9 @@ pub const RenderOptions = struct {
/// Maximum ratio of width to height when resizing.
max_xy_ratio: ?f64 = null,
/// Maximum number of cells horizontally to use.
max_constraint_width: u2 = 2,
pub const Size = enum {
/// Don't change the size of this glyph.
none,
@ -186,16 +189,26 @@ pub const RenderOptions = struct {
pub fn constrain(
self: Constraint,
glyph: GlyphSize,
/// Available width
/// Width of one cell.
cell_width: f64,
/// Available height
/// Height of one cell.
cell_height: f64,
/// Number of cells horizontally available for this glyph.
constraint_width: u2,
) GlyphSize {
var g = glyph;
const w = cell_width -
self.pad_left * cell_width -
self.pad_right * cell_width;
const available_width =
cell_width * @as(f64, @floatFromInt(
@min(
self.max_constraint_width,
constraint_width,
),
));
const w = available_width -
self.pad_left * available_width -
self.pad_right * available_width;
const h = cell_height -
self.pad_top * cell_height -
self.pad_bottom * cell_height;
@ -203,7 +216,7 @@ pub const RenderOptions = struct {
// Subtract padding from the bearings so that our
// alignment and sizing code works correctly. We
// re-add before returning.
g.x -= self.pad_left * cell_width;
g.x -= self.pad_left * available_width;
g.y -= self.pad_bottom * cell_height;
switch (self.size_horizontal) {
@ -305,7 +318,7 @@ pub const RenderOptions = struct {
}
// Re-add our padding before returning.
g.x += self.pad_left * cell_width;
g.x += self.pad_left * available_width;
g.y += self.pad_bottom * cell_height;
return g;

View File

@ -337,7 +337,7 @@ pub const Face = struct {
};
const metrics = opts.grid_metrics;
const cell_width: f64 = @floatFromInt(metrics.cell_width * opts.constraint_width);
const cell_width: f64 = @floatFromInt(metrics.cell_width);
const cell_height: f64 = @floatFromInt(metrics.cell_height);
const glyph_size = opts.constraint.constrain(
@ -349,6 +349,7 @@ pub const Face = struct {
},
cell_width,
cell_height,
opts.constraint_width,
);
const width = glyph_size.width;

View File

@ -390,7 +390,7 @@ pub const Face = struct {
// Next we need to apply any constraints.
const metrics = opts.grid_metrics;
const cell_width: f64 = @floatFromInt(metrics.cell_width * opts.constraint_width);
const cell_width: f64 = @floatFromInt(metrics.cell_width);
const cell_height: f64 = @floatFromInt(metrics.cell_height);
const glyph_x: f64 = f26dot6ToF64(glyph.*.metrics.horiBearingX);
@ -405,6 +405,7 @@ pub const Face = struct {
},
cell_width,
cell_height,
opts.constraint_width,
);
const width = glyph_size.width;

View File

@ -13,6 +13,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
.pad_left = -0.02,
@ -24,24 +25,29 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
.pad_left = 0.1,
.pad_right = 0.1,
.pad_top = 0.01,
.pad_bottom = 0.01,
.pad_top = 0.1,
.pad_bottom = 0.1,
},
0x276c...0x2771,
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
.pad_top = 0.3,
.pad_bottom = 0.3,
},
0xe0b0,
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.pad_left = -0.06,
@ -54,6 +60,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.max_xy_ratio = 0.7,
@ -62,6 +69,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.pad_left = -0.06,
@ -74,6 +82,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.max_xy_ratio = 0.7,
@ -82,6 +91,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.pad_left = -0.06,
@ -94,6 +104,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.max_xy_ratio = 0.5,
@ -102,6 +113,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.pad_left = -0.06,
@ -114,6 +126,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.max_xy_ratio = 0.5,
@ -123,6 +136,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.pad_left = -0.05,
@ -135,6 +149,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
},
@ -143,6 +158,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.pad_left = -0.05,
@ -155,6 +171,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
},
@ -204,8 +221,8 @@ pub fn getConstraint(cp: u21) Constraint {
.align_vertical = .center,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.01,
.pad_bottom = 0.01,
.pad_top = 0.03,
.pad_bottom = 0.03,
.max_xy_ratio = 0.86,
},
0xe0c5,
@ -216,8 +233,8 @@ pub fn getConstraint(cp: u21) Constraint {
.align_vertical = .center,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.01,
.pad_bottom = 0.01,
.pad_top = 0.03,
.pad_bottom = 0.03,
.max_xy_ratio = 0.86,
},
0xe0c6,
@ -228,8 +245,8 @@ pub fn getConstraint(cp: u21) Constraint {
.align_vertical = .center,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.01,
.pad_bottom = 0.01,
.pad_top = 0.03,
.pad_bottom = 0.03,
.max_xy_ratio = 0.78,
},
0xe0c7,
@ -240,8 +257,8 @@ pub fn getConstraint(cp: u21) Constraint {
.align_vertical = .center,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.01,
.pad_bottom = 0.01,
.pad_top = 0.03,
.pad_bottom = 0.03,
.max_xy_ratio = 0.78,
},
0xe0cc,
@ -285,6 +302,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.pad_left = -0.02,
@ -297,6 +315,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.pad_left = -0.02,
@ -309,6 +328,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
.align_vertical = .center,
.pad_left = -0.05,
@ -321,6 +341,7 @@ pub fn getConstraint(cp: u21) Constraint {
=> .{
.size_horizontal = .stretch,
.size_vertical = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
.align_vertical = .center,
.pad_left = -0.05,

View File

@ -6,13 +6,15 @@ attributes and scaling rules.
This does include an `eval` call! This is spooky, but we trust the nerd fonts code to
be safe and not malicious or anything.
This script requires Python 3.12 or greater.
"""
import ast
import math
import sys
from collections import defaultdict
from contextlib import suppress
from pathlib import Path
from types import SimpleNamespace
from typing import Literal, TypedDict, cast
@ -188,18 +190,31 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
s += " .size_horizontal = .fit,\n"
s += " .size_vertical = .fit,\n"
# There are two cases where we want to limit the constraint width to 1:
# - If there's a `1` in the stretch mode string.
# - If the stretch mode is `xy` and there's not an explicit `2`.
if "1" in stretch or ("xy" in stretch and "2" not in stretch):
s += " .max_constraint_width = 1,\n"
if align is not None:
s += f" .align_horizontal = {align},\n"
if valign is not None:
s += f" .align_vertical = {valign},\n"
# `overlap` and `ypadding` are mutually exclusive,
# this is asserted in the nerd fonts patcher itself.
if overlap:
pad = -overlap
s += f" .pad_left = {pad},\n"
s += f" .pad_right = {pad},\n"
v_pad = y_padding - math.copysign(min(0.01, abs(overlap)), overlap)
# In the nerd fonts patcher, overlap values
# are capped at 0.01 in the vertical direction.
v_pad = -min(0.01, overlap)
s += f" .pad_top = {v_pad},\n"
s += f" .pad_bottom = {v_pad},\n"
elif y_padding:
s += f" .pad_top = {y_padding},\n"
s += f" .pad_bottom = {y_padding},\n"
if xy_ratio > 0:
s += f" .max_xy_ratio = {xy_ratio},\n"
@ -236,9 +251,16 @@ def generate_zig_switch_arms(patch_sets: list[PatchSet]) -> str:
if __name__ == "__main__":
source = sys.stdin.read()
project_root = Path(__file__).resolve().parents[2]
patcher_path = project_root / "vendor" / "nerd-fonts" / "font-patcher.py"
source = patcher_path.read_text(encoding="utf-8")
patch_set = extract_patch_set_values(source)
print("""//! This is a generated file, produced by nerd_font_codegen.py
out_path = project_root / "src" / "font" / "nerd_font_attributes.zig"
with out_path.open("w", encoding="utf-8") as f:
f.write("""//! This is a generated file, produced by nerd_font_codegen.py
//! DO NOT EDIT BY HAND!
//!
//! This file provides info extracted from the nerd fonts patcher script,
@ -248,6 +270,7 @@ const Constraint = @import("face.zig").RenderOptions.Constraint;
/// Get the a constraints for the provided codepoint.
pub fn getConstraint(cp: u21) Constraint {
return switch (cp) {""")
print(generate_zig_switch_arms(patch_set))
print(" else => .none,\n };\n}")
return switch (cp) {
""")
f.write(generate_zig_switch_arms(patch_set))
f.write("\n else => .none,\n };\n}\n")