XFA - Use native radio and checkbox buttons

- Remove current stuff which relies on some system fonts to avoid bad rendering.
This commit is contained in:
Calixte Denizet 2021-05-27 17:44:55 +02:00
parent f2ade671ec
commit f61f80a5a3
4 changed files with 70 additions and 102 deletions

View File

@ -890,71 +890,45 @@ class CheckButton extends XFAObject {
} }
[$toHTML](availableSpace) { [$toHTML](availableSpace) {
// TODO: shape and mark == default. // TODO: border, shape and mark.
const style = toStyle(this, "border", "margin");
const style = toStyle("margin");
const size = measureToString(this.size); const size = measureToString(this.size);
style.width = style.height = size; style.width = style.height = size;
let mark, radius; let type;
if (this.shape === "square") { let className;
mark = "▪"; let groupId;
radius = "10%"; let id;
} 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;
}
const fieldId = this[$getParent]()[$getParent]()[$uid]; const fieldId = this[$getParent]()[$getParent]()[$uid];
const container = this[$getParent]()[$getParent]()[$getParent]();
if (container instanceof ExclGroup) {
groupId = container[$uid];
type = "radio";
className = "xfaRadio";
id = `${fieldId}-radio`;
} else {
type = "checkbox";
className = "xfaCheckbox";
}
const input = { const input = {
name: "input", name: "input",
attributes: { attributes: {
class: "xfaCheckbox", class: className,
style,
fieldId, fieldId,
type: "radio", type,
id: `${fieldId}-radio`,
}, },
}; };
const container = this[$getParent]()[$getParent]()[$getParent](); if (id) {
if (container instanceof ExclGroup) { input.attributes.id = id;
input.attributes.name = container[$uid]; }
if (groupId) {
input.attributes.name = groupId;
} }
return HTMLResult.success({ return HTMLResult.success({
@ -962,17 +936,7 @@ class CheckButton extends XFAObject {
attributes: { attributes: {
class: "xfaLabel", class: "xfaLabel",
}, },
children: [ children: [input],
input,
{
name: "span",
attributes: {
class: "xfaCheckboxMark",
mark,
style,
},
},
],
}); });
} }
} }

View File

@ -16,20 +16,26 @@
import { PageViewport } from "./display_utils.js"; import { PageViewport } from "./display_utils.js";
class XfaLayer { class XfaLayer {
static setupStorage(html, fieldId, element, storage) { static setupStorage(html, fieldId, element, storage, intent) {
const storedData = storage.getValue(fieldId, { value: null }); const storedData = storage.getValue(fieldId, { value: null });
switch (element.name) { switch (element.name) {
case "textarea": case "textarea":
html.textContent = storedData.value !== null ? storedData.value : ""; html.textContent = storedData.value !== null ? storedData.value : "";
if (intent === "print") {
break;
}
html.addEventListener("input", event => { html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.value }); storage.setValue(fieldId, { value: event.target.value });
}); });
break; break;
case "input": case "input":
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
}
if (element.attributes.type === "radio") { if (element.attributes.type === "radio") {
if (storedData.value) {
html.setAttribute("checked", true);
}
if (intent === "print") {
break;
}
html.addEventListener("change", event => { html.addEventListener("change", event => {
const { target } = event; const { target } = event;
for (const radio of document.getElementsByName(target.name)) { for (const radio of document.getElementsByName(target.name)) {
@ -40,7 +46,23 @@ class XfaLayer {
} }
storage.setValue(fieldId, { value: target.checked }); storage.setValue(fieldId, { value: target.checked });
}); });
} else if (element.attributes.type === "checkbox") {
if (storedData.value) {
html.setAttribute("checked", true);
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.checked });
});
} else { } else {
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => { html.addEventListener("input", event => {
storage.setValue(fieldId, { value: event.target.value }); storage.setValue(fieldId, { value: event.target.value });
}); });
@ -66,8 +88,13 @@ class XfaLayer {
} }
} }
static setAttributes(html, element, storage) { static setAttributes(html, element, storage, intent) {
const { attributes } = element; const { attributes } = element;
if (attributes.type === "radio") {
// Avoid to have a radio group when printing with the same as one
// already displayed.
attributes.name = `${attributes.name}-${intent}`;
}
for (const [key, value] of Object.entries(attributes)) { for (const [key, value] of Object.entries(attributes)) {
if (value === null || value === undefined || key === "fieldId") { if (value === null || value === undefined || key === "fieldId") {
continue; continue;
@ -94,6 +121,7 @@ class XfaLayer {
static render(parameters) { static render(parameters) {
const storage = parameters.annotationStorage; const storage = parameters.annotationStorage;
const root = parameters.xfa; const root = parameters.xfa;
const intent = parameters.intent;
const rootHtml = document.createElement(root.name); const rootHtml = document.createElement(root.name);
if (root.attributes) { if (root.attributes) {
this.setAttributes(rootHtml, root); this.setAttributes(rootHtml, root);
@ -134,7 +162,7 @@ class XfaLayer {
const childHtml = document.createElement(name); const childHtml = document.createElement(name);
html.appendChild(childHtml); html.appendChild(childHtml);
if (child.attributes) { if (child.attributes) {
this.setAttributes(childHtml, child, storage); this.setAttributes(childHtml, child, storage, intent);
} }
if (child.children && child.children.length > 0) { if (child.children && child.children.length > 0) {

View File

@ -125,38 +125,6 @@
resize: none; resize: none;
} }
.xfaLabel > input[type="radio"] {
/* Use this trick to make the checkbox invisible but
but still focusable. */
position: absolute;
left: -99999px;
}
.xfaLabel > input[type="radio"]: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 { .xfaButton {
cursor: pointer; cursor: pointer;
width: 100%; width: 100%;
@ -169,6 +137,13 @@
background: Highlight; background: Highlight;
} }
.xfaCheckbox,
.xfaRadio {
width: 100%;
height: 100%;
border: none;
}
.xfaRich { .xfaRich {
white-space: pre-wrap; white-space: pre-wrap;
} }

View File

@ -82,6 +82,7 @@ class XfaLayerBuilder {
xfa: this.xfaHtml, xfa: this.xfaHtml,
page: null, page: null,
annotationStorage: this.annotationStorage, annotationStorage: this.annotationStorage,
intent,
}; };
// Create an xfa layer div and render the form // Create an xfa layer div and render the form