1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 22:53:14 -04:00

Fix NOTDEF fallback using wrong font face and reset cluster tracking between runs

Two bugs in layout_shaped_run:

1. When the shaper returned NOTDEF, glyph_info was resolved via font
   fallback but the returned FontFaceKey was discarded. The glyph was
   then allocated with run.font_key (the face that couldn't render it),
   causing the glyph to silently render as invisible. Now uses the
   correct fallback font face and its metrics for both allocation and
   Glyph font_face_height/font_face_ascent.

2. prev_cluster was not reset between runs. Since harfrust cluster
   values are byte offsets within each run's text, comparing clusters
   across runs is semantically wrong and could skip extra_letter_spacing
   at run boundaries (e.g. when both runs start at cluster 0).
This commit is contained in:
gcailly
2026-03-26 12:27:11 +01:00
parent fc9ac39a6b
commit 96b370e0aa

View File

@@ -162,6 +162,7 @@ pub fn layout(fonts: &mut FontsImpl, pixels_per_point: f32, job: Arc<LayoutJob>)
/// Shared context for emitting shaped glyphs into a [`Paragraph`].
struct ShapingContext {
pixels_per_point: f32,
font_size: f32,
line_height: f32,
extra_letter_spacing: f32,
section_index: u32,
@@ -182,6 +183,10 @@ fn layout_shaped_run(
) {
let px_scale = face_metrics.px_scale_factor;
// Reset cluster tracking — cluster values are byte offsets within run_text,
// so they are not comparable across runs.
ctx.prev_cluster = None;
for (info, pos) in glyph_buffer
.glyph_infos()
.iter()
@@ -217,10 +222,16 @@ fn layout_shaped_run(
continue;
}
let (_, glyph_info) = font.glyph_info(chr);
// Use the fallback font face (not run.font_key which returned NOTDEF).
let (fallback_key, glyph_info) = font.glyph_info(chr);
let fallback_metrics = font
.fonts_by_id
.get(&fallback_key)
.map(|ff| ff.styled_metrics(ctx.pixels_per_point, ctx.font_size, &Default::default()))
.unwrap_or_default();
let (glyph_alloc, physical_x) =
if let Some(ff) = font.fonts_by_id.get_mut(&run.font_key) {
ff.allocate_glyph(font.atlas, face_metrics, glyph_info, chr, paragraph.cursor_x_px)
if let Some(ff) = font.fonts_by_id.get_mut(&fallback_key) {
ff.allocate_glyph(font.atlas, &fallback_metrics, glyph_info, chr, paragraph.cursor_x_px)
} else {
Default::default()
};
@@ -230,8 +241,8 @@ fn layout_shaped_run(
pos: pos2(physical_x as f32 / ctx.pixels_per_point, f32::NAN),
advance_width: glyph_alloc.advance_width_px / ctx.pixels_per_point,
line_height: ctx.line_height,
font_face_height: face_metrics.row_height,
font_face_ascent: face_metrics.ascent,
font_face_height: fallback_metrics.row_height,
font_face_ascent: fallback_metrics.ascent,
font_height: ctx.font_metrics.row_height,
font_ascent: ctx.font_metrics.ascent,
uv_rect: glyph_alloc.uv_rect,
@@ -305,6 +316,7 @@ fn layout_section(
let section_text = &job.text[byte_range.clone()];
let mut ctx = ShapingContext {
pixels_per_point,
font_size,
line_height,
extra_letter_spacing,
section_index,