mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-26 03:17:28 -06:00
cu20we: anchor box floats above paragraph spacing
Use the paragraph origin before effective space-before when placing floats in headers, footers, table cells and text boxes. Keep text and wrap exclusions relative to the text top, matching the existing body layout convention. Cover all four box contexts with synthetic layout regressions and update the gap, parity and progress records. Native Word comparison remains pending. Co-Authored-By: GPT-6 <codex@openai.com>
This commit is contained in:
+4
-1
File diff suppressed because one or more lines are too long
@@ -1274,6 +1274,9 @@ fn layout_box(
|
||||
} else if i > 0 && before > 0.0 && prev_after > 0.0 && !ctx.doc.settings.sum_paragraph_spacing {
|
||||
before = (before - prev_after).max(0.0);
|
||||
}
|
||||
// Paragraph-relative floats start above the effective space before, as in the
|
||||
// body path. Keep text and wrap exclusions relative to the text's actual top.
|
||||
let para_top = y;
|
||||
y += if i == 0 { before } else { before.max(0.0) };
|
||||
// Floating objects anchored here: place them, then wrap the text around them.
|
||||
// One left out of the layout (hidden, or deleted in the final text) takes no room.
|
||||
@@ -1288,10 +1291,10 @@ fn layout_box(
|
||||
}
|
||||
if let Some(pin) = ctx.pin.as_ref().filter(|pin| in_cell && pin.story == story && float.outside_cell && page_anchored(float)) {
|
||||
let page = PageFrame { sect: &pin.sect, origin: (0.0, 0.0), number: pin.number, mirror: pin.mirror };
|
||||
pinned.insert(oi, float_rect(Some(page), (0.0, width), y, w, h, float));
|
||||
pinned.insert(oi, float_rect(Some(page), (0.0, width), para_top, w, h, float));
|
||||
continue;
|
||||
}
|
||||
let r = float_rect(frame, (0.0, width), y, w, h, float);
|
||||
let r = float_rect(frame, (0.0, width), para_top, w, h, float);
|
||||
let outline = ctx.wrap_outline(o);
|
||||
excl.extend(wrap_area(r, float, outline.as_deref().map(|v| v.as_slice())));
|
||||
floats.insert(oi, r);
|
||||
@@ -1317,7 +1320,7 @@ fn layout_box(
|
||||
prev_end = push_para(&mut items, story, &path, &pl, 0, pl.lines.len(), 0.0, y + lead, width, edges);
|
||||
// Pictures, shapes and text boxes: drawn, their areas and text boxes' text.
|
||||
if !p.objects.is_empty() {
|
||||
let at = ObjFrame { page: frame, col: (0.0, width), para_y: y, table_chr: table.map(|t| &t.chr), pinned: Some(&pinned) };
|
||||
let at = ObjFrame { page: frame, col: (0.0, width), para_y: para_top, table_chr: table.map(|t| &t.chr), pinned: Some(&pinned) };
|
||||
let (back, front) = place_objects(ctx, story, &path, p, &pl, (0, pl.lines.len()), (0.0, y + lead), &at, &floats, depth);
|
||||
for placed in back.iter().chain(front.iter()) {
|
||||
if let Placed::Object { rect, spin, wrap, .. } = placed
|
||||
|
||||
@@ -5009,6 +5009,63 @@ fn floats_in_headers_and_table_cells_are_drawn() {
|
||||
assert!(c[0].x > 72.0 && c[0].x < 72.0 + 234.0, "inside the first cell: {:?}", c[0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraph_relative_box_floats_start_above_space_before() {
|
||||
use wordcraft_doc::{
|
||||
PartKind,
|
||||
para::{Anchor, Float, Wrap},
|
||||
};
|
||||
for context in ["header", "footer", "cell", "textbox"] {
|
||||
let gap = |before| {
|
||||
let mut d = Document::from_text("Body");
|
||||
let mut p = wordcraft_doc::Paragraph::with_text("Anchor", Default::default());
|
||||
p.props.space_before = Some(before);
|
||||
let float = Float { wrap: Wrap::BehindText, v_rel: Anchor::Paragraph, y: 7.0, ..Default::default() };
|
||||
p.insert_object(0, rect_shape(37.0, 9.0, float), &Default::default()).unwrap();
|
||||
let story = match context {
|
||||
"header" | "footer" => {
|
||||
let kind = if context == "header" { PartKind::Header } else { PartKind::Footer };
|
||||
let id = d.add_part(kind, vec![wordcraft_doc::para_block(p)]);
|
||||
if context == "header" {
|
||||
d.last_section.headers.default = Some(id);
|
||||
} else {
|
||||
d.last_section.footers.default = Some(id);
|
||||
}
|
||||
StoryRef::Part(id)
|
||||
}
|
||||
"cell" => {
|
||||
let mut t = Table::new(1, 1, 468.0);
|
||||
t.rows[0].cells[0].blocks = vec![wordcraft_doc::para_block(p)];
|
||||
d.insert_block(StoryRef::Body, &Path::top(1), wordcraft_doc::Block::Table(t)).unwrap();
|
||||
StoryRef::Body
|
||||
}
|
||||
_ => {
|
||||
let id = text_box(&mut d, 0, "", 200.0, 100.0, page_float(100.0, 200.0));
|
||||
d.parts.get_mut(&id).unwrap().blocks = vec![wordcraft_doc::para_block(p)];
|
||||
StoryRef::Part(id)
|
||||
}
|
||||
};
|
||||
let l = lay(&d);
|
||||
let page = &l.pages[0];
|
||||
let items = match context {
|
||||
"header" => &page.header,
|
||||
"footer" => &page.footer,
|
||||
_ => &page.items,
|
||||
};
|
||||
let y = items
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Placed::Lines { story: s, y, path, .. } if *s == story && (context != "cell" || path.0.len() > 1) => Some(*y),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
let r = shapes(items).into_iter().find(|r| (r.w - 37.0).abs() < 0.01).unwrap();
|
||||
r.y - y
|
||||
};
|
||||
assert!((gap(24.0) - gap(0.0) + 24.0).abs() < 0.01, "{context}: float must stay above the space before");
|
||||
}
|
||||
}
|
||||
|
||||
/// Inside a text box whose shape style gives its text a colour (`a:fontRef`, typically white on
|
||||
/// the accent fill), automatic text takes that colour instead of black; text with a colour of
|
||||
/// its own keeps it (Word, observed with synthetic documents).
|
||||
|
||||
+4
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user