Files
wordcraft/crates/layout/src/tests/float_top.rs
T
3291be0fbf Layout: an empty paragraph after a leading floating table takes no space before (#5440)
Word drops the space before of an empty paragraph that follows a document's
leading floating table: its PDFs of docxide-pdf's leading-empty-before-float-0
and -12 put the text after the empty anchor (beside the table) at the same
place for 0 pt and 12 pt before.

Flow::gap_before (crates/layout/src/flow.rs) kept it: a floating table
takes no room in the flow and clears `pb.prev`, so the paragraph after it
was laid out like a document's first paragraph, with its full space before.
It now drops it for body block 1 when block 0 was placed floating on this
page (the page's float_tables), the page is fresh and the paragraph holds
nothing but markers (bookmarks, comment and permission ranges). It
doesn't check that the paragraph sits beside the table, so below a
full-width leading float an empty paragraph loses its space before too;
that case is untested. Later pages and columns, and paragraphs with text,
keep their space before.

Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
2026-11-08 05:45:17 +01:00

54 lines
2.4 KiB
Rust

//! An empty paragraph after a document's leading floating table takes no space before. Word's PDFs
//! of docxide-pdf's `leading-empty-before-float-0` and `-12` put the text after the empty anchor at
//! the same place for 0 pt and 12 pt before.
use super::lay;
use crate::{Arc, Placed, StoryRef};
use wordcraft_doc::para::{Anchor, InlineObject};
use wordcraft_doc::props::TableFloat;
use wordcraft_doc::{Block, Document, Paragraph, Table, para_block};
/// Where the lines of body block `block` start on page one.
fn line_y(d: &Document, block: u32) -> f32 {
lay(d).pages[0]
.items
.iter()
.find_map(|i| match i {
Placed::Lines { story: StoryRef::Body, path, y, .. } if path.0 == [block] => Some(*y),
_ => None,
})
.unwrap()
}
/// `table` (a floating one) or nothing, then a paragraph of `text` with `before`, then "After".
fn document(table: bool, text: &str, before: f32) -> Document {
let mut p = Paragraph::with_text(text, Default::default());
// "#" stands for a bookmark, a zero-width marker.
if text == "#" {
p = Paragraph::new();
p.insert_object(0, InlineObject::BookmarkStart { name: "_GoBack".into() }, &Default::default()).unwrap();
}
p.props.space_before = Some(before);
let mut d = Document::new();
d.body = vec![para_block(p), para_block(Paragraph::with_text("After", Default::default()))];
if table {
let mut t = Table::new(1, 1, 200.0);
t.rows[0].cells[0].blocks = vec![para_block(Paragraph::with_text("Table", Default::default()))];
t.props.float = Some(TableFloat { h_rel: Anchor::Margin, v_rel: Anchor::Paragraph, ..Default::default() });
d.body.insert(0, Arc::new(Block::Table(t)));
}
d
}
#[test]
fn an_empty_paragraph_after_a_floating_table_at_the_page_top_takes_no_space_before() {
let empty = line_y(&document(true, "", 12.0), 2);
let plain = line_y(&document(false, "", 0.0), 1);
assert!((empty - plain).abs() < 0.01, "after the empty anchor at {empty}, with no table nor space before at {plain}");
// A bookmark alone doesn't make it a paragraph with text.
assert!((line_y(&document(true, "#", 12.0), 2) - plain).abs() < 0.01);
// One with text keeps it, and so does an empty first paragraph with no table before it.
assert!(line_y(&document(true, "Text", 12.0), 2) > empty + 11.99);
assert!(line_y(&document(false, "", 12.0), 1) > plain + 11.99);
}