diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index fc12f7da00b2f..0ae46621df0f0 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -56,6 +56,81 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js"; // enable the feature? const ENABLE_ZOOM_DETAIL = true; +class Recorder { + constructor(kind) { + this.kind = kind; + this.accumulatedTime = 0; + this.startTime = 0; + this.currentStartTime = 0; + this.running = false; + } + + start() { + if (this.running) { + console.warn(`Start ${this.kind} recorder while running`); + return; + } + + console.log(`Start rendering ${this.kind}`); + this.currentStartTime = this.startTime = performance.now(); + this.accumulatedTime = 0; + this.running = true; + } + + pause() { + if (!this.running) { + console.warn(`Pause ${this.kind} recorder while not running`); + return; + } + + console.log(`Pause rendering ${this.kind}`); + this.accumulatedTime += performance.now() - this.currentStartTime; + this.running = false; + } + + resume() { + if (this.running) { + console.warn(`Resume ${this.kind} recorder while running`); + return; + } + + console.log(`Resume rendering ${this.kind}`); + this.currentStartTime = performance.now(); + this.running = true; + } + + finish() { + if (!this.running) { + console.warn(`Finish ${this.kind} recorder while not running`); + return; + } + + this.accumulatedTime += performance.now() - this.currentStartTime; + const totalTime = performance.now() - this.startTime; + this.running = false; + + console.log( + `Finish renderig ${this.kind} (self: ${this.accumulatedTime}ms, total: ${totalTime}ms)` + ); + } + + cancel() { + if (this.running) { + this.accumulatedTime += performance.now() - this.currentStartTime; + this.running = false; + } + + const totalTime = performance.now() - this.startTime; + + console.log( + `Cancel renderig ${this.kind} (self: ${this.accumulatedTime}ms, total: ${totalTime}ms)` + ); + } +} + +const detailRecorder = new Recorder("foreground"); +const backgroundRecorder = new Recorder("background"); + /** * @typedef {Object} PDFPageViewOptions * @property {HTMLDivElement} [container] - The viewer element. @@ -253,8 +328,10 @@ class PDFPageViewBase { #renderContinueCallback = cont => { this.#showCanvas?.(false); if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) { + this.recorder.pause(); this.renderingState = RenderingStates.PAUSED; this.resume = () => { + this.recorder.resume(); this.renderingState = RenderingStates.RUNNING; cont(); }; @@ -327,6 +404,12 @@ class PDFPageViewBase { } cancelRendering({ cancelExtraDelay = 0 } = {}) { + if ( + this.renderingState !== RenderingStates.INITIAL && + this.renderingState !== RenderingStates.FINISHED + ) { + this.recorder.cancel(); + } if (this.renderTask) { this.renderTask.cancel(cancelExtraDelay); this.renderTask = null; @@ -349,6 +432,8 @@ class PDFPageViewBase { * @implements {IRenderableView} */ class PDFPageView extends PDFPageViewBase { + recorder = backgroundRecorder; + #annotationMode = AnnotationMode.ENABLE_FORMS; #hasRestrictedScaling = false; @@ -1038,6 +1123,8 @@ class PDFPageView extends PDFPageViewBase { } async draw() { + this.recorder.start(); + if (this.renderingState !== RenderingStates.INITIAL) { console.error("Must be in new state before drawing"); this.reset(); // Ensure that we reset all state to prevent issues. @@ -1138,6 +1225,15 @@ class PDFPageView extends PDFPageViewBase { } else { this.#hasRestrictedScaling = false; } + console.log({ + hasRestrictedScaling: this.#hasRestrictedScaling, + maxCanvasPixels: this.maxCanvasPixels.toLocaleString(), + pixelsInViewport: Math.round(pixelsInViewport).toLocaleString(), + outputScaleSx: outputScale.sx, + outputScaleSy: outputScale.sy, + maxScale: maxScale, + ratio: window.devicePixelRatio + }); } const sfx = approximateFraction(outputScale.sx); const sfy = approximateFraction(outputScale.sy); @@ -1182,6 +1278,8 @@ class PDFPageView extends PDFPageViewBase { renderContext, prevCanvas, renderTask => { + this.recorder.finish(); + // Ensure that the thumbnails won't become partially (or fully) blank, // for documents that contain interactive form elements. this.#useThumbnailCanvas.regularAnnotations = @@ -1289,6 +1387,8 @@ class PDFPageView extends PDFPageViewBase { * @implements {IRenderableView} */ class PDFPageDetailView extends PDFPageViewBase { + recorder = detailRecorder; + constructor({ pageView }) { super(pageView); @@ -1390,6 +1490,8 @@ class PDFPageDetailView extends PDFPageViewBase { } async draw() { + this.recorder.start(); + const initialRenderingState = this.renderingState; if (initialRenderingState !== RenderingStates.INITIAL) { console.error("Must be in new state before drawing"); @@ -1448,7 +1550,10 @@ class PDFPageDetailView extends PDFPageViewBase { viewport, pageColors: this.pageColors, }, - prevCanvas + prevCanvas, + () => { + this.recorder.finish(); + } ); div.setAttribute("data-loaded", true);