diff --git a/crates/xfa/src/layout/draw.rs b/crates/xfa/src/layout/draw.rs
index f6520c1628..cc8ff5b670 100644
--- a/crates/xfa/src/layout/draw.rs
+++ b/crates/xfa/src/layout/draw.rs
@@ -97,8 +97,11 @@ impl Layouter<'_> {
}
let (w, h) = self.size_of(k, inner.w, &ctx, depth + 1);
// A repeating subform here (a table in an area of a positioned page,
- // say) gets its instances, as in a flowed one, one under another.
- for instance in 0..self.instances(k, &ctx.som) {
+ // say) gets its instances, as in a flowed one, one under another,
+ // and the rows past them are reported as there.
+ let instances = self.instances(k, &ctx.som);
+ self.warn_truncated(k, &ctx.som, instances);
+ for instance in 0..instances {
self.draw_node(k, Rect::new(inner.x, y, w, h), &ctx, depth + 1, sibs.at(i) + instance)?;
y += h;
}
@@ -106,7 +109,11 @@ impl Layouter<'_> {
}
Layout::LrTb | Layout::RlTb => {
// A repeating subform here (rows of fields under a heading) gets its
- // instances, as in the other layouts.
+ // instances, as in the other layouts, and the rows past them are reported.
+ for k in s.children.iter().filter(|k| k.common().presence.occupies()) {
+ let instances = self.instances(k, &ctx.som);
+ self.warn_truncated(k, &ctx.som, instances);
+ }
let (placed, _) = self.pack_instances(&s.children, inner.w, &ctx, depth, &ctx.som);
for (i, instance, r) in placed {
if let Some(k) = s.children.get(i) {
diff --git a/crates/xfa/src/layout/flow.rs b/crates/xfa/src/layout/flow.rs
index 36a2af9d3b..797ec6aa6c 100644
--- a/crates/xfa/src/layout/flow.rs
+++ b/crates/xfa/src/layout/flow.rs
@@ -295,7 +295,7 @@ impl Layouter<'_> {
/// The data holds more instances of `node` than `laid_out`: say so, since calculations
/// then see only those, and say why. (The template's own counts can't ask for more: the
/// parser reads `initial` and `max` up to [`MAX_INSTANCES`].)
- fn warn_truncated(&mut self, node: &Node, parent_som: &str, laid_out: usize) {
+ pub(super) fn warn_truncated(&mut self, node: &Node, parent_som: &str, laid_out: usize) {
let Node::Subform(sf) = node else { return };
let from_data = match (&sf.common.name, self.data) {
(Some(name), Some(d)) if sf.occur.max != Some(1) => d.count(&data_path(self.tpl, parent_som), name),
diff --git a/crates/xfa/src/repeat_tests.rs b/crates/xfa/src/repeat_tests.rs
index 810847c30b..bce2051838 100644
--- a/crates/xfa/src/repeat_tests.rs
+++ b/crates/xfa/src/repeat_tests.rs
@@ -100,3 +100,21 @@ fn an_initial_count_lays_out_whole_up_to_the_limit() {
assert_eq!(crate::layout::instance_count(&unbounded, 0), MAX_INSTANCES);
assert_eq!(crate::layout::instance_count(&unbounded, usize::MAX), MAX_INSTANCES);
}
+
+#[test]
+fn rows_cut_in_a_positioned_container_are_reported_too() {
+ // The list sits in a positioned subform, so it is drawn rather than flowed: its `tb`,
+ // `lr-tb` and `rl-tb` repeats are cut at the same bounds and must say so as a flowed one does.
+ for layout in ["tb", "lr-tb", "rl-tb"] {
+ let xml = rows_form("5", 60, false)
+ .replace(r#""#, r#""#)
+ .replace("", "")
+ .replace(r#""#, &format!(r#""#))
+ .replace("");
+ let form = layout_xml(&xml).unwrap();
+ assert_eq!(values(&form), expected(5), "{layout}");
+ let w = cut_warning(&form).unwrap_or_else(|| panic!("{layout}: the rows past occur max are reported: {:?}", form.warnings));
+ assert!(w.contains("holds 60 rows") && w.contains("first 5"), "{layout}: {w}");
+ }
+}