mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-07 17:05:38 +02:00
368 lines
11 KiB
JavaScript
368 lines
11 KiB
JavaScript
/* Copyright 2024 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import {
|
|
awaitPromise,
|
|
closePages,
|
|
createPromise,
|
|
getSpanRectFromText,
|
|
loadAndWait,
|
|
} from "./test_utils.mjs";
|
|
|
|
describe("PDF viewer", () => {
|
|
describe("Zoom origin", () => {
|
|
let pages;
|
|
|
|
beforeAll(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
".textLayer .endOfContent",
|
|
"page-width",
|
|
null,
|
|
{ page: 2 }
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
async function getTextAt(page, pageNumber, coordX, coordY) {
|
|
await page.waitForFunction(
|
|
pageNum =>
|
|
!document.querySelector(
|
|
`.page[data-page-number="${pageNum}"] > .textLayer`
|
|
).hidden,
|
|
{},
|
|
pageNumber
|
|
);
|
|
return page.evaluate(
|
|
(x, y) => document.elementFromPoint(x, y)?.textContent,
|
|
coordX,
|
|
coordY
|
|
);
|
|
}
|
|
|
|
it("supports specifiying a custom origin", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
// We use this text span of page 2 because:
|
|
// - it's in the visible area even when zooming at page-width
|
|
// - it's small, so it easily catches if the page moves too much
|
|
// - it's in a "random" position: not near the center of the
|
|
// viewport, and not near the borders
|
|
const text = "guards";
|
|
|
|
const rect = await getSpanRectFromText(page, 2, text);
|
|
const originX = rect.x + rect.width / 2;
|
|
const originY = rect.y + rect.height / 2;
|
|
|
|
await page.evaluate(
|
|
origin => {
|
|
window.PDFViewerApplication.pdfViewer.increaseScale({
|
|
scaleFactor: 2,
|
|
origin,
|
|
});
|
|
},
|
|
[originX, originY]
|
|
);
|
|
const textAfterZoomIn = await getTextAt(page, 2, originX, originY);
|
|
expect(textAfterZoomIn)
|
|
.withContext(`In ${browserName}, zoom in`)
|
|
.toBe(text);
|
|
|
|
await page.evaluate(
|
|
origin => {
|
|
window.PDFViewerApplication.pdfViewer.decreaseScale({
|
|
scaleFactor: 0.8,
|
|
origin,
|
|
});
|
|
},
|
|
[originX, originY]
|
|
);
|
|
const textAfterZoomOut = await getTextAt(page, 2, originX, originY);
|
|
expect(textAfterZoomOut)
|
|
.withContext(`In ${browserName}, zoom out`)
|
|
.toBe(text);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Zoom with the mouse wheel", () => {
|
|
let pages;
|
|
|
|
beforeAll(async () => {
|
|
pages = await loadAndWait("empty.pdf", ".textLayer .endOfContent", 1000);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("must check that we can zoom with the mouse wheel and pressed control key", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await page.keyboard.down("Control");
|
|
let zoom = 10;
|
|
const zoomGetter = () =>
|
|
page.evaluate(
|
|
() => window.PDFViewerApplication.pdfViewer.currentScale
|
|
);
|
|
while (zoom > 0.1) {
|
|
await page.mouse.wheel({ deltaY: 100 });
|
|
zoom = await zoomGetter();
|
|
}
|
|
while (zoom < 10) {
|
|
await page.mouse.wheel({ deltaY: -100 });
|
|
zoom = await zoomGetter();
|
|
}
|
|
await page.keyboard.up("Control");
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Zoom commands", () => {
|
|
let pages;
|
|
|
|
beforeAll(async () => {
|
|
pages = await loadAndWait("tracemonkey.pdf", ".textLayer .endOfContent");
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("must check that zoom commands don't scroll the document", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
for (let i = 0; i < 10; i++) {
|
|
await page.evaluate(() => window.PDFViewerApplication.zoomIn());
|
|
await page.evaluate(() => window.PDFViewerApplication.zoomReset());
|
|
await page.waitForSelector(
|
|
`.page[data-page-number="1"] .textLayer .endOfContent`
|
|
);
|
|
const scrollTop = await page.evaluate(
|
|
() => document.getElementById("viewerContainer").scrollTop
|
|
);
|
|
expect(scrollTop < 100)
|
|
.withContext(`In ${browserName}`)
|
|
.toBe(true);
|
|
}
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("CSS-only zoom", () => {
|
|
function createPromiseForFirstPageRendered(page) {
|
|
return createPromise(page, (resolve, reject) => {
|
|
const controller = new AbortController();
|
|
window.PDFViewerApplication.eventBus.on(
|
|
"pagerendered",
|
|
({ pageNumber, timestamp }) => {
|
|
if (pageNumber === 1) {
|
|
resolve(timestamp);
|
|
controller.abort();
|
|
}
|
|
},
|
|
{ signal: controller.signal }
|
|
);
|
|
setTimeout(reject, 1000, new Error("Timeout"));
|
|
});
|
|
}
|
|
|
|
describe("forced (maxCanvasPixels: 0)", () => {
|
|
let pages;
|
|
|
|
beforeAll(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
".textLayer .endOfContent",
|
|
null,
|
|
null,
|
|
{ maxCanvasPixels: 0 }
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("respects drawing delay when zooming out", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
const promise = await createPromiseForFirstPageRendered(page);
|
|
|
|
const start = await page.evaluate(() => {
|
|
const startTime = performance.now();
|
|
window.PDFViewerApplication.pdfViewer.decreaseScale({
|
|
drawingDelay: 100,
|
|
scaleFactor: 0.9,
|
|
});
|
|
return startTime;
|
|
});
|
|
|
|
const end = await awaitPromise(promise);
|
|
|
|
expect(end - start)
|
|
.withContext(`In ${browserName}`)
|
|
.toBeGreaterThanOrEqual(100);
|
|
})
|
|
);
|
|
});
|
|
|
|
it("respects drawing delay when zooming in", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
const promise = await createPromiseForFirstPageRendered(page);
|
|
|
|
const start = await page.evaluate(() => {
|
|
const startTime = performance.now();
|
|
window.PDFViewerApplication.pdfViewer.increaseScale({
|
|
drawingDelay: 100,
|
|
scaleFactor: 1.1,
|
|
});
|
|
return startTime;
|
|
});
|
|
|
|
const end = await awaitPromise(promise);
|
|
|
|
expect(end - start)
|
|
.withContext(`In ${browserName}`)
|
|
.toBeGreaterThanOrEqual(100);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("triggers when going bigger than maxCanvasPixels", () => {
|
|
let pages;
|
|
|
|
const MAX_CANVAS_PIXELS = new Map();
|
|
|
|
beforeAll(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
".textLayer .endOfContent",
|
|
null,
|
|
null,
|
|
async (page, browserName) => {
|
|
const ratio = await page.evaluate(() => window.devicePixelRatio);
|
|
const maxCanvasPixels = 1_000_000 * ratio ** 2;
|
|
MAX_CANVAS_PIXELS.set(browserName, maxCanvasPixels);
|
|
|
|
return { maxCanvasPixels };
|
|
}
|
|
);
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await page.evaluate(() => {
|
|
window.PDFViewerApplication.pdfViewer.currentScale = 0.5;
|
|
});
|
|
})
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
function getCanvasSize(page) {
|
|
return page.evaluate(() => {
|
|
const canvas = window.document.querySelector(".canvasWrapper canvas");
|
|
return canvas.width * canvas.height;
|
|
});
|
|
}
|
|
|
|
// MAX_CANVAS_PIXELS must be big enough that the originally rendered
|
|
// canvas still has enough space to grow before triggering CSS-only zoom
|
|
it("test correctly configured", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
const canvasSize = await getCanvasSize(page);
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}`)
|
|
.toBeLessThan(MAX_CANVAS_PIXELS.get(browserName) / 4);
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}`)
|
|
.toBeGreaterThan(MAX_CANVAS_PIXELS.get(browserName) / 16);
|
|
})
|
|
);
|
|
});
|
|
|
|
it("does not trigger CSS-only zoom below maxCanvasPixels", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
const originalCanvasSize = await getCanvasSize(page);
|
|
const factor = 2;
|
|
|
|
await page.evaluate(scaleFactor => {
|
|
window.PDFViewerApplication.pdfViewer.increaseScale({
|
|
drawingDelay: 0,
|
|
scaleFactor,
|
|
});
|
|
}, factor);
|
|
|
|
const canvasSize = await getCanvasSize(page);
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}`)
|
|
.toBe(originalCanvasSize * factor ** 2);
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}, MAX_CANVAS_PIXELS`)
|
|
.toBeLessThan(MAX_CANVAS_PIXELS.get(browserName));
|
|
})
|
|
);
|
|
});
|
|
|
|
it("triggers CSS-only zoom above maxCanvasPixels", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
const originalCanvasSize = await getCanvasSize(page);
|
|
const factor = 4;
|
|
|
|
await page.evaluate(scaleFactor => {
|
|
window.PDFViewerApplication.pdfViewer.increaseScale({
|
|
drawingDelay: 0,
|
|
scaleFactor,
|
|
});
|
|
}, factor);
|
|
|
|
const canvasSize = await getCanvasSize(page);
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}`)
|
|
.toBeLessThan(originalCanvasSize * factor ** 2);
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}, <= MAX_CANVAS_PIXELS`)
|
|
.toBeLessThanOrEqual(MAX_CANVAS_PIXELS.get(browserName));
|
|
|
|
expect(canvasSize)
|
|
.withContext(`In ${browserName}, > MAX_CANVAS_PIXELS * 0.99`)
|
|
.toBeGreaterThan(MAX_CANVAS_PIXELS.get(browserName) * 0.99);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|