fix(xfa): every flowed subform instance spends the layout budget

Nested repeating subforms multiplied their instances (up to 50 per level
over 64 levels), and an empty instance neither measured nor placed
anything, so it spent none of the measurement or item budgets. Each
instance now counts as a measurement, and layout stops with TooLarge
once the budget is spent.
This commit is contained in:
aiongg
2026-10-22 17:08:54 +00:00
parent 77ee1c8a26
commit 2e1c029fa8
2 changed files with 15 additions and 0 deletions
+3
View File
@@ -769,6 +769,9 @@ impl Layouter<'_> {
self.warn_truncated(node, &ctx.som, instances);
let ctx_parent = ctx;
for i in 0..instances {
// Every instance spends budget, so nested repeats that place nothing still stop.
self.measures.set(self.measures.get().saturating_add(1));
self.check_measures()?;
let ctx = ctx_parent.with_som(&sf.common, sib + i);
let (rect, _, _) = self.cursor();
let (x, w) = avail.unwrap_or((rect.x, rect.w));
+12
View File
@@ -404,6 +404,18 @@ fn repeated_pictures_are_bounded_in_total_bytes() {
assert!(layout_xml(&ok).is_ok(), "one small picture must lay out");
}
#[test]
fn empty_repeating_subforms_spend_the_layout_budget() {
// Four nested levels of 50 empty instances are 6.25 million instances that place nothing
// and measure nothing; each one counts against the layout budget.
let level = "<subform layout=\"tb\"><occur initial=\"50\" max=\"-1\"/>";
let tpl = format!("<template><subform layout=\"tb\">{}{}</subform></template>", level.repeat(4), "</subform>".repeat(4));
assert!(matches!(layout_xml(&tpl), Err(XfaError::TooLarge(_))));
// Three levels (125,000 instances) still lay out.
let tpl = format!("<template><subform layout=\"tb\">{}{}</subform></template>", level.repeat(3), "</subform>".repeat(3));
assert!(layout_xml(&tpl).is_ok());
}
#[test]
fn the_packets_can_be_one_stream_or_named_parts() {
// Named parts, with the template split from the rest.