From c355f91d2e451da65224a66d163ad65ebe88a973 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 23 Apr 2020 13:04:57 +0200 Subject: [PATCH] [api-minor] Immediately release the `font.data` property once the font been attached to the DOM (PR 11777 follow-up) *This patch implements https://github.com/mozilla/pdf.js/pull/11777#issuecomment-609741348* This extends the work from PR 11773 and 11777 further, by immediately releasing the `font.data` property once the font been attached to the DOM. By not unnecessarily holding onto this data on the main-thread, we'll thus reduce the memory usage of fonts even further (especially beneficial in longer documents with composite fonts). The new behaviour is controlled by the recently added `fontExtraProperties` API option (adding a new option just for this patch didn't seem necessary), since there's one edge-case in the SVG renderer where the `font.data` property is necessary (see the `pdf2svg` example). Note that while the default viewer does run clean-up with an idle timeout, that timeout will be reset whenever rendering occurs *or* when scrolling happens in the viewer. In practice this means that unless the user doesn't interact with the viewer in *any* way during an extended period of time, currently set to 30 seconds, the `PDFDocumentProxy.cleanup` method will never be called and font resources will thus not be cleaned-up. --- examples/node/pdf2svg.js | 1 + src/display/api.js | 28 +++++++++++++++------------- src/display/canvas.js | 2 +- src/display/svg.js | 8 +++++++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/examples/node/pdf2svg.js b/examples/node/pdf2svg.js index 472ebba69..bce275ce6 100644 --- a/examples/node/pdf2svg.js +++ b/examples/node/pdf2svg.js @@ -86,6 +86,7 @@ function writeSvgToFile(svgElement, filePath) { // callback. var loadingTask = pdfjsLib.getDocument({ data: data, + fontExtraProperties: true, // Try to export JPEG images directly if they don't need any further // processing. nativeImageDecoderSupport: pdfjsLib.NativeImageDecoding.DISPLAY, diff --git a/src/display/api.js b/src/display/api.js index d3209cd75..11511f130 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -2244,20 +2244,22 @@ class WorkerTransport { fontRegistry, }); - this.fontLoader.bind(font).then( - () => { + this.fontLoader + .bind(font) + .catch(reason => { + return messageHandler.sendWithPromise("FontFallback", { id }); + }) + .finally(() => { + if (!params.fontExtraProperties && font.data) { + // Immediately release the `font.data` property once the font + // has been attached to the DOM, since it's no longer needed, + // rather than waiting for a `PDFDocumentProxy.cleanup` call. + // Since `font.data` could be very large, e.g. in some cases + // multiple megabytes, this will help reduce memory usage. + font.data = null; + } this.commonObjs.resolve(id, font); - }, - reason => { - messageHandler - .sendWithPromise("FontFallback", { - id, - }) - .finally(() => { - this.commonObjs.resolve(id, font); - }); - } - ); + }); break; case "FontPath": case "FontType3Res": diff --git a/src/display/canvas.js b/src/display/canvas.js index 5ce5be839..cf1cfca42 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -1498,7 +1498,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var isAddToPathSet = !!( textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG ); - const patternFill = current.patternFill && font.data; + const patternFill = current.patternFill && !font.missingFile; var addToPath; if (font.disableFontFace || isAddToPathSet || patternFill) { diff --git a/src/display/svg.js b/src/display/svg.js index 5fd9295ee..8e27f38d5 100644 --- a/src/display/svg.js +++ b/src/display/svg.js @@ -948,6 +948,12 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { } addFontStyle(fontObj) { + if (!fontObj.data) { + throw new Error( + "addFontStyle: No font data available, " + + 'ensure that the "fontExtraProperties" API parameter is set.' + ); + } if (!this.cssStyle) { this.cssStyle = this.svgFactory.createElement("svg:style"); this.cssStyle.setAttributeNS(null, "type", "text/css"); @@ -972,7 +978,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { if ( this.embedFonts && - fontObj.data && + !fontObj.missingFile && !this.embeddedFonts[fontObj.loadedName] ) { this.addFontStyle(fontObj);