mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-20 15:18:08 +02:00
XFA - Add a layer to display XFA forms (#13069)
- add an option to enable XFA rendering if any; - for now, let the canvas layer: it could be useful to implement XFAF forms (embedded pdf in xml stream for the background and xfa form for the foreground); - ui elements in template DOM are pretty close to their html counterpart so we generate a fake html DOM from template one: - it makes easier to translate template properties to html ones; - it makes faster the creation of the html element in the main thread.
This commit is contained in:
parent
a164941351
commit
24e598a895
20 changed files with 760 additions and 27 deletions
|
@ -518,6 +518,7 @@ const PDFViewerApplication = {
|
|||
useOnlyCssZoom: AppOptions.get("useOnlyCssZoom"),
|
||||
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
|
||||
enableScripting: AppOptions.get("enableScripting"),
|
||||
enableXfa: AppOptions.get("enableXfa"),
|
||||
});
|
||||
pdfRenderingQueue.setViewer(this.pdfViewer);
|
||||
pdfLinkService.setViewer(this.pdfViewer);
|
||||
|
|
|
@ -205,6 +205,11 @@ const defaultOptions = {
|
|||
value: "",
|
||||
kind: OptionKind.API,
|
||||
},
|
||||
enableXfa: {
|
||||
/** @type {boolean} */
|
||||
value: false,
|
||||
kind: OptionKind.API,
|
||||
},
|
||||
fontExtraProperties: {
|
||||
/** @type {boolean} */
|
||||
value: false,
|
||||
|
|
|
@ -42,6 +42,7 @@ import { NullL10n } from "./l10n_utils.js";
|
|||
import { PDFPageView } from "./pdf_page_view.js";
|
||||
import { SimpleLinkService } from "./pdf_link_service.js";
|
||||
import { TextLayerBuilder } from "./text_layer_builder.js";
|
||||
import { XfaLayerBuilder } from "./xfa_layer_builder.js";
|
||||
|
||||
const DEFAULT_CACHE_SIZE = 10;
|
||||
|
||||
|
@ -478,6 +479,7 @@ class BaseViewer {
|
|||
if (!pdfDocument) {
|
||||
return;
|
||||
}
|
||||
const isPureXfa = pdfDocument.isPureXfa;
|
||||
const pagesCount = pdfDocument.numPages;
|
||||
const firstPagePromise = pdfDocument.getPage(1);
|
||||
// Rendering (potentially) depends on this, hence fetching it immediately.
|
||||
|
@ -523,6 +525,7 @@ class BaseViewer {
|
|||
const viewport = firstPdfPage.getViewport({ scale: scale * CSS_UNITS });
|
||||
const textLayerFactory =
|
||||
this.textLayerMode !== TextLayerMode.DISABLE ? this : null;
|
||||
const xfaLayerFactory = isPureXfa ? this : null;
|
||||
|
||||
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
||||
const pageView = new PDFPageView({
|
||||
|
@ -536,6 +539,7 @@ class BaseViewer {
|
|||
textLayerFactory,
|
||||
textLayerMode: this.textLayerMode,
|
||||
annotationLayerFactory: this,
|
||||
xfaLayerFactory,
|
||||
imageResourcesPath: this.imageResourcesPath,
|
||||
renderInteractiveForms: this.renderInteractiveForms,
|
||||
renderer: this.renderer,
|
||||
|
@ -1308,6 +1312,18 @@ class BaseViewer {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLDivElement} pageDiv
|
||||
* @param {PDFPage} pdfPage
|
||||
* @returns {XfaLayerBuilder}
|
||||
*/
|
||||
createXfaLayerBuilder(pageDiv, pdfPage) {
|
||||
return new XfaLayerBuilder({
|
||||
pageDiv,
|
||||
pdfPage,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean} Whether all pages of the PDF document have identical
|
||||
* widths and heights.
|
||||
|
|
|
@ -204,6 +204,18 @@ class IPDFAnnotationLayerFactory {
|
|||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
class IPDFXfaLayerFactory {
|
||||
/**
|
||||
* @param {HTMLDivElement} pageDiv
|
||||
* @param {PDFPage} pdfPage
|
||||
* @returns {XfaLayerBuilder}
|
||||
*/
|
||||
createXfaLayerBuilder(pageDiv, pdfPage) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
|
@ -243,5 +255,6 @@ export {
|
|||
IPDFHistory,
|
||||
IPDFLinkService,
|
||||
IPDFTextLayerFactory,
|
||||
IPDFXfaLayerFactory,
|
||||
IRenderableView,
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@ import { viewerCompatibilityParams } from "./viewer_compatibility.js";
|
|||
* behaviour is enabled. The constants from {TextLayerMode} should be used.
|
||||
* The default value is `TextLayerMode.ENABLE`.
|
||||
* @property {IPDFAnnotationLayerFactory} annotationLayerFactory
|
||||
* @property {IPDFXfaLayerFactory} xfaLayerFactory
|
||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @property {boolean} renderInteractiveForms - Turns on rendering of
|
||||
|
@ -102,6 +103,7 @@ class PDFPageView {
|
|||
this.renderingQueue = options.renderingQueue;
|
||||
this.textLayerFactory = options.textLayerFactory;
|
||||
this.annotationLayerFactory = options.annotationLayerFactory;
|
||||
this.xfaLayerFactory = options.xfaLayerFactory;
|
||||
this.renderer = options.renderer || RendererType.CANVAS;
|
||||
this.enableWebGL = options.enableWebGL || false;
|
||||
this.l10n = options.l10n || NullL10n;
|
||||
|
@ -116,6 +118,7 @@ class PDFPageView {
|
|||
this.annotationLayer = null;
|
||||
this.textLayer = null;
|
||||
this.zoomLayer = null;
|
||||
this.xfaLayer = null;
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "page";
|
||||
|
@ -164,6 +167,24 @@ class PDFPageView {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async _renderXfaLayer() {
|
||||
let error = null;
|
||||
try {
|
||||
await this.xfaLayer.render(this.viewport, "display");
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
} finally {
|
||||
this.eventBus.dispatch("xfalayerrendered", {
|
||||
source: this,
|
||||
pageNumber: this.id,
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -197,9 +218,14 @@ class PDFPageView {
|
|||
const currentZoomLayerNode = (keepZoomLayer && this.zoomLayer) || null;
|
||||
const currentAnnotationNode =
|
||||
(keepAnnotations && this.annotationLayer?.div) || null;
|
||||
const currentXfaLayerNode = this.xfaLayer?.div || null;
|
||||
for (let i = childNodes.length - 1; i >= 0; i--) {
|
||||
const node = childNodes[i];
|
||||
if (currentZoomLayerNode === node || currentAnnotationNode === node) {
|
||||
if (
|
||||
currentZoomLayerNode === node ||
|
||||
currentAnnotationNode === node ||
|
||||
currentXfaLayerNode === node
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
div.removeChild(node);
|
||||
|
@ -393,6 +419,10 @@ class PDFPageView {
|
|||
if (redrawAnnotations && this.annotationLayer) {
|
||||
this._renderAnnotationLayer();
|
||||
}
|
||||
|
||||
if (this.xfaLayer) {
|
||||
this._renderXfaLayer();
|
||||
}
|
||||
}
|
||||
|
||||
get width() {
|
||||
|
@ -553,6 +583,17 @@ class PDFPageView {
|
|||
}
|
||||
this._renderAnnotationLayer();
|
||||
}
|
||||
|
||||
if (this.xfaLayerFactory) {
|
||||
if (!this.xfaLayer) {
|
||||
this.xfaLayer = this.xfaLayerFactory.createXfaLayerBuilder(
|
||||
div,
|
||||
pdfPage
|
||||
);
|
||||
}
|
||||
this._renderXfaLayer();
|
||||
}
|
||||
|
||||
div.setAttribute("data-loaded", true);
|
||||
|
||||
this.eventBus.dispatch("pagerender", {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
@import url(text_layer_builder.css);
|
||||
@import url(annotation_layer_builder.css);
|
||||
@import url(xfa_layer_builder.css);
|
||||
|
||||
.pdfViewer .canvasWrapper {
|
||||
overflow: hidden;
|
||||
|
|
22
web/xfa_layer_builder.css
Normal file
22
web/xfa_layer_builder.css
Normal file
|
@ -0,0 +1,22 @@
|
|||
*/* Copyright 2021 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.xfaLayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 200;
|
||||
transform-origin: 0 0;
|
||||
}
|
97
web/xfa_layer_builder.js
Normal file
97
web/xfa_layer_builder.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* Copyright 2021 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { XfaLayer } from "pdfjs-lib";
|
||||
|
||||
/**
|
||||
* @typedef {Object} XfaLayerBuilderOptions
|
||||
* @property {HTMLDivElement} pageDiv
|
||||
* @property {PDFPage} pdfPage
|
||||
*/
|
||||
|
||||
class XfaLayerBuilder {
|
||||
/**
|
||||
* @param {XfaLayerBuilderOptions} options
|
||||
*/
|
||||
constructor({ pageDiv, pdfPage }) {
|
||||
this.pageDiv = pageDiv;
|
||||
this.pdfPage = pdfPage;
|
||||
|
||||
this.div = null;
|
||||
this._cancelled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PageViewport} viewport
|
||||
* @param {string} intent (default value is 'display')
|
||||
* @returns {Promise<void>} A promise that is resolved when rendering of the
|
||||
* annotations is complete.
|
||||
*/
|
||||
render(viewport, intent = "display") {
|
||||
return this.pdfPage.getXfa().then(xfa => {
|
||||
if (this._cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.div.className = "xfaLayer";
|
||||
this.pageDiv.appendChild(this.div);
|
||||
parameters.div = this.div;
|
||||
|
||||
XfaLayer.render(parameters);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this._cancelled = true;
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.div) {
|
||||
return;
|
||||
}
|
||||
this.div.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements IPDFXfaLayerFactory
|
||||
*/
|
||||
class DefaultXfaLayerFactory {
|
||||
/**
|
||||
* @param {HTMLDivElement} pageDiv
|
||||
* @param {PDFPage} pdfPage
|
||||
*/
|
||||
createXfaLayerBuilder(pageDiv, pdfPage) {
|
||||
return new XfaLayerBuilder({
|
||||
pageDiv,
|
||||
pdfPage,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { DefaultXfaLayerFactory, XfaLayerBuilder };
|
Loading…
Add table
Add a link
Reference in a new issue