Merge pull request #19895 from Snuffleupagus/ObjectLoader-improve

Unify method return values in the `ObjectLoader` class, and simplify how the `ObjectLoader` is used
This commit is contained in:
Jonas Jenwald 2025-05-06 21:18:09 +02:00 committed by GitHub
commit 6f052312d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 32 deletions

View File

@ -303,6 +303,11 @@ export default [
selector: "NewExpression[callee.name='Name']", selector: "NewExpression[callee.name='Name']",
message: "Use `Name.get()` rather than `new 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']", selector: "NewExpression[callee.name='Ref']",
message: "Use `Ref.get()` rather than `new Ref()`.", message: "Use `Ref.get()` rather than `new Ref()`.",

View File

@ -1158,15 +1158,12 @@ class Annotation {
} }
} }
loadResources(keys, appearance) { async loadResources(keys, appearance) {
return appearance.dict.getAsync("Resources").then(resources => { const resources = await appearance.dict.getAsync("Resources");
if (!resources) { if (resources) {
return undefined; await ObjectLoader.load(resources, keys, resources.xref);
} }
return resources;
const objectLoader = new ObjectLoader(resources, keys, resources.xref);
return objectLoader.load().then(() => resources);
});
} }
async getOperatorList(evaluator, task, intent, annotationStorage) { async getOperatorList(evaluator, task, intent, annotationStorage) {

View File

@ -417,8 +417,7 @@ class Page {
// TODO: add async `_getInheritableProperty` and remove this. // TODO: add async `_getInheritableProperty` and remove this.
await (this.resourcesPromise ??= this.pdfManager.ensure(this, "resources")); await (this.resourcesPromise ??= this.pdfManager.ensure(this, "resources"));
const objectLoader = new ObjectLoader(this.resources, keys, this.xref); await ObjectLoader.load(this.resources, keys, this.xref);
await objectLoader.load();
} }
async #getMergedResources(streamDict, keys) { async #getMergedResources(streamDict, keys) {
@ -430,8 +429,7 @@ class Page {
if (!(localResources instanceof Dict && localResources.size)) { if (!(localResources instanceof Dict && localResources.size)) {
return this.resources; return this.resources;
} }
const objectLoader = new ObjectLoader(localResources, keys, this.xref); await ObjectLoader.load(localResources, keys, this.xref);
await objectLoader.load();
return Dict.merge({ return Dict.merge({
xref: this.xref, xref: this.xref,
@ -1254,8 +1252,7 @@ class PDFDocument {
if (!(resources instanceof Dict)) { if (!(resources instanceof Dict)) {
return; return;
} }
const objectLoader = new ObjectLoader(resources, ["Font"], this.xref); await ObjectLoader.load(resources, ["Font"], this.xref);
await objectLoader.load();
const fontRes = resources.get("Font"); const fontRes = resources.get("Font");
if (!(fontRes instanceof Dict)) { if (!(fontRes instanceof Dict)) {

View File

@ -54,21 +54,16 @@ function addChildren(node, nodesToVisit) {
* entire PDF document object graph to be traversed. * entire PDF document object graph to be traversed.
*/ */
class ObjectLoader { class ObjectLoader {
refSet = new RefSet();
constructor(dict, keys, xref) { constructor(dict, keys, xref) {
this.dict = dict; this.dict = dict;
this.keys = keys; this.keys = keys;
this.xref = xref; this.xref = xref;
this.refSet = null;
} }
async load() { 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; const { keys, dict } = this;
this.refSet = new RefSet();
// Setup the initial nodes to visit. // Setup the initial nodes to visit.
const nodesToVisit = []; const nodesToVisit = [];
for (const key of keys) { for (const key of keys) {
@ -78,10 +73,12 @@ class ObjectLoader {
nodesToVisit.push(rawValue); 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 nodesToRevisit = [];
const pendingRequests = []; const pendingRequests = [];
// DFS walk of the object graph. // DFS walk of the object graph.
@ -99,11 +96,10 @@ class ObjectLoader {
currentNode = this.xref.fetch(currentNode); currentNode = this.xref.fetch(currentNode);
} catch (ex) { } catch (ex) {
if (!(ex instanceof MissingDataException)) { if (!(ex instanceof MissingDataException)) {
warn(`ObjectLoader._walk - requesting all data: "${ex}".`); warn(`ObjectLoader.#walk - requesting all data: "${ex}".`);
this.refSet = null;
const { manager } = this.xref.stream; await this.xref.stream.manager.requestAllChunks();
return manager.requestAllChunks(); return;
} }
nodesToRevisit.push(currentNode); nodesToRevisit.push(currentNode);
pendingRequests.push({ begin: ex.begin, end: ex.end }); pendingRequests.push({ begin: ex.begin, end: ex.end });
@ -139,11 +135,18 @@ class ObjectLoader {
this.refSet.remove(node); 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();
} }
} }