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);
+ }
+ }
+}