mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-26 03:10:21 +00:00
fix(edit): keep the overlap guard for the wider footnote-marker tolerance
CodeRabbit review on #3471: the widened baseline tolerance (half the larger run's size, for a same-row footnote marker) could also let two genuinely stacked rows of ordinary body text join into one row, if horizontal compression (Tz) narrows the first row enough to put the second row's start within it — a negative `gap` inside the existing overlap allowance, with a baseline shift inside the new 50% band but outside the original 20% one. Only take the wider tolerance when the two runs don't overlap horizontally (`gap >= 0.0`), which is true for a footnote marker drawn right after the text it follows. Overlapping runs keep the original, tighter 20% tolerance. Added stacked_rows_that_merely_overlap_do_not_join_into_one_row, confirmed it fails without this change (merges into one row/block) and passes with it. cargo test -p pdfcraft-edit (197 passed), fmt, clippy -D warnings, xtask layers and xtask assets all clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5235,3 +5235,21 @@ fn a_clipping_copy_on_the_same_baseline_is_refused() {
|
||||
assert!(matches!(&block, Err(EditError::Invalid(m)) if m.contains("clipping path")), "{block:?}");
|
||||
assert_eq!(text::text_lines(&doc, 0).unwrap().len(), 2, "refused means untouched");
|
||||
}
|
||||
|
||||
/// Two genuinely stacked rows of ordinary body text must not join into one row just because the
|
||||
/// first is narrow enough to put the second's start within the #3252 tolerance band: here the
|
||||
/// second row starts 1.4pt to the left of where the first ends (so the two overlap a little,
|
||||
/// as compressed text or a tight column can) and 5pt below its baseline — inside the widened 50%
|
||||
/// tolerance but outside the original 20% one. They stay two rows (and two paragraphs).
|
||||
#[test]
|
||||
fn stacked_rows_that_merely_overlap_do_not_join_into_one_row() {
|
||||
let doc = text_page("BT /F1 12 Tf 72 700 Td (A compressed line of body text here) Tj 190 -5 Td (Second stacked line of body text) Tj ET");
|
||||
let lines = text::text_lines(&doc, 0).unwrap();
|
||||
assert_eq!(lines.len(), 2);
|
||||
let gap = lines[1].rect[0] - lines[0].rect[2];
|
||||
assert!((-2.4..0.0).contains(&gap), "the rows overlap a little, within the old tolerance band: {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:?}");
|
||||
}
|
||||
|
||||
@@ -74,8 +74,12 @@ fn group_layout(lines: &[TextLine], mixed: bool) -> Vec<TextBlock> {
|
||||
// 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, so the tolerance is scaled by the larger of the two.
|
||||
&& (prev.frame_origin().1 - line.frame_origin().1).abs() < prev.size.max(line.size) * 0.5
|
||||
// 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.
|
||||
&& ((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 >= -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
|
||||
|
||||
Reference in New Issue
Block a user