From e686db250cdb37647f6ba9c05be6b26fc9a07ea6 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Tue, 6 Sep 2016 22:26:57 +0200 Subject: [PATCH 1/2] Render interactive form (AcroForm) text widget annotations This patch is the first step towards implementing support for interactive forms (AcroForms). It makes it possible to render text widget annotations exactly like Adobe Reader/Acrobat. Everything we implement for AcroForms is disabled by default using a preference, mainly because it is not ready to use yet, but has to implemented in many steps to avoid complexity. The preference allows us to work with the code while not exposing the behavior by default. Mainly storing entered values and printing them is still absent, which would be minimal requirements for enabling this by default. --- extensions/chromium/preferences_schema.json | 4 ++ src/display/annotation_layer.js | 41 ++++++++++++++------- src/display/global.js | 7 ++++ web/annotation_layer_builder.css | 20 ++++++++++ web/annotation_layer_builder.js | 3 +- web/app.js | 3 ++ web/default_preferences.json | 3 +- 7 files changed, 66 insertions(+), 15 deletions(-) diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index c4ea94889..36f2766a3 100644 --- a/extensions/chromium/preferences_schema.json +++ b/extensions/chromium/preferences_schema.json @@ -94,6 +94,10 @@ "type": "boolean", "description": "Whether to prevent the extension from reporting the extension and browser version to the extension developers.", "default": false + }, + "renderInteractiveForms": { + "type": "boolean", + "default": false } } } diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index ec5bde70a..df23d941a 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -45,6 +45,8 @@ var getDefaultSetting = displayDOMUtils.getDefaultSetting; * @property {PageViewport} viewport * @property {IPDFLinkService} linkService * @property {DownloadManager} downloadManager + * @property {string} imageResourcesPath + * @property {boolean} renderInteractiveForms */ /** @@ -115,6 +117,7 @@ var AnnotationElement = (function AnnotationElementClosure() { this.linkService = parameters.linkService; this.downloadManager = parameters.downloadManager; this.imageResourcesPath = parameters.imageResourcesPath; + this.renderInteractiveForms = parameters.renderInteractiveForms; if (isRenderable) { this.container = this._createContainer(); @@ -437,18 +440,28 @@ var TextWidgetAnnotationElement = ( * @returns {HTMLSectionElement} */ render: function TextWidgetAnnotationElement_render() { - var content = document.createElement('div'); - content.textContent = this.data.fieldValue; - var textAlignment = this.data.textAlignment; - content.style.textAlign = ['left', 'center', 'right'][textAlignment]; - content.style.verticalAlign = 'middle'; - content.style.display = 'table-cell'; - - var font = (this.data.fontRefName ? - this.page.commonObjs.getData(this.data.fontRefName) : null); - this._setTextStyle(content, font); - - this.container.appendChild(content); + this.container.className = 'textWidgetAnnotation'; + + if (this.renderInteractiveForms) { + var input = document.createElement('input'); + input.type = 'text'; + input.value = this.data.fieldValue; + + this.container.appendChild(input); + } else { + var content = document.createElement('div'); + content.textContent = this.data.fieldValue; + var textAlignment = this.data.textAlignment; + content.style.textAlign = ['left', 'center', 'right'][textAlignment]; + content.style.verticalAlign = 'middle'; + content.style.display = 'table-cell'; + + var font = (this.data.fontRefName ? + this.page.commonObjs.getData(this.data.fontRefName) : null); + this._setTextStyle(content, font); + + this.container.appendChild(content); + } return this.container; }, @@ -875,6 +888,7 @@ var FileAttachmentAnnotationElement = ( * @property {PDFPage} page * @property {IPDFLinkService} linkService * @property {string} imageResourcesPath + * @property {boolean} renderInteractiveForms */ /** @@ -907,7 +921,8 @@ var AnnotationLayer = (function AnnotationLayerClosure() { linkService: parameters.linkService, downloadManager: parameters.downloadManager, imageResourcesPath: parameters.imageResourcesPath || - getDefaultSetting('imageResourcesPath') + getDefaultSetting('imageResourcesPath'), + renderInteractiveForms: parameters.renderInteractiveForms || false, }; var element = annotationElementFactory.create(properties); if (element.isRenderable) { diff --git a/src/display/global.js b/src/display/global.js index 70def4c7c..044a09e82 100644 --- a/src/display/global.js +++ b/src/display/global.js @@ -243,6 +243,13 @@ PDFJS.isEvalSupported = (PDFJS.isEvalSupported === undefined ? true : PDFJS.isEvalSupported); + /** + * Renders interactive form elements. + * @var {boolean} + */ + PDFJS.renderInteractiveForms = (PDFJS.renderInteractiveForms === undefined ? + false : PDFJS.renderInteractiveForms); + //#if !MOZCENTRAL var savedOpenExternalLinksInNewWindow = PDFJS.openExternalLinksInNewWindow; delete PDFJS.openExternalLinksInNewWindow; diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index 03494430a..36b62b064 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -41,6 +41,26 @@ cursor: pointer; } +.annotationLayer .textWidgetAnnotation input { + background-color: rgba(0, 54, 255, 0.13); + border: 1px solid transparent; + box-sizing: border-box; + font-size: 9px; + height: 100%; + padding: 0 3px; + vertical-align: top; + width: 100%; +} + +.annotationLayer .textWidgetAnnotation input:hover { + border: 1px solid #000; +} + +.annotationLayer .textWidgetAnnotation input:focus { + background: none; + border: 1px solid transparent; +} + .annotationLayer .popupWrapper { position: absolute; width: 20em; diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index ceca000b9..5d8dd03b5 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -78,7 +78,8 @@ var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() { annotations: annotations, page: self.pdfPage, linkService: self.linkService, - downloadManager: self.downloadManager + downloadManager: self.downloadManager, + renderInteractiveForms: pdfjsLib.PDFJS.renderInteractiveForms, }; if (self.div) { diff --git a/web/app.js b/web/app.js index a192bfbc7..81e440db7 100644 --- a/web/app.js +++ b/web/app.js @@ -359,6 +359,9 @@ var PDFViewerApplication = { } PDFJS.externalLinkTarget = value; }), + Preferences.get('renderInteractiveForms').then(function resolved(value) { + PDFJS.renderInteractiveForms = value; + }), // TODO move more preferences and other async stuff here ]).catch(function (reason) { }); diff --git a/web/default_preferences.json b/web/default_preferences.json index ebc1b4282..6102064e7 100644 --- a/web/default_preferences.json +++ b/web/default_preferences.json @@ -11,5 +11,6 @@ "disableFontFace": false, "disableTextLayer": false, "useOnlyCssZoom": false, - "externalLinkTarget": 0 + "externalLinkTarget": 0, + "renderInteractiveForms": false } From e281ce7c7313633a668719292c9f6dcfff581390 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Wed, 7 Sep 2016 16:06:18 +0200 Subject: [PATCH 2/2] Enable regression testing for interactive forms --- test/annotation_layer_test.css | 11 +++++++++++ test/driver.js | 11 +++++++---- test/test_manifest.json | 8 ++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/test/annotation_layer_test.css b/test/annotation_layer_test.css index 299e67f3f..f99ff1fad 100644 --- a/test/annotation_layer_test.css +++ b/test/annotation_layer_test.css @@ -43,6 +43,17 @@ position: absolute; } +.annotationLayer .textWidgetAnnotation input { + background-color: rgba(0, 54, 255, 0.13); + border: 1px solid transparent; + box-sizing: border-box; + font-size: 9px; + height: 100%; + padding: 0 3px; + vertical-align: top; + width: 100%; +} + .annotationLayer .popupAnnotation { display: block !important; } diff --git a/test/driver.js b/test/driver.js index 153737557..a15cc8bf7 100644 --- a/test/driver.js +++ b/test/driver.js @@ -161,7 +161,8 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() { return imagePromises; } - function rasterizeAnnotationLayer(ctx, viewport, annotations, page) { + function rasterizeAnnotationLayer(ctx, viewport, annotations, page, + renderInteractiveForms) { return new Promise(function (resolve) { // Building SVG with size of the viewport. var svg = document.createElementNS(SVG_NS, 'svg:svg'); @@ -190,7 +191,8 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() { div: div, annotations: annotations, page: page, - linkService: new LinkServiceMock() + linkService: new LinkServiceMock(), + renderInteractiveForms: renderInteractiveForms, }; PDFJS.AnnotationLayer.render(parameters); @@ -483,7 +485,7 @@ var Driver = (function DriverClosure() { textLayerCanvas = null; // Render the annotation layer if necessary. - if (task.annotations) { + if (task.annotations || task.forms) { // Create a dummy canvas for the drawing operations. annotationLayerCanvas = self.annotationLayerCanvas; if (!annotationLayerCanvas) { @@ -501,9 +503,10 @@ var Driver = (function DriverClosure() { initPromise = page.getAnnotations({ intent: 'display' }).then( function(annotations) { + var forms = task.forms || false; return rasterizeAnnotationLayer(annotationLayerContext, viewport, annotations, - page); + page, forms); }); } else { annotationLayerCanvas = null; diff --git a/test/test_manifest.json b/test/test_manifest.json index 493e04666..3e5049a2e 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -732,6 +732,14 @@ "rounds": 1, "type": "load" }, + { "id": "f1040-forms", + "file": "pdfs/f1040.pdf", + "md5": "7323b50c6d28d959b8b4b92c469b2469", + "link": true, + "rounds": 1, + "type": "eq", + "forms": true + }, { "id": "bug1046314", "file": "pdfs/bug1046314.pdf", "md5": "fc658439f44cd2dd27c8bee7e7a8344e",