diff --git a/src/core/xfa/layout.js b/src/core/xfa/layout.js index 4f3dcc230102e..231c676c26f9d 100644 --- a/src/core/xfa/layout.js +++ b/src/core/xfa/layout.js @@ -52,10 +52,21 @@ import { measureToString } from "./html_utils.js"; * returning. */ +function createLine(node, children) { + return { + name: "div", + attributes: { + class: [node.layout === "lr-tb" ? "xfaLr" : "xfaRl"], + }, + children, + }; +} + function flushHTML(node) { if (!node[$extra]) { return null; } + const attributes = node[$extra].attributes; const html = { name: "div", @@ -66,7 +77,11 @@ function flushHTML(node) { if (node[$extra].failingNode) { const htmlFromFailing = node[$extra].failingNode[$flushHTML](); if (htmlFromFailing) { - html.children.push(htmlFromFailing); + if (node.layout.endsWith("-tb")) { + html.children.push(createLine(node, [htmlFromFailing])); + } else { + html.children.push(htmlFromFailing); + } } } @@ -74,10 +89,6 @@ function flushHTML(node) { return null; } - node[$extra].children = []; - delete node[$extra].line; - node[$extra].numberInLine = 0; - return html; } @@ -96,13 +107,7 @@ function addHTML(node, html, bbox) { case "lr-tb": case "rl-tb": if (!extra.line || extra.attempt === 1) { - extra.line = { - name: "div", - attributes: { - class: [node.layout === "lr-tb" ? "xfaLr" : "xfaRl"], - }, - children: [], - }; + extra.line = createLine(node, []); extra.children.push(extra.line); extra.numberInLine = 0; } @@ -281,8 +286,14 @@ function checkDimensions(node, space) { } if (node.w !== "") { - // True if width is enough. - return Math.round(w - space.width) <= ERROR; + if (Math.round(w - space.width) <= ERROR) { + return true; + } + if (parent[$extra].numberInLine === 0) { + return space.height > 0; + } + + return false; } return space.width > 0; diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index f9e89565d2f0b..03d61db6b56e5 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -268,10 +268,17 @@ function handleBreak(node) { function handleOverflow(node, extraNode, space) { const root = node[$getTemplateRoot](); const saved = root[$extra].noLayoutFailure; + const savedMethod = extraNode[$getSubformParent]; + + // Replace $getSubformParent to emulate that extraNode is just + // under node. + extraNode[$getSubformParent] = () => node; + root[$extra].noLayoutFailure = true; const res = extraNode[$toHTML](space); node[$addHTML](res.html, res.bbox); root[$extra].noLayoutFailure = saved; + extraNode[$getSubformParent] = savedMethod; } class AppearanceFilter extends StringObject { @@ -2236,6 +2243,7 @@ class ExclGroup extends XFAObject { children, attributes, attempt: 0, + line: null, numberInLine: 0, availableSpace: { width: Math.min(this.w || Infinity, availableSpace.width), @@ -2298,12 +2306,10 @@ class ExclGroup extends XFAObject { attributes.xfaName = this.name; } - const maxRun = - this.layout === "lr-tb" || this.layout === "rl-tb" - ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT - : 1; + const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb"; + const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1; for (; this[$extra].attempt < maxRun; this[$extra].attempt++) { - if (this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { + if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { // If the layout is lr-tb then having attempt equals to // MAX_ATTEMPTS_FOR_LRTB_LAYOUT-1 means that we're trying to layout // on the next line so this on is empty. @@ -2319,6 +2325,16 @@ class ExclGroup extends XFAObject { if (result.isBreak()) { return result; } + if ( + isLrTb && + this[$extra].attempt === 0 && + this[$extra].numberInLine === 0 && + !this[$getTemplateRoot]()[$extra].noLayoutFailure + ) { + // See comment in Subform::[$toHTML]. + this[$extra].attempt = maxRun; + break; + } } if (!isSplittable) { @@ -4646,6 +4662,7 @@ class Subform extends XFAObject { Object.assign(this[$extra], { children, + line: null, attributes, attempt: 0, numberInLine: 0, @@ -4729,12 +4746,10 @@ class Subform extends XFAObject { } } - const maxRun = - this.layout === "lr-tb" || this.layout === "rl-tb" - ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT - : 1; + const isLrTb = this.layout === "lr-tb" || this.layout === "rl-tb"; + const maxRun = isLrTb ? MAX_ATTEMPTS_FOR_LRTB_LAYOUT : 1; for (; this[$extra].attempt < maxRun; this[$extra].attempt++) { - if (this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { + if (isLrTb && this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT - 1) { // If the layout is lr-tb then having attempt equals to // MAX_ATTEMPTS_FOR_LRTB_LAYOUT-1 means that we're trying to layout // on the next line so this on is empty. @@ -4750,6 +4765,23 @@ class Subform extends XFAObject { if (result.isBreak()) { return result; } + if ( + isLrTb && + this[$extra].attempt === 0 && + this[$extra].numberInLine === 0 && + !root[$extra].noLayoutFailure + ) { + // We're failing to put the first element on the line so no + // need to test on the next line. + // The goal is not only to avoid some useless checks but to avoid + // bugs too: if a descendant managed to put a node and failed + // on the next one, going to the next step here will imply to + // visit the descendant again, clear [$extra].children and restart + // on the failing node, consequently the first node just disappears + // because it has never been flushed. + this[$extra].attempt = maxRun; + break; + } } if (!isSplittable) { diff --git a/test/pdfs/xfa_issue13668.pdf.link b/test/pdfs/xfa_issue13668.pdf.link new file mode 100644 index 0000000000000..7f8e7f47bc1a8 --- /dev/null +++ b/test/pdfs/xfa_issue13668.pdf.link @@ -0,0 +1 @@ +https://web.archive.org/web/20210711170923/https://www.placementsmondiauxsunlife.com/content/dam/sunlife/regional/canada/documents/slgi/4839-I-F.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 397db806a9f88..4be2035a0d590 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -1192,6 +1192,14 @@ "enableXfa": true, "type": "eq" }, + { "id": "xfa_issue13668", + "file": "pdfs/xfa_issue13668.pdf", + "md5": "8a5ed3c8a58b425b1ec53329334a0f5b", + "link": true, + "rounds": 1, + "enableXfa": true, + "type": "eq" + }, { "id": "xfa_issue13633", "file": "pdfs/xfa_issue13633.pdf", "md5": "e5b0d09285ca6a140eba08d740be0ea0",