svelte-pdf-view 0.1.11 → 0.1.13

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 CHANGED
@@ -112,12 +112,37 @@ The main container component that provides context for toolbar and renderer.
112
112
  </PdfViewer>
113
113
  ```
114
114
 
115
- | Prop | Type | Default | Description |
116
- | ------------------ | ----------- | -------- | ---------------------------------------------------------------------- |
117
- | `src` | `PdfSource` | required | PDF source (URL, ArrayBuffer, Uint8Array, or Blob) |
118
- | `scale` | `number` | `1.0` | Initial zoom scale |
119
- | `downloadFilename` | `string` | - | Custom filename for PDF download (default: from URL or 'document.pdf') |
120
- | `class` | `string` | `''` | CSS class for the container |
115
+ | Prop | Type | Default | Description |
116
+ | ------------------ | ------------------------- | -------- | ---------------------------------------------------------------------- |
117
+ | `src` | `PdfSource` | required | PDF source (URL, ArrayBuffer, Uint8Array, or Blob) |
118
+ | `scale` | `number` | `1.0` | Initial zoom scale |
119
+ | `downloadFilename` | `string` | - | Custom filename for PDF download (default: from URL or 'document.pdf') |
120
+ | `onerror` | `(error: string) => void` | - | Callback when PDF fails to load |
121
+ | `class` | `string` | `''` | CSS class for the container |
122
+
123
+ #### Error Handling
124
+
125
+ ```svelte
126
+ <script lang="ts">
127
+ import { PdfViewer, PdfToolbar, PdfRenderer } from 'svelte-pdf-view';
128
+
129
+ let errorMessage = $state<string | null>(null);
130
+
131
+ function handleError(error: string) {
132
+ errorMessage = error;
133
+ console.error('PDF failed to load:', error);
134
+ }
135
+ </script>
136
+
137
+ {#if errorMessage}
138
+ <div class="error-banner">Failed to load PDF: {errorMessage}</div>
139
+ {/if}
140
+
141
+ <PdfViewer src="/document.pdf" onerror={handleError}>
142
+ <PdfToolbar />
143
+ <PdfRenderer />
144
+ </PdfViewer>
145
+ ```
121
146
 
122
147
  ### `<PdfToolbar>`
123
148
 
@@ -213,18 +238,19 @@ You can create your own toolbar using the context API:
213
238
 
214
239
  #### `actions: PdfViewerActions`
215
240
 
216
- | Method | Description |
217
- | -------------------------- | ---------------------------- |
218
- | `zoomIn()` | Increase zoom level |
219
- | `zoomOut()` | Decrease zoom level |
220
- | `setScale(scale: number)` | Set specific zoom scale |
221
- | `rotateClockwise()` | Rotate 90° clockwise |
222
- | `rotateCounterClockwise()` | Rotate 90° counter-clockwise |
223
- | `goToPage(page: number)` | Navigate to specific page |
224
- | `search(query: string)` | Search for text |
225
- | `searchNext()` | Go to next search match |
226
- | `searchPrevious()` | Go to previous search match |
227
- | `clearSearch()` | Clear search highlights |
241
+ | Method | Description |
242
+ | ----------------------------- | ---------------------------- |
243
+ | `zoomIn()` | Increase zoom level |
244
+ | `zoomOut()` | Decrease zoom level |
245
+ | `setScale(scale: number)` | Set specific zoom scale |
246
+ | `rotateClockwise()` | Rotate 90° clockwise |
247
+ | `rotateCounterClockwise()` | Rotate 90° counter-clockwise |
248
+ | `goToPage(page: number)` | Navigate to specific page |
249
+ | `search(query: string)` | Search for text |
250
+ | `searchNext()` | Go to next search match |
251
+ | `searchPrevious()` | Go to previous search match |
252
+ | `clearSearch()` | Clear search highlights |
253
+ | `download(filename?: string)` | Download the PDF |
228
254
 
229
255
  ## Types
230
256
 
@@ -256,6 +282,7 @@ interface PdfViewerActions {
256
282
  searchNext: () => void;
257
283
  searchPrevious: () => void;
258
284
  clearSearch: () => void;
285
+ download: (filename?: string) => Promise<void>;
259
286
  }
260
287
  ```
261
288
 
@@ -36,10 +36,11 @@
36
36
  scrollbarWidth = '10px'
37
37
  }: Props = $props();
