@vaadin-component-factory/vcf-pdf-viewer 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
- import { getOutputScale, watchScroll, getVisibleElements, scrollIntoView, isValidRotation } from './ui_utils.js';
1
+ import { RenderingStates, OutputScale, watchScroll, getVisibleElements, scrollIntoView, isValidRotation } from './ui_utils.js';
2
2
  import './pdf.js';
3
- import { RenderingStates } from './pdf_rendering_queue.js';
4
- import { R as RenderingCancelledException } from './display_utils.js';
3
+ import { R as RenderingCancelledException } from './node_stream2.js';
5
4
  import './util.js';
6
5
  import './message_handler.js';
7
6
 
@@ -19,16 +18,15 @@ import './message_handler.js';
19
18
  * See the License for the specific language governing permissions and
20
19
  * limitations under the License.
21
20
  */
22
- const DRAW_UPSCALE_FACTOR = 2; // See comment in `PDFThumbnailView.draw` below.
23
21
 
22
+ const DRAW_UPSCALE_FACTOR = 2; // See comment in `PDFThumbnailView.draw` below.
24
23
  const MAX_NUM_SCALING_STEPS = 3;
25
- const THUMBNAIL_CANVAS_BORDER_WIDTH = 1; // px
26
-
27
24
  const THUMBNAIL_WIDTH = 98; // px
28
25
 
29
26
  /**
30
27
  * @typedef {Object} PDFThumbnailViewOptions
31
28
  * @property {HTMLDivElement} container - The viewer element.
29
+ * @property {EventBus} eventBus - The application event bus.
32
30
  * @property {number} id - The thumbnail's unique ID (normally its number).
33
31
  * @property {PageViewport} defaultViewport - The page viewport.
34
32
  * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
@@ -36,72 +34,57 @@ const THUMBNAIL_WIDTH = 98; // px
36
34
  * The default value is `null`.
37
35
  * @property {IPDFLinkService} linkService - The navigation/linking service.
38
36
  * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
39
- * @property {function} checkSetImageDisabled
40
- * @property {IL10n} l10n - Localization service.
37
+ * @property {Object} [pageColors] - Overwrites background and foreground colors
38
+ * with user defined ones in order to improve readability in high contrast
39
+ * mode.
41
40
  */
42
41
 
43
- const TempImageFactory = function TempImageFactoryClosure() {
44
- let tempCanvasCache = null;
45
- return {
46
- getCanvas(width, height) {
47
- let tempCanvas = tempCanvasCache;
48
-
49
- if (!tempCanvas) {
50
- tempCanvas = document.createElement("canvas");
51
- tempCanvasCache = tempCanvas;
52
- }
53
-
54
- tempCanvas.width = width;
55
- tempCanvas.height = height; // Since this is a temporary canvas, we need to fill it with a white
56
- // background ourselves. `_getPageDrawContext` uses CSS rules for this.
57
-
58
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL || GENERIC")) {
59
- tempCanvas.mozOpaque = true;
60
- }
61
-
62
- const ctx = tempCanvas.getContext("2d", {
63
- alpha: false
64
- });
65
- ctx.save();
66
- ctx.fillStyle = "rgb(255, 255, 255)";
67
- ctx.fillRect(0, 0, width, height);
68
- ctx.restore();
69
- return [tempCanvas, tempCanvas.getContext("2d")];
70
- },
71
-
72
- destroyCanvas() {
73
- const tempCanvas = tempCanvasCache;
42
+ class TempImageFactory {
43
+ static #tempCanvas = null;
44
+ static getCanvas(width, height) {
45
+ const tempCanvas = this.#tempCanvas || (this.#tempCanvas = document.createElement("canvas"));
46
+ tempCanvas.width = width;
47
+ tempCanvas.height = height;
74
48
 
75
- if (tempCanvas) {
76
- // Zeroing the width and height causes Firefox to release graphics
77
- // resources immediately, which can greatly reduce memory consumption.
78
- tempCanvas.width = 0;
79
- tempCanvas.height = 0;
80
- }
81
-
82
- tempCanvasCache = null;
49
+ // Since this is a temporary canvas, we need to fill it with a white
50
+ // background ourselves. `#getPageDrawContext` uses CSS rules for this.
51
+ const ctx = tempCanvas.getContext("2d", {
52
+ alpha: false
53
+ });
54
+ ctx.save();
55
+ ctx.fillStyle = "rgb(255, 255, 255)";
56
+ ctx.fillRect(0, 0, width, height);
57
+ ctx.restore();
58
+ return [tempCanvas, tempCanvas.getContext("2d")];
59
+ }
60
+ static destroyCanvas() {
61
+ const tempCanvas = this.#tempCanvas;
62
+ if (tempCanvas) {
63
+ // Zeroing the width and height causes Firefox to release graphics
64
+ // resources immediately, which can greatly reduce memory consumption.
65
+ tempCanvas.width = 0;
66
+ tempCanvas.height = 0;
83
67
  }
68
+ this.#tempCanvas = null;
69
+ }
70
+ }
84
71
 
85
- };
86
- }();
87
72
  /**
88
73
  * @implements {IRenderableView}
89
74
  */
