diff --git a/src/font/face.zig b/src/font/face.zig index 363576ff0..245edcf4b 100644 --- a/src/font/face.zig +++ b/src/font/face.zig @@ -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; diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig index 89d771d95..5c9c259d2 100644 --- a/src/font/face/coretext.zig +++ b/src/font/face/coretext.zig @@ -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; diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig index 585d21c5b..b27b28ab8 100644 --- a/src/font/face/freetype.zig +++ b/src/font/face/freetype.zig @@ -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; diff --git a/src/font/nerd_font_attributes.zig b/src/font/nerd_font_attributes.zig index dfb11c5a5..817d838f8 100644 --- a/src/font/nerd_font_attributes.zig +++ b/src/font/nerd_font_attributes.zig @@ -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, diff --git a/src/font/nerd_font_codegen.py b/src/font/nerd_font_codegen.py index 52d70ac01..f8ff7caa6 100644 --- a/src/font/nerd_font_codegen.py +++ b/src/font/nerd_font_codegen.py @@ -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")