pdfcedar 0.1.0-beta.0 → 0.1.1-beta.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 CHANGED
@@ -1,223 +1,238 @@
1
- # PDFCedar
2
-
3
- An embeddable PDF viewer for web apps. View, search, annotate, rearrange pages and save standard PDFs, in
4
- React, Vue, Angular or plain HTML. One function, no framework lock-in.
5
-
6
- > **Free to try, licensed for production.** Every feature works without a key, with a "PDFCedar trial" watermark.
7
- > Live demo, docs and pricing: [pdfcedar.brimij.com](https://pdfcedar.brimij.com). Terms: [LICENSE](./LICENSE).
8
-
9
- ## Install
10
-
11
- ```sh
12
- npm install pdfcedar
13
- npx pdfcedar-assets public/pdfcedar
14
- ```
15
-
16
- The second command copies the PDF engine (a web worker plus font and character-map files, about 4.5 MB) into a
17
- folder your app serves. Bundlers copy JavaScript, not these files, so they need this one step. Add it to your
18
- `postinstall` script so upgrades stay in sync:
19
-
20
- ```json
21
- "scripts": { "postinstall": "pdfcedar-assets public/pdfcedar" }
22
- ```
23
-
24
- ## Quick start
25
-
26
- ```html
27
- <div id="viewer" style="height: 100vh"></div>
28
- ```
29
-
30
- ```js
31
- import { createViewer } from 'pdfcedar';
32
-
33
- const viewer = await createViewer({
34
- container: document.getElementById('viewer'),
35
- document: '/files/contract.pdf', // or a File, Blob or ArrayBuffer
36
- assetBaseUrl: '/pdfcedar/', // where pdfcedar-assets copied the engine files
37
- user: { id: 'u1', name: 'Asha' }, // author name on annotations
38
- });
39
-
40
- viewer.on('annotationAdded', ({ annotation }) => console.log('added', annotation));
41
- const bytes = await viewer.save(); // a standard PDF with all annotations and page changes
42
- ```
43
-
44
- The container needs a height; the viewer fills it.
45
-
46
- ### Without a bundler
47
-
48
- ```html
49
- <script src="https://your-cdn/pdfcedar/dist/index.umd.cjs"></script>
50
- <script>
51
- PDFCedar.createViewer({ container: document.getElementById('viewer'), document: 'file.pdf' });
52
- </script>
53
- ```
54
-
55
- The UMD build finds `dist/pdfjs/` next to itself, so `assetBaseUrl` isn't needed when you serve the whole `dist` folder.
56
-
57
- ### Frameworks
58
-
59
- | | Mount in | Destroy in |
60
- |---|---|---|
61
- | React | `useEffect` | the effect's cleanup |
62
- | Vue | `onMounted` | `onBeforeUnmount` |
63
- | Angular | `ngAfterViewInit` | `ngOnDestroy` |
64
-
65
- Complete, runnable examples are in the repository's `examples/` folder. Always call `viewer.destroy()` when the
66
- component goes away; it frees the worker, canvases and listeners.
67
-
68
- ## Options
69
-
70
- | Option | Type | |
71
- |---|---|---|
72
- | `container` | `HTMLElement` | Required. The viewer fills it. |
73
- | `document` | `string \| File \| Blob \| ArrayBuffer` | Required. A URL (fetched with range requests) or the file itself. |
74
- | `licenseKey` | `string` | Your licence key (see below). Without a valid one the viewer runs in trial mode. |
75
- | `assetBaseUrl` | `string` | URL of the folder holding the engine files. |
76
- | `theme` | `'light' \| 'dark' \| ThemeObject` | A `ThemeObject` maps `--pl-*` CSS variables to values. |
77
- | `toolbar` | `ToolName[]` | Which tools to show. Omit for all. |
78
- | `readOnly` | `boolean` | Viewing, search and print only. |
79
- | `user` | `{ id, name }` | Author of new annotations and replies. |
80
- | `collaboration` | `{ serverUrl, roomId }` | Real-time sharing with everyone in the room (see below). |
81
- | `signatureStore` | `{ load(), save(list) }` | Where the user's saved signatures persist (e.g. your backend). Without it they last for the page session. |
82
-
83
- `ToolName` is any of `'zoom'`, `'search'`, `'download'`, `'print'`, `'pages'`, `'select'`, `'highlight'`, `'underline'`,
84
- `'strikeout'`, `'note'`, `'ink'`, `'rectangle'`, `'ellipse'`, `'line'`, `'arrow'`, `'freeText'`, `'signature'`, `'redaction'`.
85
-
86
- ## API
87
-
88
- ```ts
89
- // Events: documentLoaded, pageChanged, annotationAdded, annotationChanged, annotationRemoved,
90
- // formFieldChanged, signatureAdded, saved. Payloads are typed (see ViewerEvents).
91
- viewer.on(event, handler); viewer.off(event, handler);
92
-
93
- // Navigation
94
- viewer.goToPage(3); viewer.setZoom(1.5); viewer.fit('width'); // or 'page'
95
- const hits = await viewer.search('total'); // highlights every match
96
-
97
- // Annotations (coordinates in PDF points, origin bottom-left)
98
- const note = viewer.annotations.add({ type: 'note', page: 1, rect: { x: 72, y: 700, width: 24, height: 24 }, contents: 'Check this' });
99
- viewer.annotations.update(note.id, { color: '#2563eb' });
100
- viewer.annotations.remove(note.id);
101
- viewer.annotations.list(); // or list({ page: 1, type: 'highlight' })
102
- const json = viewer.annotations.export(); // save to your backend…
103
- viewer.annotations.import(json); // …and restore later
104
- viewer.undo(); viewer.redo();
105
-
106
- // Pages (1-based)
107
- viewer.pages.rotate(1, 90); viewer.pages.delete(2); viewer.pages.move(3, 1);
108
- await viewer.pages.merge(file);
109
-
110
- // Forms: the file's own fields are drawn as fillable controls
111
- viewer.forms.getValues(); // { fullName: '', subscribe: false, plan: 'pro', interests: ['A'] }
112
- viewer.forms.setValues({ fullName: 'Asha Rao', subscribe: true }); // one undo step; FormValueError if invalid
113
- viewer.forms.flatten(); // on save, fields become part of the page
114
-
115
- // Signatures: the Signature tool lets users draw, type or upload one; from code:
116
- const signature = await viewer.signatures.add({
117
- page: 1,
118
- rect: { x: 360, y: 90, width: 150, height: 50 },
119
- image: blobOrDataUrlOrUrl, // PNG, JPEG, WebP, SVG…
120
- });
121
- // Movable, resizable and undoable until saved; saving draws it into the page.
122
-
123
- // Redaction: mark areas (the Redact tool marks selected text or a dragged box), then apply
124
- viewer.redaction.mark({ page: 1, rect: { x: 72, y: 640, width: 180, height: 14 } });
125
- await viewer.redaction.apply(); // final: marks turn black, undo history is cleared
126
- const redacted = await viewer.save(); // the content under the marks is removed from the file
127
-
128
- // Output
129
- const bytes = await viewer.save(); // Uint8Array
130
- await viewer.download('reviewed.pdf');
131
- await viewer.print();
132
- viewer.destroy();
133
- ```
134
-
135
- Annotation types: `highlight`, `underline`, `strikeout`, `note`, `reply`, `ink`, `rectangle`, `ellipse`, `line`,
136
- `arrow`, `freeText`, `signature`. Saved files use standard PDF annotations with appearance streams, so Acrobat Reader, Chrome,
137
- Firefox and Safari show them and can edit them. Signatures are the exception: saving draws them into the page, so they
138
- can no longer be moved or removed in any viewer.
139
-
140
- ## Redaction
141
-
142
- Applied redactions remove content from the saved file, not just cover it:
143
-
144
- - **Text:** removed glyph by glyph. The rest of the line keeps its position.
145
- - **Images, including scans:** the pixels under the mark are blacked out in the image itself.
146
- - **Shapes:** vector shapes entirely inside a mark are removed.
147
- - **Annotations and form fields:** any that overlap a mark are deleted.
148
- - **Hidden copies:** `/ActualText` that repeats removed text is stripped, and no copy of the original content stays in the file.
149
-
150
- Unapplied marks are saved as standard `/Redact` annotations, which Acrobat can apply.
151
-
152
- ## Theming
153
-
154
- ```css
155
- .pdfcedar-viewer {
156
- --pl-accent: #7c3aed;
157
- --pl-bg: #f4f4f5;
158
- --pl-surface: #ffffff;
159
- --pl-text: #18181b;
160
- --pl-font: 'Inter', system-ui, sans-serif;
161
- }
162
- ```
163
-
164
- ## Collaboration
165
-
166
- ```js
167
- createViewer({
168
- container,
169
- document: '/files/contract.pdf',
170
- user: { id: 'u1', name: 'Asha Rao' },
171
- collaboration: { serverUrl: 'wss://collab.example.com', roomId: 'contract-123' },
172
- });
173
- ```
174
-
175
- - **What's shared:** everyone in the room sees each other's annotations, replies, signatures, redaction marks and form
176
- values within a second, and the toolbar shows who's online.
177
- - **Events and undo:** changes from others fire the usual events with `source: 'remote'`, and never enter your undo
178
- history.
179
- - **Offline:** edits made offline sync when the connection is back.
180
- - **Not shared:** page changes (rotate, delete, reorder) stay per person, so everyone in a room should have the same
181
- pages.
182
- - **Server:** run your own. The repository's `apps/collab-server` is a small ready-made one (`node src/server.mjs`);
183
- any y-websocket-compatible server works too. Documents never pass through PDFCedar's servers.
184
- - **Size:** the collaboration code loads only when this option is set.
185
-
186
- ## Licence key
187
-
188
- ```js
189
- createViewer({ container, document, licenseKey: 'PL1.eyJ2Ijox…' });
190
- ```
191
-
192
- - **Offline check:** the key is checked in the browser, with no network call. It's a signed list of the domains
193
- you bought it for, so nothing about your users or documents leaves the page.
194
- - **Domains:** `example.com` covers only that host; `*.example.com` covers its subdomains. Any valid key also works
195
- on `localhost` and `127.0.0.1`, for development.
196
- - **Trial mode:** without a valid key (missing, mistyped, expired, or on another domain) everything still works,
197
- but the pages carry a "PDFCedar trial" watermark and a Trial badge. Saved, downloaded and printed files are
198
- watermarked too. The console says why, as does the
199
- viewer element's `data-license-reason` attribute.
200
-
201
- ## Phones and tablets
202
-
203
- - **Pinch** zooms around your fingers; on desktop, Ctrl/⌘ + wheel and trackpad pinches do the same.
204
- - **Drawing:** one finger draws with the drawing tools. A second finger turns a stroke into a pinch, so no stray line is left.
205
- - **Select tool:** touching a selected annotation drags it; elsewhere, one finger scrolls. Handles are larger on touch screens.
206
- - **Narrow viewers** (under 640 px wide, by the viewer's own width) get a compact toolbar:
207
- - **Annotate** shows the tools in a row that scrolls sideways.
208
- - **More** holds Search, Download and Print.
209
- - The sidebar is a drawer that starts closed.
210
-
211
- Give your page `<meta name="viewport" content="width=device-width, initial-scale=1">`, as every mobile page needs.
212
-
213
- ## Browser support
214
-
215
- Chrome and Edge 119+, Firefox 121+, Safari 17.4+ (desktop and iOS), Chrome on Android.
216
-
217
- ## Errors
218
-
219
- - `createViewer` rejects if the file can't be opened (bad URL, not a PDF).
220
- - `annotations.add` / `update` / `import` throw `AnnotationValidationError` (with a `path` such as `rect.width`) for invalid input.
221
- - `forms.setValues` throws `FormValueError` (with the `field` name) for an unknown field or a value it can't hold; nothing changes.
222
- - `annotations.update` / `remove` throw `AnnotationValidationError` for an applied redaction (applying is final).
223
- - `save` / `download` throw `EncryptedDocumentError` for password-protected PDFs.
1
+ # PDFCedar
2
+
3
+ An embeddable PDF viewer for web apps. View, search, annotate, rearrange pages and save standard PDFs, in
4
+ React, Vue, Angular or plain HTML. One function, no framework lock-in.
5
+
6
+ > **Free to try, licensed for production.** Every feature works without a key, with a "PDFCedar trial" watermark.
7
+ > Live demo, docs and pricing: [pdfcedar.brimij.com](https://pdfcedar.brimij.com). Terms: [LICENSE](./LICENSE).
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install pdfcedar
13
+ npx pdfcedar-assets public/pdfcedar
14
+ ```
15
+
16
+ The second command copies the PDF engine (a web worker plus font and character-map files, about 4.5 MB) into a
17
+ folder your app serves. Bundlers copy JavaScript, not these files, so they need this one step. Add it to your
18
+ `postinstall` script so upgrades stay in sync:
19
+
20
+ ```json
21
+ "scripts": { "postinstall": "pdfcedar-assets public/pdfcedar" }
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```html
27
+ <div id="viewer" style="height: 100vh"></div>
28
+ ```
29
+
30
+ ```js
31
+ import { createViewer } from 'pdfcedar';
32
+
33
+ const viewer = await createViewer({
34
+ container: document.getElementById('viewer'),
35
+ document: '/files/contract.pdf', // or a File, Blob or ArrayBuffer
36
+ assetBaseUrl: '/pdfcedar/', // where pdfcedar-assets copied the engine files
37
+ user: { id: 'u1', name: 'Asha' }, // author name on annotations
38
+ });
39
+
40
+ viewer.on('annotationAdded', ({ annotation }) => console.log('added', annotation));
41
+ const bytes = await viewer.save(); // a standard PDF with all annotations and page changes
42
+ ```
43
+
44
+ The container needs a height; the viewer fills it. (Without one, the viewer falls back to `80vh` and warns in the
45
+ console: otherwise it would grow to the whole document's length and draw every page.)
46
+
47
+ **Server-side rendering** (Next.js, Nuxt, Angular SSR): importing `pdfcedar` on the server is safe and does nothing;
48
+ the PDF engine loads only when `createViewer` runs in the browser. Call it from client-only code (`useEffect`,
49
+ `onMounted`, `afterNextRender`).
50
+
51
+ **Bundle size:** the entry is about 60 kB gzipped. The PDF engine (about 165 kB gzipped) and the save code load on
52
+ demand as separate chunks. Vite may still warn that those chunks are over 500 kB; that's expected, and
53
+ `build.chunkSizeWarningLimit: 1000` silences it.
54
+
55
+ ### Without a bundler
56
+
57
+ ```html
58
+ <div id="viewer" style="height: 100vh"></div>
59
+ <script src="https://cdn.jsdelivr.net/npm/pdfcedar@0.1.1-beta.0/dist/pdfcedar.umd.js"></script>
60
+ <script>
61
+ PDFCedar.createViewer({ container: document.getElementById('viewer'), document: 'file.pdf' });
62
+ </script>
63
+ ```
64
+
65
+ `dist/pdfcedar.umd.js` works from jsDelivr, unpkg or your own server. It finds the engine files (`dist/pdfjs/`) next
66
+ to itself, so `assetBaseUrl` isn't needed. Pin the exact version, as here: CDN ranges like `@0.1` skip beta releases.
67
+
68
+ ### Frameworks
69
+
70
+ | | Mount in | Destroy in |
71
+ |---|---|---|
72
+ | React | `useEffect` | the effect's cleanup |
73
+ | Vue | `onMounted` | `onBeforeUnmount` |
74
+ | Angular | `ngAfterViewInit` | `ngOnDestroy` |
75
+
76
+ Complete, runnable examples are in the repository's `examples/` folder. Always call `viewer.destroy()` when the
77
+ component goes away; it frees the worker, canvases and listeners.
78
+
79
+ ## Options
80
+
81
+ | Option | Type | |
82
+ |---|---|---|
83
+ | `container` | `HTMLElement` | Required. The viewer fills it. |
84
+ | `document` | `string \| File \| Blob \| ArrayBuffer` | Required. A URL (fetched with range requests) or the file itself. |
85
+ | `licenseKey` | `string` | Your licence key (see below). Without a valid one the viewer runs in trial mode. |
86
+ | `assetBaseUrl` | `string` | URL of the folder holding the engine files. |
87
+ | `theme` | `'light' \| 'dark' \| ThemeObject` | A `ThemeObject` maps `--pl-*` CSS variables to values. |
88
+ | `toolbar` | `ToolName[]` | Which tools to show. Omit for all. |
89
+ | `readOnly` | `boolean` | Viewing, search and print only. |
90
+ | `user` | `{ id, name }` | Author of new annotations and replies. |
91
+ | `collaboration` | `{ serverUrl, roomId }` | Real-time sharing with everyone in the room (see below). |
92
+ | `signatureStore` | `{ load(), save(list) }` | Where the user's saved signatures persist (e.g. your backend). Without it they last for the page session. |
93
+ | `password` | `string` | For password-protected PDFs. If it's missing or wrong, the viewer asks the user; `createViewer` rejects with `EncryptedDocumentError` if they cancel. |
94
+ | `httpHeaders` | `Record<string, string>` | Sent when `document` is a URL, e.g. `{ Authorization: 'Bearer …' }` for files behind a login. |
95
+ | `withCredentials` | `boolean` | Send cookies when `document` is a URL on another origin (the server must allow credentials). |
96
+
97
+ `ToolName` is any of `'zoom'`, `'search'`, `'download'`, `'print'`, `'pages'`, `'select'`, `'highlight'`, `'underline'`,
98
+ `'strikeout'`, `'note'`, `'ink'`, `'rectangle'`, `'ellipse'`, `'line'`, `'arrow'`, `'freeText'`, `'signature'`, `'redaction'`.
99
+
100
+ ## API
101
+
102
+ ```ts
103
+ // Events: documentLoaded, pageChanged, annotationAdded, annotationChanged, annotationRemoved,
104
+ // formFieldChanged, signatureAdded, saved. Payloads are typed (see ViewerEvents).
105
+ viewer.on(event, handler); viewer.off(event, handler);
106
+
107
+ // Navigation
108
+ viewer.goToPage(3); viewer.setZoom(1.5); viewer.fit('width'); // or 'page'
109
+ const hits = await viewer.search('total'); // highlights every match
110
+
111
+ // Annotations (coordinates in PDF points, origin bottom-left)
112
+ const note = viewer.annotations.add({ type: 'note', page: 1, rect: { x: 72, y: 700, width: 24, height: 24 }, contents: 'Check this' });
113
+ viewer.annotations.update(note.id, { color: '#2563eb' });
114
+ viewer.annotations.remove(note.id);
115
+ viewer.annotations.list(); // or list({ page: 1, type: 'highlight' })
116
+ const json = viewer.annotations.export(); // save to your backend…
117
+ viewer.annotations.import(json); // …and restore later
118
+ viewer.undo(); viewer.redo();
119
+
120
+ // Pages (1-based)
121
+ viewer.pages.rotate(1, 90); viewer.pages.delete(2); viewer.pages.move(3, 1);
122
+ await viewer.pages.merge(file);
123
+
124
+ // Forms: the file's own fields are drawn as fillable controls
125
+ viewer.forms.getValues(); // { fullName: '', subscribe: false, plan: 'pro', interests: ['A'] }
126
+ viewer.forms.setValues({ fullName: 'Asha Rao', subscribe: true }); // one undo step; FormValueError if invalid
127
+ viewer.forms.flatten(); // on save, fields become part of the page
128
+
129
+ // Signatures: the Signature tool lets users draw, type or upload one; from code:
130
+ const signature = await viewer.signatures.add({
131
+ page: 1,
132
+ rect: { x: 360, y: 90, width: 150, height: 50 },
133
+ image: blobOrDataUrlOrUrl, // PNG, JPEG, WebP, SVG…
134
+ });
135
+ // Movable, resizable and undoable until saved; saving draws it into the page.
136
+
137
+ // Redaction: mark areas (the Redact tool marks selected text or a dragged box), then apply
138
+ viewer.redaction.mark({ page: 1, rect: { x: 72, y: 640, width: 180, height: 14 } });
139
+ await viewer.redaction.apply(); // final: marks turn black, undo history is cleared
140
+ const redacted = await viewer.save(); // the content under the marks is removed from the file
141
+
142
+ // Output
143
+ const bytes = await viewer.save(); // Uint8Array
144
+ await viewer.download('reviewed.pdf');
145
+ await viewer.print();
146
+ viewer.destroy();
147
+ ```
148
+
149
+ Annotation types: `highlight`, `underline`, `strikeout`, `note`, `reply`, `ink`, `rectangle`, `ellipse`, `line`,
150
+ `arrow`, `freeText`, `signature`. Saved files use standard PDF annotations with appearance streams, so Acrobat Reader, Chrome,
151
+ Firefox and Safari show them and can edit them. Signatures are the exception: saving draws them into the page, so they
152
+ can no longer be moved or removed in any viewer.
153
+
154
+ ## Redaction
155
+
156
+ Applied redactions remove content from the saved file, not just cover it:
157
+
158
+ - **Text:** removed glyph by glyph. The rest of the line keeps its position.
159
+ - **Images, including scans:** the pixels under the mark are blacked out in the image itself.
160
+ - **Shapes:** vector shapes entirely inside a mark are removed.
161
+ - **Annotations and form fields:** any that overlap a mark are deleted.
162
+ - **Hidden copies:** `/ActualText` that repeats removed text is stripped, and no copy of the original content stays in the file.
163
+
164
+ Unapplied marks are saved as standard `/Redact` annotations, which Acrobat can apply.
165
+
166
+ ## Theming
167
+
168
+ ```css
169
+ .pdfcedar-viewer {
170
+ --pl-accent: #7c3aed;
171
+ --pl-bg: #f4f4f5;
172
+ --pl-surface: #ffffff;
173
+ --pl-text: #18181b;
174
+ --pl-font: 'Inter', system-ui, sans-serif;
175
+ }
176
+ ```
177
+
178
+ ## Collaboration
179
+
180
+ ```js
181
+ createViewer({
182
+ container,
183
+ document: '/files/contract.pdf',
184
+ user: { id: 'u1', name: 'Asha Rao' },
185
+ collaboration: { serverUrl: 'wss://collab.example.com', roomId: 'contract-123' },
186
+ });
187
+ ```
188
+
189
+ - **What's shared:** everyone in the room sees each other's annotations, replies, signatures, redaction marks and form
190
+ values within a second, and the toolbar shows who's online.
191
+ - **Events and undo:** changes from others fire the usual events with `source: 'remote'`, and never enter your undo
192
+ history.
193
+ - **Offline:** edits made offline sync when the connection is back.
194
+ - **Not shared:** page changes (rotate, delete, reorder) stay per person, so everyone in a room should have the same
195
+ pages.
196
+ - **Server:** run your own. The repository's `apps/collab-server` is a small ready-made one (`node src/server.mjs`);
197
+ any y-websocket-compatible server works too. Documents never pass through PDFCedar's servers.
198
+ - **Size:** the collaboration code loads only when this option is set.
199
+
200
+ ## Licence key
201
+
202
+ ```js
203
+ createViewer({ container, document, licenseKey: 'PL1.eyJ2Ijox…' });
204
+ ```
205
+
206
+ - **Offline check:** the key is checked in the browser, with no network call. It's a signed list of the domains
207
+ you bought it for, so nothing about your users or documents leaves the page.
208
+ - **Domains:** `example.com` covers only that host; `*.example.com` covers its subdomains. Any valid key also works
209
+ on `localhost` and `127.0.0.1`, for development.
210
+ - **Trial mode:** without a valid key (missing, mistyped, expired, or on another domain) everything still works,
211
+ but the pages carry a "PDFCedar trial" watermark and a Trial badge. Saved, downloaded and printed files are
212
+ watermarked too. The console says why, as does the
213
+ viewer element's `data-license-reason` attribute.
214
+
215
+ ## Phones and tablets
216
+
217
+ - **Pinch** zooms around your fingers; on desktop, Ctrl/⌘ + wheel and trackpad pinches do the same.
218
+ - **Drawing:** one finger draws with the drawing tools. A second finger turns a stroke into a pinch, so no stray line is left.
219
+ - **Select tool:** touching a selected annotation drags it; elsewhere, one finger scrolls. Handles are larger on touch screens.
220
+ - **Narrow viewers** (under 640 px wide, by the viewer's own width) get a compact toolbar:
221
+ - **Annotate** shows the tools in a row that scrolls sideways.
222
+ - **More** holds Search, Download and Print.
223
+ - The sidebar is a drawer that starts closed.
224
+
225
+ Give your page `<meta name="viewport" content="width=device-width, initial-scale=1">`, as every mobile page needs.
226
+
227
+ ## Browser support
228
+
229
+ Chrome and Edge 119+, Firefox 121+, Safari 17.4+ (desktop and iOS), Chrome on Android.
230
+
231
+ ## Errors
232
+
233
+ - `createViewer` rejects if the file can't be opened (bad URL, not a PDF).
234
+ - `annotations.add` / `update` / `import` throw `AnnotationValidationError` (with a `path` such as `rect.width`) for invalid input.
235
+ - `forms.setValues` throws `FormValueError` (with the `field` name) for an unknown field or a value it can't hold; nothing changes.
236
+ - `annotations.update` / `remove` throw `AnnotationValidationError` for an applied redaction (applying is final).
237
+ - `createViewer` rejects with `EncryptedDocumentError` when a password-protected PDF is opened and the user cancels the password prompt.
238
+ - `save` / `download` / `print` throw `EncryptedDocumentError` for password-protected PDFs (viewing, annotating and searching them work; writing encrypted files back out is not supported yet).
File without changes
@@ -176,10 +176,10 @@ var h = class extends Error {
176
176
  super(`${e} is not implemented yet (planned for Milestone ${t}).`), this.name = "NotImplementedError";
177
177
  }
178
178
  }, g = class extends Error {
179
- constructor() {
180
- super("Saving password-protected (encrypted) PDFs is not supported yet."), this.name = "EncryptedDocumentError";
179
+ constructor(e = "Saving password-protected (encrypted) PDFs is not supported yet.") {
180
+ super(e), this.name = "EncryptedDocumentError";
181
181
  }
182
182
  };
183
183
  export { l as a, p as c, u as d, s as f, n as g, t as h, d as i, f as l, e as m, h as n, o, a as p, m as r, c as s, g as t, r as u };
184
184
 
185
- //# sourceMappingURL=errors-Bd8CCmBG.js.map
185
+ //# sourceMappingURL=errors-Cq5TPos_.js.map