Text: the paragraph mark's size counts in its line's auto leading

The paragraph mark is a character of its paragraph's last line, so its leading (auto: its size times the auto leading percentage) takes part in the line's largest-leading rule. It doesn't change the line's ascent or descent. Earlier lines of the paragraph are unaffected.
This commit is contained in:
aiongg
2026-10-10 15:30:32 +00:00
parent 92649ffdb0
commit d03dd226c5
2 changed files with 35 additions and 0 deletions
+16
View File
@@ -768,6 +768,18 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
designcraft_doc::Leading::Auto => base_size * pp.auto_leading,
designcraft_doc::Leading::Points(v) => v,
};
// The paragraph mark is a character of the last line: its leading counts there like any
// other character's. It doesn't set the line's ascent or descent. A hidden or deleted
// mark takes no room.
let mark_leading = story.text.get(prange.end..).filter(|t| t.starts_with('\n')).and_then(|_| {
let props = doc.styles.resolve_char(&base_chars, story.format_after(prange.end));
let hidden = props.change == designcraft_doc::ChangeMark::Deleted
|| (!props.conditions.is_empty() && props.conditions.iter().all(|c| sub.hidden_conditions.contains(c)));
(!hidden).then_some(match props.leading {
designcraft_doc::Leading::Auto => props.size * pp.auto_leading,
designcraft_doc::Leading::Points(v) => v,
})
});
let spacing = spacing_for(&pp, base_size);
// A paragraph set across the columns moves on to the next frame where others move on to
// the next column.
@@ -914,6 +926,10 @@ fn compose_with_db(doc: &Document, story: &Story, frames: &[FrameSpec], opts: &C
let (s, e) = (g0 + b.start, g0 + b.end);
let line_glyphs = &glyphs[s..e.max(s)];
let (asc, desc, lead) = line_metrics(line_glyphs, &glyphs, s, base_leading, base_chars.size, db, &base_chars);
let lead = match mark_leading {
Some(m) if k + 1 == breaks.len() => lead.max(m),
_ => lead,
};
let reference = cjk_line_reference(line_glyphs);
let mut baseline = cur.next_baseline(f, col, lead, asc, &pp);
if cur.last_baseline.is_some() {
+19
View File
@@ -2771,3 +2771,22 @@ fn empty_paragraphs_get_no_bullet_or_number() {
assert_eq!(list_labels(&cs, LABEL_CHARS), ["1.", "", "2.", ""], "list `{name}`");
}
}
#[test]
fn paragraph_mark_size_counts_in_its_line_auto_leading() {
let gaps = |mark_size: f64| {
let (mut d, sid, _) = doc_with("One\nTwo\nThree", Rect::new(36.0, 36.0, 300.0, 300.0), ParaAttrs::default());
d.story_mut(sid).unwrap().format_chars(7..8, |f| f.over.size = Some(mark_size));
let cs = compose_story(&d, sid, &ComposeOptions::default());
let b: Vec<f64> = all_lines(&cs).iter().map(|l| l.baseline).collect();
assert_eq!(b.len(), 3);
(b[1] - b[0], b[2] - b[1])
};
// A 12 pt line ending in a 14 pt return: 14 × 120% above it, the next line unchanged.
let (above, below) = gaps(14.0);
assert!((above - 16.8).abs() < 1e-6, "{above}");
assert!((below - 14.4).abs() < 1e-6, "{below}");
let (above, below) = gaps(12.0);
assert!((above - 14.4).abs() < 1e-6, "{above}");
assert!((below - 14.4).abs() < 1e-6, "{below}");
}