diff --git a/src/core/xfa/bind.js b/src/core/xfa/bind.js index c58f6d772..3864f6bd5 100644 --- a/src/core/xfa/bind.js +++ b/src/core/xfa/bind.js @@ -37,6 +37,7 @@ import { $setValue, $text, XFAAttribute, + XFAObjectArray, XmlObject, } from "./xfa_object.js"; import { BindItems, Field, Items, SetProperty, Text } from "./template.js"; @@ -395,6 +396,8 @@ class Binder { if (matches.length > 1) { // Clone before binding to avoid bad state. baseClone = formNode[$clone](); + baseClone[$removeChild](baseClone.occur); + baseClone.occur = null; } this._bindValue(formNode, matches[0], picture); @@ -412,9 +415,6 @@ class Binder { for (let i = 1, ii = matches.length; i < ii; i++) { const match = matches[i]; const clone = baseClone[$clone](); - clone.occur.min = 1; - clone.occur.max = 1; - clone.occur.initial = 1; parent[name].push(clone); parent[$insertAt](pos + i, clone); @@ -437,20 +437,39 @@ class Binder { const parent = formNode[$getParent](); const name = formNode[$nodeName]; - for (let i = 0, ii = occur.initial; i < ii; i++) { - const clone = formNode[$clone](); - clone.occur.min = 1; - clone.occur.max = 1; - clone.occur.initial = 1; - parent[name].push(clone); - parent[$appendChild](clone); + if (!(parent[name] instanceof XFAObjectArray)) { + return; + } + + let currentNumber; + if (formNode.name) { + currentNumber = parent[name].children.filter( + e => e.name === formNode.name + ).length; + } else { + currentNumber = parent[name].children.length; + } + + const pos = parent[$indexOf](formNode) + 1; + const ii = occur.initial - currentNumber; + if (ii) { + const nodeClone = formNode[$clone](); + nodeClone[$removeChild](nodeClone.occur); + nodeClone.occur = null; + parent[name].push(nodeClone); + parent[$insertAt](pos, nodeClone); + + for (let i = 1; i < ii; i++) { + const clone = nodeClone[$clone](); + parent[name].push(clone); + parent[$insertAt](pos + i, clone); + } } } _getOccurInfo(formNode) { - const { occur } = formNode; - const dataName = formNode.name; - if (!occur || !dataName) { + const { name, occur } = formNode; + if (!occur || !name) { return [1, 1]; } const max = occur.max === -1 ? Infinity : occur.max; @@ -629,12 +648,6 @@ class Binder { } if (match) { - if (match.length < min) { - warn( - `XFA - Must have at least ${min} occurrences: ${formNode[$nodeName]}.` - ); - continue; - } this._bindOccurrences(child, match, picture); } else if (min > 0) { this._setProperties(child, dataNode); diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 3dbb9844e..80fcd4071 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -3603,25 +3603,60 @@ class Occur extends XFAObject { constructor(attributes) { super(TEMPLATE_NS_ID, "occur", /* hasChildren = */ true); this.id = attributes.id || ""; - this.initial = getInteger({ - data: attributes.initial, - defaultValue: 1, - validate: x => true, - }); - this.max = getInteger({ - data: attributes.max, - defaultValue: 1, - validate: x => true, - }); - this.min = getInteger({ - data: attributes.min, - defaultValue: 1, - validate: x => true, - }); + this.initial = + attributes.initial !== "" + ? getInteger({ + data: attributes.initial, + defaultValue: "", + validate: x => true, + }) + : ""; + this.max = + attributes.max !== "" + ? getInteger({ + data: attributes.max, + defaultValue: 1, + validate: x => true, + }) + : ""; + this.min = + attributes.min !== "" + ? getInteger({ + data: attributes.min, + defaultValue: 1, + validate: x => true, + }) + : ""; this.use = attributes.use || ""; this.usehref = attributes.usehref || ""; this.extras = null; } + + [$clean]() { + const parent = this[$getParent](); + const originalMin = this.min; + + if (this.min === "") { + this.min = + parent instanceof PageArea || parent instanceof PageSet ? 0 : 1; + } + if (this.max === "") { + if (originalMin === "") { + this.max = + parent instanceof PageArea || parent instanceof PageSet ? -1 : 1; + } else { + this.max = this.min; + } + } + + if (this.max !== -1 && this.max < this.min) { + this.max = this.min; + } + + if (this.initial === "") { + this.initial = parent instanceof Template ? 1 : this.min; + } + } } class Oid extends StringObject { @@ -5705,6 +5740,7 @@ class Value extends XFAObject { if (parent.ui && parent.ui.imageEdit) { if (!this.image) { this.image = new Image({}); + this[$appendChild](this.image); } this.image[$content] = value[$content]; return; diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js index a2444a522..84193fa95 100644 --- a/src/core/xfa/xfa_object.js +++ b/src/core/xfa/xfa_object.js @@ -204,6 +204,9 @@ class XFAObject { [$appendChild](child) { child[_parent] = this; this[_children].push(child); + if (!child[$globalData] && this[$globalData]) { + child[$globalData] = this[$globalData]; + } } [$removeChild](child) { @@ -236,6 +239,9 @@ class XFAObject { [$insertAt](i, child) { child[_parent] = this; this[_children].splice(i, 0, child); + if (!child[$globalData] && this[$globalData]) { + child[$globalData] = this[$globalData]; + } } /** diff --git a/test/pdfs/xfa_bug1722029.pdf.link b/test/pdfs/xfa_bug1722029.pdf.link new file mode 100644 index 000000000..f392413b5 --- /dev/null +++ b/test/pdfs/xfa_bug1722029.pdf.link @@ -0,0 +1 @@ +https://bugzilla.mozilla.org/attachment.cgi?id=9233058 diff --git a/test/test_manifest.json b/test/test_manifest.json index e28e30361..3c6c77f70 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -952,6 +952,14 @@ "enableXfa": true, "type": "eq" }, + { "id": "xfa_bug1722029", + "file": "pdfs/xfa_bug1722029.pdf", + "md5": "d8dd6bb20599fc777b4c7ff7b74dd5e3", + "link": true, + "rounds": 1, + "enableXfa": true, + "type": "eq" + }, { "id": "xfa_bug1721600", "file": "pdfs/xfa_bug1721600.pdf", "md5": "5f514f476169eeb7254da380a8db495a",