| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 | 
							- /* 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.
 
-  */
 
- /** @typedef {import("./interfaces").IL10n} IL10n */
 
- /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
 
- /** @typedef {import("./interfaces").IRenderableView} IRenderableView */
 
- // eslint-disable-next-line max-len
 
- /** @typedef {import("./pdf_rendering_queue").PDFRenderingQueue} PDFRenderingQueue */
 
- import { OutputScale, RenderingStates } from "./ui_utils.js";
 
- import { RenderingCancelledException } from "pdfjs-lib";
 
- const DRAW_UPSCALE_FACTOR = 2; // See comment in `PDFThumbnailView.draw` below.
 
- const MAX_NUM_SCALING_STEPS = 3;
 
- const THUMBNAIL_CANVAS_BORDER_WIDTH = 1; // px
 
- const THUMBNAIL_WIDTH = 98; // px
 
- /**
 
-  * @typedef {Object} PDFThumbnailViewOptions
 
-  * @property {HTMLDivElement} container - The viewer element.
 
-  * @property {number} id - The thumbnail's unique ID (normally its number).
 
-  * @property {PageViewport} defaultViewport - The page viewport.
 
-  * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
 
-  *   A promise that is resolved with an {@link OptionalContentConfig} instance.
 
-  *   The default value is `null`.
 
-  * @property {IPDFLinkService} linkService - The navigation/linking service.
 
-  * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
 
-  * @property {IL10n} l10n - Localization service.
 
-  * @property {Object} [pageColors] - Overwrites background and foreground colors
 
-  *   with user defined ones in order to improve readability in high contrast
 
-  *   mode.
 
-  */
 
