Merge pull request #5351 from timvandermeij/destinations
Require destinations when they are needed and do not fetch all of them in advance
This commit is contained in:
commit
878fad47a7
102
src/core/obj.js
102
src/core/obj.js
@ -457,6 +457,38 @@ var Catalog = (function CatalogClosure() {
|
||||
}
|
||||
return shadow(this, 'destinations', dests);
|
||||
},
|
||||
getDestination: function Catalog_getDestination(destinationId) {
|
||||
function fetchDestination(dest) {
|
||||
return isDict(dest) ? dest.get('D') : dest;
|
||||
}
|
||||
|
||||
var xref = this.xref;
|
||||
var dest, nameTreeRef, nameDictionaryRef;
|
||||
var obj = this.catDict.get('Names');
|
||||
if (obj && obj.has('Dests')) {
|
||||
nameTreeRef = obj.getRaw('Dests');
|
||||
} else if (this.catDict.has('Dests')) {
|
||||
nameDictionaryRef = this.catDict.get('Dests');
|
||||
}
|
||||
|
||||
if (nameDictionaryRef) {
|
||||
// reading simple destination dictionary
|
||||
obj = nameDictionaryRef;
|
||||
obj.forEach(function catalogForEach(key, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
if (key === destinationId) {
|
||||
dest = fetchDestination(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (nameTreeRef) {
|
||||
var nameTree = new NameTree(nameTreeRef, xref);
|
||||
dest = fetchDestination(nameTree.get(destinationId));
|
||||
}
|
||||
return dest;
|
||||
},
|
||||
get attachments() {
|
||||
var xref = this.xref;
|
||||
var attachments = null, nameTreeRef;
|
||||
@ -1358,6 +1390,76 @@ var NameTree = (function NameTreeClosure() {
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
},
|
||||
|
||||
get: function NameTree_get(destinationId) {
|
||||
if (!this.root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var xref = this.xref;
|
||||
var kidsOrNames = xref.fetchIfRef(this.root);
|
||||
var loopCount = 0;
|
||||
var MAX_NAMES_LEVELS = 10;
|
||||
var l, r, m;
|
||||
|
||||
// Perform a binary search to quickly find the entry that
|
||||
// contains the named destination we are looking for.
|
||||
while (kidsOrNames.has('Kids')) {
|
||||
loopCount++;
|
||||
if (loopCount > MAX_NAMES_LEVELS) {
|
||||
warn('Search depth limit for named destionations has been reached.');
|
||||
return null;
|
||||
}
|
||||
|
||||
var kids = kidsOrNames.get('Kids');
|
||||
if (!isArray(kids)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
l = 0;
|
||||
r = kids.length - 1;
|
||||
while (l <= r) {
|
||||
m = (l + r) >> 1;
|
||||
var kid = xref.fetchIfRef(kids[m]);
|
||||
var limits = kid.get('Limits');
|
||||
|
||||
if (destinationId < limits[0]) {
|
||||
r = m - 1;
|
||||
} else if (destinationId > limits[1]) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
kidsOrNames = xref.fetchIfRef(kids[m]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (l > r) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, then we have found the right entry. Now
|
||||
// go through the named destinations in the Named dictionary
|
||||
// until we find the exact destination we're looking for.
|
||||
var names = kidsOrNames.get('Names');
|
||||
if (isArray(names)) {
|
||||
// Perform a binary search to reduce the lookup time.
|
||||
l = 0;
|
||||
r = names.length - 2;
|
||||
while (l <= r) {
|
||||
// Check only even indices (0, 2, 4, ...) because the
|
||||
// odd indices contain the actual D array.
|
||||
m = (l + r) & ~1;
|
||||
if (destinationId < names[m]) {
|
||||
r = m - 2;
|
||||
} else if (destinationId > names[m]) {
|
||||
l = m + 2;
|
||||
} else {
|
||||
return xref.fetchIfRef(names[m + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return NameTree;
|
||||
|
@ -325,6 +325,12 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
||||
}
|
||||
);
|
||||
|
||||
handler.on('GetDestination',
|
||||
function wphSetupGetDestination(data) {
|
||||
return pdfManager.ensureCatalog('getDestination', [ data.id ]);
|
||||
}
|
||||
);
|
||||
|
||||
handler.on('GetAttachments',
|
||||
function wphSetupGetAttachments(data) {
|
||||
return pdfManager.ensureCatalog('attachments');
|
||||
|
@ -293,10 +293,20 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with a lookup table for
|
||||
* mapping named destinations to reference numbers.
|
||||
*
|
||||
* This can be slow for large documents: use getDestination instead
|
||||
*/
|
||||
getDestinations: function PDFDocumentProxy_getDestinations() {
|
||||
return this.transport.getDestinations();
|
||||
},
|
||||
/**
|
||||
* @param {string} id The named destination to get.
|
||||
* @return {Promise} A promise that is resolved with all information
|
||||
* of the given named destination.
|
||||
*/
|
||||
getDestination: function PDFDocumentProxy_getDestination(id) {
|
||||
return this.transport.getDestination(id);
|
||||
},
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with a lookup table for
|
||||
* mapping named attachments to their content.
|
||||
@ -1128,6 +1138,10 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
||||
return this.messageHandler.sendWithPromise('GetDestinations', null);
|
||||
},
|
||||
|
||||
getDestination: function WorkerTransport_getDestination(id) {
|
||||
return this.messageHandler.sendWithPromise('GetDestination', { id: id } );
|
||||
},
|
||||
|
||||
getAttachments: function WorkerTransport_getAttachments() {
|
||||
return this.messageHandler.sendWithPromise('GetAttachments', null);
|
||||
},
|
||||
|
@ -123,6 +123,13 @@ describe('api', function() {
|
||||
0, 841.89, null] });
|
||||
});
|
||||
});
|
||||
it('gets a destination', function() {
|
||||
var promise = doc.getDestination('chapter1');
|
||||
waitsForPromiseResolved(promise, function(data) {
|
||||
expect(data).toEqual([{ gen: 0, num: 17 }, { name: 'XYZ' },
|
||||
0, 841.89, null]);
|
||||
});
|
||||
});
|
||||
it('gets attachments', function() {
|
||||
var promise = doc.getAttachments();
|
||||
waitsForPromiseResolved(promise, function (data) {
|
||||
|
@ -675,15 +675,19 @@ var PDFViewerApplication = {
|
||||
}
|
||||
};
|
||||
|
||||
this.destinationsPromise.then(function() {
|
||||
if (typeof dest === 'string') {
|
||||
destString = dest;
|
||||
dest = self.destinations[dest];
|
||||
}
|
||||
if (!(dest instanceof Array)) {
|
||||
var destinationPromise;
|
||||
if (typeof dest === 'string') {
|
||||
destString = dest;
|
||||
destinationPromise = this.pdfDocument.getDestination(dest);
|
||||
} else {
|
||||
destinationPromise = Promise.resolve(dest);
|
||||
}
|
||||
destinationPromise.then(function(destination) {
|
||||
dest = destination;
|
||||
if (!(destination instanceof Array)) {
|
||||
return; // invalid destination
|
||||
}
|
||||
goToDestination(dest[0]);
|
||||
goToDestination(destination[0]);
|
||||
});
|
||||
},
|
||||
|
||||
@ -984,15 +988,8 @@ var PDFViewerApplication = {
|
||||
}
|
||||
});
|
||||
|
||||
var destinationsPromise =
|
||||
this.destinationsPromise = pdfDocument.getDestinations();
|
||||
destinationsPromise.then(function(destinations) {
|
||||
self.destinations = destinations;
|
||||
});
|
||||
|
||||
// outline depends on destinations and pagesRefMap
|
||||
var promises = [pagesPromise, destinationsPromise,
|
||||
this.animationStartedPromise];
|
||||
// outline depends on pagesRefMap
|
||||
var promises = [pagesPromise, this.animationStartedPromise];
|
||||
Promise.all(promises).then(function() {
|
||||
pdfDocument.getOutline().then(function(outline) {
|
||||
var outlineView = document.getElementById('outlineView');
|
||||
|
Loading…
x
Reference in New Issue
Block a user