90
-
91
-
92
75
  class PDFThumbnailView {
93
76
  /**
94
77
  * @param {PDFThumbnailViewOptions} options
95
78
  */
96
79
  constructor({
97
80
  container,
81
+ eventBus,
98
82
  id,
99
83
  defaultViewport,
100
84
  optionalContentConfigPromise,
101
85
  linkService,
102
86
  renderingQueue,
103
- checkSetImageDisabled,
104
- l10n
87
+ pageColors
105
88
  }) {
106
89
  this.id = id;
107
90
  this.renderingId = "thumbnail" + id;
@@ -111,51 +94,49 @@ class PDFThumbnailView {
111
94
  this.viewport = defaultViewport;
112
95
  this.pdfPageRotate = defaultViewport.rotation;
113
96
  this._optionalContentConfigPromise = optionalContentConfigPromise || null;
97
+ this.pageColors = pageColors || null;
98
+ this.eventBus = eventBus;
114
99
  this.linkService = linkService;
115
100
  this.renderingQueue = renderingQueue;
116
101
  this.renderTask = null;
117
102
  this.renderingState = RenderingStates.INITIAL;
118
103
  this.resume = null;
119
-
120
- this._checkSetImageDisabled = checkSetImageDisabled || function () {
121
- return false;
122
- };
123
-
124
- const pageWidth = this.viewport.width,
125
- pageHeight = this.viewport.height,
126
- pageRatio = pageWidth / pageHeight;
127
- this.canvasWidth = THUMBNAIL_WIDTH;
128
- this.canvasHeight = this.canvasWidth / pageRatio | 0;
129
- this.scale = this.canvasWidth / pageWidth;
130
- this.l10n = l10n;
131
104
  const anchor = document.createElement("a");
132
105
  anchor.href = linkService.getAnchorUrl("#page=" + id);
133
-
134
- this._thumbPageTitle.then(msg => {
135
- anchor.title = msg;
136
- });
137
-
106
+ anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title");
107
+ anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
138
108
  anchor.onclick = function () {
139
109
  linkService.goToPage(id);
140
110
  return false;
141
111
  };
142
-
143
112
  this.anchor = anchor;
144
113
  const div = document.createElement("div");
145
114
  div.className = "thumbnail";
146
115
  div.setAttribute("data-page-number", this.id);
147
116
  this.div = div;
148
- const ring = document.createElement("div");
149
- ring.className = "thumbnailSelectionRing";
150
- const borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
151
- ring.style.width = this.canvasWidth + borderAdjustment + "px";
152
- ring.style.height = this.canvasHeight + borderAdjustment + "px";
153
- this.ring = ring;
154
- div.appendChild(ring);
155
- anchor.appendChild(div);
156
- container.appendChild(anchor);
117
+ this.#updateDims();
118
+ const img = document.createElement("div");
119
+ img.className = "thumbnailImage";
120
+ this._placeholderImg = img;
121
+ div.append(img);
122
+ anchor.append(div);
123
+ container.append(anchor);
124
+ }
125
+ #updateDims() {
126
+ const {
127
+ width,
128
+ height
129
+ } = this.viewport;
130
+ const ratio = width / height;
131
+ this.canvasWidth = THUMBNAIL_WIDTH;
132
+ this.canvasHeight = this.canvasWidth / ratio | 0;
133
+ this.scale = this.canvasWidth / width;
134
+ const {
135
+ style
136
+ } = this.div;
137
+ style.setProperty("--thumbnail-width", `${this.canvasWidth}px`);
138
+ style.setProperty("--thumbnail-height", `${this.canvasHeight}px`);
157
139
  }
158
-
159
140
  setPdfPage(pdfPage) {
160
141
  this.pdfPage = pdfPage;
161
142
  this.pdfPageRotate = pdfPage.rotate;
@@ -166,42 +147,24 @@ class PDFThumbnailView {
166
147
  });
167
148
  this.reset();
168
149
  }
