Merge pull request #16226 from Snuffleupagus/rm-svg-viewer
[api-minor] Remove SVG-rendering from the viewer (PR 15173 follow-up)
This commit is contained in:
commit
184076fe7a
18
web/app.js
18
web/app.js
@ -27,7 +27,6 @@ import {
|
|||||||
normalizeWheelEventDirection,
|
normalizeWheelEventDirection,
|
||||||
parseQueryString,
|
parseQueryString,
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
RendererType,
|
|
||||||
RenderingStates,
|
RenderingStates,
|
||||||
ScrollMode,
|
ScrollMode,
|
||||||
SidebarView,
|
SidebarView,
|
||||||
@ -512,11 +511,6 @@ const PDFViewerApplication = {
|
|||||||
findController,
|
findController,
|
||||||
scriptingManager:
|
scriptingManager:
|
||||||
AppOptions.get("enableScripting") && pdfScriptingManager,
|
AppOptions.get("enableScripting") && pdfScriptingManager,
|
||||||
renderer:
|
|
||||||
typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
? AppOptions.get("renderer")
|
|
||||||
: null,
|
|
||||||
l10n: this.l10n,
|
l10n: this.l10n,
|
||||||
textLayerMode: AppOptions.get("textLayerMode"),
|
textLayerMode: AppOptions.get("textLayerMode"),
|
||||||
annotationMode: AppOptions.get("annotationMode"),
|
annotationMode: AppOptions.get("annotationMode"),
|
||||||
@ -1714,17 +1708,7 @@ const PDFViewerApplication = {
|
|||||||
this.pdfViewer.cleanup();
|
this.pdfViewer.cleanup();
|
||||||
this.pdfThumbnailViewer?.cleanup();
|
this.pdfThumbnailViewer?.cleanup();
|
||||||
|
|
||||||
if (
|
this.pdfDocument.cleanup();
|
||||||
typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
) {
|
|
||||||
// We don't want to remove fonts used by active page SVGs.
|
|
||||||
this.pdfDocument.cleanup(
|
|
||||||
/* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.pdfDocument.cleanup();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
forceRendering() {
|
forceRendering() {
|
||||||
|
@ -315,11 +315,6 @@ if (
|
|||||||
value: navigator.language || "en-US",
|
value: navigator.language || "en-US",
|
||||||
kind: OptionKind.VIEWER,
|
kind: OptionKind.VIEWER,
|
||||||
};
|
};
|
||||||
defaultOptions.renderer = {
|
|
||||||
/** @type {string} */
|
|
||||||
value: "canvas",
|
|
||||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
|
||||||
};
|
|
||||||
defaultOptions.sandboxBundleSrc = {
|
defaultOptions.sandboxBundleSrc = {
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
value:
|
value:
|
||||||
|
@ -146,7 +146,7 @@ class IRenderableView {
|
|||||||
/**
|
/**
|
||||||
* @returns {Promise} Resolved on draw completion.
|
* @returns {Promise} Resolved on draw completion.
|
||||||
*/
|
*/
|
||||||
draw() {}
|
async draw() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,18 +26,15 @@
|
|||||||
import {
|
import {
|
||||||
AbortException,
|
AbortException,
|
||||||
AnnotationMode,
|
AnnotationMode,
|
||||||
createPromiseCapability,
|
|
||||||
PixelsPerInch,
|
PixelsPerInch,
|
||||||
RenderingCancelledException,
|
RenderingCancelledException,
|
||||||
setLayerDimensions,
|
setLayerDimensions,
|
||||||
shadow,
|
shadow,
|
||||||
SVGGraphics,
|
|
||||||
} from "pdfjs-lib";
|
} from "pdfjs-lib";
|
||||||
import {
|
import {
|
||||||
approximateFraction,
|
approximateFraction,
|
||||||
DEFAULT_SCALE,
|
DEFAULT_SCALE,
|
||||||
OutputScale,
|
OutputScale,
|
||||||
RendererType,
|
|
||||||
RenderingStates,
|
RenderingStates,
|
||||||
roundToDivide,
|
roundToDivide,
|
||||||
TextLayerMode,
|
TextLayerMode,
|
||||||
@ -121,6 +118,8 @@ class PDFPageView {
|
|||||||
|
|
||||||
#previousRotation = null;
|
#previousRotation = null;
|
||||||
|
|
||||||
|
#renderError = null;
|
||||||
|
|
||||||
#renderingState = RenderingStates.INITIAL;
|
#renderingState = RenderingStates.INITIAL;
|
||||||
|
|
||||||
#useThumbnailCanvas = {
|
#useThumbnailCanvas = {
|
||||||
@ -128,6 +127,8 @@ class PDFPageView {
|
|||||||
regularAnnotations: true,
|
regularAnnotations: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#viewportMap = new WeakMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PDFPageViewOptions} options
|
* @param {PDFPageViewOptions} options
|
||||||
*/
|
*/
|
||||||
@ -160,18 +161,10 @@ class PDFPageView {
|
|||||||
|
|
||||||
this.eventBus = options.eventBus;
|
this.eventBus = options.eventBus;
|
||||||
this.renderingQueue = options.renderingQueue;
|
this.renderingQueue = options.renderingQueue;
|
||||||
if (
|
|
||||||
typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
) {
|
|
||||||
this.renderer = options.renderer || RendererType.CANVAS;
|
|
||||||
}
|
|
||||||
this.l10n = options.l10n || NullL10n;
|
this.l10n = options.l10n || NullL10n;
|
||||||
|
|
||||||
this.paintTask = null;
|
this.renderTask = null;
|
||||||
this.paintedViewportMap = new WeakMap();
|
|
||||||
this.resume = null;
|
this.resume = null;
|
||||||
this._renderError = null;
|
|
||||||
if (
|
if (
|
||||||
typeof PDFJSDev === "undefined" ||
|
typeof PDFJSDev === "undefined" ||
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
PDFJSDev.test("!PRODUCTION || GENERIC")
|
||||||
@ -438,7 +431,7 @@ class PDFPageView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const zoomLayerCanvas = this.zoomLayer.firstChild;
|
const zoomLayerCanvas = this.zoomLayer.firstChild;
|
||||||
this.paintedViewportMap.delete(zoomLayerCanvas);
|
this.#viewportMap.delete(zoomLayerCanvas);
|
||||||
// Zeroing the width and height causes Firefox to release graphics
|
// Zeroing the width and height causes Firefox to release graphics
|
||||||
// resources immediately, which can greatly reduce memory consumption.
|
// resources immediately, which can greatly reduce memory consumption.
|
||||||
zoomLayerCanvas.width = 0;
|
zoomLayerCanvas.width = 0;
|
||||||
@ -510,7 +503,7 @@ class PDFPageView {
|
|||||||
|
|
||||||
if (!zoomLayerNode) {
|
if (!zoomLayerNode) {
|
||||||
if (this.canvas) {
|
if (this.canvas) {
|
||||||
this.paintedViewportMap.delete(this.canvas);
|
this.#viewportMap.delete(this.canvas);
|
||||||
// Zeroing the width and height causes Firefox to release graphics
|
// Zeroing the width and height causes Firefox to release graphics
|
||||||
// resources immediately, which can greatly reduce memory consumption.
|
// resources immediately, which can greatly reduce memory consumption.
|
||||||
this.canvas.width = 0;
|
this.canvas.width = 0;
|
||||||
@ -519,14 +512,6 @@ class PDFPageView {
|
|||||||
}
|
}
|
||||||
this._resetZoomLayer();
|
this._resetZoomLayer();
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
(typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")) &&
|
|
||||||
this.svg
|
|
||||||
) {
|
|
||||||
this.paintedViewportMap.delete(this.svg);
|
|
||||||
delete this.svg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update({
|
update({
|
||||||
@ -573,35 +558,12 @@ class PDFPageView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
(typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")) &&
|
|
||||||
this.svg
|
|
||||||
) {
|
|
||||||
this.cssTransform({
|
|
||||||
target: this.svg,
|
|
||||||
redrawAnnotationLayer: true,
|
|
||||||
redrawAnnotationEditorLayer: true,
|
|
||||||
redrawXfaLayer: true,
|
|
||||||
redrawTextLayer: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.eventBus.dispatch("pagerendered", {
|
|
||||||
source: this,
|
|
||||||
pageNumber: this.id,
|
|
||||||
cssTransform: true,
|
|
||||||
timestamp: performance.now(),
|
|
||||||
error: this._renderError,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isScalingRestricted = false;
|
let isScalingRestricted = false;
|
||||||
if (this.canvas && this.maxCanvasPixels > 0) {
|
if (this.canvas && this.maxCanvasPixels > 0) {
|
||||||
const outputScale = this.outputScale;
|
const { width, height } = this.viewport;
|
||||||
|
const { sx, sy } = this.outputScale;
|
||||||
if (
|
if (
|
||||||
((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
|
((Math.floor(width) * sx) | 0) * ((Math.floor(height) * sy) | 0) >
|
||||||
((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
|
|
||||||
this.maxCanvasPixels
|
this.maxCanvasPixels
|
||||||
) {
|
) {
|
||||||
isScalingRestricted = true;
|
isScalingRestricted = true;
|
||||||
@ -647,7 +609,7 @@ class PDFPageView {
|
|||||||
pageNumber: this.id,
|
pageNumber: this.id,
|
||||||
cssTransform: true,
|
cssTransform: true,
|
||||||
timestamp: performance.now(),
|
timestamp: performance.now(),
|
||||||
error: this._renderError,
|
error: this.#renderError,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -679,9 +641,9 @@ class PDFPageView {
|
|||||||
keepTextLayer = false,
|
keepTextLayer = false,
|
||||||
cancelExtraDelay = 0,
|
cancelExtraDelay = 0,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (this.paintTask) {
|
if (this.renderTask) {
|
||||||
this.paintTask.cancel(cancelExtraDelay);
|
this.renderTask.cancel(cancelExtraDelay);
|
||||||
this.paintTask = null;
|
this.renderTask = null;
|
||||||
}
|
}
|
||||||
this.resume = null;
|
this.resume = null;
|
||||||
|
|
||||||
@ -722,29 +684,21 @@ class PDFPageView {
|
|||||||
redrawTextLayer = false,
|
redrawTextLayer = false,
|
||||||
hideTextLayer = false,
|
hideTextLayer = false,
|
||||||
}) {
|
}) {
|
||||||
// Scale target (canvas or svg), its wrapper and page container.
|
// Scale target (canvas), its wrapper and page container.
|
||||||
|
if (
|
||||||
if (target instanceof HTMLCanvasElement) {
|
(typeof PDFJSDev === "undefined" ||
|
||||||
if (!target.hasAttribute("zooming")) {
|
PDFJSDev.test("!PRODUCTION || TESTING")) &&
|
||||||
target.setAttribute("zooming", true);
|
!(target instanceof HTMLCanvasElement)
|
||||||
const { style } = target;
|
) {
|
||||||
style.width = style.height = "";
|
throw new Error("Expected `target` to be a canvas.");
|
||||||
}
|
}
|
||||||
} else {
|
if (!target.hasAttribute("zooming")) {
|
||||||
const div = this.div;
|
target.setAttribute("zooming", true);
|
||||||
const { width, height } = this.viewport;
|
const { style } = target;
|
||||||
|
style.width = style.height = "";
|
||||||
target.style.width =
|
|
||||||
target.parentNode.style.width =
|
|
||||||
div.style.width =
|
|
||||||
Math.floor(width) + "px";
|
|
||||||
target.style.height =
|
|
||||||
target.parentNode.style.height =
|
|
||||||
div.style.height =
|
|
||||||
Math.floor(height) + "px";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalViewport = this.paintedViewportMap.get(target);
|
const originalViewport = this.#viewportMap.get(target);
|
||||||
if (this.viewport !== originalViewport) {
|
if (this.viewport !== originalViewport) {
|
||||||
// The canvas may have been originally rotated; rotate relative to that.
|
// The canvas may have been originally rotated; rotate relative to that.
|
||||||
const relativeRotation =
|
const relativeRotation =
|
||||||
@ -793,16 +747,50 @@ class PDFPageView {
|
|||||||
return this.viewport.convertToPdfPoint(x, y);
|
return this.viewport.convertToPdfPoint(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
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;
|
||||||
|
this._resetZoomLayer(/* removeFromDOM = */ true);
|
||||||
|
|
||||||
|
// 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() {
|
||||||
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");
|
||||||
this.reset(); // Ensure that we reset all state to prevent issues.
|
this.reset(); // Ensure that we reset all state to prevent issues.
|
||||||
}
|
}
|
||||||
const { div, pdfPage } = this;
|
const { div, l10n, pageColors, pdfPage, viewport } = this;
|
||||||
|
|
||||||
if (!pdfPage) {
|
if (!pdfPage) {
|
||||||
this.renderingState = RenderingStates.FINISHED;
|
this.renderingState = RenderingStates.FINISHED;
|
||||||
return Promise.reject(new Error("pdfPage is not loaded"));
|
throw new Error("pdfPage is not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderingState = RenderingStates.RUNNING;
|
this.renderingState = RenderingStates.RUNNING;
|
||||||
@ -850,7 +838,7 @@ class PDFPageView {
|
|||||||
renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS,
|
renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS,
|
||||||
linkService,
|
linkService,
|
||||||
downloadManager,
|
downloadManager,
|
||||||
l10n: this.l10n,
|
l10n,
|
||||||
enableScripting,
|
enableScripting,
|
||||||
hasJSActionsPromise,
|
hasJSActionsPromise,
|
||||||
fieldObjectsPromise,
|
fieldObjectsPromise,
|
||||||
@ -859,92 +847,127 @@ class PDFPageView {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let renderContinueCallback = null;
|
const renderContinueCallback = cont => {
|
||||||
if (this.renderingQueue) {
|
showCanvas?.(false);
|
||||||
renderContinueCallback = cont => {
|
if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
|
||||||
if (!this.renderingQueue.isHighestPriority(this)) {
|
this.renderingState = RenderingStates.PAUSED;
|
||||||
this.renderingState = RenderingStates.PAUSED;
|
this.resume = () => {
|
||||||
this.resume = () => {
|
this.renderingState = RenderingStates.RUNNING;
|
||||||
this.renderingState = RenderingStates.RUNNING;
|
cont();
|
||||||
cont();
|
};
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cont();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const finishPaintTask = async (error = null) => {
|
|
||||||
// The paintTask may have been replaced by a new one, so only remove
|
|
||||||
// the reference to the paintTask if it matches the one that is
|
|
||||||
// triggering this callback.
|
|
||||||
if (paintTask === this.paintTask) {
|
|
||||||
this.paintTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof RenderingCancelledException) {
|
|
||||||
this._renderError = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._renderError = error;
|
cont();
|
||||||
|
|
||||||
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.
|
|
||||||
this.#useThumbnailCanvas.regularAnnotations = !paintTask.separateAnnots;
|
|
||||||
|
|
||||||
this.eventBus.dispatch("pagerendered", {
|
|
||||||
source: this,
|
|
||||||
pageNumber: this.id,
|
|
||||||
cssTransform: false,
|
|
||||||
timestamp: performance.now(),
|
|
||||||
error: this._renderError,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const paintTask =
|
const { width, height } = viewport;
|
||||||
(typeof PDFJSDev === "undefined" ||
|
const canvas = document.createElement("canvas");
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")) &&
|
canvas.setAttribute("role", "presentation");
|
||||||
this.renderer === RendererType.SVG
|
|
||||||
? this.paintOnSvg(canvasWrapper)
|
|
||||||
: this.paintOnCanvas(canvasWrapper);
|
|
||||||
paintTask.onRenderContinue = renderContinueCallback;
|
|
||||||
this.paintTask = paintTask;
|
|
||||||
|
|
||||||
const resultPromise = paintTask.promise.then(
|
// Keep the canvas hidden until the first draw callback, or until drawing
|
||||||
() => {
|
// is complete when `!this.renderingQueue`, to prevent black flickering.
|
||||||
return finishPaintTask(null).then(async () => {
|
canvas.hidden = true;
|
||||||
this.#renderTextLayer();
|
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
|
||||||
|
|
||||||
if (this.annotationLayer) {
|
let showCanvas = isLastShow => {
|
||||||
await this.#renderAnnotationLayer();
|
// 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.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
canvasWrapper.append(canvas);
|
||||||
|
this.canvas = canvas;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d", { alpha: false });
|
||||||
|
const outputScale = (this.outputScale = new OutputScale());
|
||||||
|
|
||||||
|
if (this.useOnlyCssZoom) {
|
||||||
|
const actualSizeViewport = viewport.clone({
|
||||||
|
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
||||||
|
});
|
||||||
|
// Use a scale that makes the canvas have the originally intended size
|
||||||
|
// of the page.
|
||||||
|
outputScale.sx *= actualSizeViewport.width / width;
|
||||||
|
outputScale.sy *= actualSizeViewport.height / height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.maxCanvasPixels > 0) {
|
||||||
|
const pixelsInViewport = width * height;
|
||||||
|
const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
|
||||||
|
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
|
||||||
|
outputScale.sx = maxScale;
|
||||||
|
outputScale.sy = maxScale;
|
||||||
|
this.hasRestrictedScaling = true;
|
||||||
|
} else {
|
||||||
|
this.hasRestrictedScaling = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sfx = approximateFraction(outputScale.sx);
|
||||||
|
const sfy = approximateFraction(outputScale.sy);
|
||||||
|
|
||||||
|
canvas.width = roundToDivide(width * outputScale.sx, sfx[0]);
|
||||||
|
canvas.height = roundToDivide(height * outputScale.sy, sfy[0]);
|
||||||
|
const { style } = canvas;
|
||||||
|
style.width = roundToDivide(width, sfx[1]) + "px";
|
||||||
|
style.height = roundToDivide(height, sfy[1]) + "px";
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
: null;
|
||||||
|
const renderContext = {
|
||||||
|
canvasContext: ctx,
|
||||||
|
transform,
|
||||||
|
viewport,
|
||||||
|
annotationMode: this.#annotationMode,
|
||||||
|
optionalContentConfigPromise: this._optionalContentConfigPromise,
|
||||||
|
annotationCanvasMap: this._annotationCanvasMap,
|
||||||
|
pageColors,
|
||||||
|
};
|
||||||
|
const renderTask = (this.renderTask = this.pdfPage.render(renderContext));
|
||||||
|
renderTask.onContinue = renderContinueCallback;
|
||||||
|
|
||||||
|
const resultPromise = renderTask.promise.then(
|
||||||
|
async () => {
|
||||||
|
showCanvas?.(true);
|
||||||
|
await this.#finishRenderTask(renderTask);
|
||||||
|
|
||||||
|
this.#renderTextLayer();
|
||||||
|
|
||||||
|
if (this.annotationLayer) {
|
||||||
|
await this.#renderAnnotationLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.annotationEditorLayer) {
|
||||||
|
const { annotationEditorUIManager } = this.#layerProperties();
|
||||||
|
|
||||||
|
if (!annotationEditorUIManager) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
|
||||||
if (!this.annotationEditorLayer) {
|
uiManager: annotationEditorUIManager,
|
||||||
const { annotationEditorUIManager } = this.#layerProperties();
|
pageDiv: div,
|
||||||
|
pdfPage,
|
||||||
if (!annotationEditorUIManager) {
|
l10n,
|
||||||
return;
|
accessibilityManager: this._accessibilityManager,
|
||||||
}
|
});
|
||||||
this.annotationEditorLayer = new AnnotationEditorLayerBuilder({
|
}
|
||||||
uiManager: annotationEditorUIManager,
|
this.#renderAnnotationEditorLayer();
|
||||||
pageDiv: div,
|
|
||||||
pdfPage,
|
|
||||||
l10n: this.l10n,
|
|
||||||
accessibilityManager: this._accessibilityManager,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.#renderAnnotationEditorLayer();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function (reason) {
|
error => {
|
||||||
return finishPaintTask(reason);
|
// 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);
|
||||||
|
}
|
||||||
|
return this.#finishRenderTask(renderTask, error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -974,180 +997,6 @@ class PDFPageView {
|
|||||||
return resultPromise;
|
return resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
paintOnCanvas(canvasWrapper) {
|
|
||||||
const renderCapability = createPromiseCapability();
|
|
||||||
const result = {
|
|
||||||
promise: renderCapability.promise,
|
|
||||||
onRenderContinue(cont) {
|
|
||||||
cont();
|
|
||||||
},
|
|
||||||
cancel(extraDelay = 0) {
|
|
||||||
renderTask.cancel(extraDelay);
|
|
||||||
},
|
|
||||||
get separateAnnots() {
|
|
||||||
return renderTask.separateAnnots;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const viewport = this.viewport;
|
|
||||||
const { width, height } = viewport;
|
|
||||||
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;
|
|
||||||
let isCanvasHidden = true;
|
|
||||||
const hasHCM = !!(
|
|
||||||
this.pageColors?.background && this.pageColors?.foreground
|
|
||||||
);
|
|
||||||
const showCanvas = function (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 (isCanvasHidden && (!hasHCM || isLastShow)) {
|
|
||||||
canvas.hidden = false;
|
|
||||||
isCanvasHidden = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
canvasWrapper.append(canvas);
|
|
||||||
this.canvas = canvas;
|
|
||||||
|
|
||||||
const ctx = canvas.getContext("2d", { alpha: false });
|
|
||||||
const outputScale = (this.outputScale = new OutputScale());
|
|
||||||
|
|
||||||
if (this.useOnlyCssZoom) {
|
|
||||||
const actualSizeViewport = viewport.clone({
|
|
||||||
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
||||||
});
|
|
||||||
// Use a scale that makes the canvas have the originally intended size
|
|
||||||
// of the page.
|
|
||||||
outputScale.sx *= actualSizeViewport.width / width;
|
|
||||||
outputScale.sy *= actualSizeViewport.height / height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.maxCanvasPixels > 0) {
|
|
||||||
const pixelsInViewport = width * height;
|
|
||||||
const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
|
|
||||||
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
|
|
||||||
outputScale.sx = maxScale;
|
|
||||||
outputScale.sy = maxScale;
|
|
||||||
this.hasRestrictedScaling = true;
|
|
||||||
} else {
|
|
||||||
this.hasRestrictedScaling = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sfx = approximateFraction(outputScale.sx);
|
|
||||||
const sfy = approximateFraction(outputScale.sy);
|
|
||||||
|
|
||||||
canvas.width = roundToDivide(viewport.width * outputScale.sx, sfx[0]);
|
|
||||||
canvas.height = roundToDivide(viewport.height * outputScale.sy, sfy[0]);
|
|
||||||
const { style } = canvas;
|
|
||||||
style.width = roundToDivide(viewport.width, sfx[1]) + "px";
|
|
||||||
style.height = roundToDivide(viewport.height, sfy[1]) + "px";
|
|
||||||
|
|
||||||
// Add the viewport so it's known what it was originally drawn with.
|
|
||||||
this.paintedViewportMap.set(canvas, viewport);
|
|
||||||
|
|
||||||
// Rendering area
|
|
||||||
const transform = outputScale.scaled
|
|
||||||
? [outputScale.sx, 0, 0, outputScale.sy, 0, 0]
|
|
||||||
: null;
|
|
||||||
const renderContext = {
|
|
||||||
canvasContext: ctx,
|
|
||||||
transform,
|
|
||||||
viewport,
|
|
||||||
annotationMode: this.#annotationMode,
|
|
||||||
optionalContentConfigPromise: this._optionalContentConfigPromise,
|
|
||||||
annotationCanvasMap: this._annotationCanvasMap,
|
|
||||||
pageColors: this.pageColors,
|
|
||||||
};
|
|
||||||
const renderTask = this.pdfPage.render(renderContext);
|
|
||||||
renderTask.onContinue = function (cont) {
|
|
||||||
showCanvas(false);
|
|
||||||
if (result.onRenderContinue) {
|
|
||||||
result.onRenderContinue(cont);
|
|
||||||
} else {
|
|
||||||
cont();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderTask.promise.then(
|
|
||||||
function () {
|
|
||||||
showCanvas(true);
|
|
||||||
renderCapability.resolve();
|
|
||||||
},
|
|
||||||
function (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);
|
|
||||||
}
|
|
||||||
renderCapability.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
paintOnSvg(wrapper) {
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new Error("Not implemented: paintOnSvg");
|
|
||||||
}
|
|
||||||
let cancelled = false;
|
|
||||||
const ensureNotCancelled = () => {
|
|
||||||
if (cancelled) {
|
|
||||||
throw new RenderingCancelledException(
|
|
||||||
`Rendering cancelled, page ${this.id}`,
|
|
||||||
"svg"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const pdfPage = this.pdfPage;
|
|
||||||
const actualSizeViewport = this.viewport.clone({
|
|
||||||
scale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
|
||||||
});
|
|
||||||
const promise = pdfPage
|
|
||||||
.getOperatorList({
|
|
||||||
annotationMode: this.#annotationMode,
|
|
||||||
})
|
|
||||||
.then(opList => {
|
|
||||||
ensureNotCancelled();
|
|
||||||
const svgGfx = new SVGGraphics(pdfPage.commonObjs, pdfPage.objs);
|
|
||||||
return svgGfx.getSVG(opList, actualSizeViewport).then(svg => {
|
|
||||||
ensureNotCancelled();
|
|
||||||
this.svg = svg;
|
|
||||||
this.paintedViewportMap.set(svg, actualSizeViewport);
|
|
||||||
|
|
||||||
svg.style.width = wrapper.style.width;
|
|
||||||
svg.style.height = wrapper.style.height;
|
|
||||||
this.renderingState = RenderingStates.FINISHED;
|
|
||||||
wrapper.append(svg);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
promise,
|
|
||||||
onRenderContinue(cont) {
|
|
||||||
cont();
|
|
||||||
},
|
|
||||||
cancel() {
|
|
||||||
cancelled = true;
|
|
||||||
},
|
|
||||||
get separateAnnots() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|null} label
|
* @param {string|null} label
|
||||||
*/
|
*/
|
||||||
|
@ -258,39 +258,39 @@ class PDFThumbnailView {
|
|||||||
reducedCanvas.height = 0;
|
reducedCanvas.height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
async #finishRenderTask(renderTask, canvas, 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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.renderingState = RenderingStates.FINISHED;
|
||||||
|
this._convertCanvasToImage(canvas);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
return Promise.resolve();
|
return undefined;
|
||||||
}
|
}
|
||||||
const { pdfPage } = this;
|
const { pdfPage } = this;
|
||||||
|
|
||||||
if (!pdfPage) {
|
if (!pdfPage) {
|
||||||
this.renderingState = RenderingStates.FINISHED;
|
this.renderingState = RenderingStates.FINISHED;
|
||||||
return Promise.reject(new Error("pdfPage is not loaded"));
|
throw new Error("pdfPage is not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderingState = RenderingStates.RUNNING;
|
this.renderingState = RenderingStates.RUNNING;
|
||||||
|
|
||||||
const finishRenderTask = async (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) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.renderingState = RenderingStates.FINISHED;
|
|
||||||
this._convertCanvasToImage(canvas);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the thumbnail at a larger size and downsize the canvas (similar
|
// Render the thumbnail at a larger size and downsize the canvas (similar
|
||||||
// to `setImage`), to improve consistency between thumbnails created by
|
// to `setImage`), to improve consistency between thumbnails created by
|
||||||
// the `draw` and `setImage` methods (fixes issue 8233).
|
// the `draw` and `setImage` methods (fixes issue 8233).
|
||||||
@ -324,12 +324,8 @@ class PDFThumbnailView {
|
|||||||
renderTask.onContinue = renderContinueCallback;
|
renderTask.onContinue = renderContinueCallback;
|
||||||
|
|
||||||
const resultPromise = renderTask.promise.then(
|
const resultPromise = renderTask.promise.then(
|
||||||
function () {
|
() => this.#finishRenderTask(renderTask, canvas),
|
||||||
return finishRenderTask(null);
|
error => this.#finishRenderTask(renderTask, canvas, error)
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
return finishRenderTask(error);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
resultPromise.finally(() => {
|
resultPromise.finally(() => {
|
||||||
// Zeroing the width and height causes Firefox to release graphics
|
// Zeroing the width and height causes Firefox to release graphics
|
||||||
|
@ -47,7 +47,6 @@ import {
|
|||||||
MAX_SCALE,
|
MAX_SCALE,
|
||||||
MIN_SCALE,
|
MIN_SCALE,
|
||||||
PresentationModeState,
|
PresentationModeState,
|
||||||
RendererType,
|
|
||||||
RenderingStates,
|
RenderingStates,
|
||||||
SCROLLBAR_PADDING,
|
SCROLLBAR_PADDING,
|
||||||
scrollIntoView,
|
scrollIntoView,
|
||||||
@ -266,7 +265,6 @@ class PDFViewer {
|
|||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
PDFJSDev.test("!PRODUCTION || GENERIC")
|
||||||
) {
|
) {
|
||||||
this.removePageBorders = options.removePageBorders || false;
|
this.removePageBorders = options.removePageBorders || false;
|
||||||
this.renderer = options.renderer || RendererType.CANVAS;
|
|
||||||
}
|
}
|
||||||
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
||||||
this.isOffscreenCanvasSupported =
|
this.isOffscreenCanvasSupported =
|
||||||
@ -775,11 +773,6 @@ class PDFViewer {
|
|||||||
textLayerMode,
|
textLayerMode,
|
||||||
annotationMode,
|
annotationMode,
|
||||||
imageResourcesPath: this.imageResourcesPath,
|
imageResourcesPath: this.imageResourcesPath,
|
||||||
renderer:
|
|
||||||
typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
? this.renderer
|
|
||||||
: null,
|
|
||||||
useOnlyCssZoom: this.useOnlyCssZoom,
|
useOnlyCssZoom: this.useOnlyCssZoom,
|
||||||
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
|
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
|
||||||
maxCanvasPixels: this.maxCanvasPixels,
|
maxCanvasPixels: this.maxCanvasPixels,
|
||||||
|
@ -46,14 +46,6 @@ const SidebarView = {
|
|||||||
LAYERS: 4,
|
LAYERS: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RendererType =
|
|
||||||
typeof PDFJSDev === "undefined" || PDFJSDev.test("!PRODUCTION || GENERIC")
|
|
||||||
? {
|
|
||||||
CANVAS: "canvas",
|
|
||||||
SVG: "svg",
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const TextLayerMode = {
|
const TextLayerMode = {
|
||||||
DISABLE: 0,
|
DISABLE: 0,
|
||||||
ENABLE: 1,
|
ENABLE: 1,
|
||||||
@ -880,7 +872,6 @@ export {
|
|||||||
PresentationModeState,
|
PresentationModeState,
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
removeNullCharacters,
|
removeNullCharacters,
|
||||||
RendererType,
|
|
||||||
RenderingStates,
|
RenderingStates,
|
||||||
roundToDivide,
|
roundToDivide,
|
||||||
SCROLLBAR_PADDING,
|
SCROLLBAR_PADDING,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user