From d1e945998b31670ec11026a7227e011a76f7d561 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Fri, 11 Jun 2021 17:49:13 +0200 Subject: [PATCH] XFA - Handle correctly subformSet - it aims to avoid to loop forever when opening pdf in #13213; - the idea is to consider subformSet as inexistent when running in the tree. So if we've subformA > subformSet > subformB then subformB will be visited as a direct child of subformA. --- src/core/xfa/html_utils.js | 3 +- src/core/xfa/template.js | 57 ++++++++++++++++++++----------- src/core/xfa/xfa_object.js | 17 ++++++++- test/pdfs/xfa_issue13213.pdf.link | 1 + test/test_manifest.json | 8 +++++ 5 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 test/pdfs/xfa_issue13213.pdf.link diff --git a/src/core/xfa/html_utils.js b/src/core/xfa/html_utils.js index 51906e69a..9d4270631 100644 --- a/src/core/xfa/html_utils.js +++ b/src/core/xfa/html_utils.js @@ -16,6 +16,7 @@ import { $extra, $getParent, + $getSubformParent, $nodeName, $toStyle, XFAObject, @@ -296,7 +297,7 @@ function computeBbox(node, html, availableSpace) { } function fixDimensions(node) { - const parent = node[$getParent](); + const parent = node[$getSubformParent](); if (parent.layout && parent.layout.includes("row")) { const extra = parent[$extra]; const colSpan = node.colSpan; diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 3755e8c67..ad9448d96 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -25,8 +25,10 @@ import { $flushHTML, $getAvailableSpace, $getChildren, + $getContainedChildren, $getNextPage, $getParent, + $getSubformParent, $hasItem, $hasSettableValue, $ids, @@ -106,6 +108,16 @@ function getRoot(node) { return parent; } +function* getContainedChildren(node) { + for (const child of node[$getChildren]()) { + if (child instanceof SubformSet) { + yield* child[$getContainedChildren](); + continue; + } + yield child; + } +} + function valueToHtml(value) { return HTMLResult.success({ name: "span", @@ -338,6 +350,12 @@ class Area extends XFAObject { this.subformSet = new XFAObjectArray(); } + *[$getContainedChildren]() { + // This function is overriden in order to fake that subforms under + // this set are in fact under parent subform. + yield* getContainedChildren(this); + } + [$isTransparent]() { return true; } @@ -4077,6 +4095,12 @@ class Subform extends XFAObject { this.subformSet = new XFAObjectArray(); } + *[$getContainedChildren]() { + // This function is overriden in order to fake that subforms under + // this set are in fact under parent subform. + yield* getContainedChildren(this); + } + [$flushHTML]() { return flushHTML(this); } @@ -4161,7 +4185,7 @@ class Subform extends XFAObject { ]); if (this.layout.includes("row")) { - const columnWidths = this[$getParent]().columnWidths; + const columnWidths = this[$getSubformParent]().columnWidths; if (Array.isArray(columnWidths) && columnWidths.length > 0) { this[$extra].columnWidths = columnWidths; this[$extra].currentColumn = 0; @@ -4292,27 +4316,22 @@ class SubformSet extends XFAObject { this.breakBefore = new XFAObjectArray(); this.subform = new XFAObjectArray(); this.subformSet = new XFAObjectArray(); + + // TODO: need to handle break stuff and relation. } - [$toHTML]() { - const children = []; - if (!this[$extra]) { - this[$extra] = Object.create(null); + *[$getContainedChildren]() { + // This function is overriden in order to fake that subforms under + // this set are in fact under parent subform. + yield* getContainedChildren(this); + } + + [$getSubformParent]() { + let parent = this[$getParent](); + while (!(parent instanceof Subform)) { + parent = parent[$getParent](); } - this[$extra].children = children; - - this[$childrenToHTML]({ - filter: new Set(["subform", "subformSet"]), - include: true, - }); - - return HTMLResult.success({ - name: "div", - children, - attributes: { - id: this[$uid], - }, - }); + return parent; } } diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js index 685b805eb..07047619a 100644 --- a/src/core/xfa/xfa_object.js +++ b/src/core/xfa/xfa_object.js @@ -43,7 +43,9 @@ const $getChildrenByNameIt = Symbol(); const $getDataValue = Symbol(); const $getRealChildrenByNameIt = Symbol(); const $getChildren = Symbol(); +const $getContainedChildren = Symbol(); const $getNextPage = Symbol(); +const $getSubformParent = Symbol(); const $getParent = Symbol(); const $global = Symbol(); const $hasItem = Symbol(); @@ -255,6 +257,10 @@ class XFAObject { return this[_parent]; } + [$getSubformParent]() { + return this[$getParent](); + } + [$getChildren](name = null) { if (!name) { return this[_children]; @@ -296,8 +302,15 @@ class XFAObject { return HTMLResult.EMPTY; } - *[_filteredChildrenGenerator](filter, include) { + *[$getContainedChildren]() { + // This function is overriden in Subform and SubformSet. for (const node of this[$getChildren]()) { + yield node; + } + } + + *[_filteredChildrenGenerator](filter, include) { + for (const node of this[$getContainedChildren]()) { if (!filter || include === filter.has(node[$nodeName])) { const availableSpace = this[$getAvailableSpace](); const res = node[$toHTML](availableSpace); @@ -965,10 +978,12 @@ export { $getChildrenByClass, $getChildrenByName, $getChildrenByNameIt, + $getContainedChildren, $getDataValue, $getNextPage, $getParent, $getRealChildrenByNameIt, + $getSubformParent, $global, $hasItem, $hasSettableValue, diff --git a/test/pdfs/xfa_issue13213.pdf.link b/test/pdfs/xfa_issue13213.pdf.link new file mode 100644 index 000000000..2567f173c --- /dev/null +++ b/test/pdfs/xfa_issue13213.pdf.link @@ -0,0 +1 @@ +https://github.com/mozilla/pdf.js/files/6290046/cerfa_12156-05.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index c582b83e3..8e97d7a3c 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -938,6 +938,14 @@ "enableXfa": true, "type": "eq" }, + { "id": "xfa_issue13213", + "file": "pdfs/xfa_issue13213.pdf", + "md5": "8a0e3179bffbac721589d1b1df863b49", + "link": true, + "rounds": 1, + "enableXfa": true, + "type": "eq" + }, { "id": "issue10272", "file": "pdfs/issue10272.pdf", "md5": "bf3b2f74c6878d38a70dc0825f1b9a02",