Merge pull request #11314 from Snuffleupagus/revert-11312-async-getPageDict

Revert "Convert `Catalog.getPageDict` to an `async` method"
This commit is contained in:
Tim van der Meij 2019-11-10 13:05:57 +01:00 committed by GitHub
commit c6ee4480f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,9 +14,10 @@
*/ */
import { import {
assert, bytesToString, createValidAbsoluteUrl, FormatError, info, assert, bytesToString, createPromiseCapability, createValidAbsoluteUrl,
InvalidPDFException, isBool, isNum, isString, PermissionFlag, shadow, FormatError, info, InvalidPDFException, isBool, isNum, isString,
stringToPDFString, stringToUTF8String, unreachable, warn PermissionFlag, shadow, stringToPDFString, stringToUTF8String, unreachable,
warn
} from '../shared/util'; } from '../shared/util';
import { import {
clearPrimitiveCaches, Cmd, Dict, isCmd, isDict, isName, isRef, isRefsEqual, clearPrimitiveCaches, Cmd, Dict, isCmd, isDict, isName, isRef, isRefsEqual,
@ -679,86 +680,99 @@ class Catalog {
}); });
} }
async getPageDict(pageIndex) { getPageDict(pageIndex) {
const capability = createPromiseCapability();
const nodesToVisit = [this.catDict.getRaw('Pages')]; const nodesToVisit = [this.catDict.getRaw('Pages')];
const xref = this.xref, pageKidsCountCache = this.pageKidsCountCache; const xref = this.xref, pageKidsCountCache = this.pageKidsCountCache;
let count, currentPageIndex = 0; let count, currentPageIndex = 0;
while (nodesToVisit.length) { function next() {
const currentNode = nodesToVisit.pop(); while (nodesToVisit.length) {
const currentNode = nodesToVisit.pop();
if (currentNode instanceof Ref) { if (isRef(currentNode)) {
count = pageKidsCountCache.get(currentNode); count = pageKidsCountCache.get(currentNode);
// Skip nodes where the page can't be. // Skip nodes where the page can't be.
if (count > 0 && currentPageIndex + count < pageIndex) { if (count > 0 && currentPageIndex + count < pageIndex) {
currentPageIndex += count; currentPageIndex += count;
continue; continue;
} }
const obj = await xref.fetchAsync(currentNode);
if ((obj instanceof Dict) && (isName(obj.get('Type'), 'Page') || xref.fetchAsync(currentNode).then(function(obj) {
(!obj.has('Type') && !obj.has('Kids')))) { if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) {
if (pageIndex === currentPageIndex) { if (pageIndex === currentPageIndex) {
// Cache the Page reference, since it can *greatly* improve // Cache the Page reference, since it can *greatly* improve
// performance by reducing redundant lookups in long documents // performance by reducing redundant lookups in long documents
// where all nodes are found at *one* level of the tree. // where all nodes are found at *one* level of the tree.
if (currentNode && !pageKidsCountCache.has(currentNode)) { if (currentNode && !pageKidsCountCache.has(currentNode)) {
pageKidsCountCache.put(currentNode, 1); pageKidsCountCache.put(currentNode, 1);
}
capability.resolve([obj, currentNode]);
} else {
currentPageIndex++;
next();
}
return;
} }
return [obj, currentNode]; nodesToVisit.push(obj);
next();
}, capability.reject);
return;
}
// Must be a child page dictionary.
if (!isDict(currentNode)) {
capability.reject(new FormatError(
'Page dictionary kid reference points to wrong type of object.'));
return;
}
count = currentNode.get('Count');
if (Number.isInteger(count) && count >= 0) {
// Cache the Kids count, since it can reduce redundant lookups in
// documents where all nodes are found at *one* level of the tree.
const objId = currentNode.objId;
if (objId && !pageKidsCountCache.has(objId)) {
pageKidsCountCache.put(objId, count);
} }
currentPageIndex++; // Skip nodes where the page can't be.
continue; if (currentPageIndex + count <= pageIndex) {
} currentPageIndex += count;
nodesToVisit.push(obj); continue;
continue;
}
// Must be a child page dictionary.
if (!(currentNode instanceof Dict)) {
throw new FormatError(
'Page dictionary kid reference points to wrong type of object.');
}
count = currentNode.get('Count');
if (Number.isInteger(count) && count >= 0) {
// Cache the Kids count, since it can reduce redundant lookups in
// documents where all nodes are found at *one* level of the tree.
const objId = currentNode.objId;
if (objId && !pageKidsCountCache.has(objId)) {
pageKidsCountCache.put(objId, count);
}
// Skip nodes where the page can't be.
if (currentPageIndex + count <= pageIndex) {
currentPageIndex += count;
continue;
}
}
const kids = currentNode.get('Kids');
if (!Array.isArray(kids)) {
// Prevent errors in corrupt PDF documents that violate the
// specification by *inlining* Page dicts directly in the Kids
// array, rather than using indirect objects (fixes issue9540.pdf).
if (isName(currentNode.get('Type'), 'Page') ||
(!currentNode.has('Type') && currentNode.has('Contents'))) {
if (currentPageIndex === pageIndex) {
return [currentNode, null];
} }
currentPageIndex++;
continue;
} }
throw new FormatError('Page dictionary kids object is not an array.');
}
// Always check all `Kids` nodes, to avoid getting stuck in an empty const kids = currentNode.get('Kids');
// node further down in the tree (see issue5644.pdf, issue8088.pdf), if (!Array.isArray(kids)) {
// and to ensure that we actually find the correct `Page` dict. // Prevent errors in corrupt PDF documents that violate the
for (let last = kids.length - 1; last >= 0; last--) { // specification by *inlining* Page dicts directly in the Kids
nodesToVisit.push(kids[last]); // array, rather than using indirect objects (fixes issue9540.pdf).
if (isName(currentNode.get('Type'), 'Page') ||
(!currentNode.has('Type') && currentNode.has('Contents'))) {
if (currentPageIndex === pageIndex) {
capability.resolve([currentNode, null]);
return;
}
currentPageIndex++;
continue;
}
capability.reject(new FormatError(
'Page dictionary kids object is not an array.'));
return;
}
// Always check all `Kids` nodes, to avoid getting stuck in an empty
// node further down in the tree (see issue5644.pdf, issue8088.pdf),
// and to ensure that we actually find the correct `Page` dict.
for (let last = kids.length - 1; last >= 0; last--) {
nodesToVisit.push(kids[last]);
}
} }
capability.reject(new Error(`Page index ${pageIndex} not found.`));
} }
throw new Error(`Page index ${pageIndex} not found.`); next();
return capability.promise;
} }
getPageIndex(pageRef) { getPageIndex(pageRef) {