summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Wennberg <daniel.wennberg@gmail.com>2025-09-06 19:21:25 -0700
committerMitchell Hashimoto <m@mitchellh.com>2025-09-29 12:09:21 -0700
commite3ebdc79756985ee541b6b1ed402da596b39318b (patch)
tree3411098cb9ebbf666a63f809684baf455f91f3eb /src
parentf73666a7a1d7a1af57a1ba4481032f3b529376ae (diff)
Rewrite constraint code for improved icon scaling/alignment
Diffstat (limited to 'src')
-rw-r--r--src/config/Config.zig9
-rw-r--r--src/font/Collection.zig13
-rw-r--r--src/font/Metrics.zig109
-rw-r--r--src/font/SharedGrid.zig8
-rw-r--r--src/font/face.zig375
-rw-r--r--src/font/face/coretext.zig21
-rw-r--r--src/font/face/freetype.zig65
-rw-r--r--src/font/nerd_font_attributes.zig944
-rw-r--r--src/font/nerd_font_codegen.py86
-rw-r--r--src/renderer/generic.zig3
10 files changed, 812 insertions, 821 deletions
diff --git a/src/config/Config.zig b/src/config/Config.zig
index fdea944ad..46eb03fe2 100644
--- a/src/config/Config.zig
+++ b/src/config/Config.zig
@@ -416,9 +416,12 @@ pub const compatibility = std.StaticStringMap(
/// necessarily force them to be. Decreasing this value will make nerd font
/// icons smaller.
///
-/// The default value for the icon height is 1.2 times the height of capital
-/// letters in your primary font, so something like -16.6% would make icons
-/// roughly the same height as capital letters.
+/// This value only applies to icons that are constrained to a single cell by
+/// neighboring characters. An icon that is free to spread across two cells
+/// can always use up to the full line height of the primary font.
+///
+/// The default value is 2/3 times the height of capital letters in your primary
+/// font plus 1/3 times the font's line height.
///
/// See the notes about adjustments in `adjust-cell-width`.
///
diff --git a/src/font/Collection.zig b/src/font/Collection.zig
index ad9590d70..997c72aa7 100644
--- a/src/font/Collection.zig
+++ b/src/font/Collection.zig
@@ -1213,6 +1213,9 @@ test "metrics" {
// and 1em should be the point size * dpi scale, so 12 * (96/72)
// which is 16, and 16 * 1.049 = 16.784, which finally is rounded
// to 17.
+ //
+ // The icon height is (2 * cap_height + face_height) / 3
+ // = (2 * 623 + 1049) / 3 = 765, and 16 * 0.765 = 12.24.
.cell_height = 17,
.cell_baseline = 3,
.underline_position = 17,
@@ -1223,7 +1226,10 @@ test "metrics" {
.overline_thickness = 1,
.box_thickness = 1,
.cursor_height = 17,
- .icon_height = 11,
+ .icon_height = 12.24,
+ .face_width = 8.0,
+ .face_height = 16.784,
+ .face_y = @round(3.04) - @as(f64, 3.04), // use f64, not comptime float, for exact match with runtime value
}, c.metrics);
// Resize should change metrics
@@ -1240,7 +1246,10 @@ test "metrics" {
.overline_thickness = 2,
.box_thickness = 2,
.cursor_height = 34,
- .icon_height = 23,
+ .icon_height = 24.48,
+ .face_width = 16.0,
+ .face_height = 33.568,
+ .face_y = @round(6.08) - @as(f64, 6.08), // use f64, not comptime float, for exact match with runtime value
}, c.metrics);
}
diff --git a/src/font/Metrics.zig b/src/font/Metrics.zig
index 9f6df9dc3..a0bc047c4 100644
--- a/src/font/Metrics.zig
+++ b/src/font/Metrics.zig
@@ -36,11 +36,17 @@ cursor_thickness: u32 = 1,
cursor_height: u32,
/// The constraint height for nerd fonts icons.
-icon_height: u32,
+icon_height: f64,
-/// Original cell width in pixels. This is used to keep
-/// glyphs centered if the cell width is adjusted wider.
-original_cell_width: ?u32 = null,
+/// The unrounded face width, used in scaling calculations.
+face_width: f64,
+
+/// The unrounded face height, used in scaling calculations.
+face_height: f64,
+
+/// The vertical bearing of face within the pixel-rounded
+/// and possibly height-adjusted cell
+face_y: f64,
/// Minimum acceptable values for some fields to prevent modifiers
/// from being able to, for example, cause 0-thickness underlines.
@@ -53,7 +59,9 @@ const Minimums = struct {
const box_thickness = 1;
const cursor_thickness = 1;
const cursor_height = 1;
- const icon_height = 1;
+ const icon_height = 1.0;
+ const face_height = 1.0;
+ const face_width = 1.0;
};
/// Metrics extracted from a font face, based on
@@ -214,8 +222,10 @@ pub fn calc(face: FaceMetrics) Metrics {
// We use the ceiling of the provided cell width and height to ensure
// that the cell is large enough for the provided size, since we cast
// it to an integer later.
- const cell_width = @ceil(face.cell_width);
- const cell_height = @ceil(face.lineHeight());
+ const face_width = face.cell_width;
+ const face_height = face.lineHeight();
+ const cell_width = @ceil(face_width);
+ const cell_height = @ceil(face_height);
// We split our line gap in two parts, and put half of it on the top
// of the cell and the other half on the bottom, so that our text never
@@ -224,7 +234,11 @@ pub fn calc(face: FaceMetrics) Metrics {
// Unlike all our other metrics, `cell_baseline` is relative to the
// BOTTOM of the cell.
- const cell_baseline = @round(half_line_gap - face.descent);
+ const face_baseline = half_line_gap - face.descent;
+ const cell_baseline = @round(face_baseline);
+
+ // We keep track of the vertical bearing of the face in the cell
+ const face_y = cell_baseline - face_baseline;
// We calculate a top_to_baseline to make following calculations simpler.
const top_to_baseline = cell_height - cell_baseline;
@@ -237,16 +251,8 @@ pub fn calc(face: FaceMetrics) Metrics {
const underline_position = @round(top_to_baseline - face.underlinePosition());
const strikethrough_position = @round(top_to_baseline - face.strikethroughPosition());
- // The calculation for icon height in the nerd fonts patcher
- // is two thirds cap height to one third line height, but we
- // use an opinionated default of 1.2 * cap height instead.
- //
- // Doing this prevents fonts with very large line heights
- // from having excessively oversized icons, and allows fonts
- // with very small line heights to still have roomy icons.
- //
- // We do cap it at `cell_height` though for obvious reasons.
- const icon_height = @min(cell_height, cap_height * 1.2);
+ // Same heuristic as the font_patcher script
+ const icon_height = (2 * cap_height + face_height) / 3;
var result: Metrics = .{
.cell_width = @intFromFloat(cell_width),
@@ -260,7 +266,10 @@ pub fn calc(face: FaceMetrics) Metrics {
.overline_thickness = @intFromFloat(underline_thickness),
.box_thickness = @intFromFloat(underline_thickness),
.cursor_height = @intFromFloat(cell_height),
- .icon_height = @intFromFloat(icon_height),
+ .icon_height = icon_height,
+ .face_width = face_width,
+ .face_height = face_height,
+ .face_y = face_y,
};
// Ensure all metrics are within their allowable range.
@@ -286,11 +295,6 @@ pub fn apply(self: *Metrics, mods: ModifierSet) void {
const new = @max(entry.value_ptr.apply(original), 1);
if (new == original) continue;
- // Preserve the original cell width if not set.
- if (self.original_cell_width == null) {
- self.original_cell_width = self.cell_width;
- }
-
// Set the new value
@field(self, @tagName(tag)) = new;
@@ -307,6 +311,7 @@ pub fn apply(self: *Metrics, mods: ModifierSet) void {
const diff = new - original;
const diff_bottom = diff / 2;
const diff_top = diff - diff_bottom;
+ self.face_y += @floatFromInt(diff_bottom);
self.cell_baseline +|= diff_bottom;
self.underline_position +|= diff_top;
self.strikethrough_position +|= diff_top;
@@ -315,6 +320,7 @@ pub fn apply(self: *Metrics, mods: ModifierSet) void {
const diff = original - new;
const diff_bottom = diff / 2;
const diff_top = diff - diff_bottom;
+ self.face_y -= @floatFromInt(diff_bottom);
self.cell_baseline -|= diff_bottom;
self.underline_position -|= diff_top;
self.strikethrough_position -|= diff_top;
@@ -417,25 +423,35 @@ pub const Modifier = union(enum) {
/// Apply a modifier to a numeric value.
pub fn apply(self: Modifier, v: anytype) @TypeOf(v) {
const T = @TypeOf(v);
- const signed = @typeInfo(T).int.signedness == .signed;
- return switch (self) {
- .percent => |p| percent: {
- const p_clamped: f64 = @max(0, p);
- const v_f64: f64 = @floatFromInt(v);
- const applied_f64: f64 = @round(v_f64 * p_clamped);
- const applied_T: T = @intFromFloat(applied_f64);
- break :percent applied_T;
+ const Tinfo = @typeInfo(T);
+ return switch (comptime Tinfo) {
+ .int, .comptime_int => switch (self) {
+ .percent => |p| percent: {
+ const p_clamped: f64 = @max(0, p);
+ const v_f64: f64 = @floatFromInt(v);
+ const applied_f64: f64 = @round(v_f64 * p_clamped);
+ const applied_T: T = @intFromFloat(applied_f64);
+ break :percent applied_T;
+ },
+
+ .absolute => |abs| absolute: {
+ const v_i64: i64 = @intCast(v);
+ const abs_i64: i64 = @intCast(abs);
+ const applied_i64: i64 = v_i64 +| abs_i64;
+ const clamped_i64: i64 = if (Tinfo.int.signedness == .signed)
+ applied_i64
+ else
+ @max(0, applied_i64);
+ const applied_T: T = std.math.cast(T, clamped_i64) orelse
+ std.math.maxInt(T) * @as(T, @intCast(std.math.sign(clamped_i64)));
+ break :absolute applied_T;
+ },
},
-
- .absolute => |abs| absolute: {
- const v_i64: i64 = @intCast(v);
- const abs_i64: i64 = @intCast(abs);
- const applied_i64: i64 = v_i64 +| abs_i64;
- const clamped_i64: i64 = if (signed) applied_i64 else @max(0, applied_i64);
- const applied_T: T = std.math.cast(T, clamped_i64) orelse
- std.math.maxInt(T) * @as(T, @intCast(std.math.sign(clamped_i64)));
- break :absolute applied_T;
+ .float, .comptime_float => return switch (self) {
+ .percent => |p| v * @max(0, p),
+ .absolute => |abs| v + @as(T, @floatFromInt(abs)),
},
+ else => {},
};
}
@@ -481,7 +497,7 @@ pub const Key = key: {
var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
var count: usize = 0;
for (field_infos, 0..) |field, i| {
- if (field.type != u32 and field.type != i32) continue;
+ if (field.type != u32 and field.type != i32 and field.type != f64) continue;
enumFields[i] = .{ .name = field.name, .value = i };
count += 1;
}
@@ -512,7 +528,10 @@ fn init() Metrics {
.overline_thickness = 0,
.box_thickness = 0,
.cursor_height = 0,
- .icon_height = 0,
+ .icon_height = 0.0,
+ .face_width = 0.0,
+ .face_height = 0.0,
+ .face_y = 0.0,
};
}
@@ -542,6 +561,7 @@ test "Metrics: adjust cell height smaller" {
try set.put(alloc, .cell_height, .{ .percent = 0.75 });
var m: Metrics = init();
+ m.face_y = 0.33;
m.cell_baseline = 50;
m.underline_position = 55;
m.strikethrough_position = 30;
@@ -549,6 +569,7 @@ test "Metrics: adjust cell height smaller" {
m.cell_height = 100;
m.cursor_height = 100;
m.apply(set);
+ try testing.expectEqual(-11.67, m.face_y);
try testing.expectEqual(@as(u32, 75), m.cell_height);
try testing.expectEqual(@as(u32, 38), m.cell_baseline);
try testing.expectEqual(@as(u32, 42), m.underline_position);
@@ -570,6 +591,7 @@ test "Metrics: adjust cell height larger" {
try set.put(alloc, .cell_height, .{ .percent = 1.75 });
var m: Metrics = init();
+ m.face_y = 0.33;
m.cell_baseline = 50;
m.underline_position = 55;
m.strikethrough_position = 30;
@@ -577,6 +599,7 @@ test "Metrics: adjust cell height larger" {
m.cell_height = 100;
m.cursor_height = 100;
m.apply(set);
+ try testing.expectEqual(37.33, m.face_y);
try testing.expectEqual(@as(u32, 175), m.cell_height);
try testing.expectEqual(@as(u32, 87), m.cell_baseline);
try testing.expectEqual(@as(u32, 93), m.underline_position);
diff --git a/src/font/SharedGrid.zig b/src/font/SharedGrid.zig
index e79fd117f..3fd9cf204 100644
--- a/src/font/SharedGrid.zig
+++ b/src/font/SharedGrid.zig
@@ -270,11 +270,9 @@ pub fn renderGlyph(
// Always use these constraints for emoji.
if (p == .emoji) {
render_opts.constraint = .{
- // Make the emoji as wide as possible, scaling proportionally,
- // but then scale it down as necessary if its new size exceeds
- // the cell height.
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ // Scale emoji to be as large as possible
+ // while preserving their aspect ratio.
+ .size = .cover,
// Center the emoji in its cells.
.align_horizontal = .center,
diff --git a/src/font/face.zig b/src/font/face.zig
index 9da3c30f6..0f882a77f 100644
--- a/src/font/face.zig
+++ b/src/font/face.zig
@@ -136,10 +136,8 @@ pub const RenderOptions = struct {
/// Don't constrain the glyph in any way.
pub const none: Constraint = .{};
- /// Vertical sizing rule.
- size_vertical: Size = .none,
- /// Horizontal sizing rule.
- size_horizontal: Size = .none,
+ /// Sizing rule.
+ size: Size = .none,
/// Vertical alignment rule.
align_vertical: Align = .none,
@@ -155,42 +153,40 @@ 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.
+ // Size and bearings of the glyph relative
+ // to the bounding box of its scale group.
+ relative_width: f64 = 1.0,
+ relative_height: f64 = 1.0,
+ relative_x: f64 = 0.0,
+ relative_y: f64 = 0.0,
+
+ /// Maximum aspect ratio (width/height) to allow when stretching.
max_xy_ratio: ?f64 = null,
/// Maximum number of cells horizontally to use.
max_constraint_width: u2 = 2,
- /// What to use as the height metric when constraining the glyph.
+ /// What to use as the height metric when constraining the glyph and
+ /// the constraint width is 1,
height: Height = .cell,
pub const Size = enum {
/// Don't change the size of this glyph.
none,
- /// Move the glyph and optionally scale it down
- /// proportionally to fit within the given axis.
+ /// Scale the glyph down if needed to fit within the bounds,
+ /// preserving aspect ratio.
fit,
- /// Move and resize the glyph proportionally to
- /// cover the given axis.
+ /// Scale the glyph up or down to exactly match the bounds,
+ /// preserving aspect ratio.
cover,
- /// Same as `cover` but not proportional.
+ /// Scale the glyph down if needed to fit within the bounds,
+ /// preserving aspect ratio. If the glyph doesn't cover a
+ /// single cell, scale up. If the glyph exceeds a single
+ /// cell but is within the bounds, do nothing.
+ /// (Nerd Font specific rule.)
+ fit_cover1,
+ /// Stretch the glyph to exactly fit the bounds in both
+ /// directions, disregarding aspect ratio.
stretch,
};
@@ -205,12 +201,18 @@ pub const RenderOptions = struct {
end,
/// Move the glyph so that it is centered on this axis.
center,
+ /// Move the glyph so that it is centered on this axis,
+ /// but always with respect to the first cell even for
+ /// multi-cell constraints. (Nerd Font specific rule.)
+ center1,
};
pub const Height = enum {
- /// Use the full height of the cell for constraining this glyph.
+ /// Always use the full height of the cell for constraining this glyph.
cell,
- /// Use the "icon height" from the grid metrics as the height.
+ /// When the constraint width is 1, use the "icon height" from the grid
+ /// metrics as the height. (When the constraint width is >1, the
+ /// constraint height is always the full cell height.)
icon,
};
@@ -226,9 +228,8 @@ pub const RenderOptions = struct {
/// because it neither sizes nor positions the glyph, then this
/// returns false.
pub inline fn doesAnything(self: Constraint) bool {
- return self.size_horizontal != .none or
+ return self.size != .none or
self.align_horizontal != .none or
- self.size_vertical != .none or
self.align_vertical != .none;
}
@@ -241,156 +242,202 @@ pub const RenderOptions = struct {
/// Number of cells horizontally available for this glyph.
constraint_width: u2,
) GlyphSize {
- var g = glyph;
-
- const available_width: f64 = @floatFromInt(
- metrics.cell_width * @min(
- self.max_constraint_width,
- constraint_width,
- ),
- );
- const available_height: f64 = @floatFromInt(switch (self.height) {
- .cell => metrics.cell_height,
- .icon => metrics.icon_height,
- });
-
- const w = available_width -
- self.pad_left * available_width -
- self.pad_right * available_width;
- const h = available_height -
- self.pad_top * available_height -
- self.pad_bottom * available_height;
-
- // Subtract padding from the bearings so that our
- // alignment and sizing code works correctly. We
- // re-add before returning.
- 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) {
- const orig_height = g.height;
- // Adjust our height and width to proportionally
- // scale them to fit the glyph to the cell width.
- g.height *= w / g.width;
- g.width = w;
- // Set our x to 0 since anything else would mean
- // the glyph extends outside of the cell width.
- g.x = 0;
- // Compensate our y to keep things vertically
- // centered as they're scaled down.
- g.y += (orig_height - g.height) / 2;
- } else if (g.width + g.x > w) {
- // If the width of the glyph can fit in the cell but
- // is currently outside due to the left bearing, then
- // we reduce the left bearing just enough to fit it
- // back in the cell.
- g.x = w - g.width;
- } else if (g.x < 0) {
- g.x = 0;
- },
- .cover => {
- const orig_height = g.height;
-
- g.height *= w / g.width;
- g.width = w;
-
- g.x = 0;
+ if (!self.doesAnything()) return glyph;
+
+ // For extra wide font faces, never stretch glyphs across two cells.
+ // This mirrors font_patcher.
+ const min_constraint_width: u2 = if ((self.size == .stretch) and (metrics.face_width > 0.9 * metrics.face_height))
+ 1
+ else
+ @min(self.max_constraint_width, constraint_width);
+
+ // The bounding box for the glyph's scale group.
+ // Scaling and alignment rules are calculated for
+ // this box and then applied to the glyph.
+ var group: GlyphSize = group: {
+ const group_width = glyph.width / self.relative_width;
+ const group_height = glyph.height / self.relative_height;
+ break :group .{
+ .width = group_width,
+ .height = group_height,
+ .x = glyph.x - (group_width * self.relative_x),
+ .y = glyph.y - (group_height * self.relative_y),
+ };
+ };
- g.y += (orig_height - g.height) / 2;
- },
- .stretch => {
- g.width = w;
- g.x = 0;
- },
+ // The new, constrained glyph size
+ var constrained_glyph = glyph;
+
+ // Apply prescribed scaling
+ const width_factor, const height_factor = self.scale_factors(group, metrics, min_constraint_width);
+ constrained_glyph.width *= width_factor;
+ constrained_glyph.x *= width_factor;
+ constrained_glyph.height *= height_factor;
+ constrained_glyph.y *= height_factor;
+
+ // NOTE: font_patcher jumps through a lot of hoops at this
+ // point to ensure that the glyph remains within the target
+ // bounding box after rounding to font definition units.
+ // This is irrelevant here as we're not rounding, we're
+ // staying in f64 and heading straight to rendering.
+
+ // Align vertically
+ if (self.align_vertical != .none) {
+ // Vertically scale group bounding box.
+ group.height *= height_factor;
+ group.y *= height_factor;
+
+ // Calculate offset and shift the glyph
+ constrained_glyph.y += self.offset_vertical(group, metrics);
}
- switch (self.size_vertical) {
- .none => {},
- .fit => if (g.height > h) {
- const orig_width = g.width;
- // Adjust our height and width to proportionally
- // scale them to fit the glyph to the cell height.
- g.width *= h / g.height;
- g.height = h;
- // Set our y to 0 since anything else would mean
- // the glyph extends outside of the cell height.
- g.y = 0;
- // Compensate our x to keep things horizontally
- // centered as they're scaled down.
- g.x += (orig_width - g.width) / 2;
- } else if (g.height + g.y > h) {
- // If the height of the glyph can fit in the cell but
- // is currently outside due to the bottom bearing, then
- // we reduce the bottom bearing just enough to fit it
- // back in the cell.
- g.y = h - g.height;
- } else if (g.y < 0) {
- g.y = 0;
- },
- .cover => {
- const orig_width = g.width;
+ // Align horizontally
+ if (self.align_horizontal != .none) {
+ // Horizontally scale group bounding box.
+ group.width *= width_factor;
+ group.x *= width_factor;
- g.width *= h / g.height;
- g.height = h;
+ // Calculate offset and shift the glyph
+ constrained_glyph.x += self.offset_horizontal(group, metrics, min_constraint_width);
+ }
- g.y = 0;
+ return constrained_glyph;
+ }
- g.x += (orig_width - g.width) / 2;
- },
- .stretch => {
- g.height = h;
- g.y = 0;
- },
+ /// Return width and height scaling factors for this scaling group.
+ fn scale_factors(
+ self: Constraint,
+ group: GlyphSize,
+ metrics: Metrics,
+ min_constraint_width: u2,
+ ) struct { f64, f64 } {
+ if (self.size == .none) {
+ return .{ 1.0, 1.0 };
}
- // Add group-relative position
- g.x += self.group_x * g.width;
- g.y += self.group_y * g.height;
+ const multi_cell = (min_constraint_width > 1);
- // Divide group width and height back out before we align.
- g.width /= self.group_width;
- g.height /= self.group_height;
+ const pad_width_factor = @as(f64, @floatFromInt(min_constraint_width)) - (self.pad_left + self.pad_right);
+ const pad_height_factor = 1 - (self.pad_bottom + self.pad_top);
- if (self.max_xy_ratio) |ratio| if (g.width > g.height * ratio) {
- const orig_width = g.width;
- g.width = g.height * ratio;
- g.x += (orig_width - g.width) / 2;
+ const target_width = pad_width_factor * metrics.face_width;
+ const target_height = pad_height_factor * switch (self.height) {
+ .cell => metrics.face_height,
+ // icon_height only applies with single-cell constraints.
+ // This mirrors font_patcher.
+ .icon => if (multi_cell)
+ metrics.face_height
+ else
+ metrics.icon_height,
};
- switch (self.align_horizontal) {
- .none => {},
- .start => g.x = 0,
- .end => g.x = w - g.width,
- .center => g.x = (w - g.width) / 2,
+ var width_factor = target_width / group.width;
+ var height_factor = target_height / group.height;
+
+ switch (self.size) {
+ .none => unreachable,
+ .fit => {
+ // Scale down to fit if needed
+ height_factor = @min(1, width_factor, height_factor);
+ width_factor = height_factor;
+ },
+ .cover => {
+ // Scale to cover
+ height_factor = @min(width_factor, height_factor);
+ width_factor = height_factor;
+ },
+ .fit_cover1 => {
+ // Scale down to fit or up to cover at least one cell
+ // NOTE: This is similar to font_patcher's "pa" mode,
+ // however, font_patcher will only do the upscaling
+ // part if the constraint width is 1, resulting in
+ // some icons becoming smaller when the constraint
+ // width increases. You'd see icons shrinking when
+ // opening up a space after them. This makes no
+ // sense, so we've fixed the rule such that these
+ // icons are scaled to the same size for multi-cell
+ // constraints as they would be for single-cell.
+ height_factor = @min(width_factor, height_factor);
+ if (multi_cell and (height_factor > 1)) {
+ // Call back into this function with
+ // constraint width 1 to get single-cell scale
+ // factors. We use the height factor as width
+ // could have been modified by max_xy_ratio.
+ _, const single_height_factor = self.scale_factors(group, metrics, 1);
+ height_factor = @max(1, single_height_factor);
+ }
+ width_factor = height_factor;
+ },
+ .stretch => {},
}
- switch (self.align_vertical) {
- .none => {},
- .start => g.y = 0,
- .end => g.y = h - g.height,
- .center => g.y = (h - g.height) / 2,
+ // Reduce aspect ratio if required
+ if (self.max_xy_ratio) |ratio| {
+ if (group.width * width_factor > group.height * height_factor * ratio) {
+ width_factor = group.height * height_factor * ratio / group.width;
+ }
}
- // Re-add our padding before returning.
- g.x += self.pad_left * available_width;
- g.y += self.pad_bottom * available_height;
+ return .{ width_factor, height_factor };
+ }
- // If the available height is less than the cell height, we
- // add half of the difference to center it in the full height.
- //
- // If necessary, in the future, we can adjust this to account
- // for alignment, but that isn't necessary with any of the nf
- // icons afaict.
- const cell_height: f64 = @floatFromInt(metrics.cell_height);
- g.y += (cell_height - available_height) / 2;
+ /// Return vertical offset needed to align this group
+ fn offset_vertical(
+ self: Constraint,
+ group: GlyphSize,
+ metrics: Metrics,
+ ) f64 {
+ // We use face_height and offset by face_y, rather than
+ // using cell_height directly, to account for the asymmetry
+ // of the pixel cell around the face (a consequence of
+ // aligning the baseline with a pixel boundary rather than
+ // vertically centering the face).
+ const new_group_y = metrics.face_y + switch (self.align_vertical) {
+ .none => return 0.0,
+ .start => self.pad_bottom * metrics.face_height,
+ .end => end: {
+ const pad_top_dy = self.pad_top * metrics.face_height;
+ break :end metrics.face_height - pad_top_dy - group.height;
+ },
+ .center, .center1 => (metrics.face_height - group.height) / 2,
+ };
+ return new_group_y - group.y;
+ }
- return g;
+ /// Return horizontal offset needed to align this group
+ fn offset_horizontal(
+ self: Constraint,
+ group: GlyphSize,
+ metrics: Metrics,
+ min_constraint_width: u2,
+ ) f64 {
+ // For multi-cell constraints, we align relative to the span
+ // from the left edge of the first face cell to the right
+ // edge of the last face cell as they sit within the rounded
+ // and adjusted pixel cell (centered if narrower than the
+ // pixel cell, left-aligned if wider).
+ const face_x, const full_face_span = facecalcs: {
+ const cell_width: f64 = @floatFromInt(metrics.cell_width);
+ const full_width: f64 = @floatFromInt(min_constraint_width * metrics.cell_width);
+ const cell_margin = cell_width - metrics.face_width;
+ break :facecalcs .{ @max(0, cell_margin / 2), full_width - cell_margin };
+ };
+ const pad_left_x = self.pad_left * metrics.face_width;
+ const new_group_x = face_x + switch (self.align_horizontal) {
+ .none => return 0.0,
+ .start => pad_left_x,
+ .end => end: {
+ const pad_right_dx = self.pad_right * metrics.face_width;
+ break :end @max(pad_left_x, full_face_span - pad_right_dx - group.width);
+ },
+ .center => @max(pad_left_x, (full_face_span - group.width) / 2),
+ // NOTE: .center1 implements the font_patcher rule of centering
+ // in the first cell even for multi-cell constraints. Since glyphs
+ // are not allowed to protrude to the left, this results in the
+ // left-alignment like .start when the glyph is wider than a cell.
+ .center1 => @max(pad_left_x, (metrics.face_width - group.width) / 2),
+ };
+ return new_group_x - group.x;
}
};
};
diff --git a/src/font/face/coretext.zig b/src/font/face/coretext.zig
index cb9993cbf..8c9611c04 100644
--- a/src/font/face/coretext.zig
+++ b/src/font/face/coretext.zig
@@ -388,19 +388,16 @@ pub const Face = struct {
y = @round(y);
}
- // If the cell width was adjusted wider, we re-center all glyphs
- // in the new width, so that they aren't weirdly off to the left.
- if (metrics.original_cell_width) |original| recenter: {
- // 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) break :recenter;
-
- // If the original width was wider then we don't do anything.
- if (original >= metrics.cell_width) break :recenter;
-
+ // We center all glyphs within the pixel-rounded and adjusted
+ // cell width if it's larger than the face width, so that they
+ // aren't weirdly off to the left.
+ //
+ // 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) and (metrics.face_width < cell_width)) {
// We add half the difference to re-center.
- x += (cell_width - @as(f64, @floatFromInt(original))) / 2;
+ x += (cell_width - metrics.face_width) / 2;
}
// Our whole-pixel bearings for the final glyph.
diff --git a/src/font/face/freetype.zig b/src/font/face/freetype.zig
index 82cf107c8..3094d8076 100644
--- a/src/font/face/freetype.zig
+++ b/src/font/face/freetype.zig
@@ -498,17 +498,14 @@ pub const Face = struct {
y = @round(y);
}
- // If the cell width was adjusted wider, we re-center all glyphs
- // in the new width, so that they aren't weirdly off to the left.
- if (metrics.original_cell_width) |original| recenter: {
- // 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) break :recenter;
-
- // If the original width was wider then we don't do anything.
- if (original >= metrics.cell_width) break :recenter;
-
+ // We center all glyphs within the pixel-rounded and adjusted
+ // cell width if it's larger than the face width, so that they
+ // aren't weirdly off to the left.
+ //
+ // 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) and (metrics.face_width < cell_width)) {
// We add half the difference to re-center.
//
// NOTE: We round this to a whole-pixel amount because under
@@ -516,7 +513,7 @@ pub const Face = struct {
// the case under CoreText. If we move the outlines by
// a non-whole-pixel amount, it completely ruins the
// hinting.
- x += @round((cell_width - @as(f64, @floatFromInt(original))) / 2);
+ x += @round((cell_width - metrics.face_width) / 2);
}
// Now we can render the glyph.
@@ -1211,25 +1208,31 @@ test "color emoji" {
alloc,
&atlas,
ft_font.glyphIndex('🥸').?,
- .{ .grid_metrics = .{
- .cell_width = 13,
- .cell_height = 24,
- .cell_baseline = 0,
- .underline_position = 0,
- .underline_thickness = 0,
- .strikethrough_position = 0,
- .strikethrough_thickness = 0,
- .overline_position = 0,
- .overline_thickness = 0,
- .box_thickness = 0,
- .cursor_height = 0,
- .icon_height = 0,
- }, .constraint_width = 2, .constraint = .{
- .size_horizontal = .cover,
- .size_vertical = .cover,
- .align_horizontal = .center,
- .align_vertical = .center,
- } },
+ .{
+ .grid_metrics = .{
+ .cell_width = 13,
+ .cell_height = 24,
+ .cell_baseline = 0,
+ .underline_position = 0,
+ .underline_thickness = 0,
+ .strikethrough_position = 0,
+ .strikethrough_thickness = 0,
+ .overline_position = 0,
+ .overline_thickness = 0,
+ .box_thickness = 0,
+ .cursor_height = 0,
+ .icon_height = 0,
+ .face_width = 13,
+ .face_height = 24,
+ .face_y = 0,
+ },
+ .constraint_width = 2,
+ .constraint = .{
+ .size = .fit,
+ .align_horizontal = .center,
+ .align_vertical = .center,
+ },
+ },
);
try testing.expectEqual(@as(u32, 24), glyph.height);
}
diff --git a/src/font/nerd_font_attributes.zig b/src/font/nerd_font_attributes.zig
index 11902d310..04088b1aa 100644
--- a/src/font/nerd_font_attributes.zig
+++ b/src/font/nerd_font_attributes.zig
@@ -6,16 +6,15 @@
const Constraint = @import("face.zig").RenderOptions.Constraint;
-/// Get the a constraints for the provided codepoint.
+/// Get the constraints for the provided codepoint.
pub fn getConstraint(cp: u21) ?Constraint {
return switch (cp) {
0x2500...0x259f,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
- .align_horizontal = .center,
- .align_vertical = .center,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
.pad_left = -0.02,
.pad_right = -0.02,
.pad_top = -0.01,
@@ -23,12 +22,11 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0x2630,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ .size = .cover,
.height = .icon,
.max_constraint_width = 1,
- .align_horizontal = .center,
- .align_vertical = .center,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
.pad_left = 0.1,
.pad_right = 0.1,
.pad_top = 0.1,
@@ -36,49 +34,45 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0x276c...0x276d,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ .size = .cover,
.max_constraint_width = 1,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3999999999999999,
- .group_height = 1.1222570532915361,
- .group_x = 0.1428571428571428,
- .group_y = 0.0349162011173184,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7142857142857143,
+ .relative_height = 0.8910614525139665,
+ .relative_x = 0.1428571428571428,
+ .relative_y = 0.0349162011173184,
.pad_top = 0.15,
.pad_bottom = 0.15,
},
0x276e...0x276f,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ .size = .cover,
.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,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9885714285714285,
+ .relative_height = 0.8910614525139665,
+ .relative_x = 0.0057142857142857,
+ .relative_y = 0.0125698324022346,
.pad_top = 0.15,
.pad_bottom = 0.15,
},
0x2770...0x2771,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ .size = .cover,
.max_constraint_width = 1,
- .align_horizontal = .center,
- .align_vertical = .center,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
.pad_top = 0.15,
.pad_bottom = 0.15,
},
0xe0b0,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.06,
.pad_right = -0.06,
.pad_top = -0.01,
@@ -87,20 +81,18 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0b1,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.max_xy_ratio = 0.7,
},
0xe0b2,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.06,
.pad_right = -0.06,
.pad_top = -0.01,
@@ -109,20 +101,18 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0b3,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.max_xy_ratio = 0.7,
},
0xe0b4,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.06,
.pad_right = -0.06,
.pad_top = -0.01,
@@ -131,20 +121,18 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0b5,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.max_xy_ratio = 0.5,
},
0xe0b6,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.06,
.pad_right = -0.06,
.pad_top = -0.01,
@@ -153,21 +141,19 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0b7,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.max_xy_ratio = 0.5,
},
0xe0b8,
0xe0bc,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -176,20 +162,18 @@ pub fn getConstraint(cp: u21) ?Constraint {
0xe0b9,
0xe0bd,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
},
0xe0ba,
0xe0be,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -198,19 +182,17 @@ pub fn getConstraint(cp: u21) ?Constraint {
0xe0bb,
0xe0bf,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
},
0xe0c0,
0xe0c8,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -218,18 +200,16 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0c1,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
},
0xe0c2,
0xe0ca,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -237,17 +217,15 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0c3,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
},
0xe0c4,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.03,
@@ -256,10 +234,9 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0c5,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.03,
@@ -268,10 +245,9 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0c6,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.03,
@@ -280,10 +256,9 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0c7,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = 0.03,
.pad_right = 0.03,
.pad_top = 0.03,
@@ -292,10 +267,9 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0cc,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.02,
.pad_right = -0.02,
.pad_top = -0.01,
@@ -304,36 +278,32 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0cd,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.max_xy_ratio = 0.865,
},
0xe0ce,
0xe0d0...0xe0d1,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
},
0xe0cf,
0xe0d3,
0xe0d5,
=> .{
- .size_horizontal = .cover,
- .size_vertical = .fit,
- .align_horizontal = .center,
- .align_vertical = .center,
+ .size = .fit_cover1,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
},
0xe0d2,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.02,
.pad_right = -0.02,
.pad_top = -0.01,
@@ -342,11 +312,10 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0d4,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.02,
.pad_right = -0.02,
.pad_top = -0.01,
@@ -355,11 +324,10 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0d6,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .start,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -368,11 +336,10 @@ pub fn getConstraint(cp: u21) ?Constraint {
},
0xe0d7,
=> .{
- .size_horizontal = .stretch,
- .size_vertical = .stretch,
+ .size = .stretch,
.max_constraint_width = 1,
.align_horizontal = .end,
- .align_vertical = .center,
+ .align_vertical = .center1,
.pad_left = -0.05,
.pad_right = -0.05,
.pad_top = -0.01,
@@ -425,640 +392,583 @@ pub fn getConstraint(cp: u21) ?Constraint {
0xf307...0xf847,
0xf0001...0xf1af0,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
},
0xea61,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3310225303292895,
- .group_height = 1.0762439807383628,
- .group_x = 0.0846354166666667,
- .group_y = 0.0708426547352722,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7513020833333334,
+ .relative_height = 0.9291573452647278,
+ .relative_x = 0.0846354166666667,
+ .relative_y = 0.0708426547352722,
},
0xea7d,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1912058627581612,
- .group_height = 1.1426759670259987,
- .group_x = 0.0917225950782998,
- .group_y = 0.0416204217536071,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8394854586129754,
+ .relative_height = 0.8751387347391787,
+ .relative_x = 0.0917225950782998,
+ .relative_y = 0.0416204217536071,
},
0xea99,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0642857142857143,
- .group_height = 2.0929152148664345,
- .group_x = 0.0302013422818792,
- .group_y = 0.2269700332963374,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9395973154362416,
+ .relative_height = 0.4778024417314096,
+ .relative_x = 0.0302013422818792,
+ .relative_y = 0.2269700332963374,
},
0xea9a,
0xeaa1,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3032069970845481,
- .group_height = 1.1731770833333333,
- .group_x = 0.1526845637583893,
- .group_y = 0.0754716981132075,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7673378076062640,
+ .relative_height = 0.8523862375138734,
+ .relative_x = 0.1526845637583893,
+ .relative_y = 0.0754716981132075,
},
0xea9b,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1640625000000000,
- .group_height = 1.3134110787172011,
- .group_x = 0.0721476510067114,
- .group_y = 0.0871254162042175,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8590604026845637,
+ .relative_height = 0.7613762486126526,
+ .relative_x = 0.0721476510067114,
+ .relative_y = 0.0871254162042175,
},
0xea9c,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1640625000000000,
- .group_height = 1.3201465201465201,
- .group_x = 0.0721476510067114,
- .group_y = 0.0832408435072142,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8590604026845637,
+ .relative_height = 0.7574916759156493,
+ .relative_x = 0.0721476510067114,
+ .relative_y = 0.0832408435072142,
},
0xea9d,
0xeaa0,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.4493150684931506,
- .group_height = 1.9693989071038251,
- .group_x = 0.2863534675615212,
- .group_y = 0.2763596004439512,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.4082774049217002,
+ .relative_height = 0.5077691453940066,
+ .relative_x = 0.2863534675615212,
+ .relative_y = 0.2763596004439512,
},
0xea9e...0xea9f,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.9540983606557376,
- .group_height = 2.4684931506849317,
- .group_x = 0.2136465324384788,
- .group_y = 0.3068812430632630,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5117449664429530,
+ .relative_height = 0.4051054384017758,
+ .relative_x = 0.2136465324384788,
+ .relative_y = 0.3068812430632630,
},
0xeaa2,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.2405228758169935,
- .group_height = 1.0595187680461982,
- .group_x = 0.0679662802950474,
- .group_y = 0.0147523709167545,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8061116965226555,
+ .relative_height = 0.9438247156716689,
+ .relative_x = 0.0679662802950474,
+ .relative_y = 0.0147523709167545,
},
0xeab4,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0054815974941269,
- .group_height = 1.8994082840236686,
- .group_y = 0.2024922118380062,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9945482866043613,
+ .relative_height = 0.5264797507788161,
+ .relative_y = 0.2024922118380062,
},
0xeab5,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.8994082840236686,
- .group_height = 1.0054815974941269,
- .group_x = 0.2024922118380062,
- .group_y = 0.0054517133956386,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5264797507788161,
+ .relative_height = 0.9945482866043613,
+ .relative_x = 0.2024922118380062,
+ .relative_y = 0.0054517133956386,
},
0xeab6,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.8994082840236686,
- .group_height = 1.0054815974941269,
- .group_x = 0.2710280373831775,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5264797507788161,
+ .relative_height = 0.9945482866043613,
+ .relative_x = 0.2710280373831775,
},
0xeab7,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0054815974941269,
- .group_height = 1.8994082840236686,
- .group_x = 0.0054517133956386,
- .group_y = 0.2710280373831775,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9945482866043613,
+ .relative_height = 0.5264797507788161,
+ .relative_x = 0.0054517133956386,
+ .relative_y = 0.2710280373831775,
},
0xead4...0xead5,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.4144620811287478,
- .group_x = 0.1483790523690773,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7069825436408977,
+ .relative_x = 0.1483790523690773,
},
0xead6,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_height = 1.1388535031847133,
- .group_y = 0.0687919463087248,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_height = 0.8780760626398211,
+ .relative_y = 0.0687919463087248,
},
0xeb43,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3631840796019901,
- .group_height = 1.0003813300793167,
- .group_x = 0.1991657977059437,
- .group_y = 0.0003811847221163,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7335766423357665,
+ .relative_height = 0.9996188152778837,
+ .relative_x = 0.1991657977059437,
+ .relative_y = 0.0003811847221163,
},
0xeb6e,
0xeb71,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_height = 2.0183246073298431,
- .group_y = 0.2522697795071336,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_height = 0.4954604409857328,
+ .relative_y = 0.2522697795071336,
},
0xeb6f,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.0104712041884816,
- .group_x = 0.2493489583333333,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.4973958333333333,
+ .relative_x = 0.2493489583333333,
},
0xeb70,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.0104712041884816,
- .group_height = 1.0039062500000000,
- .group_x = 0.2493489583333333,
- .group_y = 0.0038910505836576,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.4973958333333333,
+ .relative_height = 0.9961089494163424,
+ .relative_x = 0.2493489583333333,
+ .relative_y = 0.0038910505836576,
},
0xeb8a,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.8828125000000000,
- .group_height = 2.9818561935339356,
- .group_x = 0.2642276422764228,
- .group_y = 0.3313050881410256,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.3468834688346883,
+ .relative_height = 0.3353615785256410,
+ .relative_x = 0.2642276422764228,
+ .relative_y = 0.3313050881410256,
},
0xeb9a,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1440626883664857,
- .group_height = 1.0595187680461982,
- .group_x = 0.0679662802950474,
- .group_y = 0.0147523709167545,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8740779768177028,
+ .relative_height = 0.9438247156716689,
+ .relative_x = 0.0679662802950474,
+ .relative_y = 0.0147523709167545,
},
0xebd5,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0727069351230425,
- .group_height = 1.0730882652023592,
- .group_y = 0.0681102082395584,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9322210636079249,
+ .relative_height = 0.9318897917604415,
+ .relative_y = 0.0681102082395584,
},
0xebd6,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_height = 1.0003554839321263,
- .group_y = 0.0003553576082064,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_height = 0.9996446423917936,
+ .relative_y = 0.0003553576082064,
},
0xec07,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.8604846818377689,
- .group_height = 2.9804665603035656,
- .group_x = 0.2615335565120357,
- .group_y = 0.3311487268518519,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.3495911047345768,
+ .relative_height = 0.3355179398148149,
+ .relative_x = 0.2615335565120357,
+ .relative_y = 0.3311487268518519,
},
0xec0b,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0721073225265512,
- .group_height = 1.0003813300793167,
- .group_y = 0.0003811847221163,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9327424400417101,
+ .relative_height = 0.9996188152778837,
+ .relative_y = 0.0003811847221163,
},
0xec0c,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.2486979166666667,
- .group_x = 0.1991657977059437,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8008342022940563,
+ .relative_x = 0.1991657977059437,
},
0xf019,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
},
0xf030,
0xf03e,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
- .group_height = 1.1426844014510278,
- .group_y = 0.0624338624338624,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
+ .relative_height = 0.8751322751322751,
+ .relative_y = 0.0624338624338624,
},
0xf03d,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_height = 1.3328631875881523,
- .group_y = 0.1248677248677249,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_height = 0.7502645502645503,
+ .relative_y = 0.1248677248677249,
},
0xf03f,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.8003104407193382,
- .group_x = 0.0005406676069582,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5554597570408116,
+ .relative_x = 0.0005406676069582,
},
0xf040,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1263939384681190,
- .group_height = 1.0007255897868335,
- .group_x = 0.0003164442515641,
- .group_y = 0.0001959631589261,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8877888683953564,
+ .relative_height = 0.9992749363119733,
+ .relative_x = 0.0003164442515641,
+ .relative_y = 0.0001959631589261,
},
0xf044,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0087313432835820,
- .group_height = 1.0077472527472529,
- .group_y = 0.0002010014265405,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9913442331878375,
+ .relative_height = 0.9923123057630445,
+ .relative_y = 0.0002010014265405,
},
0xf04a,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
- .group_height = 1.3321224771947897,
- .group_y = 0.1247354497354497,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
+ .relative_height = 0.7506817256817256,
+ .relative_y = 0.1247354497354497,
},
0xf051,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.7994923857868019,
- .group_height = 1.3321224771947897,
- .group_y = 0.1247354497354497,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5557122708039492,
+ .relative_height = 0.7506817256817256,
+ .relative_y = 0.1247354497354497,
},
0xf052,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1439802384724422,
- .group_height = 1.1430071621244535,
- .group_y = 0.0626172338785870,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8741409740917385,
+ .relative_height = 0.8748851565736010,
+ .relative_y = 0.0626172338785870,
},
0xf053,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 2.0025185185185186,
- .group_height = 1.1416267186919362,
- .group_y = 0.0620882827561120,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.4993711622401420,
+ .relative_height = 0.8759430588185509,
+ .relative_y = 0.0620882827561120,
},
0xf05a...0xf05b,
0xf0aa,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0012592592592593,
- .group_height = 1.0002824582824583,
- .group_y = 0.0002010014265405,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9987423244802840,
+ .relative_height = 0.9997176214776941,
+ .relative_y = 0.0002010014265405,
},
0xf071,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
- .group_height = 1.1426844014510278,
- .group_x = 0.0004701457451810,
- .group_y = 0.0624338624338624,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
+ .relative_height = 0.8751322751322751,
+ .relative_x = 0.0004701457451810,
+ .relative_y = 0.0624338624338624,
},
0xf078,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1434320241691844,
- .group_height = 2.0026841590612778,
- .group_y = 0.1879786499051550,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8745600777856455,
+ .relative_height = 0.4993298596163721,
+ .relative_y = 0.1879786499051550,
},
0xf07b,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
- .group_height = 1.2285368802902055,
- .group_y = 0.0930118110236220,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
+ .relative_height = 0.8139763779527559,
+ .relative_y = 0.0930118110236220,
},
0xf081,
0xf092,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1441233373639663,
- .group_height = 1.1430071621244535,
- .group_y = 0.0626172338785870,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8740316426933279,
+ .relative_height = 0.8748851565736010,
+ .relative_y = 0.0626172338785870,
},
0xf08c,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.2859733978234582,
- .group_height = 1.1426844014510278,
- .group_y = 0.0624338624338624,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7776210625293841,
+ .relative_height = 0.8751322751322751,
+ .relative_y = 0.0624338624338624,
},
0xf09f,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.7489690176588770,
- .group_x = 0.0006952841596131,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.5717654171704958,
+ .relative_x = 0.0006952841596131,
},
0xf0a1,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1253968253968254,
- .group_height = 1.0749103295228757,
- .group_y = 0.0349409448818898,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8885754583921015,
+ .relative_height = 0.9303101594008066,
+ .relative_y = 0.0349409448818898,
},
0xf0a2,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.1429529187840552,
- .group_height = 1.0002824582824583,
- .group_x = 0.0001253913778381,
- .group_y = 0.0002010014265405,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.8749266777006549,
+ .relative_height = 0.9997176214776941,
+ .relative_x = 0.0001253913778381,
+ .relative_y = 0.0002010014265405,
},
0xf0a3,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0005921977940631,
- .group_height = 1.0001448722153810,
- .group_x = 0.0005918473033957,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9994081526966043,
+ .relative_height = 0.9998551487695376,
+ .relative_x = 0.0005918473033957,
},
0xf0a4,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0012592592592593,
- .group_height = 1.3332396658348704,
- .group_y = 0.1250334663306335,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9987423244802840,
+ .relative_height = 0.7500526916695081,
+ .relative_y = 0.1250334663306335,
},
0xf0ca,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0335226652102676,
- .group_height = 1.2308163060897437,
- .group_y = 0.0938253501046103,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9675646540335450,
+ .relative_height = 0.8124689241215546,
+ .relative_y = 0.0938253501046103,
},
0xf0d6,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_height = 1.4330042313117066,
- .group_y = 0.1510826771653543,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_height = 0.6978346456692913,
+ .relative_y = 0.1510826771653543,
},
0xf0de,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3984670905653893,
- .group_height = 2.6619718309859155,
- .group_x = 0.0004030632809351,
- .group_y = 0.5708994708994709,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7150686682199350,
+ .relative_height = 0.3756613756613756,
+ .relative_x = 0.0004030632809351,
+ .relative_y = 0.5708994708994709,
},
0xf0e7,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3348918927786344,
- .group_height = 1.0001196386424678,
- .group_x = 0.0006021702214782,
- .group_y = 0.0001196243307751,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7491243338952770,
+ .relative_height = 0.9998803756692248,
+ .relative_x = 0.0006021702214782,
+ .relative_y = 0.0001196243307751,
},
0xf296,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0005202277820979,
- .group_height = 1.0386597451628128,
- .group_x = 0.0001795653226322,
- .group_y = 0.0187142907131644,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9994800427141276,
+ .relative_height = 0.9627792014248586,
+ .relative_x = 0.0001795653226322,
+ .relative_y = 0.0187142907131644,
},
0xf2c4,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3292088488938882,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7523272214386461,
},
0xf2c5,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0118264574212998,
- .group_height = 1.1664315937940761,
- .group_x = 0.0004377219006858,
- .group_y = 0.0713422007255139,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9883117728988424,
+ .relative_height = 0.8573155985489722,
+ .relative_x = 0.0004377219006858,
+ .relative_y = 0.0713422007255139,
},
0xf2f0,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.0012592592592593,
- .group_height = 1.0342088873926949,
- .group_y = 0.0165984862232646,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.9987423244802840,
+ .relative_height = 0.9669226518842459,
+ .relative_y = 0.0165984862232646,
},
0xf306,
=> .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit_cover1,
.height = .icon,
- .align_horizontal = .center,
- .align_vertical = .center,
- .group_width = 1.3001222493887530,
+ .align_horizontal = .center1,
+ .align_vertical = .center1,
+ .relative_width = 0.7691584391161260,
},
else => null,
};
diff --git a/src/font/nerd_font_codegen.py b/src/font/nerd_font_codegen.py
index a103a30ac..4965dabe4 100644
--- a/src/font/nerd_font_codegen.py
+++ b/src/font/nerd_font_codegen.py
@@ -50,10 +50,10 @@ class PatchSetAttributeEntry(TypedDict):
stretch: str
params: dict[str, float | bool]
- group_x: float
- group_y: float
- group_width: float
- group_height: float
+ relative_x: float
+ relative_y: float
+ relative_width: float
+ relative_height: float
class PatchSet(TypedDict):
@@ -143,7 +143,7 @@ def parse_alignment(val: str) -> str | None:
return {
"l": ".start",
"r": ".end",
- "c": ".center",
+ "c": ".center1", # font-patcher specific centering rule, see face.zig
"": None,
}.get(val, ".none")
@@ -158,10 +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)),
+ float(attr.get("relative_x", 0.0)),
+ float(attr.get("relative_y", 0.0)),
+ float(attr.get("relative_width", 1.0)),
+ float(attr.get("relative_height", 1.0)),
)
@@ -187,10 +187,10 @@ 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)
+ relative_x = attr.get("relative_x", 0.0)
+ relative_y = attr.get("relative_y", 0.0)
+ relative_width = attr.get("relative_width", 1.0)
+ relative_height = attr.get("relative_height", 1.0)
overlap = params.get("overlap", 0.0)
xy_ratio = params.get("xy-ratio", -1.0)
@@ -204,28 +204,30 @@ def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry)
s = f"{keys}\n => .{{\n"
- # These translations don't quite capture the way
- # the actual patcher does scaling, but they're a
- # good enough compromise.
- if "xy" in stretch:
- s += " .size_horizontal = .stretch,\n"
- s += " .size_vertical = .stretch,\n"
- elif "!" in stretch or "^" in stretch:
- s += " .size_horizontal = .cover,\n"
- s += " .size_vertical = .fit,\n"
+ # This maps the font_patcher stretch rules to a Constrain instance
+ # NOTE: some comments in font_patcher indicate that only x or y
+ # would also be a valid spec, but no icons use it, so we won't
+ # support it until we have to.
+ if "pa" in stretch:
+ if "!" in stretch or overlap:
+ s += " .size = .cover,\n"
+ else:
+ s += " .size = .fit_cover1,\n"
+ elif "xy" in stretch:
+ s += " .size = .stretch,\n"
else:
- s += " .size_horizontal = .fit,\n"
- s += " .size_vertical = .fit,\n"
+ print(f"Warning: Unknown stretch rule {stretch}")
- # `^` indicates that scaling should fill
- # the whole cell, not just the icon height.
+ # `^` indicates that scaling should use the
+ # full cell height, not just the icon height,
+ # even when the constraint width is 1
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`.
- if "1" in stretch or ("xy" in stretch and "2" not in stretch):
+ # - If the stretch mode is not `pa` and there's not an explicit `2`.
+ if "1" in stretch or ("pa" not in stretch and "2" not in stretch):
s += " .max_constraint_width = 1,\n"
if align is not None:
@@ -233,14 +235,14 @@ 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"
+ if relative_width != 1.0:
+ s += f" .relative_width = {relative_width:.16f},\n"
+ if relative_height != 1.0:
+ s += f" .relative_height = {relative_height:.16f},\n"
+ if relative_x != 0.0:
+ s += f" .relative_x = {relative_x:.16f},\n"
+ if relative_y != 0.0:
+ s += f" .relative_y = {relative_y:.16f},\n"
# `overlap` and `ypadding` are mutually exclusive,
# this is asserted in the nerd fonts patcher itself.
@@ -286,7 +288,7 @@ def generate_zig_switch_arms(
yMin = math.inf
xMax = -math.inf
yMax = -math.inf
- individual_bounds: dict[int, tuple[int, int, int ,int]] = {}
+ individual_bounds: dict[int, tuple[int, int, int, int]] = {}
for cp in group:
if cp not in cmap:
continue
@@ -306,10 +308,10 @@ def generate_zig_switch_arms(
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
+ entries[cp]["relative_width"] = this_width / group_width
+ entries[cp]["relative_height"] = this_height / group_height
+ entries[cp]["relative_x"] = (this_bounds[0] - xMin) / group_width
+ entries[cp]["relative_y"] = (this_bounds[1] - yMin) / group_height
del entries[0]
@@ -350,7 +352,7 @@ if __name__ == "__main__":
const Constraint = @import("face.zig").RenderOptions.Constraint;
-/// Get the a constraints for the provided codepoint.
+/// Get the constraints for the provided codepoint.
pub fn getConstraint(cp: u21) ?Constraint {
return switch (cp) {
""")
diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig
index fbc8cab99..802c769a6 100644
--- a/src/renderer/generic.zig
+++ b/src/renderer/generic.zig
@@ -3093,8 +3093,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// its cell(s), we don't modify the alignment at all.
.constraint = getConstraint(cp) orelse
if (cellpkg.isSymbol(cp)) .{
- .size_horizontal = .fit,
- .size_vertical = .fit,
+ .size = .fit,
} else .none,
.constraint_width = constraintWidth(cell_pin),
},