diff --git a/eslint.config.mjs b/eslint.config.mjs index ffb146870..7dd65dd6e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -303,6 +303,11 @@ export default [ selector: "NewExpression[callee.name='Name']", message: "Use `Name.get()` rather than `new Name()`.", }, + { + selector: "NewExpression[callee.name='ObjectLoader']", + message: + "Use `ObjectLoader.load()` rather than `new ObjectLoader()`.", + }, { selector: "NewExpression[callee.name='Ref']", message: "Use `Ref.get()` rather than `new Ref()`.", diff --git a/src/core/annotation.js b/src/core/annotation.js index 3bdf9b788..b6106fbee 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -1158,15 +1158,12 @@ class Annotation { } } - loadResources(keys, appearance) { - return appearance.dict.getAsync("Resources").then(resources => { - if (!resources) { - return undefined; - } - - const objectLoader = new ObjectLoader(resources, keys, resources.xref); - return objectLoader.load().then(() => resources); - }); + async loadResources(keys, appearance) { + const resources = await appearance.dict.getAsync("Resources"); + if (resources) { + await ObjectLoader.load(resources, keys, resources.xref); + } + return resources; } async getOperatorList(evaluator, task, intent, annotationStorage) { diff --git a/src/core/document.js b/src/core/document.js index b51ddad40..0eb0f96d7 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -417,8 +417,7 @@ class Page { // TODO: add async `_getInheritableProperty` and remove this. await (this.resourcesPromise ??= this.pdfManager.ensure(this, "resources")); - const objectLoader = new ObjectLoader(this.resources, keys, this.xref); - await objectLoader.load(); + await ObjectLoader.load(this.resources, keys, this.xref); } async #getMergedResources(streamDict, keys) { @@ -430,8 +429,7 @@ class Page { if (!(localResources instanceof Dict && localResources.size)) { return this.resources; } - const objectLoader = new ObjectLoader(localResources, keys, this.xref); - await objectLoader.load(); + await ObjectLoader.load(localResources, keys, this.xref); return Dict.merge({ xref: this.xref, @@ -1254,8 +1252,7 @@ class PDFDocument { if (!(resources instanceof Dict)) { return; } - const objectLoader = new ObjectLoader(resources, ["Font"], this.xref); - await objectLoader.load(); + await ObjectLoader.load(resources, ["Font"], this.xref); const fontRes = resources.get("Font"); if (!(fontRes instanceof Dict)) { diff --git a/src/core/object_loader.js b/src/core/object_loader.js index 6f75e4561..f36959239 100644 --- a/src/core/object_loader.js +++ b/src/core/object_loader.js @@ -54,21 +54,16 @@ function addChildren(node, nodesToVisit) { * entire PDF document object graph to be traversed. */ class ObjectLoader { + refSet = new RefSet(); + constructor(dict, keys, xref) { this.dict = dict; this.keys = keys; this.xref = xref; - this.refSet = null; } async load() { - // Don't walk the graph if all the data is already loaded. - if (this.xref.stream.isDataLoaded) { - return undefined; - } - const { keys, dict } = this; - this.refSet = new RefSet(); // Setup the initial nodes to visit. const nodesToVisit = []; for (const key of keys) { @@ -78,10 +73,12 @@ class ObjectLoader { nodesToVisit.push(rawValue); } } - return this._walk(nodesToVisit); + await this.#walk(nodesToVisit); + + this.refSet = null; // Everything is loaded, clear the cache. } - async _walk(nodesToVisit) { + async #walk(nodesToVisit) { const nodesToRevisit = []; const pendingRequests = []; // DFS walk of the object graph. @@ -99,11 +96,10 @@ class ObjectLoader { currentNode = this.xref.fetch(currentNode); } catch (ex) { if (!(ex instanceof MissingDataException)) { - warn(`ObjectLoader._walk - requesting all data: "${ex}".`); - this.refSet = null; + warn(`ObjectLoader.#walk - requesting all data: "${ex}".`); - const { manager } = this.xref.stream; - return manager.requestAllChunks(); + await this.xref.stream.manager.requestAllChunks(); + return; } nodesToRevisit.push(currentNode); pendingRequests.push({ begin: ex.begin, end: ex.end }); @@ -139,11 +135,18 @@ class ObjectLoader { this.refSet.remove(node); } } - return this._walk(nodesToRevisit); + await this.#walk(nodesToRevisit); } - // Everything is loaded. - this.refSet = null; - return undefined; + } + + static async load(obj, keys, xref) { + // Don't walk the graph if all the data is already loaded. + if (xref.stream.isDataLoaded) { + return; + } + // eslint-disable-next-line no-restricted-syntax + const objLoader = new ObjectLoader(obj, keys, xref); + await objLoader.load(); } }