diff --git a/src/core/xfa/html_utils.js b/src/core/xfa/html_utils.js
index 88a7c9f85..b645a5809 100644
--- a/src/core/xfa/html_utils.js
+++ b/src/core/xfa/html_utils.js
@@ -102,24 +102,12 @@ const converters = {
       style.width = measureToString(width);
     } else {
       style.width = "auto";
-      if (node.maxW > 0) {
-        style.maxWidth = measureToString(node.maxW);
-      }
-      if (parent.layout === "position") {
-        style.minWidth = measureToString(node.minW);
-      }
     }
 
     if (height !== "") {
       style.height = measureToString(height);
     } else {
       style.height = "auto";
-      if (node.maxH > 0) {
-        style.maxHeight = measureToString(node.maxH);
-      }
-      if (parent.layout === "position") {
-        style.minHeight = measureToString(node.minH);
-      }
     }
   },
   position(node, style) {
@@ -188,6 +176,20 @@ const converters = {
   },
 };
 
+function setMinMaxDimensions(node, style) {
+  const parent = node[$getParent]();
+  if (parent.layout === "position") {
+    style.minWidth = measureToString(node.minW);
+    if (node.maxW) {
+      style.maxWidth = measureToString(node.maxW);
+    }
+    style.minHeight = measureToString(node.minH);
+    if (node.maxH) {
+      style.maxHeight = measureToString(node.maxH);
+    }
+  }
+}
+
 function layoutText(text, xfaFont, fonts, width) {
   const measure = new TextMeasure(xfaFont, fonts);
   if (typeof text === "string") {
@@ -283,16 +285,9 @@ function fixDimensions(node) {
     }
   }
 
-  if (node.layout === "position") {
-    // Acrobat doesn't take into account min, max values
-    // for containers with positioned layout (which makes sense).
-    node.minW = node.minH = 0;
-    node.maxW = node.maxH = Infinity;
-  } else {
-    if (node.layout === "table") {
-      if (node.w === "" && Array.isArray(node.columnWidths)) {
-        node.w = node.columnWidths.reduce((a, x) => a + x, 0);
-      }
+  if (node.layout === "table") {
+    if (node.w === "" && Array.isArray(node.columnWidths)) {
+      node.w = node.columnWidths.reduce((a, x) => a + x, 0);
     }
   }
 }
@@ -471,5 +466,6 @@ export {
   layoutClass,
   layoutText,
   measureToString,
+  setMinMaxDimensions,
   toStyle,
 };
diff --git a/src/core/xfa/layout.js b/src/core/xfa/layout.js
index 79b9cbecc..ad12c70dc 100644
--- a/src/core/xfa/layout.js
+++ b/src/core/xfa/layout.js
@@ -13,7 +13,13 @@
  * limitations under the License.
  */
 
-import { $extra, $flushHTML } from "./xfa_object.js";
+import {
+  $extra,
+  $flushHTML,
+  $getParent,
+  $getTemplateRoot,
+  $isSplittable,
+} from "./xfa_object.js";
 import { measureToString } from "./html_utils.js";
 
 // Subform and ExclGroup have a layout so they share these functions.
@@ -146,59 +152,181 @@ function addHTML(node, html, bbox) {
 
 function getAvailableSpace(node) {
   const availableSpace = node[$extra].availableSpace;
-  const marginH = node.margin
+  const marginV = node.margin
     ? node.margin.topInset + node.margin.bottomInset
     : 0;
+  const marginH = node.margin
+    ? node.margin.leftInset + node.margin.rightInset
+    : 0;
 
   switch (node.layout) {
     case "lr-tb":
     case "rl-tb":
-      switch (node[$extra].attempt) {
-        case 0:
-          return {
-            width: availableSpace.width - node[$extra].currentWidth,
-            height: availableSpace.height - marginH - node[$extra].prevHeight,
-          };
-        case 1:
-          return {
-            width: availableSpace.width,
-            height: availableSpace.height - marginH - node[$extra].height,
-          };
-        default:
-          // Overflow must stay in the container.
-          return {
-            width: Infinity,
-            height: Infinity,
-          };
-      }
-    case "rl-row":
-    case "row":
-      if (node[$extra].attempt === 0) {
-        const width = node[$extra].columnWidths
-          .slice(node[$extra].currentColumn)
-          .reduce((a, x) => a + x);
-        return { width, height: availableSpace.height - marginH };
-      }
-      // Overflow must stay in the container.
-      return { width: Infinity, height: Infinity };
-    case "table":
-    case "tb":
       if (node[$extra].attempt === 0) {
         return {
-          width: availableSpace.width,
-          height: availableSpace.height - marginH - node[$extra].height,
+          width: availableSpace.width - marginH - node[$extra].currentWidth,
+          height: availableSpace.height - marginV - node[$extra].prevHeight,
         };
       }
-      // Overflow must stay in the container.
-      return { width: Infinity, height: Infinity };
+      return {
+        width: availableSpace.width - marginH,
+        height: availableSpace.height - marginV - node[$extra].height,
+      };
+    case "rl-row":
+    case "row":
+      const width = node[$extra].columnWidths
+        .slice(node[$extra].currentColumn)
+        .reduce((a, x) => a + x);
+      return { width, height: availableSpace.height - marginH };
+    case "table":
+    case "tb":
+      return {
+        width: availableSpace.width - marginH,
+        height: availableSpace.height - marginV - node[$extra].height,
+      };
     case "position":
     default:
-      if (node[$extra].attempt === 0) {
-        return availableSpace;
-      }
-      // Overflow must stay in the container.
-      return { width: Infinity, height: Infinity };
+      return availableSpace;
   }
 }
 
-export { addHTML, flushHTML, getAvailableSpace };
+function getTransformedBBox(node) {
+  // Take into account rotation and anchor the get the
+  // real bounding box.
+  let w = node.w === "" ? NaN : node.w;
+  let h = node.h === "" ? NaN : node.h;
+  let [centerX, centerY] = [0, 0];
+  switch (node.anchorType || "") {
+    case "bottomCenter":
+      [centerX, centerY] = [w / 2, h];
+      break;
+    case "bottomLeft":
+      [centerX, centerY] = [0, h];
+      break;
+    case "bottomRight":
+      [centerX, centerY] = [w, h];
+      break;
+    case "middleCenter":
+      [centerX, centerY] = [w / 2, h / 2];
+      break;
+    case "middleLeft":
+      [centerX, centerY] = [0, h / 2];
+      break;
+    case "middleRight":
+      [centerX, centerY] = [w, h / 2];
+      break;
+    case "topCenter":
+      [centerX, centerY] = [w / 2, 0];
+      break;
+    case "topRight":
+      [centerX, centerY] = [w, 0];
+      break;
+  }
+
+  let x;
+  let y;
+  switch (node.rotate || 0) {
+    case 0:
+      [x, y] = [-centerX, -centerY];
+      break;
+    case 90:
+      [x, y] = [-centerY, centerX];
+      [w, h] = [h, -w];
+      break;
+    case 180:
+      [x, y] = [centerX, centerY];
+      [w, h] = [-w, -h];
+      break;
+    case 270:
+      [x, y] = [centerY, -centerX];
+      [w, h] = [-h, w];
+      break;
+  }
+
+  return [
+    node.x + x + Math.min(0, w),
+    node.y + y + Math.min(0, h),
+    Math.abs(w),
+    Math.abs(h),
+  ];
+}
+
+/**
+ * Returning true means that the node will be layed out
+ * else the layout will go to its next step (changing of line
+ * in case of lr-tb or changing content area...).
+ */
+function checkDimensions(node, space) {
+  if (node.w === 0 || node.h === 0) {
+    return true;
+  }
+
+  if (space.width <= 0 || space.height <= 0) {
+    return false;
+  }
+
+  const parent = node[$getParent]();
+  const attempt = (node[$extra] && node[$extra].attempt) || 0;
+  switch (parent.layout) {
+    case "lr-tb":
+    case "rl-tb":
+      switch (attempt) {
+        case 0: {
+          let w, h;
+          if (node.w !== "" || node.h !== "") {
+            [, , w, h] = getTransformedBBox(node);
+          }
+          if (node.h !== "" && Math.round(h - space.height) > 1) {
+            return false;
+          }
+          if (node.w !== "") {
+            return Math.round(w - space.width) <= 1;
+          }
+
+          return node.minW <= space.width;
+        }
+        case 1: {
+          if (node.h !== "" && !node[$isSplittable]()) {
+            const [, , , h] = getTransformedBBox(node);
+            if (Math.round(h - space.height) > 1) {
+              return false;
+            }
+          }
+          return true;
+        }
+        default:
+          return true;
+      }
+    case "table":
+    case "tb":
+      if (attempt !== 1 && node.h !== "" && !node[$isSplittable]()) {
+        const [, , , h] = getTransformedBBox(node);
+        if (Math.round(h - space.height) > 1) {
+          return false;
+        }
+      }
+      return true;
+    case "position":
+      const [x, y, w, h] = getTransformedBBox(node);
+      const isWidthOk = node.w === "" || Math.round(w + x - space.width) <= 1;
+      const isHeightOk = node.h === "" || Math.round(h + y - space.height) <= 1;
+
+      if (isWidthOk && isHeightOk) {
+        return true;
+      }
+
+      const area = node[$getTemplateRoot]()[$extra].currentContentArea;
+      if (isWidthOk) {
+        return h + y > area.h;
+      }
+
+      return w + x > area.w;
+    case "rl-row":
+    case "row":
+    default:
+      // No layout, so accept everything.
+      return true;
+  }
+}
+
+export { addHTML, checkDimensions, flushHTML, getAvailableSpace };
diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js
index 8e57f894d..ba4f88c0a 100644
--- a/src/core/xfa/template.js
+++ b/src/core/xfa/template.js
@@ -30,10 +30,12 @@ import {
   $getNextPage,
   $getParent,
   $getSubformParent,
+  $getTemplateRoot,
   $hasItem,
   $hasSettableValue,
   $ids,
   $isCDATAXml,
+  $isSplittable,
   $isTransparent,
   $namespaceId,
   $nodeName,
@@ -55,7 +57,12 @@ import {
   XFAObjectArray,
 } from "./xfa_object.js";
 import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
-import { addHTML, flushHTML, getAvailableSpace } from "./layout.js";
+import {
+  addHTML,
+  checkDimensions,
+  flushHTML,
+  getAvailableSpace,
+} from "./layout.js";
 import {
   computeBbox,
   createWrapper,
@@ -65,6 +72,7 @@ import {
   layoutClass,
   layoutText,
   measureToString,
+  setMinMaxDimensions,
   toStyle,
 } from "./html_utils.js";
 import {
@@ -107,14 +115,6 @@ function _setValue(templateNode, value) {
   templateNode.value[$setValue](value);
 }
 
-function getRoot(node) {
-  let parent = node[$getParent]();
-  while (!(parent instanceof Template)) {
-    parent = parent[$getParent]();
-  }
-  return parent;
-}
-
 function* getContainedChildren(node) {
   for (const child of node[$getChildren]()) {
     if (child instanceof SubformSet) {
@@ -136,90 +136,6 @@ function valueToHtml(value) {
   });
 }
 
-function getTransformedBBox(node) {
-  // Take into account rotation and anchor the get the
-  // real bounding box.
-  let w = node.w === "" ? NaN : node.w;
-  let h = node.h === "" ? NaN : node.h;
-  let [centerX, centerY] = [0, 0];
-  switch (node.anchorType || "") {
-    case "bottomCenter":
-      [centerX, centerY] = [w / 2, h];
-      break;
-    case "bottomLeft":
-      [centerX, centerY] = [0, h];
-      break;
-    case "bottomRight":
-      [centerX, centerY] = [w, h];
-      break;
-    case "middleCenter":
-      [centerX, centerY] = [w / 2, h / 2];
-      break;
-    case "middleLeft":
-      [centerX, centerY] = [0, h / 2];
-      break;
-    case "middleRight":
-      [centerX, centerY] = [w, h / 2];
-      break;
-    case "topCenter":
-      [centerX, centerY] = [w / 2, 0];
-      break;
-    case "topRight":
-      [centerX, centerY] = [w, 0];
-      break;
-  }
-
-  let x;
-  let y;
-  switch (node.rotate || 0) {
-    case 0:
-      [x, y] = [-centerX, -centerY];
-      break;
-    case 90:
-      [x, y] = [-centerY, centerX];
-      [w, h] = [h, -w];
-      break;
-    case 180:
-      [x, y] = [centerX, centerY];
-      [w, h] = [-w, -h];
-      break;
-    case 270:
-      [x, y] = [centerY, -centerX];
-      [w, h] = [-h, w];
-      break;
-  }
-
-  return [
-    node.x + x + Math.min(0, w),
-    node.y + y + Math.min(0, h),
-    Math.abs(w),
-    Math.abs(h),
-  ];
-}
-
-const NOTHING = 0;
-const NOSPACE = 1;
-const VALID = 2;
-function checkDimensions(node, space) {
-  if (node[$getParent]().layout === "position") {
-    return VALID;
-  }
-  const [x, y, w, h] = getTransformedBBox(node);
-  if (node.w === 0 || node.h === 0) {
-    return VALID;
-  }
-
-  if (node.w !== "" && Math.round(x + w - space.width) > 1) {
-    return NOSPACE;
-  }
-
-  if (node.h !== "" && Math.round(y + h - space.height) > 1) {
-    return NOSPACE;
-  }
-
-  return VALID;
-}
-
 class AppearanceFilter extends StringObject {
   constructor(attributes) {
     super(TEMPLATE_NS_ID, "appearanceFilter");
@@ -1525,7 +1441,7 @@ class Draw extends XFAObject {
 
     if ((this.w === "" || this.h === "") && this.value) {
       const maxWidth = this.w === "" ? availableSpace.width : this.w;
-      const fonts = getRoot(this)[$fonts];
+      const fonts = this[$getTemplateRoot]()[$fonts];
       let font = this.font;
       if (!font) {
         let parent = this[$getParent]();
@@ -1571,13 +1487,8 @@ class Draw extends XFAObject {
       }
     }
 
-    switch (checkDimensions(this, availableSpace)) {
-      case NOTHING:
-        return HTMLResult.EMPTY;
-      case NOSPACE:
-        return HTMLResult.FAILURE;
-      default:
-        break;
+    if (!checkDimensions(this, availableSpace)) {
+      return HTMLResult.FAILURE;
     }
 
     const style = toStyle(
@@ -1593,6 +1504,8 @@ class Draw extends XFAObject {
       "margin"
     );
 
+    setMinMaxDimensions(this, style);
+
     const classNames = ["xfaDraw"];
     if (this.font) {
       classNames.push("xfaFont");
@@ -2062,6 +1975,27 @@ class ExclGroup extends XFAObject {
     }
   }
 
+  [$isSplittable]() {
+    // We cannot cache the result here because the contentArea
+    // can change.
+    const root = this[$getTemplateRoot]();
+    const contentArea = root[$extra].currentContentArea;
+    if (contentArea && Math.max(this.minH, this.h || 0) >= contentArea.h) {
+      return true;
+    }
+
+    if (this.layout === "position") {
+      return false;
+    }
+
+    const parentLayout = this[$getParent]().layout;
+    if (parentLayout && parentLayout.includes("row")) {
+      return false;
+    }
+
+    return true;
+  }
+
   [$flushHTML]() {
     return flushHTML(this);
   }
@@ -2106,13 +2040,8 @@ class ExclGroup extends XFAObject {
       currentWidth: 0,
     });
 
-    switch (checkDimensions(this, availableSpace)) {
-      case NOTHING:
-        return HTMLResult.EMPTY;
-      case NOSPACE:
-        return HTMLResult.FAILURE;
-      default:
-        break;
+    if (!checkDimensions(this, availableSpace)) {
+      return HTMLResult.FAILURE;
     }
 
     availableSpace = {
@@ -2172,7 +2101,7 @@ class ExclGroup extends XFAObject {
         }
       }
 
-      failure = this[$extra].attempt === 2;
+      failure = this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT;
     } else {
       const result = this[$childrenToHTML]({
         filter,
@@ -2185,12 +2114,16 @@ class ExclGroup extends XFAObject {
     }
 
     if (failure) {
-      if (this.layout === "position") {
+      if (this[$isSplittable]()) {
         delete this[$extra];
       }
       return HTMLResult.FAILURE;
     }
 
+    if (children.length === 0) {
+      return HTMLResult.EMPTY;
+    }
+
     let marginH = 0;
     let marginV = 0;
     if (this.margin) {
@@ -2198,11 +2131,15 @@ class ExclGroup extends XFAObject {
       marginV = this.margin.topInset + this.margin.bottomInset;
     }
 
+    const width = Math.max(this[$extra].width + marginH, this.w || 0);
+    const height = Math.max(this[$extra].height + marginV, this.h || 0);
+    const bbox = [this.x, this.y, width, height];
+
     if (this.w === "") {
-      style.width = measureToString(this[$extra].width + marginH);
+      style.width = measureToString(width);
     }
     if (this.h === "") {
-      style.height = measureToString(this[$extra].height + marginV);
+      style.height = measureToString(height);
     }
 
     const html = {
@@ -2211,16 +2148,6 @@ class ExclGroup extends XFAObject {
       children,
     };
 
-    let bbox;
-    if (this.w !== "" && this.h !== "") {
-      bbox = [this.x, this.y, this.w, this.h];
-    } else {
-      const width = this.w === "" ? marginH + this[$extra].width : this.w;
-      const height = this.h === "" ? marginV + this[$extra].height : this.h;
-
-      bbox = [this.x, this.y, width, height];
-    }
-
     delete this[$extra];
 
     return HTMLResult.success(createWrapper(this, html), bbox);
@@ -2372,13 +2299,8 @@ class Field extends XFAObject {
 
     fixDimensions(this);
 
-    switch (checkDimensions(this, availableSpace)) {
-      case NOTHING:
-        return HTMLResult.EMPTY;
-      case NOSPACE:
-        return HTMLResult.FAILURE;
-      default:
-        break;
+    if (!checkDimensions(this, availableSpace)) {
+      return HTMLResult.FAILURE;
     }
 
     const style = toStyle(
@@ -2393,6 +2315,8 @@ class Field extends XFAObject {
       "hAlign"
     );
 
+    setMinMaxDimensions(this, style);
+
     const classNames = ["xfaField"];
     // If no font, font properties are inherited.
     if (this.font) {
@@ -3374,7 +3298,7 @@ class PageArea extends XFAObject {
   }
 
   [$getAvailableSpace]() {
-    return { width: Infinity, height: Infinity };
+    return this[$extra].space || { width: 0, height: 0 };
   }
 
   [$toHTML]() {
@@ -3392,10 +3316,18 @@ class PageArea extends XFAObject {
     if (this.medium && this.medium.short && this.medium.long) {
       style.width = measureToString(this.medium.short);
       style.height = measureToString(this.medium.long);
+      this[$extra].space = {
+        width: this.medium.short,
+        height: this.medium.long,
+      };
       if (this.medium.orientation === "landscape") {
         const x = style.width;
         style.width = style.height;
         style.height = x;
+        this[$extra].space = {
+          width: this.medium.long,
+          height: this.medium.short,
+        };
       }
     } else {
       warn("XFA - No medium specified in pageArea: please file a bug.");
@@ -3486,7 +3418,7 @@ class PageSet extends XFAObject {
 
       return this[$getNextPage]();
     }
-    const pageNumber = getRoot(this)[$extra].pageNumber;
+    const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber;
     const parity = pageNumber % 2 === 0 ? "even" : "odd";
     const position = pageNumber === 0 ? "first" : "rest";
 
@@ -4175,6 +4107,36 @@ class Subform extends XFAObject {
     return getAvailableSpace(this);
   }
 
+  [$isSplittable](x) {
+    // We cannot cache the result here because the contentArea
+    // can change.
+    const root = this[$getTemplateRoot]();
+    const contentArea = root[$extra].currentContentArea;
+    if (contentArea && Math.max(this.minH, this.h || 0) >= contentArea.h) {
+      return true;
+    }
+
+    if (this.layout === "position") {
+      return false;
+    }
+
+    if (this.keep && this.keep.intact !== "none") {
+      return false;
+    }
+
+    const parentLayout = this[$getParent]().layout;
+    if (parentLayout && parentLayout.includes("row")) {
+      return false;
+    }
+
+    if (this.overflow && this.overflow.target) {
+      const target = root[$searchNode](this.overflow.target, this);
+      return target && target[0] === contentArea;
+    }
+
+    return true;
+  }
+
   [$toHTML](availableSpace) {
     if (this.presence === "hidden" || this.presence === "inactive") {
       return HTMLResult.EMPTY;
@@ -4201,9 +4163,7 @@ class Subform extends XFAObject {
     }
 
     if (this[$extra] && this[$extra].afterBreakAfter) {
-      const result = this[$extra].afterBreakAfter;
-      delete this[$extra];
-      return result;
+      return HTMLResult.EMPTY;
     }
 
     // TODO: incomplete.
@@ -4228,21 +4188,8 @@ class Subform extends XFAObject {
       currentWidth: 0,
     });
 
-    switch (checkDimensions(this, availableSpace)) {
-      case NOTHING:
-        return HTMLResult.EMPTY;
-      case NOSPACE:
-        return HTMLResult.FAILURE;
-      default:
-        break;
-    }
-
-    let noBreakOnOverflow = false;
-    if (this.overflow && this.overflow.target) {
-      const root = getRoot(this);
-      const target = root[$searchNode](this.overflow.target, this);
-      noBreakOnOverflow =
-        target && target[0] === root[$extra].currentContentArea;
+    if (!checkDimensions(this, availableSpace)) {
+      return HTMLResult.FAILURE;
     }
 
     const filter = new Set([
@@ -4285,6 +4232,8 @@ class Subform extends XFAObject {
       attributes.xfaName = this.name;
     }
 
+    const isSplittable = this[$isSplittable]();
+
     // If the container overflows into itself we add an extra
     // layout step to accept finally the element which caused
     // the overflow.
@@ -4292,7 +4241,7 @@ class Subform extends XFAObject {
       this.layout === "lr-tb" || this.layout === "rl-tb"
         ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT
         : 1;
-    maxRun += noBreakOnOverflow ? 1 : 0;
+    maxRun += !isSplittable && this.layout !== "position" ? 1 : 0;
     for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
       const result = this[$childrenToHTML]({
         filter,
@@ -4308,15 +4257,21 @@ class Subform extends XFAObject {
 
     if (this[$extra].attempt === maxRun) {
       if (this.overflow) {
-        getRoot(this)[$extra].overflowNode = this.overflow;
+        this[$getTemplateRoot]()[$extra].overflowNode = this.overflow;
       }
 
-      if (this.layout === "position") {
+      if (!isSplittable) {
+        // Since a new try will happen in a new container with maybe
+        // new dimensions, we invalidate already layed out components.
         delete this[$extra];
       }
       return HTMLResult.FAILURE;
     }
 
+    if (children.length === 0) {
+      return HTMLResult.EMPTY;
+    }
+
     let marginH = 0;
     let marginV = 0;
     if (this.margin) {
@@ -4324,11 +4279,15 @@ class Subform extends XFAObject {
       marginV = this.margin.topInset + this.margin.bottomInset;
     }
 
+    const width = Math.max(this[$extra].width + marginH, this.w || 0);
+    const height = Math.max(this[$extra].height + marginV, this.h || 0);
+    const bbox = [this.x, this.y, width, height];
+
     if (this.w === "") {
-      style.width = measureToString(this[$extra].width + marginH);
+      style.width = measureToString(width);
     }
     if (this.h === "") {
-      style.height = measureToString(this[$extra].height + marginV);
+      style.height = measureToString(height);
     }
 
     const html = {
@@ -4337,16 +4296,6 @@ class Subform extends XFAObject {
       children,
     };
 
-    let bbox;
-    if (this.w !== "" && this.h !== "") {
-      bbox = [this.x, this.y, this.w, this.h];
-    } else {
-      const width = this.w === "" ? marginH + this[$extra].width : this.w;
-      const height = this.h === "" ? marginV + this[$extra].height : this.h;
-
-      bbox = [this.x, this.y, width, height];
-    }
-
     const result = HTMLResult.success(createWrapper(this, html), bbox);
 
     if (this.breakAfter.children.length >= 1) {
@@ -4605,12 +4554,12 @@ class Template extends XFAObject {
       mainHtml.children.push(page);
 
       if (leader) {
-        page.children.push(leader[$toHTML](page[$extra].space).html);
+        page.children.push(leader[$toHTML](pageArea[$extra].space).html);
         leader = null;
       }
 
       if (trailer) {
-        page.children.push(trailer[$toHTML](page[$extra].space).html);
+        page.children.push(trailer[$toHTML](pageArea[$extra].space).html);
         trailer = null;
       }
 
@@ -4646,7 +4595,10 @@ class Template extends XFAObject {
         const html = root[$toHTML](space);
         if (html.success) {
           if (html.html) {
+            hasSomething = true;
             htmlContentAreas[i].children.push(html.html);
+          } else if (!hasSomething) {
+            mainHtml.children.pop();
           }
           return mainHtml;
         }
diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js
index 1ed91cd1c..c59552abd 100644
--- a/src/core/xfa/xfa_object.js
+++ b/src/core/xfa/xfa_object.js
@@ -47,7 +47,7 @@ const $getContainedChildren = Symbol();
 const $getNextPage = Symbol();
 const $getSubformParent = Symbol();
 const $getParent = Symbol();
-const $pushGlyphs = Symbol();
+const $getTemplateRoot = Symbol();
 const $global = Symbol();
 const $hasItem = Symbol();
 const $hasSettableValue = Symbol();
@@ -57,6 +57,7 @@ const $insertAt = Symbol();
 const $isCDATAXml = Symbol();
 const $isDataValue = Symbol();
 const $isDescendent = Symbol();
+const $isSplittable = Symbol();
 const $isTransparent = Symbol();
 const $lastAttribute = Symbol();
 const $namespaceId = Symbol("namespaceId");
@@ -65,6 +66,7 @@ const $nsAttributes = Symbol();
 const $onChild = Symbol();
 const $onChildCheck = Symbol();
 const $onText = Symbol();
+const $pushGlyphs = Symbol();
 const $removeChild = Symbol();
 const $root = Symbol("root");
 const $resolvePrototypes = Symbol();
@@ -162,6 +164,18 @@ class XFAObject {
     }
   }
 
+  [$getTemplateRoot]() {
+    let parent = this[$getParent]();
+    while (parent[$nodeName] !== "template") {
+      parent = parent[$getParent]();
+    }
+    return parent;
+  }
+
+  [$isSplittable]() {
+    return false;
+  }
+
   [$appendChild](child) {
     child[_parent] = this;
     this[_children].push(child);
@@ -985,6 +999,7 @@ export {
   $getParent,
   $getRealChildrenByNameIt,
   $getSubformParent,
+  $getTemplateRoot,
   $global,
   $hasItem,
   $hasSettableValue,
@@ -994,6 +1009,7 @@ export {
   $isCDATAXml,
   $isDataValue,
   $isDescendent,
+  $isSplittable,
   $isTransparent,
   $namespaceId,
   $nodeName,
diff --git a/test/pdfs/xfa_candidate_petitions.pdf.link b/test/pdfs/xfa_candidate_petitions.pdf.link
new file mode 100644
index 000000000..34a4ca1a2
--- /dev/null
+++ b/test/pdfs/xfa_candidate_petitions.pdf.link
@@ -0,0 +1 @@
+https://web.archive.org/web/20210509141453/https://www.sos.state.oh.us/globalassets/elections/directives/2020/dir2020-15_independentcandidatepetitions2020signedextended.pdf
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 67d4549f5..66f2b817a 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -946,6 +946,14 @@
        "enableXfa": true,
        "type": "eq"
     },
+    {  "id": "xfa_candidate_petitions",
+       "file": "pdfs/xfa_candidate_petitions.pdf",
+       "md5": "0db96a00667f8f58f94cf81022e69341",
+       "link": true,
+       "rounds": 1,
+       "enableXfa": true,
+       "type": "eq"
+    },
     {  "id": "xfa_annual_expense_report",
        "file": "pdfs/xfa_annual_expense_report.pdf",
        "md5": "06866e7a6bbc0346789208ef5f6e885c",
diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js
index bd3959413..8e5de87eb 100644
--- a/test/unit/xfa_tohtml_spec.js
+++ b/test/unit/xfa_tohtml_spec.js
@@ -60,6 +60,9 @@ describe("XFAFactory", function () {
       </subform>
       <subform name="second">
         <breakBefore targetType="pageArea" startNew="1"/>
+        <subform>
+          <draw w="1pt" h="1pt"><value><text>foo</text></value></draw>
+        </subform>
       </subform>
     </subform>
   </template>
@@ -133,7 +136,7 @@ describe("XFAFactory", function () {
     <subform name="root" mergeMode="matchTemplate">
       <pageSet>
         <pageArea>
-          <contentArea x="123pt" w="456pt" h="789pt"/>
+          <contentArea x="0pt" w="456pt" h="789pt"/>
           <medium stock="default" short="456pt" long="789pt"/>
           <field y="1pt" w="11pt" h="22pt" x="2pt">
             <ui>
@@ -146,6 +149,7 @@ describe("XFAFactory", function () {
         </pageArea>
       </pageSet>
       <subform name="first">
+        <draw w="1pt" h="1pt"><value><text>foo</text></value></draw>
       </subform>
     </subform>
   </template>