38
38
 
39
- const { state: viewerState, src: contextSrc, _registerRenderer } = getPdfViewerContext();
39
+ const context = getPdfViewerContext();
40
+ const { state: viewerState, _registerRenderer, _setSrcDataForDownload } = context;
40
41
 
41
- // Use prop src if provided, otherwise fall back to context src
42
- let src = $derived(srcProp ?? contextSrc);
42
+ // Use prop src if provided, otherwise fall back to context src (via getter for reactivity)
43
+ let src = $derived(srcProp ?? context.src);
43
44
 
44
45
  let hostEl: HTMLDivElement | undefined = $state();
45
46
  let shadowRoot: ShadowRoot | null = null;
@@ -109,13 +110,17 @@
109
110
  if (typeof source === 'string') {
110
111
  // URL string
111
112
  documentSource = source;
113
+ _setSrcDataForDownload(null); // URL doesn't need copying
112
114
  } else if (source instanceof Blob) {
113
115
  // Convert Blob to ArrayBuffer
114
116
  const arrayBuffer = await source.arrayBuffer();
117
+ _setSrcDataForDownload(arrayBuffer.slice(0)); // Store a copy for download
115
118
  documentSource = { data: arrayBuffer };
116
119
  } else if (source instanceof ArrayBuffer) {
120
+ _setSrcDataForDownload(source.slice(0)); // Store a copy before PDF.js detaches it
117
121
  documentSource = { data: source };
118
122
  } else if (source instanceof Uint8Array) {
123
+ _setSrcDataForDownload(new Uint8Array(source).buffer.slice(0) as ArrayBuffer); // Store a copy for download
119
124
  documentSource = { data: source };
120
125
  } else {
121
126
  throw new Error('Invalid PDF source type');
@@ -130,8 +135,11 @@
130
135
  viewer = newViewer;
131
136
  viewerState.loading = false;
132
137
  } catch (e) {
133
- viewerState.error = e instanceof Error ? e.message : 'Failed to load PDF';
138
+ const errorMessage = e instanceof Error ? e.message : 'Failed to load PDF';
139
+ viewerState.error = errorMessage;
134
140
  viewerState.loading = false;
141
+ // Call the error callback if provided
142
+ context._onerror?.(errorMessage);
135
143
  }
136
144
  }
137
145
 
@@ -20,6 +20,8 @@
20
20
  scale?: number;
21
21
  /** Custom filename for PDF download (default: extracted from URL or 'document.pdf') */
22
22
  downloadFilename?: string;
23
+ /** Callback when PDF fails to load */
24
+ onerror?: (error: string) => void;
23
25
  /** CSS class for the container */
24
26
  class?: string;
25
27
  /** Children (toolbar and renderer) */
@@ -30,12 +32,17 @@
30
32
  src,
31
33
  scale: initialScale = 1.0,
32
34
  downloadFilename,
35
+ onerror,
33
36
  class: className = '',
34
37
  children
35
38
  }: Props = $props();
36
39
 
40
+ // Keep a copy of binary source data for download (PDF.js transfers/detaches ArrayBuffers)
41
+ // This is set by PdfRenderer before it passes data to PDF.js
42
+ let srcDataForDownload = $state<ArrayBuffer | null>(null);
43
+
37
44
  // Reactive state that will be shared via context
