Extract PDFPageViewBase class out of PDFPageView
This base class contains the generic logic for: - Creating a canvas and showing when appropriate - Rendering in the canvas - Keeping track of the rendering state
This commit is contained in:
parent
e3ea92603d
commit
06257f782e
228
web/base_pdf_page_view.js
Normal file
228
web/base_pdf_page_view.js
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/* Copyright 2012 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 { RenderingCancelledException } from "pdfjs-lib";
|
||||||
|
import { RenderingStates } from "./ui_utils.js";
|
||||||
|
|
||||||
|
class BasePDFPageView {
|
||||||
|
#enableHWA = false;
|
||||||
|
|
||||||
|
#loadingId = null;
|
||||||
|
|
||||||
|
#renderError = null;
|
||||||
|
|
||||||
|
#renderingState = RenderingStates.INITIAL;
|
||||||
|
|
||||||
|
#showCanvas = null;
|
||||||
|
|
||||||
|
canvas = null;
|
||||||
|
|
||||||
|
/** @type {null | HTMLDivElement} */
|
||||||
|
div = null;
|
||||||
|
|
||||||
|
eventBus = null;
|
||||||
|
|
||||||
|
id = null;
|
||||||
|
|
||||||
|
pageColors = null;
|
||||||
|
|
||||||
|
renderingQueue = null;
|
||||||
|
|
||||||
|
renderTask = null;
|
||||||
|
|
||||||
|
resume = null;
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
this.#enableHWA =
|
||||||
|
#enableHWA in options ? options.#enableHWA : options.enableHWA || false;
|
||||||
|
this.eventBus = options.eventBus;
|
||||||
|
this.id = options.id;
|
||||||
|
this.pageColors = options.pageColors || null;
|
||||||
|
this.renderingQueue = options.renderingQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
get renderingState() {
|
||||||
|
return this.#renderingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
set renderingState(state) {
|
||||||
|
if (state === this.#renderingState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#renderingState = state;
|
||||||
|
|
||||||
|
if (this.#loadingId) {
|
||||||
|
clearTimeout(this.#loadingId);
|
||||||
|
this.#loadingId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case RenderingStates.PAUSED:
|
||||||
|
this.div.classList.remove("loading");
|
||||||
|
break;
|
||||||
|
case RenderingStates.RUNNING:
|
||||||
|
this.div.classList.add("loadingIcon");
|
||||||
|
this.#loadingId = setTimeout(() => {
|
||||||
|
// Adding the loading class is slightly postponed in order to not have
|
||||||
|
// it with loadingIcon.
|
||||||
|
// If we don't do that the visibility of the background is changed but
|
||||||
|
// the transition isn't triggered.
|
||||||
|
this.div.classList.add("loading");
|
||||||
|
this.#loadingId = null;
|
||||||
|
}, 0);
|
||||||
|
break;
|
||||||
|
case RenderingStates.INITIAL:
|
||||||
|
case RenderingStates.FINISHED:
|
||||||
|
this.div.classList.remove("loadingIcon", "loading");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_createCanvas(onShow) {
|
||||||
|
const { pageColors } = this;
|
||||||
|
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
|
||||||
|
const prevCanvas = this.canvas;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
const updateOnFirstShow = !prevCanvas && !hasHCM;
|
||||||
|
|
||||||
|
const canvas = (this.canvas = document.createElement("canvas"));
|
||||||
|
|
||||||
|
this.#showCanvas = isLastShow => {
|
||||||
|
if (updateOnFirstShow) {
|
||||||
|
// Don't add the canvas until the first draw callback, or until
|
||||||
|
// drawing is complete when `!this.renderingQueue`, to prevent black
|
||||||
|
// flickering.
|
||||||
|
onShow(canvas);
|
||||||
|
this.#showCanvas = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isLastShow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevCanvas) {
|
||||||
|
prevCanvas.replaceWith(canvas);
|
||||||
|
prevCanvas.width = prevCanvas.height = 0;
|
||||||
|
} else {
|
||||||
|
onShow(canvas);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d", {
|
||||||
|
alpha: false,
|
||||||
|
willReadFrequently: !this.#enableHWA,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { canvas, prevCanvas, ctx };
|
||||||
|
}
|
||||||
|
|
||||||
|
#renderContinueCallback = cont => {
|
||||||
|
this.#showCanvas?.(false);
|
||||||
|
if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
|
||||||
|
this.renderingState = RenderingStates.PAUSED;
|
||||||
|
this.resume = () => {
|
||||||
|
this.renderingState = RenderingStates.RUNNING;
|
||||||
|
cont();
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cont();
|
||||||
|
};
|
||||||
|
|
||||||
|
_resetCanvas() {
|
||||||
|
const { canvas } = this;
|
||||||
|
if (!canvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
canvas.remove();
|
||||||
|
canvas.width = canvas.height = 0;
|
||||||
|
this.canvas = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _drawCanvas(options, prevCanvas, onFinish) {
|
||||||
|
const renderTask = (this.renderTask = this.pdfPage.render(options));
|
||||||
|
renderTask.onContinue = this.#renderContinueCallback;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await renderTask.promise;
|
||||||
|
this.#showCanvas?.(true);
|
||||||
|
this.#finishRenderTask(renderTask, null, onFinish);
|
||||||
|
} catch (error) {
|
||||||
|
// When zooming with a `drawingDelay` set, avoid temporarily showing
|
||||||
|
// a black canvas if rendering was cancelled before the `onContinue`-
|
||||||
|
// callback had been invoked at least once.
|
||||||
|
if (!(error instanceof RenderingCancelledException)) {
|
||||||
|
this.#showCanvas?.(true);
|
||||||
|
} else {
|
||||||
|
prevCanvas?.remove();
|
||||||
|
this._resetCanvas();
|
||||||
|
}
|
||||||
|
this.#finishRenderTask(renderTask, error, onFinish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #finishRenderTask(renderTask, error, onFinish) {
|
||||||
|
// The renderTask may have been replaced by a new one, so only remove
|
||||||
|
// the reference to the renderTask if it matches the one that is
|
||||||
|
// triggering this callback.
|
||||||
|
if (renderTask === this.renderTask) {
|
||||||
|
this.renderTask = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof RenderingCancelledException) {
|
||||||
|
this.#renderError = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#renderError = error;
|
||||||
|
|
||||||
|
this.renderingState = RenderingStates.FINISHED;
|
||||||
|
|
||||||
|
onFinish(renderTask);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRendering({ cancelExtraDelay = 0 } = {}) {
|
||||||
|
if (this.renderTask) {
|
||||||
|
this.renderTask.cancel(cancelExtraDelay);
|
||||||
|
this.renderTask = null;
|
||||||
|
}
|
||||||
|
this.resume = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchPageRender() {
|
||||||
|
this.eventBus.dispatch("pagerender", {
|
||||||
|
source: this,
|
||||||
|
pageNumber: this.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchPageRendered(cssTransform) {
|
||||||
|
this.eventBus.dispatch("pagerendered", {
|
||||||
|
source: this,
|
||||||
|
pageNumber: this.id,
|
||||||
|
cssTransform,
|
||||||
|
timestamp: performance.now(),
|
||||||
|
error: this.#renderError,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { BasePDFPageView };
|
||||||
@ -28,7 +28,6 @@ import {
|
|||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
PixelsPerInch,
|
PixelsPerInch,
|
||||||
RenderingCancelledException,
|
|
||||||
setLayerDimensions,
|
setLayerDimensions,
|
||||||
shadow,
|
shadow,
|
||||||
} from "pdfjs-lib";
|
} from "pdfjs-lib";
|
||||||
@ -44,6 +43,7 @@ import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.
|
|||||||
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
||||||
import { AppOptions } from "./app_options.js";
|
import { AppOptions } from "./app_options.js";
|
||||||
import { Autolinker } from "./autolinker.js";
|
import { Autolinker } from "./autolinker.js";
|
||||||
|
import { BasePDFPageView } from "./base_pdf_page_view.js";
|
||||||
import { DrawLayerBuilder } from "./draw_layer_builder.js";
|
import { DrawLayerBuilder } from "./draw_layer_builder.js";
|
||||||
import { GenericL10n } from "web-null_l10n";
|
import { GenericL10n } from "web-null_l10n";
|
||||||
import { SimpleLinkService } from "./pdf_link_service.js";
|
import { SimpleLinkService } from "./pdf_link_service.js";
|
||||||
@ -116,13 +116,11 @@ const LAYERS_ORDER = new Map([
|
|||||||
/**
|
/**
|
||||||
* @implements {IRenderableView}
|
* @implements {IRenderableView}
|
||||||
*/
|
*/
|
||||||
class PDFPageView {
|
class PDFPageView extends BasePDFPageView {
|
||||||
#annotationMode = AnnotationMode.ENABLE_FORMS;
|
#annotationMode = AnnotationMode.ENABLE_FORMS;
|
||||||
|
|
||||||
#canvasWrapper = null;
|
#canvasWrapper = null;
|
||||||
|
|
||||||
#enableHWA = false;
|
|
||||||
|
|
||||||
#enableAutoLinking = false;
|
#enableAutoLinking = false;
|
||||||
|
|
||||||
#hasRestrictedScaling = false;
|
#hasRestrictedScaling = false;
|
||||||
@ -131,8 +129,6 @@ class PDFPageView {
|
|||||||
|
|
||||||
#layerProperties = null;
|
#layerProperties = null;
|
||||||
|
|
||||||
#loadingId = null;
|
|
||||||
|
|
||||||
#originalViewport = null;
|
#originalViewport = null;
|
||||||
|
|
||||||
#previousRotation = null;
|
#previousRotation = null;
|
||||||
@ -141,10 +137,6 @@ class PDFPageView {
|
|||||||
|
|
||||||
#scaleRoundY = 1;
|
#scaleRoundY = 1;
|
||||||
|
|
||||||
#renderError = null;
|
|
||||||
|
|
||||||
#renderingState = RenderingStates.INITIAL;
|
|
||||||
|
|
||||||
#textLayerMode = TextLayerMode.ENABLE;
|
#textLayerMode = TextLayerMode.ENABLE;
|
||||||
|
|
||||||
#userUnit = 1;
|
#userUnit = 1;
|
||||||
@ -161,10 +153,11 @@ class PDFPageView {
|
|||||||
* @param {PDFPageViewOptions} options
|
* @param {PDFPageViewOptions} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
const container = options.container;
|
const container = options.container;
|
||||||
const defaultViewport = options.defaultViewport;
|
const defaultViewport = options.defaultViewport;
|
||||||
|
|
||||||
this.id = options.id;
|
|
||||||
this.renderingId = "page" + this.id;
|
this.renderingId = "page" + this.id;
|
||||||
this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES;
|
this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES;
|
||||||
|
|
||||||
@ -182,19 +175,13 @@ class PDFPageView {
|
|||||||
this.imageResourcesPath = options.imageResourcesPath || "";
|
this.imageResourcesPath = options.imageResourcesPath || "";
|
||||||
this.maxCanvasPixels =
|
this.maxCanvasPixels =
|
||||||
options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
|
options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
|
||||||
this.pageColors = options.pageColors || null;
|
|
||||||
this.#enableHWA = options.enableHWA || false;
|
|
||||||
this.#enableAutoLinking = options.enableAutoLinking || false;
|
this.#enableAutoLinking = options.enableAutoLinking || false;
|
||||||
|
|
||||||
this.eventBus = options.eventBus;
|
|
||||||
this.renderingQueue = options.renderingQueue;
|
|
||||||
this.l10n = options.l10n;
|
this.l10n = options.l10n;
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
this.l10n ||= new GenericL10n();
|
this.l10n ||= new GenericL10n();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderTask = null;
|
|
||||||
this.resume = null;
|
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
this._isStandalone = !this.renderingQueue?.hasViewer();
|
this._isStandalone = !this.renderingQueue?.hasViewer();
|
||||||
this._container = container;
|
this._container = container;
|
||||||
@ -278,43 +265,6 @@ class PDFPageView {
|
|||||||
this.div.prepend(div);
|
this.div.prepend(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
get renderingState() {
|
|
||||||
return this.#renderingState;
|
|
||||||
}
|
|
||||||
|
|
||||||
set renderingState(state) {
|
|
||||||
if (state === this.#renderingState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.#renderingState = state;
|
|
||||||
|
|
||||||
if (this.#loadingId) {
|
|
||||||
clearTimeout(this.#loadingId);
|
|
||||||
this.#loadingId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case RenderingStates.PAUSED:
|
|
||||||
this.div.classList.remove("loading");
|
|
||||||
break;
|
|
||||||
case RenderingStates.RUNNING:
|
|
||||||
this.div.classList.add("loadingIcon");
|
|
||||||
this.#loadingId = setTimeout(() => {
|
|
||||||
// Adding the loading class is slightly postponed in order to not have
|
|
||||||
// it with loadingIcon.
|
|
||||||
// If we don't do that the visibility of the background is changed but
|
|
||||||
// the transition isn't triggered.
|
|
||||||
this.div.classList.add("loading");
|
|
||||||
this.#loadingId = null;
|
|
||||||
}, 0);
|
|
||||||
break;
|
|
||||||
case RenderingStates.INITIAL:
|
|
||||||
case RenderingStates.FINISHED:
|
|
||||||
this.div.classList.remove("loadingIcon", "loading");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#setDimensions() {
|
#setDimensions() {
|
||||||
const { div, viewport } = this;
|
const { div, viewport } = this;
|
||||||
|
|
||||||
@ -558,14 +508,8 @@ class PDFPageView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetCanvas() {
|
_resetCanvas() {
|
||||||
const { canvas } = this;
|
super._resetCanvas();
|
||||||
if (!canvas) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
canvas.remove();
|
|
||||||
canvas.width = canvas.height = 0;
|
|
||||||
this.canvas = null;
|
|
||||||
this.#originalViewport = null;
|
this.#originalViewport = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,7 +576,7 @@ class PDFPageView {
|
|||||||
|
|
||||||
if (!keepCanvasWrapper && this.#canvasWrapper) {
|
if (!keepCanvasWrapper && this.#canvasWrapper) {
|
||||||
this.#canvasWrapper = null;
|
this.#canvasWrapper = null;
|
||||||
this.#resetCanvas();
|
this._resetCanvas();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,18 +702,11 @@ class PDFPageView {
|
|||||||
hideTextLayer: postponeDrawing,
|
hideTextLayer: postponeDrawing,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (postponeDrawing) {
|
// The "pagerendered"-event will be dispatched once the actual
|
||||||
// The "pagerendered"-event will be dispatched once the actual
|
// rendering is done, hence don't dispatch it here as well.
|
||||||
// rendering is done, hence don't dispatch it here as well.
|
if (!postponeDrawing) {
|
||||||
return;
|
this.dispatchPageRendered(true);
|
||||||
}
|
}
|
||||||
this.eventBus.dispatch("pagerendered", {
|
|
||||||
source: this,
|
|
||||||
pageNumber: this.id,
|
|
||||||
cssTransform: true,
|
|
||||||
timestamp: performance.now(),
|
|
||||||
error: this.#renderError,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,11 +731,7 @@ class PDFPageView {
|
|||||||
keepTextLayer = false,
|
keepTextLayer = false,
|
||||||
cancelExtraDelay = 0,
|
cancelExtraDelay = 0,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (this.renderTask) {
|
super.cancelRendering({ cancelExtraDelay });
|
||||||
this.renderTask.cancel(cancelExtraDelay);
|
|
||||||
this.renderTask = null;
|
|
||||||
}
|
|
||||||
this.resume = null;
|
|
||||||
|
|
||||||
if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) {
|
if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) {
|
||||||
this.textLayer.cancel();
|
this.textLayer.cancel();
|
||||||
@ -897,39 +830,6 @@ class PDFPageView {
|
|||||||
return this.viewport.convertToPdfPoint(x, y);
|
return this.viewport.convertToPdfPoint(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #finishRenderTask(renderTask, error = null) {
|
|
||||||
// The renderTask may have been replaced by a new one, so only remove
|
|
||||||
// the reference to the renderTask if it matches the one that is
|
|
||||||
// triggering this callback.
|
|
||||||
if (renderTask === this.renderTask) {
|
|
||||||
this.renderTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof RenderingCancelledException) {
|
|
||||||
this.#renderError = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.#renderError = error;
|
|
||||||
|
|
||||||
this.renderingState = RenderingStates.FINISHED;
|
|
||||||
|
|
||||||
// Ensure that the thumbnails won't become partially (or fully) blank,
|
|
||||||
// for documents that contain interactive form elements.
|
|
||||||
this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots;
|
|
||||||
|
|
||||||
this.eventBus.dispatch("pagerendered", {
|
|
||||||
source: this,
|
|
||||||
pageNumber: this.id,
|
|
||||||
cssTransform: false,
|
|
||||||
timestamp: performance.now(),
|
|
||||||
error: this.#renderError,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async draw() {
|
async draw() {
|
||||||
if (this.renderingState !== RenderingStates.INITIAL) {
|
if (this.renderingState !== RenderingStates.INITIAL) {
|
||||||
console.error("Must be in new state before drawing");
|
console.error("Must be in new state before drawing");
|
||||||
@ -1009,61 +909,15 @@ class PDFPageView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderContinueCallback = cont => {
|
|
||||||
showCanvas?.(false);
|
|
||||||
if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
|
|
||||||
this.renderingState = RenderingStates.PAUSED;
|
|
||||||
this.resume = () => {
|
|
||||||
this.renderingState = RenderingStates.RUNNING;
|
|
||||||
cont();
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cont();
|
|
||||||
};
|
|
||||||
|
|
||||||
const { width, height } = viewport;
|
const { width, height } = viewport;
|
||||||
const canvas = document.createElement("canvas");
|
|
||||||
canvas.setAttribute("role", "presentation");
|
|
||||||
|
|
||||||
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
|
|
||||||
const prevCanvas = this.canvas;
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
const updateOnFirstShow = !prevCanvas && !hasHCM;
|
|
||||||
this.canvas = canvas;
|
|
||||||
this.#originalViewport = viewport;
|
this.#originalViewport = viewport;
|
||||||
|
|
||||||
let showCanvas = isLastShow => {
|
const { canvas, prevCanvas, ctx } = this._createCanvas(newCanvas => {
|
||||||
if (updateOnFirstShow) {
|
// Always inject the canvas as the first element in the wrapper.
|
||||||
// Don't add the canvas until the first draw callback, or until
|
canvasWrapper.prepend(newCanvas);
|
||||||
// drawing is complete when `!this.renderingQueue`, to prevent black
|
|
||||||
// flickering.
|
|
||||||
// In whatever case, the canvas must be the first child.
|
|
||||||
canvasWrapper.prepend(canvas);
|
|
||||||
showCanvas = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isLastShow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevCanvas) {
|
|
||||||
prevCanvas.replaceWith(canvas);
|
|
||||||
prevCanvas.width = prevCanvas.height = 0;
|
|
||||||
} else {
|
|
||||||
canvasWrapper.prepend(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
showCanvas = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ctx = canvas.getContext("2d", {
|
|
||||||
alpha: false,
|
|
||||||
willReadFrequently: !this.#enableHWA,
|
|
||||||
});
|
});
|
||||||
|
canvas.setAttribute("role", "presentation");
|
||||||
|
|
||||||
const outputScale = (this.outputScale = new OutputScale());
|
const outputScale = (this.outputScale = new OutputScale());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -1126,68 +980,59 @@ class PDFPageView {
|
|||||||
pageColors,
|
pageColors,
|
||||||
isEditing: this.#isEditing,
|
isEditing: this.#isEditing,
|
||||||
};
|
};
|
||||||
const renderTask = (this.renderTask = pdfPage.render(renderContext));
|
const resultPromise = this._drawCanvas(
|
||||||
renderTask.onContinue = renderContinueCallback;
|
renderContext,
|
||||||
|
prevCanvas,
|
||||||
|
renderTask => {
|
||||||
|
// Ensure that the thumbnails won't become partially (or fully) blank,
|
||||||
|
// for documents that contain interactive form elements.
|
||||||
|
this.#useThumbnailCanvas.regularAnnotations =
|
||||||
|
!renderTask.separateAnnots;
|
||||||
|
|
||||||
const resultPromise = renderTask.promise.then(
|
this.dispatchPageRendered(false);
|
||||||
async () => {
|
|
||||||
showCanvas?.(true);
|
|
||||||
await this.#finishRenderTask(renderTask);
|
|
||||||
|
|
||||||
this.structTreeLayer ||= new StructTreeLayerBuilder(
|
|
||||||
pdfPage,
|
|
||||||
viewport.rawDims
|
|
||||||
);
|
|
||||||
|
|
||||||
const textLayerPromise = this.#renderTextLayer();
|
|
||||||
|
|
||||||
if (this.annotationLayer) {
|
|
||||||
await this.#renderAnnotationLayer();
|
|
||||||
|
|
||||||
if (this.#enableAutoLinking && this.annotationLayer) {
|
|
||||||
await this.#injectLinkAnnotations(textLayerPromise);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { annotationEditorUIManager } = this.#layerProperties;
|
|
||||||
|
|
||||||
if (!annotationEditorUIManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.drawLayer ||= new DrawLayerBuilder({
|
|
||||||
pageIndex: this.id,
|
|
||||||
});
|
|
||||||
await this.#renderDrawLayer();
|
|
||||||
this.drawLayer.setParent(canvasWrapper);
|
|
||||||
|
|
||||||
this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({
|
|
||||||
uiManager: annotationEditorUIManager,
|
|
||||||
pdfPage,
|
|
||||||
l10n,
|
|
||||||
structTreeLayer: this.structTreeLayer,
|
|
||||||
accessibilityManager: this._accessibilityManager,
|
|
||||||
annotationLayer: this.annotationLayer?.annotationLayer,
|
|
||||||
textLayer: this.textLayer,
|
|
||||||
drawLayer: this.drawLayer.getDrawLayer(),
|
|
||||||
onAppend: annotationEditorLayerDiv => {
|
|
||||||
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.#renderAnnotationEditorLayer();
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
// When zooming with a `drawingDelay` set, avoid temporarily showing
|
|
||||||
// a black canvas if rendering was cancelled before the `onContinue`-
|
|
||||||
// callback had been invoked at least once.
|
|
||||||
if (!(error instanceof RenderingCancelledException)) {
|
|
||||||
showCanvas?.(true);
|
|
||||||
} else {
|
|
||||||
prevCanvas?.remove();
|
|
||||||
this.#resetCanvas();
|
|
||||||
}
|
|
||||||
return this.#finishRenderTask(renderTask, error);
|
|
||||||
}
|
}
|
||||||
);
|
).then(async () => {
|
||||||
|
this.structTreeLayer ||= new StructTreeLayerBuilder(
|
||||||
|
pdfPage,
|
||||||
|
viewport.rawDims
|
||||||
|
);
|
||||||
|
|
||||||
|
const textLayerPromise = this.#renderTextLayer();
|
||||||
|
|
||||||
|
if (this.annotationLayer) {
|
||||||
|
await this.#renderAnnotationLayer();
|
||||||
|
|
||||||
|
if (this.#enableAutoLinking && this.annotationLayer) {
|
||||||
|
await this.#injectLinkAnnotations(textLayerPromise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { annotationEditorUIManager } = this.#layerProperties;
|
||||||
|
|
||||||
|
if (!annotationEditorUIManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.drawLayer ||= new DrawLayerBuilder({
|
||||||
|
pageIndex: this.id,
|
||||||
|
});
|
||||||
|
await this.#renderDrawLayer();
|
||||||
|
this.drawLayer.setParent(canvasWrapper);
|
||||||
|
|
||||||
|
this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({
|
||||||
|
uiManager: annotationEditorUIManager,
|
||||||
|
pdfPage,
|
||||||
|
l10n,
|
||||||
|
structTreeLayer: this.structTreeLayer,
|
||||||
|
accessibilityManager: this._accessibilityManager,
|
||||||
|
annotationLayer: this.annotationLayer?.annotationLayer,
|
||||||
|
textLayer: this.textLayer,
|
||||||
|
drawLayer: this.drawLayer.getDrawLayer(),
|
||||||
|
onAppend: annotationEditorLayerDiv => {
|
||||||
|
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.#renderAnnotationEditorLayer();
|
||||||
|
});
|
||||||
|
|
||||||
if (pdfPage.isPureXfa) {
|
if (pdfPage.isPureXfa) {
|
||||||
if (!this.xfaLayer) {
|
if (!this.xfaLayer) {
|
||||||
@ -1204,10 +1049,8 @@ class PDFPageView {
|
|||||||
|
|
||||||
div.setAttribute("data-loaded", true);
|
div.setAttribute("data-loaded", true);
|
||||||
|
|
||||||
this.eventBus.dispatch("pagerender", {
|
this.dispatchPageRender();
|
||||||
source: this,
|
|
||||||
pageNumber: this.id,
|
|
||||||
});
|
|
||||||
return resultPromise;
|
return resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user