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.
*/
import { PageViewport } from "./display_utils.js";
class XfaLayer {
static setupStorage(html, fieldId, element, storage) {
const storedData = storage.getValue(fieldId, { value: null });
@ -100,7 +102,12 @@ class XfaLayer {
const rootDiv = parameters.div;
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})`;
// Set defaults.

View File

@ -14,6 +14,7 @@
*/
import { RenderingCancelledException, shadow } from "pdfjs-lib";
import { getXfaHtmlForPrinting } from "./ui_utils.js";
import { PDFPrintServiceFactory } from "./app.js";
// 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);
const canvasWrapper = document.createElement("div");
canvasWrapper.setAttribute("class", "printedPage");
canvasWrapper.appendChild(canvas);
printContainer.appendChild(canvasWrapper);
@ -130,6 +132,11 @@ FirefoxPrintService.prototype = {
const body = document.querySelector("body");
body.setAttribute("data-pdfjsprinting", true);
if (pdfDocument.isPureXfa) {
getXfaHtmlForPrinting(printContainer, pdfDocument);
return;
}
for (let i = 0, ii = pagesOverview.length; i < ii; ++i) {
composePage(
pdfDocument,

View File

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

View File

@ -13,6 +13,8 @@
* limitations under the License.
*/
import { DefaultXfaLayerFactory } from "./xfa_layer_builder.js";
const CSS_UNITS = 96.0 / 72.0;
const DEFAULT_SCALE_VALUE = "auto";
const DEFAULT_SCALE = 1.0;
@ -994,6 +996,29 @@ function apiPageModeToSidebarView(mode) {
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 {
animationStarted,
apiPageLayoutToSpreadMode,
@ -1010,6 +1035,7 @@ export {
getOutputScale,
getPageSizeInches,
getVisibleElements,
getXfaHtmlForPrinting,
isPortraitOrientation,
isValidRotation,
isValidScrollMode,

View File

@ -1772,7 +1772,8 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
.toolbar,
#loadingBox,
#errorWrapper,
.textLayer {
.textLayer,
.canvasWrapper {
display: none;
}
#viewerContainer {
@ -1816,7 +1817,7 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
height: 100%;
}
/* wrapper around (scaled) print canvas elements */
#printContainer > div {
#printContainer > .printedPage {
page-break-after: always;
page-break-inside: avoid;
@ -1829,8 +1830,17 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
justify-content: 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. */
max-width: 100%;
max-height: 100%;

View File

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

View File

@ -26,9 +26,10 @@ class XfaLayerBuilder {
/**
* @param {XfaLayerBuilderOptions} options
*/
constructor({ pageDiv, pdfPage, annotationStorage }) {
constructor({ pageDiv, pdfPage, xfaHtml, annotationStorage }) {
this.pageDiv = pageDiv;
this.pdfPage = pdfPage;
this.xfaHtml = xfaHtml;
this.annotationStorage = annotationStorage;
this.div = null;
@ -42,34 +43,55 @@ class XfaLayerBuilder {
* 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,
annotationStorage: this.annotationStorage,
};
if (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,
annotationStorage: this.annotationStorage,
};
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);
}
})
.catch(error => {
console.error(error);
});
XfaLayer.render(parameters);
}
})
.catch(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() {
@ -92,12 +114,19 @@ class DefaultXfaLayerFactory {
* @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage
* @param {AnnotationStorage} [annotationStorage]
* @param {Object} [xfaHtml]
*/
createXfaLayerBuilder(pageDiv, pdfPage, annotationStorage = null) {
createXfaLayerBuilder(
pageDiv,
pdfPage,
annotationStorage = null,
xfaHtml = null
) {
return new XfaLayerBuilder({
pageDiv,
pdfPage,
annotationStorage,
xfaHtml,
});
}
}