diff --git a/web/app.js b/web/app.js index 2d23ca39a..b73c7a0ad 100644 --- a/web/app.js +++ b/web/app.js @@ -477,7 +477,8 @@ const PDFViewerApplication = { ) : null; - const enableHWA = AppOptions.get("enableHWA"); + const enableHWA = AppOptions.get("enableHWA"), + maxCanvasDim = AppOptions.get("maxCanvasDim"); const pdfViewer = new PDFViewer({ container, viewer, @@ -506,6 +507,7 @@ const PDFViewerApplication = { imageResourcesPath: AppOptions.get("imageResourcesPath"), enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"), maxCanvasPixels: AppOptions.get("maxCanvasPixels"), + maxCanvasDim, enableDetailCanvas: AppOptions.get("enableDetailCanvas"), enablePermissions: AppOptions.get("enablePermissions"), pageColors, @@ -527,6 +529,7 @@ const PDFViewerApplication = { eventBus, renderingQueue: pdfRenderingQueue, linkService: pdfLinkService, + maxCanvasDim, pageColors, abortSignal, enableHWA, diff --git a/web/app_options.js b/web/app_options.js index 6f768b316..6a671aa80 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -96,6 +96,11 @@ const defaultOptions = { : null, kind: OptionKind.BROWSER, }, + maxCanvasDim: { + /** @type {number} */ + value: 32767, + kind: OptionKind.BROWSER + OptionKind.VIEWER, + }, nimbusDataStr: { /** @type {string} */ value: "", diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 618b6b6c5..164c668fd 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -78,12 +78,14 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js"; * @property {number} [maxCanvasPixels] - The maximum supported canvas size in * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + * @property {number} [maxCanvasDim] - The maximum supported canvas dimension, + * in either width or height. Use `-1` for no limit. + * The default value is 32767. * @property {boolean} [enableDetailCanvas] - When enabled, if the rendered - * pages would need a canvas that is larger than `maxCanvasPixels`, it will - * draw a second canvas on top of the CSS-zoomed one, that only renders the - * part of the page that is close to the viewport. The default value is - * `true`. - + * pages would need a canvas that is larger than `maxCanvasPixels` or + * `maxCanvasDim`, it will draw a second canvas on top of the CSS-zoomed one, + * that only renders the part of the page that is close to the viewport. + * The default value is `true`. * @property {Object} [pageColors] - Overwrites background and foreground colors * with user defined ones in order to improve readability in high contrast * mode. @@ -185,6 +187,7 @@ class PDFPageView extends BasePDFPageView { this.enableDetailCanvas = options.enableDetailCanvas ?? true; this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels"); + this.maxCanvasDim = options.maxCanvasDim || AppOptions.get("maxCanvasDim"); this.#enableAutoLinking = options.enableAutoLinking || false; this.l10n = options.l10n; @@ -772,9 +775,21 @@ class PDFPageView extends BasePDFPageView { outputScale.sx *= invScale; outputScale.sy *= invScale; this.#needsRestrictedScaling = true; - } else if (this.maxCanvasPixels > 0) { - const pixelsInViewport = width * height; - const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport); + } else if (this.maxCanvasPixels > 0 || this.maxCanvasDim !== -1) { + let maxAreaScale = Infinity, + maxWidthScale = Infinity, + maxHeightScale = Infinity; + + if (this.maxCanvasPixels > 0) { + const pixelsInViewport = width * height; + maxAreaScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport); + } + if (this.maxCanvasDim !== -1) { + maxWidthScale = this.maxCanvasDim / width; + maxHeightScale = this.maxCanvasDim / height; + } + const maxScale = Math.min(maxAreaScale, maxWidthScale, maxHeightScale); + if (outputScale.sx > maxScale || outputScale.sy > maxScale) { outputScale.sx = maxScale; outputScale.sy = maxScale; diff --git a/web/pdf_thumbnail_view.js b/web/pdf_thumbnail_view.js index df28a7632..665c7f573 100644 --- a/web/pdf_thumbnail_view.js +++ b/web/pdf_thumbnail_view.js @@ -24,6 +24,7 @@ /** @typedef {import("./pdf_rendering_queue").PDFRenderingQueue} PDFRenderingQueue */ import { OutputScale, RenderingCancelledException } from "pdfjs-lib"; +import { AppOptions } from "./app_options.js"; import { RenderingStates } from "./ui_utils.js"; const DRAW_UPSCALE_FACTOR = 2; // See comment in `PDFThumbnailView.draw` below. @@ -41,6 +42,9 @@ const THUMBNAIL_WIDTH = 98; // px * The default value is `null`. * @property {IPDFLinkService} linkService - The navigation/linking service. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @property {number} [maxCanvasDim] - The maximum supported canvas dimension, + * in either width or height. Use `-1` for no limit. + * The default value is 32767. * @property {Object} [pageColors] - Overwrites background and foreground colors * with user defined ones in order to improve readability in high contrast * mode. @@ -93,6 +97,7 @@ class PDFThumbnailView { optionalContentConfigPromise, linkService, renderingQueue, + maxCanvasDim, pageColors, enableHWA, }) { @@ -105,6 +110,7 @@ class PDFThumbnailView { this.viewport = defaultViewport; this.pdfPageRotate = defaultViewport.rotation; this._optionalContentConfigPromise = optionalContentConfigPromise || null; + this.maxCanvasDim = maxCanvasDim || AppOptions.get("maxCanvasDim"); this.pageColors = pageColors || null; this.enableHWA = enableHWA || false; @@ -363,9 +369,24 @@ class PDFThumbnailView { ); return canvas; } + const { maxCanvasDim } = this; + // drawImage does an awful job of rescaling the image, doing it gradually. let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS; let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS; + + if (maxCanvasDim !== -1) { + const maxWidthScale = maxCanvasDim / reducedWidth, + maxHeightScale = maxCanvasDim / reducedHeight; + + if (maxWidthScale < 1) { + reducedWidth = maxCanvasDim; + reducedHeight = (reducedHeight * maxWidthScale) | 0; + } else if (maxHeightScale < 1) { + reducedWidth = (reducedWidth * maxHeightScale) | 0; + reducedHeight = maxCanvasDim; + } + } const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas( reducedWidth, reducedHeight diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js index b6bdf36f3..d0a5eb576 100644 --- a/web/pdf_thumbnail_viewer.js +++ b/web/pdf_thumbnail_viewer.js @@ -39,6 +39,9 @@ const THUMBNAIL_SELECTED_CLASS = "selected"; * @property {EventBus} eventBus - The application event bus. * @property {IPDFLinkService} linkService - The navigation/linking service. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object. + * @property {number} [maxCanvasDim] - The maximum supported canvas dimension, + * in either width or height. Use `-1` for no limit. + * The default value is 32767. * @property {Object} [pageColors] - Overwrites background and foreground colors * with user defined ones in order to improve readability in high contrast * mode. @@ -60,6 +63,7 @@ class PDFThumbnailViewer { eventBus, linkService, renderingQueue, + maxCanvasDim, pageColors, abortSignal, enableHWA, @@ -68,6 +72,7 @@ class PDFThumbnailViewer { this.eventBus = eventBus; this.linkService = linkService; this.renderingQueue = renderingQueue; + this.maxCanvasDim = maxCanvasDim; this.pageColors = pageColors || null; this.enableHWA = enableHWA || false; @@ -209,6 +214,7 @@ class PDFThumbnailViewer { optionalContentConfigPromise, linkService: this.linkService, renderingQueue: this.renderingQueue, + maxCanvasDim: this.maxCanvasDim, pageColors: this.pageColors, enableHWA: this.enableHWA, }); diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index f0ef305d5..adc47cbc3 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -118,11 +118,14 @@ function isValidAnnotationEditorMode(mode) { * @property {number} [maxCanvasPixels] - The maximum supported canvas size in * total pixels, i.e. width * height. Use `-1` for no limit, or `0` for * CSS-only zooming. The default value is 4096 * 8192 (32 mega-pixels). + * @property {number} [maxCanvasDim] - The maximum supported canvas dimension, + * in either width or height. Use `-1` for no limit. + * The default value is 32767. * @property {boolean} [enableDetailCanvas] - When enabled, if the rendered - * pages would need a canvas that is larger than `maxCanvasPixels`, it will - * draw a second canvas on top of the CSS-zoomed one, that only renders the - * part of the page that is close to the viewport. The default value is - * `true`. + * pages would need a canvas that is larger than `maxCanvasPixels` or + * `maxCanvasDim`, it will draw a second canvas on top of the CSS-zoomed one, + * that only renders the part of the page that is close to the viewport. + * The default value is `true`. * @property {IL10n} [l10n] - Localization service. * @property {boolean} [enablePermissions] - Enables PDF document permissions, * when they exist. The default value is `false`. @@ -326,6 +329,7 @@ class PDFViewer { this.removePageBorders = options.removePageBorders || false; } this.maxCanvasPixels = options.maxCanvasPixels; + this.maxCanvasDim = options.maxCanvasDim; this.enableDetailCanvas = options.enableDetailCanvas ?? true; this.l10n = options.l10n; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { @@ -1001,6 +1005,7 @@ class PDFViewer { annotationMode, imageResourcesPath: this.imageResourcesPath, maxCanvasPixels: this.maxCanvasPixels, + maxCanvasDim: this.maxCanvasDim, enableDetailCanvas: this.enableDetailCanvas, pageColors, l10n: this.l10n,