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 ef8fa4ab8..ec3fba695 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;
}
@@ -4079,6 +4097,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);
}
@@ -4163,7 +4187,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;
@@ -4294,27 +4318,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",