diff --git a/crates/text/src/layout/flow.rs b/crates/text/src/layout/flow.rs index 83aacda3c..652213940 100644 --- a/crates/text/src/layout/flow.rs +++ b/crates/text/src/layout/flow.rs @@ -607,6 +607,17 @@ fn is_cjk_gap(glyph: usize, held: &[usize], after: &[Option], index: usiz }) } +// May a pushed-in line close a CJK gap after `g`? Not after a list marker or an inline graphic, +// which the push-in's room does not count either. +fn may_squeeze(g: &SGlyph) -> bool { + g.len > 0 && g.inline.is_none() +} + +// How much a pushed-in line's `squeeze` adds to the CJK gap after `g`. +fn squeeze_after(g: &SGlyph, squeeze: f64, vertical: bool) -> f64 { + if may_squeeze(g) { squeeze * squeeze_em(g, vertical) } else { 0.0 } +} + // What justification may change of glyph `j`: nothing of a `held` glyph, and no glyph // scaling of a `hung` one. fn flow_line_flex(glyph: usize, flex: &[Flex], held: &[usize], hung: &[usize]) -> Flex { @@ -672,8 +683,10 @@ fn justify_flow_line(cx: &Ctx<'_>, paragraph: &ParagraphData<'_, '_>, line: &Lin // the same share of the em of the glyph before it, at most [`PUSH_IN_SQUEEZE`]. let pushed_in = line.position.pushed_in && paragraph.regions.is_some() && cjk_gaps > 0 && width > room + EPS; if pushed_in { - let ems: f64 = - (line.index..line.body).filter(|&j| is_cjk_gap(j, held, &after, line.index, sg)).map(|j| squeeze_em(&sg[j], cx.vertical)).sum(); + let ems: f64 = (line.index..line.body) + .filter(|&j| is_cjk_gap(j, held, &after, line.index, sg) && may_squeeze(&sg[j])) + .map(|j| squeeze_em(&sg[j], cx.vertical)) + .sum(); if ems > EPS { squeeze = ((room - width) / ems).max(-PUSH_IN_SQUEEZE); } @@ -778,7 +791,7 @@ fn emit_flow_line(cx: &mut Ctx<'_>, pen: &mut Pen<'_>, paragraph: &ParagraphData advance += justification.per_space; } else if justification.per_cjk != 0.0 || justification.squeeze != 0.0 { if is_cjk_gap(glyph_index, &line.held, &justification.after, index, sg) { - advance += justification.per_cjk + justification.squeeze * squeeze_em(glyph, cx.vertical); + advance += justification.per_cjk + squeeze_after(glyph, justification.squeeze, cx.vertical); } } else if !line.held.contains(&glyph_index) && drawn_after(index, glyph_index, &justification.after).and_then(|next| sg.get(next)).is_some_and(|next| !next.continues_tcy()) diff --git a/crates/text/src/tests/inline.rs b/crates/text/src/tests/inline.rs index d492608cc..76c90e65b 100644 --- a/crates/text/src/tests/inline.rs +++ b/crates/text/src/tests/inline.rs @@ -347,6 +347,25 @@ fn top_to_top_leading_spaces_inline_art_by_its_run() { assert!((small0 - tall0).abs() < 1e-6 && (small1 - tall1).abs() < 1e-6, "{small0} {small1} / {tall0} {tall1}"); } +/// A pushed-in line closes the gaps next to its CJK characters and leaves the gap after inline art +/// open. Needs a font with full-width Japanese (skipped without one). +#[test] +fn a_pushed_in_line_leaves_the_gap_after_inline_art() { + let mut t = TextObject::point(Point::ZERO, "一二", style(20.0)); + t.xf = Affine::IDENTITY; + t.runs.push(TextRun::inline(art(0.5), style(20.0))); + t.runs.push(TextRun::new("四五六七八九十一二三四五」六", style(20.0))); + t.para.kinsoku_type = vectorcraft_doc::KinsokuShoriType::PushInFirst; + t.kind = TextKind::Area { frame: PathData::from_bezpath(&Rect::new(0.0, 0.0, 300.0, 200.0).to_path(0.1)) }; + let l = layout(FontDb::global(), &t); + if (l.glyphs[0].advance - 20.0).abs() > 2.0 { + return; // no font with full-width Japanese here + } + assert!(l.glyphs[1].advance < 20.0 - 0.5, "the other gaps close up: {}", l.glyphs[1].advance); + let art = inline_glyph(&l); + assert!((art.advance - 20.0).abs() < 1e-6, "the art keeps its advance: {}", art.advance); +} + #[test] fn inline_art_moves_with_vertically_aligned_lines() { use vectorcraft_doc::text::{AreaOptions, VerticalAlign};