Add support for "GoToE" actions with destinations (issue 17056)
This shouldn't be very common in practice, since "GoToE" actions themselves seem quite uncommon; see PR 15537.
This commit is contained in:
parent
da4fdc76a3
commit
bf9c33e60f
@ -59,6 +59,21 @@ function fetchDestination(dest) {
|
|||||||
return Array.isArray(dest) ? dest : null;
|
return Array.isArray(dest) ? dest : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchRemoteDest(action) {
|
||||||
|
let dest = action.get("D");
|
||||||
|
if (dest) {
|
||||||
|
if (dest instanceof Name) {
|
||||||
|
dest = dest.name;
|
||||||
|
}
|
||||||
|
if (typeof dest === "string") {
|
||||||
|
return stringToPDFString(dest);
|
||||||
|
} else if (Array.isArray(dest)) {
|
||||||
|
return JSON.stringify(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
class Catalog {
|
class Catalog {
|
||||||
constructor(pdfManager, xref) {
|
constructor(pdfManager, xref) {
|
||||||
this.pdfManager = pdfManager;
|
this.pdfManager = pdfManager;
|
||||||
@ -1514,19 +1529,9 @@ class Catalog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: the destination is relative to the *remote* document.
|
// NOTE: the destination is relative to the *remote* document.
|
||||||
let remoteDest = action.get("D");
|
const remoteDest = fetchRemoteDest(action);
|
||||||
if (remoteDest) {
|
if (remoteDest && typeof url === "string") {
|
||||||
if (remoteDest instanceof Name) {
|
url = /* baseUrl = */ url.split("#", 1)[0] + "#" + remoteDest;
|
||||||
remoteDest = remoteDest.name;
|
|
||||||
}
|
|
||||||
if (typeof url === "string") {
|
|
||||||
const baseUrl = url.split("#")[0];
|
|
||||||
if (typeof remoteDest === "string") {
|
|
||||||
url = baseUrl + "#" + remoteDest;
|
|
||||||
} else if (Array.isArray(remoteDest)) {
|
|
||||||
url = baseUrl + "#" + JSON.stringify(remoteDest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
||||||
const newWindow = action.get("NewWindow");
|
const newWindow = action.get("NewWindow");
|
||||||
@ -1550,6 +1555,12 @@ class Catalog {
|
|||||||
|
|
||||||
if (attachment) {
|
if (attachment) {
|
||||||
resultObj.attachment = attachment;
|
resultObj.attachment = attachment;
|
||||||
|
|
||||||
|
// NOTE: the destination is relative to the *attachment*.
|
||||||
|
const attachmentDest = fetchRemoteDest(action);
|
||||||
|
if (attachmentDest) {
|
||||||
|
resultObj.attachmentDest = attachmentDest;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warn(`parseDestDictionary - unimplemented "GoToE" action.`);
|
warn(`parseDestDictionary - unimplemented "GoToE" action.`);
|
||||||
}
|
}
|
||||||
|
@ -709,7 +709,7 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
this._bindNamedAction(link, data.action);
|
this._bindNamedAction(link, data.action);
|
||||||
isBound = true;
|
isBound = true;
|
||||||
} else if (data.attachment) {
|
} else if (data.attachment) {
|
||||||
this._bindAttachment(link, data.attachment);
|
this.#bindAttachment(link, data.attachment, data.attachmentDest);
|
||||||
isBound = true;
|
isBound = true;
|
||||||
} else if (data.setOCGState) {
|
} else if (data.setOCGState) {
|
||||||
this.#bindSetOCGState(link, data.setOCGState);
|
this.#bindSetOCGState(link, data.setOCGState);
|
||||||
@ -793,14 +793,16 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
* Bind attachments to the link element.
|
* Bind attachments to the link element.
|
||||||
* @param {Object} link
|
* @param {Object} link
|
||||||
* @param {Object} attachment
|
* @param {Object} attachment
|
||||||
|
* @param {str} [dest]
|
||||||
*/
|
*/
|
||||||
_bindAttachment(link, attachment) {
|
#bindAttachment(link, attachment, dest = null) {
|
||||||
link.href = this.linkService.getAnchorUrl("");
|
link.href = this.linkService.getAnchorUrl("");
|
||||||
link.onclick = () => {
|
link.onclick = () => {
|
||||||
this.downloadManager?.openOrDownloadData(
|
this.downloadManager?.openOrDownloadData(
|
||||||
this.container,
|
this.container,
|
||||||
attachment.content,
|
attachment.content,
|
||||||
attachment.filename
|
attachment.filename,
|
||||||
|
dest
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -51,6 +51,7 @@
|
|||||||
!issue7439.pdf
|
!issue7439.pdf
|
||||||
!issue7847_radial.pdf
|
!issue7847_radial.pdf
|
||||||
!issue8844.pdf
|
!issue8844.pdf
|
||||||
|
!issue17056.pdf
|
||||||
!issue14953.pdf
|
!issue14953.pdf
|
||||||
!issue15367.pdf
|
!issue15367.pdf
|
||||||
!issue15372.pdf
|
!issue15372.pdf
|
||||||
|
BIN
test/pdfs/issue17056.pdf
Normal file
BIN
test/pdfs/issue17056.pdf
Normal file
Binary file not shown.
@ -2855,6 +2855,29 @@ describe("api", function () {
|
|||||||
expect(content instanceof Uint8Array).toEqual(true);
|
expect(content instanceof Uint8Array).toEqual(true);
|
||||||
expect(content.length).toEqual(4508);
|
expect(content.length).toEqual(4508);
|
||||||
|
|
||||||
|
expect(annotations[0].attachmentDest).toEqual('[-1,{"name":"Fit"}]');
|
||||||
|
|
||||||
|
await loadingTask.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gets annotations containing GoToE action with destination (issue 17056)", async function () {
|
||||||
|
const loadingTask = getDocument(buildGetDocumentParams("issue17056.pdf"));
|
||||||
|
const pdfDoc = await loadingTask.promise;
|
||||||
|
const pdfPage = await pdfDoc.getPage(1);
|
||||||
|
|
||||||
|
const annotations = await pdfPage.getAnnotations();
|
||||||
|
expect(annotations.length).toEqual(30);
|
||||||
|
|
||||||
|
const { annotationType, attachment, attachmentDest } = annotations[0];
|
||||||
|
expect(annotationType).toEqual(AnnotationType.LINK);
|
||||||
|
|
||||||
|
const { filename, content } = attachment;
|
||||||
|
expect(filename).toEqual("destination-doc.pdf");
|
||||||
|
expect(content instanceof Uint8Array).toEqual(true);
|
||||||
|
expect(content.length).toEqual(10305);
|
||||||
|
|
||||||
|
expect(attachmentDest).toEqual('[0,{"name":"Fit"}]');
|
||||||
|
|
||||||
await loadingTask.destroy();
|
await loadingTask.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class DownloadManager {
|
|||||||
/**
|
/**
|
||||||
* @returns {boolean} Indicating if the data was opened.
|
* @returns {boolean} Indicating if the data was opened.
|
||||||
*/
|
*/
|
||||||
openOrDownloadData(element, data, filename) {
|
openOrDownloadData(element, data, filename, dest = null) {
|
||||||
const isPdfData = isPdfFile(filename);
|
const isPdfData = isPdfFile(filename);
|
||||||
const contentType = isPdfData ? "application/pdf" : "";
|
const contentType = isPdfData ? "application/pdf" : "";
|
||||||
|
|
||||||
@ -93,6 +93,9 @@ class DownloadManager {
|
|||||||
"?file=" +
|
"?file=" +
|
||||||
encodeURIComponent(blobUrl + "#" + filename);
|
encodeURIComponent(blobUrl + "#" + filename);
|
||||||
}
|
}
|
||||||
|
if (dest) {
|
||||||
|
viewerUrl += `#${escape(dest)}`;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.open(viewerUrl);
|
window.open(viewerUrl);
|
||||||
|
@ -132,7 +132,7 @@ class DownloadManager {
|
|||||||
/**
|
/**
|
||||||
* @returns {boolean} Indicating if the data was opened.
|
* @returns {boolean} Indicating if the data was opened.
|
||||||
*/
|
*/
|
||||||
openOrDownloadData(element, data, filename) {
|
openOrDownloadData(element, data, filename, dest = null) {
|
||||||
const isPdfData = isPdfFile(filename);
|
const isPdfData = isPdfFile(filename);
|
||||||
const contentType = isPdfData ? "application/pdf" : "";
|
const contentType = isPdfData ? "application/pdf" : "";
|
||||||
|
|
||||||
@ -143,7 +143,10 @@ class DownloadManager {
|
|||||||
this.#openBlobUrls.set(element, blobUrl);
|
this.#openBlobUrls.set(element, blobUrl);
|
||||||
}
|
}
|
||||||
// Let Firefox's content handler catch the URL and display the PDF.
|
// Let Firefox's content handler catch the URL and display the PDF.
|
||||||
const viewerUrl = blobUrl + "#filename=" + encodeURIComponent(filename);
|
let viewerUrl = blobUrl + "?filename=" + encodeURIComponent(filename);
|
||||||
|
if (dest) {
|
||||||
|
viewerUrl += `#${escape(dest)}`;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.open(viewerUrl);
|
window.open(viewerUrl);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user