font: add scale groups to nerd font constraints

We do this by characterizing the shared bounding boxes in a static copy
of the symbols only nerd font when we're doing the codegen. This allows
us to get results of our scaling that are just as good as in a patched
font, since related glyphs can now be sized and positioned relative to
each other.
This commit is contained in:
Qwerasd
2025-07-08 12:00:22 -06:00
parent 1430660933
commit 8b8e0bedad
3 changed files with 818 additions and 9 deletions

View File

@ -144,6 +144,23 @@ pub const RenderOptions = struct {
/// Bottom padding when resizing.
pad_bottom: f64 = 0.0,
// This acts as a multiple of the provided width when applying
// constraints, so if this is 1.6 for example, then a width of
// 10 would be treated as though it were 16.
group_width: f64 = 1.0,
// This acts as a multiple of the provided height when applying
// constraints, so if this is 1.6 for example, then a height of
// 10 would be treated as though it were 16.
group_height: f64 = 1.0,
// This is an x offset for the actual width within the group width.
// If this is 0.5 then the glyph will be offset so that its left
// edge sits at the halfway point of the group width.
group_x: f64 = 0.0,
// This is a y offset for the actual height within the group height.
// If this is 0.5 then the glyph will be offset so that its bottom
// edge sits at the halfway point of the group height.
group_y: f64 = 0.0,
/// Maximum ratio of width to height when resizing.
max_xy_ratio: ?f64 = null,
@ -245,6 +262,10 @@ pub const RenderOptions = struct {
g.x -= self.pad_left * available_width;
g.y -= self.pad_bottom * available_height;
// Multiply by group width and height for better sizing.
g.width *= self.group_width;
g.height *= self.group_height;
switch (self.size_horizontal) {
.none => {},
.fit => if (g.width > w) {
@ -323,6 +344,14 @@ pub const RenderOptions = struct {
},
}
// Add group-relative position
g.x += self.group_x * g.width;
g.y += self.group_y * g.height;
// Divide group width and height back out before we align.
g.width /= self.group_width;
g.height /= self.group_height;
if (self.max_xy_ratio) |ratio| if (g.width > g.height * ratio) {
const orig_width = g.width;
g.width = g.height * ratio;

View File

@ -34,7 +34,35 @@ pub fn getConstraint(cp: u21) Constraint {
.pad_top = 0.1,
.pad_bottom = 0.1,
},
0x276c...0x2771,
0x276c...0x276d,
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.4028056112224450,
.group_height = 1.1222570532915361,
.group_x = 0.1428571428571428,
.group_y = 0.0349162011173184,
.pad_top = 0.15,
.pad_bottom = 0.15,
},
0x276e...0x276f,
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
.max_constraint_width = 1,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0115606936416186,
.group_height = 1.1222570532915361,
.group_x = 0.0057142857142857,
.group_y = 0.0125698324022346,
.pad_top = 0.15,
.pad_bottom = 0.15,
},
0x2770...0x2771,
=> .{
.size_horizontal = .cover,
.size_vertical = .fit,
@ -357,8 +385,46 @@ pub fn getConstraint(cp: u21) Constraint {
0x2b58,
0xe000...0xe0a9,
0xe4fa...0xe7ef,
0xea60...0xec1e,
0xed00...0xf847,
0xea60,
0xea62...0xea7c,
0xea7e...0xea98,
0xeaa3...0xeab3,
0xeab8...0xead3,
0xead7...0xeb42,
0xeb44...0xeb6d,
0xeb72...0xeb89,
0xeb8b...0xeb99,
0xeb9b...0xebd4,
0xebd6,
0xebd8...0xec06,
0xec08...0xec0a,
0xec0d...0xec1e,
0xed00...0xf018,
0xf01a...0xf02f,
0xf031...0xf03c,
0xf041...0xf043,
0xf045...0xf049,
0xf04b...0xf050,
0xf054...0xf059,
0xf05c...0xf070,
0xf072...0xf077,
0xf079...0xf07a,
0xf07c...0xf080,
0xf082...0xf08b,
0xf08d...0xf091,
0xf093...0xf09b,
0xf09d...0xf09e,
0xf0a0,
0xf0a5...0xf0a9,
0xf0ab...0xf0c9,
0xf0cb...0xf0d5,
0xf0d7...0xf0dd,
0xf0df...0xf0e6,
0xf0e8...0xf295,
0xf297...0xf2c1,
0xf2c6...0xf2ef,
0xf2f1...0xf305,
0xf307...0xf847,
0xf0001...0xf1af0,
=> .{
.size_horizontal = .fit,
@ -367,6 +433,641 @@ pub fn getConstraint(cp: u21) Constraint {
.align_horizontal = .center,
.align_vertical = .center,
},
0xea61,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.3315669947009841,
.group_height = 1.0763840224246670,
.group_x = 0.0847072200113701,
.group_y = 0.0709635416666667,
},
0xea7d,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1913145539906103,
.group_height = 1.1428571428571428,
.group_x = 0.0916256157635468,
.group_y = 0.0415039062500000,
},
0xea99,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0639412997903563,
.group_height = 2.0940695296523519,
.group_x = 0.0295566502463054,
.group_y = 0.2270507812500000,
},
0xea9a,
0xeaa1,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.3029525032092426,
.group_height = 1.1729667812142039,
.group_x = 0.1527093596059113,
.group_y = 0.0751953125000000,
},
0xea9b,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1639908256880733,
.group_height = 1.3128205128205128,
.group_x = 0.0719211822660099,
.group_y = 0.0869140625000000,
},
0xea9c,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1639908256880733,
.group_height = 1.3195876288659794,
.group_x = 0.0719211822660099,
.group_y = 0.0830078125000000,
},
0xea9d,
0xeaa0,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 2.4457831325301207,
.group_height = 1.9692307692307693,
.group_x = 0.2857142857142857,
.group_y = 0.2763671875000000,
},
0xea9e...0xea9f,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.9556840077071291,
.group_height = 2.4674698795180725,
.group_x = 0.2137931034482759,
.group_y = 0.3066406250000000,
},
0xeaa2,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.2412121212121212,
.group_height = 1.0591799039527152,
.group_x = 0.0683593750000000,
.group_y = 0.0146484375000000,
},
0xeab4,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0049115913555993,
.group_height = 1.8998144712430427,
.group_y = 0.2026367187500000,
},
0xeab5,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.8979591836734695,
.group_height = 1.0054000981836033,
.group_x = 0.2023460410557185,
.group_y = 0.0053710937500000,
},
0xeab6,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.8979591836734695,
.group_height = 1.0054000981836033,
.group_x = 0.2707722385141740,
},
0xeab7,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0049115913555993,
.group_height = 1.8980537534754403,
.group_x = 0.0048875855327468,
.group_y = 0.2709960937500000,
},
0xead4...0xead5,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.4152542372881356,
.group_x = 0.1486118671747414,
},
0xead6,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 1.1390433815350389,
.group_y = 0.0688476562500000,
},
0xeb43,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.3635153129161119,
.group_height = 1.0002360944082516,
.group_x = 0.1992187500000000,
.group_y = 0.0002360386808388,
},
0xeb6e,
0xeb71,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 2.0197238658777121,
.group_y = 0.2524414062500000,
},
0xeb6f,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 2.0098619329388558,
.group_x = 0.2492639842983317,
},
0xeb70,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 2.0098619329388558,
.group_height = 1.0039215686274510,
.group_x = 0.2492639842983317,
.group_y = 0.0039062500000000,
},
0xeb8a,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 2.8826979472140764,
.group_height = 2.9804097167804766,
.group_x = 0.2634791454730417,
.group_y = 0.3314678485576923,
},
0xeb9a,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1441340782122904,
.group_height = 1.0591799039527152,
.group_x = 0.0683593750000000,
.group_y = 0.0146484375000000,
},
0xebd5,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0723270440251573,
.group_height = 1.0728129910948141,
.group_y = 0.0678710937500000,
},
0xebd7,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 1.0000418544302916,
.group_y = 0.0000418526785714,
},
0xec07,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 2.8615369874243446,
.group_height = 2.9789458113505249,
.group_x = 0.2609446802539727,
.group_y = 0.3313029661016949,
},
0xec0b,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0722513089005237,
.group_height = 1.0002360944082516,
.group_y = 0.0002360386808388,
},
0xec0c,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.2487804878048780,
.group_x = 0.1992187500000000,
},
0xf019,
0xf08c,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0004882812500000,
},
0xf030,
0xf03e,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0004882812500000,
.group_height = 1.1428571428571428,
.group_y = 0.0625000000000000,
},
0xf03d,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0004882812500000,
.group_height = 1.5014662756598240,
.group_y = 0.1669921875000000,
},
0xf03f,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.6018762826150690,
.group_x = 0.1876220107369448,
},
0xf040,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0006976906439684,
.group_height = 1.0001808776182035,
.group_x = 0.0006972042111134,
.group_y = 0.0001808449074074,
},
0xf044,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1029147024980515,
.group_height = 1.1024142703367676,
.group_x = 0.0463592039005675,
.group_y = 0.0430325010461710,
},
0xf04a,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0004882812500000,
.group_height = 1.3312975252838291,
.group_y = 0.1245571402616279,
},
0xf051,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.6007812500000000,
.group_height = 1.3312170271945341,
.group_x = 0.1874084919472914,
.group_y = 0.1245117187500000,
},
0xf052,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1436671384194865,
.group_height = 1.1430165816326530,
.group_x = 0.0624629273607646,
.group_y = 0.0625610266424885,
},
0xf053,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.8765709864847797,
.group_height = 1.0707191397207079,
.group_x = 0.2332599943628554,
.group_y = 0.0332682382480123,
},
0xf05a...0xf05b,
0xf081,
0xf092,
0xf0aa,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005935142780173,
.group_height = 1.0001395089285714,
.group_y = 0.0000697447342726,
},
0xf071,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0014662756598240,
.group_height = 1.1428571428571428,
.group_x = 0.0004880429477794,
.group_y = 0.0625000000000000,
},
0xf078,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0717654378877508,
.group_height = 1.8757195185766613,
.group_x = 0.0331834301604062,
.group_y = 0.1670386385827870,
},
0xf07b,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 1.1428571428571428,
.group_y = 0.0625000000000000,
},
0xf09c,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 1.0810546875000000,
},
0xf09f,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.7506617925122907,
.group_height = 1.0810546875000000,
.group_x = 0.2143937211981567,
},
0xf0a1,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0009775171065494,
.group_x = 0.0004882812500000,
},
0xf0a2,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.1433271023627367,
.group_height = 1.0001395089285714,
.group_x = 0.0624235731978609,
.group_y = 0.0000697447342726,
},
0xf0a3,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005760656161586,
.group_height = 1.0001220681837999,
.group_x = 0.0004792774839344,
.group_y = 0.0000610266424885,
},
0xf0a4,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005935142780173,
.group_height = 1.3335193452380951,
.group_y = 0.1250523085507044,
},
0xf0ca,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005935142780173,
.group_height = 1.1922501247297521,
.group_y = 0.0806249128190822,
},
0xf0d6,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_height = 1.5014662756598240,
.group_y = 0.1669921875000000,
},
0xf0de,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.2253421114919656,
.group_height = 2.5216400911161729,
.group_x = 0.0918898809523810,
.group_y = 0.6034327009936766,
},
0xf0e7,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.3336843856081169,
.group_x = 0.1247597299147187,
},
0xf296,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005148743038617,
.group_height = 1.0385966606705219,
.group_x = 0.0005146093447336,
.group_y = 0.0186218440507742,
},
0xf2c2...0xf2c3,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0000770970394737,
.group_height = 1.2864321608040201,
.group_y = 0.1113281250000000,
},
0xf2c4,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0344231791600214,
.group_x = 0.0166002826673519,
},
0xf2c5,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0004538836055876,
.group_height = 1.4840579710144928,
.group_x = 0.0004536776887225,
.group_y = 0.1630859375000000,
},
0xf2f0,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.0005935142780173,
.group_height = 1.0334438518091393,
.group_y = 0.0161807783512345,
},
0xf306,
=> .{
.size_horizontal = .fit,
.size_vertical = .fit,
.height = .icon,
.align_horizontal = .center,
.align_vertical = .center,
.group_width = 1.2427184466019416,
.group_x = 0.0976562500000000,
},
else => .none,
};
}

