From 12d033cabebab63a6da97517ae2c743b74e82c25 Mon Sep 17 00:00:00 2001 From: Ujjwal Sharma Date: Wed, 16 Jul 2025 12:58:54 +0200 Subject: [PATCH] move common handler code to a shared file --- src/core/document.js | 15 ++--- src/core/evaluator.js | 31 +++------- src/core/worker.js | 4 ++ src/display/api.js | 110 +++------------------------------ src/display/renderer_worker.js | 109 +++----------------------------- src/shared/handle_objs.js | 108 ++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 233 deletions(-) create mode 100644 src/shared/handle_objs.js diff --git a/src/core/document.js b/src/core/document.js index 239ae333f..f05101d5d 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -129,11 +129,10 @@ class Page { }; } - #createPartialEvaluator(handler, rendererHandler) { + #createPartialEvaluator(handler) { return new PartialEvaluator({ xref: this.xref, handler, - rendererHandler, pageIndex: this.pageIndex, idFactory: this._localIdFactory, fontCache: this.fontCache, @@ -450,10 +449,7 @@ class Page { const contentStreamPromise = this.getContentStream(); const resourcesPromise = this.loadResources(RESOURCES_KEYS_OPERATOR_LIST); - const partialEvaluator = this.#createPartialEvaluator( - handler, - rendererHandler - ); + const partialEvaluator = this.#createPartialEvaluator(handler); const newAnnotsByPage = !this.xfaFactory ? getNewAnnotationsMap(annotationStorage) @@ -1336,7 +1332,7 @@ class PDFDocument { this.xfaFactory.setImages(xfaImages); } - async #loadXfaFonts(handler, task, rendererHandler) { + async #loadXfaFonts(handler, task) { const acroForm = await this.pdfManager.ensureCatalog("acroForm"); if (!acroForm) { return; @@ -1362,7 +1358,6 @@ class PDFDocument { const partialEvaluator = new PartialEvaluator({ xref: this.xref, handler, - rendererHandler, pageIndex: -1, idFactory: this._globalIdFactory, fontCache, @@ -1475,9 +1470,9 @@ class PDFDocument { this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts); } - loadXfaResources(handler, task, rendererHandler) { + loadXfaResources(handler, task) { return Promise.all([ - this.#loadXfaFonts(handler, task, rendererHandler).catch(() => { + this.#loadXfaFonts(handler, task).catch(() => { // Ignore errors, to allow the document to load. }), this.#loadXfaImages(), diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 6f369c1a5..fcef81106 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -222,7 +222,6 @@ class PartialEvaluator { constructor({ xref, handler, - rendererHandler, pageIndex, idFactory, fontCache, @@ -235,7 +234,6 @@ class PartialEvaluator { }) { this.xref = xref; this.handler = handler; - this.rendererHandler = rendererHandler; this.pageIndex = pageIndex; this.idFactory = idFactory; this.fontCache = fontCache; @@ -555,19 +553,13 @@ class PartialEvaluator { const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null; if (this.parsingType3Font || cacheGlobally) { - this.handler.send("commonobj", [objId, "Image", imgData], transfers); - return this.rendererHandler.send( + return this.handler.send( "commonobj", [objId, "Image", imgData], transfers ); } - this.handler.send( - "obj", - [objId, this.pageIndex, "Image", imgData], - transfers - ); - return this.rendererHandler.send( + return this.handler.send( "obj", [objId, this.pageIndex, "Image", imgData], transfers @@ -795,10 +787,11 @@ class PartialEvaluator { // globally, check if the image is still cached locally on the main-thread // to avoid having to re-parse the image (since that can be slow). if (w * h > 250000 || hasMask) { - const localLength = await this.rendererHandler.sendWithPromise( - "commonobj", - [objId, "CopyLocalImage", { imageRef }] - ); + const localLength = await this.sendWithPromise("commonobj", [ + objId, + "CopyLocalImage", + { imageRef }, + ]); if (localLength) { this.globalImageCache.setData(imageRef, globalCacheData); @@ -1028,7 +1021,6 @@ class PartialEvaluator { state.font = translated.font; translated.send(this.handler); - translated.send(this.rendererHandler); return translated.loadedName; } @@ -1048,7 +1040,7 @@ class PartialEvaluator { PartialEvaluator.buildFontPaths( font, glyphs, - this.rendererHandler, + this.handler, this.options ); } @@ -1526,15 +1518,8 @@ class PartialEvaluator { if (this.parsingType3Font) { this.handler.send("commonobj", [id, "Pattern", patternIR]); - this.rendererHandler.send("commonobj", [id, "Pattern", patternIR]); } else { this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]); - this.rendererHandler.send("obj", [ - id, - this.pageIndex, - "Pattern", - patternIR, - ]); } return id; } diff --git a/src/core/worker.js b/src/core/worker.js index 34e920b24..6e663f394 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -873,6 +873,10 @@ class WorkerMessageHandler { .then(page => pdfManager.ensure(page, "getStructTree")); }); + handler.on("FontFallback", function (data) { + return pdfManager.fontFallback(data.id, handler); + }); + rendererHandler.on("FontFallback", function (data) { return pdfManager.fontFallback(data.id, rendererHandler); }); diff --git a/src/display/api.js b/src/display/api.js index 74a2f27e4..2eae3e4e5 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -45,7 +45,6 @@ import { RenderingCancelledException, StatTimer, } from "./display_utils.js"; -import { FontFaceObject, FontLoader } from "./font_loader.js"; import { getDataProp, getFactoryUrlProp, @@ -68,7 +67,7 @@ import { DOMCMapReaderFactory } from "display-cmap_reader_factory"; import { DOMFilterFactory } from "./filter_factory.js"; import { DOMStandardFontDataFactory } from "display-standard_fontdata_factory"; import { DOMWasmFactory } from "display-wasm_factory"; -import { FontInfo } from "../shared/obj-bin-transform.js"; +import { FontLoader } from "./font_loader.js"; import { GlobalWorkerOptions } from "./worker_options.js"; import { Metadata } from "./metadata.js"; import { OptionalContentConfig } from "./optional_content_config.js"; @@ -76,6 +75,7 @@ import { PDFDataTransportStream } from "./transport_stream.js"; import { PDFFetchStream } from "display-fetch_stream"; import { PDFNetworkStream } from "display-network"; import { PDFNodeStream } from "display-node_stream"; +import { setupHandler } from "../shared/handle_objs.js"; import { TextLayer } from "./text_layer.js"; import { XfaText } from "./xfa_text.js"; @@ -2828,105 +2828,13 @@ class WorkerTransport { page._startRenderPage(data.transparency, data.cacheKey); }); - messageHandler.on("commonobj", ([id, type, exportedData]) => { - if (this.destroyed) { - return null; // Ignore any pending requests if the worker was terminated. - } - - if (this.commonObjs.has(id)) { - return null; - } - - switch (type) { - case "Font": - if ("error" in exportedData) { - const exportedError = exportedData.error; - warn(`Error during font loading: ${exportedError}`); - this.commonObjs.resolve(id, exportedError); - break; - } - - const fontData = new FontInfo(exportedData); - const inspectFont = - this._params.pdfBug && globalThis.FontInspector?.enabled - ? (font, url) => globalThis.FontInspector.fontAdded(font, url) - : null; - const font = new FontFaceObject( - fontData, - inspectFont, - exportedData.extra, - exportedData.charProcOperatorList - ); - - this.fontLoader - .bind(font) - .catch(() => messageHandler.sendWithPromise("FontFallback", { id })) - .finally(() => { - if (!font.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.clearData(); - } - this.commonObjs.resolve(id, font); - }); - break; - case "CopyLocalImage": - const { imageRef } = exportedData; - assert(imageRef, "The imageRef must be defined."); - - for (const pageProxy of this.#pageCache.values()) { - for (const [, data] of pageProxy.objs) { - if (data?.ref !== imageRef) { - continue; - } - if (!data.dataLen) { - return null; - } - this.commonObjs.resolve(id, structuredClone(data)); - return data.dataLen; - } - } - break; - case "FontPath": - case "Image": - case "Pattern": - this.commonObjs.resolve(id, exportedData); - break; - default: - throw new Error(`Got unknown common object type ${type}`); - } - - return null; - }); - - messageHandler.on("obj", ([id, pageIndex, type, imageData]) => { - if (this.destroyed) { - // Ignore any pending requests if the worker was terminated. - return; - } - - const pageProxy = this.#pageCache.get(pageIndex); - if (pageProxy.objs.has(id)) { - return; - } - // Don't store data *after* cleanup has successfully run, see bug 1854145. - if (pageProxy._intentStates.size === 0) { - imageData?.bitmap?.close(); // Release any `ImageBitmap` data. - return; - } - - switch (type) { - case "Image": - case "Pattern": - pageProxy.objs.resolve(id, imageData); - break; - default: - throw new Error(`Got unknown object type ${type}`); - } - }); + setupHandler( + messageHandler, + this.destroyed, + this.commonObjs, + this.#pageCache, + this.fontLoader + ); messageHandler.on("DocProgress", data => { if (this.destroyed) { diff --git a/src/display/renderer_worker.js b/src/display/renderer_worker.js index 22b488907..588158ee0 100644 --- a/src/display/renderer_worker.js +++ b/src/display/renderer_worker.js @@ -1,10 +1,11 @@ -import { assert, warn } from "../shared/util.js"; -import { FontFaceObject, FontLoader } from "./font_loader.js"; +import { assert } from "../shared/util.js"; import { CanvasGraphics } from "./canvas.js"; import { DOMFilterFactory } from "./filter_factory.js"; +import { FontLoader } from "./font_loader.js"; import { MessageHandler } from "../shared/message_handler.js"; import { OffscreenCanvasFactory } from "./canvas_factory.js"; import { PDFObjects } from "./display_utils.js"; +import { setupHandler } from "../shared/handle_objs.js"; class RendererMessageHandler { static #commonObjs = new PDFObjects(); @@ -56,19 +57,13 @@ class RendererMessageHandler { }); this.#filterFactory = new DOMFilterFactory({}); - workerHandler.on("commonobj", ([id, type, data]) => { - if (terminated) { - throw new Error("Renderer worker has been terminated."); - } - this.handleCommonObj(id, type, data, workerHandler); - }); - - workerHandler.on("obj", ([id, pageIndex, type, data]) => { - if (terminated) { - throw new Error("Renderer worker has been terminated."); - } - this.handleObj(pageIndex, id, type, data); - }); + setupHandler( + workerHandler, + terminated, + this.#commonObjs, + this.#objsMap, + this.#fontLoader + ); }); mainHandler.on( @@ -168,90 +163,6 @@ class RendererMessageHandler { mainHandler = null; }); } - - static handleCommonObj(id, type, exportedData, handler) { - if (this.#commonObjs.has(id)) { - return null; - } - - switch (type) { - case "Font": - if ("error" in exportedData) { - const exportedError = exportedData.error; - warn(`Error during font loading: ${exportedError}`); - this.#commonObjs.resolve(id, exportedError); - break; - } - - // TODO: Make FontInspector work again. - const inspectFont = null; - // this._params.pdfBug && globalThis.FontInspector?.enabled - // ? (font, url) => globalThis.FontInspector.fontAdded(font, url) - // : null; - const font = new FontFaceObject(exportedData, inspectFont); - - this.#fontLoader - .bind(font) - .catch(() => handler.sendWithPromise("FontFallback", { id })) - .finally(() => { - if (!font.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); - }); - break; - case "CopyLocalImage": - const { imageRef } = exportedData; - assert(imageRef, "The imageRef must be defined."); - - for (const objs of this.#objsMap.values()) { - for (const [, data] of objs) { - if (data?.ref !== imageRef) { - continue; - } - if (!data.dataLen) { - return null; - } - this.#commonObjs.resolve(id, structuredClone(data)); - return data.dataLen; - } - } - break; - case "FontPath": - case "Image": - case "Pattern": - this.#commonObjs.resolve(id, exportedData); - break; - default: - throw new Error(`Got unknown common object type ${type}`); - } - - return null; - } - - static handleObj(pageIndex, id, type, exportedData) { - const objs = this.pageObjs(pageIndex); - - if (objs.has(id)) { - return; - } - - switch (type) { - case "Image": - case "Pattern": - objs.resolve(id, exportedData); - break; - default: - throw new Error( - `Got unknown object type ${type} id ${id} for page ${pageIndex} data ${JSON.stringify(exportedData)}` - ); - } - } } export { RendererMessageHandler }; diff --git a/src/shared/handle_objs.js b/src/shared/handle_objs.js new file mode 100644 index 000000000..4c45d501c --- /dev/null +++ b/src/shared/handle_objs.js @@ -0,0 +1,108 @@ +import { assert, warn } from "../shared/util.js"; +import { FontFaceObject } from "../display/font_loader.js"; +import { FontInfo } from "../shared/obj-bin-transform.js"; + +function setupHandler(handler, destroyed, commonObjs, pages, fontLoader) { + handler.on("commonobj", ([id, type, exportedData]) => { + if (destroyed) { + return null; // Ignore any pending requests if the worker was terminated. + } + + if (commonObjs.has(id)) { + return null; + } + + switch (type) { + case "Font": + if ("error" in exportedData) { + const exportedError = exportedData.error; + warn(`Error during font loading: ${exportedError}`); + commonObjs.resolve(id, exportedError); + break; + } + + const fontData = new FontInfo(exportedData); + const inspectFont = // TODO: Fix this + // this._params.pdfBug && globalThis.FontInspector?.enabled + // ? (font, url) => globalThis.FontInspector.fontAdded(font, url) + // : null; + null; + const font = new FontFaceObject( + fontData, + inspectFont, + exportedData.extra, + exportedData.charProcOperatorList + ); + + fontLoader + .bind(font) + .catch(() => handler.sendWithPromise("FontFallback", { id })) + .finally(() => { + if (!font.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.clearData(); + } + commonObjs.resolve(id, font); + }); + break; + case "CopyLocalImage": + const { imageRef } = exportedData; + assert(imageRef, "The imageRef must be defined."); + + for (const page of pages.values()) { + for (const [, data] of page.objs) { + if (data?.ref !== imageRef) { + continue; + } + if (!data.dataLen) { + return null; + } + commonObjs.resolve(id, structuredClone(data)); + return data.dataLen; + } + } + break; + case "FontPath": + case "Image": + case "Pattern": + commonObjs.resolve(id, exportedData); + break; + default: + throw new Error(`Got unknown common object type ${type}`); + } + + return null; + }); + + handler.on("obj", ([id, pageIndex, type, imageData]) => { + if (destroyed) { + // Ignore any pending requests if the worker was terminated. + return; + } + + const page = pages.get(pageIndex); + if (page.objs.has(id)) { + return; + } + // Don't store data *after* cleanup has successfully run, see bug 1854145. + if (page._intentStates.size === 0) { + imageData?.bitmap?.close(); // Release any `ImageBitmap` data. + return; + } + + switch (type) { + case "Image": + case "Pattern": + page.objs.resolve(id, imageData); + break; + default: + throw new Error(`Got unknown object type ${type}`); + } + }); +} + +export { setupHandler };