1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Add font variations API (#7859)

<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md)
before opening a Pull Request!

* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.

Please be patient! I will review your PR, but my time is limited!
-->

* Closes N/A
* [x] I have followed the instructions in the PR template

This was mostly from last month, but I never got around to submitting
it.

This PR adds font variation coordinates to the `TextFormat` struct, and
uses them when rendering text. The coordinates are stored in a
`SmallVec`; I've chosen to store up to 2 inline, which makes it take up
24 bytes (the minimum possible for a `SmallVec`). The variation axis
tags are stored as the `font_types::Tag` type, which I've chosen to
re-export from `epaint::text`.

The variation coordinates are resolved to a `skrifa::Location` during
font rendering/scaling, and are cached in the same way as all the other
scaled metrics. I've renamed the `ScaledMetrics` struct to
`StyledMetrics`, since it now also contains the resolved variation
coordinates. I haven't benchmarked the performance of text layout with
variation coordinates, but the existing text layout performance is
unchanged.

I've replaced the API for manually overriding a font's weight
(https://github.com/emilk/egui/pull/7790) with an API for manually
overriding any variation coordinates via `FontTweak`. This should
support the same use case as #7790 while being substantially more
flexible.

I have *not* yet added any higher-level API for mapping style attributes
(weight, width, slant, etc) to variation coordinates or to different
font faces within a single family. That's a pretty huge can of worms,
and it'd involve rethinking the split between `FontId` and `TextFormat`
(and whether `FontId` is so big that we should provide a way to reuse
it). This API is intentionally pretty low-level for now.

Likewise, I've intentionally not used variation coordinates when
computing a font's row height. I can't think of any fonts that change
their vertical metrics depending on variation axes, so this should be
fine for now.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
valadaptive
2026-03-03 16:58:42 -05:00
committed by GitHub
parent ee3e73bdf9
commit 699fc7e887
11 changed files with 258 additions and 110 deletions

View File

@@ -8,7 +8,7 @@ use crate::{
Color32, Mesh, Stroke, Vertex,
stroke::PathStroke,
text::{
font::{ScaledMetrics, is_cjk, is_cjk_break_allowed},
font::{StyledMetrics, is_cjk, is_cjk_break_allowed},
fonts::FontFaceKey,
},
};
@@ -160,7 +160,7 @@ fn layout_section(
} = section;
let mut font = fonts.font(&format.font_id.family);
let font_size = format.font_id.size;
let font_metrics = font.scaled_metrics(pixels_per_point, font_size);
let font_metrics = font.styled_metrics(pixels_per_point, font_size, &format.coords);
let line_height = section
.format
.line_height
@@ -178,7 +178,7 @@ fn layout_section(
// Optimization: only recompute `ScaledMetrics` when the concrete `FontImpl` changes.
let mut current_font = FontFaceKey::INVALID;
let mut current_font_face_metrics = ScaledMetrics::default();
let mut current_font_face_metrics = StyledMetrics::default();
for chr in job.text[byte_range.clone()].chars() {
if job.break_on_newline && chr == '\n' {
@@ -192,7 +192,9 @@ fn layout_section(
current_font = font_id;
current_font_face_metrics = font_face
.as_ref()
.map(|font_face| font_face.scaled_metrics(pixels_per_point, font_size))
.map(|font_face| {
font_face.styled_metrics(pixels_per_point, font_size, &format.coords)
})
.unwrap_or_default();
}
@@ -468,7 +470,7 @@ fn replace_last_glyph_with_overflow_character(
let mut font_face = font.fonts_by_id.get_mut(&font_id);
let font_face_metrics = font_face
.as_mut()
.map(|f| f.scaled_metrics(pixels_per_point, font_size))
.map(|f| f.styled_metrics(pixels_per_point, font_size, &section.format.coords))
.unwrap_or_default();
let overflow_glyph_x = if let Some(prev_glyph) = row.glyphs.last() {
@@ -519,7 +521,8 @@ fn replace_last_glyph_with_overflow_character(
})
.unwrap_or_default();
let font_metrics = font.scaled_metrics(pixels_per_point, font_size);
let font_metrics =
font.styled_metrics(pixels_per_point, font_size, &section.format.coords);
let line_height = section
.format
.line_height
@@ -1212,7 +1215,7 @@ mod tests {
let font_id = FontId::default();
let font_height = fonts
.font(&font_id.family)
.scaled_metrics(pixels_per_point, font_id.size)
.styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default())
.row_height;
let job = LayoutJob::simple(String::new(), font_id, Color32::WHITE, f32::INFINITY);
@@ -1245,7 +1248,7 @@ mod tests {
let font_id = FontId::default();
let font_height = fonts
.font(&font_id.family)
.scaled_metrics(pixels_per_point, font_id.size)
.styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default())
.row_height;
let job = LayoutJob::simple("Hi!\n".to_owned(), font_id, Color32::WHITE, f32::INFINITY);