XFA - Add support for few ui elements (#13115)

- input;
  - layout;
  - border;
  - margin;
  - color.
This commit is contained in:
calixteman 2021-03-31 15:42:21 +02:00 committed by GitHub
parent 84d7cccb1d
commit b3528868c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 799 additions and 74 deletions

View File

@ -13,10 +13,13 @@
* limitations under the License.
*/
import { $toStyle, XFAObject } from "./xfa_object.js";
import { $getParent, $toStyle, XFAObject } from "./xfa_object.js";
import { warn } from "../../shared/util.js";
function measureToString(m) {
if (typeof m === "string") {
return "0px";
}
return Number.isInteger(m) ? `${m}px` : `${m.toFixed(2)}px`;
}
@ -56,31 +59,34 @@ const converters = {
if (node.w) {
style.width = measureToString(node.w);
} else {
if (node.maxW && node.maxW.value > 0) {
style.width = "auto";
if (node.maxW > 0) {
style.maxWidth = measureToString(node.maxW);
}
if (node.minW && node.minW.value > 0) {
style.minWidth = measureToString(node.minW);
}
style.minWidth = measureToString(node.minW);
}
if (node.h) {
style.height = measureToString(node.h);
} else {
if (node.maxH && node.maxH.value > 0) {
style.height = "auto";
if (node.maxH > 0) {
style.maxHeight = measureToString(node.maxH);
}
if (node.minH && node.minH.value > 0) {
style.minHeight = measureToString(node.minH);
}
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);
const parent = node[$getParent]();
if (parent && parent.layout && parent.layout !== "position") {
// IRL, we've some x/y in tb layout.
// Specs say x/y is only used in positioned layout.
return;
}
style.position = "absolute";
style.left = measureToString(node.x);
style.top = measureToString(node.y);
},
rotate(node, style) {
if (node.rotate) {
@ -91,8 +97,40 @@ const converters = {
style.transformOrigin = "top left";
}
},
presence(node, style) {
switch (node.presence) {
case "invisible":
style.visibility = "hidden";
break;
case "hidden":
case "inactive":
style.display = "none";
break;
}
},
};
function layoutClass(node) {
switch (node.layout) {
case "position":
return "xfaPosition";
case "lr-tb":
return "xfaLrTb";
case "rl-row":
return "xfaRlRow";
case "rl-tb":
return "xfaRlTb";
case "row":
return "xfaRow";
case "table":
return "xfaTable";
case "tb":
return "xfaTb";
default:
return "xfaPosition";
}
}
function toStyle(node, ...names) {
const style = Object.create(null);
for (const name of names) {
@ -117,4 +155,4 @@ function toStyle(node, ...names) {
return style;
}
export { measureToString, toStyle };
export { layoutClass, measureToString, toStyle };

View File

@ -51,7 +51,7 @@ import {
getRelevant,
getStringOption,
} from "./utils.js";
import { measureToString, toStyle } from "./html_utils.js";
import { layoutClass, measureToString, toStyle } from "./html_utils.js";
import { Util, warn } from "../../shared/util.js";
const TEMPLATE_NS_ID = NamespaceIds.template.id;
@ -115,8 +115,8 @@ class Area extends XFAObject {
this.relevant = getRelevant(attributes.relevant);
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.desc = null;
this.extras = null;
this.area = new XFAObjectArray();
@ -346,6 +346,10 @@ class BooleanElement extends Option01 {
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
}
[$toHTML]() {
return this[$content] === 1;
}
}
class Border extends XFAObject {
@ -369,6 +373,83 @@ class Border extends XFAObject {
this.fill = null;
this.margin = null;
}
[$toStyle](widths, margins) {
// TODO: incomplete.
const edgeStyles = this.edge.children.map(node => node[$toStyle]());
const cornerStyles = this.edge.children.map(node => node[$toStyle]());
let style;
if (this.margin) {
style = this.margin[$toStyle]();
if (margins) {
margins.push(
this.margin.topInset,
this.margin.rightInset,
this.margin.bottomInset,
this.margin.leftInset
);
}
} else {
style = Object.create(null);
if (margins) {
margins.push(0, 0, 0, 0);
}
}
if (this.fill) {
Object.assign(style, this.fill[$toStyle]());
}
if (edgeStyles.length > 0) {
if (widths) {
this.edge.children.forEach(node => widths.push(node.thickness));
if (widths.length < 4) {
const last = widths[widths.length - 1];
for (let i = widths.length; i < 4; i++) {
widths.push(last);
}
}
}
if (edgeStyles.length === 2 || edgeStyles.length === 3) {
const last = edgeStyles[edgeStyles.length - 1];
for (let i = edgeStyles.length; i < 4; i++) {
edgeStyles.push(last);
}
}
style.borderWidth = edgeStyles.map(s => s.width).join(" ");
style.borderColor = edgeStyles.map(s => s.color).join(" ");
style.borderStyle = edgeStyles.map(s => s.style).join(" ");
} else {
if (widths) {
widths.push(0, 0, 0, 0);
}
}
if (cornerStyles.length > 0) {
if (cornerStyles.length === 2 || cornerStyles.length === 3) {
const last = cornerStyles[cornerStyles.length - 1];
for (let i = cornerStyles.length; i < 4; i++) {
cornerStyles.push(last);
}
}
style.borderRadius = cornerStyles.map(s => s.radius).join(" ");
}
switch (this.presence) {
case "invisible":
case "hidden":
style.borderStyle = "";
break;
case "inactive":
style.borderStyle = "none";
break;
}
return style;
}
}
class Break extends XFAObject {
@ -471,6 +552,17 @@ class Button extends XFAObject {
this.usehref = attributes.usehref || "";
this.extras = null;
}
[$toHTML]() {
// TODO: highlight.
return {
name: "button",
attributes: {
class: "xfaButton",
style: {},
},
};
}
}
class Calculate extends XFAObject {
@ -521,6 +613,47 @@ class Caption extends XFAObject {
[$setValue](value) {
_setValue(this, value);
}
[$toHTML]() {
// TODO: incomplete.
if (!this.value) {
return null;
}
const value = this.value[$toHTML]();
if (!value) {
return null;
}
const children = [];
if (typeof value === "string") {
children.push({
name: "#text",
value,
});
} else {
children.push(value);
}
const style = toStyle(this, "font", "margin", "para", "visibility");
switch (this.placement) {
case "left":
case "right":
style.minWidth = measureToString(this.reserve);
break;
case "top":
case "bottom":
style.minHeight = measureToString(this.reserve);
break;
}
return {
name: "div",
attributes: {
style,
},
children,
};
}
}
class Certificate extends StringObject {
@ -575,6 +708,83 @@ class CheckButton extends XFAObject {
this.extras = null;
this.margin = null;
}
[$toHTML]() {
// TODO: shape and mark == default.
const style = toStyle(this, "border", "margin");
const size = measureToString(this.size);
style.width = style.height = size;
let mark, radius;
if (this.shape === "square") {
mark = "■";
radius = "10%";
} else {
mark = "●";
radius = "50%";
}
if (!style.borderRadius) {
style.borderRadius = radius;
}
if (this.mark !== "default") {
// TODO: To avoid some rendering issues we should use svg
// to draw marks.
switch (this.mark) {
case "check":
mark = "✓";
break;
case "circle":
mark = "●";
break;
case "cross":
mark = "✕";
break;
case "diamond":
mark = "♦";
break;
case "square":
mark = "■";
break;
case "star":
mark = "★";
break;
}
}
if (size !== "10px") {
style.fontSize = size;
style.lineHeight = size;
style.width = size;
style.height = size;
}
return {
name: "label",
attributes: {
class: "xfaLabel",
},
children: [
{
name: "input",
attributes: {
class: "xfaCheckbox",
type: "checkbox",
},
},
{
name: "span",
attributes: {
class: "xfaCheckboxMark",
mark,
style,
},
},
],
};
}
}
class ChoiceList extends XFAObject {
@ -599,6 +809,27 @@ class ChoiceList extends XFAObject {
this.extras = null;
this.margin = null;
}
[$toHTML]() {
// TODO: incomplete.
const style = toStyle(this, "border", "margin");
return {
name: "label",
attributes: {
class: "xfaLabel",
},
children: [
{
name: "select",
attributes: {
class: "xfaSxelect",
multiple: this.open === "multiSelect",
style,
},
},
],
};
}
}
class Color extends XFAObject {
@ -662,8 +893,8 @@ class ContentArea extends XFAObject {
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.desc = null;
this.extras = null;
}
@ -727,12 +958,19 @@ class Corner extends XFAObject {
this.extras = null;
}
[$finalize]() {
this.color = this.color || getColor(null, [0, 0, 0]);
[$toStyle]() {
// In using CSS it's only possible to handle radius
// (at least with basic css).
// Is there a real use (interest ?) of all these properties ?
// Maybe it's possible to implement them using svg and border-image...
// TODO: implement all the missing properties.
const style = toStyle(this, "visibility");
style.radius = measureToString(this.radius);
return style;
}
}
class Date extends ContentObject {
class DateElement extends ContentObject {
constructor(attributes) {
super(TEMPLATE_NS_ID, "date");
this.id = attributes.id || "";
@ -744,6 +982,10 @@ class Date extends ContentObject {
[$finalize]() {
this[$content] = new Date(this[$content].trim());
}
[$toHTML]() {
return this[$content].toString();
}
}
class DateTime extends ContentObject {
@ -758,6 +1000,10 @@ class DateTime extends ContentObject {
[$finalize]() {
this[$content] = new Date(this[$content].trim());
}
[$toHTML]() {
return this[$content].toString();
}
}
class DateTimeEdit extends XFAObject {
@ -777,6 +1023,29 @@ class DateTimeEdit extends XFAObject {
this.extras = null;
this.margin = null;
}
[$toHTML]() {
// TODO: incomplete.
// When the picker is host we should use type=date for the input
// but we need to put the buttons outside the text-field.
const style = toStyle(this, "border", "font", "margin");
const html = {
name: "input",
attributes: {
type: "text",
class: "xfaTextfield",
style,
},
};
return {
name: "label",
attributes: {
class: "xfaLabel",
},
children: [html],
};
}
}
class Decimal extends ContentObject {
@ -802,6 +1071,10 @@ class Decimal extends ContentObject {
const number = parseFloat(this[$content].trim());
this[$content] = isNaN(number) ? null : number;
}
[$toHTML]() {
return this[$content] !== null ? this[$content].toString() : "";
}
}
class DefaultUi extends XFAObject {
@ -878,7 +1151,7 @@ class Draw extends XFAObject {
defaultValue: 1,
validate: x => x >= 1,
});
this.h = getMeasurement(attributes.h);
this.h = attributes.h ? getMeasurement(attributes.h) : "";
this.hAlign = getStringOption(attributes.hAlign, [
"left",
"center",
@ -889,10 +1162,10 @@ class Draw extends XFAObject {
]);
this.id = attributes.id || "";
this.locale = attributes.locale || "";
this.maxH = getMeasurement(attributes.maxH);
this.maxW = getMeasurement(attributes.maxW);
this.minH = getMeasurement(attributes.minH);
this.minW = getMeasurement(attributes.minW);
this.maxH = getMeasurement(attributes.maxH, "0pt");
this.maxW = getMeasurement(attributes.maxW, "0pt");
this.minH = getMeasurement(attributes.minH, "0pt");
this.minW = getMeasurement(attributes.minW, "0pt");
this.name = attributes.name || "";
this.presence = getStringOption(attributes.presence, [
"visible",
@ -908,9 +1181,9 @@ class Draw extends XFAObject {
});
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.w = attributes.w ? getMeasurement(attributes.w) : "";
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.assist = null;
this.border = null;
this.caption = null;
@ -940,6 +1213,7 @@ class Draw extends XFAObject {
"font",
"dimensions",
"position",
"presence",
"rotate",
"anchorType"
);
@ -955,6 +1229,10 @@ class Draw extends XFAObject {
class: clazz.join(" "),
};
if (this.name) {
attributes.xfaName = this.name;
}
return {
name: "div",
attributes,
@ -992,8 +1270,50 @@ class Edge extends XFAObject {
this.extras = null;
}
[$finalize]() {
this.color = this.color || getColor(null, [0, 0, 0]);
[$toStyle]() {
// TODO: dashDot & dashDotDot.
const style = toStyle(this, "visibility");
Object.assign(style, {
linecap: this.cap,
width: measureToString(this.thickness),
color: this.color ? this.color[$toHTML]() : "#000000",
style: "",
});
if (this.presence !== "visible") {
style.style = "none";
} else {
switch (this.stroke) {
case "solid":
style.style = "solid";
break;
case "dashDot":
style.style = "dashed";
break;
case "dashDotDot":
style.style = "dashed";
break;
case "dashed":
style.style = "dashed";
break;
case "dotted":
style.style = "dotted";
break;
case "embossed":
style.style = "ridge";
break;
case "etched":
style.style = "groove";
break;
case "lowered":
style.style = "inset";
break;
case "raised":
style.style = "outset";
break;
}
}
return style;
}
}
@ -1228,7 +1548,7 @@ class ExclGroup extends XFAObject {
defaultValue: 1,
validate: x => x >= 1,
});
this.h = getMeasurement(attributes.h);
this.h = attributes.h ? getMeasurement(attributes.h) : "";
this.hAlign = getStringOption(attributes.hAlign, [
"left",
"center",
@ -1247,10 +1567,10 @@ class ExclGroup extends XFAObject {
"table",
"tb",
]);
this.maxH = getMeasurement(attributes.maxH);
this.maxW = getMeasurement(attributes.maxW);
this.minH = getMeasurement(attributes.minH);
this.minW = getMeasurement(attributes.minW);
this.maxH = getMeasurement(attributes.maxH, "0pt");
this.maxW = getMeasurement(attributes.maxW, "0pt");
this.minH = getMeasurement(attributes.minH, "0pt");
this.minW = getMeasurement(attributes.minW, "0pt");
this.name = attributes.name || "";
this.presence = getStringOption(attributes.presence, [
"visible",
@ -1261,9 +1581,9 @@ class ExclGroup extends XFAObject {
this.relevant = getRelevant(attributes.relevant);
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.w = attributes.w ? getMeasurement(attributes.w) : "";
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.assist = null;
this.bind = null;
this.border = null;
@ -1399,7 +1719,7 @@ class Field extends XFAObject {
defaultValue: 1,
validate: x => x >= 1,
});
this.h = getMeasurement(attributes.h);
this.h = attributes.h ? getMeasurement(attributes.h) : "";
this.hAlign = getStringOption(attributes.hAlign, [
"left",
"center",
@ -1410,10 +1730,10 @@ class Field extends XFAObject {
]);
this.id = attributes.id || "";
this.locale = attributes.locale || "";
this.maxH = getMeasurement(attributes.maxH);
this.maxW = getMeasurement(attributes.maxW);
this.minH = getMeasurement(attributes.minH);
this.minW = getMeasurement(attributes.minW);
this.maxH = getMeasurement(attributes.maxH, "0pt");
this.maxW = getMeasurement(attributes.maxW, "0pt");
this.minH = getMeasurement(attributes.minH, "0pt");
this.minW = getMeasurement(attributes.minW, "0pt");
this.name = attributes.name || "";
this.presence = getStringOption(attributes.presence, [
"visible",
@ -1429,9 +1749,9 @@ class Field extends XFAObject {
});
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.w = attributes.w ? getMeasurement(attributes.w) : "";
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.assist = null;
this.bind = null;
this.border = null;
@ -1462,7 +1782,7 @@ class Field extends XFAObject {
}
[$toHTML]() {
if (!this.value) {
if (!this.ui) {
return null;
}
@ -1472,10 +1792,39 @@ class Field extends XFAObject {
"dimensions",
"position",
"rotate",
"anchorType"
"anchorType",
"presence"
);
// Get border width in order to compute margin and padding.
const borderWidths = [];
const marginWidths = [];
if (this.border) {
Object.assign(style, this.border[$toStyle](borderWidths, marginWidths));
}
if (this.margin) {
style.paddingTop = measureToString(
this.margin.topInset - borderWidths[0] - marginWidths[0]
);
style.paddingRight = measureToString(
this.margin.rightInset - borderWidths[1] - marginWidths[1]
);
style.paddingBottom = measureToString(
this.margin.bottomInset - borderWidths[2] - marginWidths[2]
);
style.paddingLeft = measureToString(
this.margin.leftInset - borderWidths[3] - marginWidths[3]
);
} else {
style.paddingTop = measureToString(-borderWidths[0] - marginWidths[0]);
style.paddingRight = measureToString(-borderWidths[1] - marginWidths[1]);
style.paddingBottom = measureToString(-borderWidths[2] - marginWidths[2]);
style.paddingLeft = measureToString(-borderWidths[3] - marginWidths[3]);
}
const clazz = ["xfaField"];
// If no font, font properties are inherited.
if (this.font) {
clazz.push("xfaFont");
}
@ -1486,11 +1835,68 @@ class Field extends XFAObject {
class: clazz.join(" "),
};
return {
if (this.name) {
attributes.xfaName = this.name;
}
const children = [];
const html = {
name: "div",
attributes,
children: [],
children,
};
const ui = this.ui ? this.ui[$toHTML]() : null;
if (!ui) {
return html;
}
if (!ui.attributes.style) {
ui.attributes.style = Object.create(null);
}
children.push(ui);
if (this.value && ui.name !== "button") {
// TODO: should be ok with string but html ??
ui.children[0].attributes.value = this.value[$toHTML]();
}
const caption = this.caption ? this.caption[$toHTML]() : null;
if (!caption) {
return html;
}
if (ui.name === "button") {
ui.attributes.style.background = style.color;
delete style.color;
if (caption.name === "div") {
caption.name = "span";
}
ui.children = [caption];
return html;
}
ui.children.splice(0, 0, caption);
switch (this.caption.placement) {
case "left":
ui.attributes.style.flexDirection = "row";
break;
case "right":
ui.attributes.style.flexDirection = "row-reverse";
break;
case "top":
ui.attributes.style.flexDirection = "column";
break;
case "bottom":
ui.attributes.style.flexDirection = "column-reverse";
break;
case "inline":
delete ui.attributes.class;
caption.attributes.style.float = "left";
break;
}
return html;
}
}
@ -1509,7 +1915,7 @@ class Fill extends XFAObject {
this.color = null;
this.extras = null;
// One-of properties
// One-of properties or none
this.linear = null;
this.pattern = null;
this.radial = null;
@ -1518,7 +1924,6 @@ class Fill extends XFAObject {
}
[$toStyle]() {
let fill = "#000000";
for (const name of Object.getOwnPropertyNames(this)) {
if (name === "extras" || name === "color") {
continue;
@ -1528,12 +1933,13 @@ class Fill extends XFAObject {
continue;
}
fill = obj[$toStyle](this.color);
break;
return {
color: obj[$toStyle](this.color),
};
}
return {
color: fill,
color: this.color ? this.color[$toStyle]() : "#000000",
};
}
}
@ -1582,6 +1988,10 @@ class Float extends ContentObject {
const number = parseFloat(this[$content].trim());
this[$content] = isNaN(number) ? null : number;
}
[$toHTML]() {
return this[$content] !== null ? this[$content].toString() : "";
}
}
class Font extends XFAObject {
@ -1798,6 +2208,31 @@ class Image extends StringObject {
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
}
[$toHTML]() {
const html = {
name: "img",
attributes: {
style: {},
},
};
if (this.href) {
html.attributes.src = new URL(this.href);
return html;
}
if (this.transferEncoding === "base64") {
const buffer = Uint8Array.from(atob(this[$content]), c =>
c.charCodeAt(0)
);
const blob = new Blob([buffer], { type: this.contentType });
html.attributes.src = URL.createObjectURL(blob);
return html;
}
return null;
}
}
class ImageEdit extends XFAObject {
@ -1826,6 +2261,10 @@ class Integer extends ContentObject {
const number = parseInt(this[$content].trim(), 10);
this[$content] = isNaN(number) ? null : number;
}
[$toHTML]() {
return this[$content] !== null ? this[$content].toString() : "";
}
}
class Issuers extends XFAObject {
@ -1999,6 +2438,15 @@ class Margin extends XFAObject {
this.usehref = attributes.usehref || "";
this.extras = null;
}
[$toStyle]() {
return {
marginLeft: measureToString(this.leftInset),
marginRight: measureToString(this.rightInset),
marginTop: measureToString(this.topInset),
marginBottom: measureToString(this.bottomInset),
};
}
}
class Mdp extends XFAObject {
@ -2068,6 +2516,27 @@ class NumericEdit extends XFAObject {
this.extras = null;
this.margin = null;
}
[$toHTML]() {
// TODO: incomplete.
const style = toStyle(this, "border", "font", "margin");
const html = {
name: "input",
attributes: {
type: "text",
class: "xfaTextfield",
style,
},
};
return {
name: "label",
attributes: {
class: "xfaLabel",
},
children: [html],
};
}
}
class Occur extends XFAObject {
@ -2295,6 +2764,35 @@ class Para extends XFAObject {
});
this.hyphenation = null;
}
[$toHTML]() {
const style = {
marginLeft: measureToString(this.marginLeft),
marginRight: measureToString(this.marginRight),
paddingTop: measureToString(this.spaceAbove),
paddingBottom: measureToString(this.spaceBelow),
textIndent: measureToString(this.textIndent),
verticalAlign: this.vAlign,
};
if (this.lineHeight.value >= 0) {
style.lineHeight = measureToString(this.lineHeight);
}
if (this.tabDefault) {
style.tabSize = measureToString(this.tabDefault);
}
if (this.tabStops.length > 0) {
// TODO
}
if (this.hyphenatation) {
Object.assign(style, this.hyphenatation[$toHTML]());
}
return style;
}
}
class PasswordEdit extends XFAObject {
@ -2714,7 +3212,7 @@ class Subform extends XFAObject {
.trim()
.split(/\s+/)
.map(x => (x === "-1" ? -1 : getMeasurement(x)));
this.h = getMeasurement(attributes.h);
this.h = attributes.h ? getMeasurement(attributes.h) : "";
this.hAlign = getStringOption(attributes.hAlign, [
"left",
"center",
@ -2734,14 +3232,14 @@ class Subform extends XFAObject {
"tb",
]);
this.locale = attributes.locale || "";
this.maxH = getMeasurement(attributes.maxH);
this.maxW = getMeasurement(attributes.maxW);
this.maxH = getMeasurement(attributes.maxH, "0pt");
this.maxW = getMeasurement(attributes.maxW, "0pt");
this.mergeMode = getStringOption(attributes.mergeMode, [
"consumeData",
"matchTemplate",
]);
this.minH = getMeasurement(attributes.minH);
this.minW = getMeasurement(attributes.minW);
this.minH = getMeasurement(attributes.minH, "0pt");
this.minW = getMeasurement(attributes.minW, "0pt");
this.name = attributes.name || "";
this.presence = getStringOption(attributes.presence, [
"visible",
@ -2757,9 +3255,9 @@ class Subform extends XFAObject {
this.scope = getStringOption(attributes.scope, ["name", "none"]);
this.use = attributes.use || "";
this.usehref = attributes.usehref || "";
this.w = getMeasurement(attributes.w);
this.x = getMeasurement(attributes.x);
this.y = getMeasurement(attributes.y);
this.w = attributes.w ? getMeasurement(attributes.w) : "";
this.x = getMeasurement(attributes.x, "0pt");
this.y = getMeasurement(attributes.y, "0pt");
this.assist = null;
this.bind = null;
this.bookend = null;
@ -2816,12 +3314,17 @@ class Subform extends XFAObject {
page = pageAreas[pageNumber][$toHTML]();
}
const style = toStyle(this, "dimensions", "position");
const style = toStyle(this, "dimensions", "position", "presence");
const clazz = ["xfaSubform"];
const cl = layoutClass(this);
if (cl) {
clazz.push(cl);
}
const attributes = {
style,
id: this[$uid],
class: "xfaSubform",
class: clazz.join(" "),
};
if (this.name) {
@ -3014,6 +3517,13 @@ class Text extends ContentObject {
warn(`XFA - Invalid content in Text: ${child[$nodeName]}.`);
return false;
}
[$toHTML]() {
if (typeof this[$content] === "string") {
return this[$content];
}
return this[$content][$toHTML]();
}
}
class TextEdit extends XFAObject {
@ -3047,6 +3557,37 @@ class TextEdit extends XFAObject {
this.extras = null;
this.margin = null;
}
[$toHTML]() {
// TODO: incomplete.
const style = toStyle(this, "border", "font", "margin");
let html;
if (this.multiline === 1) {
html = {
name: "textarea",
attributes: {
style,
},
};
} else {
html = {
name: "input",
attributes: {
type: "text",
class: "xfaTextfield",
style,
},
};
}
return {
name: "label",
attributes: {
class: "xfaLabel",
},
children: [html],
};
}
}
class Time extends StringObject {
@ -3062,6 +3603,10 @@ class Time extends StringObject {
// TODO
this[$content] = new Date(this[$content]);
}
[$toHTML]() {
return this[$content].toString();
}
}
class TimeStamp extends XFAObject {
@ -3148,6 +3693,22 @@ class Ui extends XFAObject {
this.signature = null;
this.textEdit = null;
}
[$toHTML]() {
// TODO: picture.
for (const name of Object.getOwnPropertyNames(this)) {
if (name === "extras" || name === "picture") {
continue;
}
const obj = this[name];
if (!(obj instanceof XFAObject)) {
continue;
}
return obj[$toHTML]();
}
return null;
}
}
class Validate extends XFAObject {
@ -3226,6 +3787,19 @@ class Value extends XFAObject {
this[value[$nodeName]] = value;
this[$appendChild](value);
}
[$toHTML]() {
for (const name of Object.getOwnPropertyNames(this)) {
const obj = this[name];
if (!(obj instanceof XFAObject)) {
continue;
}
return obj[$toHTML]();
}
return null;
}
}
class Variables extends XFAObject {
@ -3364,7 +3938,7 @@ class TemplateNamespace {
}
static date(attrs) {
return new Date(attrs);
return new DateElement(attrs);
}
static dateTime(attrs) {

View File

@ -117,7 +117,7 @@ describe("XFAParser", function () {
anchorType: "topLeft",
colSpan: 1,
columnWidths: [0],
h: 0,
h: "",
hAlign: "left",
id: "",
layout: "position",
@ -134,7 +134,7 @@ describe("XFAParser", function () {
scope: "name",
use: "",
usehref: "",
w: 0,
w: "",
x: 0,
y: 0,
proto: {

View File

@ -26,8 +26,10 @@
font: inherit;
font-kerning: inherit;
letter-spacing: inherit;
text-align: inherit;
text-decoration: inherit;
vertical-align: inherit;
box-sizing: border-box;
}
.xfaFont {
@ -43,20 +45,131 @@
.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;
}
.xfaLabel {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
height: 100%;
}
.xfaCaption {
flex: 1 1 auto;
}
.xfaTextfield,
.xfaSelect {
width: 100%;
height: 100%;
flex: 1 1 auto;
border: none;
}
.xfaLabel > input[type="checkbox"] {
/* Use this trick to make the checkbox invisible but
but still focusable. */
position: absolute;
left: -99999px;
}
.xfaLabel > input[type="checkbox"]:focus + .xfaCheckboxMark {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
}
.xfaCheckboxMark {
cursor: pointer;
flex: 0 0 auto;
border-style: solid;
border-width: 2px;
border-color: #8f8f9d;
font-size: 10px;
line-height: 10px;
width: 10px;
height: 10px;
text-align: center;
vertical-align: middle;
display: flex;
flex-direction: row;
align-items: center;
}
.xfaCheckbox:checked + .xfaCheckboxMark::after {
content: attr(mark);
}
.xfaButton {
cursor: pointer;
width: 100%;
height: 100%;
border: none;
text-align: center;
}
.xfaButton:hover {
background: Highlight;
}
.xfaLrTb,
.xfaRlTb,
.xfaTb,
.xfaPosition {
display: block;
}
.xfaPosition {
position: relative;
}
.xfaValignMiddle {
display: flex;
align-items: center;
}
.xfaLrTb > div {
display: inline;
float: left;
}
.xfaRlTb > div {
display: inline;
float: right;
}
.xfaTable {
display: flex;
flex-direction: column;
}
.xfaTable .xfaRow {
display: flex;
flex-direction: row;
flex: 1 1 auto;
}
.xfaTable .xfaRow > div {
flex: 1 1 auto;
}
.xfaTable .xfaRlRow {
display: flex;
flex-direction: row-reverse;
flex: 1;
}
.xfaTable .xfaRlRow > div {
flex: 1;
}