169
-
170
150
  reset() {
151
+ var _this$image;
171
152
  this.cancelRendering();
172
153
  this.renderingState = RenderingStates.INITIAL;
173
- const pageWidth = this.viewport.width,
174
- pageHeight = this.viewport.height,
175
- pageRatio = pageWidth / pageHeight;
176
- this.canvasHeight = this.canvasWidth / pageRatio | 0;
177
- this.scale = this.canvasWidth / pageWidth;
178
154
  this.div.removeAttribute("data-loaded");
179
- const ring = this.ring;
180
- ring.textContent = ""; // Remove the thumbnail from the DOM.
181
-
182
- const borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
183
- ring.style.width = this.canvasWidth + borderAdjustment + "px";
184
- ring.style.height = this.canvasHeight + borderAdjustment + "px";
185
-
186
- if (this.canvas) {
187
- // Zeroing the width and height causes Firefox to release graphics
188
- // resources immediately, which can greatly reduce memory consumption.
189
- this.canvas.width = 0;
190
- this.canvas.height = 0;
191
- delete this.canvas;
192
- }
193
-
155
+ (_this$image = this.image) === null || _this$image === void 0 ? void 0 : _this$image.replaceWith(this._placeholderImg);
156
+ this.#updateDims();
194
157
  if (this.image) {
195
158
  this.image.removeAttribute("src");
196
159
  delete this.image;
197
160
  }
198
161
  }
