diff --git a/crates/xfa/src/layout/draw.rs b/crates/xfa/src/layout/draw.rs index 1cd7fa1771..69309aa0e0 100644 --- a/crates/xfa/src/layout/draw.rs +++ b/crates/xfa/src/layout/draw.rs @@ -136,8 +136,8 @@ impl Layouter<'_> { // A repeating row (a table in a positioned subform, say) gets its // instances one under another, as in a flowed table (XFA 3.3, // "Tables", p. 327), and the rows past them are reported. - let h = self.row_height(r, &cols, inner.w, &ctx, depth + 1); for instance in 0..self.laid_out_instances(k, &ctx.som) { + let h = self.row_height(r, &cols, inner.w, &ctx, depth + 1, csib.saturating_add(instance)); self.draw_row(r, &cols, Rect::new(inner.x, y, inner.w, h), &ctx, depth + 1, csib + instance)?; y += h; } diff --git a/crates/xfa/src/layout/flow.rs b/crates/xfa/src/layout/flow.rs index 1ec19849d8..52a19c60e8 100644 --- a/crates/xfa/src/layout/flow.rs +++ b/crates/xfa/src/layout/flow.rs @@ -233,8 +233,8 @@ impl Layouter<'_> { Some(Node::Subform(l)) => Some((li, l.as_ref())), _ => None, }); - let lead_h = lead.map_or(0.0, |(_, l)| self.row_height(l, &cols, inner_w, ctx, depth + 1)); let sibs = SomIndices::of(&sf.children); + let lead_h = lead.map_or(0.0, |(li, l)| self.row_height(l, &cols, inner_w, ctx, depth + 1, sibs.at(li))); for (ci, child) in sf.children.iter().enumerate() { if !child.common().presence.occupies() { continue; @@ -246,7 +246,7 @@ impl Layouter<'_> { // Rows added as data (in another viewer) repeat the row. let instances = self.laid_out_instances(child, &ctx.som); for inst in 0..instances { - let h = self.row_height(row, &cols, inner_w, ctx, depth + 1); + let h = self.row_height(row, &cols, inner_w, ctx, depth + 1, csib.saturating_add(inst)); let led = lead.filter(|(li, _)| *li != ci); if self.make_room(h, led.map_or(0.0, |_| lead_h))? && let Some((li, l)) = led diff --git a/crates/xfa/src/layout/measure.rs b/crates/xfa/src/layout/measure.rs index 5c2ca82b32..78c1ca10ec 100644 --- a/crates/xfa/src/layout/measure.rs +++ b/crates/xfa/src/layout/measure.rs @@ -108,15 +108,16 @@ impl Layouter<'_> { .enumerate() .filter(|(_, k)| k.common().presence.occupies()) .map(|(i, k)| match k { - // A repeating row takes its height once per instance, as drawn. - Node::Subform(r) if r.layout == Layout::Row => { - self.row_height(r, &cols, inner_w, ctx, depth + 1) * self.instances(k, &som) as f64 - } + // Each instance of a repeating row takes its own height, as drawn. + Node::Subform(r) if r.layout == Layout::Row => (0..self.instances(k, &som)) + .map(|n| self.row_height(r, &cols, inner_w, ctx, depth + 1, sibs.at(i).saturating_add(n))) + .sum(), _ => self.size_at(k, inner_w, ctx, depth + 1, sibs.at(i)).1, }) .sum() } - Layout::Row => self.row_height(s, &self.columns(s, inner_w), inner_w, ctx, depth + 1), + // `ctx` is already this row instance's own. + Layout::Row => self.cells(s, &self.columns(s, inner_w), inner_w, ctx, depth + 1).1, }, }; content + c.margin.vertical() @@ -389,7 +390,10 @@ impl Layouter<'_> { (out, clamp_opt(h, row.common.min_h, row.common.max_h)) } - pub(super) fn row_height(&self, row: &Subform, cols: &[f64], inner_w: f64, ctx: &Ctx, depth: usize) -> f64 { - self.cells(row, cols, inner_w, ctx, depth).1 + /// The height of instance `sib` of `row` in the table at `ctx`, measured as [`Self::draw_row`] + /// draws it: under the instance's own SOM path, so a cell that grows with its text takes + /// that instance's value (XFA 3.3, "Tables", p. 327; "Growable Containers", p. 275). + pub(super) fn row_height(&self, row: &Subform, cols: &[f64], inner_w: f64, ctx: &Ctx, depth: usize, sib: usize) -> f64 { + self.cells(row, cols, inner_w, &ctx.with_som(&row.common, sib), depth).1 } } diff --git a/crates/xfa/src/table_repeat_tests.rs b/crates/xfa/src/table_repeat_tests.rs index 22c3cad708..68c36023f3 100644 --- a/crates/xfa/src/table_repeat_tests.rs +++ b/crates/xfa/src/table_repeat_tests.rs @@ -247,3 +247,35 @@ fn each_instance_of_a_repeating_container_is_measured_with_its_own_rows() { } } } + +#[test] +fn each_row_instance_takes_the_height_of_its_own_growing_text() { + // A repeating row whose cell grows with its text (a multi-line field without a height) is + // as tall as its own instance's value (XFA 3.3, "Tables", p. 327; "Growable Containers", + // p. 271): a long value makes its row taller and pushes the rows after it down, wherever + // the table sits. + let long = "a long value that wraps over several lines of the cell ".repeat(6); + for place in [Place::Positioned, Place::PositionedFlow, Place::Flowed] { + let xml = table_form(place, r#"min="1" max="-1""#, 3) + .replace( + r#""#, + r#""#, + ) + .replace("row 2", &format!("{long}")); + let form = layout_xml(&xml).unwrap(); + let all = laid_out(&form); + let qty = named(&all, "qty"); + assert_eq!(qty.len(), 3, "{place:?}"); + let (short, tall) = (qty[0].3, qty[1].3); + assert!(tall.h > short.h + 20.0, "{place:?}: the long row {tall:?} is no taller than a short one {short:?}"); + for pair in qty.windows(2) { + let (a, b) = (pair[0].3, pair[1].3); + assert!((b.y - a.bottom()).abs() < 0.01, "{place:?}: row at {b:?} after {a:?}"); + } + let foot = named(&all, "total")[0].3; + assert!((foot.y - qty[2].3.bottom()).abs() < 0.01, "{place:?}: footer {foot:?} after {:?}", qty[2].3); + if let Some(after) = named(&all, "after").first() { + assert!((after.3.y - foot.bottom()).abs() < 0.01, "{place:?}: after {:?}, footer {foot:?}", after.3); + } + } +}