XFA - Add support to print XFA forms

This commit is contained in:
Calixte Denizet 2021-05-27 16:56:31 +02:00
parent 8c53bf8647
commit a434011517
7 changed files with 130 additions and 35 deletions

View File

@ -13,6 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { PageViewport } from "./display_utils.js";
class XfaLayer { class XfaLayer {
static setupStorage(html, fieldId, element, storage) { static setupStorage(html, fieldId, element, storage) {
const storedData = storage.getValue(fieldId, { value: null }); const storedData = storage.getValue(fieldId, { value: null });
@ -100,7 +102,12 @@ class XfaLayer {
const rootDiv = parameters.div; const rootDiv = parameters.div;
rootDiv.appendChild(rootHtml); rootDiv.appendChild(rootHtml);
const coeffs = parameters.viewport.transform.join(",");
let { viewport } = parameters;
if (!(viewport instanceof PageViewport)) {
viewport = new PageViewport(viewport);
}
const coeffs = viewport.transform.join(",");
rootDiv.style.transform = `matrix(${coeffs})`; rootDiv.style.transform = `matrix(${coeffs})`;
// Set defaults. // Set defaults.

View File

@ -14,6 +14,7 @@
*/ */
import { RenderingCancelledException, shadow } from "pdfjs-lib"; import { RenderingCancelledException, shadow } from "pdfjs-lib";
import { getXfaHtmlForPrinting } from "./ui_utils.js";
import { PDFPrintServiceFactory } from "./app.js"; import { PDFPrintServiceFactory } from "./app.js";
// Creates a placeholder with div and canvas with right size for the page. // Creates a placeholder with div and canvas with right size for the page.
@ -33,6 +34,7 @@ function composePage(
canvas.height = Math.floor(size.height * PRINT_UNITS); canvas.height = Math.floor(size.height * PRINT_UNITS);
const canvasWrapper = document.createElement("div"); const canvasWrapper = document.createElement("div");
canvasWrapper.setAttribute("class", "printedPage");
canvasWrapper.appendChild(canvas); canvasWrapper.appendChild(canvas);
printContainer.appendChild(canvasWrapper); printContainer.appendChild(canvasWrapper);
@ -130,6 +132,11 @@ FirefoxPrintService.prototype = {
const body = document.querySelector("body"); const body = document.querySelector("body");
body.setAttribute("data-pdfjsprinting", true); body.setAttribute("data-pdfjsprinting", true);
if (pdfDocument.isPureXfa) {
getXfaHtmlForPrinting(printContainer, pdfDocument);
return;
}
for (let i = 0, ii = pagesOverview.length; i < ii; ++i) { for (let i = 0, ii = pagesOverview.length; i < ii; ++i) {
composePage( composePage(
pdfDocument, pdfDocument,

View File

@ -14,6 +14,7 @@
*/ */
import { PDFPrintServiceFactory, PDFViewerApplication } from "./app.js"; import { PDFPrintServiceFactory, PDFViewerApplication } from "./app.js";
import { getXfaHtmlForPrinting } from "./ui_utils.js";
import { viewerCompatibilityParams } from "./viewer_compatibility.js"; import { viewerCompatibilityParams } from "./viewer_compatibility.js";
let activeService = null; let activeService = null;
@ -139,6 +140,11 @@ PDFPrintService.prototype = {
}, },
renderPages() { renderPages() {
if (this.pdfDocument.isPureXfa) {
getXfaHtmlForPrinting(this.printContainer, this.pdfDocument);
return Promise.resolve();
}
const pageCount = this.pagesOverview.length; const pageCount = this.pagesOverview.length;
const renderNextPage = (resolve, reject) => { const renderNextPage = (resolve, reject) => {
this.throwIfInactive(); this.throwIfInactive();
@ -157,8 +163,10 @@ PDFPrintService.prototype = {
this._printResolution, this._printResolution,
this._optionalContentConfigPromise this._optionalContentConfigPromise
) )
.then(this.useRenderedPage.bind(this)) .then(() => {
.then(function () { this.useRenderedPage.bind(this);
})
.then(() => {
renderNextPage(resolve, reject); renderNextPage(resolve, reject);
}, reject); }, reject);
}; };
@ -181,6 +189,7 @@ PDFPrintService.prototype = {
} }
const wrapper = document.createElement("div"); const wrapper = document.createElement("div");
wrapper.setAttribute("class", "printedPage");
wrapper.appendChild(img); wrapper.appendChild(img);
this.printContainer.appendChild(wrapper); this.printContainer.appendChild(wrapper);

View File

@ -13,6 +13,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { DefaultXfaLayerFactory } from "./xfa_layer_builder.js";
const CSS_UNITS = 96.0 / 72.0; const CSS_UNITS = 96.0 / 72.0;
const DEFAULT_SCALE_VALUE = "auto"; const DEFAULT_SCALE_VALUE = "auto";
const DEFAULT_SCALE = 1.0; const DEFAULT_SCALE = 1.0;
@ -994,6 +996,29 @@ function apiPageModeToSidebarView(mode) {
return SidebarView.NONE; // Default value. return SidebarView.NONE; // Default value.
} }
function getXfaHtmlForPrinting(printContainer, pdfDocument) {
const xfaHtml = pdfDocument.allXfaHtml;
const factory = new DefaultXfaLayerFactory();
const scale = Math.round(CSS_UNITS * 100) / 100;
for (const xfaPage of xfaHtml.children) {
const page = document.createElement("div");
page.setAttribute("class", "xfaPrintedPage");
printContainer.appendChild(page);
const { width, height } = xfaPage.attributes.style;
const viewBox = [0, 0, parseInt(width), parseInt(height)];
const viewport = { viewBox, scale, rotation: 0 };
const builder = factory.createXfaLayerBuilder(
page,
null,
pdfDocument.annotationStorage,
xfaPage
);
builder.render(viewport, "print");
}
}
export { export {
animationStarted, animationStarted,
apiPageLayoutToSpreadMode, apiPageLayoutToSpreadMode,
@ -1010,6 +1035,7 @@ export {
getOutputScale, getOutputScale,
getPageSizeInches, getPageSizeInches,
getVisibleElements, getVisibleElements,
getXfaHtmlForPrinting,
isPortraitOrientation, isPortraitOrientation,
isValidRotation, isValidRotation,
isValidScrollMode, isValidScrollMode,

View File

@ -1772,7 +1772,8 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
.toolbar, .toolbar,
#loadingBox, #loadingBox,
#errorWrapper, #errorWrapper,
.textLayer { .textLayer,
.canvasWrapper {
display: none; display: none;
} }
#viewerContainer { #viewerContainer {
@ -1816,7 +1817,7 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
height: 100%; height: 100%;
} }
/* wrapper around (scaled) print canvas elements */ /* wrapper around (scaled) print canvas elements */
#printContainer > div { #printContainer > .printedPage {
page-break-after: always; page-break-after: always;
page-break-inside: avoid; page-break-inside: avoid;
@ -1829,8 +1830,17 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
#printContainer canvas,
#printContainer img { #printContainer > .xfaPrintedPage {
page-break-after: always;
page-break-inside: avoid;
width: 100%;
height: 100%;
position: relative;
}
.printedPage canvas,
.printedPage img {
/* The intrinsic canvas / image size will make sure that we fit the page. */ /* The intrinsic canvas / image size will make sure that we fit the page. */
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;

View File

@ -262,3 +262,10 @@
.xfaTable .xfaRlRow > div { .xfaTable .xfaRlRow > div {
flex: 1; flex: 1;
} }
@media print {
.xfaTextfield,
.xfaSelect {
background-color: transparent;
}
}

View File

@ -26,9 +26,10 @@ class XfaLayerBuilder {
/** /**
* @param {XfaLayerBuilderOptions} options * @param {XfaLayerBuilderOptions} options
*/ */
constructor({ pageDiv, pdfPage, annotationStorage }) { constructor({ pageDiv, pdfPage, xfaHtml, annotationStorage }) {
this.pageDiv = pageDiv; this.pageDiv = pageDiv;
this.pdfPage = pdfPage; this.pdfPage = pdfPage;
this.xfaHtml = xfaHtml;
this.annotationStorage = annotationStorage; this.annotationStorage = annotationStorage;
this.div = null; this.div = null;
@ -42,34 +43,55 @@ class XfaLayerBuilder {
* annotations is complete. * annotations is complete.
*/ */
render(viewport, intent = "display") { render(viewport, intent = "display") {
return this.pdfPage if (intent === "display") {
.getXfa() return this.pdfPage
.then(xfa => { .getXfa()
if (this._cancelled) { .then(xfa => {
return; if (this._cancelled) {
} return;
const parameters = { }
viewport: viewport.clone({ dontFlip: true }), const parameters = {
div: this.div, viewport: viewport.clone({ dontFlip: true }),
xfa, div: this.div,
page: this.pdfPage, xfa,
annotationStorage: this.annotationStorage, page: this.pdfPage,
}; annotationStorage: this.annotationStorage,
};
if (this.div) { if (this.div) {
XfaLayer.update(parameters); XfaLayer.update(parameters);
} else { } else {
// Create an xfa layer div and render the form // Create an xfa layer div and render the form
this.div = document.createElement("div"); this.div = document.createElement("div");
this.pageDiv.appendChild(this.div); this.pageDiv.appendChild(this.div);
parameters.div = this.div; parameters.div = this.div;
XfaLayer.render(parameters); XfaLayer.render(parameters);
} }
}) })
.catch(error => { .catch(error => {
console.error(error); console.error(error);
}); });
}
// intent === "print".
viewport.dontFlip = true;
const parameters = {
viewport,
div: this.div,
xfa: this.xfaHtml,
page: null,
annotationStorage: this.annotationStorage,
};
// Create an xfa layer div and render the form
const div = document.createElement("div");
this.pageDiv.appendChild(div);
parameters.div = div;
XfaLayer.render(parameters);
return null;
} }
cancel() { cancel() {
@ -92,12 +114,19 @@ class DefaultXfaLayerFactory {
* @param {HTMLDivElement} pageDiv * @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage * @param {PDFPage} pdfPage
* @param {AnnotationStorage} [annotationStorage] * @param {AnnotationStorage} [annotationStorage]
* @param {Object} [xfaHtml]
*/ */
createXfaLayerBuilder(pageDiv, pdfPage, annotationStorage = null) { createXfaLayerBuilder(
pageDiv,
pdfPage,
annotationStorage = null,
xfaHtml = null
) {
return new XfaLayerBuilder({ return new XfaLayerBuilder({
pageDiv, pageDiv,
pdfPage, pdfPage,
annotationStorage, annotationStorage,
xfaHtml,
}); });
} }
} }