199
-
200
- update(rotation) {
201
- if (typeof rotation !== "undefined") {
202
- this.rotation = rotation;
162
+ update({
163
+ rotation = null
164
+ }) {
165
+ if (typeof rotation === "number") {
166
+ this.rotation = rotation; // The rotation may be zero.
203
167
  }
204
-
205
168
  const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
206
169
  this.viewport = this.viewport.clone({
207
170
  scale: 1,
@@ -209,38 +172,26 @@ class PDFThumbnailView {
209
172
  });
210
173
  this.reset();
211
174
  }
175
+
212
176
  /**
213
177
  * PLEASE NOTE: Most likely you want to use the `this.reset()` method,
214
178
  * rather than calling this one directly.
215
179
  */
216
-
217
-
218
180
  cancelRendering() {
219
181
  if (this.renderTask) {
220
182
  this.renderTask.cancel();
221
183
  this.renderTask = null;
222
184
  }
223
-
224
185
  this.resume = null;
225
186
  }
226
- /**
227
- * @private
228
- */
229
-
230
-
231
- _getPageDrawContext(upscaleFactor = 1) {
187
+ #getPageDrawContext(upscaleFactor = 1) {
232
188
  // Keep the no-thumbnail outline visible, i.e. `data-loaded === false`,
233
189
  // until rendering/image conversion is complete, to avoid display issues.
234
190
  const canvas = document.createElement("canvas");
235
-
236
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL || GENERIC")) {
237
- canvas.mozOpaque = true;
238
- }
239
-
240
191
  const ctx = canvas.getContext("2d", {
241
192
  alpha: false
242
193
  });
243
- const outputScale = getOutputScale(ctx);
194
+ const outputScale = new OutputScale();
244
195
  canvas.width = upscaleFactor * this.canvasWidth * outputScale.sx | 0;
245
196
  canvas.height = upscaleFactor * this.canvasHeight * outputScale.sy | 0;
246
197
  const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null;
@@ -250,238 +201,169 @@ class PDFThumbnailView {
250
201
  transform
251
202
  };
252
203
  }
253
- /**
254
- * @private
255
- */
256
-
257
-
258
- _convertCanvasToImage(canvas) {
204
+ #convertCanvasToImage(canvas) {
259
205
  if (this.renderingState !== RenderingStates.FINISHED) {
260
- throw new Error("_convertCanvasToImage: Rendering has not finished.");
206
+ throw new Error("#convertCanvasToImage: Rendering has not finished.");
261
207
  }
262
-
263
- const reducedCanvas = this._reduceImage(canvas);
264
-
208
+ const reducedCanvas = this.#reduceImage(canvas);
265
209
  const image = document.createElement("img");
266
210
  image.className = "thumbnailImage";
267
-
268
- this._thumbPageCanvas.then(msg => {
269
- image.setAttribute("aria-label", msg);
270
- });
271
-
272
- image.style.width = this.canvasWidth + "px";
273
- image.style.height = this.canvasHeight + "px";
211
+ image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas");
212
+ image.setAttribute("data-l10n-args", this.#pageL10nArgs);
274
213
  image.src = reducedCanvas.toDataURL();
275
214
  this.image = image;
276
215
  this.div.setAttribute("data-loaded", true);
277
- this.ring.appendChild(image); // Zeroing the width and height causes Firefox to release graphics
278
- // resources immediately, which can greatly reduce memory consumption.
216
+ this._placeholderImg.replaceWith(image);
279
217
 
218
+ // Zeroing the width and height causes Firefox to release graphics
219
+ // resources immediately, which can greatly reduce memory consumption.
280
220
  reducedCanvas.width = 0;
281
221
  reducedCanvas.height = 0;
282
222
  }
283
-
284
- draw() {
223
+ async #finishRenderTask(renderTask, canvas, error = null) {
224
+ // The renderTask may have been replaced by a new one, so only remove
225
+ // the reference to the renderTask if it matches the one that is
226
+ // triggering this callback.
227
+ if (renderTask === this.renderTask) {
228
+ this.renderTask = null;
229
+ }
230
+ if (error instanceof RenderingCancelledException) {
231
+ return;
232
+ }
233
+ this.renderingState = RenderingStates.FINISHED;
234
+ this.#convertCanvasToImage(canvas);
235
+ if (error) {
236
+ throw error;
237
+ }
238
+ }
239
+ async draw() {
285
240
  if (this.renderingState !== RenderingStates.INITIAL) {
286
241
  console.error("Must be in new state before drawing");
287
- return Promise.resolve(undefined);
242
+ return undefined;
288
243
  }
289
-
290
244
  const {
291
245
  pdfPage
292
246
  } = this;
293
-
294
247
  if (!pdfPage) {
295
248
  this.renderingState = RenderingStates.FINISHED;
296
- return Promise.reject(new Error("pdfPage is not loaded"));
249
+ throw new Error("pdfPage is not loaded");
297
250
  }
298
-
299
251
  this.renderingState = RenderingStates.RUNNING;
300
252
 
301
- const finishRenderTask = async (error = null) => {
302
- // The renderTask may have been replaced by a new one, so only remove
303
- // the reference to the renderTask if it matches the one that is
304
- // triggering this callback.
305
- if (renderTask === this.renderTask) {
306
- this.renderTask = null;
307
- }
308
-
309
- if (error instanceof RenderingCancelledException) {
310
- return;
311
- }
312
-
313
- this.renderingState = RenderingStates.FINISHED;
314
-
315
- this._convertCanvasToImage(canvas);
316
-
317
- if (error) {
318
- throw error;
319
- }
320
- }; // Render the thumbnail at a larger size and downsize the canvas (similar
253
+ // Render the thumbnail at a larger size and downsize the canvas (similar
321
254
  // to `setImage`), to improve consistency between thumbnails created by
322
255
  // the `draw` and `setImage` methods (fixes issue 8233).
323
256
  // NOTE: To primarily avoid increasing memory usage too much, but also to
324
257
  // reduce downsizing overhead, we purposely limit the up-scaling factor.
325
-
326
-
327
258
  const {
328
259
  ctx,
329
260
  canvas,
330
261
  transform
331
- } = this._getPageDrawContext(DRAW_UPSCALE_FACTOR);
332
-
262
+ } = this.#getPageDrawContext(DRAW_UPSCALE_FACTOR);
333
263
  const drawViewport = this.viewport.clone({
334
264
  scale: DRAW_UPSCALE_FACTOR * this.scale
335
265
  });
336
-
337
266
  const renderContinueCallback = cont => {
338
267
  if (!this.renderingQueue.isHighestPriority(this)) {
339
268
  this.renderingState = RenderingStates.PAUSED;
340
-
341
269
  this.resume = () => {
342
270
  this.renderingState = RenderingStates.RUNNING;
343
271
  cont();
344
272
  };
345
-
346
273
  return;
347
274
  }
348
-
349
275
  cont();
350
276
  };
351
-
352
277
  const renderContext = {
353
278
  canvasContext: ctx,
354
279
  transform,
355
280
  viewport: drawViewport,
356
- optionalContentConfigPromise: this._optionalContentConfigPromise
281
+ optionalContentConfigPromise: this._optionalContentConfigPromise,
282
+ pageColors: this.pageColors
357
283
  };
358
284
  const renderTask = this.renderTask = pdfPage.render(renderContext);
359
285
  renderTask.onContinue = renderContinueCallback;
360
- const resultPromise = renderTask.promise.then(function () {
361
- return finishRenderTask(null);
362
- }, function (error) {
363
- return finishRenderTask(error);
364
- });
286
+ const resultPromise = renderTask.promise.then(() => this.#finishRenderTask(renderTask, canvas), error => this.#finishRenderTask(renderTask, canvas, error));
365
287
  resultPromise.finally(() => {
366
288
  // Zeroing the width and height causes Firefox to release graphics
367
289
  // resources immediately, which can greatly reduce memory consumption.
368
290
  canvas.width = 0;
369
- canvas.height = 0; // Only trigger cleanup, once rendering has finished, when the current
370
- // pageView is *not* cached on the `BaseViewer`-instance.
371
-
372
- const pageCached = this.linkService.isPageCached(this.id);
373
-
374
- if (!pageCached) {
375
- var _this$pdfPage;
376
-
377
- (_this$pdfPage = this.pdfPage) === null || _this$pdfPage === void 0 ? void 0 : _this$pdfPage.cleanup();
378
- }
291
+ canvas.height = 0;
292
+ this.eventBus.dispatch("thumbnailrendered", {
293
+ source: this,
294
+ pageNumber: this.id,
295
+ pdfPage: this.pdfPage
296
+ });
379
297
  });
380
298
  return resultPromise;
381
299
  }
382
-
383
300
  setImage(pageView) {
384
- if (this._checkSetImageDisabled()) {
385
- return;
386
- }
387
-
388
301
  if (this.renderingState !== RenderingStates.INITIAL) {
389
302
  return;
390
303
  }
391
-
392
304
  const {
393
- canvas,
394
- pdfPage
305
+ thumbnailCanvas: canvas,
306
+ pdfPage,
307
+ scale
395
308
  } = pageView;
396
-
397
309
  if (!canvas) {
398
310
  return;
399
311
  }
400
-
401
312
  if (!this.pdfPage) {
402
313
  this.setPdfPage(pdfPage);
403
314
  }
404
-
315
+ if (scale < this.scale) {
316
+ // Avoid upscaling the image, since that makes the thumbnail look blurry.
317
+ return;
318
+ }
405
319
  this.renderingState = RenderingStates.FINISHED;
406
-
407
- this._convertCanvasToImage(canvas);
320
+ this.#convertCanvasToImage(canvas);
408
321
  }
409
- /**
410
- * @private
411
- */
412
-
413
-
414
- _reduceImage(img) {
322
+ #reduceImage(img) {
415
323
  const {
416
324
  ctx,
417
325
  canvas
418
- } = this._getPageDrawContext();
419
-
326
+ } = this.#getPageDrawContext();
420
327
  if (img.width <= 2 * canvas.width) {
421
328
  ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
422
329
  return canvas;
423
- } // drawImage does an awful job of rescaling the image, doing it gradually.
424
-
425
-
330
+ }
331
+ // drawImage does an awful job of rescaling the image, doing it gradually.
426
332
  let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
427
333
  let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
428
334
  const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
429
-
430
335
  while (reducedWidth > img.width || reducedHeight > img.height) {
431
336
  reducedWidth >>= 1;
432
337
  reducedHeight >>= 1;
433
338
  }
434
-
435
339
  reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
436
-
437
340
  while (reducedWidth > 2 * canvas.width) {
438
341
  reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
439
342
  reducedWidth >>= 1;
440
343
  reducedHeight >>= 1;
441
344
  }
442
-
443
345
  ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
444
346
  return canvas;
445
347
  }
446
-
447
- get _thumbPageTitle() {
348
+ get #pageL10nArgs() {
448
349
  var _this$pageLabel;
449
-
450
- return this.l10n.get("thumb_page_title", {
350
+ return JSON.stringify({
451
351
  page: (_this$pageLabel = this.pageLabel) !== null && _this$pageLabel !== void 0 ? _this$pageLabel : this.id
452
352
  });
453
353
  }
454
354
 
455
- get _thumbPageCanvas() {
456
- var _this$pageLabel2;
457
-
458
- return this.l10n.get("thumb_page_canvas", {
459
- page: (_this$pageLabel2 = this.pageLabel) !== null && _this$pageLabel2 !== void 0 ? _this$pageLabel2 : this.id
460
- });
461
- }
462
355
  /**
463
356
  * @param {string|null} label
464
357
  */
465
-
466
-
467
358
  setPageLabel(label) {
359
+ var _this$image2;
468
360
  this.pageLabel = typeof label === "string" ? label : null;
469
-
470
- this._thumbPageTitle.then(msg => {
471
- this.anchor.title = msg;
472
- });
473
-
361
+ this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
474
362
  if (this.renderingState !== RenderingStates.FINISHED) {
475
363
  return;
476
364
  }
477
-
478
- this._thumbPageCanvas.then(msg => {
479
- var _this$image;
480
-
481
- (_this$image = this.image) === null || _this$image === void 0 ? void 0 : _this$image.setAttribute("aria-label", msg);
482
- });
365
+ (_this$image2 = this.image) === null || _this$image2 === void 0 ? void 0 : _this$image2.setAttribute("data-l10n-args", this.#pageL10nArgs);
483
366
  }
484
-
485
367
  }
486
368
 
487
369
  /* Copyright 2012 Mozilla Foundation
@@ -498,8 +380,10 @@ class PDFThumbnailView {
498
380
  * See the License for the specific language governing permissions and
499
381
  * limitations under the License.
500
382
  */
383
+
501
384
  const THUMBNAIL_SCROLL_MARGIN = -19;
502
385
  const THUMBNAIL_SELECTED_CLASS = "selected";
386
+
503
387
  /**
504
388
  * @typedef {Object} PDFThumbnailViewerOptions
505
389
  * @property {HTMLDivElement} container - The container for the thumbnail
@@ -507,15 +391,14 @@ const THUMBNAIL_SELECTED_CLASS = "selected";
507
391
  * @property {EventBus} eventBus - The application event bus.
508
392
  * @property {IPDFLinkService} linkService - The navigation/linking service.
509
393
  * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
510
- * @property {IL10n} l10n - Localization service.
394
+ * @property {Object} [pageColors] - Overwrites background and foreground colors
395
+ * with user defined ones in order to improve readability in high contrast
396
+ * mode.
511
397
  */
512
398
 
513
399
  /**
514
400
  * Viewer control to display thumbnails for pages in a PDF document.
515
- *
516
- * @implements {IRenderableView}
517
401
  */
518
-
519
402
  class PDFThumbnailViewer {
520
403
  /**
521
404
  * @param {PDFThumbnailViewerOptions} options
@@ -525,228 +408,174 @@ class PDFThumbnailViewer {
525
408
  eventBus,
526
409
  linkService,
527
410
  renderingQueue,
528
- l10n
411
+ pageColors
529
412
  }) {
530
413
  this.container = container;
414
+ this.eventBus = eventBus;
531
415
  this.linkService = linkService;
532
416
  this.renderingQueue = renderingQueue;
533
- this.l10n = l10n;
534
- this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this));
535
-
536
- this._resetView();
537
-
538
- eventBus._on("optionalcontentconfigchanged", () => {
539
- // Ensure that the thumbnails always render with the *default* optional
540
- // content configuration.
541
- this._setImageDisabled = true;
542
- });
417
+ this.pageColors = pageColors || null;
418
+ this.scroll = watchScroll(this.container, this.#scrollUpdated.bind(this));
419
+ this.#resetView();
543
420
  }
544
- /**
545
- * @private
546
- */
547
-
548
-
549
- _scrollUpdated() {
421
+ #scrollUpdated() {
550
422
  this.renderingQueue.renderHighestPriority();
551
423
  }
552
-
553
424
  getThumbnail(index) {
554
425
  return this._thumbnails[index];
555
426
  }
556
- /**
557
- * @private
558
- */
559
-
560
-
561
- _getVisibleThumbs() {
427
+ #getVisibleThumbs() {
562
428
  return getVisibleElements({
563
429
  scrollEl: this.container,
564
430
  views: this._thumbnails
565
431
  });
566
432
  }
567
-
568
433
  scrollThumbnailIntoView(pageNumber) {
569
434
  if (!this.pdfDocument) {
570
435
  return;
571
436
  }
572
-
573
437
  const thumbnailView = this._thumbnails[pageNumber - 1];
574
-
575
438
  if (!thumbnailView) {
576
439
  console.error('scrollThumbnailIntoView: Invalid "pageNumber" parameter.');
577
440
  return;
578
441
  }
579
-
580
442
  if (pageNumber !== this._currentPageNumber) {
581
- const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1]; // Remove the highlight from the previous thumbnail...
582
-
583
- prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS); // ... and add the highlight to the new thumbnail.
584
-
443
+ const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1];
444
+ // Remove the highlight from the previous thumbnail...
445
+ prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS);
446
+ // ... and add the highlight to the new thumbnail.
585
447
  thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
586
448
  }
449
+ const {
450
+ first,
451
+ last,
452
+ views
453
+ } = this.#getVisibleThumbs();
587
454
 
588
- const visibleThumbs = this._getVisibleThumbs();
589
-
590
- const numVisibleThumbs = visibleThumbs.views.length; // If the thumbnail isn't currently visible, scroll it into view.
591
-
592
- if (numVisibleThumbs > 0) {
593
- const first = visibleThumbs.first.id; // Account for only one thumbnail being visible.
594
-
595
- const last = numVisibleThumbs > 1 ? visibleThumbs.last.id : first;
455
+ // If the thumbnail isn't currently visible, scroll it into view.
456
+ if (views.length > 0) {
596
457
  let shouldScroll = false;
597
-
598
- if (pageNumber <= first || pageNumber >= last) {
458
+ if (pageNumber <= first.id || pageNumber >= last.id) {
599
459
  shouldScroll = true;
600
460
  } else {
601
- visibleThumbs.views.some(function (view) {
602
- if (view.id !== pageNumber) {
603
- return false;
461
+ for (const {
462
+ id,
463
+ percent
464
+ } of views) {
465
+ if (id !== pageNumber) {
466
+ continue;
604
467
  }
605
-
606
- shouldScroll = view.percent < 100;
607
- return true;
608
- });
468
+ shouldScroll = percent < 100;
469
+ break;
470
+ }
609
471
  }
610
-
611
472
  if (shouldScroll) {
612
473
  scrollIntoView(thumbnailView.div, {
613
474
  top: THUMBNAIL_SCROLL_MARGIN
614
475
  });
615
476
  }
616
477
  }
617
-
618
478
  this._currentPageNumber = pageNumber;
619
479
  }
620
-
621
480
  get pagesRotation() {
622
481
  return this._pagesRotation;
623
482
  }
624
-
625
483
  set pagesRotation(rotation) {
626
484
  if (!isValidRotation(rotation)) {
627
485
  throw new Error("Invalid thumbnails rotation angle.");
628
486
  }
629
-
630
487
  if (!this.pdfDocument) {
631
488
  return;
632
489
  }
633
-
634
490
  if (this._pagesRotation === rotation) {
635
491
  return; // The rotation didn't change.
636
492
  }
637
-
638
493
  this._pagesRotation = rotation;
639
-
640
- for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
641
- this._thumbnails[i].update(rotation);
494
+ const updateArgs = {
495
+ rotation
496
+ };
497
+ for (const thumbnail of this._thumbnails) {
498
+ thumbnail.update(updateArgs);
642
499
  }
643
500
  }
644
-
645
501
  cleanup() {
646
- for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
647
- if (this._thumbnails[i] && this._thumbnails[i].renderingState !== RenderingStates.FINISHED) {
648
- this._thumbnails[i].reset();
502
+ for (const thumbnail of this._thumbnails) {
503
+ if (thumbnail.renderingState !== RenderingStates.FINISHED) {
504
+ thumbnail.reset();
649
505
  }
650
506
  }
651
-
652
507
  TempImageFactory.destroyCanvas();
653
508
  }
654
- /**
655
- * @private
656
- */
657
-
658
-
659
- _resetView() {
509
+ #resetView() {
660
510
  this._thumbnails = [];
661
511
  this._currentPageNumber = 1;
662
512
  this._pageLabels = null;
663
513
  this._pagesRotation = 0;
664
- this._optionalContentConfigPromise = null;
665
- this._pagesRequests = new WeakMap();
666
- this._setImageDisabled = false; // Remove the thumbnails from the DOM.
667
514
 
515
+ // Remove the thumbnails from the DOM.
668
516
  this.container.textContent = "";
669
517
  }
670
518
 
519
+ /**
520
+ * @param {PDFDocumentProxy} pdfDocument
521
+ */
671
522
  setDocument(pdfDocument) {
672
523
  if (this.pdfDocument) {
673
- this._cancelRendering();
674
-
675
- this._resetView();
524
+ this.#cancelRendering();
525
+ this.#resetView();
676
526
  }
677
-
678
527
  this.pdfDocument = pdfDocument;
679
-
680
528
  if (!pdfDocument) {
681
529
  return;
682
530
  }
683
-
684
531
  const firstPagePromise = pdfDocument.getPage(1);
685
- const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
532
+ const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
533
+ intent: "display"
534
+ });
686
535
  firstPagePromise.then(firstPdfPage => {
687
- this._optionalContentConfigPromise = optionalContentConfigPromise;
536
+ var _this$_thumbnails$;
688
537
  const pagesCount = pdfDocument.numPages;
689
538
  const viewport = firstPdfPage.getViewport({
690
539
  scale: 1
691
540
  });
692
-
693
- const checkSetImageDisabled = () => {
694
- return this._setImageDisabled;
695
- };
696
-
697
541
  for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
698
542
  const thumbnail = new PDFThumbnailView({
699
543
  container: this.container,
544
+ eventBus: this.eventBus,
700
545
  id: pageNum,
701
546
  defaultViewport: viewport.clone(),
702
547
  optionalContentConfigPromise,
703
548
  linkService: this.linkService,
704
549
  renderingQueue: this.renderingQueue,
705
- checkSetImageDisabled,
706
- l10n: this.l10n
550
+ pageColors: this.pageColors
707
551
  });
708
-
709
552
  this._thumbnails.push(thumbnail);
710
- } // Set the first `pdfPage` immediately, since it's already loaded,
553
+ }
554
+ // Set the first `pdfPage` immediately, since it's already loaded,
711
555
  // rather than having to repeat the `PDFDocumentProxy.getPage` call in
712
- // the `this._ensurePdfPageLoaded` method before rendering can start.
713
-
714
-
715
- const firstThumbnailView = this._thumbnails[0];
716
-
717
- if (firstThumbnailView) {
718
- firstThumbnailView.setPdfPage(firstPdfPage);
719
- } // Ensure that the current thumbnail is always highlighted on load.
720
-
556
+ // the `this.#ensurePdfPageLoaded` method before rendering can start.
557
+ (_this$_thumbnails$ = this._thumbnails[0]) === null || _this$_thumbnails$ === void 0 ? void 0 : _this$_thumbnails$.setPdfPage(firstPdfPage);
721
558
 
559
+ // Ensure that the current thumbnail is always highlighted on load.
722
560
  const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
723
561
  thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
724
562
  }).catch(reason => {
725
563
  console.error("Unable to initialize thumbnail viewer", reason);
726
564
  });
727
565
  }
