diff --git a/src/core/xfa/html_utils.js b/src/core/xfa/html_utils.js
index 88a7c9f85..b645a5809 100644
--- a/src/core/xfa/html_utils.js
+++ b/src/core/xfa/html_utils.js
@@ -102,24 +102,12 @@ const converters = {
style.width = measureToString(width);
} else {
style.width = "auto";
- if (node.maxW > 0) {
- style.maxWidth = measureToString(node.maxW);
- }
- if (parent.layout === "position") {
- style.minWidth = measureToString(node.minW);
- }
}
if (height !== "") {
style.height = measureToString(height);
} else {
style.height = "auto";
- if (node.maxH > 0) {
- style.maxHeight = measureToString(node.maxH);
- }
- if (parent.layout === "position") {
- style.minHeight = measureToString(node.minH);
- }
}
},
position(node, style) {
@@ -188,6 +176,20 @@ const converters = {
},
};
+function setMinMaxDimensions(node, style) {
+ const parent = node[$getParent]();
+ if (parent.layout === "position") {
+ style.minWidth = measureToString(node.minW);
+ if (node.maxW) {
+ style.maxWidth = measureToString(node.maxW);
+ }
+ style.minHeight = measureToString(node.minH);
+ if (node.maxH) {
+ style.maxHeight = measureToString(node.maxH);
+ }
+ }
+}
+
function layoutText(text, xfaFont, fonts, width) {
const measure = new TextMeasure(xfaFont, fonts);
if (typeof text === "string") {
@@ -283,16 +285,9 @@ function fixDimensions(node) {
}
}
- if (node.layout === "position") {
- // Acrobat doesn't take into account min, max values
- // for containers with positioned layout (which makes sense).
- node.minW = node.minH = 0;
- node.maxW = node.maxH = Infinity;
- } else {
- if (node.layout === "table") {
- if (node.w === "" && Array.isArray(node.columnWidths)) {
- node.w = node.columnWidths.reduce((a, x) => a + x, 0);
- }
+ if (node.layout === "table") {
+ if (node.w === "" && Array.isArray(node.columnWidths)) {
+ node.w = node.columnWidths.reduce((a, x) => a + x, 0);
}
}
}
@@ -471,5 +466,6 @@ export {
layoutClass,
layoutText,
measureToString,
+ setMinMaxDimensions,
toStyle,
};
diff --git a/src/core/xfa/layout.js b/src/core/xfa/layout.js
index 79b9cbecc..ad12c70dc 100644
--- a/src/core/xfa/layout.js
+++ b/src/core/xfa/layout.js
@@ -13,7 +13,13 @@
* limitations under the License.
*/
-import { $extra, $flushHTML } from "./xfa_object.js";
+import {
+ $extra,
+ $flushHTML,
+ $getParent,
+ $getTemplateRoot,
+ $isSplittable,
+} from "./xfa_object.js";
import { measureToString } from "./html_utils.js";
// Subform and ExclGroup have a layout so they share these functions.
@@ -146,59 +152,181 @@ function addHTML(node, html, bbox) {
function getAvailableSpace(node) {
const availableSpace = node[$extra].availableSpace;
- const marginH = node.margin
+ const marginV = node.margin
? node.margin.topInset + node.margin.bottomInset
: 0;
+ const marginH = node.margin
+ ? node.margin.leftInset + node.margin.rightInset
+ : 0;
switch (node.layout) {
case "lr-tb":
case "rl-tb":
- switch (node[$extra].attempt) {
- case 0:
- return {
- width: availableSpace.width - node[$extra].currentWidth,
- height: availableSpace.height - marginH - node[$extra].prevHeight,
- };
- case 1:
- return {
- width: availableSpace.width,
- height: availableSpace.height - marginH - node[$extra].height,
- };
- default:
- // Overflow must stay in the container.
- return {
- width: Infinity,
- height: Infinity,
- };
- }
- case "rl-row":
- case "row":
- if (node[$extra].attempt === 0) {
- const width = node[$extra].columnWidths
- .slice(node[$extra].currentColumn)
- .reduce((a, x) => a + x);
- return { width, height: availableSpace.height - marginH };
- }
- // Overflow must stay in the container.
- return { width: Infinity, height: Infinity };
- case "table":
- case "tb":
if (node[$extra].attempt === 0) {
return {
- width: availableSpace.width,
- height: availableSpace.height - marginH - node[$extra].height,
+ width: availableSpace.width - marginH - node[$extra].currentWidth,
+ height: availableSpace.height - marginV - node[$extra].prevHeight,
};
}
- // Overflow must stay in the container.
- return { width: Infinity, height: Infinity };
+ return {
+ width: availableSpace.width - marginH,
+ height: availableSpace.height - marginV - node[$extra].height,
+ };
+ case "rl-row":
+ case "row":
+ const width = node[$extra].columnWidths
+ .slice(node[$extra].currentColumn)
+ .reduce((a, x) => a + x);
+ return { width, height: availableSpace.height - marginH };
+ case "table":
+ case "tb":
+ return {
+ width: availableSpace.width - marginH,
+ height: availableSpace.height - marginV - node[$extra].height,
+ };
case "position":
default:
- if (node[$extra].attempt === 0) {
- return availableSpace;
- }
- // Overflow must stay in the container.
- return { width: Infinity, height: Infinity };
+ return availableSpace;
}
}
-export { addHTML, flushHTML, getAvailableSpace };
+function getTransformedBBox(node) {
+ // Take into account rotation and anchor the get the
+ // real bounding box.
+ let w = node.w === "" ? NaN : node.w;
+ let h = node.h === "" ? NaN : node.h;
+ let [centerX, centerY] = [0, 0];
+ switch (node.anchorType || "") {
+ case "bottomCenter":
+ [centerX, centerY] = [w / 2, h];
+ break;
+ case "bottomLeft":
+ [centerX, centerY] = [0, h];
+ break;
+ case "bottomRight":
+ [centerX, centerY] = [w, h];
+ break;
+ case "middleCenter":
+ [centerX, centerY] = [w / 2, h / 2];
+ break;
+ case "middleLeft":
+ [centerX, centerY] = [0, h / 2];
+ break;
+ case "middleRight":
+ [centerX, centerY] = [w, h / 2];
+ break;
+ case "topCenter":
+ [centerX, centerY] = [w / 2, 0];
+ break;
+ case "topRight":
+ [centerX, centerY] = [w, 0];
+ break;
+ }
+
+ let x;
+ let y;
+ switch (node.rotate || 0) {
+ case 0:
+ [x, y] = [-centerX, -centerY];
+ break;
+ case 90:
+ [x, y] = [-centerY, centerX];
+ [w, h] = [h, -w];
+ break;
+ case 180:
+ [x, y] = [centerX, centerY];
+ [w, h] = [-w, -h];
+ break;
+ case 270:
+ [x, y] = [centerY, -centerX];
+ [w, h] = [-h, w];
+ break;
+ }
+
+ return [
+ node.x + x + Math.min(0, w),
+ node.y + y + Math.min(0, h),
+ Math.abs(w),
+ Math.abs(h),
+ ];
+}
+
+/**
+ * Returning true means that the node will be layed out
+ * else the layout will go to its next step (changing of line
+ * in case of lr-tb or changing content area...).
+ */
+function checkDimensions(node, space) {
+ if (node.w === 0 || node.h === 0) {
+ return true;
+ }
+
+ if (space.width <= 0 || space.height <= 0) {
+ return false;
+ }
+
+ const parent = node[$getParent]();
+ const attempt = (node[$extra] && node[$extra].attempt) || 0;
+ switch (parent.layout) {
+ case "lr-tb":
+ case "rl-tb":
+ switch (attempt) {
+ case 0: {
+ let w, h;
+ if (node.w !== "" || node.h !== "") {
+ [, , w, h] = getTransformedBBox(node);
+ }
+ if (node.h !== "" && Math.round(h - space.height) > 1) {
+ return false;
+ }
+ if (node.w !== "") {
+ return Math.round(w - space.width) <= 1;
+ }
+
+ return node.minW <= space.width;
+ }
+ case 1: {
+ if (node.h !== "" && !node[$isSplittable]()) {
+ const [, , , h] = getTransformedBBox(node);
+ if (Math.round(h - space.height) > 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+ default:
+ return true;
+ }
+ case "table":
+ case "tb":
+ if (attempt !== 1 && node.h !== "" && !node[$isSplittable]()) {
+ const [, , , h] = getTransformedBBox(node);
+ if (Math.round(h - space.height) > 1) {
+ return false;
+ }
+ }
+ return true;
+ case "position":
+ const [x, y, w, h] = getTransformedBBox(node);
+ const isWidthOk = node.w === "" || Math.round(w + x - space.width) <= 1;
+ const isHeightOk = node.h === "" || Math.round(h + y - space.height) <= 1;
+
+ if (isWidthOk && isHeightOk) {
+ return true;
+ }
+
+ const area = node[$getTemplateRoot]()[$extra].currentContentArea;
+ if (isWidthOk) {
+ return h + y > area.h;
+ }
+
+ return w + x > area.w;
+ case "rl-row":
+ case "row":
+ default:
+ // No layout, so accept everything.
+ return true;
+ }
+}
+
+export { addHTML, checkDimensions, flushHTML, getAvailableSpace };
diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js
index 8e57f894d..ba4f88c0a 100644
--- a/src/core/xfa/template.js
+++ b/src/core/xfa/template.js
@@ -30,10 +30,12 @@ import {
$getNextPage,
$getParent,
$getSubformParent,
+ $getTemplateRoot,
$hasItem,
$hasSettableValue,
$ids,
$isCDATAXml,
+ $isSplittable,
$isTransparent,
$namespaceId,
$nodeName,
@@ -55,7 +57,12 @@ import {
XFAObjectArray,
} from "./xfa_object.js";
import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
-import { addHTML, flushHTML, getAvailableSpace } from "./layout.js";
+import {
+ addHTML,
+ checkDimensions,
+ flushHTML,
+ getAvailableSpace,
+} from "./layout.js";
import {
computeBbox,
createWrapper,
@@ -65,6 +72,7 @@ import {
layoutClass,
layoutText,
measureToString,
+ setMinMaxDimensions,
toStyle,
} from "./html_utils.js";
import {
@@ -107,14 +115,6 @@ function _setValue(templateNode, value) {
templateNode.value[$setValue](value);
}
-function getRoot(node) {
- let parent = node[$getParent]();
- while (!(parent instanceof Template)) {
- parent = parent[$getParent]();
- }
- return parent;
-}
-
function* getContainedChildren(node) {
for (const child of node[$getChildren]()) {
if (child instanceof SubformSet) {
@@ -136,90 +136,6 @@ function valueToHtml(value) {
});
}
-function getTransformedBBox(node) {
- // Take into account rotation and anchor the get the
- // real bounding box.
- let w = node.w === "" ? NaN : node.w;
- let h = node.h === "" ? NaN : node.h;
- let [centerX, centerY] = [0, 0];
- switch (node.anchorType || "") {
- case "bottomCenter":
- [centerX, centerY] = [w / 2, h];
- break;
- case "bottomLeft":
- [centerX, centerY] = [0, h];
- break;
- case "bottomRight":
- [centerX, centerY] = [w, h];
- break;
- case "middleCenter":
- [centerX, centerY] = [w / 2, h / 2];
- break;
- case "middleLeft":
- [centerX, centerY] = [0, h / 2];
- break;
- case "middleRight":
- [centerX, centerY] = [w, h / 2];
- break;
- case "topCenter":
- [centerX, centerY] = [w / 2, 0];
- break;
- case "topRight":
- [centerX, centerY] = [w, 0];
- break;
- }
-
- let x;
- let y;
- switch (node.rotate || 0) {
- case 0:
- [x, y] = [-centerX, -centerY];
- break;
- case 90:
- [x, y] = [-centerY, centerX];
- [w, h] = [h, -w];
- break;
- case 180:
- [x, y] = [centerX, centerY];
- [w, h] = [-w, -h];
- break;
- case 270:
- [x, y] = [centerY, -centerX];
- [w, h] = [-h, w];
- break;
- }
-
- return [
- node.x + x + Math.min(0, w),
- node.y + y + Math.min(0, h),
- Math.abs(w),
- Math.abs(h),
- ];
-}
-
-const NOTHING = 0;
-const NOSPACE = 1;
-const VALID = 2;
-function checkDimensions(node, space) {
- if (node[$getParent]().layout === "position") {
- return VALID;
- }
- const [x, y, w, h] = getTransformedBBox(node);
- if (node.w === 0 || node.h === 0) {
- return VALID;
- }
-
- if (node.w !== "" && Math.round(x + w - space.width) > 1) {
- return NOSPACE;
- }
-
- if (node.h !== "" && Math.round(y + h - space.height) > 1) {
- return NOSPACE;
- }
-
- return VALID;
-}
-
class AppearanceFilter extends StringObject {
constructor(attributes) {
super(TEMPLATE_NS_ID, "appearanceFilter");
@@ -1525,7 +1441,7 @@ class Draw extends XFAObject {
if ((this.w === "" || this.h === "") && this.value) {
const maxWidth = this.w === "" ? availableSpace.width : this.w;
- const fonts = getRoot(this)[$fonts];
+ const fonts = this[$getTemplateRoot]()[$fonts];
let font = this.font;
if (!font) {
let parent = this[$getParent]();
@@ -1571,13 +1487,8 @@ class Draw extends XFAObject {
}
}
- switch (checkDimensions(this, availableSpace)) {
- case NOTHING:
- return HTMLResult.EMPTY;
- case NOSPACE:
- return HTMLResult.FAILURE;
- default:
- break;
+ if (!checkDimensions(this, availableSpace)) {
+ return HTMLResult.FAILURE;
}
const style = toStyle(
@@ -1593,6 +1504,8 @@ class Draw extends XFAObject {
"margin"
);
+ setMinMaxDimensions(this, style);
+
const classNames = ["xfaDraw"];
if (this.font) {
classNames.push("xfaFont");
@@ -2062,6 +1975,27 @@ class ExclGroup extends XFAObject {
}
}
+ [$isSplittable]() {
+ // We cannot cache the result here because the contentArea
+ // can change.
+ const root = this[$getTemplateRoot]();
+ const contentArea = root[$extra].currentContentArea;
+ if (contentArea && Math.max(this.minH, this.h || 0) >= contentArea.h) {
+ return true;
+ }
+
+ if (this.layout === "position") {
+ return false;
+ }
+
+ const parentLayout = this[$getParent]().layout;
+ if (parentLayout && parentLayout.includes("row")) {
+ return false;
+ }
+
+ return true;
+ }
+
[$flushHTML]() {
return flushHTML(this);
}
@@ -2106,13 +2040,8 @@ class ExclGroup extends XFAObject {
currentWidth: 0,
});
- switch (checkDimensions(this, availableSpace)) {
- case NOTHING:
- return HTMLResult.EMPTY;
- case NOSPACE:
- return HTMLResult.FAILURE;
- default:
- break;
+ if (!checkDimensions(this, availableSpace)) {
+ return HTMLResult.FAILURE;
}
availableSpace = {
@@ -2172,7 +2101,7 @@ class ExclGroup extends XFAObject {
}
}
- failure = this[$extra].attempt === 2;
+ failure = this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT;
} else {
const result = this[$childrenToHTML]({
filter,
@@ -2185,12 +2114,16 @@ class ExclGroup extends XFAObject {
}
if (failure) {
- if (this.layout === "position") {
+ if (this[$isSplittable]()) {
delete this[$extra];
}
return HTMLResult.FAILURE;
}
+ if (children.length === 0) {
+ return HTMLResult.EMPTY;
+ }
+
let marginH = 0;
let marginV = 0;
if (this.margin) {
@@ -2198,11 +2131,15 @@ class ExclGroup extends XFAObject {
marginV = this.margin.topInset + this.margin.bottomInset;
}
+ const width = Math.max(this[$extra].width + marginH, this.w || 0);
+ const height = Math.max(this[$extra].height + marginV, this.h || 0);
+ const bbox = [this.x, this.y, width, height];
+
if (this.w === "") {
- style.width = measureToString(this[$extra].width + marginH);
+ style.width = measureToString(width);
}
if (this.h === "") {
- style.height = measureToString(this[$extra].height + marginV);
+ style.height = measureToString(height);
}
const html = {
@@ -2211,16 +2148,6 @@ class ExclGroup extends XFAObject {
children,
};
- let bbox;
- if (this.w !== "" && this.h !== "") {
- bbox = [this.x, this.y, this.w, this.h];
- } else {
- const width = this.w === "" ? marginH + this[$extra].width : this.w;
- const height = this.h === "" ? marginV + this[$extra].height : this.h;
-
- bbox = [this.x, this.y, width, height];
- }
-
delete this[$extra];
return HTMLResult.success(createWrapper(this, html), bbox);
@@ -2372,13 +2299,8 @@ class Field extends XFAObject {
fixDimensions(this);
- switch (checkDimensions(this, availableSpace)) {
- case NOTHING:
- return HTMLResult.EMPTY;
- case NOSPACE:
- return HTMLResult.FAILURE;
- default:
- break;
+ if (!checkDimensions(this, availableSpace)) {
+ return HTMLResult.FAILURE;
}
const style = toStyle(
@@ -2393,6 +2315,8 @@ class Field extends XFAObject {
"hAlign"
);
+ setMinMaxDimensions(this, style);
+
const classNames = ["xfaField"];
// If no font, font properties are inherited.
if (this.font) {
@@ -3374,7 +3298,7 @@ class PageArea extends XFAObject {
}
[$getAvailableSpace]() {
- return { width: Infinity, height: Infinity };
+ return this[$extra].space || { width: 0, height: 0 };
}
[$toHTML]() {
@@ -3392,10 +3316,18 @@ class PageArea extends XFAObject {
if (this.medium && this.medium.short && this.medium.long) {
style.width = measureToString(this.medium.short);
style.height = measureToString(this.medium.long);
+ this[$extra].space = {
+ width: this.medium.short,
+ height: this.medium.long,
+ };
if (this.medium.orientation === "landscape") {
const x = style.width;
style.width = style.height;
style.height = x;
+ this[$extra].space = {
+ width: this.medium.long,
+ height: this.medium.short,
+ };
}
} else {
warn("XFA - No medium specified in pageArea: please file a bug.");
@@ -3486,7 +3418,7 @@ class PageSet extends XFAObject {
return this[$getNextPage]();
}
- const pageNumber = getRoot(this)[$extra].pageNumber;
+ const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber;
const parity = pageNumber % 2 === 0 ? "even" : "odd";
const position = pageNumber === 0 ? "first" : "rest";
@@ -4175,6 +4107,36 @@ class Subform extends XFAObject {
return getAvailableSpace(this);
}
+ [$isSplittable](x) {
+ // We cannot cache the result here because the contentArea
+ // can change.
+ const root = this[$getTemplateRoot]();
+ const contentArea = root[$extra].currentContentArea;
+ if (contentArea && Math.max(this.minH, this.h || 0) >= contentArea.h) {
+ return true;
+ }
+
+ if (this.layout === "position") {
+ return false;
+ }
+
+ if (this.keep && this.keep.intact !== "none") {
+ return false;
+ }
+
+ const parentLayout = this[$getParent]().layout;
+ if (parentLayout && parentLayout.includes("row")) {
+ return false;
+ }
+
+ if (this.overflow && this.overflow.target) {
+ const target = root[$searchNode](this.overflow.target, this);
+ return target && target[0] === contentArea;
+ }
+
+ return true;
+ }
+
[$toHTML](availableSpace) {
if (this.presence === "hidden" || this.presence === "inactive") {
return HTMLResult.EMPTY;
@@ -4201,9 +4163,7 @@ class Subform extends XFAObject {
}
if (this[$extra] && this[$extra].afterBreakAfter) {
- const result = this[$extra].afterBreakAfter;
- delete this[$extra];
- return result;
+ return HTMLResult.EMPTY;
}
// TODO: incomplete.
@@ -4228,21 +4188,8 @@ class Subform extends XFAObject {
currentWidth: 0,
});
- switch (checkDimensions(this, availableSpace)) {
- case NOTHING:
- return HTMLResult.EMPTY;
- case NOSPACE:
- return HTMLResult.FAILURE;
- default:
- break;
- }
-
- let noBreakOnOverflow = false;
- if (this.overflow && this.overflow.target) {
- const root = getRoot(this);
- const target = root[$searchNode](this.overflow.target, this);
- noBreakOnOverflow =
- target && target[0] === root[$extra].currentContentArea;
+ if (!checkDimensions(this, availableSpace)) {
+ return HTMLResult.FAILURE;
}
const filter = new Set([
@@ -4285,6 +4232,8 @@ class Subform extends XFAObject {
attributes.xfaName = this.name;
}
+ const isSplittable = this[$isSplittable]();
+
// If the container overflows into itself we add an extra
// layout step to accept finally the element which caused
// the overflow.
@@ -4292,7 +4241,7 @@ class Subform extends XFAObject {
this.layout === "lr-tb" || this.layout === "rl-tb"
? MAX_ATTEMPTS_FOR_LRTB_LAYOUT
: 1;
- maxRun += noBreakOnOverflow ? 1 : 0;
+ maxRun += !isSplittable && this.layout !== "position" ? 1 : 0;
for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
const result = this[$childrenToHTML]({
filter,
@@ -4308,15 +4257,21 @@ class Subform extends XFAObject {
if (this[$extra].attempt === maxRun) {
if (this.overflow) {
- getRoot(this)[$extra].overflowNode = this.overflow;
+ this[$getTemplateRoot]()[$extra].overflowNode = this.overflow;
}
- if (this.layout === "position") {
+ if (!isSplittable) {
+ // Since a new try will happen in a new container with maybe
+ // new dimensions, we invalidate already layed out components.
delete this[$extra];
}
return HTMLResult.FAILURE;
}
+ if (children.length === 0) {
+ return HTMLResult.EMPTY;
+ }
+
let marginH = 0;
let marginV = 0;
if (this.margin) {
@@ -4324,11 +4279,15 @@ class Subform extends XFAObject {
marginV = this.margin.topInset + this.margin.bottomInset;
}
+ const width = Math.max(this[$extra].width + marginH, this.w || 0);
+ const height = Math.max(this[$extra].height + marginV, this.h || 0);
+ const bbox = [this.x, this.y, width, height];
+
if (this.w === "") {
- style.width = measureToString(this[$extra].width + marginH);
+ style.width = measureToString(width);
}
if (this.h === "") {
- style.height = measureToString(this[$extra].height + marginV);
+ style.height = measureToString(height);
}
const html = {
@@ -4337,16 +4296,6 @@ class Subform extends XFAObject {
children,
};
- let bbox;
- if (this.w !== "" && this.h !== "") {
- bbox = [this.x, this.y, this.w, this.h];
- } else {
- const width = this.w === "" ? marginH + this[$extra].width : this.w;
- const height = this.h === "" ? marginV + this[$extra].height : this.h;
-
- bbox = [this.x, this.y, width, height];
- }
-
const result = HTMLResult.success(createWrapper(this, html), bbox);
if (this.breakAfter.children.length >= 1) {
@@ -4605,12 +4554,12 @@ class Template extends XFAObject {
mainHtml.children.push(page);
if (leader) {
- page.children.push(leader[$toHTML](page[$extra].space).html);
+ page.children.push(leader[$toHTML](pageArea[$extra].space).html);
leader = null;
}
if (trailer) {
- page.children.push(trailer[$toHTML](page[$extra].space).html);
+ page.children.push(trailer[$toHTML](pageArea[$extra].space).html);
trailer = null;
}
@@ -4646,7 +4595,10 @@ class Template extends XFAObject {
const html = root[$toHTML](space);
if (html.success) {
if (html.html) {
+ hasSomething = true;
htmlContentAreas[i].children.push(html.html);
+ } else if (!hasSomething) {
+ mainHtml.children.pop();
}
return mainHtml;
}
diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js
index 1ed91cd1c..c59552abd 100644
--- a/src/core/xfa/xfa_object.js
+++ b/src/core/xfa/xfa_object.js
@@ -47,7 +47,7 @@ const $getContainedChildren = Symbol();
const $getNextPage = Symbol();
const $getSubformParent = Symbol();
const $getParent = Symbol();
-const $pushGlyphs = Symbol();
+const $getTemplateRoot = Symbol();
const $global = Symbol();
const $hasItem = Symbol();
const $hasSettableValue = Symbol();
@@ -57,6 +57,7 @@ const $insertAt = Symbol();
const $isCDATAXml = Symbol();
const $isDataValue = Symbol();
const $isDescendent = Symbol();
+const $isSplittable = Symbol();
const $isTransparent = Symbol();
const $lastAttribute = Symbol();
const $namespaceId = Symbol("namespaceId");
@@ -65,6 +66,7 @@ const $nsAttributes = Symbol();
const $onChild = Symbol();
const $onChildCheck = Symbol();
const $onText = Symbol();
+const $pushGlyphs = Symbol();
const $removeChild = Symbol();
const $root = Symbol("root");
const $resolvePrototypes = Symbol();
@@ -162,6 +164,18 @@ class XFAObject {
}
}
+ [$getTemplateRoot]() {
+ let parent = this[$getParent]();
+ while (parent[$nodeName] !== "template") {
+ parent = parent[$getParent]();
+ }
+ return parent;
+ }
+
+ [$isSplittable]() {
+ return false;
+ }
+
[$appendChild](child) {
child[_parent] = this;
this[_children].push(child);
@@ -985,6 +999,7 @@ export {
$getParent,
$getRealChildrenByNameIt,
$getSubformParent,
+ $getTemplateRoot,
$global,
$hasItem,
$hasSettableValue,
@@ -994,6 +1009,7 @@ export {
$isCDATAXml,
$isDataValue,
$isDescendent,
+ $isSplittable,
$isTransparent,
$namespaceId,
$nodeName,
diff --git a/test/pdfs/xfa_candidate_petitions.pdf.link b/test/pdfs/xfa_candidate_petitions.pdf.link
new file mode 100644
index 000000000..34a4ca1a2
--- /dev/null
+++ b/test/pdfs/xfa_candidate_petitions.pdf.link
@@ -0,0 +1 @@
+https://web.archive.org/web/20210509141453/https://www.sos.state.oh.us/globalassets/elections/directives/2020/dir2020-15_independentcandidatepetitions2020signedextended.pdf
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 67d4549f5..66f2b817a 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -946,6 +946,14 @@
"enableXfa": true,
"type": "eq"
},
+ { "id": "xfa_candidate_petitions",
+ "file": "pdfs/xfa_candidate_petitions.pdf",
+ "md5": "0db96a00667f8f58f94cf81022e69341",
+ "link": true,
+ "rounds": 1,
+ "enableXfa": true,
+ "type": "eq"
+ },
{ "id": "xfa_annual_expense_report",
"file": "pdfs/xfa_annual_expense_report.pdf",
"md5": "06866e7a6bbc0346789208ef5f6e885c",
diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js
index bd3959413..8e5de87eb 100644
--- a/test/unit/xfa_tohtml_spec.js
+++ b/test/unit/xfa_tohtml_spec.js
@@ -60,6 +60,9 @@ describe("XFAFactory", function () {
+
+ foo
+
@@ -133,7 +136,7 @@ describe("XFAFactory", function () {
-
+
@@ -146,6 +149,7 @@ describe("XFAFactory", function () {
+ foo