mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-19 22:58:07 +02:00
Merge pull request #19164 from calixteman/issue18622
When zooming replace the css-zoomed canvas by the new one only when rendering is finished
This commit is contained in:
commit
e04b62ba2d
3 changed files with 90 additions and 96 deletions
|
@ -276,9 +276,20 @@ describe("PDF viewer", () => {
|
|||
beforeEach(async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await page.evaluate(() => {
|
||||
window.PDFViewerApplication.pdfViewer.currentScale = 0.5;
|
||||
});
|
||||
const handle = await waitForPageRendered(page);
|
||||
if (
|
||||
await page.evaluate(() => {
|
||||
if (
|
||||
window.PDFViewerApplication.pdfViewer.currentScale !== 0.5
|
||||
) {
|
||||
window.PDFViewerApplication.pdfViewer.currentScale = 0.5;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
) {
|
||||
await awaitPromise(handle);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -317,12 +328,14 @@ describe("PDF viewer", () => {
|
|||
const originalCanvasSize = await getCanvasSize(page);
|
||||
const factor = 2;
|
||||
|
||||
const handle = await waitForPageRendered(page);
|
||||
await page.evaluate(scaleFactor => {
|
||||
window.PDFViewerApplication.pdfViewer.increaseScale({
|
||||
drawingDelay: 0,
|
||||
scaleFactor,
|
||||
});
|
||||
}, factor);
|
||||
await awaitPromise(handle);
|
||||
|
||||
const canvasSize = await getCanvasSize(page);
|
||||
|
||||
|
@ -343,12 +356,14 @@ describe("PDF viewer", () => {
|
|||
const originalCanvasSize = await getCanvasSize(page);
|
||||
const factor = 4;
|
||||
|
||||
const handle = await waitForPageRendered(page);
|
||||
await page.evaluate(scaleFactor => {
|
||||
window.PDFViewerApplication.pdfViewer.increaseScale({
|
||||
drawingDelay: 0,
|
||||
scaleFactor,
|
||||
});
|
||||
}, factor);
|
||||
await awaitPromise(handle);
|
||||
|
||||
const canvasSize = await getCanvasSize(page);
|
||||
|
||||
|
|
|
@ -116,6 +116,8 @@ const LAYERS_ORDER = new Map([
|
|||
class PDFPageView {
|
||||
#annotationMode = AnnotationMode.ENABLE_FORMS;
|
||||
|
||||
#canvasWrapper = null;
|
||||
|
||||
#enableHWA = false;
|
||||
|
||||
#hasRestrictedScaling = false;
|
||||
|
@ -126,6 +128,8 @@ class PDFPageView {
|
|||
|
||||
#loadingId = null;
|
||||
|
||||
#originalViewport = null;
|
||||
|
||||
#previousRotation = null;
|
||||
|
||||
#scaleRoundX = 1;
|
||||
|
@ -144,8 +148,6 @@ class PDFPageView {
|
|||
regularAnnotations: true,
|
||||
};
|
||||
|
||||
#viewportMap = new WeakMap();
|
||||
|
||||
#layers = [null, null, null, null];
|
||||
|
||||
/**
|
||||
|
@ -195,7 +197,6 @@ class PDFPageView {
|
|||
this.annotationLayer = null;
|
||||
this.annotationEditorLayer = null;
|
||||
this.textLayer = null;
|
||||
this.zoomLayer = null;
|
||||
this.xfaLayer = null;
|
||||
this.structTreeLayer = null;
|
||||
this.drawLayer = null;
|
||||
|
@ -508,33 +509,23 @@ class PDFPageView {
|
|||
this._textHighlighter.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_resetZoomLayer(removeFromDOM = false) {
|
||||
if (!this.zoomLayer) {
|
||||
#resetCanvas() {
|
||||
const { canvas } = this;
|
||||
if (!canvas) {
|
||||
return;
|
||||
}
|
||||
const zoomLayerCanvas = this.zoomLayer.firstChild;
|
||||
this.#viewportMap.delete(zoomLayerCanvas);
|
||||
// Zeroing the width and height causes Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
zoomLayerCanvas.width = 0;
|
||||
zoomLayerCanvas.height = 0;
|
||||
|
||||
if (removeFromDOM) {
|
||||
// Note: `ChildNode.remove` doesn't throw if the parent node is undefined.
|
||||
this.zoomLayer.remove();
|
||||
}
|
||||
this.zoomLayer = null;
|
||||
canvas.remove();
|
||||
canvas.width = canvas.height = 0;
|
||||
this.canvas = null;
|
||||
this.#originalViewport = null;
|
||||
}
|
||||
|
||||
reset({
|
||||
keepZoomLayer = false,
|
||||
keepAnnotationLayer = false,
|
||||
keepAnnotationEditorLayer = false,
|
||||
keepXfaLayer = false,
|
||||
keepTextLayer = false,
|
||||
keepCanvasWrapper = false,
|
||||
} = {}) {
|
||||
this.cancelRendering({
|
||||
keepAnnotationLayer,
|
||||
|
@ -547,21 +538,21 @@ class PDFPageView {
|
|||
const div = this.div;
|
||||
|
||||
const childNodes = div.childNodes,
|
||||
zoomLayerNode = (keepZoomLayer && this.zoomLayer) || null,
|
||||
annotationLayerNode =
|
||||
(keepAnnotationLayer && this.annotationLayer?.div) || null,
|
||||
annotationEditorLayerNode =
|
||||
(keepAnnotationEditorLayer && this.annotationEditorLayer?.div) || null,
|
||||
xfaLayerNode = (keepXfaLayer && this.xfaLayer?.div) || null,
|
||||
textLayerNode = (keepTextLayer && this.textLayer?.div) || null;
|
||||
textLayerNode = (keepTextLayer && this.textLayer?.div) || null,
|
||||
canvasWrapperNode = (keepCanvasWrapper && this.#canvasWrapper) || null;
|
||||
for (let i = childNodes.length - 1; i >= 0; i--) {
|
||||
const node = childNodes[i];
|
||||
switch (node) {
|
||||
case zoomLayerNode:
|
||||
case annotationLayerNode:
|
||||
case annotationEditorLayerNode:
|
||||
case xfaLayerNode:
|
||||
case textLayerNode:
|
||||
case canvasWrapperNode:
|
||||
continue;
|
||||
}
|
||||
node.remove();
|
||||
|
@ -590,16 +581,9 @@ class PDFPageView {
|
|||
}
|
||||
this.structTreeLayer?.hide();
|
||||
|
||||
if (!zoomLayerNode) {
|
||||
if (this.canvas) {
|
||||
this.#viewportMap.delete(this.canvas);
|
||||
// Zeroing the width and height causes Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
this.canvas.width = 0;
|
||||
this.canvas.height = 0;
|
||||
delete this.canvas;
|
||||
}
|
||||
this._resetZoomLayer();
|
||||
if (!keepCanvasWrapper && this.#canvasWrapper) {
|
||||
this.#canvasWrapper = null;
|
||||
this.#resetCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -609,11 +593,11 @@ class PDFPageView {
|
|||
}
|
||||
this.#isEditing = isEditing;
|
||||
this.reset({
|
||||
keepZoomLayer: true,
|
||||
keepAnnotationLayer: true,
|
||||
keepAnnotationEditorLayer: true,
|
||||
keepXfaLayer: true,
|
||||
keepTextLayer: true,
|
||||
keepCanvasWrapper: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -697,7 +681,6 @@ class PDFPageView {
|
|||
this.renderingState !== RenderingStates.FINISHED
|
||||
) {
|
||||
this.cancelRendering({
|
||||
keepZoomLayer: true,
|
||||
keepAnnotationLayer: true,
|
||||
keepAnnotationEditorLayer: true,
|
||||
keepXfaLayer: true,
|
||||
|
@ -715,7 +698,6 @@ class PDFPageView {
|
|||
}
|
||||
|
||||
this.cssTransform({
|
||||
target: this.canvas,
|
||||
redrawAnnotationLayer: true,
|
||||
redrawAnnotationEditorLayer: true,
|
||||
redrawXfaLayer: true,
|
||||
|
@ -737,20 +719,14 @@ class PDFPageView {
|
|||
});
|
||||
return;
|
||||
}
|
||||
if (!this.zoomLayer && !this.canvas.hidden) {
|
||||
this.zoomLayer = this.canvas.parentNode;
|
||||
this.zoomLayer.style.position = "absolute";
|
||||
}
|
||||
}
|
||||
if (this.zoomLayer) {
|
||||
this.cssTransform({ target: this.zoomLayer.firstChild });
|
||||
}
|
||||
this.cssTransform({});
|
||||
this.reset({
|
||||
keepZoomLayer: true,
|
||||
keepAnnotationLayer: true,
|
||||
keepAnnotationEditorLayer: true,
|
||||
keepXfaLayer: true,
|
||||
keepTextLayer: true,
|
||||
keepCanvasWrapper: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -805,41 +781,32 @@ class PDFPageView {
|
|||
}
|
||||
|
||||
cssTransform({
|
||||
target,
|
||||
redrawAnnotationLayer = false,
|
||||
redrawAnnotationEditorLayer = false,
|
||||
redrawXfaLayer = false,
|
||||
redrawTextLayer = false,
|
||||
hideTextLayer = false,
|
||||
}) {
|
||||
// Scale target (canvas), its wrapper and page container.
|
||||
if (
|
||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
||||
!(target instanceof HTMLCanvasElement)
|
||||
) {
|
||||
throw new Error("Expected `target` to be a canvas.");
|
||||
}
|
||||
if (!target.hasAttribute("zooming")) {
|
||||
target.setAttribute("zooming", true);
|
||||
const { style } = target;
|
||||
style.width = style.height = "";
|
||||
const { canvas } = this;
|
||||
if (!canvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalViewport = this.#viewportMap.get(target);
|
||||
const originalViewport = this.#originalViewport;
|
||||
if (this.viewport !== originalViewport) {
|
||||
// The canvas may have been originally rotated; rotate relative to that.
|
||||
const relativeRotation =
|
||||
this.viewport.rotation - originalViewport.rotation;
|
||||
const absRotation = Math.abs(relativeRotation);
|
||||
let scaleX = 1,
|
||||
scaleY = 1;
|
||||
if (absRotation === 90 || absRotation === 270) {
|
||||
(360 + this.viewport.rotation - originalViewport.rotation) % 360;
|
||||
if (relativeRotation === 90 || relativeRotation === 270) {
|
||||
const { width, height } = this.viewport;
|
||||
// Scale x and y because of the rotation.
|
||||
scaleX = height / width;
|
||||
scaleY = width / height;
|
||||
const scaleX = height / width;
|
||||
const scaleY = width / height;
|
||||
canvas.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX},${scaleY})`;
|
||||
} else {
|
||||
canvas.style.transform =
|
||||
relativeRotation === 0 ? "" : `rotate(${relativeRotation}deg)`;
|
||||
}
|
||||
target.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX}, ${scaleY})`;
|
||||
}
|
||||
|
||||
if (redrawAnnotationLayer && this.annotationLayer) {
|
||||
|
@ -892,7 +859,6 @@ class PDFPageView {
|
|||
this.#renderError = error;
|
||||
|
||||
this.renderingState = RenderingStates.FINISHED;
|
||||
this._resetZoomLayer(/* removeFromDOM = */ true);
|
||||
|
||||
// Ensure that the thumbnails won't become partially (or fully) blank,
|
||||
// for documents that contain interactive form elements.
|
||||
|
@ -927,9 +893,12 @@ class PDFPageView {
|
|||
|
||||
// Wrap the canvas so that if it has a CSS transform for high DPI the
|
||||
// overflow will be hidden in Firefox.
|
||||
const canvasWrapper = document.createElement("div");
|
||||
canvasWrapper.classList.add("canvasWrapper");
|
||||
this.#addLayer(canvasWrapper, "canvasWrapper");
|
||||
let canvasWrapper = this.#canvasWrapper;
|
||||
if (!canvasWrapper) {
|
||||
canvasWrapper = this.#canvasWrapper = document.createElement("div");
|
||||
canvasWrapper.classList.add("canvasWrapper");
|
||||
this.#addLayer(canvasWrapper, "canvasWrapper");
|
||||
}
|
||||
|
||||
if (
|
||||
!this.textLayer &&
|
||||
|
@ -1004,22 +973,37 @@ class PDFPageView {
|
|||
const canvas = document.createElement("canvas");
|
||||
canvas.setAttribute("role", "presentation");
|
||||
|
||||
// Keep the canvas hidden until the first draw callback, or until drawing
|
||||
// is complete when `!this.renderingQueue`, to prevent black flickering.
|
||||
canvas.hidden = true;
|
||||
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
|
||||
const prevCanvas = this.canvas;
|
||||
const updateOnFirstShow = !prevCanvas && !hasHCM;
|
||||
this.canvas = canvas;
|
||||
this.#originalViewport = viewport;
|
||||
|
||||
let showCanvas = isLastShow => {
|
||||
// In HCM, a final filter is applied on the canvas which means that
|
||||
// before it's applied we've normal colors. Consequently, to avoid to have
|
||||
// a final flash we just display it once all the drawing is done.
|
||||
if (!hasHCM || isLastShow) {
|
||||
canvas.hidden = false;
|
||||
showCanvas = null; // Only invoke the function once.
|
||||
if (updateOnFirstShow) {
|
||||
// Don't add the canvas until the first draw callback, or until
|
||||
// drawing is complete when `!this.renderingQueue`, to prevent black
|
||||
// flickering.
|
||||
canvasWrapper.append(canvas);
|
||||
showCanvas = null;
|
||||
return;
|
||||
}
|
||||
if (!isLastShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevCanvas) {
|
||||
prevCanvas.replaceWith(canvas);
|
||||
prevCanvas.width = prevCanvas.height = 0;
|
||||
} else {
|
||||
// In HCM, a final filter is applied on the canvas which means that
|
||||
// before it's applied we've normal colors. Consequently, to avoid to
|
||||
// have a final flash we just display it once all the drawing is done.
|
||||
canvasWrapper.append(canvas);
|
||||
}
|
||||
|
||||
showCanvas = null;
|
||||
};
|
||||
canvasWrapper.append(canvas);
|
||||
this.canvas = canvas;
|
||||
|
||||
const ctx = canvas.getContext("2d", {
|
||||
alpha: false,
|
||||
|
@ -1073,9 +1057,6 @@ class PDFPageView {
|
|||
this.#scaleRoundY = sfy[1];
|
||||
}
|
||||
|
||||
// Add the viewport so it's known what it was originally drawn with.
|
||||
this.#viewportMap.set(canvas, viewport);
|
||||
|
||||
// Rendering area
|
||||
const transform = outputScale.scaled
|
||||
? [outputScale.sx, 0, 0, outputScale.sy, 0, 0]
|
||||
|
@ -1141,6 +1122,9 @@ class PDFPageView {
|
|||
// callback had been invoked at least once.
|
||||
if (!(error instanceof RenderingCancelledException)) {
|
||||
showCanvas?.(true);
|
||||
} else {
|
||||
prevCanvas?.remove();
|
||||
this.#resetCanvas();
|
||||
}
|
||||
return this.#finishRenderTask(renderTask, error);
|
||||
}
|
||||
|
|
|
@ -83,19 +83,14 @@
|
|||
height: 100%;
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&[zooming] {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
contain: content;
|
||||
|
||||
.structTree {
|
||||
contain: strict;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue