Skip to content

Commit a47e65e

Browse files
committed
XFA - Add support to print XFA forms
1 parent faf6b10 commit a47e65e

File tree

5 files changed

+91
-17
lines changed

5 files changed

+91
-17
lines changed

web/base_viewer.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -581,21 +581,35 @@ class BaseViewer {
581581
if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > 7500) {
582582
// XXX: Printing is semi-broken with auto fetch disabled.
583583
this._pagesCapability.resolve();
584-
return;
584+
return null;
585585
}
586586
let getPagesLeft = pagesCount - 1; // The first page was already loaded.
587587

588588
if (getPagesLeft <= 0) {
589589
this._pagesCapability.resolve();
590-
return;
590+
return null;
591591
}
592+
593+
const xfaDrawPromises = [];
594+
592595
for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
593596
pdfDocument.getPage(pageNum).then(
594597
pdfPage => {
595598
const pageView = this._pages[pageNum - 1];
596599
if (!pageView.pdfPage) {
597600
pageView.setPdfPage(pdfPage);
598601
}
602+
if (isPureXfa) {
603+
// Force the rendering of all pages (in case the pdf is
604+
// printed). It isn't really an issue since the document
605+
// has been fully layed out in worker in order to just
606+
// get the total page number. For now, in order to print
607+
// a filled form, we print the generated html as the user
608+
// can see it. It could lead to a memory issue in case
609+
// the document as a huge number of pages which is pretty
610+
// unlikely since normally XFA is used to render forms.
611+
xfaDrawPromises.push(pageView.draw());
612+
}
599613
this.linkService.cachePageRef(pageNum, pdfPage.ref);
600614
if (--getPagesLeft === 0) {
601615
this._pagesCapability.resolve();
@@ -612,6 +626,11 @@ class BaseViewer {
612626
}
613627
);
614628
}
629+
630+
if (isPureXfa) {
631+
return Promise.all(xfaDrawPromises);
632+
}
633+
return null;
615634
});
616635

617636
this.eventBus.dispatch("pagesinit", { source: this });

web/firefox_print_service.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
import { RenderingCancelledException, shadow } from "pdfjs-lib";
17+
import { cloneXfaPagesForPrinting } from "./ui_utils.js";
1718
import { PDFPrintServiceFactory } from "./app.js";
1819

