From 641e2f506e8c1d384e985374caa963d5ef5f9b2d Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 21 Feb 2025 23:20:44 +0100 Subject: [PATCH 1/2] [api-minor] Re-factor how the `useWorkerFetch` option is used internally With the recently added OpenJPEG no-wasm fallback we need to send the `wasmUrl` option to the worker-thread *regardless* of the value of the `useWorkerFetch` option, since the fallback won't work if we don't have a URL to `import` it from. For consistency the code is re-factored to always send the factory-urls to the worker-thread, and simply check the `useWorkerFetch` option there instead. Also, as a follow-up to PR 19525, introduce a new `useWasm` option that can be used in e.g. browser-tests to forcibly disable WebAssembly usage. --- src/core/evaluator.js | 6 ++++-- src/core/jpx.js | 24 ++++++++++++------------ src/display/api.js | 12 +++++++++--- test/driver.js | 3 ++- test/test_manifest.json | 4 ++-- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 484e167c0..2f9e6897e 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -90,6 +90,8 @@ const DefaultPartialEvaluatorOptions = Object.freeze({ canvasMaxAreaInBytes: -1, fontExtraProperties: false, useSystemFonts: true, + useWasm: true, + useWorkerFetch: true, cMapUrl: null, standardFontDataUrl: null, wasmUrl: null, @@ -384,7 +386,7 @@ class PartialEvaluator { } let data; - if (this.options.cMapUrl !== null) { + if (this.options.useWorkerFetch) { // Only compressed CMaps are (currently) supported here. data = { cMapData: await fetchBinaryData(`${this.options.cMapUrl}${name}.bcmap`), @@ -424,7 +426,7 @@ class PartialEvaluator { let data; try { - if (this.options.standardFontDataUrl !== null) { + if (this.options.useWorkerFetch) { data = await fetchBinaryData( `${this.options.standardFontDataUrl}${filename}` ); diff --git a/src/core/jpx.js b/src/core/jpx.js index 2414ed4f8..f25fc7c7d 100644 --- a/src/core/jpx.js +++ b/src/core/jpx.js @@ -31,24 +31,26 @@ class JpxImage { static #modulePromise = null; - static #hasJSFallback = false; + static #useWasm = true; + + static #useWorkerFetch = true; static #wasmUrl = null; - static setOptions({ handler, wasmUrl }) { - if (this.#buffer || this.#hasJSFallback || this.#modulePromise) { + static setOptions({ handler, useWasm, useWorkerFetch, wasmUrl }) { + if (this.#buffer || this.#modulePromise) { return; } - this.#wasmUrl = wasmUrl || null; - if (wasmUrl === null) { + this.#useWasm = useWasm; + this.#useWorkerFetch = useWorkerFetch; + this.#wasmUrl = wasmUrl; + + if (!useWorkerFetch) { this.#handler = handler; } } static async #getJsModule(fallbackCallback) { - if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { - this.#wasmUrl ??= "/build/generic/web/wasm/"; - } const path = typeof PDFJSDev === "undefined" ? `../${this.#wasmUrl}openjpeg_nowasm_fallback.js` @@ -63,8 +65,6 @@ class JpxImage { } catch (e) { warn(`JpxImage#getJsModule: ${e}`); } - - this.#hasJSFallback = true; fallbackCallback(instance); } @@ -72,7 +72,7 @@ class JpxImage { const filename = "openjpeg.wasm"; try { if (!this.#buffer) { - if (this.#wasmUrl !== null) { + if (this.#useWorkerFetch) { this.#buffer = await fetchBinaryData(`${this.#wasmUrl}${filename}`); } else { this.#buffer = await this.#handler.sendWithPromise( @@ -100,7 +100,7 @@ class JpxImage { if (!this.#modulePromise) { const { promise, resolve } = Promise.withResolvers(); const promises = [promise]; - if (this.#hasJSFallback) { + if (!this.#useWasm) { this.#getJsModule(resolve); } else { promises.push( diff --git a/src/display/api.js b/src/display/api.js index 63b01b5e6..99ee4ba06 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -144,6 +144,9 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms * the `CMapReaderFactory`, `StandardFontDataFactory`, and `WasmFactory` * options are ignored. * The default value is `true` in web environments and `false` in Node.js. + * @property {boolean} [useWasm] - Attempt to use WebAssembly in order to + * improve e.g. image decoding performance. + * The default value is `true`. * @property {boolean} [stopAtErrors] - Reject certain promises, e.g. * `getOperatorList`, `getTextContent`, and `RenderTask`, when the associated * PDF data cannot be successfully parsed, instead of attempting to recover @@ -320,6 +323,7 @@ function getDocument(src = {}) { ? NodeFilterFactory : DOMFilterFactory); const enableHWA = src.enableHWA === true; + const useWasm = src.useWasm !== false; // Parameters whose default values depend on other parameters. const length = rangeTransport ? rangeTransport.length : (src.length ?? NaN); @@ -410,9 +414,11 @@ function getDocument(src = {}) { canvasMaxAreaInBytes, fontExtraProperties, useSystemFonts, - cMapUrl: useWorkerFetch ? cMapUrl : null, - standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null, - wasmUrl: useWorkerFetch ? wasmUrl : null, + useWasm, + useWorkerFetch, + cMapUrl, + standardFontDataUrl, + wasmUrl, }, }; const transportParams = { diff --git a/test/driver.js b/test/driver.js index 6e6ee5d97..c02c455cb 100644 --- a/test/driver.js +++ b/test/driver.js @@ -638,10 +638,11 @@ class Driver { password: task.password, cMapUrl: CMAP_URL, standardFontDataUrl: STANDARD_FONT_DATA_URL, - wasmUrl: task.noWasm ? null : WASM_URL, + wasmUrl: WASM_URL, disableAutoFetch: !task.enableAutoFetch, pdfBug: true, useSystemFonts: task.useSystemFonts, + useWasm: task.useWasm, useWorkerFetch: task.useWorkerFetch, enableXfa: task.enableXfa, isOffscreenCanvasSupported, diff --git a/test/test_manifest.json b/test/test_manifest.json index 1101d1ad3..8c9058adf 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -6422,9 +6422,9 @@ "id": "issue19326_nowasm", "file": "pdfs/issue19326.pdf", "md5": "b4d937017daf439a6318501428e0c6ba", - "noWasm": true, "rounds": 1, - "type": "eq" + "type": "eq", + "useWasm": false }, { "id": "issue19326_main_thread_fetch", From 637e95985a70ce7107b03b7cb2ca610e9f06bddb Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 22 Feb 2025 13:38:10 +0100 Subject: [PATCH 2/2] Simplify `JpxImage.setOptions` a little bit After PR 19392 we're only invoking this method *once* per document, hence the early-return branch shouldn't be necessary any more. --- src/core/jpx.js | 3 --- src/core/pdf_manager.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/core/jpx.js b/src/core/jpx.js index f25fc7c7d..8fd7c8b7f 100644 --- a/src/core/jpx.js +++ b/src/core/jpx.js @@ -38,9 +38,6 @@ class JpxImage { static #wasmUrl = null; static setOptions({ handler, useWasm, useWorkerFetch, wasmUrl }) { - if (this.#buffer || this.#modulePromise) { - return; - } this.#useWasm = useWasm; this.#useWorkerFetch = useWorkerFetch; this.#wasmUrl = wasmUrl; diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index 3b277ea85..02c4e58b4 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -70,7 +70,7 @@ class BasePdfManager { FeatureTest.isImageDecoderSupported; this.evaluatorOptions = Object.freeze(evaluatorOptions); - // Initially image-options once per document. + // Initialize image-options once per document. ImageResizer.setOptions(evaluatorOptions); JpegStream.setOptions(evaluatorOptions); JpxImage.setOptions({ ...evaluatorOptions, handler });