From f61f80a5a33ded03ae5a3cb66e0cfc05cea2e2da Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 27 May 2021 17:44:55 +0200 Subject: [PATCH] XFA - Use native radio and checkbox buttons - Remove current stuff which relies on some system fonts to avoid bad rendering. --- src/core/xfa/template.js | 88 ++++++++++++--------------------------- src/display/xfa_layer.js | 40 +++++++++++++++--- web/xfa_layer_builder.css | 39 ++++------------- web/xfa_layer_builder.js | 1 + 4 files changed, 68 insertions(+), 100 deletions(-) diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index a05041a19da01..527e2c842ca01 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -890,71 +890,45 @@ class CheckButton extends XFAObject { } [$toHTML](availableSpace) { - // TODO: shape and mark == default. - const style = toStyle(this, "border", "margin"); + // TODO: border, shape and mark. + + const style = toStyle("margin"); const size = measureToString(this.size); style.width = style.height = size; - let mark, radius; - if (this.shape === "square") { - mark = "▪"; - radius = "10%"; + let type; + let className; + let groupId; + let id; + 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 { - 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; + type = "checkbox"; + className = "xfaCheckbox"; } - const fieldId = this[$getParent]()[$getParent]()[$uid]; const input = { name: "input", attributes: { - class: "xfaCheckbox", + class: className, + style, fieldId, - type: "radio", - id: `${fieldId}-radio`, + type, }, }; - const container = this[$getParent]()[$getParent]()[$getParent](); - if (container instanceof ExclGroup) { - input.attributes.name = container[$uid]; + if (id) { + input.attributes.id = id; + } + + if (groupId) { + input.attributes.name = groupId; } return HTMLResult.success({ @@ -962,17 +936,7 @@ class CheckButton extends XFAObject { attributes: { class: "xfaLabel", }, - children: [ - input, - { - name: "span", - attributes: { - class: "xfaCheckboxMark", - mark, - style, - }, - }, - ], + children: [input], }); } } diff --git a/src/display/xfa_layer.js b/src/display/xfa_layer.js index e6dd9645341e5..9de3dbf7d1d54 100644 --- a/src/display/xfa_layer.js +++ b/src/display/xfa_layer.js @@ -16,20 +16,26 @@ import { PageViewport } from "./display_utils.js"; class XfaLayer { - static setupStorage(html, fieldId, element, storage) { + static setupStorage(html, fieldId, element, storage, intent) { const storedData = storage.getValue(fieldId, { value: null }); switch (element.name) { case "textarea": html.textContent = storedData.value !== null ? storedData.value : ""; + if (intent === "print") { + break; + } html.addEventListener("input", event => { storage.setValue(fieldId, { value: event.target.value }); }); break; case "input": - if (storedData.value !== null) { - html.setAttribute("value", storedData.value); - } if (element.attributes.type === "radio") { + if (storedData.value) { + html.setAttribute("checked", true); + } + if (intent === "print") { + break; + } html.addEventListener("change", event => { const { target } = event; for (const radio of document.getElementsByName(target.name)) { @@ -40,7 +46,23 @@ class XfaLayer { } 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 { + if (storedData.value !== null) { + html.setAttribute("value", storedData.value); + } + if (intent === "print") { + break; + } html.addEventListener("input", event => { 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; + 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)) { if (value === null || value === undefined || key === "fieldId") { continue; @@ -94,6 +121,7 @@ class XfaLayer { static render(parameters) { const storage = parameters.annotationStorage; const root = parameters.xfa; + const intent = parameters.intent; const rootHtml = document.createElement(root.name); if (root.attributes) { this.setAttributes(rootHtml, root); @@ -134,7 +162,7 @@ class XfaLayer { const childHtml = document.createElement(name); html.appendChild(childHtml); if (child.attributes) { - this.setAttributes(childHtml, child, storage); + this.setAttributes(childHtml, child, storage, intent); } if (child.children && child.children.length > 0) { diff --git a/web/xfa_layer_builder.css b/web/xfa_layer_builder.css index 19a897660efd8..f812aa6089d61 100644 --- a/web/xfa_layer_builder.css +++ b/web/xfa_layer_builder.css @@ -125,38 +125,6 @@ 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 { cursor: pointer; width: 100%; @@ -169,6 +137,13 @@ background: Highlight; } +.xfaCheckbox, +.xfaRadio { + width: 100%; + height: 100%; + border: none; +} + .xfaRich { white-space: pre-wrap; } diff --git a/web/xfa_layer_builder.js b/web/xfa_layer_builder.js index 5ef667749abe7..99c95c5de0d90 100644 --- a/web/xfa_layer_builder.js +++ b/web/xfa_layer_builder.js @@ -82,6 +82,7 @@ class XfaLayerBuilder { xfa: this.xfaHtml, page: null, annotationStorage: this.annotationStorage, + intent, }; // Create an xfa layer div and render the form