Skip to content

Commit 228adbf

Browse files
Merge pull request #13172 from Snuffleupagus/cleanup-keepFonts
[api-minor] Add an option, in `PDFDocumentProxy.cleanup`, to allow fonts to remain attached to the DOM
2 parents 11f2eab + 232fbd2 commit 228adbf

File tree

3 files changed

+92
-100
lines changed

3 files changed

+92
-100
lines changed

src/display/api.js

+30-23
Original file line numberDiff line numberDiff line change
@@ -921,10 +921,13 @@ class PDFDocumentProxy {
921921
* NOTE: Do not, under any circumstances, call this method when rendering is
922922
* currently ongoing since that may lead to rendering errors.
923923
*
924+
* @param {boolean} [keepLoadedFonts] - Let fonts remain attached to the DOM.
925+
* NOTE: This will increase persistent memory usage, hence don't use this
926+
* option unless absolutely necessary. The default value is `false`.
924927
* @returns {Promise} A promise that is resolved when clean-up has finished.
925928
*/
926-
cleanup() {
927-
return this._transport.startCleanup();
929+
cleanup(keepLoadedFonts = false) {
930+
return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
928931
}
929932

930933
/**
@@ -1180,14 +1183,14 @@ class PDFPageProxy {
11801183
* {Array} of the annotation objects.
11811184
*/
11821185
getAnnotations({ intent = null } = {}) {
1183-
if (!this.annotationsPromise || this.annotationsIntent !== intent) {
1184-
this.annotationsPromise = this._transport.getAnnotations(
1186+
if (!this._annotationsPromise || this._annotationsIntent !== intent) {
1187+
this._annotationsPromise = this._transport.getAnnotations(
11851188
this._pageIndex,
11861189
intent
11871190
);
1188-
this.annotationsIntent = intent;
1191+
this._annotationsIntent = intent;
11891192
}
1190-
return this.annotationsPromise;
1193+
return this._annotationsPromise;
11911194
}
11921195

11931196
/**
@@ -1480,7 +1483,7 @@ class PDFPageProxy {
14801483
}
14811484
}
14821485
this.objs.clear();
1483-
this.annotationsPromise = null;
1486+
this._annotationsPromise = null;
14841487
this._jsActionsPromise = null;
14851488
this._xfaPromise = null;
14861489
this.pendingCleanup = false;
@@ -1515,7 +1518,7 @@ class PDFPageProxy {
15151518

15161519
this._intentStates.clear();
15171520
this.objs.clear();
1518-
this.annotationsPromise = null;
1521+
this._annotationsPromise = null;
15191522
this._jsActionsPromise = null;
15201523
this._xfaPromise = null;
15211524
if (resetStats && this._stats) {
@@ -2789,24 +2792,28 @@ class WorkerTransport {
27892792
return this.messageHandler.sendWithPromise("GetStats", null);
27902793
}
27912794

2792-
startCleanup() {
2793-
return this.messageHandler.sendWithPromise("Cleanup", null).then(() => {
2794-
for (let i = 0, ii = this.pageCache.length; i < ii; i++) {
2795-
const page = this.pageCache[i];
2796-
if (page) {
2797-
const cleanupSuccessful = page.cleanup();
2795+
async startCleanup(keepLoadedFonts = false) {
2796+
await this.messageHandler.sendWithPromise("Cleanup", null);
27982797

2799-
if (!cleanupSuccessful) {
2800-
throw new Error(
2801-
`startCleanup: Page ${i + 1} is currently rendering.`
2802-
);
2803-
}
2804-
}
2798+
if (this.destroyed) {
2799+
return; // No need to manually clean-up when destruction has started.
2800+
}
2801+
for (let i = 0, ii = this.pageCache.length; i < ii; i++) {
2802+
const page = this.pageCache[i];
2803+
if (!page) {
2804+
continue;
28052805
}
2806-
this.commonObjs.clear();
2806+
const cleanupSuccessful = page.cleanup();
2807+
2808+
if (!cleanupSuccessful) {
2809+
throw new Error(`startCleanup: Page ${i + 1} is currently rendering.`);
2810+
}
2811+
}
2812+
this.commonObjs.clear();
2813+
if (!keepLoadedFonts) {
28072814
this.fontLoader.clear();
2808-
this._hasJSActionsPromise = null;
2809-
});
2815+
}
2816+
this._hasJSActionsPromise = null;
28102817
}
28112818

28122819
get loadingParams() {

test/unit/api_spec.js

+54-72
Original file line numberDiff line numberDiff line change
@@ -1339,12 +1339,10 @@ describe("api", function () {
13391339
.catch(done.fail);
13401340
});
13411341

1342-
it("cleans up document resources", function (done) {
1343-
const promise = pdfDocument.cleanup();
1344-
promise.then(function () {
1345-
expect(true).toEqual(true);
1346-
done();
1347-
}, done.fail);
1342+
it("cleans up document resources", async function () {
1343+
await pdfDocument.cleanup();
1344+
1345+
expect(true).toEqual(true);
13481346
});
13491347

13501348
it("checks that fingerprints are unique", function (done) {
@@ -1982,85 +1980,69 @@ describe("api", function () {
19821980
]).then(done);
19831981
});
19841982

1985-
it("cleans up document resources after rendering of page", function (done) {
1983+
it("cleans up document resources after rendering of page", async function () {
19861984
const loadingTask = getDocument(buildGetDocumentParams(basicApiFileName));
1987-
let canvasAndCtx;
1985+
const pdfDoc = await loadingTask.promise;
1986+
const pdfPage = await pdfDoc.getPage(1);
19881987

1989-
loadingTask.promise
1990-
.then(pdfDoc => {
1991-
return pdfDoc.getPage(1).then(pdfPage => {
1992-
const viewport = pdfPage.getViewport({ scale: 1 });
1993-
canvasAndCtx = CanvasFactory.create(
1994-
viewport.width,
1995-
viewport.height
1996-
);
1988+
const viewport = pdfPage.getViewport({ scale: 1 });
1989+
const canvasAndCtx = CanvasFactory.create(
1990+
viewport.width,
1991+
viewport.height
1992+
);
19971993

1998-
const renderTask = pdfPage.render({
1999-
canvasContext: canvasAndCtx.context,
2000-
canvasFactory: CanvasFactory,
2001-
viewport,
2002-
});
2003-
return renderTask.promise.then(() => {
2004-
return pdfDoc.cleanup();
2005-
});
2006-
});
2007-
})
2008-
.then(() => {
2009-
expect(true).toEqual(true);
1994+
const renderTask = pdfPage.render({
1995+
canvasContext: canvasAndCtx.context,
1996+
canvasFactory: CanvasFactory,
1997+
viewport,
1998+
});
1999+
await renderTask.promise;
20102000

2011-
CanvasFactory.destroy(canvasAndCtx);
2012-
loadingTask.destroy().then(done);
2013-
}, done.fail);
2001+
await pdfDoc.cleanup();
2002+
2003+
expect(true).toEqual(true);
2004+
2005+
CanvasFactory.destroy(canvasAndCtx);
2006+
await loadingTask.destroy();
20142007
});
20152008

2016-
it("cleans up document resources during rendering of page", function (done) {
2009+
it("cleans up document resources during rendering of page", async function () {
20172010
const loadingTask = getDocument(
20182011
buildGetDocumentParams("tracemonkey.pdf")
20192012
);
2020-
let canvasAndCtx;
2013+
const pdfDoc = await loadingTask.promise;
2014+
const pdfPage = await pdfDoc.getPage(1);
20212015

2022-
loadingTask.promise
2023-
.then(pdfDoc => {
2024-
return pdfDoc.getPage(1).then(pdfPage => {
2025-
const viewport = pdfPage.getViewport({ scale: 1 });
2026-
canvasAndCtx = CanvasFactory.create(
2027-
viewport.width,
2028-
viewport.height
2029-
);
2016+
const viewport = pdfPage.getViewport({ scale: 1 });
2017+
const canvasAndCtx = CanvasFactory.create(
2018+
viewport.width,
2019+
viewport.height
2020+
);
20302021

2031-
const renderTask = pdfPage.render({
2032-
canvasContext: canvasAndCtx.context,
2033-
canvasFactory: CanvasFactory,
2034-
viewport,
2035-
});
2022+
const renderTask = pdfPage.render({
2023+
canvasContext: canvasAndCtx.context,
2024+
canvasFactory: CanvasFactory,
2025+
viewport,
2026+
});
2027+
// Ensure that clean-up runs during rendering.
2028+
renderTask.onContinue = function (cont) {
2029+
waitSome(cont);
2030+
};
20362031

2037-
renderTask.onContinue = function (cont) {
2038-
waitSome(cont);
2039-
};
2032+
try {
2033+
await pdfDoc.cleanup();
20402034

2041-
return pdfDoc
2042-
.cleanup()
2043-
.then(
2044-
() => {
2045-
throw new Error("shall fail cleanup");
2046-
},
2047-
reason => {
2048-
expect(reason instanceof Error).toEqual(true);
2049-
expect(reason.message).toEqual(
2050-
"startCleanup: Page 1 is currently rendering."
2051-
);
2052-
}
2053-
)
2054-
.then(() => {
2055-
return renderTask.promise;
2056-
})
2057-
.then(() => {
2058-
CanvasFactory.destroy(canvasAndCtx);
2059-
loadingTask.destroy().then(done);
2060-
});
2061-
});
2062-
})
2063-
.catch(done.fail);
2035+
throw new Error("shall fail cleanup");
2036+
} catch (reason) {
2037+
expect(reason instanceof Error).toEqual(true);
2038+
expect(reason.message).toEqual(
2039+
"startCleanup: Page 1 is currently rendering."
2040+
);
2041+
}
2042+
await renderTask.promise;
2043+
2044+
CanvasFactory.destroy(canvasAndCtx);
2045+
await loadingTask.destroy();
20642046
});
20652047

20662048
it("caches image resources at the document/page level as expected (issue 11878)", async function () {

web/app.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ const PDFViewerApplication = {
465465
this.overlayManager = new OverlayManager();
466466

467467
const pdfRenderingQueue = new PDFRenderingQueue();
468-
pdfRenderingQueue.onIdle = this.cleanup.bind(this);
468+
pdfRenderingQueue.onIdle = this._cleanup.bind(this);
469469
this.pdfRenderingQueue = pdfRenderingQueue;
470470

471471
const pdfLinkService = new PDFLinkService({
@@ -1782,17 +1782,20 @@ const PDFViewerApplication = {
17821782
}
17831783
},
17841784

1785-
cleanup() {
1785+
/**
1786+
* @private
1787+
*/
1788+
_cleanup() {
17861789
if (!this.pdfDocument) {
17871790
return; // run cleanup when document is loaded
17881791
}
17891792
this.pdfViewer.cleanup();
17901793
this.pdfThumbnailViewer.cleanup();
17911794

17921795
// We don't want to remove fonts used by active page SVGs.
1793-
if (this.pdfViewer.renderer !== RendererType.SVG) {
1794-
this.pdfDocument.cleanup();
1795-
}
1796+
this.pdfDocument.cleanup(
1797+
/* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG
1798+
);
17961799
},
17971800

17981801
forceRendering() {

0 commit comments

Comments
 (0)