diff --git a/src/core/xfa/html_utils.js b/src/core/xfa/html_utils.js
index 83aaa9a35..0753c6244 100644
--- a/src/core/xfa/html_utils.js
+++ b/src/core/xfa/html_utils.js
@@ -13,57 +13,108 @@
* limitations under the License.
*/
-const converters = {
- pt: x => x,
- cm: x => Math.round((x / 2.54) * 72),
- mm: x => Math.round((x / (10 * 2.54)) * 72),
- in: x => Math.round(x * 72),
-};
+import { $toStyle, XFAObject } from "./xfa_object.js";
+import { warn } from "../../shared/util.js";
function measureToString(m) {
- const conv = converters[m.unit];
- if (conv) {
- return `${conv(m.value)}px`;
- }
- return `${m.value}${m.unit}`;
+ return Number.isInteger(m) ? `${m}px` : `${m.toFixed(2)}px`;
}
-function setWidthHeight(node, style) {
- if (node.w) {
- style.width = measureToString(node.w);
- } else {
- if (node.maxW && node.maxW.value > 0) {
- style.maxWidth = measureToString(node.maxW);
+const converters = {
+ anchorType(node, style) {
+ if (!("transform" in style)) {
+ style.transform = "";
}
- if (node.minW && node.minW.value > 0) {
- style.minWidth = measureToString(node.minW);
+ switch (node.anchorType) {
+ case "bottomCenter":
+ style.transform += "translate(-50%, -100%)";
+ break;
+ case "bottomLeft":
+ style.transform += "translate(0,-100%)";
+ break;
+ case "bottomRight":
+ style.transform += "translate(-100%,-100%)";
+ break;
+ case "middleCenter":
+ style.transform += "translate(-50%,-50%)";
+ break;
+ case "middleLeft":
+ style.transform += "translate(0,-50%)";
+ break;
+ case "middleRight":
+ style.transform += "translate(-100%,-50%)";
+ break;
+ case "topCenter":
+ style.transform += "translate(-50%,0)";
+ break;
+ case "topRight":
+ style.transform += "translate(-100%,0)";
+ break;
+ }
+ },
+ dimensions(node, style) {
+ if (node.w) {
+ style.width = measureToString(node.w);
+ } else {
+ if (node.maxW && node.maxW.value > 0) {
+ style.maxWidth = measureToString(node.maxW);
+ }
+ if (node.minW && node.minW.value > 0) {
+ style.minWidth = measureToString(node.minW);
+ }
}
- }
- if (node.h) {
- style.height = measureToString(node.h);
- } else {
- if (node.maxH && node.maxH.value > 0) {
- style.maxHeight = measureToString(node.maxH);
+ if (node.h) {
+ style.height = measureToString(node.h);
+ } else {
+ if (node.maxH && node.maxH.value > 0) {
+ style.maxHeight = measureToString(node.maxH);
+ }
+ if (node.minH && node.minH.value > 0) {
+ style.minHeight = measureToString(node.minH);
+ }
}
- if (node.minH && node.minH.value > 0) {
- style.minHeight = measureToString(node.minH);
+ },
+ position(node, style) {
+ if (node.x !== "" || node.y !== "") {
+ style.position = "absolute";
+ style.left = measureToString(node.x);
+ style.top = measureToString(node.y);
+ }
+ },
+ rotate(node, style) {
+ if (node.rotate) {
+ if (!("transform" in style)) {
+ style.transform = "";
+ }
+ style.transform += `rotate(-${node.rotate}deg)`;
+ style.transformOrigin = "top left";
+ }
+ },
+};
+
+function toStyle(node, ...names) {
+ const style = Object.create(null);
+ for (const name of names) {
+ const value = node[name];
+ if (value === null) {
+ continue;
+ }
+ if (value instanceof XFAObject) {
+ const newStyle = value[$toStyle]();
+ if (newStyle) {
+ Object.assign(style, newStyle);
+ } else {
+ warn(`(DEBUG) - XFA - style for ${name} not implemented yet`);
+ }
+ continue;
+ }
+
+ if (converters.hasOwnProperty(name)) {
+ converters[name](node, style);
}
}
+ return style;
}
-function setPosition(node, style) {
- style.transform = "";
- if (node.rotate) {
- style.transform = `rotate(-${node.rotate}deg) `;
- style.transformOrigin = "top left";
- }
-
- if (node.x !== "" || node.y !== "") {
- style.position = "absolute";
- style.left = node.x ? measureToString(node.x) : "0pt";
- style.top = node.y ? measureToString(node.y) : "0pt";
- }
-}
-
-export { measureToString, setPosition, setWidthHeight };
+export { measureToString, toStyle };
diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js
index 78f66c0e4..98cbb6c63 100644
--- a/src/core/xfa/template.js
+++ b/src/core/xfa/template.js
@@ -30,6 +30,7 @@ import {
$setSetAttributes,
$setValue,
$toHTML,
+ $toStyle,
$uid,
ContentObject,
Option01,
@@ -50,8 +51,8 @@ import {
getRelevant,
getStringOption,
} from "./utils.js";
-import { measureToString, setPosition, setWidthHeight } from "./html_utils.js";
-import { warn } from "../../shared/util.js";
+import { measureToString, toStyle } from "./html_utils.js";
+import { Util, warn } from "../../shared/util.js";
const TEMPLATE_NS_ID = NamespaceIds.template.id;
@@ -614,6 +615,10 @@ class Color extends XFAObject {
[$hasSettableValue]() {
return false;
}
+
+ [$toStyle]() {
+ return Util.makeHexColor(this.value.r, this.value.g, this.value.b);
+ }
}
class Comb extends XFAObject {
@@ -680,7 +685,7 @@ class ContentArea extends XFAObject {
children: [],
attributes: {
style,
- className: "xfa-contentarea",
+ class: "xfaContentarea",
id: this[$uid],
},
};
@@ -896,10 +901,10 @@ class Draw extends XFAObject {
"invisible",
]);
this.relevant = getRelevant(attributes.relevant);
- this.rotate = getFloat({
+ this.rotate = getInteger({
data: attributes.rotate,
defaultValue: 0,
- validate: x => true,
+ validate: x => x % 90 === 0,
});
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
@@ -924,6 +929,38 @@ class Draw extends XFAObject {
[$setValue](value) {
_setValue(this, value);
}
+
+ [$toHTML]() {
+ if (!this.value) {
+ return null;
+ }
+
+ const style = toStyle(
+ this,
+ "font",
+ "dimensions",
+ "position",
+ "rotate",
+ "anchorType"
+ );
+
+ const clazz = ["xfaDraw"];
+ if (this.font) {
+ clazz.push("xfaFont");
+ }
+
+ const attributes = {
+ style,
+ id: this[$uid],
+ class: clazz.join(" "),
+ };
+
+ return {
+ name: "div",
+ attributes,
+ children: [],
+ };
+ }
}
class Edge extends XFAObject {
@@ -1269,6 +1306,31 @@ class ExclGroup extends XFAObject {
field.value[$setValue](nodeBoolean);
}
}
+
+ [$toHTML]() {
+ if (!this.value) {
+ return null;
+ }
+
+ const style = toStyle(this, "dimensions", "position", "anchorType");
+ const attributes = {
+ style,
+ id: this[$uid],
+ class: "xfaExclgroup",
+ };
+
+ const children = this[$childrenToHTML]({
+ // TODO: exObject & exclGroup
+ filter: new Set(["field"]),
+ include: true,
+ });
+
+ return {
+ name: "div",
+ attributes,
+ children,
+ };
+ }
}
class Execute extends XFAObject {
@@ -1360,7 +1422,11 @@ class Field extends XFAObject {
"invisible",
]);
this.relevant = getRelevant(attributes.relevant);
- this.rotate = getStringOption(attributes.rotate, ["0", "angle"]);
+ this.rotate = getInteger({
+ data: attributes.rotate,
+ defaultValue: 0,
+ validate: x => x % 90 === 0,
+ });
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
@@ -1394,6 +1460,38 @@ class Field extends XFAObject {
[$setValue](value) {
_setValue(this, value);
}
+
+ [$toHTML]() {
+ if (!this.value) {
+ return null;
+ }
+
+ const style = toStyle(
+ this,
+ "font",
+ "dimensions",
+ "position",
+ "rotate",
+ "anchorType"
+ );
+
+ const clazz = ["xfaField"];
+ if (this.font) {
+ clazz.push("xfaFont");
+ }
+
+ const attributes = {
+ style,
+ id: this[$uid],
+ class: clazz.join(" "),
+ };
+
+ return {
+ name: "div",
+ attributes,
+ children: [],
+ };
+ }
}
class Fill extends XFAObject {
@@ -1418,6 +1516,26 @@ class Fill extends XFAObject {
this.solid = null;
this.stipple = null;
}
+
+ [$toStyle]() {
+ let fill = "#000000";
+ for (const name of Object.getOwnPropertyNames(this)) {
+ if (name === "extras" || name === "color") {
+ continue;
+ }
+ const obj = this[name];
+ if (!(obj instanceof XFAObject)) {
+ continue;
+ }
+
+ fill = obj[$toStyle](this.color);
+ break;
+ }
+
+ return {
+ color: fill,
+ };
+ }
}
class Filter extends XFAObject {
@@ -1522,6 +1640,80 @@ class Font extends XFAObject {
this.extras = null;
this.fill = null;
}
+
+ [$toStyle]() {
+ const style = toStyle(this, "fill");
+ if (style.color) {
+ if (!style.color.startsWith("#")) {
+ // We've a gradient which is not possible for a font color
+ // so use a workaround.
+ style.backgroundClip = "text";
+ style.background = style.color;
+ style.color = "transparent";
+ } else if (style.color === "#000000") {
+ delete style.color;
+ }
+ }
+
+ if (this.baselineShift) {
+ style.verticalAlign = measureToString(this.baselineShift);
+ }
+
+ // TODO: fontHorizontalScale
+ // TODO: fontVerticalScale
+
+ if (this.kerningMode !== "none") {
+ style.fontKerning = "normal";
+ }
+
+ if (this.letterSpacing) {
+ style.letterSpacing = measureToString(this.letterSpacing);
+ }
+
+ if (this.lineThrough !== 0) {
+ style.textDecoration = "line-through";
+ if (this.lineThrough === 2) {
+ style.textDecorationStyle = "double";
+ }
+ }
+
+ // TODO: lineThroughPeriod
+
+ if (this.overline !== 0) {
+ style.textDecoration = "overline";
+ if (this.overline === 2) {
+ style.textDecorationStyle = "double";
+ }
+ }
+
+ // TODO: overlinePeriod
+
+ if (this.posture !== "normal") {
+ style.fontStyle = this.posture;
+ }
+
+ const fontSize = measureToString(this.size);
+ if (fontSize !== "10px") {
+ style.fontSize = fontSize;
+ }
+
+ style.fontFamily = this.typeface;
+
+ if (this.underline !== 0) {
+ style.textDecoration = "underline";
+ if (this.underline === 2) {
+ style.textDecorationStyle = "double";
+ }
+ }
+
+ // TODO: underlinePeriod
+
+ if (this.weight !== "normal") {
+ style.fontWeight = this.weight;
+ }
+
+ return style;
+ }
}
class Format extends XFAObject {
@@ -1755,6 +1947,13 @@ class Linear extends XFAObject {
this.color = null;
this.extras = null;
}
+
+ [$toStyle](startColor) {
+ startColor = startColor ? startColor[$toStyle]() : "#FFFFFF";
+ const transf = this.type.replace(/([RBLT])/, " $1").toLowerCase();
+ const endColor = this.color ? this.color[$toStyle]() : "#000000";
+ return `linear-gradient(${transf}, ${startColor}, ${endColor})`;
+ }
}
class LockDocument extends ContentObject {
@@ -1989,11 +2188,11 @@ class PageArea extends XFAObject {
// TODO: handle the case where there are several content areas.
const contentArea = children.find(
- node => node.attributes.className === "xfa-contentarea"
+ node => node.attributes.class === "xfaContentarea"
);
const style = Object.create(null);
- if (this.medium && this.medium.short.value && this.medium.long.value) {
+ if (this.medium && this.medium.short && this.medium.long) {
style.width = measureToString(this.medium.short);
style.height = measureToString(this.medium.long);
} else {
@@ -2133,6 +2332,32 @@ class Pattern extends XFAObject {
this.color = null;
this.extras = null;
}
+
+ [$toStyle](startColor) {
+ startColor = startColor ? startColor[$toStyle]() : "#FFFFFF";
+ const endColor = this.color ? this.color[$toStyle]() : "#000000";
+ const width = 5;
+ const cmd = "repeating-linear-gradient";
+ const colors = `${startColor},${startColor} ${width}px,${endColor} ${width}px,${endColor} ${
+ 2 * width
+ }px`;
+ switch (this.type) {
+ case "crossHatch":
+ return `${cmd}(to top,${colors}) ${cmd}(to right,${colors})`;
+ case "crossDiagonal":
+ return `${cmd}(45deg,${colors}) ${cmd}(-45deg,${colors})`;
+ case "diagonalLeft":
+ return `${cmd}(45deg,${colors})`;
+ case "diagonalRight":
+ return `${cmd}(-45deg,${colors})`;
+ case "horizontal":
+ return `${cmd}(to top,${colors})`;
+ case "vertical":
+ return `${cmd}(to right,${colors})`;
+ }
+
+ return "";
+ }
}
class Picture extends StringObject {
@@ -2270,6 +2495,16 @@ class Radial extends XFAObject {
this.color = null;
this.extras = null;
}
+
+ [$toStyle](startColor) {
+ startColor = startColor ? startColor[$toStyle]() : "#FFFFFF";
+ const endColor = this.color ? this.color[$toStyle]() : "#000000";
+ const colors =
+ this.type === "toEdge"
+ ? `${startColor},${endColor}`
+ : `${endColor},${startColor}`;
+ return `radial-gradient(circle to center, ${colors})`;
+ }
}
class Reason extends StringObject {
@@ -2393,6 +2628,10 @@ class Solid extends XFAObject {
this.usehref = attributes.usehref || "";
this.extras = null;
}
+
+ [$toStyle](startColor) {
+ return startColor ? startColor[$toStyle]() : "#FFFFFF";
+ }
}
class Speak extends StringObject {
@@ -2430,6 +2669,15 @@ class Stipple extends XFAObject {
this.color = null;
this.extras = null;
}
+
+ [$toStyle](bgColor) {
+ const alpha = this.rate / 100;
+ return Util.makeHexColor(
+ Math.round(bgColor.value.r * (1 - alpha) + this.value.r * alpha),
+ Math.round(bgColor.value.g * (1 - alpha) + this.value.g * alpha),
+ Math.round(bgColor.value.b * (1 - alpha) + this.value.b * alpha)
+ );
+ }
}
class Subform extends XFAObject {
@@ -2568,17 +2816,16 @@ class Subform extends XFAObject {
page = pageAreas[pageNumber][$toHTML]();
}
- const style = Object.create(null);
- setWidthHeight(this, style);
- setPosition(this, style);
+ const style = toStyle(this, "dimensions", "position");
const attributes = {
style,
id: this[$uid],
+ class: "xfaSubform",
};
if (this.name) {
- attributes["xfa-name"] = this.name;
+ attributes.xfaName = this.name;
}
const children = this[$childrenToHTML]({
diff --git a/src/core/xfa/utils.js b/src/core/xfa/utils.js
index 67e137a87..872fff44f 100644
--- a/src/core/xfa/utils.js
+++ b/src/core/xfa/utils.js
@@ -13,7 +13,13 @@
* limitations under the License.
*/
-const measurementPattern = /([+-]?)([0-9]+\.?[0-9]*)(.*)/;
+const dimConverters = {
+ pt: x => x,
+ cm: x => (x / 2.54) * 72,
+ mm: x => (x / (10 * 2.54)) * 72,
+ in: x => x * 72,
+};
+const measurementPattern = /([+-]?[0-9]+\.?[0-9]*)(.*)/;
function getInteger({ data, defaultValue, validate }) {
if (!data) {
@@ -67,15 +73,22 @@ function getMeasurement(str, def = "0") {
if (!match) {
return getMeasurement(def);
}
- const [, sign, valueStr, unit] = match;
+ const [, valueStr, unit] = match;
const value = parseFloat(valueStr);
if (isNaN(value)) {
return getMeasurement(def);
}
- return {
- value: sign === "-" ? -value : value,
- unit: unit || "pt",
- };
+
+ if (value === 0) {
+ return 0;
+ }
+
+ const conv = dimConverters[unit];
+ if (conv) {
+ return conv(value);
+ }
+
+ return value;
}
function getRatio(data) {
@@ -134,7 +147,7 @@ function getColor(data, def = [0, 0, 0]) {
}
function getBBox(data) {
- const def = getMeasurement("-1");
+ const def = -1;
if (!data) {
return { x: def, y: def, width: def, height: def };
}
@@ -142,7 +155,7 @@ function getBBox(data) {
.trim()
.split(/\s*,\s*/)
.map(m => getMeasurement(m, "-1"));
- if (bbox.length < 4 || bbox[2].value < 0 || bbox[3].value < 0) {
+ if (bbox.length < 4 || bbox[2] < 0 || bbox[3] < 0) {
return { x: def, y: def, width: def, height: def };
}
diff --git a/src/core/xfa/xfa_object.js b/src/core/xfa/xfa_object.js
index 611af1df0..94b083baf 100644
--- a/src/core/xfa/xfa_object.js
+++ b/src/core/xfa/xfa_object.js
@@ -59,6 +59,7 @@ const $setSetAttributes = Symbol();
const $setValue = Symbol();
const $text = Symbol();
const $toHTML = Symbol();
+const $toStyle = Symbol();
const $uid = Symbol("uid");
const _applyPrototype = Symbol();
@@ -259,6 +260,10 @@ class XFAObject {
return dumped;
}
+ [$toStyle]() {
+ return null;
+ }
+
[$toHTML]() {
return null;
}
@@ -839,6 +844,7 @@ export {
$setValue,
$text,
$toHTML,
+ $toStyle,
$uid,
ContentObject,
IntegerObject,
diff --git a/src/display/xfa_layer.js b/src/display/xfa_layer.js
index cfedad885..7bd7d3620 100644
--- a/src/display/xfa_layer.js
+++ b/src/display/xfa_layer.js
@@ -36,9 +36,13 @@ class XfaLayer {
}
const stack = [[root, -1, rootHtml]];
- parameters.div.appendChild(rootHtml);
+ const rootDiv = parameters.div;
+ rootDiv.appendChild(rootHtml);
const coeffs = parameters.viewport.transform.join(",");
- parameters.div.style.transform = `matrix(${coeffs})`;
+ rootDiv.style.transform = `matrix(${coeffs})`;
+
+ // Set defaults.
+ rootDiv.setAttribute("class", "xfaLayer xfaFont");
while (stack.length > 0) {
const [parent, i, html] = stack[stack.length - 1];
diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js
index d54427eb8..56aed3cf7 100644
--- a/test/unit/jasmine-boot.js
+++ b/test/unit/jasmine-boot.js
@@ -87,6 +87,7 @@ async function initializePDFJS(callback) {
"pdfjs-test/unit/writer_spec.js",
"pdfjs-test/unit/xfa_formcalc_spec.js",
"pdfjs-test/unit/xfa_parser_spec.js",
+ "pdfjs-test/unit/xfa_tohtml_spec.js",
"pdfjs-test/unit/xml_spec.js",
].map(function (moduleName) {
// eslint-disable-next-line no-unsanitized/method
diff --git a/test/unit/xfa_parser_spec.js b/test/unit/xfa_parser_spec.js
index 6bfbb78c2..329a40fe7 100644
--- a/test/unit/xfa_parser_spec.js
+++ b/test/unit/xfa_parser_spec.js
@@ -81,9 +81,9 @@ describe("XFAParser", function () {
};
const mediumAttributes = {
id: "",
- long: { value: 0, unit: "pt" },
+ long: 0,
orientation: "portrait",
- short: { value: 0, unit: "pt" },
+ short: 0,
stock: "",
trayIn: "auto",
trayOut: "auto",
@@ -116,17 +116,17 @@ describe("XFAParser", function () {
allowMacro: 0,
anchorType: "topLeft",
colSpan: 1,
- columnWidths: [{ value: 0, unit: "pt" }],
- h: { value: 0, unit: "pt" },
+ columnWidths: [0],
+ h: 0,
hAlign: "left",
id: "",
layout: "position",
locale: "",
- maxH: { value: 0, unit: "pt" },
- maxW: { value: 0, unit: "pt" },
+ maxH: 0,
+ maxW: 0,
mergeMode: "consumeData",
- minH: { value: 0, unit: "pt" },
- minW: { value: 0, unit: "pt" },
+ minH: 0,
+ minW: 0,
name: "",
presence: "visible",
relevant: [],
@@ -134,15 +134,15 @@ describe("XFAParser", function () {
scope: "name",
use: "",
usehref: "",
- w: { value: 0, unit: "pt" },
- x: { value: 0, unit: "pt" },
- y: { value: 0, unit: "pt" },
+ w: 0,
+ x: 0,
+ y: 0,
proto: {
area: {
...attributes,
colSpan: 1,
- x: { value: 0, unit: "pt" },
- y: { value: -3.14, unit: "in" },
+ x: 0,
+ y: -226.08,
relevant: [
{ excluded: true, viewname: "foo" },
{ excluded: false, viewname: "bar" },
@@ -162,19 +162,19 @@ describe("XFAParser", function () {
{
...mediumAttributes,
imagingBBox: {
- x: { value: 1, unit: "pt" },
- y: { value: 2, unit: "in" },
- width: { value: 3.4, unit: "cm" },
- height: { value: 5.67, unit: "px" },
+ x: 1,
+ y: 144,
+ width: 96.3779527559055,
+ height: 5.67,
},
},
{
...mediumAttributes,
imagingBBox: {
- x: { value: -1, unit: "pt" },
- y: { value: -1, unit: "pt" },
- width: { value: -1, unit: "pt" },
- height: { value: -1, unit: "pt" },
+ x: -1,
+ y: -1,
+ width: -1,
+ height: -1,
},
},
],
@@ -288,7 +288,7 @@ describe("XFAParser", function () {
let font = root.template.subform.field[0].font;
expect(font.typeface).toEqual("Foo");
expect(font.overline).toEqual(0);
- expect(font.size).toEqual({ value: 123, unit: "pt" });
+ expect(font.size).toEqual(123);
expect(font.weight).toEqual("bold");
expect(font.posture).toEqual("italic");
expect(font.fill.color.value).toEqual({ r: 1, g: 2, b: 3 });
@@ -297,7 +297,7 @@ describe("XFAParser", function () {
font = root.template.subform.field[1].font;
expect(font.typeface).toEqual("Foo");
expect(font.overline).toEqual(0);
- expect(font.size).toEqual({ value: 456, unit: "pt" });
+ expect(font.size).toEqual(456);
expect(font.weight).toEqual("bold");
expect(font.posture).toEqual("normal");
expect(font.fill.color.value).toEqual({ r: 4, g: 5, b: 6 });
@@ -387,7 +387,7 @@ describe("XFAParser", function () {
expect(font.typeface).toEqual("helvetica");
expect(font.overline).toEqual(0);
- expect(font.size).toEqual({ value: 31, unit: "pt" });
+ expect(font.size).toEqual(31);
expect(font.weight).toEqual("normal");
expect(font.posture).toEqual("italic");
expect(font.fill.color.value).toEqual({ r: 7, g: 8, b: 9 });
@@ -1005,10 +1005,7 @@ describe("XFAParser", function () {
).toBe("myfont");
expect(
searchNode(form, form, "Id.LastName.font.size")[0][$text]()
- ).toEqual({
- value: 123.4,
- unit: "pt",
- });
+ ).toEqual(123.4);
expect(
searchNode(form, form, "Id.LastName.assist.toolTip")[0][$dump]()
.$content
diff --git a/test/unit/xfa_tohtml_spec.js b/test/unit/xfa_tohtml_spec.js
new file mode 100644
index 000000000..299dce95a
--- /dev/null
+++ b/test/unit/xfa_tohtml_spec.js
@@ -0,0 +1,98 @@
+/* Copyright 2020 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { XFAFactory } from "../../src/core/xfa/factory.js";
+
+describe("XFAFactory", function () {
+ describe("toHTML", function () {
+ it("should convert some basic properties to CSS", function () {
+ const xml = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ const factory = new XFAFactory({ "xdp:xdp": xml });
+
+ expect(factory.numberPages).toEqual(2);
+
+ const page1 = factory.getPage(0);
+ expect(page1.attributes.style).toEqual({
+ height: "789px",
+ width: "456px",
+ });
+
+ expect(page1.children.length).toEqual(2);
+ const container = page1.children[0];
+ expect(container.attributes.class).toEqual("xfaContentarea");
+ expect(container.attributes.style).toEqual({
+ height: "789px",
+ width: "456px",
+ left: "123px",
+ top: "0px",
+ position: "absolute",
+ });
+
+ const draw = page1.children[1];
+ expect(draw.attributes.class).toEqual("xfaDraw xfaFont");
+ expect(draw.attributes.style).toEqual({
+ color: "#0c1722",
+ fontFamily: "Arial",
+ fontSize: "7px",
+ height: "22px",
+ left: "2px",
+ position: "absolute",
+ top: "1px",
+ transform: "rotate(-90deg)",
+ transformOrigin: "top left",
+ verticalAlign: "2px",
+ width: "11px",
+ });
+
+ // draw element must be on each page.
+ expect(draw.attributes.style).toEqual(
+ factory.getPage(1).children[1].attributes.style
+ );
+ });
+ });
+});
diff --git a/web/xfa_layer_builder.css b/web/xfa_layer_builder.css
index b891225f4..3a36581ca 100644
--- a/web/xfa_layer_builder.css
+++ b/web/xfa_layer_builder.css
@@ -20,3 +20,43 @@
z-index: 200;
transform-origin: 0 0;
}
+
+.xfaLayer * {
+ color: inherit;
+ font: inherit;
+ font-kerning: inherit;
+ letter-spacing: inherit;
+ text-decoration: inherit;
+ vertical-align: inherit;
+}
+
+.xfaFont {
+ color: black;
+ font-weight: normal;
+ font-kerning: none;
+ font-size: 10px;
+ font-style: normal;
+ letter-spacing: 0;
+ text-decoration: none;
+ vertical-align: 0;
+}
+
+.xfaDraw {
+ z-index: 200;
+ background-color: #ff000080;
+}
+
+.xfaExclgroup {
+ z-index: 300;
+ background-color: #0000ff80;
+}
+
+.xfaField {
+ z-index: 300;
+ background-color: #00ff0080;
+}
+
+.xfaSubform {
+ z-index: 100;
+ background-color: #ffff0080;
+}
diff --git a/web/xfa_layer_builder.js b/web/xfa_layer_builder.js
index b875db21e..1b13a13cb 100644
--- a/web/xfa_layer_builder.js
+++ b/web/xfa_layer_builder.js
@@ -57,7 +57,6 @@ class XfaLayerBuilder {
} else {
// Create an xfa layer div and render the form
this.div = document.createElement("div");
- this.div.className = "xfaLayer";
this.pageDiv.appendChild(this.div);
parameters.div = this.div;