1920
// Creates a placeholder with div and canvas with right size for the page.
@@ -33,6 +34,7 @@ function composePage(
3334
canvas.height = Math.floor(size.height * PRINT_UNITS);
3435

3536
const canvasWrapper = document.createElement("div");
37+
canvasWrapper.setAttribute("class", "printedPage");
3638
canvasWrapper.appendChild(canvas);
3739
printContainer.appendChild(canvasWrapper);
3840

@@ -130,15 +132,19 @@ FirefoxPrintService.prototype = {
130132
const body = document.querySelector("body");
131133
body.setAttribute("data-pdfjsprinting", true);
132134

133-
for (let i = 0, ii = pagesOverview.length; i < ii; ++i) {
134-
composePage(
135-
pdfDocument,
136-
/* pageNumber = */ i + 1,
137-
pagesOverview[i],
138-
printContainer,
139-
_printResolution,
140-
_optionalContentConfigPromise
141-
);
135+
if (pdfDocument.isPureXfa && pagesOverview.length > 0) {
136+
cloneXfaPagesForPrinting(printContainer, pagesOverview);
137+
} else {
138+
for (let i = 0, ii = pagesOverview.length; i < ii; ++i) {
139+
composePage(
140+
pdfDocument,
141+
/* pageNumber = */ i + 1,
142+
pagesOverview[i],
143+
printContainer,
144+
_printResolution,
145+
_optionalContentConfigPromise
146+
);
147+
}
142148
}
143149
},
144150

web/pdf_print_service.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
import { PDFPrintServiceFactory, PDFViewerApplication } from "./app.js";
17+
import { cloneXfaPagesForPrinting } from "./ui_utils.js";
1718
import { viewerCompatibilityParams } from "./viewer_compatibility.js";
1819

1920
let activeService = null;
@@ -27,7 +28,8 @@ function renderPage(
2728
pageNumber,
2829
size,
2930
printResolution,
30-
optionalContentConfigPromise
31+
optionalContentConfigPromise,
32+
printContainer
3133
) {
3234
const scratchCanvas = activeService.scratchCanvas;
3335

@@ -140,6 +142,15 @@ PDFPrintService.prototype = {
140142

141143
renderPages() {
142144
const pageCount = this.pagesOverview.length;
145+
if (pageCount === 0) {
146+
return Promise.resolve();
147+
}
148+
149+
if (this.pdfDocument.isPureXfa) {
150+
cloneXfaPagesForPrinting(this.printContainer, this.pagesOverview[0]);
151+
return Promise.resolve();
152+
}
153+
143154
const renderNextPage = (resolve, reject) => {
144155
this.throwIfInactive();
145156
if (++this.currentPage >= pageCount) {
@@ -155,7 +166,8 @@ PDFPrintService.prototype = {
155166
/* pageNumber = */ index + 1,
156167
this.pagesOverview[index],
157168
this._printResolution,
158-
this._optionalContentConfigPromise
169+
this._optionalContentConfigPromise,
170+
this.printContainer
159171
)
160172
.then(this.useRenderedPage.bind(this))
161173
.then(function () {
@@ -181,6 +193,7 @@ PDFPrintService.prototype = {
181193
}
182194

183195
const wrapper = document.createElement("div");
196+
wrapper.setAttribute("class", "printedPage");
184197
wrapper.appendChild(img);
185198
this.printContainer.appendChild(wrapper);
186199

web/ui_utils.js

+28
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,33 @@ function apiPageModeToSidebarView(mode) {
994994
return SidebarView.NONE; // Default value.
995995
}
996996

997+
function cloneXfaPagesForPrinting(container, pagesOverview) {
998+
const ratio = CSS_UNITS;
999+
const viewer = document.getElementById("viewer");
1000+
let pageNumber = 0;
1001+
1002+
for (const element of viewer.getElementsByClassName("page")) {
1003+
const clone = element.cloneNode(true);
1004+
clone.setAttribute("class", "xfaPrintedPage");
1005+
let pageSize;
1006+
if (Array.isArray(pagesOverview)) {
1007+
pageSize = pagesOverview[pageNumber++];
1008+
} else {
1009+
pageSize = pagesOverview;
1010+
}
1011+
clone.style.width = `${pageSize.width * ratio}px`;
1012+
clone.style.height = `${pageSize.height * ratio}px`;
1013+
for (let i = 0; i < clone.childNodes.length; i++) {
1014+
const child = clone.childNodes[i];
1015+
if (!child.getAttribute("class").includes("xfaLayer")) {
1016+
continue;
1017+
}
1018+
child.style.transform = `scale(${ratio},${ratio})`;
1019+
}
1020+
container.appendChild(clone);
1021+
}
1022+
}
1023+
9971024
export {
9981025
animationStarted,
9991026
apiPageLayoutToSpreadMode,
@@ -1002,6 +1029,7 @@ export {
10021029
AutoPrintRegExp,
10031030
backtrackBeforeAllVisibleElements, // only exported for testing
10041031
binarySearchFirstItem,
1032+
cloneXfaPagesForPrinting,
10051033
CSS_UNITS,
10061034
DEFAULT_SCALE,
10071035
DEFAULT_SCALE_VALUE,

web/viewer.css

+12-4
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,8 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
17721772
.toolbar,
17731773
#loadingBox,
17741774
#errorWrapper,
1775-
.textLayer {
1775+
.textLayer,
1776+
.canvasWrapper {
17761777
display: none;
17771778
}
17781779
#viewerContainer {
@@ -1816,7 +1817,7 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
18161817
height: 100%;
18171818
}
18181819
/* wrapper around (scaled) print canvas elements */
1819-
#printContainer > div {
1820+
#printContainer > .printedPage {
18201821
page-break-after: always;
18211822
page-break-inside: avoid;
18221823

@@ -1829,8 +1830,15 @@ html[dir="rtl"] #documentPropertiesOverlay .row > * {
18291830
justify-content: center;
18301831
align-items: center;
18311832
}
1832-
#printContainer canvas,
1833-
#printContainer img {
1833+
1834+
#printContainer > .xfaPrintedPage {
1835+
page-break-after: always;
1836+
page-break-inside: avoid;
1837+
position: relative;
1838+
}
1839+
1840+
.printedPage canvas,
1841+
.printedPage img {
18341842
/* The intrinsic canvas / image size will make sure that we fit the page. */
18351843
max-width: 100%;
18361844
max-height: 100%;

0 commit comments

Comments
 (0)