Keep inter-word spaces in stroke-font MTEXT (#628) (#644)

Squash of PR #644 by @MarekSpodBiedry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MarekSpodBiedry 2026-10-12 01:52:51 +02:00 committed by GitHub
parent 83b8a84050
commit c5ebf4d1cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 19 additions and 2 deletions

View File

@ -213,6 +213,17 @@ pub fn line_width(s: &str, height: f64, width_factor: f64) -> f64 {
(n - stroke::GAP / stroke::CAP * height * width_factor).max(0.0)
}
/// Width of the gap MTEXT leaves between two words. Words are measured without their trailing
/// letter gap, so the stroke font's gap is added on both sides of the space, as TEXT lays it out.
pub fn space_width(font: &TextFont, height: f64, width_factor: f64) -> f64 {
if let TextFont::Outline(face) = font
&& let Some(w) = ttf::width(face, " ", height, width_factor)
{
return w;
}
stroke_advance(' ', height, width_factor) + stroke::GAP / stroke::CAP * height * width_factor
}
/// The font a piece of text is set in: the built-in stroke font or an installed TrueType /
/// OpenType face (outlines). Either way, characters the font lacks come from a fallback font.
#[derive(Clone, Debug, Default)]

View File

@ -571,7 +571,7 @@ pub fn layout_mtext_with(contents: &str, p: &MTextParams) -> MTextLayout {
}
Atom::Word(segs) => {
let ww: f64 = segs.iter().map(|(sg, f)| seg_width(sg, fmt(*f))).sum();
let sw = pending_space.map(|f| text_width(&fmt(f).font, " ", fmt(f).height, fmt(f).wf)).unwrap_or(0.0);
let sw = pending_space.map(|f| crate::space_width(&fmt(f).font, fmt(f).height, fmt(f).wf)).unwrap_or(0.0);
if width > 0.0 && !line.items.is_empty() && line.width + sw + ww > width + 1e-9 {
finish(&mut lines, &mut line, fmt(last_fmt), para);
} else if !line.items.is_empty() || pending_space.is_some() {
@ -693,6 +693,12 @@ mod tests {
assert!((c.x - 5.0).abs() < 1e-9 && (c.y - 5.0).abs() < 1e-9);
}
#[test]
fn stroke_word_space_matches_text() {
let l = layout_mtext("A B", Vec2::ZERO, 1.0, 0.0, 1, 0.0, 1.0);
assert!((l.bounds.width() - crate::line_width("A B", 1.0, 1.0)).abs() < 1e-9);
}
fn lay(s: &str, width: f64) -> MTextLayout {
layout_mtext(s, Vec2::ZERO, 1.0, width, 1, 0.0, 1.0)
}

View File

@ -426,7 +426,7 @@ pub(super) fn layout(contents: &str, p: &MTextParams, base: &Fmt) -> MTextLayout
let paras = Paragraphs::scan(contents);
let (atoms, fmts) = parse(contents, base.clone());
let fmt = |i: usize| fmts.get(i).unwrap_or(base);
let space_w = |f: usize| crate::text_width(&fmt(f).font, " ", fmt(f).height, fmt(f).wf);
let space_w = |f: usize| crate::space_width(&fmt(f).font, fmt(f).height, fmt(f).wf);
let mut lines: Vec<PLine> = Vec::new();
let mut k = 0usize;