View File

@ -7,11 +7,16 @@ 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.
This script requires Python 3.12 or greater, requires that the `fontTools`
python module is installed, and requires that the path to a copy of the
SymbolsNerdFontMono font is passed as the first argument to the script.
"""
import ast
import sys
import math
from fontTools.ttLib import TTFont
from fontTools.pens.boundsPen import BoundsPen
from collections import defaultdict
from contextlib import suppress
from pathlib import Path
@ -19,7 +24,18 @@ from types import SimpleNamespace
from typing import Literal, TypedDict, cast
type PatchSetAttributes = dict[Literal["default"] | int, PatchSetAttributeEntry]
type AttributeHash = tuple[str | None, str | None, str, float, float, float]
type AttributeHash = tuple[
str | None,
str | None,
str,
float,
float,
float,
float,
float,
float,
float,
]
type ResolvedSymbol = PatchSetAttributes | PatchSetScaleRules | int | None
@ -34,6 +50,11 @@ class PatchSetAttributeEntry(TypedDict):
stretch: str
params: dict[str, float | bool]
group_x: float
group_y: float
group_width: float
group_height: float
class PatchSet(TypedDict):
SymStart: int
@ -137,6 +158,10 @@ def attr_key(attr: PatchSetAttributeEntry) -> AttributeHash:
float(params.get("overlap", 0.0)),
float(params.get("xy-ratio", -1.0)),
float(params.get("ypadding", 0.0)),
float(attr.get("group_x", 0.0)),
float(attr.get("group_y", 0.0)),
float(attr.get("group_width", 1.0)),
float(attr.get("group_height", 1.0)),
)
@ -162,6 +187,11 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
stretch = attr.get("stretch", "")
params = attr.get("params", {})
group_x = attr.get("group_x", 0.0)
group_y = attr.get("group_y", 0.0)
group_width = attr.get("group_width", 1.0)
group_height = attr.get("group_height", 1.0)
overlap = params.get("overlap", 0.0)
xy_ratio = params.get("xy-ratio", -1.0)
y_padding = params.get("ypadding", 0.0)
@ -192,7 +222,6 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
if "^" not in stretch:
s += " .height = .icon,\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`.
@ -204,6 +233,15 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
if valign is not None:
s += f" .align_vertical = {valign},\n"
if group_width != 1.0:
s += f" .group_width = {group_width:.16f},\n"
if group_height != 1.0:
s += f" .group_height = {group_height:.16f},\n"
if group_x != 0.0:
s += f" .group_x = {group_x:.16f},\n"
if group_y != 0.0:
s += f" .group_y = {group_y:.16f},\n"
# `overlap` and `ypadding` are mutually exclusive,
# this is asserted in the nerd fonts patcher itself.
if overlap:
@ -226,16 +264,53 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
return s
def generate_zig_switch_arms(patch_sets: list[PatchSet]) -> str:
def generate_zig_switch_arms(
patch_sets: list[PatchSet],
nerd_font: TTFont,
) -> str:
cmap = nerd_font.getBestCmap()
glyphs = nerd_font.getGlyphSet()
entries: dict[int, PatchSetAttributeEntry] = {}
for entry in patch_sets:
attributes = entry["Attributes"]
for cp in range(entry["SymStart"], entry["SymEnd"] + 1):
entries[cp] = attributes["default"]
entries[cp] = attributes["default"].copy()
entries |= {k: v for k, v in attributes.items() if isinstance(k, int)}
if entry["ScaleRules"] is not None and "ScaleGroups" in entry["ScaleRules"]:
for group in entry["ScaleRules"]["ScaleGroups"]:
xMin = math.inf
yMin = math.inf
xMax = -math.inf
yMax = -math.inf
individual_bounds: dict[int, tuple[int, int, int ,int]] = {}
for cp in group:
if cp not in cmap:
continue
glyph = glyphs[cmap[cp]]
bounds = BoundsPen(glyphSet=glyphs)
glyph.draw(bounds)
individual_bounds[cp] = bounds.bounds
xMin = min(bounds.bounds[0], xMin)
yMin = min(bounds.bounds[1], yMin)
xMax = max(bounds.bounds[2], xMax)
yMax = max(bounds.bounds[3], yMax)
group_width = xMax - xMin
group_height = yMax - yMin
for cp in group:
if cp not in cmap or cp not in entries:
continue
this_bounds = individual_bounds[cp]
this_width = this_bounds[2] - this_bounds[0]
this_height = this_bounds[3] - this_bounds[1]
entries[cp]["group_width"] = group_width / this_width
entries[cp]["group_height"] = group_height / this_height
entries[cp]["group_x"] = (this_bounds[0] - xMin) / group_width
entries[cp]["group_y"] = (this_bounds[1] - yMin) / group_height
del entries[0]
# Group codepoints by attribute key
@ -256,6 +331,10 @@ def generate_zig_switch_arms(patch_sets: list[PatchSet]) -> str:
if __name__ == "__main__":
project_root = Path(__file__).resolve().parents[2]
nf_path = sys.argv[1]
nerd_font = TTFont(nf_path)
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)
@ -275,5 +354,5 @@ const Constraint = @import("face.zig").RenderOptions.Constraint;
pub fn getConstraint(cp: u21) Constraint {
return switch (cp) {
""")
f.write(generate_zig_switch_arms(patch_set))
f.write(generate_zig_switch_arms(patch_set, nerd_font))
f.write("\n else => .none,\n };\n}\n")