From 1ea4c4b519157ee54da4385a7db17339f9e0db64 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 6 Oct 2022 22:05:32 +0200 Subject: [PATCH] [api-minor] Make `isOffscreenCanvasSupported` configurable via the API (issue 14952) This patch first of all makes `isOffscreenCanvasSupported` configurable, defaulting to `true` in browsers and `false` in Node.js environments, with a new `getDocument` parameter. While you normally want to use this, in order to improve performance, it should still be possible for users to control it (similar to e.g. `isEvalSupported`). The specific problem, as reported in issue 14952, is that the SVG back-end doesn't support the new ImageMask data-format that's introduced in PR 14754. In particular: - When the SVG back-end is used in Node.js environments, this patch will "just work" without the user needing to make any code changes. - If the SVG back-end is used in browsers, this patch will require that `isOffscreenCanvasSupported: false` is added to the `getDocument`-call. --- src/core/evaluator.js | 2 ++ src/core/image.js | 3 ++- src/core/worker.js | 1 + src/display/api.js | 8 ++++++++ src/display/svg.js | 23 +++++++++++++++++++---- web/app_options.js | 5 +++++ 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 3178b0f11..53c8282a3 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -84,6 +84,7 @@ const DefaultPartialEvaluatorOptions = Object.freeze({ disableFontFace: false, ignoreErrors: false, isEvalSupported: true, + isOffscreenCanvasSupported: true, fontExtraProperties: false, useSystemFonts: true, cMapUrl: null, @@ -652,6 +653,7 @@ class PartialEvaluator { imageIsFromDecodeStream: image instanceof DecodeStream, inverseDecode: !!decode && decode[0] > 0, interpolate, + isOffscreenCanvasSupported: this.options.isOffscreenCanvasSupported, }); if (imgData.isSingleOpaquePixel) { diff --git a/src/core/image.js b/src/core/image.js index f16f189de..14f9699a2 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -356,6 +356,7 @@ class PDFImage { imageIsFromDecodeStream, inverseDecode, interpolate, + isOffscreenCanvasSupported = true, }) { const isSingleOpaquePixel = width === 1 && @@ -366,7 +367,7 @@ class PDFImage { return { isSingleOpaquePixel }; } - if (FeatureTest.isOffscreenCanvasSupported) { + if (isOffscreenCanvasSupported && FeatureTest.isOffscreenCanvasSupported) { const canvas = new OffscreenCanvas(width, height); const ctx = canvas.getContext("2d"); const imgData = ctx.createImageData(width, height); diff --git a/src/core/worker.js b/src/core/worker.js index ae5221f06..0749e1541 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -410,6 +410,7 @@ class WorkerMessageHandler { disableFontFace: data.disableFontFace, ignoreErrors: data.ignoreErrors, isEvalSupported: data.isEvalSupported, + isOffscreenCanvasSupported: data.isOffscreenCanvasSupported, fontExtraProperties: data.fontExtraProperties, useSystemFonts: data.useSystemFonts, cMapUrl: data.cMapUrl, diff --git a/src/display/api.js b/src/display/api.js index a0b2156c9..b7914fe10 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -189,6 +189,10 @@ function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) { * @property {boolean} [isEvalSupported] - Determines if we can evaluate strings * as JavaScript. Primarily used to improve performance of font rendering, and * when parsing PDF functions. The default value is `true`. + * @property {boolean} [isOffscreenCanvasSupported] - Determines if we can use + * `OffscreenCanvas` in the worker. Primarily used to improve performance of + * image conversion/rendering. + * The default value is `true` in web environments and `false` in Node.js. * @property {boolean} [disableFontFace] - By default fonts are converted to * OpenType fonts and loaded via the Font Loading API or `@font-face` rules. * If disabled, fonts will be rendered using a built-in font renderer that @@ -365,6 +369,9 @@ function getDocument(src) { if (typeof params.isEvalSupported !== "boolean") { params.isEvalSupported = true; } + if (typeof params.isOffscreenCanvasSupported !== "boolean") { + params.isOffscreenCanvasSupported = !isNodeJS; + } if (typeof params.disableFontFace !== "boolean") { params.disableFontFace = isNodeJS; } @@ -516,6 +523,7 @@ async function _fetchDocument(worker, source, pdfDataRangeTransport, docId) { docBaseUrl: source.docBaseUrl, ignoreErrors: source.ignoreErrors, isEvalSupported: source.isEvalSupported, + isOffscreenCanvasSupported: source.isOffscreenCanvasSupported, fontExtraProperties: source.fontExtraProperties, enableXfa: source.enableXfa, useSystemFonts: source.useSystemFonts, diff --git a/src/display/svg.js b/src/display/svg.js index 8cd2e04a6..0373c7263 100644 --- a/src/display/svg.js +++ b/src/display/svg.js @@ -495,6 +495,15 @@ if ( } } + getObject(data, fallback = null) { + if (typeof data === "string") { + return data.startsWith("g_") + ? this.commonObjs.get(data) + : this.objs.get(data); + } + return fallback; + } + save() { this.transformStack.push(this.transformMatrix); const old = this.current; @@ -1586,9 +1595,7 @@ if ( } paintImageXObject(objId) { - const imgData = objId.startsWith("g_") - ? this.commonObjs.get(objId) - : this.objs.get(objId); + const imgData = this.getObject(objId); if (!imgData) { warn(`Dependent image with object ID ${objId} is not ready yet`); return; @@ -1627,7 +1634,15 @@ if ( } } - paintImageMaskXObject(imgData) { + paintImageMaskXObject(img) { + const imgData = this.getObject(img.data, img); + if (imgData.bitmap) { + warn( + "paintImageMaskXObject: ImageBitmap support is not implemented, " + + "ensure that the `isOffscreenCanvasSupported` API parameter is disabled." + ); + return; + } const current = this.current; const width = imgData.width; const height = imgData.height; diff --git a/web/app_options.js b/web/app_options.js index 0f09c490b..de96674a2 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -242,6 +242,11 @@ const defaultOptions = { value: true, kind: OptionKind.API, }, + isOffscreenCanvasSupported: { + /** @type {boolean} */ + value: true, + kind: OptionKind.API, + }, maxImageSize: { /** @type {number} */ value: -1,