728
- /**
729
- * @private
730
- */
731
-
732
-
733
- _cancelRendering() {
734
- for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
735
- if (this._thumbnails[i]) {
736
- this._thumbnails[i].cancelRendering();
737
- }
566
+ #cancelRendering() {
567
+ for (const thumbnail of this._thumbnails) {
568
+ thumbnail.cancelRendering();
738
569
  }
739
570
  }
571
+
740
572
  /**
741
573
  * @param {Array|null} labels
742
574
  */
743
-
744
-
745
575
  setPageLabels(labels) {
746
576
  if (!this.pdfDocument) {
747
577
  return;
748
578
  }
749
-
750
579
  if (!labels) {
751
580
  this._pageLabels = null;
752
581
  } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
@@ -754,66 +583,54 @@ class PDFThumbnailViewer {
754
583
  console.error("PDFThumbnailViewer_setPageLabels: Invalid page labels.");
755
584
  } else {
756
585
  this._pageLabels = labels;
757
- } // Update all the `PDFThumbnailView` instances.
758
-
759
-
586
+ }
587
+ // Update all the `PDFThumbnailView` instances.
760
588
  for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
761
589
  var _this$_pageLabels$i, _this$_pageLabels;
762
-
763
590
  this._thumbnails[i].setPageLabel((_this$_pageLabels$i = (_this$_pageLabels = this._pageLabels) === null || _this$_pageLabels === void 0 ? void 0 : _this$_pageLabels[i]) !== null && _this$_pageLabels$i !== void 0 ? _this$_pageLabels$i : null);
