diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index ef7e89e9f..b29dc3e0a 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -941,11 +941,14 @@ class CheckButton extends XFAObject { style.height = size; } + const fieldId = this[$getParent]()[$getParent]()[$uid]; const input = { name: "input", attributes: { class: "xfaCheckbox", + fieldId, type: "radio", + id: `${fieldId}-radio`, }, }; @@ -1023,6 +1026,7 @@ class ChoiceList extends XFAObject { const selectAttributes = { class: "xfaSelect", + fieldId: this[$getParent]()[$getParent]()[$uid], style, }; @@ -1249,6 +1253,7 @@ class DateTimeEdit extends XFAObject { name: "input", attributes: { type: "text", + fieldId: this[$getParent]()[$getParent]()[$uid], class: "xfaTextfield", style, }, @@ -3012,6 +3017,7 @@ class NumericEdit extends XFAObject { name: "input", attributes: { type: "text", + fieldId: this[$getParent]()[$getParent]()[$uid], class: "xfaTextfield", style, }, @@ -4550,6 +4556,7 @@ class TextEdit extends XFAObject { html = { name: "textarea", attributes: { + fieldId: this[$getParent]()[$getParent]()[$uid], class: "xfaTextfield", style, }, @@ -4559,6 +4566,7 @@ class TextEdit extends XFAObject { name: "input", attributes: { type: "text", + fieldId: this[$getParent]()[$getParent]()[$uid], class: "xfaTextfield", style, }, diff --git a/src/display/xfa_layer.js b/src/display/xfa_layer.js index 5949d5661..85d94f860 100644 --- a/src/display/xfa_layer.js +++ b/src/display/xfa_layer.js @@ -14,9 +14,60 @@ */ class XfaLayer { - static setAttributes(html, attrs) { - for (const [key, value] of Object.entries(attrs)) { - if (value === null || value === undefined) { + static setupStorage(html, fieldId, element, storage) { + const storedData = storage.getValue(fieldId, { value: null }); + switch (element.name) { + case "textarea": + html.textContent = storedData.value !== null ? storedData.value : ""; + 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") { + html.addEventListener("change", event => { + const { target } = event; + for (const radio of document.getElementsByName(target.name)) { + if (radio !== target) { + const id = radio.id; + storage.setValue(id.split("-")[0], { value: false }); + } + } + storage.setValue(fieldId, { value: target.checked }); + }); + } else { + html.addEventListener("input", event => { + storage.setValue(fieldId, { value: event.target.value }); + }); + } + break; + case "select": + if (storedData.value !== null) { + for (const option of element.children) { + if (option.attributes.value === storedData.value) { + option.attributes.selected = true; + } + } + } + html.addEventListener("input", event => { + const options = event.target.options; + const value = + options.selectedIndex === -1 + ? null + : options[options.selectedIndex].value; + storage.setValue(fieldId, { value }); + }); + break; + } + } + + static setAttributes(html, element, storage) { + const { attributes } = element; + for (const [key, value] of Object.entries(attributes)) { + if (value === null || value === undefined || key === "fieldId") { continue; } @@ -30,13 +81,20 @@ class XfaLayer { Object.assign(html.style, value); } } + + // Set the value after the others to be sure overwrite + // any other values. + if (storage && attributes.fieldId !== undefined) { + this.setupStorage(html, attributes.fieldId, element, storage); + } } static render(parameters) { + const storage = parameters.annotationStorage; const root = parameters.xfa; const rootHtml = document.createElement(root.name); if (root.attributes) { - XfaLayer.setAttributes(rootHtml, root.attributes); + this.setAttributes(rootHtml, root); } const stack = [[root, -1, rootHtml]]; @@ -69,7 +127,7 @@ class XfaLayer { const childHtml = document.createElement(name); html.appendChild(childHtml); if (child.attributes) { - XfaLayer.setAttributes(childHtml, child.attributes); + this.setAttributes(childHtml, child, storage); } if (child.children && child.children.length > 0) { diff --git a/web/base_viewer.js b/web/base_viewer.js index 2468e1cfc..ec36ad9c5 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -1317,12 +1317,16 @@ class BaseViewer { /** * @param {HTMLDivElement} pageDiv * @param {PDFPage} pdfPage + * @param {AnnotationStorage} [annotationStorage] - Storage for annotation + * data in forms. * @returns {XfaLayerBuilder} */ - createXfaLayerBuilder(pageDiv, pdfPage) { + createXfaLayerBuilder(pageDiv, pdfPage, annotationStorage = null) { return new XfaLayerBuilder({ pageDiv, pdfPage, + annotationStorage: + annotationStorage || this.pdfDocument?.annotationStorage, }); } diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index ee85d4809..4cf10b0fc 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -603,7 +603,8 @@ class PDFPageView { if (!this.xfaLayer) { this.xfaLayer = this.xfaLayerFactory.createXfaLayerBuilder( div, - pdfPage + pdfPage, + /* annotationStorage = */ null ); } this._renderXfaLayer(); diff --git a/web/xfa_layer_builder.js b/web/xfa_layer_builder.js index 1b13a13cb..e7d2ae2ea 100644 --- a/web/xfa_layer_builder.js +++ b/web/xfa_layer_builder.js @@ -19,15 +19,17 @@ import { XfaLayer } from "pdfjs-lib"; * @typedef {Object} XfaLayerBuilderOptions * @property {HTMLDivElement} pageDiv * @property {PDFPage} pdfPage + * @property {AnnotationStorage} [annotationStorage] */ class XfaLayerBuilder { /** * @param {XfaLayerBuilderOptions} options */ - constructor({ pageDiv, pdfPage }) { + constructor({ pageDiv, pdfPage, annotationStorage }) { this.pageDiv = pageDiv; this.pdfPage = pdfPage; + this.annotationStorage = annotationStorage; this.div = null; this._cancelled = false; @@ -40,29 +42,34 @@ class XfaLayerBuilder { * annotations is complete. */ render(viewport, intent = "display") { - return this.pdfPage.getXfa().then(xfa => { - if (this._cancelled) { - return; - } + return this.pdfPage + .getXfa() + .then(xfa => { + if (this._cancelled) { + return; + } + const parameters = { + viewport: viewport.clone({ dontFlip: true }), + div: this.div, + xfa, + page: this.pdfPage, + annotationStorage: this.annotationStorage, + }; - const parameters = { - viewport: viewport.clone({ dontFlip: true }), - div: this.div, - xfa, - page: this.pdfPage, - }; + if (this.div) { + XfaLayer.update(parameters); + } else { + // Create an xfa layer div and render the form + this.div = document.createElement("div"); + this.pageDiv.appendChild(this.div); + parameters.div = this.div; - if (this.div) { - XfaLayer.update(parameters); - } else { - // Create an xfa layer div and render the form - this.div = document.createElement("div"); - this.pageDiv.appendChild(this.div); - parameters.div = this.div; - - XfaLayer.render(parameters); - } - }); + XfaLayer.render(parameters); + } + }) + .catch(error => { + console.error(error); + }); } cancel() { @@ -84,11 +91,13 @@ class DefaultXfaLayerFactory { /** * @param {HTMLDivElement} pageDiv * @param {PDFPage} pdfPage + * @param {AnnotationStorage} [annotationStorage] */ - createXfaLayerBuilder(pageDiv, pdfPage) { + createXfaLayerBuilder(pageDiv, pdfPage, annotationStorage = null) { return new XfaLayerBuilder({ pageDiv, pdfPage, + annotationStorage, }); } }