diff --git a/test/integration/viewer_spec.mjs b/test/integration/viewer_spec.mjs index f2d3447eb..a01478aeb 100644 --- a/test/integration/viewer_spec.mjs +++ b/test/integration/viewer_spec.mjs @@ -17,6 +17,7 @@ import { awaitPromise, closePages, createPromise, + getRect, getSpanRectFromText, loadAndWait, scrollIntoView, @@ -1416,4 +1417,49 @@ describe("PDF viewer", () => { ); }); }); + + describe("Scroll into view", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "tracemonkey_annotation_on_page_8.pdf", + `.page[data-page-number = "1"] .endOfContent` + ); + }); + + it("Check that the top right corner of the annotation is centered vertically", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const handle = await page.evaluateHandle(() => [ + new Promise(resolve => { + const container = document.getElementById("viewerContainer"); + container.addEventListener("scrollend", resolve, { + once: true, + }); + window.PDFViewerApplication.pdfLinkService.goToXY( + 8, + 43.55, + 198.36, + { + center: "vertical", + } + ); + }), + ]); + await awaitPromise(handle); + const annotationSelector = + ".page[data-page-number='8'] .stampAnnotation"; + await page.waitForSelector(annotationSelector, { visible: true }); + const rect = await getRect(page, annotationSelector); + const containerRect = await getRect(page, "#viewerContainer"); + expect( + Math.abs(2 * (rect.y - containerRect.y) - containerRect.height) + ) + .withContext(`In ${browserName}`) + .toBeLessThan(1); + }) + ); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 59455a89d..4ba07c109 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -742,3 +742,4 @@ !tracemonkey_with_annotations.pdf !tracemonkey_with_editable_annotations.pdf !bug1980958.pdf +!tracemonkey_annotation_on_page_8.pdf diff --git a/test/pdfs/tracemonkey_annotation_on_page_8.pdf b/test/pdfs/tracemonkey_annotation_on_page_8.pdf new file mode 100755 index 000000000..3684386a5 Binary files /dev/null and b/test/pdfs/tracemonkey_annotation_on_page_8.pdf differ diff --git a/web/pdf_link_service.js b/web/pdf_link_service.js index 9f46a0475..885de2bc8 100644 --- a/web/pdf_link_service.js +++ b/web/pdf_link_service.js @@ -244,12 +244,14 @@ class PDFLinkService { * @param {number} pageNumber - The page number to scroll to. * @param {number} x - The x-coordinate to scroll to in page coordinates. * @param {number} y - The y-coordinate to scroll to in page coordinates. + * @param {Object} [options] */ - goToXY(pageNumber, x, y) { + goToXY(pageNumber, x, y, options = {}) { this.pdfViewer.scrollPageIntoView({ pageNumber, destArray: [null, { name: "XYZ" }, x, y], ignoreDestinationZoom: true, + ...options, }); } diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 424180447..07c476459 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -1554,6 +1554,9 @@ class PDFViewer { * The default value is `false`. * @property {boolean} [ignoreDestinationZoom] - Ignore the zoom argument in * the destination array. The default value is `false`. + * @property {string} [center] - Center the view on the specified coordinates. + * The default value is `null`. Possible values are: `null` (don't center), + * `horizontal`, `vertical` and `both`. */ /** @@ -1565,6 +1568,7 @@ class PDFViewer { destArray = null, allowNegativeOffset = false, ignoreDestinationZoom = false, + center = null, }) { if (!this.pdfDocument) { return; @@ -1687,7 +1691,20 @@ class PDFViewer { let left = Math.min(boundingRect[0][0], boundingRect[1][0]); let top = Math.min(boundingRect[0][1], boundingRect[1][1]); - if (!allowNegativeOffset) { + if (center) { + if (center === "both" || center === "vertical") { + top -= + (this.container.clientHeight - + Math.abs(boundingRect[1][1] - boundingRect[0][1])) / + 2; + } + if (center === "both" || center === "horizontal") { + left -= + (this.container.clientWidth - + Math.abs(boundingRect[1][0] - boundingRect[0][0])) / + 2; + } + } else if (!allowNegativeOffset) { // Some bad PDF generators will create destinations with e.g. top values // that exceeds the page height. Ensure that offsets are not negative, // to prevent a previous page from becoming visible (fixes bug 874482).