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
This commit is contained in:
Tim van der Meij 2021-04-05 14:21:34 +02:00 committed by GitHub
commit 228adbf673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 100 deletions

View File

@ -921,10 +921,13 @@ class PDFDocumentProxy {
* NOTE: Do not, under any circumstances, call this method when rendering is * NOTE: Do not, under any circumstances, call this method when rendering is
* currently ongoing since that may lead to rendering errors. * currently ongoing since that may lead to rendering errors.
* *
* @param {boolean} [keepLoadedFonts] - Let fonts remain attached to the DOM.
* NOTE: This will increase persistent memory usage, hence don't use this
* option unless absolutely necessary. The default value is `false`.
* @returns {Promise} A promise that is resolved when clean-up has finished. * @returns {Promise} A promise that is resolved when clean-up has finished.
*/ */
cleanup() { cleanup(keepLoadedFonts = false) {
return this._transport.startCleanup(); return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
} }
/** /**
@ -1180,14 +1183,14 @@ class PDFPageProxy {
* {Array} of the annotation objects. * {Array} of the annotation objects.
*/ */
getAnnotations({ intent = null } = {}) { getAnnotations({ intent = null } = {}) {
if (!this.annotationsPromise || this.annotationsIntent !== intent) { if (!this._annotationsPromise || this._annotationsIntent !== intent) {
this.annotationsPromise = this._transport.getAnnotations( this._annotationsPromise = this._transport.getAnnotations(
this._pageIndex, this._pageIndex,
intent intent
); );
this.annotationsIntent = intent; this._annotationsIntent = intent;
} }
return this.annotationsPromise; return this._annotationsPromise;
} }
/** /**
@ -1480,7 +1483,7 @@ class PDFPageProxy {
} }
} }
this.objs.clear(); this.objs.clear();
this.annotationsPromise = null; this._annotationsPromise = null;
this._jsActionsPromise = null; this._jsActionsPromise = null;
this._xfaPromise = null; this._xfaPromise = null;
this.pendingCleanup = false; this.pendingCleanup = false;
@ -1515,7 +1518,7 @@ class PDFPageProxy {
this._intentStates.clear(); this._intentStates.clear();
this.objs.clear(); this.objs.clear();
this.annotationsPromise = null; this._annotationsPromise = null;
this._jsActionsPromise = null; this._jsActionsPromise = null;
this._xfaPromise = null; this._xfaPromise = null;
if (resetStats && this._stats) { if (resetStats && this._stats) {
@ -2789,24 +2792,28 @@ class WorkerTransport {
return this.messageHandler.sendWithPromise("GetStats", null); return this.messageHandler.sendWithPromise("GetStats", null);
} }
startCleanup() { async startCleanup(keepLoadedFonts = false) {
return this.messageHandler.sendWithPromise("Cleanup", null).then(() => { await this.messageHandler.sendWithPromise("Cleanup", null);
if (this.destroyed) {
return; // No need to manually clean-up when destruction has started.
}
for (let i = 0, ii = this.pageCache.length; i < ii; i++) { for (let i = 0, ii = this.pageCache.length; i < ii; i++) {
const page = this.pageCache[i]; const page = this.pageCache[i];
if (page) { if (!page) {
continue;
}
const cleanupSuccessful = page.cleanup(); const cleanupSuccessful = page.cleanup();
if (!cleanupSuccessful) { if (!cleanupSuccessful) {
throw new Error( throw new Error(`startCleanup: Page ${i + 1} is currently rendering.`);
`startCleanup: Page ${i + 1} is currently rendering.`
);
}
} }
} }
this.commonObjs.clear(); this.commonObjs.clear();
if (!keepLoadedFonts) {
this.fontLoader.clear(); this.fontLoader.clear();
}
this._hasJSActionsPromise = null; this._hasJSActionsPromise = null;
});
} }
get loadingParams() { get loadingParams() {

View File

@ -1339,12 +1339,10 @@ describe("api", function () {
.catch(done.fail); .catch(done.fail);
}); });
it("cleans up document resources", function (done) { it("cleans up document resources", async function () {
const promise = pdfDocument.cleanup(); await pdfDocument.cleanup();
promise.then(function () {
expect(true).toEqual(true); expect(true).toEqual(true);
done();
}, done.fail);
}); });
it("checks that fingerprints are unique", function (done) { it("checks that fingerprints are unique", function (done) {
@ -1982,15 +1980,13 @@ describe("api", function () {
]).then(done); ]).then(done);
}); });
it("cleans up document resources after rendering of page", function (done) { it("cleans up document resources after rendering of page", async function () {
const loadingTask = getDocument(buildGetDocumentParams(basicApiFileName)); const loadingTask = getDocument(buildGetDocumentParams(basicApiFileName));
let canvasAndCtx; const pdfDoc = await loadingTask.promise;
const pdfPage = await pdfDoc.getPage(1);
loadingTask.promise
.then(pdfDoc => {
return pdfDoc.getPage(1).then(pdfPage => {
const viewport = pdfPage.getViewport({ scale: 1 }); const viewport = pdfPage.getViewport({ scale: 1 });
canvasAndCtx = CanvasFactory.create( const canvasAndCtx = CanvasFactory.create(
viewport.width, viewport.width,
viewport.height viewport.height
); );
@ -2000,30 +1996,25 @@ describe("api", function () {
canvasFactory: CanvasFactory, canvasFactory: CanvasFactory,
viewport, viewport,
}); });
return renderTask.promise.then(() => { await renderTask.promise;
return pdfDoc.cleanup();
}); await pdfDoc.cleanup();
});
})
.then(() => {
expect(true).toEqual(true); expect(true).toEqual(true);
CanvasFactory.destroy(canvasAndCtx); CanvasFactory.destroy(canvasAndCtx);
loadingTask.destroy().then(done); await loadingTask.destroy();
}, done.fail);
}); });
it("cleans up document resources during rendering of page", function (done) { it("cleans up document resources during rendering of page", async function () {
const loadingTask = getDocument( const loadingTask = getDocument(
buildGetDocumentParams("tracemonkey.pdf") buildGetDocumentParams("tracemonkey.pdf")
); );
let canvasAndCtx; const pdfDoc = await loadingTask.promise;
const pdfPage = await pdfDoc.getPage(1);
loadingTask.promise
.then(pdfDoc => {
return pdfDoc.getPage(1).then(pdfPage => {
const viewport = pdfPage.getViewport({ scale: 1 }); const viewport = pdfPage.getViewport({ scale: 1 });
canvasAndCtx = CanvasFactory.create( const canvasAndCtx = CanvasFactory.create(
viewport.width, viewport.width,
viewport.height viewport.height
); );
@ -2033,34 +2024,25 @@ describe("api", function () {
canvasFactory: CanvasFactory, canvasFactory: CanvasFactory,
viewport, viewport,
}); });
// Ensure that clean-up runs during rendering.
renderTask.onContinue = function (cont) { renderTask.onContinue = function (cont) {
waitSome(cont); waitSome(cont);
}; };
return pdfDoc try {
.cleanup() await pdfDoc.cleanup();
.then(
() => {
throw new Error("shall fail cleanup"); throw new Error("shall fail cleanup");
}, } catch (reason) {
reason => {
expect(reason instanceof Error).toEqual(true); expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual( expect(reason.message).toEqual(
"startCleanup: Page 1 is currently rendering." "startCleanup: Page 1 is currently rendering."
); );
} }
) await renderTask.promise;
.then(() => {
return renderTask.promise;
})
.then(() => {
CanvasFactory.destroy(canvasAndCtx); CanvasFactory.destroy(canvasAndCtx);
loadingTask.destroy().then(done); await loadingTask.destroy();
});
});
})
.catch(done.fail);
}); });
it("caches image resources at the document/page level as expected (issue 11878)", async function () { it("caches image resources at the document/page level as expected (issue 11878)", async function () {

View File

@ -465,7 +465,7 @@ const PDFViewerApplication = {
this.overlayManager = new OverlayManager(); this.overlayManager = new OverlayManager();
const pdfRenderingQueue = new PDFRenderingQueue(); const pdfRenderingQueue = new PDFRenderingQueue();
pdfRenderingQueue.onIdle = this.cleanup.bind(this); pdfRenderingQueue.onIdle = this._cleanup.bind(this);
this.pdfRenderingQueue = pdfRenderingQueue; this.pdfRenderingQueue = pdfRenderingQueue;
const pdfLinkService = new PDFLinkService({ const pdfLinkService = new PDFLinkService({
@ -1782,7 +1782,10 @@ const PDFViewerApplication = {
} }
}, },
cleanup() { /**
* @private
*/
_cleanup() {
if (!this.pdfDocument) { if (!this.pdfDocument) {
return; // run cleanup when document is loaded return; // run cleanup when document is loaded
} }
@ -1790,9 +1793,9 @@ const PDFViewerApplication = {
this.pdfThumbnailViewer.cleanup(); this.pdfThumbnailViewer.cleanup();
// We don't want to remove fonts used by active page SVGs. // We don't want to remove fonts used by active page SVGs.
if (this.pdfViewer.renderer !== RendererType.SVG) { this.pdfDocument.cleanup(
this.pdfDocument.cleanup(); /* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG
} );
}, },
forceRendering() { forceRendering() {