svelte-pdf-view 0.1.12 → 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
 
@@ -37,7 +37,7 @@
37
37
  }: Props = $props();
38
38
 
39
39
  const context = getPdfViewerContext();
40
- const { state: viewerState, _registerRenderer } = context;
40
+ const { state: viewerState, _registerRenderer, _setSrcDataForDownload } = context;
41
41
 
42
42
  // Use prop src if provided, otherwise fall back to context src (via getter for reactivity)
43
43
  let src = $derived(srcProp ?? context.src);
@@ -110,13 +110,17 @@
110
110
  if (typeof source === 'string') {
111
111
  // URL string
112
112
  documentSource = source;
113
+ _setSrcDataForDownload(null); // URL doesn't need copying
113
114
  } else if (source instanceof Blob) {
114
115
  // Convert Blob to ArrayBuffer
115
116
  const arrayBuffer = await source.arrayBuffer();
117
+ _setSrcDataForDownload(arrayBuffer.slice(0)); // Store a copy for download
116
118
  documentSource = { data: arrayBuffer };
117
119
  } else if (source instanceof ArrayBuffer) {
120
+ _setSrcDataForDownload(source.slice(0)); // Store a copy before PDF.js detaches it
118
121
  documentSource = { data: source };
119
122
  } else if (source instanceof Uint8Array) {
123
+ _setSrcDataForDownload(new Uint8Array(source).buffer.slice(0) as ArrayBuffer); // Store a copy for download
120
124
  documentSource = { data: source };
121
125
  } else {
122
126
  throw new Error('Invalid PDF source type');
@@ -131,8 +135,11 @@
131
135
  viewer = newViewer;
132
136
  viewerState.loading = false;
133
137
  } catch (e) {
134
- 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;
135
140
  viewerState.loading = false;
141
+ // Call the error callback if provided
142
+ context._onerror?.(errorMessage);
136
143
  }
137
144
  }
138
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,22 +121,26 @@
113
121
 
114
122
  // Set up context
115
123
  setPdfViewerContext({
116
- state,
124
+ state: viewerState,
117
125
  actions,
118
126
  get src() {
119
127
  return src;
120
128
  },
121
129
  _registerRenderer: (renderer: PdfViewerActions) => {
122
130
  rendererActions = renderer;
131
+ },
132
+ _onerror: onerror,
133
+ _setSrcDataForDownload: (data: ArrayBuffer | null) => {
134
+ srcDataForDownload = data;
123
135
  }
124
136
  });
125
137
  </script>
126
138
 
127
139
  <div class="pdf-viewer-container {className}">
128
- {#if state.loading}
140
+ {#if viewerState.loading}
129
141
  <div class="pdf-loading">Loading PDF...</div>
130
- {:else if state.error}
131
- <div class="pdf-error">Error: {state.error}</div>
142
+ {:else if viewerState.error}
143
+ <div class="pdf-error">Error: {viewerState.error}</div>
132
144
  {/if}
133
145
 
134
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.12",
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
+ }