38
- let state = $state<PdfViewerState>({
45
+ let viewerState = $state<PdfViewerState>({
39
46
  loading: true,
40
47
  error: null,
41
48
  totalPages: 0,
@@ -77,11 +84,12 @@
77
84
  }
78
85
  } else if (src instanceof Blob) {
79
86
  blob = src;
80
- } else if (src instanceof ArrayBuffer) {
81
- blob = new Blob([src], { type: 'application/pdf' });
87
+ } else if (srcDataForDownload) {
88
+ // Use the pre-copied data (original ArrayBuffer/Uint8Array gets detached by PDF.js)
89
+ blob = new Blob([srcDataForDownload], { type: 'application/pdf' });
82
90
  } else {
83
- // Uint8Array
84
- blob = new Blob([new Uint8Array(src)], { type: 'application/pdf' });
91
+ console.error('Cannot download: no valid source data available');
92
+ return;
85
93
  }
86
94
 
87
95
  const url = URL.createObjectURL(blob);
@@ -113,20 +121,26 @@
113
121
 
114
122
  // Set up context
115
123
  setPdfViewerContext({
116
- state,
124
+ state: viewerState,
117
125
  actions,
118
- src,
126
+ get src() {
127
+ return src;
128
+ },
119
129
  _registerRenderer: (renderer: PdfViewerActions) => {
120
130
  rendererActions = renderer;
131
+ },
132
+ _onerror: onerror,
133
+ _setSrcDataForDownload: (data: ArrayBuffer | null) => {
134
+ srcDataForDownload = data;
121
135
  }
122
136
  });
123
137
  </script>
124
138
 
125
139
  <div class="pdf-viewer-container {className}">
126
- {#if state.loading}
140
+ {#if viewerState.loading}
127
141
  <div class="pdf-loading">Loading PDF...</div>
128
- {:else if state.error}
129
- <div class="pdf-error">Error: {state.error}</div>
142
+ {:else if viewerState.error}
143
+ <div class="pdf-error">Error: {viewerState.error}</div>
130
144
  {/if}
131
145
 
132
146
  {#if children}
@@ -9,6 +9,8 @@ interface Props {
9
9
  scale?: number;
10
10
  /** Custom filename for PDF download (default: extracted from URL or 'document.pdf') */
11
11
  downloadFilename?: string;
12
+ /** Callback when PDF fails to load */
13
+ onerror?: (error: string) => void;
12
14
  /** CSS class for the container */
13
15
  class?: string;
14
16
  /** Children (toolbar and renderer) */
@@ -31,6 +31,8 @@ export interface PdfViewerContext {
31
31
  /** The PDF source - shared from PdfViewer to PdfRenderer */
32
32
  src: PdfSource;
33
33
  _registerRenderer: (renderer: PdfViewerActions) => void;
34
+ _onerror?: (error: string) => void;
35
+ _setSrcDataForDownload: (data: ArrayBuffer | null) => void;
34
36
  }
35
37
  export declare function setPdfViewerContext(ctx: PdfViewerContext): void;
36
38
  export declare function getPdfViewerContext(): PdfViewerContext;
package/package.json CHANGED
@@ -1,91 +1,89 @@
1
1
  {
2
- "name": "svelte-pdf-view",
3
- "version": "0.1.11",
4
- "description": "A modern, modular PDF viewer component for Svelte 5. Built on PDF.js with TypeScript support",
5
- "author": "Louis Li",
6
- "license": "Apache-2.0",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/nullpointerexceptionkek/svelte-pdf-view.git"
10
- },
11
- "bugs": {
12
- "url": "https://github.com/nullpointerexceptionkek/svelte-pdf-view/issues"
13
- },
14
- "homepage": "https://github.com/nullpointerexceptionkek/svelte-pdf-view#readme",
15
- "scripts": {
16
- "dev": "vite dev",
17
- "build": "vite build && npm run prepack",
18
- "preview": "vite preview",
19
- "prepare": "svelte-kit sync || echo ''",
20
- "prepack": "svelte-kit sync && svelte-package && publint",
21
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
22
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
23
- "format": "prettier --write .",
24
- "lint": "prettier --check . && eslint ."
25
- },
26
- "files": [
27
- "dist",
28
- "!dist/**/*.test.*",
29
- "!dist/**/*.spec.*",
30
- "LICENSE.md",
31
- "README.md"
32
- ],
33
- "sideEffects": [
34
- "**/*.css"
35
- ],
36
- "svelte": "./dist/index.js",
37
- "types": "./dist/index.d.ts",
38
- "type": "module",
39
- "exports": {
40
- ".": {
41
- "types": "./dist/index.d.ts",
42
- "svelte": "./dist/index.js"
43
- }
44
- },
45
- "peerDependencies": {
46
- "@lucide/svelte": ">=0.500.0",
47
- "pdfjs-dist": "^5.0.0",
48
- "svelte": "^5.0.0"
49
- },
50
- "peerDependenciesMeta": {
51
- "@lucide/svelte": {
52
- "optional": true
53
- }
54
- },
55
- "devDependencies": {
56
- "@eslint/compat": "^1.4.0",
57
- "@eslint/js": "^9.39.1",
58
- "@sveltejs/adapter-auto": "^7.0.0",
59
- "@sveltejs/adapter-static": "^3.0.10",
60
- "@sveltejs/kit": "^2.48.5",
61
- "@sveltejs/package": "^2.5.6",
62
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
63
- "@types/node": "^22",
64
- "eslint": "^9.39.1",
65
- "eslint-config-prettier": "^10.1.8",
66
- "eslint-plugin-svelte": "^3.13.0",
67
- "globals": "^16.5.0",
68
- "pdfjs-dist": "^5.4.394",
69
- "prettier": "^3.6.2",
70
- "prettier-plugin-svelte": "^3.4.0",
71
- "publint": "^0.3.15",
72
- "svelte": "^5.43.8",
73
- "svelte-check": "^4.3.4",
74
- "typescript": "^5.9.3",
75
- "typescript-eslint": "^8.47.0",
76
- "vite": "^7.2.2"
77
- },
78
- "keywords": [
79
- "svelte",
80
- "svelte5",
81
- "pdf",
82
- "pdf-viewer",
83
- "pdfjs",
84
- "pdf.js",
85
- "document-viewer",
86
- "svelte-component"
87
- ],
88
- "dependencies": {
89
- "esm-env": "^1.2.2"
90
- }
91
- }
2
+ "name": "svelte-pdf-view",
3
+ "version": "0.1.13",
4
+ "description": "A modern, modular PDF viewer component for Svelte 5. Built on PDF.js with TypeScript support",
5
+ "author": "Louis Li",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/nullpointerexceptionkek/svelte-pdf-view.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/nullpointerexceptionkek/svelte-pdf-view/issues"
13
+ },
14
+ "homepage": "https://github.com/nullpointerexceptionkek/svelte-pdf-view#readme",
15
+ "files": [
16
+ "dist",
17
+ "!dist/**/*.test.*",
18
+ "!dist/**/*.spec.*",
19
+ "LICENSE.md",
20
+ "README.md"
21
+ ],
22
+ "sideEffects": [
23
+ "**/*.css"
24
+ ],
25
+ "svelte": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "type": "module",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "svelte": "./dist/index.js"
32
+ }
33
+ },
34
+ "peerDependencies": {
35
+ "@lucide/svelte": ">=0.500.0",
36
+ "pdfjs-dist": "^5.0.0",
37
+ "svelte": "^5.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@lucide/svelte": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "devDependencies": {
45
+ "@eslint/compat": "^1.4.0",
46
+ "@eslint/js": "^9.39.1",
47
+ "@sveltejs/adapter-auto": "^7.0.0",
48
+ "@sveltejs/adapter-static": "^3.0.10",
49
+ "@sveltejs/kit": "^2.49.1",
50
+ "@sveltejs/package": "^2.5.7",
51
+ "@sveltejs/vite-plugin-svelte": "^6.2.1",
52
+ "@types/node": "^22",
53
+ "eslint": "^9.39.1",
54
+ "eslint-config-prettier": "^10.1.8",
55
+ "eslint-plugin-svelte": "^3.13.0",
56
+ "globals": "^16.5.0",
57
+ "pdfjs-dist": "^5.4.394",
58
+ "prettier": "^3.6.2",
59
+ "prettier-plugin-svelte": "^3.4.0",
60
+ "publint": "^0.3.15",
61
+ "svelte": "^5.45.6",
62
+ "svelte-check": "^4.3.4",
63
+ "typescript": "^5.9.3",
64
+ "typescript-eslint": "^8.47.0",
65
+ "vite": "^7.2.2"
66
+ },
67
+ "keywords": [
68
+ "svelte",
69
+ "svelte5",
70
+ "pdf",
71
+ "pdf-viewer",
72
+ "pdfjs",
73
+ "pdf.js",
74
+ "document-viewer",
75
+ "svelte-component"
76
+ ],
77
+ "dependencies": {
78
+ "esm-env": "^1.2.2"
79
+ },
80
+ "scripts": {
81
+ "dev": "vite dev",
82
+ "build": "vite build && npm run prepack",
83
+ "preview": "vite preview",
84
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
85
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
86
+ "format": "prettier --write .",
87
+ "lint": "prettier --check . && eslint ."
88
+ }
89
+ }