svelte-pdf-view 0.1.13 → 0.3.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/README.md +2 -4
- package/dist/PdfRenderer.svelte +27 -4
- package/dist/PdfToolbar.svelte +10 -2
- package/dist/PdfViewer.svelte +15 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/pdf-viewer/AnnotationLayerBuilder.d.ts +54 -0
- package/dist/pdf-viewer/AnnotationLayerBuilder.js +145 -0
- package/dist/pdf-viewer/PDFPageView.d.ts +7 -1
- package/dist/pdf-viewer/PDFPageView.js +51 -0
- package/dist/pdf-viewer/PDFViewerCore.d.ts +1 -0
- package/dist/pdf-viewer/PDFViewerCore.js +12 -1
- package/dist/pdf-viewer/PdfPresentationMode.d.ts +96 -0
- package/dist/pdf-viewer/PdfPresentationMode.js +437 -0
- package/dist/pdf-viewer/SimpleLinkService.d.ts +74 -0
- package/dist/pdf-viewer/SimpleLinkService.js +212 -0
- package/dist/pdf-viewer/context.d.ts +12 -0
- package/dist/pdf-viewer/context.js +8 -0
- package/dist/pdf-viewer/renderer-styles.d.ts +1 -1
- package/dist/pdf-viewer/renderer-styles.js +155 -0
- package/dist/pdf-viewer/styles.css +1 -137
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -314,11 +314,9 @@ interface PdfViewerActions {
|
|
|
314
314
|
/>
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
##
|
|
317
|
+
## Use Cases
|
|
318
318
|
|
|
319
|
-
|
|
320
|
-
- Firefox 78+
|
|
321
|
-
- Safari 14+
|
|
319
|
+
**Texpile** ([texpile.com](https://texpile.com/)) uses this package to display generated documents (PDFs) directly in the browser.
|
|
322
320
|
|
|
323
321
|
## License
|
|
324
322
|
|
package/dist/PdfRenderer.svelte
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
type PdfSource
|
|
8
8
|
} from './pdf-viewer/context.js';
|
|
9
9
|
import { getPdfJs } from './pdf-viewer/pdfjs-singleton.js';
|
|
10
|
+
import { PdfPresentationMode } from './pdf-viewer/PdfPresentationMode.js';
|
|
10
11
|
import { rendererStyles } from './pdf-viewer/renderer-styles.js';
|
|
11
12
|
|
|
12
13
|
interface Props {
|
|
@@ -51,6 +52,17 @@
|
|
|
51
52
|
let viewer: import('./pdf-viewer/PDFViewerCore.js').PDFViewerCore | null = null;
|
|
52
53
|
let findController: import('./pdf-viewer/FindController.js').FindController | null = null;
|
|
53
54
|
|
|
55
|
+
// Presentation mode
|
|
56
|
+
const presentationMode = new PdfPresentationMode({
|
|
57
|
+
onStateChange: (newState) => {
|
|
58
|
+
viewerState.presentationMode = newState;
|
|
59
|
+
},
|
|
60
|
+
onPageChange: (pageNumber) => {
|
|
61
|
+
// Sync page number back to main viewer when changed in presentation mode
|
|
62
|
+
viewer?.scrollToPage(pageNumber);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
54
66
|
async function loadPdf(source: PdfSource) {
|
|
55
67
|
if (!BROWSER || !scrollContainerEl) return;
|
|
56
68
|
|
|
@@ -127,10 +139,13 @@
|
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
const loadingTask = pdfjs.getDocument(documentSource);
|
|
130
|
-
const
|
|
142
|
+
const loadedPdfDocument = await loadingTask.promise;
|
|
131
143
|
|
|
132
|
-
await newViewer.setDocument(
|
|
133
|
-
findController.setDocument(
|
|
144
|
+
await newViewer.setDocument(loadedPdfDocument);
|
|
145
|
+
findController.setDocument(loadedPdfDocument);
|
|
146
|
+
|
|
147
|
+
// Set document on presentation mode
|
|
148
|
+
presentationMode.setDocument(loadedPdfDocument);
|
|
134
149
|
|
|
135
150
|
viewer = newViewer;
|
|
136
151
|
viewerState.loading = false;
|
|
@@ -174,7 +189,14 @@
|
|
|
174
189
|
viewerState.searchTotal = 0;
|
|
175
190
|
}
|
|
176
191
|
},
|
|
177
|
-
download: async () => {} // Download is handled by PdfViewer, not renderer
|
|
192
|
+
download: async () => {}, // Download is handled by PdfViewer, not renderer
|
|
193
|
+
enterPresentationMode: async () => {
|
|
194
|
+
presentationMode.setCurrentPage(viewerState.currentPage);
|
|
195
|
+
return presentationMode.request();
|
|
196
|
+
},
|
|
197
|
+
exitPresentationMode: async () => {
|
|
198
|
+
await presentationMode.exit();
|
|
199
|
+
}
|
|
178
200
|
};
|
|
179
201
|
|
|
180
202
|
onMount(async () => {
|
|
@@ -225,6 +247,7 @@
|
|
|
225
247
|
viewer = null;
|
|
226
248
|
}
|
|
227
249
|
findController = null;
|
|
250
|
+
presentationMode.destroy();
|
|
228
251
|
// Note: Worker is a global singleton, not cleaned up per-component
|
|
229
252
|
// Use destroyPdfJs() from pdfjs-singleton.js if you need to fully cleanup
|
|
230
253
|
});
|
package/dist/PdfToolbar.svelte
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
Search,
|
|
8
8
|
ChevronLeft,
|
|
9
9
|
ChevronRight,
|
|
10
|
-
Download
|
|
10
|
+
Download,
|
|
11
|
+
Presentation
|
|
11
12
|
} from '@lucide/svelte';
|
|
12
13
|
import { getPdfViewerContext } from './pdf-viewer/context.js';
|
|
13
14
|
|
|
@@ -116,8 +117,15 @@
|
|
|
116
117
|
{/if}
|
|
117
118
|
</div>
|
|
118
119
|
|
|
119
|
-
<!-- Download -->
|
|
120
|
+
<!-- Presentation & Download -->
|
|
120
121
|
<div class="pdf-toolbar-group">
|
|
122
|
+
<button
|
|
123
|
+
onclick={() => actions.enterPresentationMode()}
|
|
124
|
+
aria-label="Presentation Mode"
|
|
125
|
+
title="Presentation Mode"
|
|
126
|
+
>
|
|
127
|
+
<Presentation size={18} />
|
|
128
|
+
</button>
|
|
121
129
|
<button onclick={() => actions.download()} aria-label="Download PDF" title="Download">
|
|
122
130
|
<Download size={18} />
|
|
123
131
|
</button>
|
package/dist/PdfViewer.svelte
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { Snippet } from 'svelte';
|
|
9
9
|
import {
|
|
10
10
|
setPdfViewerContext,
|
|
11
|
+
PresentationModeState,
|
|
11
12
|
type PdfViewerState,
|
|
12
13
|
type PdfViewerActions,
|
|
13
14
|
type PdfSource
|
|
@@ -52,7 +53,8 @@
|
|
|
52
53
|
searchQuery: '',
|
|
53
54
|
searchCurrent: 0,
|
|
54
55
|
searchTotal: 0,
|
|
55
|
-
isSearching: false
|
|
56
|
+
isSearching: false,
|
|
57
|
+
presentationMode: PresentationModeState.NORMAL
|
|
56
58
|
});
|
|
57
59
|
|
|
58
60
|
// Renderer actions - will be populated when renderer mounts
|
|
@@ -116,7 +118,18 @@
|
|
|
116
118
|
searchNext: () => rendererActions?.searchNext(),
|
|
117
119
|
searchPrevious: () => rendererActions?.searchPrevious(),
|
|
118
120
|
clearSearch: () => rendererActions?.clearSearch(),
|
|
119
|
-
download: downloadPdf
|
|
121
|
+
download: downloadPdf,
|
|
122
|
+
enterPresentationMode: async () => {
|
|
123
|
+
if (rendererActions) {
|
|
124
|
+
return rendererActions.enterPresentationMode();
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
},
|
|
128
|
+
exitPresentationMode: async () => {
|
|
129
|
+
if (rendererActions) {
|
|
130
|
+
await rendererActions.exitPresentationMode();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
120
133
|
};
|
|
121
134
|
|
|
122
135
|
// Set up context
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as PdfViewer, Toolbar as PdfToolbar, Renderer as PdfRenderer } from './PdfViewer.svelte';
|
|
2
2
|
export type { PdfSource } from './pdf-viewer/context.js';
|
|
3
|
-
export { getPdfViewerContext, type PdfViewerState, type PdfViewerActions, type PdfViewerContext } from './pdf-viewer/context.js';
|
|
3
|
+
export { getPdfViewerContext, PresentationModeState, type PdfViewerState, type PdfViewerActions, type PdfViewerContext } from './pdf-viewer/context.js';
|
|
4
4
|
export { destroyPdfJs } from './pdf-viewer/pdfjs-singleton.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Reexport your entry components here
|
|
2
2
|
export { default as PdfViewer, Toolbar as PdfToolbar, Renderer as PdfRenderer } from './PdfViewer.svelte';
|
|
3
3
|
// Export context for custom toolbars
|
|
4
|
-
export { getPdfViewerContext } from './pdf-viewer/context.js';
|
|
4
|
+
export { getPdfViewerContext, PresentationModeState } from './pdf-viewer/context.js';
|
|
5
5
|
// Export PDF.js singleton utilities
|
|
6
6
|
export { destroyPdfJs } from './pdf-viewer/pdfjs-singleton.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnnotationLayerBuilder - Renders PDF annotations (links, form widgets, popups).
|
|
3
|
+
* Adapted from PDF.js annotation_layer_builder.js
|
|
4
|
+
*/
|
|
5
|
+
import type { PDFPageProxy, PageViewport } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
6
|
+
import type { SimpleLinkService } from './SimpleLinkService.js';
|
|
7
|
+
export interface AnnotationLayerBuilderOptions {
|
|
8
|
+
pdfPage: PDFPageProxy;
|
|
9
|
+
linkService: SimpleLinkService;
|
|
10
|
+
annotationStorage?: any;
|
|
11
|
+
imageResourcesPath?: string;
|
|
12
|
+
renderForms?: boolean;
|
|
13
|
+
onAppend?: (div: HTMLDivElement) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface AnnotationLayerBuilderRenderOptions {
|
|
16
|
+
viewport: PageViewport;
|
|
17
|
+
intent?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class AnnotationLayerBuilder {
|
|
20
|
+
private pdfPage;
|
|
21
|
+
private linkService;
|
|
22
|
+
private annotationStorage;
|
|
23
|
+
private imageResourcesPath;
|
|
24
|
+
private renderForms;
|
|
25
|
+
private onAppend;
|
|
26
|
+
private annotationLayer;
|
|
27
|
+
div: HTMLDivElement | null;
|
|
28
|
+
private cancelled;
|
|
29
|
+
constructor(options: AnnotationLayerBuilderOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Render the annotation layer.
|
|
32
|
+
*/
|
|
33
|
+
render(options: AnnotationLayerBuilderRenderOptions): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Update the annotation layer viewport.
|
|
36
|
+
*/
|
|
37
|
+
update(viewport: PageViewport): void;
|
|
38
|
+
/**
|
|
39
|
+
* Cancel rendering.
|
|
40
|
+
*/
|
|
41
|
+
cancel(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Hide the annotation layer.
|
|
44
|
+
*/
|
|
45
|
+
hide(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Show the annotation layer.
|
|
48
|
+
*/
|
|
49
|
+
show(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Destroy the annotation layer.
|
|
52
|
+
*/
|
|
53
|
+
destroy(): void;
|
|
54
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/* Copyright 2024 Mozilla Foundation
|
|
2
|
+
*
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
// Dynamically loaded pdfjs components
|
|
16
|
+
let AnnotationLayer;
|
|
17
|
+
async function ensurePdfJsLoaded() {
|
|
18
|
+
if (!AnnotationLayer) {
|
|
19
|
+
const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
20
|
+
AnnotationLayer = pdfjs.AnnotationLayer;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class AnnotationLayerBuilder {
|
|
24
|
+
pdfPage;
|
|
25
|
+
linkService;
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
annotationStorage;
|
|
28
|
+
imageResourcesPath;
|
|
29
|
+
renderForms;
|
|
30
|
+
onAppend;
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
annotationLayer = null;
|
|
33
|
+
div = null;
|
|
34
|
+
cancelled = false;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.pdfPage = options.pdfPage;
|
|
37
|
+
this.linkService = options.linkService;
|
|
38
|
+
this.annotationStorage = options.annotationStorage ?? null;
|
|
39
|
+
this.imageResourcesPath = options.imageResourcesPath ?? '';
|
|
40
|
+
this.renderForms = options.renderForms ?? true;
|
|
41
|
+
this.onAppend = options.onAppend ?? null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render the annotation layer.
|
|
45
|
+
*/
|
|
46
|
+
async render(options) {
|
|
47
|
+
const { viewport, intent = 'display' } = options;
|
|
48
|
+
if (this.div) {
|
|
49
|
+
// Already rendered - just update
|
|
50
|
+
if (this.cancelled || !this.annotationLayer) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.annotationLayer.update({
|
|
54
|
+
viewport: viewport.clone({ dontFlip: true })
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
await ensurePdfJsLoaded();
|
|
59
|
+
const annotations = await this.pdfPage.getAnnotations({ intent });
|
|
60
|
+
if (this.cancelled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Create annotation layer div
|
|
64
|
+
this.div = document.createElement('div');
|
|
65
|
+
this.div.className = 'annotationLayer';
|
|
66
|
+
this.onAppend?.(this.div);
|
|
67
|
+
// Set layer dimensions directly (setLayerDimensions uses CSS round() which may not work everywhere)
|
|
68
|
+
const { width, height } = viewport;
|
|
69
|
+
this.div.style.width = `${Math.floor(width)}px`;
|
|
70
|
+
this.div.style.height = `${Math.floor(height)}px`;
|
|
71
|
+
// Initialize the annotation layer
|
|
72
|
+
this.annotationLayer = new AnnotationLayer({
|
|
73
|
+
div: this.div,
|
|
74
|
+
accessibilityManager: null,
|
|
75
|
+
annotationCanvasMap: null,
|
|
76
|
+
annotationEditorUIManager: null,
|
|
77
|
+
page: this.pdfPage,
|
|
78
|
+
viewport: viewport.clone({ dontFlip: true }),
|
|
79
|
+
structTreeLayer: null,
|
|
80
|
+
commentManager: null,
|
|
81
|
+
linkService: this.linkService,
|
|
82
|
+
annotationStorage: this.annotationStorage
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
});
|
|
85
|
+
if (annotations.length === 0) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Render annotations
|
|
89
|
+
await this.annotationLayer.render({
|
|
90
|
+
annotations,
|
|
91
|
+
imageResourcesPath: this.imageResourcesPath,
|
|
92
|
+
renderForms: this.renderForms,
|
|
93
|
+
linkService: this.linkService,
|
|
94
|
+
downloadManager: undefined,
|
|
95
|
+
annotationStorage: this.annotationStorage,
|
|
96
|
+
enableScripting: false,
|
|
97
|
+
hasJSActions: false,
|
|
98
|
+
fieldObjects: null
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Update the annotation layer viewport.
|
|
103
|
+
*/
|
|
104
|
+
update(viewport) {
|
|
105
|
+
if (!this.div || !this.annotationLayer) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.annotationLayer.update({
|
|
109
|
+
viewport: viewport.clone({ dontFlip: true })
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Cancel rendering.
|
|
114
|
+
*/
|
|
115
|
+
cancel() {
|
|
116
|
+
this.cancelled = true;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Hide the annotation layer.
|
|
120
|
+
*/
|
|
121
|
+
hide() {
|
|
122
|
+
if (this.div) {
|
|
123
|
+
this.div.hidden = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Show the annotation layer.
|
|
128
|
+
*/
|
|
129
|
+
show() {
|
|
130
|
+
if (this.div) {
|
|
131
|
+
this.div.hidden = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Destroy the annotation layer.
|
|
136
|
+
*/
|
|
137
|
+
destroy() {
|
|
138
|
+
this.cancel();
|
|
139
|
+
if (this.div) {
|
|
140
|
+
this.div.remove();
|
|
141
|
+
this.div = null;
|
|
142
|
+
}
|
|
143
|
+
this.annotationLayer = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PDFPageView - Renders a single PDF page with canvas and
|
|
2
|
+
* PDFPageView - Renders a single PDF page with canvas, text layer, and annotation layer.
|
|
3
3
|
* This is a derivative work based on PDF.js pdf_page_view.js
|
|
4
4
|
*/
|
|
5
5
|
import type { PDFPageProxy, PageViewport } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
6
6
|
import type { EventBus } from './EventBus.js';
|
|
7
|
+
import type { SimpleLinkService } from './SimpleLinkService.js';
|
|
7
8
|
export interface PDFPageViewOptions {
|
|
8
9
|
container: HTMLElement;
|
|
9
10
|
id: number;
|
|
@@ -11,6 +12,7 @@ export interface PDFPageViewOptions {
|
|
|
11
12
|
eventBus: EventBus;
|
|
12
13
|
scale?: number;
|
|
13
14
|
rotation?: number;
|
|
15
|
+
linkService?: SimpleLinkService;
|
|
14
16
|
}
|
|
15
17
|
export declare const RenderingStates: {
|
|
16
18
|
readonly INITIAL: 0;
|
|
@@ -33,6 +35,9 @@ export declare class PDFPageView {
|
|
|
33
35
|
private canvasWrapper;
|
|
34
36
|
private textLayerDiv;
|
|
35
37
|
private loadingDiv;
|
|
38
|
+
private linkService;
|
|
39
|
+
private annotationLayerBuilder;
|
|
40
|
+
private annotationLayerRendered;
|
|
36
41
|
renderingState: RenderingState;
|
|
37
42
|
private renderTask;
|
|
38
43
|
private textLayer;
|
|
@@ -51,6 +56,7 @@ export declare class PDFPageView {
|
|
|
51
56
|
reset(): void;
|
|
52
57
|
draw(): Promise<void>;
|
|
53
58
|
private renderTextLayer;
|
|
59
|
+
private renderAnnotationLayer;
|
|
54
60
|
cancelRendering(): void;
|
|
55
61
|
destroy(): void;
|
|
56
62
|
get width(): number;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* See the License for the specific language governing permissions and
|
|
13
13
|
* limitations under the License.
|
|
14
14
|
*/
|
|
15
|
+
import { AnnotationLayerBuilder } from './AnnotationLayerBuilder.js';
|
|
15
16
|
// Dynamically loaded pdfjs utilities
|
|
16
17
|
let setLayerDimensions;
|
|
17
18
|
async function ensurePdfJsLoaded() {
|
|
@@ -40,6 +41,10 @@ export class PDFPageView {
|
|
|
40
41
|
canvasWrapper = null;
|
|
41
42
|
textLayerDiv = null;
|
|
42
43
|
loadingDiv = null;
|
|
44
|
+
// Annotation layer
|
|
45
|
+
linkService = null;
|
|
46
|
+
annotationLayerBuilder = null;
|
|
47
|
+
annotationLayerRendered = false;
|
|
43
48
|
renderingState = RenderingStates.INITIAL;
|
|
44
49
|
renderTask = null;
|
|
45
50
|
// Text layer instance for updates
|
|
@@ -55,6 +60,7 @@ export class PDFPageView {
|
|
|
55
60
|
this.scale = options.scale ?? 1.0;
|
|
56
61
|
this.rotation = options.rotation ?? 0;
|
|
57
62
|
this.viewport = options.defaultViewport;
|
|
63
|
+
this.linkService = options.linkService ?? null;
|
|
58
64
|
// Create page container
|
|
59
65
|
this.div = document.createElement('div');
|
|
60
66
|
this.div.className = 'page';
|
|
@@ -120,6 +126,10 @@ export class PDFPageView {
|
|
|
120
126
|
});
|
|
121
127
|
this.textLayerDiv.hidden = false;
|
|
122
128
|
}
|
|
129
|
+
// Update annotation layer
|
|
130
|
+
if (this.annotationLayerBuilder && this.annotationLayerRendered) {
|
|
131
|
+
this.annotationLayerBuilder.update(this.viewport);
|
|
132
|
+
}
|
|
123
133
|
// Re-render canvas
|
|
124
134
|
this.resetCanvas();
|
|
125
135
|
this.draw();
|
|
@@ -163,6 +173,12 @@ export class PDFPageView {
|
|
|
163
173
|
this.textLayerRendered = false;
|
|
164
174
|
this.textDivs = [];
|
|
165
175
|
this.textContentItemsStr = [];
|
|
176
|
+
// Clear annotation layer
|
|
177
|
+
if (this.annotationLayerBuilder) {
|
|
178
|
+
this.annotationLayerBuilder.destroy();
|
|
179
|
+
this.annotationLayerBuilder = null;
|
|
180
|
+
}
|
|
181
|
+
this.annotationLayerRendered = false;
|
|
166
182
|
// Show loading
|
|
167
183
|
if (this.loadingDiv) {
|
|
168
184
|
this.loadingDiv.style.display = '';
|
|
@@ -205,6 +221,10 @@ export class PDFPageView {
|
|
|
205
221
|
if (!this.textLayerRendered) {
|
|
206
222
|
await this.renderTextLayer();
|
|
207
223
|
}
|
|
224
|
+
// Render annotation layer (only if not already rendered)
|
|
225
|
+
if (!this.annotationLayerRendered) {
|
|
226
|
+
await this.renderAnnotationLayer();
|
|
227
|
+
}
|
|
208
228
|
this.renderingState = RenderingStates.FINISHED;
|
|
209
229
|
this.eventBus.dispatch('pagerendered', {
|
|
210
230
|
pageNumber: this.id,
|
|
@@ -271,6 +291,37 @@ export class PDFPageView {
|
|
|
271
291
|
console.error('Error rendering text layer:', error);
|
|
272
292
|
}
|
|
273
293
|
}
|
|
294
|
+
async renderAnnotationLayer() {
|
|
295
|
+
if (!this.pdfPage || !this.linkService) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// If annotation layer already rendered, just update it
|
|
299
|
+
if (this.annotationLayerRendered && this.annotationLayerBuilder) {
|
|
300
|
+
this.annotationLayerBuilder.update(this.viewport);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
this.annotationLayerBuilder = new AnnotationLayerBuilder({
|
|
305
|
+
pdfPage: this.pdfPage,
|
|
306
|
+
linkService: this.linkService,
|
|
307
|
+
renderForms: true,
|
|
308
|
+
onAppend: (div) => {
|
|
309
|
+
this.div.appendChild(div);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
await this.annotationLayerBuilder.render({
|
|
313
|
+
viewport: this.viewport
|
|
314
|
+
});
|
|
315
|
+
this.annotationLayerRendered = true;
|
|
316
|
+
this.eventBus.dispatch('annotationlayerrendered', {
|
|
317
|
+
pageNumber: this.id,
|
|
318
|
+
source: this
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
console.error('Error rendering annotation layer:', error);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
274
325
|
cancelRendering() {
|
|
275
326
|
if (this.renderTask) {
|
|
276
327
|
this.renderTask.cancel();
|
|
@@ -22,6 +22,7 @@ export declare class PDFViewerCore {
|
|
|
22
22
|
private scrollAbortController;
|
|
23
23
|
private renderingQueue;
|
|
24
24
|
private isRendering;
|
|
25
|
+
private linkService;
|
|
25
26
|
constructor(options: PDFViewerOptions);
|
|
26
27
|
private setupScrollListener;
|
|
27
28
|
setDocument(pdfDocument: PDFDocumentProxy): Promise<void>;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { EventBus } from './EventBus.js';
|
|
16
16
|
import { PDFPageView, RenderingStates } from './PDFPageView.js';
|
|
17
|
+
import { SimpleLinkService } from './SimpleLinkService.js';
|
|
17
18
|
const DEFAULT_SCALE = 1.0;
|
|
18
19
|
const MIN_SCALE = 0.1;
|
|
19
20
|
const MAX_SCALE = 10.0;
|
|
@@ -31,6 +32,8 @@ export class PDFViewerCore {
|
|
|
31
32
|
scrollAbortController = null;
|
|
32
33
|
renderingQueue = new Set();
|
|
33
34
|
isRendering = false;
|
|
35
|
+
// Link service for annotation navigation
|
|
36
|
+
linkService;
|
|
34
37
|
constructor(options) {
|
|
35
38
|
this.container = options.container;
|
|
36
39
|
this.eventBus = options.eventBus ?? new EventBus();
|
|
@@ -40,6 +43,10 @@ export class PDFViewerCore {
|
|
|
40
43
|
this.viewer = document.createElement('div');
|
|
41
44
|
this.viewer.className = 'pdfViewer';
|
|
42
45
|
this.container.appendChild(this.viewer);
|
|
46
|
+
// Create link service for annotation navigation
|
|
47
|
+
this.linkService = new SimpleLinkService({
|
|
48
|
+
eventBus: this.eventBus
|
|
49
|
+
});
|
|
43
50
|
// Setup scroll listener for lazy rendering
|
|
44
51
|
this.setupScrollListener();
|
|
45
52
|
}
|
|
@@ -61,6 +68,9 @@ export class PDFViewerCore {
|
|
|
61
68
|
this.cleanup();
|
|
62
69
|
this.pdfDocument = pdfDocument;
|
|
63
70
|
const numPages = pdfDocument.numPages;
|
|
71
|
+
// Setup link service with document and viewer
|
|
72
|
+
this.linkService.setDocument(pdfDocument);
|
|
73
|
+
this.linkService.setViewer(this);
|
|
64
74
|
// Create page views
|
|
65
75
|
for (let i = 1; i <= numPages; i++) {
|
|
66
76
|
const page = await pdfDocument.getPage(i);
|
|
@@ -74,7 +84,8 @@ export class PDFViewerCore {
|
|
|
74
84
|
defaultViewport: viewport,
|
|
75
85
|
eventBus: this.eventBus,
|
|
76
86
|
scale: this.currentScale,
|
|
77
|
-
rotation: this.currentRotation
|
|
87
|
+
rotation: this.currentRotation,
|
|
88
|
+
linkService: this.linkService
|
|
78
89
|
});
|
|
79
90
|
pageView.setPdfPage(page);
|
|
80
91
|
this.pages.push(pageView);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PdfPresentationMode - Fullscreen presentation mode for PDF viewing.
|
|
3
|
+
* This is a derivative work based on PDF.js pdf_presentation_mode.js
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Browser fullscreen mode
|
|
7
|
+
* - Black background
|
|
8
|
+
* - Single page view scaled to fit screen
|
|
9
|
+
* - No text layer or toolbar
|
|
10
|
+
* - Mouse/keyboard/touch navigation
|
|
11
|
+
*/
|
|
12
|
+
import type { PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
13
|
+
export declare enum PresentationModeState {
|
|
14
|
+
UNKNOWN = 0,
|
|
15
|
+
NORMAL = 1,
|
|
16
|
+
CHANGING = 2,
|
|
17
|
+
FULLSCREEN = 3
|
|
18
|
+
}
|
|
19
|
+
export interface PresentationModeCallbacks {
|
|
20
|
+
onStateChange?: (state: PresentationModeState) => void;
|
|
21
|
+
onPageChange?: (pageNumber: number) => void;
|
|
22
|
+
}
|
|
23
|
+
export declare class PdfPresentationMode {
|
|
24
|
+
private state;
|
|
25
|
+
private pdfDocument;
|
|
26
|
+
private currentPageNumber;
|
|
27
|
+
private totalPages;
|
|
28
|
+
private container;
|
|
29
|
+
private canvas;
|
|
30
|
+
private callbacks;
|
|
31
|
+
private fullscreenChangeAbortController;
|
|
32
|
+
private windowAbortController;
|
|
33
|
+
private mouseScrollTimeStamp;
|
|
34
|
+
private mouseScrollDelta;
|
|
35
|
+
private touchSwipeState;
|
|
36
|
+
private renderingPage;
|
|
37
|
+
constructor(callbacks?: PresentationModeCallbacks);
|
|
38
|
+
/**
|
|
39
|
+
* Set the PDF document for presentation
|
|
40
|
+
*/
|
|
41
|
+
setDocument(pdfDocument: PDFDocumentProxy | null): void;
|
|
42
|
+
/**
|
|
43
|
+
* Set the current page number (used when entering presentation mode)
|
|
44
|
+
*/
|
|
45
|
+
setCurrentPage(pageNumber: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Check if presentation mode is active
|
|
48
|
+
*/
|
|
49
|
+
get active(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Get current state
|
|
52
|
+
*/
|
|
53
|
+
get currentState(): PresentationModeState;
|
|
54
|
+
/**
|
|
55
|
+
* Request entering fullscreen presentation mode
|
|
56
|
+
*/
|
|
57
|
+
request(): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Exit presentation mode
|
|
60
|
+
*/
|
|
61
|
+
exit(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Go to next page
|
|
64
|
+
*/
|
|
65
|
+
nextPage(): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Go to previous page
|
|
68
|
+
*/
|
|
69
|
+
previousPage(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Go to a specific page
|
|
72
|
+
*/
|
|
73
|
+
goToPage(pageNumber: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Destroy and cleanup
|
|
76
|
+
*/
|
|
77
|
+
destroy(): void;
|
|
78
|
+
private createPresentationContainer;
|
|
79
|
+
private destroyPresentationContainer;
|
|
80
|
+
private renderCurrentPage;
|
|
81
|
+
private notifyStateChange;
|
|
82
|
+
private enter;
|
|
83
|
+
private doExit;
|
|
84
|
+
private handleMouseWheel;
|
|
85
|
+
private normalizeWheelDelta;
|
|
86
|
+
private handleMouseDown;
|
|
87
|
+
private handleKeyDown;
|
|
88
|
+
private handleContextMenu;
|
|
89
|
+
private handleTouchSwipe;
|
|
90
|
+
private handleResize;
|
|
91
|
+
private resetMouseScrollState;
|
|
92
|
+
private addWindowListeners;
|
|
93
|
+
private removeWindowListeners;
|
|
94
|
+
private addFullscreenChangeListeners;
|
|
95
|
+
private removeFullscreenChangeListeners;
|
|
96
|
+
}
|