Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[api-minor] Move the page reference/number caching into the API #18001

Merged
merged 3 commits into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/worker.js
Expand Up @@ -419,6 +419,7 @@ class WorkerMessageHandler {
return {
rotate,
ref,
refStr: ref?.toString() ?? null,
userUnit,
view,
};
Expand Down
41 changes: 34 additions & 7 deletions src/display/api.js
Expand Up @@ -560,6 +560,16 @@ function getDataProp(val) {
);
}

function isRefProxy(ref) {
return (
typeof ref === "object" &&
Number.isInteger(ref?.num) &&
ref.num >= 0 &&
Number.isInteger(ref?.gen) &&
ref.gen >= 0
);
}

/**
* @typedef {Object} OnProgressParameters
* @property {number} loaded - Currently loaded number of bytes.
Expand Down Expand Up @@ -1066,6 +1076,14 @@ class PDFDocumentProxy {
return this.loadingTask.destroy();
}

/**
* @param {RefProxy} ref - The page reference.
* @returns {number | null} The page number, if it's cached.
*/
cachedPageNumber(ref) {
return this._transport.cachedPageNumber(ref);
}

/**
* @type {DocumentInitParameters} A subset of the current
* {DocumentInitParameters}, which are needed in the viewer.
Expand Down Expand Up @@ -2340,6 +2358,8 @@ class WorkerTransport {

#pagePromises = new Map();

#pageRefCache = new Map();

#passwordCapability = null;

constructor(messageHandler, loadingTask, networkStream, params, factory) {
Expand Down Expand Up @@ -2483,6 +2503,7 @@ class WorkerTransport {
}
this.#pageCache.clear();
this.#pagePromises.clear();
this.#pageRefCache.clear();
// Allow `AnnotationStorage`-related clean-up when destroying the document.
if (this.hasOwnProperty("annotationStorage")) {
this.annotationStorage.resetModified();
Expand Down Expand Up @@ -2915,6 +2936,10 @@ class WorkerTransport {
if (this.destroyed) {
throw new Error("Transport destroyed");
}
if (pageInfo.refStr) {
this.#pageRefCache.set(pageInfo.refStr, pageNumber);
}

const page = new PDFPageProxy(
pageIndex,
pageInfo,
Expand All @@ -2929,13 +2954,7 @@ class WorkerTransport {
}

getPageIndex(ref) {
if (
typeof ref !== "object" ||
!Number.isInteger(ref?.num) ||
ref.num < 0 ||
!Number.isInteger(ref?.gen) ||
ref.gen < 0
) {
if (!isRefProxy(ref)) {
return Promise.reject(new Error("Invalid pageIndex request."));
}
return this.messageHandler.sendWithPromise("GetPageIndex", {
Expand Down Expand Up @@ -3076,6 +3095,14 @@ class WorkerTransport {
cleanupTextLayer();
}

cachedPageNumber(ref) {
if (!isRefProxy(ref)) {
return null;
}
const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;
return this.#pageRefCache.get(refStr) ?? null;
}

get loadingParams() {
const { disableAutoFetch, enableXfa } = this._params;
return shadow(this, "loadingParams", {
Expand Down
6 changes: 0 additions & 6 deletions web/interfaces.js
Expand Up @@ -106,12 +106,6 @@ class IPDFLinkService {
* @param {Object} action
*/
executeSetOCGState(action) {}

/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef(pageNum, pageRef) {}
}

