@design.estate/dees-catalog 6.12.1 → 7.0.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.
- package/.smartconfig.json +5 -1
- package/dist_bundle/bundle.js +24088 -1233
- package/dist_bundle/bundle.js.map +1 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/.tsbundle-artifacts.json +7 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/worker-KZQQYSUG.js +58301 -0
- package/dist_bundle/chunks/U6Ua8p1giFoD1s7vrUZUdpbg/worker-KZQQYSUG.js.map +1 -0
- package/dist_bundle/third-party-notices.md +190 -0
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +29 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.js +65 -8
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.js +15 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/PdfManager.d.ts +6 -4
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/PdfManager.js +183 -21
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/utils.d.ts +3 -0
- package/dist_ts_web/elements/00group-media/dees-pdf-shared/utils.js +67 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/component.d.ts +25 -1
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/component.js +693 -285
- package/dist_ts_web/elements/00group-media/dees-pdf-viewer/demo.js +7 -7
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/component.d.ts +20 -1
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/component.js +379 -116
- package/dist_ts_web/elements/00group-media/dees-thumbnail-pdf/demo.js +2 -2
- package/dist_ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.d.ts +1 -0
- package/dist_ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.js +7 -1
- package/package.json +5 -4
- package/readme.hints.md +26 -19
- package/readme.md +97 -53
- package/third-party-notices.md +190 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +13 -0
- package/ts_web/elements/00group-harness/dees-harness-session-sidebar/dees-harness-session-sidebar.ts +51 -6
- package/ts_web/elements/00group-harness/interfaces.ts +2 -0
- package/ts_web/elements/00group-media/dees-pdf-shared/CanvasPool.ts +15 -1
- package/ts_web/elements/00group-media/dees-pdf-shared/PdfManager.ts +214 -24
- package/ts_web/elements/00group-media/dees-pdf-shared/utils.ts +77 -1
- package/ts_web/elements/00group-media/dees-pdf-viewer/component.ts +774 -327
- package/ts_web/elements/00group-media/dees-pdf-viewer/demo.ts +7 -7
- package/ts_web/elements/00group-media/dees-thumbnail-pdf/component.ts +427 -128
- package/ts_web/elements/00group-media/dees-thumbnail-pdf/demo.ts +1 -1
- package/ts_web/elements/00group-media/dees-thumbnail-shared/DeesThumbnailBase.ts +5 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { DeesElement, property, html, customElement, type TemplateResult, directives } from '@design.estate/dees-element';
|
|
2
|
+
import {
|
|
3
|
+
TextLayer,
|
|
4
|
+
type PDFDocumentProxy,
|
|
5
|
+
type PDFPageProxy,
|
|
6
|
+
type RenderTask,
|
|
7
|
+
} from 'pdfjs-dist';
|
|
2
8
|
import { PdfManager } from '../dees-pdf-shared/PdfManager.js';
|
|
9
|
+
import {
|
|
10
|
+
raceWithAbort,
|
|
11
|
+
settlePromisesWithin,
|
|
12
|
+
waitForAnimationFrame,
|
|
13
|
+
} from '../dees-pdf-shared/utils.js';
|
|
3
14
|
import { viewerStyles } from './styles.js';
|
|
4
15
|
import { demo as demoFunc } from './demo.js';
|
|
5
16
|
import '../../00group-utility/dees-icon/dees-icon.js';
|
|
@@ -13,6 +24,24 @@ declare global {
|
|
|
13
24
|
|
|
14
25
|
type RenderState = 'idle' | 'loading' | 'rendering-main' | 'rendering-thumbs' | 'rendered' | 'error' | 'disposed';
|
|
15
26
|
|
|
27
|
+
interface ILoadOperation {
|
|
28
|
+
key: string;
|
|
29
|
+
promise: Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface IRenderRecoveryState {
|
|
33
|
+
currentPage: number;
|
|
34
|
+
currentZoom: number;
|
|
35
|
+
viewportMode: 'auto' | 'page-fit' | 'page-width' | 'custom';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const pipelineSettlementGraceMs = 2_500;
|
|
39
|
+
|
|
40
|
+
interface ITextLayerRenderTask {
|
|
41
|
+
textLayer: TextLayer;
|
|
42
|
+
promise: Promise<unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
16
45
|
@customElement('dees-pdf-viewer')
|
|
17
46
|
export class DeesPdfViewer extends DeesElement {
|
|
18
47
|
public static demo = demoFunc;
|
|
@@ -61,19 +90,38 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
61
90
|
@property({ type: Number })
|
|
62
91
|
accessor pdfFileSize: number = 0;
|
|
63
92
|
|
|
64
|
-
private pdfDocument:
|
|
93
|
+
private pdfDocument: PDFDocumentProxy | null = null;
|
|
94
|
+
private pdfLoadPromise: Promise<PDFDocumentProxy> | null = null;
|
|
95
|
+
private loadOperation: ILoadOperation | null = null;
|
|
96
|
+
private loadGeneration = 0;
|
|
97
|
+
private connectionEpoch = 0;
|
|
98
|
+
private cleanupPromise: Promise<void> | null = null;
|
|
99
|
+
private renderAdmissionOpen = false;
|
|
100
|
+
private renderGeneration = 0;
|
|
65
101
|
private renderState: RenderState = 'idle';
|
|
66
102
|
private renderAbortController: AbortController | null = null;
|
|
67
103
|
private pageRendering: boolean = false;
|
|
68
104
|
private pageNumPending: number | null = null;
|
|
69
105
|
private currentRenderTask: any = null;
|
|
70
106
|
private currentRenderPromise: Promise<void> | null = null;
|
|
71
|
-
private thumbnailRenderTasks
|
|
72
|
-
private
|
|
73
|
-
private
|
|
107
|
+
private thumbnailRenderTasks = new Set<RenderTask>();
|
|
108
|
+
private thumbnailRenderPromise: Promise<void> | null = null;
|
|
109
|
+
private thumbnailRenderGeneration = 0;
|
|
110
|
+
private thumbnailRenderQueued = false;
|
|
111
|
+
private thumbnailRenderQueueEpoch = 0;
|
|
112
|
+
private pageRenderTasks = new Map<number, RenderTask>();
|
|
113
|
+
private pageRenderPipelines = new Set<Promise<void>>();
|
|
114
|
+
private documentUsePipelines = new Set<Promise<void>>();
|
|
115
|
+
private rerenderPromise: Promise<void> | null = null;
|
|
116
|
+
private rerenderQueued = false;
|
|
117
|
+
private rerenderQueueEpoch = 0;
|
|
118
|
+
private rerenderRecoveryTimeout?: number;
|
|
119
|
+
private textLayerRenderTasks = new Map<number, ITextLayerRenderTask>();
|
|
120
|
+
private textSelectionCleanupFunctions = new Map<number, () => void>();
|
|
74
121
|
private canvas: HTMLCanvasElement | undefined;
|
|
75
122
|
private ctx: CanvasRenderingContext2D | undefined;
|
|
76
123
|
private viewerMain: HTMLElement | null = null;
|
|
124
|
+
private wheelEventTarget: HTMLElement | null = null;
|
|
77
125
|
private resizeObserver?: ResizeObserver;
|
|
78
126
|
private intersectionObserver?: IntersectionObserver;
|
|
79
127
|
private scrollThrottleTimeout?: number;
|
|
@@ -268,35 +316,28 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
268
316
|
}
|
|
269
317
|
|
|
270
318
|
public async connectedCallback() {
|
|
319
|
+
const connectionEpoch = ++this.connectionEpoch;
|
|
271
320
|
await super.connectedCallback();
|
|
272
321
|
await this.updateComplete;
|
|
322
|
+
if (!this.isConnected || connectionEpoch !== this.connectionEpoch) return;
|
|
273
323
|
this.ensureViewerRefs();
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
await this.
|
|
324
|
+
const operationKey = `${connectionEpoch}:${this.pdfUrl}`;
|
|
325
|
+
if (this.loadOperation?.key === operationKey) {
|
|
326
|
+
await this.loadOperation.promise;
|
|
327
|
+
} else if (connectionEpoch > 1 || !this.pdfDocument) {
|
|
328
|
+
await this.schedulePdfLoad(connectionEpoch);
|
|
279
329
|
}
|
|
280
330
|
}
|
|
281
331
|
|
|
282
332
|
public async disconnectedCallback() {
|
|
333
|
+
this.connectionEpoch += 1;
|
|
334
|
+
this.loadGeneration += 1;
|
|
335
|
+
this.renderAdmissionOpen = false;
|
|
336
|
+
this.renderAbortController?.abort();
|
|
337
|
+
const cleanupPromise = this.cleanupDocument();
|
|
283
338
|
await super.disconnectedCallback();
|
|
284
|
-
|
|
285
|
-
this.resizeObserver = undefined;
|
|
286
|
-
this.intersectionObserver?.disconnect();
|
|
287
|
-
this.intersectionObserver = undefined;
|
|
288
|
-
|
|
289
|
-
// Clear scroll timeout
|
|
290
|
-
if (this.scrollThrottleTimeout) {
|
|
291
|
-
clearTimeout(this.scrollThrottleTimeout);
|
|
292
|
-
this.scrollThrottleTimeout = undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Mark as disposed and clean up
|
|
339
|
+
await cleanupPromise;
|
|
296
340
|
this.renderState = 'disposed';
|
|
297
|
-
await this.cleanupDocument();
|
|
298
|
-
|
|
299
|
-
// Clear all references
|
|
300
341
|
this.canvas = undefined;
|
|
301
342
|
this.ctx = undefined;
|
|
302
343
|
}
|
|
@@ -304,23 +345,52 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
304
345
|
public async updated(changedProperties: Map<PropertyKey, unknown>) {
|
|
305
346
|
super.updated(changedProperties);
|
|
306
347
|
|
|
307
|
-
if (changedProperties.has('pdfUrl') && this.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
348
|
+
if (changedProperties.has('pdfUrl') && this.isConnected) {
|
|
349
|
+
await this.schedulePdfLoad(this.connectionEpoch);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (changedProperties.has('showSidebar') && !this.showSidebar) {
|
|
353
|
+
this.thumbnailRenderGeneration += 1;
|
|
354
|
+
this.thumbnailRenderQueued = false;
|
|
355
|
+
for (const task of this.thumbnailRenderTasks) {
|
|
356
|
+
try {
|
|
357
|
+
task.cancel();
|
|
358
|
+
} catch {
|
|
359
|
+
// The scheduler still owns each task promise until it settles.
|
|
360
|
+
}
|
|
311
361
|
}
|
|
312
|
-
// Generate new document ID for new URL
|
|
313
|
-
this.documentId = `${this.pdfUrl}-${Date.now()}-${Math.random()}`;
|
|
314
|
-
await this.loadPdf();
|
|
315
362
|
}
|
|
316
363
|
|
|
317
364
|
// Re-render thumbnails when sidebar becomes visible and document is loaded
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
365
|
+
if (
|
|
366
|
+
changedProperties.has('showSidebar')
|
|
367
|
+
&& this.showSidebar
|
|
368
|
+
&& this.renderAdmissionOpen
|
|
369
|
+
&& this.pdfDocument
|
|
370
|
+
) {
|
|
371
|
+
const connectionEpoch = this.connectionEpoch;
|
|
372
|
+
const loadGeneration = this.loadGeneration;
|
|
373
|
+
const pdfDocument = this.pdfDocument;
|
|
374
|
+
await waitForAnimationFrame();
|
|
375
|
+
if (
|
|
376
|
+
!this.isConnected
|
|
377
|
+
|| connectionEpoch !== this.connectionEpoch
|
|
378
|
+
|| loadGeneration !== this.loadGeneration
|
|
379
|
+
|| pdfDocument !== this.pdfDocument
|
|
380
|
+
|| !this.renderAdmissionOpen
|
|
381
|
+
|| !this.showSidebar
|
|
382
|
+
) return;
|
|
321
383
|
// Force re-render of thumbnails by resetting their rendered state
|
|
322
384
|
this.thumbnailData.forEach(thumb => thumb.rendered = false);
|
|
323
385
|
await this.renderThumbnails();
|
|
386
|
+
if (
|
|
387
|
+
!this.isConnected
|
|
388
|
+
|| connectionEpoch !== this.connectionEpoch
|
|
389
|
+
|| loadGeneration !== this.loadGeneration
|
|
390
|
+
|| pdfDocument !== this.pdfDocument
|
|
391
|
+
|| !this.renderAdmissionOpen
|
|
392
|
+
|| !this.showSidebar
|
|
393
|
+
) return;
|
|
324
394
|
// Re-setup intersection observer for lazy loading of pages
|
|
325
395
|
this.setupIntersectionObserver();
|
|
326
396
|
// Scroll to active thumbnail after rendering
|
|
@@ -334,82 +404,159 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
334
404
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
335
405
|
}
|
|
336
406
|
|
|
337
|
-
private
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
407
|
+
private schedulePdfLoad(
|
|
408
|
+
connectionEpochArg: number,
|
|
409
|
+
renderRecoveryStateArg?: IRenderRecoveryState,
|
|
410
|
+
): Promise<void> {
|
|
411
|
+
const url = this.pdfUrl;
|
|
412
|
+
const baseKey = `${connectionEpochArg}:${url}`;
|
|
413
|
+
if (!renderRecoveryStateArg && this.loadOperation?.key === baseKey) {
|
|
414
|
+
return this.loadOperation.promise;
|
|
415
|
+
}
|
|
416
|
+
const generation = ++this.loadGeneration;
|
|
417
|
+
const key = renderRecoveryStateArg
|
|
418
|
+
? `${baseKey}:render-recovery:${generation}`
|
|
419
|
+
: baseKey;
|
|
420
|
+
const operation: ILoadOperation = {
|
|
421
|
+
key,
|
|
422
|
+
promise: Promise.resolve(),
|
|
423
|
+
};
|
|
424
|
+
operation.promise = this.performPdfLoad(
|
|
425
|
+
generation,
|
|
426
|
+
connectionEpochArg,
|
|
427
|
+
url,
|
|
428
|
+
renderRecoveryStateArg,
|
|
429
|
+
)
|
|
430
|
+
.finally(() => {
|
|
431
|
+
if (this.loadOperation === operation) {
|
|
432
|
+
this.loadOperation = null;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
this.loadOperation = operation;
|
|
436
|
+
return operation.promise;
|
|
437
|
+
}
|
|
343
438
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
439
|
+
private isCurrentLoad(generationArg: number, connectionEpochArg: number, urlArg: string): boolean {
|
|
440
|
+
return this.isConnected
|
|
441
|
+
&& generationArg === this.loadGeneration
|
|
442
|
+
&& connectionEpochArg === this.connectionEpoch
|
|
443
|
+
&& urlArg === this.pdfUrl;
|
|
444
|
+
}
|
|
347
445
|
|
|
348
|
-
|
|
349
|
-
|
|
446
|
+
private async performPdfLoad(
|
|
447
|
+
generationArg: number,
|
|
448
|
+
connectionEpochArg: number,
|
|
449
|
+
urlArg: string,
|
|
450
|
+
renderRecoveryStateArg?: IRenderRecoveryState,
|
|
451
|
+
): Promise<void> {
|
|
452
|
+
await this.cleanupDocument();
|
|
453
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (!urlArg) {
|
|
457
|
+
this.loading = false;
|
|
458
|
+
this.renderState = 'idle';
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
350
461
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
462
|
+
this.loading = true;
|
|
463
|
+
this.renderState = 'loading';
|
|
464
|
+
const abortController = new AbortController();
|
|
465
|
+
this.renderAbortController = abortController;
|
|
466
|
+
const { signal } = abortController;
|
|
354
467
|
|
|
355
|
-
|
|
468
|
+
try {
|
|
469
|
+
const pdfLoadPromise = PdfManager.loadDocument(urlArg, signal);
|
|
470
|
+
this.pdfLoadPromise = pdfLoadPromise;
|
|
471
|
+
let pdfDocument: PDFDocumentProxy;
|
|
356
472
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
473
|
+
pdfDocument = await pdfLoadPromise;
|
|
474
|
+
} finally {
|
|
475
|
+
if (this.pdfLoadPromise === pdfLoadPromise) {
|
|
476
|
+
this.pdfLoadPromise = null;
|
|
477
|
+
}
|
|
361
478
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
page: i + 1,
|
|
366
|
-
rendered: false
|
|
367
|
-
}));
|
|
368
|
-
|
|
369
|
-
this.pageData = Array.from({length: this.totalPages}, (_, i) => ({
|
|
370
|
-
page: i + 1,
|
|
371
|
-
rendered: false,
|
|
372
|
-
rendering: false,
|
|
373
|
-
textLayerRendered: false,
|
|
374
|
-
}));
|
|
375
|
-
|
|
376
|
-
// Set loading to false to render the pages
|
|
377
|
-
this.loading = false;
|
|
378
|
-
await this.updateComplete;
|
|
379
|
-
this.ensureViewerRefs();
|
|
380
|
-
this.setupIntersectionObserver();
|
|
381
|
-
|
|
382
|
-
// Wait for next frame to ensure DOM is ready
|
|
383
|
-
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
384
|
-
if (signal.aborted) return;
|
|
385
|
-
|
|
386
|
-
this.renderState = 'rendering-main';
|
|
387
|
-
|
|
388
|
-
// Render initial visible pages
|
|
389
|
-
await this.renderVisiblePages();
|
|
390
|
-
if (signal.aborted) return;
|
|
391
|
-
|
|
392
|
-
// Scroll to initial page
|
|
393
|
-
if (this.initialPage > 1) {
|
|
394
|
-
await this.scrollToPage(this.initialPage, false);
|
|
479
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) {
|
|
480
|
+
await PdfManager.releaseDocument(pdfDocument);
|
|
481
|
+
return;
|
|
395
482
|
}
|
|
483
|
+
this.pdfDocument = pdfDocument;
|
|
484
|
+
this.renderAdmissionOpen = true;
|
|
485
|
+
this.documentId = `${urlArg}-${generationArg}`;
|
|
486
|
+
const documentUsePipeline = (async () => {
|
|
487
|
+
this.totalPages = pdfDocument.numPages;
|
|
488
|
+
if (renderRecoveryStateArg) {
|
|
489
|
+
this.currentPage = Math.min(
|
|
490
|
+
Math.max(renderRecoveryStateArg.currentPage, 1),
|
|
491
|
+
this.totalPages,
|
|
492
|
+
);
|
|
493
|
+
this.currentZoom = renderRecoveryStateArg.currentZoom;
|
|
494
|
+
this.viewportMode = renderRecoveryStateArg.viewportMode;
|
|
495
|
+
} else {
|
|
496
|
+
this.currentPage = this.initialPage;
|
|
497
|
+
this.resolveInitialViewportMode();
|
|
498
|
+
}
|
|
396
499
|
|
|
397
|
-
|
|
398
|
-
|
|
500
|
+
try {
|
|
501
|
+
const data = await raceWithAbort(pdfDocument.getData(), signal);
|
|
502
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
503
|
+
this.pdfFileSize = data.length;
|
|
504
|
+
} catch {
|
|
505
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
506
|
+
this.pdfFileSize = 0;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this.thumbnailData = Array.from({length: this.totalPages}, (_, i) => ({
|
|
510
|
+
page: i + 1,
|
|
511
|
+
rendered: false
|
|
512
|
+
}));
|
|
513
|
+
this.pageData = Array.from({length: this.totalPages}, (_, i) => ({
|
|
514
|
+
page: i + 1,
|
|
515
|
+
rendered: false,
|
|
516
|
+
rendering: false,
|
|
517
|
+
textLayerRendered: false,
|
|
518
|
+
}));
|
|
519
|
+
|
|
520
|
+
this.loading = false;
|
|
399
521
|
await this.updateComplete;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
522
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) return;
|
|
523
|
+
this.ensureViewerRefs();
|
|
524
|
+
this.setupIntersectionObserver();
|
|
525
|
+
|
|
526
|
+
await waitForAnimationFrame(signal);
|
|
527
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) return;
|
|
528
|
+
this.renderState = 'rendering-main';
|
|
529
|
+
|
|
530
|
+
await this.renderVisiblePages();
|
|
531
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) return;
|
|
532
|
+
if (this.currentPage > 1) {
|
|
533
|
+
await this.scrollToPage(this.currentPage, false);
|
|
534
|
+
}
|
|
403
535
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
536
|
+
if (this.showSidebar) {
|
|
537
|
+
await this.updateComplete;
|
|
538
|
+
await waitForAnimationFrame(signal);
|
|
539
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) return;
|
|
540
|
+
await this.renderThumbnails();
|
|
541
|
+
if (!this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) || signal.aborted) return;
|
|
542
|
+
}
|
|
407
543
|
|
|
408
|
-
|
|
544
|
+
if (this.isCurrentLoad(generationArg, connectionEpochArg, urlArg)) {
|
|
545
|
+
this.renderState = 'rendered';
|
|
546
|
+
}
|
|
547
|
+
})();
|
|
548
|
+
this.documentUsePipelines.add(documentUsePipeline);
|
|
549
|
+
try {
|
|
550
|
+
await documentUsePipeline;
|
|
551
|
+
} finally {
|
|
552
|
+
this.documentUsePipelines.delete(documentUsePipeline);
|
|
553
|
+
}
|
|
409
554
|
} catch (error) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
555
|
+
if (this.isCurrentLoad(generationArg, connectionEpochArg, urlArg) && !signal.aborted) {
|
|
556
|
+
console.error('Error loading PDF:', error);
|
|
557
|
+
this.loading = false;
|
|
558
|
+
this.renderState = 'error';
|
|
559
|
+
}
|
|
413
560
|
}
|
|
414
561
|
}
|
|
415
562
|
|
|
@@ -468,101 +615,169 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
468
615
|
}
|
|
469
616
|
}
|
|
470
617
|
|
|
471
|
-
private
|
|
618
|
+
private renderPageIfNeeded(pageNum: number): Promise<void> {
|
|
472
619
|
const pageInfo = this.pageData.find(p => p.page === pageNum);
|
|
473
|
-
|
|
474
|
-
|
|
620
|
+
const pdfDocument = this.pdfDocument;
|
|
621
|
+
const signal = this.renderAbortController?.signal;
|
|
622
|
+
if (
|
|
623
|
+
!this.renderAdmissionOpen
|
|
624
|
+
|| !pdfDocument
|
|
625
|
+
|| !signal
|
|
626
|
+
|| !pageInfo
|
|
627
|
+
|| pageInfo.rendered
|
|
628
|
+
|| pageInfo.rendering
|
|
629
|
+
) return Promise.resolve();
|
|
630
|
+
|
|
631
|
+
const renderGeneration = this.renderGeneration;
|
|
632
|
+
const isCurrentRender = (): boolean => (
|
|
633
|
+
this.isConnected
|
|
634
|
+
&& this.renderAdmissionOpen
|
|
635
|
+
&& !signal.aborted
|
|
636
|
+
&& renderGeneration === this.renderGeneration
|
|
637
|
+
&& pdfDocument === this.pdfDocument
|
|
638
|
+
);
|
|
475
639
|
pageInfo.rendering = true;
|
|
640
|
+
const renderPipeline = (async () => {
|
|
641
|
+
let page: PDFPageProxy | undefined;
|
|
642
|
+
let renderTask: RenderTask | undefined;
|
|
643
|
+
try {
|
|
644
|
+
const canvas = this.shadowRoot?.querySelector(
|
|
645
|
+
`.page-canvas[data-page="${pageNum}"]`,
|
|
646
|
+
) as HTMLCanvasElement;
|
|
647
|
+
if (!canvas) {
|
|
648
|
+
pageInfo.rendering = false;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
476
651
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
pageInfo.rendering = false;
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const page = await this.pdfDocument.getPage(pageNum);
|
|
485
|
-
const viewport = this.computeViewport(page);
|
|
486
|
-
|
|
487
|
-
// Set canvas dimensions with device pixel ratio for sharp rendering
|
|
488
|
-
const dpr = window.devicePixelRatio || 1;
|
|
489
|
-
canvas.width = Math.floor(viewport.width * dpr);
|
|
490
|
-
canvas.height = Math.floor(viewport.height * dpr);
|
|
491
|
-
canvas.style.width = `${viewport.width}px`;
|
|
492
|
-
canvas.style.height = `${viewport.height}px`;
|
|
493
|
-
|
|
494
|
-
const ctx = canvas.getContext('2d');
|
|
495
|
-
if (!ctx) {
|
|
496
|
-
page.cleanup?.();
|
|
497
|
-
pageInfo.rendering = false;
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// Scale context for high-DPI displays
|
|
502
|
-
ctx.scale(dpr, dpr);
|
|
652
|
+
page = await raceWithAbort(pdfDocument.getPage(pageNum), signal);
|
|
653
|
+
if (!isCurrentRender()) return;
|
|
654
|
+
const viewport = this.computeViewport(page);
|
|
503
655
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
viewport
|
|
507
|
-
|
|
656
|
+
// Set canvas dimensions with device pixel ratio for sharp rendering
|
|
657
|
+
const dpr = window.devicePixelRatio || 1;
|
|
658
|
+
canvas.width = Math.floor(viewport.width * dpr);
|
|
659
|
+
canvas.height = Math.floor(viewport.height * dpr);
|
|
660
|
+
canvas.style.width = `${viewport.width}px`;
|
|
661
|
+
canvas.style.height = `${viewport.height}px`;
|
|
508
662
|
|
|
509
|
-
|
|
510
|
-
|
|
663
|
+
const ctx = canvas.getContext('2d');
|
|
664
|
+
if (!ctx) {
|
|
665
|
+
pageInfo.rendering = false;
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
511
668
|
|
|
512
|
-
|
|
669
|
+
// Scale context for high-DPI displays
|
|
670
|
+
ctx.scale(dpr, dpr);
|
|
513
671
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
672
|
+
const renderContext = {
|
|
673
|
+
canvasContext: ctx,
|
|
674
|
+
viewport: viewport,
|
|
675
|
+
};
|
|
518
676
|
|
|
519
|
-
|
|
520
|
-
|
|
677
|
+
renderTask = page.render(renderContext);
|
|
678
|
+
this.pageRenderTasks.set(pageNum, renderTask);
|
|
679
|
+
await raceWithAbort(renderTask.promise, signal);
|
|
680
|
+
if (!isCurrentRender()) return;
|
|
681
|
+
pageInfo.rendered = true;
|
|
682
|
+
pageInfo.rendering = false;
|
|
683
|
+
if (this.pageRenderTasks.get(pageNum) === renderTask) {
|
|
684
|
+
this.pageRenderTasks.delete(pageNum);
|
|
685
|
+
}
|
|
521
686
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
687
|
+
// Render text layer for selection
|
|
688
|
+
await this.renderTextLayer(pageNum, renderGeneration, pdfDocument, signal);
|
|
689
|
+
if (!isCurrentRender()) return;
|
|
690
|
+
|
|
691
|
+
// Update page data to reflect rendered state
|
|
692
|
+
this.requestUpdate('pageData');
|
|
693
|
+
} catch (error: unknown) {
|
|
694
|
+
if (
|
|
695
|
+
!(error instanceof Error)
|
|
696
|
+
|| !['AbortError', 'AbortException', 'RenderingCancelledException'].includes(error.name)
|
|
697
|
+
) {
|
|
698
|
+
console.error(`Error rendering page ${pageNum}:`, error);
|
|
699
|
+
}
|
|
700
|
+
if (renderGeneration === this.renderGeneration) {
|
|
701
|
+
pageInfo.rendering = false;
|
|
702
|
+
}
|
|
703
|
+
} finally {
|
|
704
|
+
if (renderTask && this.pageRenderTasks.get(pageNum) === renderTask) {
|
|
705
|
+
this.pageRenderTasks.delete(pageNum);
|
|
706
|
+
}
|
|
707
|
+
page?.cleanup();
|
|
527
708
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
709
|
+
})();
|
|
710
|
+
this.pageRenderPipelines.add(renderPipeline);
|
|
711
|
+
void renderPipeline.finally(() => {
|
|
712
|
+
this.pageRenderPipelines.delete(renderPipeline);
|
|
713
|
+
}).catch(() => {});
|
|
714
|
+
return renderPipeline;
|
|
531
715
|
}
|
|
532
716
|
|
|
533
|
-
private async renderTextLayer(
|
|
717
|
+
private async renderTextLayer(
|
|
718
|
+
pageNum: number,
|
|
719
|
+
renderGenerationArg: number,
|
|
720
|
+
pdfDocumentArg: PDFDocumentProxy,
|
|
721
|
+
signalArg: AbortSignal,
|
|
722
|
+
): Promise<void> {
|
|
534
723
|
const pageInfo = this.pageData.find(p => p.page === pageNum);
|
|
535
|
-
if (
|
|
536
|
-
|
|
724
|
+
if (
|
|
725
|
+
renderGenerationArg !== this.renderGeneration
|
|
726
|
+
|| signalArg.aborted
|
|
727
|
+
|| pdfDocumentArg !== this.pdfDocument
|
|
728
|
+
|| !pageInfo
|
|
729
|
+
|| pageInfo.textLayerRendered
|
|
730
|
+
) return;
|
|
731
|
+
|
|
732
|
+
let page: PDFPageProxy | undefined;
|
|
733
|
+
let renderTask: ITextLayerRenderTask | undefined;
|
|
537
734
|
try {
|
|
538
735
|
const textLayerDiv = this.shadowRoot?.querySelector(
|
|
539
736
|
`.text-layer[data-page="${pageNum}"]`
|
|
540
737
|
) as HTMLElement;
|
|
541
738
|
if (!textLayerDiv) return;
|
|
542
739
|
|
|
740
|
+
this.textSelectionCleanupFunctions.get(pageNum)?.();
|
|
741
|
+
this.textSelectionCleanupFunctions.delete(pageNum);
|
|
543
742
|
textLayerDiv.innerHTML = '';
|
|
544
743
|
|
|
545
|
-
|
|
546
|
-
|
|
744
|
+
page = await raceWithAbort(pdfDocumentArg.getPage(pageNum), signalArg);
|
|
745
|
+
if (
|
|
746
|
+
renderGenerationArg !== this.renderGeneration
|
|
747
|
+
|| signalArg.aborted
|
|
748
|
+
|| pdfDocumentArg !== this.pdfDocument
|
|
749
|
+
) return;
|
|
750
|
+
const textContent = await raceWithAbort(page.getTextContent(), signalArg);
|
|
751
|
+
if (
|
|
752
|
+
renderGenerationArg !== this.renderGeneration
|
|
753
|
+
|| signalArg.aborted
|
|
754
|
+
|| pdfDocumentArg !== this.pdfDocument
|
|
755
|
+
) return;
|
|
547
756
|
const viewport = this.computeViewport(page);
|
|
548
757
|
|
|
549
|
-
// @ts-ignore - Dynamic import of pdfjs
|
|
550
|
-
const pdfjs = await import('https://cdn.jsdelivr.net/npm/pdfjs-dist@4.0.379/+esm');
|
|
551
|
-
|
|
552
758
|
textLayerDiv.style.width = `${viewport.width}px`;
|
|
553
759
|
textLayerDiv.style.height = `${viewport.height}px`;
|
|
554
760
|
|
|
555
761
|
// Set the scale factor CSS variable - required by PDF.js text layer
|
|
556
762
|
textLayerDiv.style.setProperty('--scale-factor', String(viewport.scale));
|
|
557
763
|
|
|
558
|
-
const
|
|
764
|
+
const textLayer = new TextLayer({
|
|
559
765
|
textContentSource: textContent,
|
|
560
766
|
container: textLayerDiv,
|
|
561
767
|
viewport: viewport,
|
|
562
768
|
});
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
769
|
+
const textLayerRenderPromise = textLayer.render();
|
|
770
|
+
renderTask = {
|
|
771
|
+
textLayer,
|
|
772
|
+
promise: textLayerRenderPromise,
|
|
773
|
+
};
|
|
774
|
+
this.textLayerRenderTasks.set(pageNum, renderTask);
|
|
775
|
+
await raceWithAbort(textLayerRenderPromise, signalArg);
|
|
776
|
+
if (
|
|
777
|
+
renderGenerationArg !== this.renderGeneration
|
|
778
|
+
|| signalArg.aborted
|
|
779
|
+
|| pdfDocumentArg !== this.pdfDocument
|
|
780
|
+
) return;
|
|
566
781
|
|
|
567
782
|
// Add endOfContent for selection boundary
|
|
568
783
|
const endOfContent = document.createElement('div');
|
|
@@ -622,7 +837,7 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
622
837
|
}
|
|
623
838
|
};
|
|
624
839
|
|
|
625
|
-
|
|
840
|
+
const handleMouseDown = (e: MouseEvent) => {
|
|
626
841
|
if (e.button !== 0) return;
|
|
627
842
|
|
|
628
843
|
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
|
|
@@ -643,16 +858,27 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
643
858
|
document.addEventListener('mousemove', handleMouseMove);
|
|
644
859
|
document.addEventListener('mouseup', handleMouseUp);
|
|
645
860
|
}
|
|
861
|
+
};
|
|
862
|
+
textLayerDiv.addEventListener('mousedown', handleMouseDown);
|
|
863
|
+
this.textSelectionCleanupFunctions.set(pageNum, () => {
|
|
864
|
+
textLayerDiv.removeEventListener('mousedown', handleMouseDown);
|
|
865
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
866
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
646
867
|
});
|
|
647
868
|
|
|
648
869
|
pageInfo.textLayerRendered = true;
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
870
|
+
} catch (error: unknown) {
|
|
871
|
+
if (
|
|
872
|
+
!(error instanceof Error)
|
|
873
|
+
|| !['AbortError', 'AbortException', 'RenderingCancelledException'].includes(error.name)
|
|
874
|
+
) {
|
|
653
875
|
console.error(`Error rendering text layer for page ${pageNum}:`, error);
|
|
654
876
|
}
|
|
655
|
-
|
|
877
|
+
} finally {
|
|
878
|
+
if (renderTask && this.textLayerRenderTasks.get(pageNum) === renderTask) {
|
|
879
|
+
this.textLayerRenderTasks.delete(pageNum);
|
|
880
|
+
}
|
|
881
|
+
page?.cleanup();
|
|
656
882
|
}
|
|
657
883
|
}
|
|
658
884
|
|
|
@@ -762,23 +988,67 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
762
988
|
}
|
|
763
989
|
}
|
|
764
990
|
|
|
765
|
-
private
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
return;
|
|
991
|
+
private renderThumbnails(): Promise<void> {
|
|
992
|
+
if (!this.renderAdmissionOpen || !this.showSidebar || !this.pdfDocument) {
|
|
993
|
+
return Promise.resolve();
|
|
769
994
|
}
|
|
995
|
+
if (this.thumbnailRenderPromise) {
|
|
996
|
+
this.thumbnailRenderGeneration += 1;
|
|
997
|
+
this.thumbnailRenderQueued = true;
|
|
998
|
+
for (const task of this.thumbnailRenderTasks) {
|
|
999
|
+
try {
|
|
1000
|
+
task.cancel();
|
|
1001
|
+
} catch {
|
|
1002
|
+
// The active queue iteration still awaits the task promise.
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return this.thumbnailRenderPromise;
|
|
1006
|
+
}
|
|
1007
|
+
this.thumbnailRenderGeneration += 1;
|
|
1008
|
+
const thumbnailRenderQueueEpoch = ++this.thumbnailRenderQueueEpoch;
|
|
1009
|
+
const renderPromise = (async () => {
|
|
1010
|
+
do {
|
|
1011
|
+
if (thumbnailRenderQueueEpoch !== this.thumbnailRenderQueueEpoch) return;
|
|
1012
|
+
this.thumbnailRenderQueued = false;
|
|
1013
|
+
const renderGeneration = this.thumbnailRenderGeneration;
|
|
1014
|
+
await this.performRenderThumbnails(renderGeneration);
|
|
1015
|
+
} while (
|
|
1016
|
+
thumbnailRenderQueueEpoch === this.thumbnailRenderQueueEpoch
|
|
1017
|
+
&& this.thumbnailRenderQueued
|
|
1018
|
+
&& this.renderAdmissionOpen
|
|
1019
|
+
&& this.showSidebar
|
|
1020
|
+
&& this.pdfDocument
|
|
1021
|
+
);
|
|
1022
|
+
})();
|
|
1023
|
+
this.thumbnailRenderPromise = renderPromise;
|
|
1024
|
+
void renderPromise.finally(() => {
|
|
1025
|
+
if (this.thumbnailRenderPromise === renderPromise) {
|
|
1026
|
+
this.thumbnailRenderPromise = null;
|
|
1027
|
+
}
|
|
1028
|
+
}).catch(() => {});
|
|
1029
|
+
return renderPromise;
|
|
1030
|
+
}
|
|
770
1031
|
|
|
771
|
-
|
|
772
|
-
|
|
1032
|
+
private async performRenderThumbnails(renderGenerationArg: number): Promise<void> {
|
|
1033
|
+
const pdfDocument = this.pdfDocument;
|
|
1034
|
+
if (!this.renderAdmissionOpen || !this.showSidebar || !pdfDocument) {
|
|
773
1035
|
return;
|
|
774
1036
|
}
|
|
1037
|
+
const isCurrentRender = (): boolean => (
|
|
1038
|
+
this.isConnected
|
|
1039
|
+
&& this.renderAdmissionOpen
|
|
1040
|
+
&& this.showSidebar
|
|
1041
|
+
&& renderGenerationArg === this.thumbnailRenderGeneration
|
|
1042
|
+
&& pdfDocument === this.pdfDocument
|
|
1043
|
+
);
|
|
775
1044
|
|
|
776
|
-
// Check
|
|
777
|
-
if (this.
|
|
1045
|
+
// Check if already rendered
|
|
1046
|
+
if (this.thumbnailData.length > 0 && this.thumbnailData.every(t => t.rendered)) {
|
|
778
1047
|
return;
|
|
779
1048
|
}
|
|
780
1049
|
|
|
781
1050
|
const signal = this.renderAbortController?.signal;
|
|
1051
|
+
if (!signal || signal.aborted) return;
|
|
782
1052
|
this.renderState = 'rendering-thumbs';
|
|
783
1053
|
|
|
784
1054
|
// Cancel any existing thumbnail render tasks
|
|
@@ -789,13 +1059,14 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
789
1059
|
// Ignore cancellation errors
|
|
790
1060
|
}
|
|
791
1061
|
}
|
|
792
|
-
this.thumbnailRenderTasks = [];
|
|
793
1062
|
|
|
794
1063
|
try {
|
|
795
1064
|
await this.updateComplete;
|
|
1065
|
+
if (!isCurrentRender()) return;
|
|
796
1066
|
const thumbnails = this.shadowRoot?.querySelectorAll('.thumbnail') as NodeListOf<HTMLElement>;
|
|
797
1067
|
const thumbnailCanvases = this.shadowRoot?.querySelectorAll('.thumbnail-canvas') as NodeListOf<HTMLCanvasElement>;
|
|
798
1068
|
const sidebarContent = this.shadowRoot?.querySelector('.sidebar-content') as HTMLElement;
|
|
1069
|
+
if (!sidebarContent) return;
|
|
799
1070
|
|
|
800
1071
|
// Get the actual available width for thumbnails (sidebar width minus padding)
|
|
801
1072
|
const sidebarStyles = window.getComputedStyle(sidebarContent);
|
|
@@ -811,68 +1082,62 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
811
1082
|
}
|
|
812
1083
|
|
|
813
1084
|
for (let i = 0; i < thumbnailCanvases.length; i++) {
|
|
814
|
-
if (signal?.aborted) return;
|
|
1085
|
+
if (signal?.aborted || !isCurrentRender()) return;
|
|
815
1086
|
|
|
816
1087
|
const canvas = thumbnailCanvases[i];
|
|
817
1088
|
const thumbnail = thumbnails[i];
|
|
818
1089
|
const pageNum = parseInt(canvas.dataset.page || '1');
|
|
819
|
-
const page = await
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
context.scale(dpr, dpr);
|
|
849
|
-
|
|
850
|
-
const renderContext = {
|
|
851
|
-
canvasContext: context,
|
|
852
|
-
viewport: viewport,
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
const renderTask = page.render(renderContext);
|
|
856
|
-
this.thumbnailRenderTasks.push(renderTask);
|
|
857
|
-
await renderTask.promise;
|
|
858
|
-
page.cleanup?.();
|
|
1090
|
+
const page = await raceWithAbort(pdfDocument.getPage(pageNum), signal);
|
|
1091
|
+
try {
|
|
1092
|
+
if (!isCurrentRender()) return;
|
|
1093
|
+
const initialViewport = page.getViewport({ scale: 1 });
|
|
1094
|
+
const scale = maxThumbnailWidth / initialViewport.width;
|
|
1095
|
+
const viewport = page.getViewport({ scale });
|
|
1096
|
+
const dpr = window.devicePixelRatio || 1;
|
|
1097
|
+
canvas.width = Math.floor(viewport.width * dpr);
|
|
1098
|
+
canvas.height = Math.floor(viewport.height * dpr);
|
|
1099
|
+
canvas.style.width = `${viewport.width}px`;
|
|
1100
|
+
canvas.style.height = `${viewport.height}px`;
|
|
1101
|
+
thumbnail.style.height = `${viewport.height}px`;
|
|
1102
|
+
thumbnail.style.minHeight = `${viewport.height}px`;
|
|
1103
|
+
|
|
1104
|
+
const context = canvas.getContext('2d');
|
|
1105
|
+
if (!context) continue;
|
|
1106
|
+
context.scale(dpr, dpr);
|
|
1107
|
+
|
|
1108
|
+
const renderTask = page.render({
|
|
1109
|
+
canvasContext: context,
|
|
1110
|
+
viewport,
|
|
1111
|
+
});
|
|
1112
|
+
this.thumbnailRenderTasks.add(renderTask);
|
|
1113
|
+
try {
|
|
1114
|
+
await raceWithAbort(renderTask.promise, signal);
|
|
1115
|
+
} finally {
|
|
1116
|
+
this.thumbnailRenderTasks.delete(renderTask);
|
|
1117
|
+
}
|
|
1118
|
+
if (!isCurrentRender()) return;
|
|
859
1119
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1120
|
+
const thumbData = this.thumbnailData.find(t => t.page === pageNum);
|
|
1121
|
+
if (thumbData) {
|
|
1122
|
+
thumbData.rendered = true;
|
|
1123
|
+
}
|
|
1124
|
+
} finally {
|
|
1125
|
+
page.cleanup();
|
|
864
1126
|
}
|
|
865
1127
|
}
|
|
866
1128
|
|
|
867
1129
|
// Trigger update to reflect rendered state
|
|
868
|
-
|
|
869
|
-
|
|
1130
|
+
if (isCurrentRender()) {
|
|
1131
|
+
this.requestUpdate('thumbnailData');
|
|
1132
|
+
}
|
|
1133
|
+
} catch (error: unknown) {
|
|
870
1134
|
// Only log non-cancellation errors
|
|
871
|
-
if (
|
|
1135
|
+
if (
|
|
1136
|
+
!(error instanceof Error)
|
|
1137
|
+
|| !['AbortError', 'AbortException', 'RenderingCancelledException'].includes(error.name)
|
|
1138
|
+
) {
|
|
872
1139
|
console.error('Error rendering thumbnails:', error);
|
|
873
1140
|
}
|
|
874
|
-
} finally {
|
|
875
|
-
this.thumbnailRenderTasks = [];
|
|
876
1141
|
}
|
|
877
1142
|
}
|
|
878
1143
|
|
|
@@ -936,6 +1201,7 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
936
1201
|
}
|
|
937
1202
|
|
|
938
1203
|
private reRenderAllPages() {
|
|
1204
|
+
this.renderGeneration += 1;
|
|
939
1205
|
// Clear all rendered pages to force re-render with new zoom
|
|
940
1206
|
this.pageData.forEach(page => {
|
|
941
1207
|
page.rendered = false;
|
|
@@ -956,51 +1222,140 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
956
1222
|
// Cancel text layer render tasks
|
|
957
1223
|
this.textLayerRenderTasks.forEach(task => {
|
|
958
1224
|
try {
|
|
959
|
-
task.cancel
|
|
1225
|
+
task.textLayer.cancel();
|
|
960
1226
|
} catch (error) {
|
|
961
1227
|
// Ignore cancellation errors
|
|
962
1228
|
}
|
|
963
1229
|
});
|
|
964
1230
|
this.textLayerRenderTasks.clear();
|
|
1231
|
+
for (const cleanup of this.textSelectionCleanupFunctions.values()) {
|
|
1232
|
+
cleanup();
|
|
1233
|
+
}
|
|
1234
|
+
this.textSelectionCleanupFunctions.clear();
|
|
965
1235
|
|
|
966
1236
|
// Request update to re-render pages
|
|
967
1237
|
this.requestUpdate();
|
|
968
1238
|
|
|
969
|
-
|
|
970
|
-
this.
|
|
971
|
-
|
|
972
|
-
|
|
1239
|
+
this.rerenderQueued = true;
|
|
1240
|
+
if (this.rerenderPromise) return;
|
|
1241
|
+
|
|
1242
|
+
const rerenderQueueEpoch = ++this.rerenderQueueEpoch;
|
|
1243
|
+
const rerenderPromise = (async () => {
|
|
1244
|
+
while (
|
|
1245
|
+
rerenderQueueEpoch === this.rerenderQueueEpoch
|
|
1246
|
+
&& this.rerenderQueued
|
|
1247
|
+
&& this.renderAdmissionOpen
|
|
1248
|
+
&& this.isConnected
|
|
1249
|
+
) {
|
|
1250
|
+
this.rerenderQueued = false;
|
|
1251
|
+
const renderGeneration = this.renderGeneration;
|
|
1252
|
+
const priorRenderPipelines = [...this.pageRenderPipelines];
|
|
1253
|
+
const pipelinesSettled = await settlePromisesWithin(
|
|
1254
|
+
priorRenderPipelines,
|
|
1255
|
+
pipelineSettlementGraceMs,
|
|
1256
|
+
);
|
|
1257
|
+
if (!pipelinesSettled) {
|
|
1258
|
+
if (!this.rerenderRecoveryTimeout) {
|
|
1259
|
+
const connectionEpoch = this.connectionEpoch;
|
|
1260
|
+
const loadGeneration = this.loadGeneration;
|
|
1261
|
+
const url = this.pdfUrl;
|
|
1262
|
+
this.rerenderRecoveryTimeout = window.setTimeout(() => {
|
|
1263
|
+
this.rerenderRecoveryTimeout = undefined;
|
|
1264
|
+
if (
|
|
1265
|
+
rerenderQueueEpoch !== this.rerenderQueueEpoch
|
|
1266
|
+
|| connectionEpoch !== this.connectionEpoch
|
|
1267
|
+
|| loadGeneration !== this.loadGeneration
|
|
1268
|
+
|| url !== this.pdfUrl
|
|
1269
|
+
|| !this.renderAdmissionOpen
|
|
1270
|
+
|| !this.isConnected
|
|
1271
|
+
) return;
|
|
1272
|
+
void this.schedulePdfLoad(connectionEpoch, {
|
|
1273
|
+
currentPage: this.currentPage,
|
|
1274
|
+
currentZoom: this.currentZoom,
|
|
1275
|
+
viewportMode: this.viewportMode,
|
|
1276
|
+
});
|
|
1277
|
+
}, 0);
|
|
1278
|
+
}
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
await this.updateComplete;
|
|
1282
|
+
if (
|
|
1283
|
+
rerenderQueueEpoch === this.rerenderQueueEpoch
|
|
1284
|
+
&& renderGeneration === this.renderGeneration
|
|
1285
|
+
&& !this.rerenderQueued
|
|
1286
|
+
&& this.renderAdmissionOpen
|
|
1287
|
+
&& this.isConnected
|
|
1288
|
+
) {
|
|
1289
|
+
await this.renderVisiblePages();
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
})();
|
|
1293
|
+
this.rerenderPromise = rerenderPromise;
|
|
1294
|
+
void rerenderPromise.finally(() => {
|
|
1295
|
+
if (this.rerenderPromise === rerenderPromise) {
|
|
1296
|
+
this.rerenderPromise = null;
|
|
1297
|
+
}
|
|
1298
|
+
}).catch(() => {});
|
|
973
1299
|
}
|
|
974
1300
|
|
|
975
1301
|
private async downloadPdf() {
|
|
976
|
-
|
|
1302
|
+
const pdfDocument = this.pdfDocument;
|
|
1303
|
+
const signal = this.renderAbortController?.signal;
|
|
1304
|
+
if (!pdfDocument || !signal) return;
|
|
1305
|
+
const generation = this.loadGeneration;
|
|
1306
|
+
const connectionEpoch = this.connectionEpoch;
|
|
1307
|
+
const url = this.pdfUrl;
|
|
977
1308
|
|
|
1309
|
+
let ownedBlobUrl: string | undefined;
|
|
978
1310
|
try {
|
|
979
1311
|
// Get raw PDF data from the loaded document
|
|
980
|
-
const data = await
|
|
981
|
-
|
|
982
|
-
|
|
1312
|
+
const data = await raceWithAbort(pdfDocument.getData(), signal);
|
|
1313
|
+
if (
|
|
1314
|
+
!this.isCurrentLoad(generation, connectionEpoch, url)
|
|
1315
|
+
|| signal.aborted
|
|
1316
|
+
|| pdfDocument !== this.pdfDocument
|
|
1317
|
+
) return;
|
|
1318
|
+
const blob = new Blob([new Uint8Array(data)], { type: 'application/pdf' });
|
|
1319
|
+
ownedBlobUrl = URL.createObjectURL(blob);
|
|
983
1320
|
|
|
984
1321
|
const link = document.createElement('a');
|
|
985
|
-
link.href =
|
|
986
|
-
link.download =
|
|
1322
|
+
link.href = ownedBlobUrl;
|
|
1323
|
+
link.download = url ? url.split('/').pop() || 'document.pdf' : 'document.pdf';
|
|
987
1324
|
link.click();
|
|
988
1325
|
|
|
989
1326
|
// Clean up blob URL after short delay
|
|
990
|
-
|
|
1327
|
+
const scheduledBlobUrl = ownedBlobUrl;
|
|
1328
|
+
setTimeout(() => URL.revokeObjectURL(scheduledBlobUrl), 1000);
|
|
1329
|
+
ownedBlobUrl = undefined;
|
|
991
1330
|
} catch (error) {
|
|
992
|
-
|
|
1331
|
+
if (!(error instanceof Error) || error.name !== 'AbortError') {
|
|
1332
|
+
console.error('Error downloading PDF:', error);
|
|
1333
|
+
}
|
|
1334
|
+
} finally {
|
|
1335
|
+
if (ownedBlobUrl) URL.revokeObjectURL(ownedBlobUrl);
|
|
993
1336
|
}
|
|
994
1337
|
}
|
|
995
1338
|
|
|
996
1339
|
private async printPdf() {
|
|
997
|
-
|
|
1340
|
+
const pdfDocument = this.pdfDocument;
|
|
1341
|
+
const signal = this.renderAbortController?.signal;
|
|
1342
|
+
if (!pdfDocument || !signal) return;
|
|
1343
|
+
const generation = this.loadGeneration;
|
|
1344
|
+
const connectionEpoch = this.connectionEpoch;
|
|
1345
|
+
const url = this.pdfUrl;
|
|
998
1346
|
|
|
1347
|
+
let ownedPdfUrl: string | undefined;
|
|
1348
|
+
let ownedHtmlUrl: string | undefined;
|
|
999
1349
|
try {
|
|
1000
1350
|
// Get raw PDF data from the loaded document
|
|
1001
|
-
const data = await
|
|
1002
|
-
|
|
1003
|
-
|
|
1351
|
+
const data = await raceWithAbort(pdfDocument.getData(), signal);
|
|
1352
|
+
if (
|
|
1353
|
+
!this.isCurrentLoad(generation, connectionEpoch, url)
|
|
1354
|
+
|| signal.aborted
|
|
1355
|
+
|| pdfDocument !== this.pdfDocument
|
|
1356
|
+
) return;
|
|
1357
|
+
const blob = new Blob([new Uint8Array(data)], { type: 'application/pdf' });
|
|
1358
|
+
ownedPdfUrl = URL.createObjectURL(blob);
|
|
1004
1359
|
|
|
1005
1360
|
// Create an HTML wrapper page that embeds the PDF and handles print/close
|
|
1006
1361
|
// This gives us control over the afterprint event (direct PDF URLs don't support it)
|
|
@@ -1019,7 +1374,7 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
1019
1374
|
</style>
|
|
1020
1375
|
</head>
|
|
1021
1376
|
<body>
|
|
1022
|
-
<iframe src="${
|
|
1377
|
+
<iframe src="${ownedPdfUrl}" type="application/pdf"></iframe>
|
|
1023
1378
|
<script>
|
|
1024
1379
|
window.onload = function() {
|
|
1025
1380
|
setTimeout(function() {
|
|
@@ -1037,32 +1392,49 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
1037
1392
|
</html>
|
|
1038
1393
|
`;
|
|
1039
1394
|
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
|
|
1040
|
-
|
|
1395
|
+
ownedHtmlUrl = URL.createObjectURL(htmlBlob);
|
|
1041
1396
|
|
|
1042
|
-
const printWindow = window.open(
|
|
1397
|
+
const printWindow = window.open(ownedHtmlUrl, '_blank', 'width=800,height=600');
|
|
1043
1398
|
if (printWindow) {
|
|
1399
|
+
const scheduledPdfUrl = ownedPdfUrl;
|
|
1400
|
+
const scheduledHtmlUrl = ownedHtmlUrl;
|
|
1401
|
+
let urlsRevoked = false;
|
|
1402
|
+
const revokeUrls = (): void => {
|
|
1403
|
+
if (urlsRevoked) return;
|
|
1404
|
+
urlsRevoked = true;
|
|
1405
|
+
URL.revokeObjectURL(scheduledPdfUrl);
|
|
1406
|
+
URL.revokeObjectURL(scheduledHtmlUrl);
|
|
1407
|
+
};
|
|
1044
1408
|
// Cleanup blob URLs when window closes
|
|
1045
1409
|
const checkClosed = setInterval(() => {
|
|
1046
1410
|
if (printWindow.closed) {
|
|
1047
1411
|
clearInterval(checkClosed);
|
|
1048
|
-
|
|
1049
|
-
URL.revokeObjectURL(htmlUrl);
|
|
1412
|
+
revokeUrls();
|
|
1050
1413
|
}
|
|
1051
1414
|
}, 500);
|
|
1052
1415
|
// Safety cleanup after 2 minutes
|
|
1053
1416
|
setTimeout(() => {
|
|
1054
1417
|
clearInterval(checkClosed);
|
|
1055
|
-
|
|
1056
|
-
URL.revokeObjectURL(htmlUrl);
|
|
1418
|
+
revokeUrls();
|
|
1057
1419
|
}, 120000);
|
|
1420
|
+
ownedPdfUrl = undefined;
|
|
1421
|
+
ownedHtmlUrl = undefined;
|
|
1058
1422
|
} else {
|
|
1059
1423
|
// Popup blocked - fall back to direct navigation
|
|
1060
|
-
window.open(
|
|
1061
|
-
|
|
1062
|
-
URL.revokeObjectURL(
|
|
1424
|
+
window.open(ownedPdfUrl, '_blank');
|
|
1425
|
+
const scheduledPdfUrl = ownedPdfUrl;
|
|
1426
|
+
setTimeout(() => URL.revokeObjectURL(scheduledPdfUrl), 60000);
|
|
1427
|
+
ownedPdfUrl = undefined;
|
|
1428
|
+
URL.revokeObjectURL(ownedHtmlUrl);
|
|
1429
|
+
ownedHtmlUrl = undefined;
|
|
1063
1430
|
}
|
|
1064
1431
|
} catch (error) {
|
|
1065
|
-
|
|
1432
|
+
if (!(error instanceof Error) || error.name !== 'AbortError') {
|
|
1433
|
+
console.error('Error printing PDF:', error);
|
|
1434
|
+
}
|
|
1435
|
+
} finally {
|
|
1436
|
+
if (ownedPdfUrl) URL.revokeObjectURL(ownedPdfUrl);
|
|
1437
|
+
if (ownedHtmlUrl) URL.revokeObjectURL(ownedHtmlUrl);
|
|
1066
1438
|
}
|
|
1067
1439
|
}
|
|
1068
1440
|
|
|
@@ -1114,6 +1486,28 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
1114
1486
|
return this.viewportMode !== 'custom' || this.currentZoom > this.MANUAL_MIN_ZOOM;
|
|
1115
1487
|
}
|
|
1116
1488
|
|
|
1489
|
+
private readonly handleViewerWheel = (eventArg: WheelEvent): void => {
|
|
1490
|
+
const element = eventArg.currentTarget as HTMLElement;
|
|
1491
|
+
const scrollTop = element.scrollTop;
|
|
1492
|
+
const scrollHeight = element.scrollHeight;
|
|
1493
|
+
const clientHeight = element.clientHeight;
|
|
1494
|
+
const isAtTop = scrollTop === 0;
|
|
1495
|
+
const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 1;
|
|
1496
|
+
|
|
1497
|
+
if (
|
|
1498
|
+
(eventArg.deltaY < 0 && !isAtTop)
|
|
1499
|
+
|| (eventArg.deltaY > 0 && !isAtBottom)
|
|
1500
|
+
) {
|
|
1501
|
+
eventArg.stopPropagation();
|
|
1502
|
+
} else if (
|
|
1503
|
+
(eventArg.deltaY < 0 && isAtTop)
|
|
1504
|
+
|| (eventArg.deltaY > 0 && isAtBottom)
|
|
1505
|
+
) {
|
|
1506
|
+
eventArg.preventDefault();
|
|
1507
|
+
eventArg.stopPropagation();
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1117
1511
|
private ensureViewerRefs() {
|
|
1118
1512
|
if (!this.viewerMain) {
|
|
1119
1513
|
this.viewerMain = this.shadowRoot?.querySelector('.viewer-main') as HTMLElement;
|
|
@@ -1128,28 +1522,11 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
1128
1522
|
});
|
|
1129
1523
|
this.resizeObserver.observe(this.viewerMain);
|
|
1130
1524
|
this.measureViewportDimensions();
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
this.
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
const scrollHeight = element.scrollHeight;
|
|
1137
|
-
const clientHeight = element.clientHeight;
|
|
1138
|
-
const deltaY = e.deltaY;
|
|
1139
|
-
|
|
1140
|
-
// Check if we're at the boundaries
|
|
1141
|
-
const isAtTop = scrollTop === 0;
|
|
1142
|
-
const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 1;
|
|
1143
|
-
|
|
1144
|
-
// Prevent propagation if we're scrolling within bounds
|
|
1145
|
-
if ((deltaY < 0 && !isAtTop) || (deltaY > 0 && !isAtBottom)) {
|
|
1146
|
-
e.stopPropagation();
|
|
1147
|
-
} else if ((deltaY < 0 && isAtTop) || (deltaY > 0 && isAtBottom)) {
|
|
1148
|
-
// Prevent default and propagation when at boundaries
|
|
1149
|
-
e.preventDefault();
|
|
1150
|
-
e.stopPropagation();
|
|
1151
|
-
}
|
|
1152
|
-
}, { passive: false });
|
|
1525
|
+
}
|
|
1526
|
+
if (this.viewerMain && this.wheelEventTarget !== this.viewerMain) {
|
|
1527
|
+
this.wheelEventTarget?.removeEventListener('wheel', this.handleViewerWheel);
|
|
1528
|
+
this.wheelEventTarget = this.viewerMain;
|
|
1529
|
+
this.wheelEventTarget.addEventListener('wheel', this.handleViewerWheel, { passive: false });
|
|
1153
1530
|
}
|
|
1154
1531
|
}
|
|
1155
1532
|
|
|
@@ -1232,82 +1609,152 @@ export class DeesPdfViewer extends DeesElement {
|
|
|
1232
1609
|
return Math.min(Math.max(value, min), max);
|
|
1233
1610
|
}
|
|
1234
1611
|
|
|
1235
|
-
private
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
this.renderAbortController.abort();
|
|
1239
|
-
this.renderAbortController = null;
|
|
1612
|
+
private cleanupDocument(): Promise<void> {
|
|
1613
|
+
if (this.cleanupPromise) {
|
|
1614
|
+
return this.cleanupPromise;
|
|
1240
1615
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
} catch (error) {
|
|
1247
|
-
// Ignore errors
|
|
1616
|
+
const cleanupPromise = this.performDocumentCleanup();
|
|
1617
|
+
this.cleanupPromise = cleanupPromise;
|
|
1618
|
+
void cleanupPromise.finally(() => {
|
|
1619
|
+
if (this.cleanupPromise === cleanupPromise) {
|
|
1620
|
+
this.cleanupPromise = null;
|
|
1248
1621
|
}
|
|
1249
|
-
|
|
1622
|
+
}).catch(() => {});
|
|
1623
|
+
return cleanupPromise;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
private async performDocumentCleanup(): Promise<void> {
|
|
1627
|
+
this.renderAdmissionOpen = false;
|
|
1628
|
+
this.renderGeneration += 1;
|
|
1629
|
+
this.rerenderQueueEpoch += 1;
|
|
1630
|
+
this.thumbnailRenderGeneration += 1;
|
|
1631
|
+
this.thumbnailRenderQueueEpoch += 1;
|
|
1632
|
+
this.thumbnailRenderQueued = false;
|
|
1633
|
+
if (this.rerenderRecoveryTimeout) {
|
|
1634
|
+
clearTimeout(this.rerenderRecoveryTimeout);
|
|
1635
|
+
this.rerenderRecoveryTimeout = undefined;
|
|
1636
|
+
}
|
|
1637
|
+
this.intersectionObserver?.disconnect();
|
|
1638
|
+
this.intersectionObserver = undefined;
|
|
1639
|
+
this.resizeObserver?.disconnect();
|
|
1640
|
+
this.resizeObserver = undefined;
|
|
1641
|
+
this.wheelEventTarget?.removeEventListener('wheel', this.handleViewerWheel);
|
|
1642
|
+
this.wheelEventTarget = null;
|
|
1643
|
+
if (this.scrollThrottleTimeout) {
|
|
1644
|
+
clearTimeout(this.scrollThrottleTimeout);
|
|
1645
|
+
this.scrollThrottleTimeout = undefined;
|
|
1250
1646
|
}
|
|
1251
1647
|
|
|
1252
|
-
|
|
1253
|
-
this.
|
|
1648
|
+
const abortController = this.renderAbortController;
|
|
1649
|
+
this.renderAbortController = null;
|
|
1650
|
+
abortController?.abort();
|
|
1651
|
+
const loadOperation = this.loadOperation;
|
|
1652
|
+
const pdfLoadPromise = this.pdfLoadPromise;
|
|
1653
|
+
const pageRenderTasks = [...this.pageRenderTasks.values()];
|
|
1654
|
+
const textLayerRenderTasks = [...this.textLayerRenderTasks.values()];
|
|
1655
|
+
const thumbnailRenderTasks = [...this.thumbnailRenderTasks];
|
|
1656
|
+
const pageRenderPipelines = [...this.pageRenderPipelines];
|
|
1657
|
+
const documentUsePipelines = [...this.documentUsePipelines];
|
|
1658
|
+
const rerenderPromise = this.rerenderPromise;
|
|
1659
|
+
this.rerenderQueued = false;
|
|
1660
|
+
const thumbnailRenderPromise = this.thumbnailRenderPromise;
|
|
1661
|
+
const currentRenderPromise = this.currentRenderPromise;
|
|
1662
|
+
for (const cleanup of this.textSelectionCleanupFunctions.values()) {
|
|
1663
|
+
cleanup();
|
|
1664
|
+
}
|
|
1665
|
+
this.textSelectionCleanupFunctions.clear();
|
|
1254
1666
|
|
|
1255
|
-
|
|
1256
|
-
this.pageRenderTasks.forEach(task => {
|
|
1667
|
+
for (const task of pageRenderTasks) {
|
|
1257
1668
|
try {
|
|
1258
1669
|
task.cancel();
|
|
1259
|
-
} catch
|
|
1260
|
-
//
|
|
1670
|
+
} catch {
|
|
1671
|
+
// The tracked task promise still participates in settlement below.
|
|
1261
1672
|
}
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
// Cancel text layer render tasks
|
|
1266
|
-
this.textLayerRenderTasks.forEach(task => {
|
|
1673
|
+
}
|
|
1674
|
+
for (const task of textLayerRenderTasks) {
|
|
1267
1675
|
try {
|
|
1268
|
-
task.cancel
|
|
1269
|
-
} catch
|
|
1270
|
-
//
|
|
1676
|
+
task.textLayer.cancel();
|
|
1677
|
+
} catch {
|
|
1678
|
+
// The tracked task promise still participates in settlement below.
|
|
1271
1679
|
}
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
// Cancel any thumbnail render tasks
|
|
1276
|
-
for (const task of (this.thumbnailRenderTasks || [])) {
|
|
1680
|
+
}
|
|
1681
|
+
for (const task of thumbnailRenderTasks) {
|
|
1277
1682
|
try {
|
|
1278
1683
|
task.cancel();
|
|
1279
|
-
} catch
|
|
1280
|
-
//
|
|
1684
|
+
} catch {
|
|
1685
|
+
// The tracked task promise still participates in settlement below.
|
|
1281
1686
|
}
|
|
1282
1687
|
}
|
|
1283
|
-
|
|
1688
|
+
const renderCanvases = this.shadowRoot?.querySelectorAll<HTMLCanvasElement>(
|
|
1689
|
+
'.page-canvas, .thumbnail-canvas',
|
|
1690
|
+
);
|
|
1691
|
+
renderCanvases?.forEach((canvasArg) => {
|
|
1692
|
+
canvasArg.width = 0;
|
|
1693
|
+
canvasArg.height = 0;
|
|
1694
|
+
canvasArg.style.removeProperty('width');
|
|
1695
|
+
canvasArg.style.removeProperty('height');
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
const pdfDocument = this.pdfDocument;
|
|
1699
|
+
this.pdfDocument = null;
|
|
1700
|
+
const releasePromise = pdfDocument
|
|
1701
|
+
? PdfManager.releaseDocument(pdfDocument).catch((error) => {
|
|
1702
|
+
console.error('Error destroying PDF document:', error);
|
|
1703
|
+
})
|
|
1704
|
+
: null;
|
|
1705
|
+
|
|
1706
|
+
await settlePromisesWithin([
|
|
1707
|
+
...(pdfLoadPromise ? [pdfLoadPromise] : []),
|
|
1708
|
+
...(currentRenderPromise ? [currentRenderPromise] : []),
|
|
1709
|
+
...documentUsePipelines,
|
|
1710
|
+
...(rerenderPromise ? [rerenderPromise] : []),
|
|
1711
|
+
...pageRenderPipelines,
|
|
1712
|
+
...(thumbnailRenderPromise ? [thumbnailRenderPromise] : []),
|
|
1713
|
+
...pageRenderTasks.map((task) => task.promise),
|
|
1714
|
+
...textLayerRenderTasks.map((task) => task.promise),
|
|
1715
|
+
...thumbnailRenderTasks.map((task) => task.promise),
|
|
1716
|
+
...(releasePromise ? [releasePromise] : []),
|
|
1717
|
+
], pipelineSettlementGraceMs);
|
|
1718
|
+
for (const cleanup of this.textSelectionCleanupFunctions.values()) {
|
|
1719
|
+
cleanup();
|
|
1720
|
+
}
|
|
1721
|
+
this.textSelectionCleanupFunctions.clear();
|
|
1284
1722
|
|
|
1285
|
-
|
|
1723
|
+
if (this.pdfLoadPromise === pdfLoadPromise) {
|
|
1724
|
+
this.pdfLoadPromise = null;
|
|
1725
|
+
}
|
|
1726
|
+
if (this.loadOperation === loadOperation) {
|
|
1727
|
+
this.loadOperation = null;
|
|
1728
|
+
}
|
|
1729
|
+
this.currentRenderPromise = null;
|
|
1730
|
+
this.currentRenderTask = null;
|
|
1731
|
+
this.pageRenderTasks.clear();
|
|
1732
|
+
this.pageRenderPipelines.clear();
|
|
1733
|
+
this.documentUsePipelines.clear();
|
|
1734
|
+
if (this.rerenderPromise === rerenderPromise) {
|
|
1735
|
+
this.rerenderPromise = null;
|
|
1736
|
+
}
|
|
1737
|
+
this.textLayerRenderTasks.clear();
|
|
1738
|
+
for (const task of thumbnailRenderTasks) {
|
|
1739
|
+
this.thumbnailRenderTasks.delete(task);
|
|
1740
|
+
}
|
|
1741
|
+
if (this.thumbnailRenderPromise === thumbnailRenderPromise) {
|
|
1742
|
+
this.thumbnailRenderPromise = null;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
this.loading = false;
|
|
1286
1746
|
this.renderState = 'idle';
|
|
1287
1747
|
this.pageRendering = false;
|
|
1288
1748
|
this.pageNumPending = null;
|
|
1289
1749
|
this.thumbnailData = [];
|
|
1290
1750
|
this.pageData = [];
|
|
1291
1751
|
this.documentId = '';
|
|
1292
|
-
|
|
1293
|
-
|
|
1752
|
+
this.totalPages = 1;
|
|
1753
|
+
this.currentPage = 1;
|
|
1754
|
+
this.pdfFileSize = 0;
|
|
1294
1755
|
if (this.canvas && this.ctx) {
|
|
1295
1756
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
1296
1757
|
}
|
|
1297
|
-
|
|
1298
|
-
// Destroy the document to free memory
|
|
1299
|
-
if (this.pdfDocument) {
|
|
1300
|
-
try {
|
|
1301
|
-
this.pdfDocument.destroy();
|
|
1302
|
-
} catch (error) {
|
|
1303
|
-
console.error('Error destroying PDF document:', error);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
// Finally null the document reference
|
|
1308
|
-
this.pdfDocument = null;
|
|
1309
|
-
|
|
1310
|
-
// Request update to reflect state changes
|
|
1311
1758
|
this.requestUpdate();
|
|
1312
1759
|
}
|
|
1313
1760
|
}
|