Text: a fallback face (emoji, symbols, a missing script) no longer moves the line's baseline
Line height and baseline were the largest over every face on the line, fallback faces included, so a line with an emoji, a math symbol or Thai in an Aptos run sat 1-5 px lower than PowerPoint's and pushed the lines below it down. PowerPoint keeps the run's own font metrics (measured: the second line of six boxes starts at the same height whatever the first line holds). Faces that are the run's Latin, East Asian, complex-script or symbol font still count. w14-xml s8 (baselines) 5.54 -> 3.21, w09-scripts s1 10.27 -> 8.39, s2 9.37 -> 7.46, w15-mixed s8 13.65 -> 8.77; w15-mixed s2 (Arabic) 6.57 -> 7.05. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
232501a9f3
commit
85a7f0ce3e
|
|
@ -1684,7 +1684,11 @@ fn layout_scaled(ctx: &Ctx, shape: &Shape, body: &TextBody, bp: &BodyProps, opts
|
|||
for c in p.cells.get(rl.start..rl.end.max(rl.start)).unwrap_or(&[]) {
|
||||
if let (Some(st), Some(face)) = (p.styles.get(c.style), p.faces.get(c.face)) {
|
||||
let sz = st.size;
|
||||
let (a, d) = line_metrics(face, sz);
|
||||
// The run's own fonts set the line; a fallback face (emoji, a symbol or a
|
||||
// script the run's fonts lack) does not move the baseline in PowerPoint.
|
||||
let own = Arc::ptr_eq(face, &st.face)
|
||||
|| [st.ea.as_deref(), st.cs.as_deref(), st.sym.as_deref()].into_iter().flatten().any(|f| face.family.eq_ignore_ascii_case(f));
|
||||
let (a, d) = line_metrics(if own { face } else { &st.face }, sz);
|
||||
asc = asc.max(a);
|
||||
desc = desc.max(d);
|
||||
size = size.max(sz);
|
||||
|
|
|
|||
|
|
@ -1134,3 +1134,19 @@ fn symbols_after_an_emoji_keep_a_text_face() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_faces_do_not_move_the_baseline() {
|
||||
// PowerPoint keeps a line's baseline and pitch from the run's own font when an emoji, a symbol
|
||||
// or a script the font lacks is drawn from another face.
|
||||
let base = |text: &str| {
|
||||
let (p, sh) = bidi_setup(vec![Run::new(text)], 2000.0, false, Align::Left);
|
||||
let l = lay(&p, &sh, 2000.0, 300.0);
|
||||
(l.lines[0].baseline, l.lines[0].bottom)
|
||||
};
|
||||
let plain = base("Hxg Hxg");
|
||||
for text in ["Hxg \u{1F600} Hxg", "Hxg \u{0E2A}\u{0E27}\u{0E31}\u{0E2A}", "Hxg \u{222B}\u{2211}"] {
|
||||
let b = base(text);
|
||||
assert!((b.0 - plain.0).abs() < 0.01 && (b.1 - plain.1).abs() < 0.01, "{text:?}: {b:?} vs {plain:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue