diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index 39627c08c..602d0d83b 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -5253,3 +5253,22 @@ fn stacked_rows_that_merely_overlap_do_not_join_into_one_row() { let blocks = text::text_blocks(&doc, 0).unwrap(); assert_eq!(blocks.len(), 2, "two separate rows stay two paragraphs, not one: {blocks:?}"); } + +/// Two equal-size rows with no horizontal overlap must not join into one row either (#3252): +/// the wider tolerance is for a run distinctly smaller or larger than its neighbour (a +/// super/subscript), not for two ordinary body-text rows that happen to sit close enough +/// vertically — here a 5pt baseline shift, inside the widened 50% tolerance but outside the +/// original 20% one, with the second row starting just past where the first ends. +#[test] +fn equal_size_rows_need_the_tighter_tolerance_even_without_overlap() { + let doc = text_page("BT /F1 12 Tf 72 700 Td (Short line one) Tj 75 -5 Td (Second line two here) Tj ET"); + let lines = text::text_lines(&doc, 0).unwrap(); + assert_eq!(lines.len(), 2); + assert_eq!(lines[0].size, lines[1].size, "same size: no super/subscript here"); + let gap = lines[1].rect[0] - lines[0].rect[2]; + assert!(gap >= 0.0, "no horizontal overlap: {gap}"); + let baseline_shift = lines[0].rect[3] - lines[1].rect[3]; + assert!((2.4..6.0).contains(&baseline_shift), "between the old and new vertical tolerance: {baseline_shift}"); + let blocks = text::text_blocks(&doc, 0).unwrap(); + assert_eq!(blocks.len(), 2, "two separate rows stay two paragraphs, not one: {blocks:?}"); +} diff --git a/crates/edit/src/text_regions.rs b/crates/edit/src/text_regions.rs index 0baccc408..66c80fb06 100644 --- a/crates/edit/src/text_regions.rs +++ b/crates/edit/src/text_regions.rs @@ -74,12 +74,15 @@ fn group_layout(lines: &[TextLine], mixed: bool) -> Vec { // A superscript or subscript run (a footnote marker, say) sits on the same visual // row as the text around it, raised or lowered by `Ts` from the body baseline // (#3252). That shift is typically up to half the body size, not the marker's own - // smaller size — but only between runs with no horizontal overlap (`gap >= 0.0`): - // two stacked rows of ordinary body text can land within that wider tolerance too - // when horizontal compression (`Tz`) narrows the first row enough to overlap the - // second's start, and the tighter 20% tolerance is what keeps those apart. + // smaller size — but the wider tolerance only applies between a run and a + // distinctly smaller or larger one, with no horizontal overlap (`gap >= 0.0`): + // two equal-size runs can land within it too, at a baseline shift tight columns + // or compressed text (`Tz`) can produce between genuinely separate rows, and the + // original 20% tolerance is what keeps those apart. && ((prev.frame_origin().1 - line.frame_origin().1).abs() < prev.size.min(line.size) * 0.2 - || (gap >= 0.0 && (prev.frame_origin().1 - line.frame_origin().1).abs() < prev.size.max(line.size) * 0.5)) + || (gap >= 0.0 + && prev.size.min(line.size) < prev.size.max(line.size) * 0.9 + && (prev.frame_origin().1 - line.frame_origin().1).abs() < prev.size.max(line.size) * 0.5)) && gap >= -prev.size.min(line.size) * 0.2 && gap < prev.size.max(line.size) * 1.5 // Separate same-style text objects that merely touch or overlap, with no space