XFA - Fix layout issues
- PR #13554 is buggy, so this patch aims to fix bugs. - check if a component fits into its parent in taking into account the parent layout. - introduce method isSplittable for template nodes to know if a component can be splitted in case of overflow.
This commit is contained in:
parent
326226df45
commit
df08b1548b
@ -102,24 +102,12 @@ const converters = {
|
|||||||
style.width = measureToString(width);
|
style.width = measureToString(width);
|
||||||
} else {
|
} else {
|
||||||
style.width = "auto";
|
style.width = "auto";
|
||||||
if (node.maxW > 0) {
|
|
||||||
style.maxWidth = measureToString(node.maxW);
|
|
||||||
}
|
|
||||||
if (parent.layout === "position") {
|
|
||||||
style.minWidth = measureToString(node.minW);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height !== "") {
|
if (height !== "") {
|
||||||
style.height = measureToString(height);
|
style.height = measureToString(height);
|
||||||
} else {
|
} else {
|
||||||
style.height = "auto";
|
style.height = "auto";
|
||||||
if (node.maxH > 0) {
|
|
||||||
style.maxHeight = measureToString(node.maxH);
|
|
||||||
}
|
|
||||||
if (parent.layout === "position") {
|
|
||||||
style.minHeight = measureToString(node.minH);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
position(node, style) {
|
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) {
|
function layoutText(text, xfaFont, fonts, width) {
|
||||||
const measure = new TextMeasure(xfaFont, fonts);
|
const measure = new TextMeasure(xfaFont, fonts);
|
||||||
if (typeof text === "string") {
|
if (typeof text === "string") {
|
||||||
@ -283,16 +285,9 @@ function fixDimensions(node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.layout === "position") {
|
if (node.layout === "table") {
|
||||||
// Acrobat doesn't take into account min, max values
|
if (node.w === "" && Array.isArray(node.columnWidths)) {
|
||||||
// for containers with positioned layout (which makes sense).
|
node.w = node.columnWidths.reduce((a, x) => a + x, 0);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,5 +466,6 @@ export {
|
|||||||
layoutClass,
|
layoutClass,
|
||||||
layoutText,
|
layoutText,
|
||||||
measureToString,
|
measureToString,
|
||||||
|
setMinMaxDimensions,
|
||||||
toStyle,
|
toStyle,
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,13 @@
|
|||||||
* limitations under the License.
|
* 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";
|
import { measureToString } from "./html_utils.js";
|
||||||
|
|
||||||
// Subform and ExclGroup have a layout so they share these functions.
|
// Subform and ExclGroup have a layout so they share these functions.
|
||||||
@ -146,59 +152,181 @@ function addHTML(node, html, bbox) {
|
|||||||
|
|
||||||
function getAvailableSpace(node) {
|
function getAvailableSpace(node) {
|
||||||
const availableSpace = node[$extra].availableSpace;
|
const availableSpace = node[$extra].availableSpace;
|
||||||
const marginH = node.margin
|
const marginV = node.margin
|
||||||
? node.margin.topInset + node.margin.bottomInset
|
? node.margin.topInset + node.margin.bottomInset
|
||||||
: 0;
|
: 0;
|
||||||
|
const marginH = node.margin
|
||||||
|
? node.margin.leftInset + node.margin.rightInset
|
||||||
|
: 0;
|
||||||
|
|
||||||
switch (node.layout) {
|
switch (node.layout) {
|
||||||
case "lr-tb":
|
case "lr-tb":
|
||||||
case "rl-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) {
|
if (node[$extra].attempt === 0) {
|
||||||
return {
|
return {
|
||||||
width: availableSpace.width,
|
width: availableSpace.width - marginH - node[$extra].currentWidth,
|
||||||
height: availableSpace.height - marginH - node[$extra].height,
|
height: availableSpace.height - marginV - node[$extra].prevHeight,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Overflow must stay in the container.
|
return {
|
||||||
return { width: Infinity, height: Infinity };
|
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":
|
case "position":
|
||||||
default:
|
default:
|
||||||
if (node[$extra].attempt === 0) {
|
return availableSpace;
|
||||||
return availableSpace;
|
|
||||||
}
|
|
||||||
// Overflow must stay in the container.
|
|
||||||
return { width: Infinity, height: Infinity };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 };
|
||||||
|
@ -30,10 +30,12 @@ import {
|
|||||||
$getNextPage,
|
$getNextPage,
|
||||||
$getParent,
|
$getParent,
|
||||||
$getSubformParent,
|
$getSubformParent,
|
||||||
|
$getTemplateRoot,
|
||||||
$hasItem,
|
$hasItem,
|
||||||
$hasSettableValue,
|
$hasSettableValue,
|
||||||
$ids,
|
$ids,
|
||||||
$isCDATAXml,
|
$isCDATAXml,
|
||||||
|
$isSplittable,
|
||||||
$isTransparent,
|
$isTransparent,
|
||||||
$namespaceId,
|
$namespaceId,
|
||||||
$nodeName,
|
$nodeName,
|
||||||
@ -55,7 +57,12 @@ import {
|
|||||||
XFAObjectArray,
|
XFAObjectArray,
|
||||||
} from "./xfa_object.js";
|
} from "./xfa_object.js";
|
||||||
import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
|
import { $buildXFAObject, NamespaceIds } from "./namespaces.js";
|
||||||
import { addHTML, flushHTML, getAvailableSpace } from "./layout.js";
|
import {
|
||||||
|
addHTML,
|
||||||
|
checkDimensions,
|
||||||
|
flushHTML,
|
||||||
|
getAvailableSpace,
|
||||||
|
} from "./layout.js";
|
||||||
import {
|
import {
|
||||||
computeBbox,
|
computeBbox,
|
||||||
createWrapper,
|
createWrapper,
|
||||||
@ -65,6 +72,7 @@ import {
|
|||||||
layoutClass,
|
layoutClass,
|
||||||
layoutText,
|
layoutText,
|
||||||
measureToString,
|
measureToString,
|
||||||
|
setMinMaxDimensions,
|
||||||
toStyle,
|
toStyle,
|
||||||
} from "./html_utils.js";
|
} from "./html_utils.js";
|
||||||
import {
|
import {
|
||||||
@ -107,14 +115,6 @@ function _setValue(templateNode, value) {
|
|||||||
templateNode.value[$setValue](value);
|
templateNode.value[$setValue](value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoot(node) {
|
|
||||||
let parent = node[$getParent]();
|
|
||||||
while (!(parent instanceof Template)) {
|
|
||||||
parent = parent[$getParent]();
|
|
||||||
}
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function* getContainedChildren(node) {
|
function* getContainedChildren(node) {
|
||||||
for (const child of node[$getChildren]()) {
|
for (const child of node[$getChildren]()) {
|
||||||
if (child instanceof SubformSet) {
|
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 {
|
class AppearanceFilter extends StringObject {
|
||||||
constructor(attributes) {
|
constructor(attributes) {
|
||||||
super(TEMPLATE_NS_ID, "appearanceFilter");
|
super(TEMPLATE_NS_ID, "appearanceFilter");
|
||||||
@ -1525,7 +1441,7 @@ class Draw extends XFAObject {
|
|||||||
|
|
||||||
if ((this.w === "" || this.h === "") && this.value) {
|
if ((this.w === "" || this.h === "") && this.value) {
|
||||||
const maxWidth = this.w === "" ? availableSpace.width : this.w;
|
const maxWidth = this.w === "" ? availableSpace.width : this.w;
|
||||||
const fonts = getRoot(this)[$fonts];
|
const fonts = this[$getTemplateRoot]()[$fonts];
|
||||||
let font = this.font;
|
let font = this.font;
|
||||||
if (!font) {
|
if (!font) {
|
||||||
let parent = this[$getParent]();
|
let parent = this[$getParent]();
|
||||||
@ -1571,13 +1487,8 @@ class Draw extends XFAObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (checkDimensions(this, availableSpace)) {
|
if (!checkDimensions(this, availableSpace)) {
|
||||||
case NOTHING:
|
return HTMLResult.FAILURE;
|
||||||
return HTMLResult.EMPTY;
|
|
||||||
case NOSPACE:
|
|
||||||
return HTMLResult.FAILURE;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = toStyle(
|
const style = toStyle(
|
||||||
@ -1593,6 +1504,8 @@ class Draw extends XFAObject {
|
|||||||
"margin"
|
"margin"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setMinMaxDimensions(this, style);
|
||||||
|
|
||||||
const classNames = ["xfaDraw"];
|
const classNames = ["xfaDraw"];
|
||||||
if (this.font) {
|
if (this.font) {
|
||||||
classNames.push("xfaFont");
|
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]() {
|
[$flushHTML]() {
|
||||||
return flushHTML(this);
|
return flushHTML(this);
|
||||||
}
|
}
|
||||||
@ -2106,13 +2040,8 @@ class ExclGroup extends XFAObject {
|
|||||||
currentWidth: 0,
|
currentWidth: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (checkDimensions(this, availableSpace)) {
|
if (!checkDimensions(this, availableSpace)) {
|
||||||
case NOTHING:
|
return HTMLResult.FAILURE;
|
||||||
return HTMLResult.EMPTY;
|
|
||||||
case NOSPACE:
|
|
||||||
return HTMLResult.FAILURE;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
availableSpace = {
|
availableSpace = {
|
||||||
@ -2172,7 +2101,7 @@ class ExclGroup extends XFAObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
failure = this[$extra].attempt === 2;
|
failure = this[$extra].attempt === MAX_ATTEMPTS_FOR_LRTB_LAYOUT;
|
||||||
} else {
|
} else {
|
||||||
const result = this[$childrenToHTML]({
|
const result = this[$childrenToHTML]({
|
||||||
filter,
|
filter,
|
||||||
@ -2185,12 +2114,16 @@ class ExclGroup extends XFAObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (failure) {
|
if (failure) {
|
||||||
if (this.layout === "position") {
|
if (this[$isSplittable]()) {
|
||||||
delete this[$extra];
|
delete this[$extra];
|
||||||
}
|
}
|
||||||
return HTMLResult.FAILURE;
|
return HTMLResult.FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (children.length === 0) {
|
||||||
|
return HTMLResult.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
let marginH = 0;
|
let marginH = 0;
|
||||||
let marginV = 0;
|
let marginV = 0;
|
||||||
if (this.margin) {
|
if (this.margin) {
|
||||||
@ -2198,11 +2131,15 @@ class ExclGroup extends XFAObject {
|
|||||||
marginV = this.margin.topInset + this.margin.bottomInset;
|
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 === "") {
|
if (this.w === "") {
|
||||||
style.width = measureToString(this[$extra].width + marginH);
|
style.width = measureToString(width);
|
||||||
}
|
}
|
||||||
if (this.h === "") {
|
if (this.h === "") {
|
||||||
style.height = measureToString(this[$extra].height + marginV);
|
style.height = measureToString(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = {
|
const html = {
|
||||||
@ -2211,16 +2148,6 @@ class ExclGroup extends XFAObject {
|
|||||||
children,
|
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];
|
delete this[$extra];
|
||||||
|
|
||||||
return HTMLResult.success(createWrapper(this, html), bbox);
|
return HTMLResult.success(createWrapper(this, html), bbox);
|
||||||
@ -2372,13 +2299,8 @@ class Field extends XFAObject {
|
|||||||
|
|
||||||
fixDimensions(this);
|
fixDimensions(this);
|
||||||
|
|
||||||
switch (checkDimensions(this, availableSpace)) {
|
if (!checkDimensions(this, availableSpace)) {
|
||||||
case NOTHING:
|
return HTMLResult.FAILURE;
|
||||||
return HTMLResult.EMPTY;
|
|
||||||
case NOSPACE:
|
|
||||||
return HTMLResult.FAILURE;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = toStyle(
|
const style = toStyle(
|
||||||
@ -2393,6 +2315,8 @@ class Field extends XFAObject {
|
|||||||
"hAlign"
|
"hAlign"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setMinMaxDimensions(this, style);
|
||||||
|
|
||||||
const classNames = ["xfaField"];
|
const classNames = ["xfaField"];
|
||||||
// If no font, font properties are inherited.
|
// If no font, font properties are inherited.
|
||||||
if (this.font) {
|
if (this.font) {
|
||||||
@ -3374,7 +3298,7 @@ class PageArea extends XFAObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[$getAvailableSpace]() {
|
[$getAvailableSpace]() {
|
||||||
return { width: Infinity, height: Infinity };
|
return this[$extra].space || { width: 0, height: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
[$toHTML]() {
|
[$toHTML]() {
|
||||||
@ -3392,10 +3316,18 @@ class PageArea extends XFAObject {
|
|||||||
if (this.medium && this.medium.short && this.medium.long) {
|
if (this.medium && this.medium.short && this.medium.long) {
|
||||||
style.width = measureToString(this.medium.short);
|
style.width = measureToString(this.medium.short);
|
||||||
style.height = measureToString(this.medium.long);
|
style.height = measureToString(this.medium.long);
|
||||||
|
this[$extra].space = {
|
||||||
|
width: this.medium.short,
|
||||||
|
height: this.medium.long,
|
||||||
|
};
|
||||||
if (this.medium.orientation === "landscape") {
|
if (this.medium.orientation === "landscape") {
|
||||||
const x = style.width;
|
const x = style.width;
|
||||||
style.width = style.height;
|
style.width = style.height;
|
||||||
style.height = x;
|
style.height = x;
|
||||||
|
this[$extra].space = {
|
||||||
|
width: this.medium.long,
|
||||||
|
height: this.medium.short,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn("XFA - No medium specified in pageArea: please file a bug.");
|
warn("XFA - No medium specified in pageArea: please file a bug.");
|
||||||
@ -3486,7 +3418,7 @@ class PageSet extends XFAObject {
|
|||||||
|
|
||||||
return this[$getNextPage]();
|
return this[$getNextPage]();
|
||||||
}
|
}
|
||||||
const pageNumber = getRoot(this)[$extra].pageNumber;
|
const pageNumber = this[$getTemplateRoot]()[$extra].pageNumber;
|
||||||
const parity = pageNumber % 2 === 0 ? "even" : "odd";
|
const parity = pageNumber % 2 === 0 ? "even" : "odd";
|
||||||
const position = pageNumber === 0 ? "first" : "rest";
|
const position = pageNumber === 0 ? "first" : "rest";
|
||||||
|
|
||||||
@ -4175,6 +4107,36 @@ class Subform extends XFAObject {
|
|||||||
return getAvailableSpace(this);
|
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) {
|
[$toHTML](availableSpace) {
|
||||||
if (this.presence === "hidden" || this.presence === "inactive") {
|
if (this.presence === "hidden" || this.presence === "inactive") {
|
||||||
return HTMLResult.EMPTY;
|
return HTMLResult.EMPTY;
|
||||||
@ -4201,9 +4163,7 @@ class Subform extends XFAObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this[$extra] && this[$extra].afterBreakAfter) {
|
if (this[$extra] && this[$extra].afterBreakAfter) {
|
||||||
const result = this[$extra].afterBreakAfter;
|
return HTMLResult.EMPTY;
|
||||||
delete this[$extra];
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: incomplete.
|
// TODO: incomplete.
|
||||||
@ -4228,21 +4188,8 @@ class Subform extends XFAObject {
|
|||||||
currentWidth: 0,
|
currentWidth: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (checkDimensions(this, availableSpace)) {
|
if (!checkDimensions(this, availableSpace)) {
|
||||||
case NOTHING:
|
return HTMLResult.FAILURE;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = new Set([
|
const filter = new Set([
|
||||||
@ -4285,6 +4232,8 @@ class Subform extends XFAObject {
|
|||||||
attributes.xfaName = this.name;
|
attributes.xfaName = this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isSplittable = this[$isSplittable]();
|
||||||
|
|
||||||
// If the container overflows into itself we add an extra
|
// If the container overflows into itself we add an extra
|
||||||
// layout step to accept finally the element which caused
|
// layout step to accept finally the element which caused
|
||||||
// the overflow.
|
// the overflow.
|
||||||
@ -4292,7 +4241,7 @@ class Subform extends XFAObject {
|
|||||||
this.layout === "lr-tb" || this.layout === "rl-tb"
|
this.layout === "lr-tb" || this.layout === "rl-tb"
|
||||||
? MAX_ATTEMPTS_FOR_LRTB_LAYOUT
|
? MAX_ATTEMPTS_FOR_LRTB_LAYOUT
|
||||||
: 1;
|
: 1;
|
||||||
maxRun += noBreakOnOverflow ? 1 : 0;
|
maxRun += !isSplittable && this.layout !== "position" ? 1 : 0;
|
||||||
for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
|
for (; this[$extra].attempt < maxRun; this[$extra].attempt++) {
|
||||||
const result = this[$childrenToHTML]({
|
const result = this[$childrenToHTML]({
|
||||||
filter,
|
filter,
|
||||||
@ -4308,15 +4257,21 @@ class Subform extends XFAObject {
|
|||||||
|
|
||||||
if (this[$extra].attempt === maxRun) {
|
if (this[$extra].attempt === maxRun) {
|
||||||
if (this.overflow) {
|
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];
|
delete this[$extra];
|
||||||
}
|
}
|
||||||
return HTMLResult.FAILURE;
|
return HTMLResult.FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (children.length === 0) {
|
||||||
|
return HTMLResult.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
let marginH = 0;
|
let marginH = 0;
|
||||||
let marginV = 0;
|
let marginV = 0;
|
||||||
if (this.margin) {
|
if (this.margin) {
|
||||||
@ -4324,11 +4279,15 @@ class Subform extends XFAObject {
|
|||||||
marginV = this.margin.topInset + this.margin.bottomInset;
|
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 === "") {
|
if (this.w === "") {
|
||||||
style.width = measureToString(this[$extra].width + marginH);
|
style.width = measureToString(width);
|
||||||
}
|
}
|
||||||
if (this.h === "") {
|
if (this.h === "") {
|
||||||
style.height = measureToString(this[$extra].height + marginV);
|
style.height = measureToString(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = {
|
const html = {
|
||||||
@ -4337,16 +4296,6 @@ class Subform extends XFAObject {
|
|||||||
children,
|
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);
|
const result = HTMLResult.success(createWrapper(this, html), bbox);
|
||||||
|
|
||||||
if (this.breakAfter.children.length >= 1) {
|
if (this.breakAfter.children.length >= 1) {
|
||||||
@ -4605,12 +4554,12 @@ class Template extends XFAObject {
|
|||||||
mainHtml.children.push(page);
|
mainHtml.children.push(page);
|
||||||
|
|
||||||
if (leader) {
|
if (leader) {
|
||||||
page.children.push(leader[$toHTML](page[$extra].space).html);
|
page.children.push(leader[$toHTML](pageArea[$extra].space).html);
|
||||||
leader = null;
|
leader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trailer) {
|
if (trailer) {
|
||||||
page.children.push(trailer[$toHTML](page[$extra].space).html);
|
page.children.push(trailer[$toHTML](pageArea[$extra].space).html);
|
||||||
trailer = null;
|
trailer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4646,7 +4595,10 @@ class Template extends XFAObject {
|
|||||||
const html = root[$toHTML](space);
|
const html = root[$toHTML](space);
|
||||||
if (html.success) {
|
if (html.success) {
|
||||||
if (html.html) {
|
if (html.html) {
|
||||||
|
hasSomething = true;
|
||||||
htmlContentAreas[i].children.push(html.html);
|
htmlContentAreas[i].children.push(html.html);
|
||||||
|
} else if (!hasSomething) {
|
||||||
|
mainHtml.children.pop();
|
||||||
}
|
}
|
||||||
return mainHtml;
|
return mainHtml;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ const $getContainedChildren = Symbol();
|
|||||||
const $getNextPage = Symbol();
|
const $getNextPage = Symbol();
|
||||||
const $getSubformParent = Symbol();
|
const $getSubformParent = Symbol();
|
||||||
const $getParent = Symbol();
|
const $getParent = Symbol();
|
||||||
const $pushGlyphs = Symbol();
|
const $getTemplateRoot = Symbol();
|
||||||
const $global = Symbol();
|
const $global = Symbol();
|
||||||
const $hasItem = Symbol();
|
const $hasItem = Symbol();
|
||||||
const $hasSettableValue = Symbol();
|
const $hasSettableValue = Symbol();
|
||||||
@ -57,6 +57,7 @@ const $insertAt = Symbol();
|
|||||||
const $isCDATAXml = Symbol();
|
const $isCDATAXml = Symbol();
|
||||||
const $isDataValue = Symbol();
|
const $isDataValue = Symbol();
|
||||||
const $isDescendent = Symbol();
|
const $isDescendent = Symbol();
|
||||||
|
const $isSplittable = Symbol();
|
||||||
const $isTransparent = Symbol();
|
const $isTransparent = Symbol();
|
||||||
const $lastAttribute = Symbol();
|
const $lastAttribute = Symbol();
|
||||||
const $namespaceId = Symbol("namespaceId");
|
const $namespaceId = Symbol("namespaceId");
|
||||||
@ -65,6 +66,7 @@ const $nsAttributes = Symbol();
|
|||||||
const $onChild = Symbol();
|
const $onChild = Symbol();
|
||||||
const $onChildCheck = Symbol();
|
const $onChildCheck = Symbol();
|
||||||
const $onText = Symbol();
|
const $onText = Symbol();
|
||||||
|
const $pushGlyphs = Symbol();
|
||||||
const $removeChild = Symbol();
|
const $removeChild = Symbol();
|
||||||
const $root = Symbol("root");
|
const $root = Symbol("root");
|
||||||
const $resolvePrototypes = Symbol();
|
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) {
|
[$appendChild](child) {
|
||||||
child[_parent] = this;
|
child[_parent] = this;
|
||||||
this[_children].push(child);
|
this[_children].push(child);
|
||||||
@ -985,6 +999,7 @@ export {
|
|||||||
$getParent,
|
$getParent,
|
||||||
$getRealChildrenByNameIt,
|
$getRealChildrenByNameIt,
|
||||||
$getSubformParent,
|
$getSubformParent,
|
||||||
|
$getTemplateRoot,
|
||||||
$global,
|
$global,
|
||||||
$hasItem,
|
$hasItem,
|
||||||
$hasSettableValue,
|
$hasSettableValue,
|
||||||
@ -994,6 +1009,7 @@ export {
|
|||||||
$isCDATAXml,
|
$isCDATAXml,
|
||||||
$isDataValue,
|
$isDataValue,
|
||||||
$isDescendent,
|
$isDescendent,
|
||||||
|
$isSplittable,
|
||||||
$isTransparent,
|
$isTransparent,
|
||||||
$namespaceId,
|
$namespaceId,
|
||||||
$nodeName,
|
$nodeName,
|
||||||
|
1
test/pdfs/xfa_candidate_petitions.pdf.link
Normal file
1
test/pdfs/xfa_candidate_petitions.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://web.archive.org/web/20210509141453/https://www.sos.state.oh.us/globalassets/elections/directives/2020/dir2020-15_independentcandidatepetitions2020signedextended.pdf
|
@ -946,6 +946,14 @@
|
|||||||
"enableXfa": true,
|
"enableXfa": true,
|
||||||
"type": "eq"
|
"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",
|
{ "id": "xfa_annual_expense_report",
|
||||||
"file": "pdfs/xfa_annual_expense_report.pdf",
|
"file": "pdfs/xfa_annual_expense_report.pdf",
|
||||||
"md5": "06866e7a6bbc0346789208ef5f6e885c",
|
"md5": "06866e7a6bbc0346789208ef5f6e885c",
|
||||||
|
@ -60,6 +60,9 @@ describe("XFAFactory", function () {
|
|||||||
</subform>
|
</subform>
|
||||||
<subform name="second">
|
<subform name="second">
|
||||||
<breakBefore targetType="pageArea" startNew="1"/>
|
<breakBefore targetType="pageArea" startNew="1"/>
|
||||||
|
<subform>
|
||||||
|
<draw w="1pt" h="1pt"><value><text>foo</text></value></draw>
|
||||||
|
</subform>
|
||||||
</subform>
|
</subform>
|
||||||
</subform>
|
</subform>
|
||||||
</template>
|
</template>
|
||||||
@ -133,7 +136,7 @@ describe("XFAFactory", function () {
|
|||||||
<subform name="root" mergeMode="matchTemplate">
|
<subform name="root" mergeMode="matchTemplate">
|
||||||
<pageSet>
|
<pageSet>
|
||||||
<pageArea>
|
<pageArea>
|
||||||
<contentArea x="123pt" w="456pt" h="789pt"/>
|
<contentArea x="0pt" w="456pt" h="789pt"/>
|
||||||
<medium stock="default" short="456pt" long="789pt"/>
|
<medium stock="default" short="456pt" long="789pt"/>
|
||||||
<field y="1pt" w="11pt" h="22pt" x="2pt">
|
<field y="1pt" w="11pt" h="22pt" x="2pt">
|
||||||
<ui>
|
<ui>
|
||||||
@ -146,6 +149,7 @@ describe("XFAFactory", function () {
|
|||||||
</pageArea>
|
</pageArea>
|
||||||
</pageSet>
|
</pageSet>
|
||||||
<subform name="first">
|
<subform name="first">
|
||||||
|
<draw w="1pt" h="1pt"><value><text>foo</text></value></draw>
|
||||||
</subform>
|
</subform>
|
||||||
</subform>
|
</subform>
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
Reference in New Issue
Block a user