···11//! Glyph is a single loaded glyph for a face.
22const Glyph = @This();
3344+const std = @import("std");
55+const Metrics = @import("Metrics.zig");
66+47/// width of glyph in pixels
58width: u32,
69···1720/// be normalized to be between 0 and 1 prior to use in shaders.
1821atlas_x: u32,
1922atlas_y: u32,
2323+2424+/// The size and position of a glyph.
2525+pub const Size = struct {
2626+ width: f64,
2727+ height: f64,
2828+ x: f64,
2929+ y: f64,
3030+};
3131+3232+/// Metrics describing the authored glyph coordinate space.
3333+pub const DesignMetrics = struct {
3434+ /// Units-per-em for outline/design coordinates.
3535+ units_per_em: u32,
3636+3737+ /// Authored advance width in design units.
3838+ advance_width: u32,
3939+4040+ /// Authored line height in design units.
4141+ line_height: u32,
4242+};
4343+4444+/// Additional options for rendering glyphs.
4545+pub const RenderOptions = struct {
4646+ /// The metrics that are defining the grid layout. These are usually
4747+ /// the metrics of the primary font face. The grid metrics are used
4848+ /// by the font face to better layout the glyph in situations where
4949+ /// the font is not exactly the same size as the grid.
5050+ grid_metrics: Metrics,
5151+5252+ /// The number of grid cells this glyph will take up. This can be used
5353+ /// optionally by the rasterizer to better layout the glyph.
5454+ cell_width: ?u2 = null,
5555+5656+ /// Constraint and alignment properties for the glyph. The rasterizer
5757+ /// should call the `constrain` function on this with the original size
5858+ /// and bearings of the glyph to get remapped values that the glyph
5959+ /// should be scaled/moved to.
6060+ constraint: Constraint = .none,
6161+6262+ /// The number of cells, horizontally that the glyph is free to take up
6363+ /// when resized and aligned by `constraint`. This is usually 1, but if
6464+ /// there's whitespace to the right of the cell then it can be 2.
6565+ constraint_width: u2 = 1,
6666+6767+ /// Thicken the glyph. This draws the glyph with a thicker stroke width.
6868+ /// This is purely an aesthetic setting.
6969+ ///
7070+ /// This only works with CoreText currently.
7171+ thicken: bool = false,
7272+7373+ /// "Strength" of the thickening, between `0` and `255`.
7474+ /// Only has an effect when `thicken` is enabled.
7575+ ///
7676+ /// `0` does not correspond to *no* thickening,
7777+ /// just the *lightest* thickening available.
7878+ ///
7979+ /// CoreText only.
8080+ thicken_strength: u8 = 255,
8181+8282+ /// See the `constraint` field.
8383+ pub const Constraint = struct {
8484+ /// Don't constrain the glyph in any way.
8585+ pub const none: Constraint = .{};
8686+8787+ /// Sizing rule.
8888+ size: Constraint.Size = .none,
8989+9090+ /// Vertical alignment rule.
9191+ align_vertical: Align = .none,
9292+ /// Horizontal alignment rule.
9393+ align_horizontal: Align = .none,
9494+9595+ /// Top padding when resizing.
9696+ pad_top: f64 = 0.0,
9797+ /// Left padding when resizing.
9898+ pad_left: f64 = 0.0,
9999+ /// Right padding when resizing.
100100+ pad_right: f64 = 0.0,
101101+ /// Bottom padding when resizing.
102102+ pad_bottom: f64 = 0.0,
103103+104104+ // Size and bearings of the glyph relative
105105+ // to the bounding box of its scale group.
106106+ relative_width: f64 = 1.0,
107107+ relative_height: f64 = 1.0,
108108+ relative_x: f64 = 0.0,
109109+ relative_y: f64 = 0.0,
110110+111111+ /// Maximum aspect ratio (width/height) to allow when stretching.
112112+ max_xy_ratio: ?f64 = null,
113113+114114+ /// Maximum number of cells horizontally to use.
115115+ max_constraint_width: u2 = 2,
116116+117117+ /// What to use as the height metric when constraining the glyph and
118118+ /// the constraint width is 1,
119119+ height: Height = .cell,
120120+121121+ pub const Size = enum {
122122+ /// Don't change the size of this glyph.
123123+ none,
124124+ /// Scale the glyph down if needed to fit within the bounds,
125125+ /// preserving aspect ratio.
126126+ fit,
127127+ /// Scale the glyph up or down to exactly match the bounds,
128128+ /// preserving aspect ratio.
129129+ cover,
130130+ /// Scale the glyph down if needed to fit within the bounds,
131131+ /// preserving aspect ratio. If the glyph doesn't cover a
132132+ /// single cell, scale up. If the glyph exceeds a single
133133+ /// cell but is within the bounds, do nothing.
134134+ /// (Nerd Font specific rule.)
135135+ fit_cover1,
136136+ /// Stretch the glyph to exactly fit the bounds in both
137137+ /// directions, disregarding aspect ratio.
138138+ stretch,
139139+ };
140140+141141+ pub const Align = enum {
142142+ /// Don't move the glyph on this axis.
143143+ none,
144144+ /// Move the glyph so that its leading (bottom/left)
145145+ /// edge aligns with the leading edge of the axis.
146146+ start,
147147+ /// Move the glyph so that its trailing (top/right)
148148+ /// edge aligns with the trailing edge of the axis.
149149+ end,
150150+ /// Move the glyph so that it is centered on this axis.
151151+ center,
152152+ /// Move the glyph so that it is centered on this axis,
153153+ /// but always with respect to the first cell even for
154154+ /// multi-cell constraints. (Nerd Font specific rule.)
155155+ center1,
156156+ };
157157+158158+ pub const Height = enum {
159159+ /// Use the full line height of the primary face for
160160+ /// constraining this glyph.
161161+ cell,
162162+ /// Use the icon height from the grid metrics for
163163+ /// constraining this glyph. Unlike `cell`, the value of
164164+ /// this height depends on both the constraint width and the
165165+ /// affected by the `adjust-icon-height` config option.
166166+ icon,
167167+ };
168168+169169+ /// Returns true if the constraint does anything. If it doesn't,
170170+ /// because it neither sizes nor positions the glyph, then this
171171+ /// returns false.
172172+ pub inline fn doesAnything(self: Constraint) bool {
173173+ return self.size != .none or
174174+ self.align_horizontal != .none or
175175+ self.align_vertical != .none;
176176+ }
177177+178178+ /// Apply this constraint to the provided glyph
179179+ /// size, given the available width and height.
180180+ pub fn constrain(
181181+ self: Constraint,
182182+ glyph: Glyph.Size,
183183+ metrics: Metrics,
184184+ /// Number of cells horizontally available for this glyph.
185185+ constraint_width: u2,
186186+ ) Glyph.Size {
187187+ if (!self.doesAnything()) return glyph;
188188+189189+ switch (self.size) {
190190+ .stretch => {
191191+ // Stretched glyphs are usually meant to align across cell
192192+ // boundaries, which works best if they're scaled and
193193+ // aligned to the grid rather than the face. This is most
194194+ // easily done by inserting this little fib in the metrics.
195195+ var m = metrics;
196196+ m.face_width = @floatFromInt(m.cell_width);
197197+ m.face_height = @floatFromInt(m.cell_height);
198198+ m.face_y = 0.0;
199199+200200+ // Negative padding for stretched glyphs is a band-aid to
201201+ // avoid gaps due to pixel rounding, but at the cost of
202202+ // unsightly overlap artifacts. Since we scale and align to
203203+ // the grid rather than the face, we don't need it.
204204+ var c = self;
205205+ c.pad_bottom = @max(0, c.pad_bottom);
206206+ c.pad_top = @max(0, c.pad_top);
207207+ c.pad_left = @max(0, c.pad_left);
208208+ c.pad_right = @max(0, c.pad_right);
209209+210210+ return c.constrainInner(glyph, m, constraint_width);
211211+ },
212212+ else => return self.constrainInner(glyph, metrics, constraint_width),
213213+ }
214214+ }
215215+216216+ fn constrainInner(
217217+ self: Constraint,
218218+ glyph: Glyph.Size,
219219+ metrics: Metrics,
220220+ constraint_width: u2,
221221+ ) Glyph.Size {
222222+ // For extra wide font faces, never stretch glyphs across two cells.
223223+ // This mirrors font_patcher.
224224+ const min_constraint_width: u2 = if ((self.size == .stretch) and (metrics.face_width > 0.9 * metrics.face_height))
225225+ 1
226226+ else
227227+ @min(self.max_constraint_width, constraint_width);
228228+229229+ // The bounding box for the glyph's scale group.
230230+ // Scaling and alignment rules are calculated for
231231+ // this box and then applied to the glyph.
232232+ var group: Glyph.Size = group: {
233233+ const group_width = glyph.width / self.relative_width;
234234+ const group_height = glyph.height / self.relative_height;
235235+ break :group .{
236236+ .width = group_width,
237237+ .height = group_height,
238238+ .x = glyph.x - (group_width * self.relative_x),
239239+ .y = glyph.y - (group_height * self.relative_y),
240240+ };
241241+ };
242242+243243+ // Apply prescribed scaling, preserving the
244244+ // center bearings of the group bounding box
245245+ const width_factor, const height_factor = self.scale_factors(group, metrics, min_constraint_width);
246246+ const center_x = group.x + (group.width / 2);
247247+ const center_y = group.y + (group.height / 2);
248248+ group.width *= width_factor;
249249+ group.height *= height_factor;
250250+ group.x = center_x - (group.width / 2);
251251+ group.y = center_y - (group.height / 2);
252252+253253+ // NOTE: font_patcher jumps through a lot of hoops at this
254254+ // point to ensure that the glyph remains within the target
255255+ // bounding box after rounding to font definition units.
256256+ // This is irrelevant here as we're not rounding, we're
257257+ // staying in f64 and heading straight to rendering.
258258+259259+ // Apply prescribed alignment
260260+ group.y = self.aligned_y(group, metrics);
261261+ group.x = self.aligned_x(group, metrics, min_constraint_width);
262262+263263+ // Transfer the scaling and alignment back to the glyph and return.
264264+ return .{
265265+ .width = width_factor * glyph.width,
266266+ .height = height_factor * glyph.height,
267267+ .x = group.x + (group.width * self.relative_x),
268268+ .y = group.y + (group.height * self.relative_y),
269269+ };
270270+ }
271271+272272+ /// Return width and height scaling factors for this scaling group.
273273+ fn scale_factors(
274274+ self: Constraint,
275275+ group: Glyph.Size,
276276+ metrics: Metrics,
277277+ min_constraint_width: u2,
278278+ ) struct { f64, f64 } {
279279+ if (self.size == .none) {
280280+ return .{ 1.0, 1.0 };
281281+ }
282282+283283+ const multi_cell = (min_constraint_width > 1);
284284+285285+ const pad_width_factor = @as(f64, @floatFromInt(min_constraint_width)) - (self.pad_left + self.pad_right);
286286+ const pad_height_factor = 1 - (self.pad_bottom + self.pad_top);
287287+288288+ const target_width = pad_width_factor * metrics.face_width;
289289+ const target_height = pad_height_factor * switch (self.height) {
290290+ .cell => metrics.face_height,
291291+ // Like font-patcher, the icon constraint height depends on the
292292+ // constraint width. Unlike font-patcher, the multi-cell
293293+ // icon_height may be different from face_height due to the
294294+ // `adjust-icon-height` config option.
295295+ .icon => if (multi_cell)
296296+ metrics.icon_height
297297+ else
298298+ metrics.icon_height_single,
299299+ };
300300+301301+ var width_factor = target_width / group.width;
302302+ var height_factor = target_height / group.height;
303303+304304+ switch (self.size) {
305305+ .none => unreachable,
306306+ .fit => {
307307+ // Scale down to fit if needed
308308+ height_factor = @min(1, width_factor, height_factor);
309309+ width_factor = height_factor;
310310+ },
311311+ .cover => {
312312+ // Scale to cover
313313+ height_factor = @min(width_factor, height_factor);
314314+ width_factor = height_factor;
315315+ },
316316+ .fit_cover1 => {
317317+ // Scale down to fit or up to cover at least one cell
318318+ // NOTE: This is similar to font_patcher's "pa" mode,
319319+ // however, font_patcher will only do the upscaling
320320+ // part if the constraint width is 1, resulting in
321321+ // some icons becoming smaller when the constraint
322322+ // width increases. You'd see icons shrinking when
323323+ // opening up a space after them. This makes no
324324+ // sense, so we've fixed the rule such that these
325325+ // icons are scaled to the same size for multi-cell
326326+ // constraints as they would be for single-cell.
327327+ height_factor = @min(width_factor, height_factor);
328328+ if (multi_cell and (height_factor > 1)) {
329329+ // Call back into this function with
330330+ // constraint width 1 to get single-cell scale
331331+ // factors. We use the height factor as width
332332+ // could have been modified by max_xy_ratio.
333333+ _, const single_height_factor = self.scale_factors(group, metrics, 1);
334334+ height_factor = @max(1, single_height_factor);
335335+ }
336336+ width_factor = height_factor;
337337+ },
338338+ .stretch => {},
339339+ }
340340+341341+ // Reduce aspect ratio if required
342342+ if (self.max_xy_ratio) |ratio| {
343343+ if (group.width * width_factor > group.height * height_factor * ratio) {
344344+ width_factor = group.height * height_factor * ratio / group.width;
345345+ }
346346+ }
347347+348348+ return .{ width_factor, height_factor };
349349+ }
350350+351351+ /// Return vertical bearing for aligning this group
352352+ fn aligned_y(
353353+ self: Constraint,
354354+ group: Glyph.Size,
355355+ metrics: Metrics,
356356+ ) f64 {
357357+ if ((self.size == .none) and (self.align_vertical == .none)) {
358358+ // If we don't have any constraints affecting the vertical axis,
359359+ // we don't touch vertical alignment.
360360+ return group.y;
361361+ }
362362+ // We use face_height and offset by face_y, rather than
363363+ // using cell_height directly, to account for the asymmetry
364364+ // of the pixel cell around the face (a consequence of
365365+ // aligning the baseline with a pixel boundary rather than
366366+ // vertically centering the face).
367367+ const pad_bottom_dy = self.pad_bottom * metrics.face_height;
368368+ const pad_top_dy = self.pad_top * metrics.face_height;
369369+ const start_y = metrics.face_y + pad_bottom_dy;
370370+ const end_y = metrics.face_y + (metrics.face_height - group.height - pad_top_dy);
371371+ const center_y = (start_y + end_y) / 2;
372372+ return switch (self.align_vertical) {
373373+ // NOTE: Even if there is no prescribed alignment, we ensure
374374+ // that the group doesn't protrude outside the padded cell,
375375+ // since this is implied by every available size constraint. If
376376+ // the group is too high we fall back to centering, though if we
377377+ // hit the .none prong we always have self.size != .none, so
378378+ // this should never happen.
379379+ .none => if (end_y < start_y)
380380+ center_y
381381+ else
382382+ @max(start_y, @min(group.y, end_y)),
383383+ .start => start_y,
384384+ .end => end_y,
385385+ .center, .center1 => center_y,
386386+ };
387387+ }
388388+389389+ /// Return horizontal bearing for aligning this group
390390+ fn aligned_x(
391391+ self: Constraint,
392392+ group: Glyph.Size,
393393+ metrics: Metrics,
394394+ min_constraint_width: u2,
395395+ ) f64 {
396396+ if ((self.size == .none) and (self.align_horizontal == .none)) {
397397+ // If we don't have any constraints affecting the horizontal
398398+ // axis, we don't touch horizontal alignment.
399399+ return group.x;
400400+ }
401401+ // For multi-cell constraints, we align relative to the span
402402+ // from the left edge of the first cell to the right edge of
403403+ // the last face cell assuming it's left-aligned within the
404404+ // rounded and adjusted pixel cell. Any horizontal offset to
405405+ // center the face within the grid cell is the responsibility
406406+ // of the backend-specific rendering code, and should be done
407407+ // after applying constraints.
408408+ const full_face_span = metrics.face_width + @as(f64, @floatFromInt((min_constraint_width - 1) * metrics.cell_width));
409409+ const pad_left_dx = self.pad_left * metrics.face_width;
410410+ const pad_right_dx = self.pad_right * metrics.face_width;
411411+ const start_x = pad_left_dx;
412412+ const end_x = full_face_span - group.width - pad_right_dx;
413413+ return switch (self.align_horizontal) {
414414+ // NOTE: Even if there is no prescribed alignment, we ensure
415415+ // that the glyph doesn't protrude outside the padded cell,
416416+ // since this is implied by every available size constraint. The
417417+ // left-side bound has priority if the group is too wide, though
418418+ // if we hit the .none prong we always have self.size != .none,
419419+ // so this should never happen.
420420+ .none => @max(start_x, @min(group.x, end_x)),
421421+ .start => start_x,
422422+ .end => @max(start_x, end_x),
423423+ .center => @max(start_x, (start_x + end_x) / 2),
424424+ // NOTE: .center1 implements the font_patcher rule of centering
425425+ // in the first cell even for multi-cell constraints. Since glyphs
426426+ // are not allowed to protrude to the left, this results in the
427427+ // left-alignment like .start when the glyph is wider than a cell.
428428+ .center1 => center1: {
429429+ const end1_x = metrics.face_width - group.width - pad_right_dx;
430430+ break :center1 @max(start_x, (start_x + end1_x) / 2);
431431+ },
432432+ };
433433+ }
434434+ };
435435+};
436436+437437+test "Constraints" {
438438+ const comparison = @import("../datastruct/comparison.zig");
439439+ const getConstraint = @import("nerd_font_attributes.zig").getConstraint;
440440+ const GlyphSize = Size;
441441+442442+ // Hardcoded data matches metrics from CoreText at size 12 and DPI 96.
443443+444444+ // Define grid metrics (matches font-family = JetBrains Mono)
445445+ const metrics: Metrics = .{
446446+ .cell_width = 10,
447447+ .cell_height = 22,
448448+ .cell_baseline = 5,
449449+ .underline_position = 19,
450450+ .underline_thickness = 1,
451451+ .strikethrough_position = 12,
452452+ .strikethrough_thickness = 1,
453453+ .overline_position = 0,
454454+ .overline_thickness = 1,
455455+ .box_thickness = 1,
456456+ .cursor_thickness = 1,
457457+ .cursor_height = 22,
458458+ .icon_height = 21.12,
459459+ .icon_height_single = 44.48 / 3.0,
460460+ .face_width = 9.6,
461461+ .face_height = 21.12,
462462+ .face_y = 0.2,
463463+ };
464464+465465+ // ASCII (no constraint).
466466+ {
467467+ const constraint: RenderOptions.Constraint = .none;
468468+469469+ // BBox of 'x' from JetBrains Mono.
470470+ const glyph_x: GlyphSize = .{
471471+ .width = 6.784,
472472+ .height = 15.28,
473473+ .x = 1.408,
474474+ .y = 4.84,
475475+ };
476476+477477+ // Any constraint width: do nothing.
478478+ inline for (.{ 1, 2 }) |constraint_width| {
479479+ try comparison.expectApproxEqual(
480480+ glyph_x,
481481+ constraint.constrain(glyph_x, metrics, constraint_width),
482482+ );
483483+ }
484484+ }
485485+486486+ // Symbol (same constraint as hardcoded in Renderer.addGlyph).
487487+ {
488488+ const constraint: RenderOptions.Constraint = .{ .size = .fit };
489489+490490+ // BBox of '■' (0x25A0 black square) from Iosevka.
491491+ // NOTE: This glyph is designed to span two cells.
492492+ const glyph_25A0: GlyphSize = .{
493493+ .width = 10.272,
494494+ .height = 10.272,
495495+ .x = 2.864,
496496+ .y = 5.304,
497497+ };
498498+499499+ // Constraint width 1: scale down and shift to fit a single cell.
500500+ try comparison.expectApproxEqual(
501501+ GlyphSize{
502502+ .width = metrics.face_width,
503503+ .height = metrics.face_width,
504504+ .x = 0,
505505+ .y = 5.64,
506506+ },
507507+ constraint.constrain(glyph_25A0, metrics, 1),
508508+ );
509509+510510+ // Constraint width 2: do nothing.
511511+ try comparison.expectApproxEqual(
512512+ glyph_25A0,
513513+ constraint.constrain(glyph_25A0, metrics, 2),
514514+ );
515515+ }
516516+517517+ // Emoji (same constraint as hardcoded in SharedGrid.renderGlyph).
518518+ {
519519+ const constraint: RenderOptions.Constraint = .{
520520+ .size = .cover,
521521+ .align_horizontal = .center,
522522+ .align_vertical = .center,
523523+ .pad_left = 0.025,
524524+ .pad_right = 0.025,
525525+ };
526526+527527+ // BBox of '🥸' (0x1F978) from Apple Color Emoji.
528528+ const glyph_1F978: GlyphSize = .{
529529+ .width = 20,
530530+ .height = 20,
531531+ .x = 0.46,
532532+ .y = 1,
533533+ };
534534+535535+ // Constraint width 2: scale to cover two cells with padding, center;
536536+ try comparison.expectApproxEqual(
537537+ GlyphSize{
538538+ .width = 18.72,
539539+ .height = 18.72,
540540+ .x = 0.44,
541541+ .y = 1.4,
542542+ },
543543+ constraint.constrain(glyph_1F978, metrics, 2),
544544+ );
545545+ }
546546+547547+ // Nerd Font default.
548548+ {
549549+ const constraint = getConstraint(0xea61).?;
550550+551551+ // Verify that this is the constraint we expect.
552552+ try std.testing.expectEqual(.fit_cover1, constraint.size);
553553+ try std.testing.expectEqual(.icon, constraint.height);
554554+ try std.testing.expectEqual(.center1, constraint.align_horizontal);
555555+ try std.testing.expectEqual(.center1, constraint.align_vertical);
556556+557557+ // BBox of '' (0xEA61 nf-cod-lightbulb) from Symbols Only.
558558+ // NOTE: This icon is part of a group, so the
559559+ // constraint applies to a larger bounding box.
560560+ const glyph_EA61: GlyphSize = .{
561561+ .width = 9.015625,
562562+ .height = 13.015625,
563563+ .x = 3.015625,
564564+ .y = 3.76525,
565565+ };
566566+567567+ // Constraint width 1: scale and shift group to fit a single cell.
568568+ try comparison.expectApproxEqual(
569569+ GlyphSize{
570570+ .width = 7.2125,
571571+ .height = 10.4125,
572572+ .x = 0.8125,
573573+ .y = 5.950695224719102,
574574+ },
575575+ constraint.constrain(glyph_EA61, metrics, 1),
576576+ );
577577+578578+ // Constraint width 2: no scaling; left-align and vertically center group.
579579+ try comparison.expectApproxEqual(
580580+ GlyphSize{
581581+ .width = glyph_EA61.width,
582582+ .height = glyph_EA61.height,
583583+ .x = 1.015625,
584584+ .y = 4.7483690308988775,
585585+ },
586586+ constraint.constrain(glyph_EA61, metrics, 2),
587587+ );
588588+ }
589589+590590+ // Nerd Font stretch.
591591+ {
592592+ const constraint = getConstraint(0xe0c0).?;
593593+594594+ // Verify that this is the constraint we expect.
595595+ try std.testing.expectEqual(.stretch, constraint.size);
596596+ try std.testing.expectEqual(.cell, constraint.height);
597597+ try std.testing.expectEqual(.start, constraint.align_horizontal);
598598+ try std.testing.expectEqual(.center1, constraint.align_vertical);
599599+600600+ // BBox of ' ' (0xE0C0 nf-ple-flame_thick) from Symbols Only.
601601+ const glyph_E0C0: GlyphSize = .{
602602+ .width = 16.796875,
603603+ .height = 16.46875,
604604+ .x = -0.796875,
605605+ .y = 1.7109375,
606606+ };
607607+608608+ // Constraint width 1: stretch and position to exactly cover one cell.
609609+ try comparison.expectApproxEqual(
610610+ GlyphSize{
611611+ .width = @floatFromInt(metrics.cell_width),
612612+ .height = @floatFromInt(metrics.cell_height),
613613+ .x = 0,
614614+ .y = 0,
615615+ },
616616+ constraint.constrain(glyph_E0C0, metrics, 1),
617617+ );
618618+619619+ // Constraint width 1: stretch and position to exactly cover two cells.
620620+ try comparison.expectApproxEqual(
621621+ GlyphSize{
622622+ .width = @floatFromInt(2 * metrics.cell_width),
623623+ .height = @floatFromInt(metrics.cell_height),
624624+ .x = 0,
625625+ .y = 0,
626626+ },
627627+ constraint.constrain(glyph_E0C0, metrics, 2),
628628+ );
629629+ }
630630+}
···22const builtin = @import("builtin");
33const build_config = @import("../build_config.zig");
44const options = @import("main.zig").options;
55-const Metrics = @import("main.zig").Metrics;
65const config = @import("../config.zig");
76const freetype = @import("face/freetype.zig");
87const coretext = @import("face/coretext.zig");
···9493 };
9594};
96959797-/// The size and position of a glyph.
9898-pub const GlyphSize = struct {
9999- width: f64,
100100- height: f64,
101101- x: f64,
102102- y: f64,
103103-};
104104-105105-/// Additional options for rendering glyphs.
106106-pub const RenderOptions = struct {
107107- /// The metrics that are defining the grid layout. These are usually
108108- /// the metrics of the primary font face. The grid metrics are used
109109- /// by the font face to better layout the glyph in situations where
110110- /// the font is not exactly the same size as the grid.
111111- grid_metrics: Metrics,
112112-113113- /// The number of grid cells this glyph will take up. This can be used
114114- /// optionally by the rasterizer to better layout the glyph.
115115- cell_width: ?u2 = null,
116116-117117- /// Constraint and alignment properties for the glyph. The rasterizer
118118- /// should call the `constrain` function on this with the original size
119119- /// and bearings of the glyph to get remapped values that the glyph
120120- /// should be scaled/moved to.
121121- constraint: Constraint = .none,
122122-123123- /// The number of cells, horizontally that the glyph is free to take up
124124- /// when resized and aligned by `constraint`. This is usually 1, but if
125125- /// there's whitespace to the right of the cell then it can be 2.
126126- constraint_width: u2 = 1,
127127-128128- /// Thicken the glyph. This draws the glyph with a thicker stroke width.
129129- /// This is purely an aesthetic setting.
130130- ///
131131- /// This only works with CoreText currently.
132132- thicken: bool = false,
133133-134134- /// "Strength" of the thickening, between `0` and `255`.
135135- /// Only has an effect when `thicken` is enabled.
136136- ///
137137- /// `0` does not correspond to *no* thickening,
138138- /// just the *lightest* thickening available.
139139- ///
140140- /// CoreText only.
141141- thicken_strength: u8 = 255,
142142-143143- /// See the `constraint` field.
144144- pub const Constraint = struct {
145145- /// Don't constrain the glyph in any way.
146146- pub const none: Constraint = .{};
147147-148148- /// Sizing rule.
149149- size: Size = .none,
150150-151151- /// Vertical alignment rule.
152152- align_vertical: Align = .none,
153153- /// Horizontal alignment rule.
154154- align_horizontal: Align = .none,
155155-156156- /// Top padding when resizing.
157157- pad_top: f64 = 0.0,
158158- /// Left padding when resizing.
159159- pad_left: f64 = 0.0,
160160- /// Right padding when resizing.
161161- pad_right: f64 = 0.0,
162162- /// Bottom padding when resizing.
163163- pad_bottom: f64 = 0.0,
164164-165165- // Size and bearings of the glyph relative
166166- // to the bounding box of its scale group.
167167- relative_width: f64 = 1.0,
168168- relative_height: f64 = 1.0,
169169- relative_x: f64 = 0.0,
170170- relative_y: f64 = 0.0,
171171-172172- /// Maximum aspect ratio (width/height) to allow when stretching.
173173- max_xy_ratio: ?f64 = null,
174174-175175- /// Maximum number of cells horizontally to use.
176176- max_constraint_width: u2 = 2,
177177-178178- /// What to use as the height metric when constraining the glyph and
179179- /// the constraint width is 1,
180180- height: Height = .cell,
181181-182182- pub const Size = enum {
183183- /// Don't change the size of this glyph.
184184- none,
185185- /// Scale the glyph down if needed to fit within the bounds,
186186- /// preserving aspect ratio.
187187- fit,
188188- /// Scale the glyph up or down to exactly match the bounds,
189189- /// preserving aspect ratio.
190190- cover,
191191- /// Scale the glyph down if needed to fit within the bounds,
192192- /// preserving aspect ratio. If the glyph doesn't cover a
193193- /// single cell, scale up. If the glyph exceeds a single
194194- /// cell but is within the bounds, do nothing.
195195- /// (Nerd Font specific rule.)
196196- fit_cover1,
197197- /// Stretch the glyph to exactly fit the bounds in both
198198- /// directions, disregarding aspect ratio.
199199- stretch,
200200- };
201201-202202- pub const Align = enum {
203203- /// Don't move the glyph on this axis.
204204- none,
205205- /// Move the glyph so that its leading (bottom/left)
206206- /// edge aligns with the leading edge of the axis.
207207- start,
208208- /// Move the glyph so that its trailing (top/right)
209209- /// edge aligns with the trailing edge of the axis.
210210- end,
211211- /// Move the glyph so that it is centered on this axis.
212212- center,
213213- /// Move the glyph so that it is centered on this axis,
214214- /// but always with respect to the first cell even for
215215- /// multi-cell constraints. (Nerd Font specific rule.)
216216- center1,
217217- };
218218-219219- pub const Height = enum {
220220- /// Use the full line height of the primary face for
221221- /// constraining this glyph.
222222- cell,
223223- /// Use the icon height from the grid metrics for
224224- /// constraining this glyph. Unlike `cell`, the value of
225225- /// this height depends on both the constraint width and the
226226- /// affected by the `adjust-icon-height` config option.
227227- icon,
228228- };
229229-230230- /// Returns true if the constraint does anything. If it doesn't,
231231- /// because it neither sizes nor positions the glyph, then this
232232- /// returns false.
233233- pub inline fn doesAnything(self: Constraint) bool {
234234- return self.size != .none or
235235- self.align_horizontal != .none or
236236- self.align_vertical != .none;
237237- }
238238-239239- /// Apply this constraint to the provided glyph
240240- /// size, given the available width and height.
241241- pub fn constrain(
242242- self: Constraint,
243243- glyph: GlyphSize,
244244- metrics: Metrics,
245245- /// Number of cells horizontally available for this glyph.
246246- constraint_width: u2,
247247- ) GlyphSize {
248248- if (!self.doesAnything()) return glyph;
249249-250250- switch (self.size) {
251251- .stretch => {
252252- // Stretched glyphs are usually meant to align across cell
253253- // boundaries, which works best if they're scaled and
254254- // aligned to the grid rather than the face. This is most
255255- // easily done by inserting this little fib in the metrics.
256256- var m = metrics;
257257- m.face_width = @floatFromInt(m.cell_width);
258258- m.face_height = @floatFromInt(m.cell_height);
259259- m.face_y = 0.0;
260260-261261- // Negative padding for stretched glyphs is a band-aid to
262262- // avoid gaps due to pixel rounding, but at the cost of
263263- // unsightly overlap artifacts. Since we scale and align to
264264- // the grid rather than the face, we don't need it.
265265- var c = self;
266266- c.pad_bottom = @max(0, c.pad_bottom);
267267- c.pad_top = @max(0, c.pad_top);
268268- c.pad_left = @max(0, c.pad_left);
269269- c.pad_right = @max(0, c.pad_right);
270270-271271- return c.constrainInner(glyph, m, constraint_width);
272272- },
273273- else => return self.constrainInner(glyph, metrics, constraint_width),
274274- }
275275- }
276276-277277- fn constrainInner(
278278- self: Constraint,
279279- glyph: GlyphSize,
280280- metrics: Metrics,
281281- constraint_width: u2,
282282- ) GlyphSize {
283283- // For extra wide font faces, never stretch glyphs across two cells.
284284- // This mirrors font_patcher.
285285- const min_constraint_width: u2 = if ((self.size == .stretch) and (metrics.face_width > 0.9 * metrics.face_height))
286286- 1
287287- else
288288- @min(self.max_constraint_width, constraint_width);
289289-290290- // The bounding box for the glyph's scale group.
291291- // Scaling and alignment rules are calculated for
292292- // this box and then applied to the glyph.
293293- var group: GlyphSize = group: {
294294- const group_width = glyph.width / self.relative_width;
295295- const group_height = glyph.height / self.relative_height;
296296- break :group .{
297297- .width = group_width,
298298- .height = group_height,
299299- .x = glyph.x - (group_width * self.relative_x),
300300- .y = glyph.y - (group_height * self.relative_y),
301301- };
302302- };
303303-304304- // Apply prescribed scaling, preserving the
305305- // center bearings of the group bounding box
306306- const width_factor, const height_factor = self.scale_factors(group, metrics, min_constraint_width);
307307- const center_x = group.x + (group.width / 2);
308308- const center_y = group.y + (group.height / 2);
309309- group.width *= width_factor;
310310- group.height *= height_factor;
311311- group.x = center_x - (group.width / 2);
312312- group.y = center_y - (group.height / 2);
313313-314314- // NOTE: font_patcher jumps through a lot of hoops at this
315315- // point to ensure that the glyph remains within the target
316316- // bounding box after rounding to font definition units.
317317- // This is irrelevant here as we're not rounding, we're
318318- // staying in f64 and heading straight to rendering.
319319-320320- // Apply prescribed alignment
321321- group.y = self.aligned_y(group, metrics);
322322- group.x = self.aligned_x(group, metrics, min_constraint_width);
323323-324324- // Transfer the scaling and alignment back to the glyph and return.
325325- return .{
326326- .width = width_factor * glyph.width,
327327- .height = height_factor * glyph.height,
328328- .x = group.x + (group.width * self.relative_x),
329329- .y = group.y + (group.height * self.relative_y),
330330- };
331331- }
332332-333333- /// Return width and height scaling factors for this scaling group.
334334- fn scale_factors(
335335- self: Constraint,
336336- group: GlyphSize,
337337- metrics: Metrics,
338338- min_constraint_width: u2,
339339- ) struct { f64, f64 } {
340340- if (self.size == .none) {
341341- return .{ 1.0, 1.0 };
342342- }
343343-344344- const multi_cell = (min_constraint_width > 1);
345345-346346- const pad_width_factor = @as(f64, @floatFromInt(min_constraint_width)) - (self.pad_left + self.pad_right);
347347- const pad_height_factor = 1 - (self.pad_bottom + self.pad_top);
348348-349349- const target_width = pad_width_factor * metrics.face_width;
350350- const target_height = pad_height_factor * switch (self.height) {
351351- .cell => metrics.face_height,
352352- // Like font-patcher, the icon constraint height depends on the
353353- // constraint width. Unlike font-patcher, the multi-cell
354354- // icon_height may be different from face_height due to the
355355- // `adjust-icon-height` config option.
356356- .icon => if (multi_cell)
357357- metrics.icon_height
358358- else
359359- metrics.icon_height_single,
360360- };
361361-362362- var width_factor = target_width / group.width;
363363- var height_factor = target_height / group.height;
364364-365365- switch (self.size) {
366366- .none => unreachable,
367367- .fit => {
368368- // Scale down to fit if needed
369369- height_factor = @min(1, width_factor, height_factor);
370370- width_factor = height_factor;
371371- },
372372- .cover => {
373373- // Scale to cover
374374- height_factor = @min(width_factor, height_factor);
375375- width_factor = height_factor;
376376- },
377377- .fit_cover1 => {
378378- // Scale down to fit or up to cover at least one cell
379379- // NOTE: This is similar to font_patcher's "pa" mode,
380380- // however, font_patcher will only do the upscaling
381381- // part if the constraint width is 1, resulting in
382382- // some icons becoming smaller when the constraint
383383- // width increases. You'd see icons shrinking when
384384- // opening up a space after them. This makes no
385385- // sense, so we've fixed the rule such that these
386386- // icons are scaled to the same size for multi-cell
387387- // constraints as they would be for single-cell.
388388- height_factor = @min(width_factor, height_factor);
389389- if (multi_cell and (height_factor > 1)) {
390390- // Call back into this function with
391391- // constraint width 1 to get single-cell scale
392392- // factors. We use the height factor as width
393393- // could have been modified by max_xy_ratio.
394394- _, const single_height_factor = self.scale_factors(group, metrics, 1);
395395- height_factor = @max(1, single_height_factor);
396396- }
397397- width_factor = height_factor;
398398- },
399399- .stretch => {},
400400- }
401401-402402- // Reduce aspect ratio if required
403403- if (self.max_xy_ratio) |ratio| {
404404- if (group.width * width_factor > group.height * height_factor * ratio) {
405405- width_factor = group.height * height_factor * ratio / group.width;
406406- }
407407- }
408408-409409- return .{ width_factor, height_factor };
410410- }
411411-412412- /// Return vertical bearing for aligning this group
413413- fn aligned_y(
414414- self: Constraint,
415415- group: GlyphSize,
416416- metrics: Metrics,
417417- ) f64 {
418418- if ((self.size == .none) and (self.align_vertical == .none)) {
419419- // If we don't have any constraints affecting the vertical axis,
420420- // we don't touch vertical alignment.
421421- return group.y;
422422- }
423423- // We use face_height and offset by face_y, rather than
424424- // using cell_height directly, to account for the asymmetry
425425- // of the pixel cell around the face (a consequence of
426426- // aligning the baseline with a pixel boundary rather than
427427- // vertically centering the face).
428428- const pad_bottom_dy = self.pad_bottom * metrics.face_height;
429429- const pad_top_dy = self.pad_top * metrics.face_height;
430430- const start_y = metrics.face_y + pad_bottom_dy;
431431- const end_y = metrics.face_y + (metrics.face_height - group.height - pad_top_dy);
432432- const center_y = (start_y + end_y) / 2;
433433- return switch (self.align_vertical) {
434434- // NOTE: Even if there is no prescribed alignment, we ensure
435435- // that the group doesn't protrude outside the padded cell,
436436- // since this is implied by every available size constraint. If
437437- // the group is too high we fall back to centering, though if we
438438- // hit the .none prong we always have self.size != .none, so
439439- // this should never happen.
440440- .none => if (end_y < start_y)
441441- center_y
442442- else
443443- @max(start_y, @min(group.y, end_y)),
444444- .start => start_y,
445445- .end => end_y,
446446- .center, .center1 => center_y,
447447- };
448448- }
449449-450450- /// Return horizontal bearing for aligning this group
451451- fn aligned_x(
452452- self: Constraint,
453453- group: GlyphSize,
454454- metrics: Metrics,
455455- min_constraint_width: u2,
456456- ) f64 {
457457- if ((self.size == .none) and (self.align_horizontal == .none)) {
458458- // If we don't have any constraints affecting the horizontal
459459- // axis, we don't touch horizontal alignment.
460460- return group.x;
461461- }
462462- // For multi-cell constraints, we align relative to the span
463463- // from the left edge of the first cell to the right edge of
464464- // the last face cell assuming it's left-aligned within the
465465- // rounded and adjusted pixel cell. Any horizontal offset to
466466- // center the face within the grid cell is the responsibility
467467- // of the backend-specific rendering code, and should be done
468468- // after applying constraints.
469469- const full_face_span = metrics.face_width + @as(f64, @floatFromInt((min_constraint_width - 1) * metrics.cell_width));
470470- const pad_left_dx = self.pad_left * metrics.face_width;
471471- const pad_right_dx = self.pad_right * metrics.face_width;
472472- const start_x = pad_left_dx;
473473- const end_x = full_face_span - group.width - pad_right_dx;
474474- return switch (self.align_horizontal) {
475475- // NOTE: Even if there is no prescribed alignment, we ensure
476476- // that the glyph doesn't protrude outside the padded cell,
477477- // since this is implied by every available size constraint. The
478478- // left-side bound has priority if the group is too wide, though
479479- // if we hit the .none prong we always have self.size != .none,
480480- // so this should never happen.
481481- .none => @max(start_x, @min(group.x, end_x)),
482482- .start => start_x,
483483- .end => @max(start_x, end_x),
484484- .center => @max(start_x, (start_x + end_x) / 2),
485485- // NOTE: .center1 implements the font_patcher rule of centering
486486- // in the first cell even for multi-cell constraints. Since glyphs
487487- // are not allowed to protrude to the left, this results in the
488488- // left-alignment like .start when the glyph is wider than a cell.
489489- .center1 => center1: {
490490- const end1_x = metrics.face_width - group.width - pad_right_dx;
491491- break :center1 @max(start_x, (start_x + end1_x) / 2);
492492- },
493493- };
494494- }
495495- };
496496-};
497497-49896test {
49997 @import("std").testing.refAllDecls(@This());
50098}
···512110 try testing.expectEqual(@as(u32, 1936486004), @as(u32, @bitCast(id)));
513111 try testing.expectEqualStrings("slnt", &(id.str()));
514112}
515515-516516-test "Constraints" {
517517- const comparison = @import("../datastruct/comparison.zig");
518518- const getConstraint = @import("nerd_font_attributes.zig").getConstraint;
519519-520520- // Hardcoded data matches metrics from CoreText at size 12 and DPI 96.
521521-522522- // Define grid metrics (matches font-family = JetBrains Mono)
523523- const metrics: Metrics = .{
524524- .cell_width = 10,
525525- .cell_height = 22,
526526- .cell_baseline = 5,
527527- .underline_position = 19,
528528- .underline_thickness = 1,
529529- .strikethrough_position = 12,
530530- .strikethrough_thickness = 1,
531531- .overline_position = 0,
532532- .overline_thickness = 1,
533533- .box_thickness = 1,
534534- .cursor_thickness = 1,
535535- .cursor_height = 22,
536536- .icon_height = 21.12,
537537- .icon_height_single = 44.48 / 3.0,
538538- .face_width = 9.6,
539539- .face_height = 21.12,
540540- .face_y = 0.2,
541541- };
542542-543543- // ASCII (no constraint).
544544- {
545545- const constraint: RenderOptions.Constraint = .none;
546546-547547- // BBox of 'x' from JetBrains Mono.
548548- const glyph_x: GlyphSize = .{
549549- .width = 6.784,
550550- .height = 15.28,
551551- .x = 1.408,
552552- .y = 4.84,
553553- };
554554-555555- // Any constraint width: do nothing.
556556- inline for (.{ 1, 2 }) |constraint_width| {
557557- try comparison.expectApproxEqual(
558558- glyph_x,
559559- constraint.constrain(glyph_x, metrics, constraint_width),
560560- );
561561- }
562562- }
563563-564564- // Symbol (same constraint as hardcoded in Renderer.addGlyph).
565565- {
566566- const constraint: RenderOptions.Constraint = .{ .size = .fit };
567567-568568- // BBox of '■' (0x25A0 black square) from Iosevka.
569569- // NOTE: This glyph is designed to span two cells.
570570- const glyph_25A0: GlyphSize = .{
571571- .width = 10.272,
572572- .height = 10.272,
573573- .x = 2.864,
574574- .y = 5.304,
575575- };
576576-577577- // Constraint width 1: scale down and shift to fit a single cell.
578578- try comparison.expectApproxEqual(
579579- GlyphSize{
580580- .width = metrics.face_width,
581581- .height = metrics.face_width,
582582- .x = 0,
583583- .y = 5.64,
584584- },
585585- constraint.constrain(glyph_25A0, metrics, 1),
586586- );
587587-588588- // Constraint width 2: do nothing.
589589- try comparison.expectApproxEqual(
590590- glyph_25A0,
591591- constraint.constrain(glyph_25A0, metrics, 2),
592592- );
593593- }
594594-595595- // Emoji (same constraint as hardcoded in SharedGrid.renderGlyph).
596596- {
597597- const constraint: RenderOptions.Constraint = .{
598598- .size = .cover,
599599- .align_horizontal = .center,
600600- .align_vertical = .center,
601601- .pad_left = 0.025,
602602- .pad_right = 0.025,
603603- };
604604-605605- // BBox of '🥸' (0x1F978) from Apple Color Emoji.
606606- const glyph_1F978: GlyphSize = .{
607607- .width = 20,
608608- .height = 20,
609609- .x = 0.46,
610610- .y = 1,
611611- };
612612-613613- // Constraint width 2: scale to cover two cells with padding, center;
614614- try comparison.expectApproxEqual(
615615- GlyphSize{
616616- .width = 18.72,
617617- .height = 18.72,
618618- .x = 0.44,
619619- .y = 1.4,
620620- },
621621- constraint.constrain(glyph_1F978, metrics, 2),
622622- );
623623- }
624624-625625- // Nerd Font default.
626626- {
627627- const constraint = getConstraint(0xea61).?;
628628-629629- // Verify that this is the constraint we expect.
630630- try std.testing.expectEqual(.fit_cover1, constraint.size);
631631- try std.testing.expectEqual(.icon, constraint.height);
632632- try std.testing.expectEqual(.center1, constraint.align_horizontal);
633633- try std.testing.expectEqual(.center1, constraint.align_vertical);
634634-635635- // BBox of '' (0xEA61 nf-cod-lightbulb) from Symbols Only.
636636- // NOTE: This icon is part of a group, so the
637637- // constraint applies to a larger bounding box.
638638- const glyph_EA61: GlyphSize = .{
639639- .width = 9.015625,
640640- .height = 13.015625,
641641- .x = 3.015625,
642642- .y = 3.76525,
643643- };
644644-645645- // Constraint width 1: scale and shift group to fit a single cell.
646646- try comparison.expectApproxEqual(
647647- GlyphSize{
648648- .width = 7.2125,
649649- .height = 10.4125,
650650- .x = 0.8125,
651651- .y = 5.950695224719102,
652652- },
653653- constraint.constrain(glyph_EA61, metrics, 1),
654654- );
655655-656656- // Constraint width 2: no scaling; left-align and vertically center group.
657657- try comparison.expectApproxEqual(
658658- GlyphSize{
659659- .width = glyph_EA61.width,
660660- .height = glyph_EA61.height,
661661- .x = 1.015625,
662662- .y = 4.7483690308988775,
663663- },
664664- constraint.constrain(glyph_EA61, metrics, 2),
665665- );
666666- }
667667-668668- // Nerd Font stretch.
669669- {
670670- const constraint = getConstraint(0xe0c0).?;
671671-672672- // Verify that this is the constraint we expect.
673673- try std.testing.expectEqual(.stretch, constraint.size);
674674- try std.testing.expectEqual(.cell, constraint.height);
675675- try std.testing.expectEqual(.start, constraint.align_horizontal);
676676- try std.testing.expectEqual(.center1, constraint.align_vertical);
677677-678678- // BBox of ' ' (0xE0C0 nf-ple-flame_thick) from Symbols Only.
679679- const glyph_E0C0: GlyphSize = .{
680680- .width = 16.796875,
681681- .height = 16.46875,
682682- .x = -0.796875,
683683- .y = 1.7109375,
684684- };
685685-686686- // Constraint width 1: stretch and position to exactly cover one cell.
687687- try comparison.expectApproxEqual(
688688- GlyphSize{
689689- .width = @floatFromInt(metrics.cell_width),
690690- .height = @floatFromInt(metrics.cell_height),
691691- .x = 0,
692692- .y = 0,
693693- },
694694- constraint.constrain(glyph_E0C0, metrics, 1),
695695- );
696696-697697- // Constraint width 1: stretch and position to exactly cover two cells.
698698- try comparison.expectApproxEqual(
699699- GlyphSize{
700700- .width = @floatFromInt(2 * metrics.cell_width),
701701- .height = @floatFromInt(metrics.cell_height),
702702- .x = 0,
703703- .y = 0,
704704- },
705705- constraint.constrain(glyph_E0C0, metrics, 2),
706706- );
707707- }
708708-}
···393393 }
394394395395 /// Get a rect that represents the position and size of the loaded glyph.
396396- fn getGlyphSize(glyph: freetype.c.FT_GlyphSlot) font.face.GlyphSize {
396396+ fn getGlyphSize(glyph: freetype.c.FT_GlyphSlot) font.Glyph.Size {
397397 // If we're dealing with an outline glyph then we get the
398398 // outline's bounding box instead of using the built-in
399399 // metrics, since that's more precise and allows better
···427427 alloc: Allocator,
428428 atlas: *font.Atlas,
429429 glyph_index: u32,
430430- opts: font.face.RenderOptions,
430430+ opts: font.Glyph.RenderOptions,
431431 ) !Glyph {
432432 self.ft_mutex.lock();
433433 defer self.ft_mutex.unlock();
···99const Allocator = std.mem.Allocator;
1010const z2d = @import("z2d");
11111212-const face = @import("face.zig");
1212+const Glyph = @import("Glyph.zig");
1313const glyf = @import("opentype/glyf.zig");
14141515-/// Metrics describing the authored glyf coordinate space, since
1616-/// a glyf table doesn't contain this on its own.
1717-pub const DesignMetrics = struct {
1818- /// Units-per-em for outline/design coordinates.
1919- units_per_em: u32,
2020-2121- /// Authored advance width in design units.
2222- advance_width: u32,
2323-2424- /// Authored line height in design units.
2525- line_height: u32,
2626-};
1515+const DesignMetrics = Glyph.DesignMetrics;
27162817/// An owned, tightly packed alpha8 bitmap.
2918pub const Bitmap = struct {
···5241///
5342/// The returned bitmap is always `grid_metrics.cell_width * cell_width` by
5443/// `grid_metrics.cell_height`. `opts.constraint` is applied using the same
5555-/// `face.RenderOptions.Constraint` machinery used by the platform font
4444+/// `RenderOptions.Constraint` machinery used by the platform font
5645/// backends.
5746///
5847/// The caller owns the returned bitmap.
···6049 alloc: Allocator,
6150 outline: glyf.Glyf.Outline,
6251 design: DesignMetrics,
6363- opts: face.RenderOptions,
5252+ opts: Glyph.RenderOptions,
6453) Error!Bitmap {
6554 assert(design.units_per_em > 0);
6655 assert(design.advance_width > 0);
···207196208197 /// Bottom edge of the rasterized outline bounds in bitmap pixels, measured
209198 /// from the bitmap's bottom edge. This matches the cell-relative y axis
210210- /// used by font.face.GlyphSize and is converted to z2d's y-down axis when
199199+ /// used by font.Glyph.Size and is converted to z2d's y-down axis when
211200 /// points are transformed.
212201 y: f64,
213202214203 /// Width of the rasterized outline bounds in bitmap pixels after applying
215215- /// font.face.RenderOptions.Constraint.
204204+ /// font.Glyph.RenderOptions.Constraint.
216205 width: f64,
217206218207 /// Height of the rasterized outline bounds in bitmap pixels after applying
219219- /// font.face.RenderOptions.Constraint.
208208+ /// font.Glyph.RenderOptions.Constraint.
220209 height: f64,
221210222211 /// Full bitmap height in pixels, used to convert cell-relative y-up-ish
···240229 fn init(
241230 bounds: Bounds,
242231 design: DesignMetrics,
243243- opts: face.RenderOptions,
232232+ opts: Glyph.RenderOptions,
244233 ) Placement {
245234 // Start with protocol-like design units mapped so that the em square
246235 // occupies one cell. This makes units_per_em the scale reference and
···253242 // Convert the decoded point bounds into the same pixel coordinate space
254243 // expected by RenderOptions.Constraint. This rectangle is the visible
255244 // outline bounds, not the full advance/line-height layout box.
256256- const glyph: face.GlyphSize = .{
245245+ const glyph: Glyph.Size = .{
257246 .width = bounds.width() * scale,
258247 .height = bounds.height() * scale,
259248 .x = bounds.x_min * scale,
···268257 // Apply the same fit/cover/stretch/alignment/padding rules used by
269258 // normal font rendering. The result is still the outline bounds, but
270259 // placed as if its containing advance/line-height box was constrained.
271271- const constraint: face.RenderOptions.Constraint = constraint: {
260260+ const constraint: Glyph.RenderOptions.Constraint = constraint: {
272261 var constraint = opts.constraint;
273262 if (group_width > 0 and group_height > 0) {
274263 // Tell Constraint that `glyph` is a sub-rectangle of the
···44//! This file provides info extracted from the nerd fonts patcher script,
55//! specifying the scaling/positioning attributes of various glyphs.
6677-const Constraint = @import("face.zig").RenderOptions.Constraint;
77+const Constraint = @import("Glyph.zig").RenderOptions.Constraint;
8899/// Get the constraints for the provided codepoint.
1010pub fn getConstraint(cp: u21) ?Constraint {