/**
Expand Down
122 changes: 35 additions & 87 deletions web/pdf_link_service.js
Expand Up @@ -49,8 +49,6 @@ const LinkTarget = {
class PDFLinkService {
externalLinkEnabled = true;

#pagesRefCache = new Map();

/**
* @param {PDFLinkServiceOptions} options
*/
Expand All @@ -74,7 +72,6 @@ class PDFLinkService {
setDocument(pdfDocument, baseUrl = null) {
this.baseUrl = baseUrl;
this.pdfDocument = pdfDocument;
this.#pagesRefCache.clear();
}

setViewer(pdfViewer) {
Expand Down Expand Up @@ -131,44 +128,53 @@ class PDFLinkService {
return this.pdfDocument ? this.pdfViewer.isInPresentationMode : false;
}

#goToDestinationHelper(rawDest, namedDest = null, explicitDest) {
/**
* This method will, when available, also update the browser history.
*
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/
async goToDestination(dest) {
if (!this.pdfDocument) {
return;
}
let namedDest, explicitDest, pageNumber;
if (typeof dest === "string") {
namedDest = dest;
explicitDest = await this.pdfDocument.getDestination(dest);
} else {
namedDest = null;
explicitDest = await dest;
}
if (!Array.isArray(explicitDest)) {
console.error(
`goToDestination: "${explicitDest}" is not a valid destination array, for dest="${dest}".`
);
return;
}
// Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
const destRef = explicitDest[0];
let pageNumber;
const [destRef] = explicitDest;

if (typeof destRef === "object" && destRef !== null) {
pageNumber = this._cachedPageNumber(destRef);
if (destRef && typeof destRef === "object") {
pageNumber = this.pdfDocument.cachedPageNumber(destRef);

if (!pageNumber) {
// Fetch the page reference if it's not yet available. This could
// only occur during loading, before all pages have been resolved.
this.pdfDocument
.getPageIndex(destRef)
.then(pageIndex => {
this.cachePageRef(pageIndex + 1, destRef);
this.#goToDestinationHelper(rawDest, namedDest, explicitDest);
})
.catch(() => {
console.error(
`PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
`a valid page reference, for dest="${rawDest}".`
);
});
return;
try {
pageNumber = (await this.pdfDocument.getPageIndex(destRef)) + 1;
} catch {
console.error(
`goToDestination: "${destRef}" is not a valid page reference, for dest="${dest}".`
);
return;
}
}
} else if (Number.isInteger(destRef)) {
pageNumber = destRef + 1;
} else {
console.error(
`PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
`a valid destination reference, for dest="${rawDest}".`
);
return;
}
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
console.error(
`PDFLinkService.#goToDestinationHelper: "${pageNumber}" is not ` +
`a valid page number, for dest="${rawDest}".`
`goToDestination: "${pageNumber}" is not a valid page number, for dest="${dest}".`
);
return;
}
Expand All @@ -187,33 +193,6 @@ class PDFLinkService {
});
}

/**
* This method will, when available, also update the browser history.
*
* @param {string|Array} dest - The named, or explicit, PDF destination.
*/
async goToDestination(dest) {
if (!this.pdfDocument) {
return;
}
let namedDest, explicitDest;
if (typeof dest === "string") {
namedDest = dest;
explicitDest = await this.pdfDocument.getDestination(dest);
} else {
namedDest = null;
explicitDest = await dest;
}
if (!Array.isArray(explicitDest)) {
console.error(
`PDFLinkService.goToDestination: "${explicitDest}" is not ` +
`a valid destination array, for dest="${dest}".`
);
return;
}
this.#goToDestinationHelper(dest, namedDest, explicitDest);
}

/**
* This method will, when available, also update the browser history.
*
Expand Down Expand Up @@ -508,31 +487,6 @@ class PDFLinkService {
);
}

/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef(pageNum, pageRef) {
if (!pageRef) {
return;
}
const refStr =
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
this.#pagesRefCache.set(refStr, pageNum);
}

/**
* @ignore
*/
_cachedPageNumber(pageRef) {
if (!pageRef) {
return null;
}
const refStr =
pageRef.gen === 0 ? `${pageRef.num}R` : `${pageRef.num}R${pageRef.gen}`;
return this.#pagesRefCache.get(refStr) || null;
}

static #isValidExplicitDest(dest) {
if (!Array.isArray(dest) || dest.length < 2) {
return false;
Expand Down Expand Up @@ -592,12 +546,6 @@ class PDFLinkService {
*/
class SimpleLinkService extends PDFLinkService {
setDocument(pdfDocument, baseUrl = null) {}

/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
*/
cachePageRef(pageNum, pageRef) {}
}

export { LinkTarget, PDFLinkService, SimpleLinkService };
19 changes: 4 additions & 15 deletions web/pdf_outline_viewer.js
Expand Up @@ -325,21 +325,10 @@ class PDFOutlineViewer extends BaseTreeViewer {
if (Array.isArray(explicitDest)) {
const [destRef] = explicitDest;

if (typeof destRef === "object" && destRef !== null) {
pageNumber = this.linkService._cachedPageNumber(destRef);

if (!pageNumber) {
try {
pageNumber = (await pdfDocument.getPageIndex(destRef)) + 1;

if (pdfDocument !== this._pdfDocument) {
return null; // The document was closed while the data resolved.
}
this.linkService.cachePageRef(pageNumber, destRef);
} catch {
// Invalid page reference, ignore it and continue parsing.
}
}
if (destRef && typeof destRef === "object") {
// The page reference must be available, since the current method
// won't be invoked until all pages have been loaded.
pageNumber = pdfDocument.cachedPageNumber(destRef);
} else if (Number.isInteger(destRef)) {
pageNumber = destRef + 1;
}
Expand Down
10 changes: 1 addition & 9 deletions web/pdf_viewer.js
Expand Up @@ -935,11 +935,7 @@ class PDFViewer {
// Set the first `pdfPage` immediately, since it's already loaded,
// rather than having to repeat the `PDFDocumentProxy.getPage` call in
// the `this.#ensurePdfPageLoaded` method before rendering can start.
const firstPageView = this._pages[0];
if (firstPageView) {
firstPageView.setPdfPage(firstPdfPage);
this.linkService.cachePageRef(1, firstPdfPage.ref);
}
this._pages[0]?.setPdfPage(firstPdfPage);

if (this._scrollMode === ScrollMode.PAGE) {
// Ensure that the current page becomes visible on document load.
Expand Down Expand Up @@ -994,7 +990,6 @@ class PDFViewer {
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
this.linkService.cachePageRef(pageNum, pdfPage.ref);
if (--getPagesLeft === 0) {
this._pagesCapability.resolve();
}
Expand Down Expand Up @@ -1718,9 +1713,6 @@ class PDFViewer {
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
if (!this.linkService._cachedPageNumber?.(pdfPage.ref)) {
this.linkService.cachePageRef(pageView.id, pdfPage.ref);
}
return pdfPage;
} catch (reason) {
console.error("Unable to get page for page view", reason);
Expand Down