Skip to content

Support the maxCanvasPixels option in the thumbnails code #19635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/display/display_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,9 +640,39 @@ class OutputScale {
return this.sx !== 1 || this.sy !== 1;
}

/**
* @type {boolean} Returns `true` when scaling is symmetric,
* `false` otherwise.
*/
get symmetric() {
return this.sx === this.sy;
}

/**
* @returns {boolean} Returns `true` if scaling was limited,
* `false` otherwise.
*/
limitCanvas(width, height, maxPixels, maxDim) {
let maxAreaScale = Infinity,
maxWidthScale = Infinity,
maxHeightScale = Infinity;

if (maxPixels > 0) {
maxAreaScale = Math.sqrt(maxPixels / (width * height));
}
if (maxDim !== -1) {
maxWidthScale = maxDim / width;
maxHeightScale = maxDim / height;
}
const maxScale = Math.min(maxAreaScale, maxWidthScale, maxHeightScale);

if (this.sx > maxScale || this.sy > maxScale) {
this.sx = maxScale;
this.sy = maxScale;
return true;
}
return false;
}
}

// See https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types
Expand Down
4 changes: 3 additions & 1 deletion web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ const PDFViewerApplication = {
: null;

const enableHWA = AppOptions.get("enableHWA"),
maxCanvasPixels = AppOptions.get("maxCanvasPixels"),
maxCanvasDim = AppOptions.get("maxCanvasDim");
const pdfViewer = new PDFViewer({
container,
Expand Down Expand Up @@ -506,7 +507,7 @@ const PDFViewerApplication = {
),
imageResourcesPath: AppOptions.get("imageResourcesPath"),
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
maxCanvasPixels,
maxCanvasDim,
enableDetailCanvas: AppOptions.get("enableDetailCanvas"),
enablePermissions: AppOptions.get("enablePermissions"),
Expand All @@ -529,6 +530,7 @@ const PDFViewerApplication = {
eventBus,
renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService,
maxCanvasPixels,
maxCanvasDim,
pageColors,
abortSignal,
Expand Down
29 changes: 7 additions & 22 deletions web/pdf_page_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,28 +775,13 @@ class PDFPageView extends BasePDFPageView {
outputScale.sx *= invScale;
outputScale.sy *= invScale;
this.#needsRestrictedScaling = true;
} 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;
this.#needsRestrictedScaling = true;
} else {
this.#needsRestrictedScaling = false;
}
} else {
this.#needsRestrictedScaling = outputScale.limitCanvas(
width,
height,
this.maxCanvasPixels,
this.maxCanvasDim
);
}
}

Expand Down
60 changes: 33 additions & 27 deletions web/pdf_thumbnail_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,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} [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.
Expand Down Expand Up @@ -97,6 +100,7 @@ class PDFThumbnailView {
optionalContentConfigPromise,
linkService,
renderingQueue,
maxCanvasPixels,
maxCanvasDim,
pageColors,
enableHWA,
Expand All @@ -110,6 +114,7 @@ class PDFThumbnailView {
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this._optionalContentConfigPromise = optionalContentConfigPromise || null;
this.maxCanvasPixels = maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
this.maxCanvasDim = maxCanvasDim || AppOptions.get("maxCanvasDim");
this.pageColors = pageColors || null;
this.enableHWA = enableHWA || false;
Expand Down Expand Up @@ -218,16 +223,12 @@ class PDFThumbnailView {
const width = upscaleFactor * this.canvasWidth,
height = upscaleFactor * this.canvasHeight;

if (this.maxCanvasDim !== -1) {
const maxScale = Math.min(
this.maxCanvasDim / width,
this.maxCanvasDim / height
);
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
outputScale.sx = maxScale;
outputScale.sy = maxScale;
}
}
outputScale.limitCanvas(
width,
height,
this.maxCanvasPixels,
this.maxCanvasDim
);
canvas.width = (width * outputScale.sx) | 0;
canvas.height = (height * outputScale.sy) | 0;

Expand Down Expand Up @@ -364,6 +365,27 @@ class PDFThumbnailView {
this.#convertCanvasToImage(canvas);
}

#getReducedImageDims(canvas) {
let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS,
reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;

const outputScale = new OutputScale();
// Here we're not actually "rendering" to the canvas and the `OutputScale`
// is thus only used to limit the canvas size, hence the identity scale.
outputScale.sx = outputScale.sy = 1;

outputScale.limitCanvas(
reducedWidth,
reducedHeight,
this.maxCanvasPixels,
this.maxCanvasDim
);
reducedWidth = (reducedWidth * outputScale.sx) | 0;
reducedHeight = (reducedHeight * outputScale.sy) | 0;

return [reducedWidth, reducedHeight];
}

#reduceImage(img) {
const { ctx, canvas } = this.#getPageDrawContext(1, true);

Expand All @@ -381,24 +403,8 @@ 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;
}
}
let [reducedWidth, reducedHeight] = this.#getReducedImageDims(canvas);
const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(
reducedWidth,
reducedHeight
Expand Down
6 changes: 6 additions & 0 deletions web/pdf_thumbnail_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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} [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.
Expand All @@ -63,6 +66,7 @@ class PDFThumbnailViewer {
eventBus,
linkService,
renderingQueue,
maxCanvasPixels,
maxCanvasDim,
pageColors,
abortSignal,
Expand All @@ -72,6 +76,7 @@ class PDFThumbnailViewer {
this.eventBus = eventBus;
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.maxCanvasPixels = maxCanvasPixels;
this.maxCanvasDim = maxCanvasDim;
this.pageColors = pageColors || null;
this.enableHWA = enableHWA || false;
Expand Down Expand Up @@ -214,6 +219,7 @@ class PDFThumbnailViewer {
optionalContentConfigPromise,
linkService: this.linkService,
renderingQueue: this.renderingQueue,
maxCanvasPixels: this.maxCanvasPixels,
maxCanvasDim: this.maxCanvasDim,
pageColors: this.pageColors,
enableHWA: this.enableHWA,
Expand Down