From fccc6c224216dd6d2665a28184053f5e5b9ea06a Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Sun, 11 Jul 2021 19:12:50 +0200 Subject: [PATCH] XFA - Avoid to have containers not pushed in the html - it aims to fix issue #13668. --- src/core/xfa/layout.js | 29 ++++++++++-------- src/core/xfa/template.js | 50 ++++++++++++++++++++++++------- test/pdfs/xfa_issue13668.pdf.link | 1 + test/test_manifest.json | 8 +++++ 4 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 test/pdfs/xfa_issue13668.pdf.link diff --git a/src/core/xfa/layout.js b/src/core/xfa/layout.js index 4f3dcc230..2134f2fd5 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; } diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 6188fb509..b64deaabf 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 { @@ -2225,6 +2232,7 @@ class ExclGroup extends XFAObject { children, attributes, attempt: 0, + line: null, numberInLine: 0, availableSpace: { width: Math.min(this.w || Infinity, availableSpace.width), @@ -2287,12 +2295,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. @@ -2308,6 +2314,15 @@ class ExclGroup extends XFAObject { if (result.isBreak()) { return result; } + if ( + isLrTb && + this[$extra].attempt === 0 && + this[$extra].numberInLine === 0 + ) { + // See comment in Subform::[$toHTML]. + this[$extra].attempt = maxRun; + break; + } } if (!isSplittable) { @@ -4625,6 +4640,7 @@ class Subform extends XFAObject { Object.assign(this[$extra], { children, + line: null, attributes, attempt: 0, numberInLine: 0, @@ -4708,12 +4724,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. @@ -4729,6 +4743,22 @@ class Subform extends XFAObject { if (result.isBreak()) { return result; } + if ( + isLrTb && + this[$extra].attempt === 0 && + this[$extra].numberInLine === 0 + ) { + // 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 000000000..7f8e7f47b --- /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 397db806a..4be2035a0 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",