- class TempImageFactory {
 
-   static #tempCanvas = null;
 
-   static getCanvas(width, height) {
 
-     const tempCanvas = (this.#tempCanvas ||= document.createElement("canvas"));
 
-     tempCanvas.width = width;
 
-     tempCanvas.height = height;
 
-     // Since this is a temporary canvas, we need to fill it with a white
 
-     // background ourselves. `_getPageDrawContext` uses CSS rules for this.
 
-     const ctx = tempCanvas.getContext("2d", { alpha: false });
 
-     ctx.save();
 
-     ctx.fillStyle = "rgb(255, 255, 255)";
 
-     ctx.fillRect(0, 0, width, height);
 
-     ctx.restore();
 
-     return [tempCanvas, tempCanvas.getContext("2d")];
 
-   }
 
-   static destroyCanvas() {
 
-     const tempCanvas = this.#tempCanvas;
 
-     if (tempCanvas) {
 
-       // Zeroing the width and height causes Firefox to release graphics
 
-       // resources immediately, which can greatly reduce memory consumption.
 
-       tempCanvas.width = 0;
 
-       tempCanvas.height = 0;
 
-     }
 
-     this.#tempCanvas = null;
 
-   }
 
- }
 
- /**
 
-  * @implements {IRenderableView}
 
-  */
 
- class PDFThumbnailView {
 
-   /**
 
-    * @param {PDFThumbnailViewOptions} options
 
-    */
 
-   constructor({
 
-     container,
 
-     id,
 
-     defaultViewport,
 
-     optionalContentConfigPromise,
 
-     linkService,
 
-     renderingQueue,
 
-     l10n,
 
-     pageColors,
 
-   }) {
 
-     this.id = id;
 
-     this.renderingId = "thumbnail" + id;
 
-     this.pageLabel = null;
 
-     this.pdfPage = null;
 
-     this.rotation = 0;
 
-     this.viewport = defaultViewport;
 
-     this.pdfPageRotate = defaultViewport.rotation;
 
-     this._optionalContentConfigPromise = optionalContentConfigPromise || null;
 
-     this.pageColors = pageColors || null;
 
-     this.linkService = linkService;
 
-     this.renderingQueue = renderingQueue;
 
-     this.renderTask = null;
 
-     this.renderingState = RenderingStates.INITIAL;
 
-     this.resume = null;
 
-     const pageWidth = this.viewport.width,
 
-       pageHeight = this.viewport.height,
 
-       pageRatio = pageWidth / pageHeight;
 
-     this.canvasWidth = THUMBNAIL_WIDTH;
 
-     this.canvasHeight = (this.canvasWidth / pageRatio) | 0;
 
-     this.scale = this.canvasWidth / pageWidth;
 
-     this.l10n = l10n;
 
-     const anchor = document.createElement("a");
 
-     anchor.href = linkService.getAnchorUrl("#page=" + id);
 
-     this._thumbPageTitle.then(msg => {
 
-       anchor.title = msg;
 
-     });
 
-     anchor.onclick = function () {
 
-       linkService.goToPage(id);
 
-       return false;
 
-     };
 
-     this.anchor = anchor;
 
-     const div = document.createElement("div");
 
-     div.className = "thumbnail";
 
-     div.setAttribute("data-page-number", this.id);
 
-     this.div = div;
 
-     const ring = document.createElement("div");
 
-     ring.className = "thumbnailSelectionRing";
 
-     const borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
 
-     ring.style.width = this.canvasWidth + borderAdjustment + "px";
 
-     ring.style.height = this.canvasHeight + borderAdjustment + "px";
 
-     this.ring = ring;
 
-     div.append(ring);
 
-     anchor.append(div);
 
-     container.append(anchor);
 
-   }
 
-   setPdfPage(pdfPage) {
 
-     this.pdfPage = pdfPage;
 
-     this.pdfPageRotate = pdfPage.rotate;
 
-     const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
 
-     this.viewport = pdfPage.getViewport({ scale: 1, rotation: totalRotation });
 
-     this.reset();
 
-   }
 
-   reset() {
 
-     this.cancelRendering();
 
-     this.renderingState = RenderingStates.INITIAL;
 
-     const pageWidth = this.viewport.width,
 
-       pageHeight = this.viewport.height,
 
-       pageRatio = pageWidth / pageHeight;
 
-     this.canvasHeight = (this.canvasWidth / pageRatio) | 0;
 
-     this.scale = this.canvasWidth / pageWidth;
 
-     this.div.removeAttribute("data-loaded");
 
-     const ring = this.ring;
 
-     ring.textContent = ""; // Remove the thumbnail from the DOM.
 
-     const borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
 
-     ring.style.width = this.canvasWidth + borderAdjustment + "px";
 
-     ring.style.height = this.canvasHeight + borderAdjustment + "px";
 
-     if (this.canvas) {
 
-       // Zeroing the width and height causes Firefox to release graphics
 
-       // resources immediately, which can greatly reduce memory consumption.
 
-       this.canvas.width = 0;
 
-       this.canvas.height = 0;
 
-       delete this.canvas;
 
-     }
 
-     if (this.image) {
 
-       this.image.removeAttribute("src");
 
-       delete this.image;
 
-     }
 
-   }
 
-   update({ rotation = null }) {
 
-     if (typeof rotation === "number") {
 
-       this.rotation = rotation; // The rotation may be zero.
 
-     }
 
-     const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
 
-     this.viewport = this.viewport.clone({
 
-       scale: 1,
 
-       rotation: totalRotation,
 
-     });
 
-     this.reset();
 
-   }
 
-   /**
 
-    * PLEASE NOTE: Most likely you want to use the `this.reset()` method,
 
-    *              rather than calling this one directly.
 
-    */
 
-   cancelRendering() {
 
-     if (this.renderTask) {
 
-       this.renderTask.cancel();
 
-       this.renderTask = null;
 
-     }
 
-     this.resume = null;
 
-   }
 
-   /**
 
-    * @private
 
-    */
 
-   _getPageDrawContext(upscaleFactor = 1) {
 
-     // Keep the no-thumbnail outline visible, i.e. `data-loaded === false`,
 
-     // until rendering/image conversion is complete, to avoid display issues.
 
-     const canvas = document.createElement("canvas");
 
-     const ctx = canvas.getContext("2d", { alpha: false });
 
-     const outputScale = new OutputScale();
 
-     canvas.width = (upscaleFactor * this.canvasWidth * outputScale.sx) | 0;
 
-     canvas.height = (upscaleFactor * this.canvasHeight * outputScale.sy) | 0;
 
-     const transform = outputScale.scaled
 
-       ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0]
 
-       : null;
 
-     return { ctx, canvas, transform };
 
-   }
 
-   /**
 
-    * @private
 
-    */
 
-   _convertCanvasToImage(canvas) {
 
-     if (this.renderingState !== RenderingStates.FINISHED) {
 
-       throw new Error("_convertCanvasToImage: Rendering has not finished.");
 
-     }
 
-     const reducedCanvas = this._reduceImage(canvas);
 
-     const image = document.createElement("img");
 
-     image.className = "thumbnailImage";
 
-     this._thumbPageCanvas.then(msg => {
 
-       image.setAttribute("aria-label", msg);
 
-     });
 
-     image.style.width = this.canvasWidth + "px";
 
-     image.style.height = this.canvasHeight + "px";
 
-     image.src = reducedCanvas.toDataURL();
 
-     this.image = image;
 
-     this.div.setAttribute("data-loaded", true);
 
-     this.ring.append(image);
 
-     // Zeroing the width and height causes Firefox to release graphics
 
-     // resources immediately, which can greatly reduce memory consumption.
 
-     reducedCanvas.width = 0;
 
-     reducedCanvas.height = 0;
 
-   }
 
-   draw() {
 
-     if (this.renderingState !== RenderingStates.INITIAL) {
 
-       console.error("Must be in new state before drawing");
 
-       return Promise.resolve();
 
-     }
 
-     const { pdfPage } = this;
 
-     if (!pdfPage) {
 
-       this.renderingState = RenderingStates.FINISHED;
 
-       return Promise.reject(new Error("pdfPage is not loaded"));
 
-     }
 
-     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
 
-     // to `setImage`), to improve consistency between thumbnails created by
 
-     // the `draw` and `setImage` methods (fixes issue 8233).
 
-     // NOTE: To primarily avoid increasing memory usage too much, but also to
 
-     //   reduce downsizing overhead, we purposely limit the up-scaling factor.
 
-     const { ctx, canvas, transform } =
 
-       this._getPageDrawContext(DRAW_UPSCALE_FACTOR);
 
-     const drawViewport = this.viewport.clone({
 
-       scale: DRAW_UPSCALE_FACTOR * this.scale,
 
-     });
 
-     const renderContinueCallback = cont => {
 
-       if (!this.renderingQueue.isHighestPriority(this)) {
 
-         this.renderingState = RenderingStates.PAUSED;
 
-         this.resume = () => {
 
-           this.renderingState = RenderingStates.RUNNING;
 
-           cont();
 
-         };
 
-         return;
 
-       }
 
-       cont();
 
-     };
 
-     const renderContext = {
 
-       canvasContext: ctx,
 
-       transform,
 
-       viewport: drawViewport,
 
-       optionalContentConfigPromise: this._optionalContentConfigPromise,
 
-       pageColors: this.pageColors,
 
-     };
 
-     const renderTask = (this.renderTask = pdfPage.render(renderContext));
 
-     renderTask.onContinue = renderContinueCallback;
 
-     const resultPromise = renderTask.promise.then(
 
-       function () {
 
-         return finishRenderTask(null);
 
-       },
 
-       function (error) {
 
-         return finishRenderTask(error);
 
-       }
 
-     );
 
-     resultPromise.finally(() => {
 
-       // Zeroing the width and height causes Firefox to release graphics
 
-       // resources immediately, which can greatly reduce memory consumption.
 
-       canvas.width = 0;
 
-       canvas.height = 0;
 
-       // Only trigger cleanup, once rendering has finished, when the current
 
-       // pageView is *not* cached on the `BaseViewer`-instance.
 
-       const pageCached = this.linkService.isPageCached(this.id);
 
-       if (!pageCached) {
 
-         this.pdfPage?.cleanup();
 
-       }
 
-     });
 
-     return resultPromise;
 
-   }
 
-   setImage(pageView) {
 
-     if (this.renderingState !== RenderingStates.INITIAL) {
 
-       return;
 
-     }
 
-     const { thumbnailCanvas: canvas, pdfPage, scale } = pageView;
 
-     if (!canvas) {
 
-       return;
 
-     }
 
-     if (!this.pdfPage) {
 
-       this.setPdfPage(pdfPage);
 
-     }
 
-     if (scale < this.scale) {
 
-       // Avoid upscaling the image, since that makes the thumbnail look blurry.
 
-       return;
 
-     }
 
-     this.renderingState = RenderingStates.FINISHED;
 
-     this._convertCanvasToImage(canvas);
 
-   }
 
-   /**
 
-    * @private
 
-    */
 
-   _reduceImage(img) {
 
-     const { ctx, canvas } = this._getPageDrawContext();
 
-     if (img.width <= 2 * canvas.width) {
 
-       ctx.drawImage(
 
-         img,
 
-         0,
 
-         0,
 
-         img.width,
 
-         img.height,
 
-         0,
 
-         0,
 
-         canvas.width,
 
-         canvas.height
 
-       );
 
-       return canvas;
 
-     }
 
-     // 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;
 
-     const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(
 
-       reducedWidth,
 
-       reducedHeight
 
-     );
 
-     while (reducedWidth > img.width || reducedHeight > img.height) {
 
-       reducedWidth >>= 1;
 
-       reducedHeight >>= 1;
 
-     }
 
-     reducedImageCtx.drawImage(
 
-       img,
 
-       0,
 
-       0,
 
-       img.width,
 
-       img.height,
 
-       0,
 
-       0,
 
-       reducedWidth,
 
-       reducedHeight
 
-     );
 
-     while (reducedWidth > 2 * canvas.width) {
 
-       reducedImageCtx.drawImage(
 
-         reducedImage,
 
-         0,
 
-         0,
 
-         reducedWidth,
 
-         reducedHeight,
 
-         0,
 
-         0,
 
-         reducedWidth >> 1,
 
-         reducedHeight >> 1
 
-       );
 
-       reducedWidth >>= 1;
 
-       reducedHeight >>= 1;
 
-     }
 
-     ctx.drawImage(
 
-       reducedImage,
 
-       0,
 
-       0,
 
-       reducedWidth,
 
-       reducedHeight,
 
-       0,
 
-       0,
 
-       canvas.width,
 
-       canvas.height
 
-     );
 
-     return canvas;
 
-   }
 
-   get _thumbPageTitle() {
 
-     return this.l10n.get("thumb_page_title", {
 
-       page: this.pageLabel ?? this.id,
 
-     });
 
-   }
 
-   get _thumbPageCanvas() {
 
-     return this.l10n.get("thumb_page_canvas", {
 
-       page: this.pageLabel ?? this.id,
 
-     });
 
-   }
 
-   /**
 
-    * @param {string|null} label
 
-    */
 
-   setPageLabel(label) {
 
-     this.pageLabel = typeof label === "string" ? label : null;
 
-     this._thumbPageTitle.then(msg => {
 
-       this.anchor.title = msg;
 
-     });
 
-     if (this.renderingState !== RenderingStates.FINISHED) {
 
-       return;
 
-     }
 
-     this._thumbPageCanvas.then(msg => {
 
-       this.image?.setAttribute("aria-label", msg);
 
-     });
 
-   }
 
- }
 
- export { PDFThumbnailView, TempImageFactory };
 
 
  |