From 68332ec23670c020c8aade17d24256b86ccd8a6d Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 5 Sep 2024 17:05:22 +0200 Subject: [PATCH] Avoid to have a white line around the canvas The canvas must have the same dims as the page in order to avoid to see the page background. --- src/display/display_utils.js | 8 +++- test/integration/viewer_spec.mjs | 74 +++++++++++++++++++++++++++++++ test/pdfs/.gitignore | 1 + test/pdfs/issue18694.pdf | Bin 0 -> 7531 bytes test/unit/ui_utils_spec.js | 14 ++++++ web/pdf_page_view.js | 31 ++++++++++--- web/pdf_viewer.css | 5 +++ web/ui_utils.js | 20 +++++++++ 8 files changed, 146 insertions(+), 7 deletions(-) create mode 100755 test/pdfs/issue18694.pdf diff --git a/src/display/display_utils.js b/src/display/display_utils.js index 31fd390234be9..d8694e1542a29 100644 --- a/src/display/display_utils.js +++ b/src/display/display_utils.js @@ -1103,8 +1103,12 @@ function setLayerDimensions( const w = `var(--scale-factor) * ${pageWidth}px`, h = `var(--scale-factor) * ${pageHeight}px`; - const widthStr = useRound ? `round(${w}, 1px)` : `calc(${w})`, - heightStr = useRound ? `round(${h}, 1px)` : `calc(${h})`; + const widthStr = useRound + ? `round(down, ${w}, var(--scale-round-x, 1px))` + : `calc(${w})`, + heightStr = useRound + ? `round(down, ${h}, var(--scale-round-y, 1px))` + : `calc(${h})`; if (!mustFlip || viewport.rotation % 180 === 0) { style.width = widthStr; diff --git a/test/integration/viewer_spec.mjs b/test/integration/viewer_spec.mjs index 61b4f071d08dd..4d14649d28bc8 100644 --- a/test/integration/viewer_spec.mjs +++ b/test/integration/viewer_spec.mjs @@ -19,7 +19,10 @@ import { createPromise, getSpanRectFromText, loadAndWait, + scrollIntoView, + waitForPageRendered, } from "./test_utils.mjs"; +import { PNG } from "pngjs"; describe("PDF viewer", () => { describe("Zoom origin", () => { @@ -365,4 +368,75 @@ describe("PDF viewer", () => { }); }); }); + + describe("Canvas fits the page", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "issue18694.pdf", + ".textLayer .endOfContent", + "page-width" + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that canvas perfectly fits the page whatever the zoom level is", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const debug = false; + + // The pdf has a single page with a red background. + // We set the viewer background to red, because when screenshoting + // some part of the viewer background can be visible. + // But here we don't care about the viewer background: we only + // care about the page background and the canvas default color. + + await page.evaluate(() => { + document.body.style.background = "#ff0000"; + const toolbar = document.querySelector(".toolbar"); + toolbar.style.display = "none"; + }); + await page.waitForSelector(".toolbar", { visible: false }); + await page.evaluate(() => { + const p = document.querySelector(`.page[data-page-number="1"]`); + p.style.border = "none"; + }); + + for (let i = 0; ; i++) { + const handle = await waitForPageRendered(page); + await page.evaluate(() => window.PDFViewerApplication.zoomOut()); + await awaitPromise(handle); + await scrollIntoView(page, `.page[data-page-number="1"]`); + + const element = await page.$(`.page[data-page-number="1"]`); + const png = await element.screenshot({ + type: "png", + path: debug ? `foo${i}.png` : "", + }); + const pageImage = PNG.sync.read(Buffer.from(png)); + let buffer = new Uint32Array(pageImage.data.buffer); + + // Search for the first red pixel. + const j = buffer.indexOf(0xff0000ff); + buffer = buffer.slice(j); + + expect(buffer.every(x => x === 0xff0000ff)) + .withContext(`In ${browserName}, in the ${i}th zoom in`) + .toBe(true); + + const currentScale = await page.evaluate( + () => window.PDFViewerApplication.pdfViewer.currentScale + ); + if (currentScale <= 0.1) { + break; + } + } + }) + ); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 0e3f5cf3c1168..2cacef5120edd 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -665,3 +665,4 @@ !highlights.pdf !highlight.pdf !bug1708040.pdf +!issue18694.pdf diff --git a/test/pdfs/issue18694.pdf b/test/pdfs/issue18694.pdf new file mode 100755 index 0000000000000000000000000000000000000000..8ed563810ca60de579c41f59dfd4ae2abf9a8480 GIT binary patch literal 7531 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 x; + } + const e = document.createElement("div"); + e.style.width = "round(down, calc(1.6666666666666665 * 792px), 1px)"; + return e.style.width === "calc(1320px)" ? Math.fround : x => x; + })(); + export { animationStarted, apiPageLayoutToViewerModes, @@ -870,6 +889,7 @@ export { AutoPrintRegExp, backtrackBeforeAllVisibleElements, // only exported for testing binarySearchFirstItem, + calcRound, CursorTool, DEFAULT_SCALE, DEFAULT_SCALE_DELTA,