764
591
  }
765
592
  }
593
+
766
594
  /**
767
595
  * @param {PDFThumbnailView} thumbView
768
- * @returns {PDFPage}
769
- * @private
596
+ * @returns {Promise<PDFPageProxy | null>}
770
597
  */
771
-
772
-
773
- _ensurePdfPageLoaded(thumbView) {
598
+ async #ensurePdfPageLoaded(thumbView) {
774
599
  if (thumbView.pdfPage) {
775
- return Promise.resolve(thumbView.pdfPage);
776
- }
777
-
778
- if (this._pagesRequests.has(thumbView)) {
779
- return this._pagesRequests.get(thumbView);
600
+ return thumbView.pdfPage;
780
601
  }
781
-
782
- const promise = this.pdfDocument.getPage(thumbView.id).then(pdfPage => {
602
+ try {
603
+ const pdfPage = await this.pdfDocument.getPage(thumbView.id);
783
604
  if (!thumbView.pdfPage) {
784
605
  thumbView.setPdfPage(pdfPage);
785
606
  }
786
-
787
- this._pagesRequests.delete(thumbView);
788
-
789
607
  return pdfPage;
790
- }).catch(reason => {
791
- console.error("Unable to get page for thumb view", reason); // Page error -- there is nothing that can be done.
792
-
793
- this._pagesRequests.delete(thumbView);
794
- });
795
-
796
- this._pagesRequests.set(thumbView, promise);
797
-
798
- return promise;
608
+ } catch (reason) {
609
+ console.error("Unable to get page for thumb view", reason);
610
+ return null; // Page error -- there is nothing that can be done.
611
+ }
612
+ }
613
+ #getScrollAhead(visible) {
614
+ var _visible$first, _visible$last;
615
+ if (((_visible$first = visible.first) === null || _visible$first === void 0 ? void 0 : _visible$first.id) === 1) {
616
+ return true;
617
+ } else if (((_visible$last = visible.last) === null || _visible$last === void 0 ? void 0 : _visible$last.id) === this._thumbnails.length) {
618
+ return false;
619
+ }
620
+ return this.scroll.down;
799
621
  }
800
-
801
622
  forceRendering() {
802
- const visibleThumbs = this._getVisibleThumbs();
803
-
804
- const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, this.scroll.down);
805
-
623
+ const visibleThumbs = this.#getVisibleThumbs();
624
+ const scrollAhead = this.#getScrollAhead(visibleThumbs);
625
+ const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, scrollAhead);
806
626
  if (thumbView) {
807
- this._ensurePdfPageLoaded(thumbView).then(() => {
627
+ this.#ensurePdfPageLoaded(thumbView).then(() => {
808
628
  this.renderingQueue.renderView(thumbView);
809
629
  });
810
-
811
630
  return true;
812
631
  }
813
-
814
632
  return false;
815
633
  }
816
-
817
634
  }
818
635
 
819
636
  export { PDFThumbnailViewer };