M12: a flowed subform's repeated top margins give way to a child that fits a fresh content area only without them

This commit is contained in:
Eric Kaden
2026-10-23 22:15:07 -04:00
parent a7d86dea76
commit cc6b042f0d
2 changed files with 20 additions and 4 deletions
+10 -4
View File
@@ -649,15 +649,17 @@ impl Layouter<'_> {
if self.frames.is_empty() || !placed || y + h <= rect.bottom() + 0.01 || h > rect.h + 0.01 {
return Ok(());
}
self.break_frames()
self.break_frames(h)
}
/// Break the flowed subforms being laid out ([`Self::frames`]) across content areas, as
/// another viewer breaks such a section on a synthetic form: each one's part here ends after
/// its content and its bottom margin (the innermost first, each inside the next), the flow
/// moves to the next content area, and each starts a new part there after its top margin
/// (the outermost first).
fn break_frames(&mut self) -> Result<(), XfaError> {
/// (the outermost first). The top margins take only what the `need` points of the child that
/// comes next leave of the new area: a child that fits a fresh area but not after the margins
/// would otherwise run past its bottom, out of its border.
fn break_frames(&mut self, need: f64) -> Result<(), XfaError> {
let (rect, mut y, _) = self.cursor();
let page = self.pages.len().saturating_sub(1);
let here: Vec<bool> = self.frames.iter().map(|f| self.on_area(&f.part)).collect();
@@ -671,11 +673,15 @@ impl Layouter<'_> {
}
}
self.next_area()?;
let (rect, y, _) = self.cursor();
let mut room = (rect.bottom() - y - need).max(0.0);
for i in 0..self.frames.len() {
let Some(mark) = self.flow_mark() else { break };
let Some(f) = self.frames.get_mut(i) else { break };
f.part = mark;
let top = f.margin.top;
// `max` before `min`: a NaN margin counts as none and leaves `room` a number.
let top = f.margin.top.max(0.0).min(room);
room = (room - top).max(0.0);
self.advance(top);
}
Ok(())
+10
View File
@@ -2117,6 +2117,16 @@ fn a_flowed_subform_is_drawn_with_its_border_under_its_content() {
assert!(form.pages.len() >= 2, "the long text runs onto another page");
assert!(form.pages.iter().all(|p| !p.items.iter().any(|i| matches!(i, Item::Fill { .. }))), "no fill drawn");
assert_eq!(form.warnings.iter().filter(|w| w.contains("bordered section")).count(), 1, "{:?}", form.warnings);
// A field that fits a fresh page (720pt) but not after the 7.2pt top margin: the margin
// gives way, so the field ends at the content area's bottom, inside the second part.
let tall = r#"<field name="tall" w="3in" h="715pt"><ui><textEdit/></ui></field>"#;
let form = lay(&boxed(&(field("a") + tall)));
assert_eq!(form.pages.len(), 2);
let tall = form.pages[1].items.iter().find_map(|i| if let Item::Widget(w) = i { (w.name == "tall").then_some(w.rect) } else { None }).unwrap();
assert!(tall.bottom() <= 756.01, "the field stays in the content area: {tall:?}");
let part = form.pages[1].items.iter().find_map(|i| if let Item::Fill { rect, .. } = i { Some(*rect) } else { None }).expect("the second part");
assert!(part.y <= tall.y + 0.01 && part.bottom() >= tall.bottom() - 0.01, "the border holds the field: {part:?} {tall:?}");
assert!(form.warnings.is_empty(), "{:?}", form.warnings);
}
#[test]