From 2979e23f3c9336ede2e5862aa2e721dc6f2942da Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 4 May 2025 12:34:30 +0200 Subject: [PATCH 1/3] Ensure that `XFAFactory.prototype.isValid` returns a boolean value Considering the name of the method, and how it's actually being used, you'd expect it to return a boolean value. Given how it's currently being used this inconsistency doesn't cause any issues, however we should still fix this. --- src/core/xfa/factory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/xfa/factory.js b/src/core/xfa/factory.js index 7f9f4f28d..5dacfbb10 100644 --- a/src/core/xfa/factory.js +++ b/src/core/xfa/factory.js @@ -43,7 +43,7 @@ class XFAFactory { } isValid() { - return this.root && this.form; + return !!(this.root && this.form); } /** From 604153957afee4a2857de343258276d8e813ed63 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 4 May 2025 12:34:52 +0200 Subject: [PATCH 2/3] Reduce duplication when parsing fonts in `loadXfaFonts` Currently we repeat virtually the same code when calling the `PartialEvaluator.prototype.handleSetFont` method, which we can avoid by introducing an inline helper function. --- src/core/document.js | 58 ++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/core/document.js b/src/core/document.js index 55df5bf66..f02437043 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -1292,6 +1292,23 @@ class PDFDocument { }, }; + const parseFont = (fontName, fallbackFontDict, cssFontInfo) => + partialEvaluator + .handleSetFont( + resources, + [Name.get(fontName), 1], + /* fontRef = */ null, + operatorList, + task, + initialState, + fallbackFontDict, + cssFontInfo + ) + .catch(reason => { + warn(`loadXfaFonts: "${reason}".`); + return null; + }); + const promises = []; for (const [fontName, font] of fontRes) { const descriptor = font.get("FontDescriptor"); @@ -1313,21 +1330,7 @@ class PDFDocument { continue; } promises.push( - partialEvaluator - .handleSetFont( - resources, - [Name.get(fontName), 1], - /* fontRef = */ null, - operatorList, - task, - initialState, - /* fallbackFontDict = */ null, - /* cssFontInfo = */ cssFontInfo - ) - .catch(function (reason) { - warn(`loadXfaFonts: "${reason}".`); - return null; - }) + parseFont(fontName, /* fallbackFontDict = */ null, cssFontInfo) ); } @@ -1365,28 +1368,13 @@ class PDFDocument { { name: "BoldItalic", fontWeight: 700, italicAngle: 12 }, ]) { const name = `${missing}-${fontInfo.name}`; - const dict = getXfaFontDict(name); promises.push( - partialEvaluator - .handleSetFont( - resources, - [Name.get(name), 1], - /* fontRef = */ null, - operatorList, - task, - initialState, - /* fallbackFontDict = */ dict, - /* cssFontInfo = */ { - fontFamily: missing, - fontWeight: fontInfo.fontWeight, - italicAngle: fontInfo.italicAngle, - } - ) - .catch(function (reason) { - warn(`loadXfaFonts: "${reason}".`); - return null; - }) + parseFont(name, getXfaFontDict(name), { + fontFamily: missing, + fontWeight: fontInfo.fontWeight, + italicAngle: fontInfo.italicAngle, + }) ); } } From d9548b1c18b003c20c0d46c6363cde82863617ea Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 4 May 2025 12:35:00 +0200 Subject: [PATCH 3/3] Slightly re-factor how we pre-load fonts and images in XFA documents Rather than "manually" invoking the methods from the `src/core/worker.js` file we introduce a single `PDFDocument`-method that handles this for us, and make the current methods private. Since this code is only invoked at most *once* per document, and only for XFA documents, we can use `BasePdfManager.prototype.ensureDoc` directly rather than needing a stand-alone method. --- src/core/document.js | 24 +++++++++++++++++------- src/core/pdf_manager.js | 8 -------- src/core/worker.js | 13 +++---------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/core/document.js b/src/core/document.js index f02437043..b51ddad40 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -1237,7 +1237,7 @@ class PDFDocument { return this.xfaFactory ? this.xfaFactory.getPages() : null; } - async loadXfaImages() { + async #loadXfaImages() { const xfaImages = await this.pdfManager.ensureCatalog("xfaImages"); if (!xfaImages) { return; @@ -1245,7 +1245,7 @@ class PDFDocument { this.xfaFactory.setImages(xfaImages); } - async loadXfaFonts(handler, task) { + async #loadXfaFonts(handler, task) { const acroForm = await this.pdfManager.ensureCatalog("acroForm"); if (!acroForm) { return; @@ -1264,18 +1264,19 @@ class PDFDocument { const options = Object.assign( Object.create(null), - this.pdfManager.evaluatorOptions + this.pdfManager.evaluatorOptions, + { useSystemFonts: false } ); - options.useSystemFonts = false; + const { builtInCMapCache, fontCache, standardFontDataCache } = this.catalog; const partialEvaluator = new PartialEvaluator({ xref: this.xref, handler, pageIndex: -1, idFactory: this._globalIdFactory, - fontCache: this.catalog.fontCache, - builtInCMapCache: this.catalog.builtInCMapCache, - standardFontDataCache: this.catalog.standardFontDataCache, + fontCache, + builtInCMapCache, + standardFontDataCache, options, }); const operatorList = new OperatorList(); @@ -1383,6 +1384,15 @@ class PDFDocument { this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts); } + loadXfaResources(handler, task) { + return Promise.all([ + this.#loadXfaFonts(handler, task).catch(() => { + // Ignore errors, to allow the document to load. + }), + this.#loadXfaImages(), + ]); + } + serializeXfaData(annotationStorage) { return this.xfaFactory ? this.xfaFactory.serializeData(annotationStorage) diff --git a/src/core/pdf_manager.js b/src/core/pdf_manager.js index cac6c3c5e..9ee866cdb 100644 --- a/src/core/pdf_manager.js +++ b/src/core/pdf_manager.js @@ -115,14 +115,6 @@ class BasePdfManager { return this.pdfDocument.fontFallback(id, handler); } - loadXfaFonts(handler, task) { - return this.pdfDocument.loadXfaFonts(handler, task); - } - - loadXfaImages() { - return this.pdfDocument.loadXfaImages(); - } - cleanup(manuallyTriggered = false) { return this.pdfDocument.cleanup(manuallyTriggered); } diff --git a/src/core/worker.js b/src/core/worker.js index 66fa9ae15..3d1abbaf1 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -170,18 +170,11 @@ class WorkerMessageHandler { const isPureXfa = await pdfManager.ensureDoc("isPureXfa"); if (isPureXfa) { - const task = new WorkerTask("loadXfaFonts"); + const task = new WorkerTask("loadXfaResources"); startWorkerTask(task); - await Promise.all([ - pdfManager - .loadXfaFonts(handler, task) - .catch(reason => { - // Ignore errors, to allow the document to load. - }) - .then(() => finishWorkerTask(task)), - pdfManager.loadXfaImages(), - ]); + await pdfManager.ensureDoc("loadXfaResources", [handler, task]); + finishWorkerTask(task); } const [numPages, fingerprints] = await Promise.all([