From 6a935682fd6f81795e5fa8d855d5bde49d8ccb5c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 13 Apr 2021 18:25:42 +0200 Subject: [PATCH] Covert the `ObjectLoader` to a "normal" class --- src/core/object_loader.js | 229 +++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 117 deletions(-) diff --git a/src/core/object_loader.js b/src/core/object_loader.js index f2bff6ac0..f36016e6e 100644 --- a/src/core/object_loader.js +++ b/src/core/object_loader.js @@ -17,6 +17,30 @@ import { Dict, isStream, Ref, RefSet } from "./primitives.js"; import { MissingDataException } from "./core_utils.js"; import { warn } from "../shared/util.js"; +function mayHaveChildren(value) { + return ( + value instanceof Ref || + value instanceof Dict || + Array.isArray(value) || + isStream(value) + ); +} + +function addChildren(node, nodesToVisit) { + if (node instanceof Dict) { + node = node.getRawValues(); + } else if (isStream(node)) { + node = node.dict.getRawValues(); + } else if (!Array.isArray(node)) { + return; + } + for (const rawValue of node) { + if (mayHaveChildren(rawValue)) { + nodesToVisit.push(rawValue); + } + } +} + /** * A helper for loading missing data in `Dict` graphs. It traverses the graph * depth first and queues up any objects that have missing data. Once it has @@ -28,130 +52,101 @@ import { warn } from "../shared/util.js"; * that have references to the catalog or other pages since that will cause the * entire PDF document object graph to be traversed. */ -const ObjectLoader = (function () { - function mayHaveChildren(value) { - return ( - value instanceof Ref || - value instanceof Dict || - Array.isArray(value) || - isStream(value) - ); - } - - function addChildren(node, nodesToVisit) { - if (node instanceof Dict) { - node = node.getRawValues(); - } else if (isStream(node)) { - node = node.dict.getRawValues(); - } else if (!Array.isArray(node)) { - return; - } - for (const rawValue of node) { - if (mayHaveChildren(rawValue)) { - nodesToVisit.push(rawValue); - } - } - } - - // eslint-disable-next-line no-shadow - function ObjectLoader(dict, keys, xref) { +class ObjectLoader { + constructor(dict, keys, xref) { this.dict = dict; this.keys = keys; this.xref = xref; this.refSet = null; } - ObjectLoader.prototype = { - async load() { - // Don't walk the graph if all the data is already loaded; note that only - // `ChunkedStream` instances have a `allChunksLoaded` method. - if ( - !this.xref.stream.allChunksLoaded || - this.xref.stream.allChunksLoaded() - ) { - return undefined; - } - - const { keys, dict } = this; - this.refSet = new RefSet(); - // Setup the initial nodes to visit. - const nodesToVisit = []; - for (let i = 0, ii = keys.length; i < ii; i++) { - const rawValue = dict.getRaw(keys[i]); - // Skip nodes that are guaranteed to be empty. - if (rawValue !== undefined) { - nodesToVisit.push(rawValue); - } - } - return this._walk(nodesToVisit); - }, - - async _walk(nodesToVisit) { - const nodesToRevisit = []; - const pendingRequests = []; - // DFS walk of the object graph. - while (nodesToVisit.length) { - let currentNode = nodesToVisit.pop(); - - // Only references or chunked streams can cause missing data exceptions. - if (currentNode instanceof Ref) { - // Skip nodes that have already been visited. - if (this.refSet.has(currentNode)) { - continue; - } - try { - this.refSet.put(currentNode); - currentNode = this.xref.fetch(currentNode); - } catch (ex) { - if (!(ex instanceof MissingDataException)) { - warn(`ObjectLoader._walk - requesting all data: "${ex}".`); - this.refSet = null; - - const { manager } = this.xref.stream; - return manager.requestAllChunks(); - } - nodesToRevisit.push(currentNode); - pendingRequests.push({ begin: ex.begin, end: ex.end }); - } - } - if (currentNode && currentNode.getBaseStreams) { - const baseStreams = currentNode.getBaseStreams(); - let foundMissingData = false; - for (let i = 0, ii = baseStreams.length; i < ii; i++) { - const stream = baseStreams[i]; - if (stream.allChunksLoaded && !stream.allChunksLoaded()) { - foundMissingData = true; - pendingRequests.push({ begin: stream.start, end: stream.end }); - } - } - if (foundMissingData) { - nodesToRevisit.push(currentNode); - } - } - - addChildren(currentNode, nodesToVisit); - } - - if (pendingRequests.length) { - await this.xref.stream.manager.requestRanges(pendingRequests); - - for (let i = 0, ii = nodesToRevisit.length; i < ii; i++) { - const node = nodesToRevisit[i]; - // Remove any reference nodes from the current `RefSet` so they - // aren't skipped when we revist them. - if (node instanceof Ref) { - this.refSet.remove(node); - } - } - return this._walk(nodesToRevisit); - } - // Everything is loaded. - this.refSet = null; + async load() { + // Don't walk the graph if all the data is already loaded; note that only + // `ChunkedStream` instances have a `allChunksLoaded` method. + if ( + !this.xref.stream.allChunksLoaded || + this.xref.stream.allChunksLoaded() + ) { return undefined; - }, - }; + } - return ObjectLoader; -})(); + const { keys, dict } = this; + this.refSet = new RefSet(); + // Setup the initial nodes to visit. + const nodesToVisit = []; + for (let i = 0, ii = keys.length; i < ii; i++) { + const rawValue = dict.getRaw(keys[i]); + // Skip nodes that are guaranteed to be empty. + if (rawValue !== undefined) { + nodesToVisit.push(rawValue); + } + } + return this._walk(nodesToVisit); + } + + async _walk(nodesToVisit) { + const nodesToRevisit = []; + const pendingRequests = []; + // DFS walk of the object graph. + while (nodesToVisit.length) { + let currentNode = nodesToVisit.pop(); + + // Only references or chunked streams can cause missing data exceptions. + if (currentNode instanceof Ref) { + // Skip nodes that have already been visited. + if (this.refSet.has(currentNode)) { + continue; + } + try { + this.refSet.put(currentNode); + currentNode = this.xref.fetch(currentNode); + } catch (ex) { + if (!(ex instanceof MissingDataException)) { + warn(`ObjectLoader._walk - requesting all data: "${ex}".`); + this.refSet = null; + + const { manager } = this.xref.stream; + return manager.requestAllChunks(); + } + nodesToRevisit.push(currentNode); + pendingRequests.push({ begin: ex.begin, end: ex.end }); + } + } + if (currentNode && currentNode.getBaseStreams) { + const baseStreams = currentNode.getBaseStreams(); + let foundMissingData = false; + for (let i = 0, ii = baseStreams.length; i < ii; i++) { + const stream = baseStreams[i]; + if (stream.allChunksLoaded && !stream.allChunksLoaded()) { + foundMissingData = true; + pendingRequests.push({ begin: stream.start, end: stream.end }); + } + } + if (foundMissingData) { + nodesToRevisit.push(currentNode); + } + } + + addChildren(currentNode, nodesToVisit); + } + + if (pendingRequests.length) { + await this.xref.stream.manager.requestRanges(pendingRequests); + + for (let i = 0, ii = nodesToRevisit.length; i < ii; i++) { + const node = nodesToRevisit[i]; + // Remove any reference nodes from the current `RefSet` so they + // aren't skipped when we revist them. + if (node instanceof Ref) { + this.refSet.remove(node); + } + } + return this._walk(nodesToRevisit); + } + // Everything is loaded. + this.refSet = null; + return undefined; + } +} export { ObjectLoader };