unpdf 0.12.2 → 1.0.1

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.
Files changed (80) hide show
  1. package/README.md +147 -79
  2. package/dist/index.cjs +119 -74
  3. package/dist/index.d.cts +24 -41
  4. package/dist/index.d.mts +24 -41
  5. package/dist/index.d.ts +24 -41
  6. package/dist/index.mjs +118 -73
  7. package/dist/pdfjs.mjs +1 -1
  8. package/dist/types/src/display/annotation_layer.d.ts +25 -1
  9. package/dist/types/src/display/annotation_storage.d.ts +1 -8
  10. package/dist/types/src/display/api.d.ts +147 -57
  11. package/dist/types/src/display/canvas.d.ts +21 -28
  12. package/dist/types/src/display/canvas_factory.d.ts +27 -0
  13. package/dist/types/src/display/cmap_reader_factory.d.ts +25 -0
  14. package/dist/types/src/display/display_utils.d.ts +40 -60
  15. package/dist/types/src/display/draw_layer.d.ts +7 -19
  16. package/dist/types/src/display/editor/alt_text.d.ts +3 -2
  17. package/dist/types/src/display/editor/annotation_editor_layer.d.ts +16 -11
  18. package/dist/types/src/display/editor/color_picker.d.ts +1 -0
  19. package/dist/types/src/display/editor/draw.d.ts +121 -0
  20. package/dist/types/src/display/editor/drawers/contour.d.ts +3 -0
  21. package/dist/types/src/display/editor/drawers/freedraw.d.ts +31 -0
  22. package/dist/types/src/display/editor/drawers/highlight.d.ts +36 -0
  23. package/dist/types/src/display/editor/drawers/inkdraw.d.ts +129 -0
  24. package/dist/types/src/display/editor/drawers/outline.d.ts +19 -0
  25. package/dist/types/src/display/editor/drawers/signaturedraw.d.ts +79 -0
  26. package/dist/types/src/display/editor/editor.d.ts +55 -13
  27. package/dist/types/src/display/editor/freetext.d.ts +5 -16
  28. package/dist/types/src/display/editor/highlight.d.ts +8 -25
  29. package/dist/types/src/display/editor/ink.d.ts +21 -74
  30. package/dist/types/src/display/editor/signature.d.ts +80 -0
  31. package/dist/types/src/display/editor/stamp.d.ts +16 -22
  32. package/dist/types/src/display/editor/toolbar.d.ts +5 -2
  33. package/dist/types/src/display/editor/tools.d.ts +17 -9
  34. package/dist/types/src/display/fetch_stream.d.ts +6 -13
  35. package/dist/types/src/display/filter_factory.d.ts +29 -0
  36. package/dist/types/src/display/font_loader.d.ts +3 -6
  37. package/dist/types/src/display/metadata.d.ts +1 -2
  38. package/dist/types/src/display/network.d.ts +11 -7
  39. package/dist/types/src/display/network_utils.d.ts +7 -6
  40. package/dist/types/src/display/node_stream.d.ts +6 -22
  41. package/dist/types/src/display/node_utils.d.ts +15 -11
  42. package/dist/types/src/display/optional_content_config.d.ts +2 -2
  43. package/dist/types/src/display/pattern_helper.d.ts +4 -2
  44. package/dist/types/src/display/standard_fontdata_factory.d.ts +20 -0
  45. package/dist/types/src/display/svg_factory.d.ts +14 -0
  46. package/dist/types/src/display/text_layer.d.ts +12 -9
  47. package/dist/types/src/display/touch_manager.d.ts +18 -0
  48. package/dist/types/src/display/transport_stream.d.ts +1 -1
  49. package/dist/types/src/display/wasm_factory.d.ts +20 -0
  50. package/dist/types/src/display/worker_options.d.ts +2 -2
  51. package/dist/types/src/interfaces.d.ts +4 -0
  52. package/dist/types/src/pdf.d.ts +13 -5
  53. package/dist/types/src/shared/message_handler.d.ts +9 -4
  54. package/dist/types/src/shared/util.d.ts +137 -129
  55. package/dist/types/web/annotation_editor_layer_builder.d.ts +20 -5
  56. package/dist/types/web/annotation_layer_builder.d.ts +35 -4
  57. package/dist/types/web/app_options.d.ts +1 -1
  58. package/dist/types/web/autolinker.d.ts +28 -0
  59. package/dist/types/web/base_pdf_page_view.d.ts +27 -0
  60. package/dist/types/web/draw_layer_builder.d.ts +13 -2
  61. package/dist/types/web/event_utils.d.ts +2 -2
  62. package/dist/types/web/genericl10n.d.ts +5 -5
  63. package/dist/types/web/interfaces.d.ts +5 -5
  64. package/dist/types/web/l10n.d.ts +2 -2
  65. package/dist/types/web/pdf_find_controller.d.ts +2 -0
  66. package/dist/types/web/pdf_link_service.d.ts +1 -2
  67. package/dist/types/web/pdf_page_detail_view.d.ts +32 -0
  68. package/dist/types/web/pdf_page_view.d.ts +44 -23
  69. package/dist/types/web/pdf_rendering_queue.d.ts +2 -1
  70. package/dist/types/web/pdf_thumbnail_view.d.ts +23 -3
  71. package/dist/types/web/pdf_thumbnail_viewer.d.ts +21 -1
  72. package/dist/types/web/pdf_viewer.d.ts +44 -16
  73. package/dist/types/web/struct_tree_layer_builder.d.ts +20 -2
  74. package/dist/types/web/text_accessibility.d.ts +1 -1
  75. package/dist/types/web/text_layer_builder.d.ts +25 -17
  76. package/dist/types/web/ui_utils.d.ts +3 -19
  77. package/dist/types/web/xfa_layer_builder.d.ts +14 -3
  78. package/package.json +31 -28
  79. package/dist/types/src/display/base_factory.d.ts +0 -60
  80. package/dist/types/src/display/editor/outliner.d.ts +0 -70
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # unpdf
2
2
 
3
- A collection of utilities to work with PDFs. Designed specifically for Deno, workers and other nodeless environments.
3
+ A collection of utilities for working with PDF files. Designed specifically for Bun, Deno, and serverless environments. However, it also works in Node.js and the browser.
4
4
 
5
5
  `unpdf` ships with a serverless build/redistribution of Mozilla's [PDF.js](https://github.com/mozilla/pdf.js) for serverless environments. Apart from some string replacements and mocks, [`unenv`](https://github.com/unjs/unenv) does the heavy lifting by converting Node.js specific code to be platform-agnostic. See [`pdfjs.rollup.config.ts`](./pdfjs.rollup.config.ts) for all the details.
6
6
 
@@ -8,15 +8,18 @@ This library is also intended as a modern alternative to the unmaintained but st
8
8
 
9
9
  ## Features
10
10
 
11
- - 🏗️ Works in Node.js, browser and workers
11
+ - 🏗️ Made for Node.js, browser and serverless environments
12
12
  - 🪭 Includes serverless build of PDF.js ([`unpdf/pdfjs`](./package.json#L34))
13
- - 💬 Extract text and images from PDFs
13
+ - 💬 Extract [text](#extract-text-from-pdf) and [images](#extractimages) from PDF files
14
14
  - 🧱 Opt-in to legacy PDF.js build
15
15
  - 💨 Zero dependencies
16
16
 
17
17
  ## PDF.js Compatibility
18
18
 
19
- The serverless build of PDF.js provided by `unpdf` is based on PDF.js v4.6.82. If you need a different version, you can [use another PDF.js build](#use-official-or-legacy-pdfjs-build).
19
+ > [!Tip]
20
+ > The serverless PDF.js bundle provided by `unpdf` is built from PDF.js v5.2.133.
21
+
22
+ You can use an [official PDF.js build](#use-official-or-legacy-pdfjs-build) by using the [`definePDFJSModule`](#definepdfjsmodule) method. This is useful if you want to use a specific version or a custom build of PDF.js.
20
23
 
21
24
  ## Installation
22
25
 
@@ -24,13 +27,13 @@ Run the following command to add `unpdf` to your project.
24
27
 
25
28
  ```bash
26
29
  # pnpm
27
- pnpm add unpdf
30
+ pnpm add -D unpdf
28
31
 
29
32
  # npm
30
- npm install unpdf
33
+ npm install -D unpdf
31
34
 
32
35
  # yarn
33
- yarn add unpdf
36
+ yarn add -D unpdf
34
37
  ```
35
38
 
36
39
  ## Usage
@@ -40,32 +43,37 @@ yarn add unpdf
40
43
  ```ts
41
44
  import { extractText, getDocumentProxy } from 'unpdf'
42
45
 
43
- // Fetch a PDF file from the web
44
- const buffer = await fetch(
45
- 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
46
- ).then(res => res.arrayBuffer())
47
-
48
- // Or load it from the filesystem
46
+ // Either fetch a PDF file from the web or load it from the file system
47
+ const buffer = await fetch('https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf')
48
+ .then(res => res.arrayBuffer())
49
49
  const buffer = await readFile('./dummy.pdf')
50
50
 
51
- // Load PDF from buffer
51
+ // Then, load the PDF file into a PDF.js document
52
52
  const pdf = await getDocumentProxy(new Uint8Array(buffer))
53
- // Extract text from PDF
53
+
54
+ // Finally, extract the text from the PDF file
54
55
  const { totalPages, text } = await extractText(pdf, { mergePages: true })
55
56
 
56
57
  console.log(`Total pages: ${totalPages}`)
57
58
  console.log(text)
58
59
  ```
59
60
 
60
- ### Access the PDF.js API
61
+ ### PDF.js API
61
62
 
62
- This will return the resolved PDF.js module and gives full access to the PDF.js API, like:
63
+ `unpdf` provides helpful [methods](#api) to work with PDF files, such as `extractText` and `extractImages`, which should cover most use cases. However, if you need more control over the PDF.js API, you can use the `getResolvedPDFJS` method to get the resolved PDF.js module.
63
64
 
64
- - `getDocument`
65
- - `version`
66
- - … and all other methods
65
+ Access the PDF.js API directly by calling `getResolvedPDFJS`:
67
66
 
68
- Especially useful for platforms like 🦕 Deno or if you want to use the PDF.js API directly. If no custom build was defined beforehand, the serverless build bundled with `unpdf` will be initialized.
67
+ ```ts
68
+ import { getResolvedPDFJS } from 'unpdf'
69
+
70
+ const { version } = await getResolvedPDFJS()
71
+ ```
72
+
73
+ > [!NOTE]
74
+ > If no other PDF.js build was defined, the serverless build will always be used.
75
+
76
+ For example, you can use the `getDocument` method to load a PDF file and then use the `getPage` method to get a specific page. You can also use the `getTextContent` method to extract the text from the page.
69
77
 
70
78
  ```ts
71
79
  import { getResolvedPDFJS } from 'unpdf'
@@ -86,55 +94,40 @@ for (let i = 1; i <= doc.numPages; i++) {
86
94
 
87
95
  ### Use Official or Legacy PDF.js Build
88
96
 
89
- Generally speaking, you don't need to worry about the PDF.js build. `unpdf` ships with a serverless build of the latest PDF.js version. However, if you want to use the official PDF.js version or the legacy build, you can define a custom PDF.js module.
97
+ Usually you don't need to worry about the PDF.js build. `unpdf` ships with a serverless build of the latest PDF.js version. However, if you want to use the official PDF.js version or the legacy build, you can define a custom PDF.js module.
90
98
 
91
99
  > [!WARNING]
92
- > The latest PDF.js v4.6.82 uses `Promise.withResolvers`, which may not be supported in all environments, such as Node < 22. Consider to use the bundled serverless build, which includes a polyfill, or use an older version of PDF.js.
100
+ > Later PDF.js v4.x versions uses `Promise.withResolvers`, which may not be supported in all environments, such as Node < 22. Consider to use the bundled serverless build, which includes a polyfill, or use an older version of PDF.js.
101
+
102
+ For example, if you want to use the official PDF.js build, you can do the following:
93
103
 
94
104
  ```ts
95
- // Before using any other method, define the PDF.js module
96
- // if you need another PDF.js build
97
- import { configureUnPDF } from 'unpdf'
105
+ import { definePDFJSModule } from 'unpdf'
98
106
 
99
- await configureUnPDF({
100
- // Use the official PDF.js build (make sure to install it first)
101
- pdfjs: () => import('pdfjs-dist'),
102
- })
107
+ // Define the PDF.js build before using any other unpdf method
108
+ await definePDFJSModule(() => import('pdfjs-dist'))
103
109
 
104
- // Now, you can use the other methods
105
- // …
110
+ // Now, you can use all unpdf methods with the official PDF.js build
111
+ const { text } = await extractText(pdf)
106
112
  ```
107
113
 
108
- ## Config
109
-
110
- ```ts
111
- interface UnPDFConfiguration {
112
- /**
113
- * By default, UnPDF will use the latest version of PDF.js compiled for
114
- * serverless environments. If you want to use a different version, you can
115
- * provide a custom resolver function.
116
- *
117
- * @example
118
- * // Use the official PDF.js build (make sure to install it first)
119
- * () => import('pdfjs-dist')
120
- */
121
- pdfjs?: () => Promise<PDFJS>
122
- }
123
- ```
114
+ ## API
124
115
 
125
- ## Methods
116
+ ### `definePDFJSModule`
126
117
 
127
- ### `configureUnPDF`
118
+ Allows to define a custom PDF.js build. This method should be called before using any other method. If no custom build is defined, the serverless build will be used.
128
119
 
129
- Define a custom PDF.js module, like the legacy build. Make sure to call this method before using any other methods.
120
+ **Type Declaration**
130
121
 
131
122
  ```ts
132
- function configureUnPDF(config: UnPDFConfiguration): Promise<void>
123
+ function definePDFJSModule(pdfjs: () => Promise<PDFJS>): Promise<void>
133
124
  ```
134
125
 
135
126
  ### `getResolvedPDFJS`
136
127
 
137
- Returns the resolved PDF.js module. If no build is defined, the latest version will be initialized.
128
+ Returns the resolved PDF.js module. If no other PDF.js build was defined, the serverless build will be used. This method is useful if you want to use the PDF.js API directly.
129
+
130
+ **Type Declaration**
138
131
 
139
132
  ```ts
140
133
  function getResolvedPDFJS(): Promise<PDFJS>
@@ -142,6 +135,8 @@ function getResolvedPDFJS(): Promise<PDFJS>
142
135
 
143
136
  ### `getMeta`
144
137
 
138
+ **Type Declaration**
139
+
145
140
  ```ts
146
141
  function getMeta(
147
142
  data: DocumentInitParameters['data'] | PDFDocumentProxy,
@@ -155,47 +150,109 @@ function getMeta(
155
150
 
156
151
  Extracts all text from a PDF. If `mergePages` is set to `true`, the text of all pages will be merged into a single string. Otherwise, an array of strings for each page will be returned.
157
152
 
153
+ **Type Declaration**
154
+
158
155
  ```ts
159
156
  function extractText(
160
157
  data: DocumentInitParameters['data'] | PDFDocumentProxy,
161
- { mergePages }?: { mergePages?: boolean },
158
+ options?: {
159
+ mergePages?: false
160
+ }
162
161
  ): Promise<{
163
162
  totalPages: number
164
- text: string | string[]
163
+ text: string[]
164
+ }>
165
+ function extractText(
166
+ data: DocumentInitParameters['data'] | PDFDocumentProxy,
167
+ options: {
168
+ mergePages: true
169
+ }
170
+ ): Promise<{
171
+ totalPages: number
172
+ text: string
165
173
  }>
166
174
  ```
167
175
 
168
- ### `renderPageAsImage`
176
+ ### `extractImages`
177
+
178
+ Extracts images from a specific page of a PDF document, including necessary metadata such as width, height, and calculated color channels.
169
179
 
170
180
  > [!NOTE]
171
181
  > This method will only work in Node.js and browser environments.
172
182
 
173
- To render a PDF page as an image, you can use the `renderPageAsImage` method. This method will return an `ArrayBuffer` of the rendered image.
183
+ In order to use this method, make sure to meet the following requirements:
174
184
 
175
- In order to use this method, you have to meet the following requirements:
185
+ - Use the official PDF.js build (see below for details).
186
+ - Install the [`@napi-rs/canvas`](https://github.com/Brooooooklyn/canvas) package if you are using Node.js. This package is required to render the PDF page as an image.
176
187
 
177
- - Use the official PDF.js build
178
- - Install the [`canvas`](https://www.npmjs.com/package/canvas) package in Node.js environments
188
+ **Type Declaration**
189
+
190
+ ```ts
191
+ interface ExtractedImageObject {
192
+ data: Uint8ClampedArray
193
+ width: number
194
+ height: number
195
+ channels: 1 | 3 | 4
196
+ key: string
197
+ }
198
+
199
+ function extractImages(
200
+ data: DocumentInitParameters['data'] | PDFDocumentProxy,
201
+ pageNumber: number,
202
+ ): Promise<ExtractedImageObject[]>
203
+ ```
179
204
 
180
205
  **Example**
181
206
 
207
+ > [!NOTE]
208
+ > The following example uses the [sharp](https://github.com/lovell/sharp) library to process and save the extracted images. You will need to install it with your preferred package manager.
209
+
182
210
  ```ts
183
- import { configureUnPDF, renderPageAsImage } from 'unpdf'
211
+ import { readFile, writeFile } from 'node:fs/promises'
212
+ import sharp from 'sharp'
213
+ import { extractImages, getDocumentProxy } from 'unpdf'
214
+
215
+ async function extractPdfImages() {
216
+ const buffer = await readFile('./document.pdf')
217
+ const pdf = await getDocumentProxy(new Uint8Array(buffer))
218
+
219
+ // Extract images from page 1
220
+ const imagesData = await extractImages(pdf, 1)
221
+ console.log(`Found ${imagesData.length} images on page 1`)
222
+
223
+ // Process each image with sharp (optional)
224
+ let totalImagesProcessed = 0
225
+ for (const imgData of imagesData) {
226
+ const imageIndex = ++totalImagesProcessed
227
+
228
+ await sharp(imgData.data, {
229
+ raw: {
230
+ width: imgData.width,
231
+ height: imgData.height,
232
+ channels: imgData.channels
233
+ }
234
+ })
235
+ .png()
236
+ .toFile(`image-${imageIndex}.png`)
237
+
238
+ console.log(`Saved image ${imageIndex} (${imgData.width}x${imgData.height}, ${imgData.channels} channels)`)
239
+ }
240
+ }
184
241
 
185
- await configureUnPDF({
186
- // Use the official PDF.js build
187
- pdfjs: () => import('pdfjs-dist'),
188
- })
242
+ extractPdfImages().catch(console.error)
243
+ ```
189
244
 
190
- const pdf = await readFile('./dummy.pdf')
191
- const buffer = new Uint8Array(pdf)
192
- const pageNumber = 1
245
+ ### `renderPageAsImage`
193
246
 
194
- const result = await renderPageAsImage(buffer, pageNumber, {
195
- canvas: () => import('canvas'),
196
- })
197
- await writeFile('dummy-page-1.png', result)
198
- ```
247
+ To render a PDF page as an image, you can use the `renderPageAsImage` method. This method will return an `ArrayBuffer` of the rendered image.
248
+
249
+ > [!NOTE]
250
+ > This method will only work in Node.js and browser environments.
251
+
252
+ In order to use this method, make sure to meet the following requirements:
253
+
254
+ - Use the official PDF.js build (see below for details).
255
+ - Install the [`@napi-rs/canvas`](https://github.com/Brooooooklyn/canvas) package if you are using Node.js. This package is required to render the PDF page as an image.
199
256
 
200
257
  **Type Declaration**
201
258
 
@@ -204,8 +261,8 @@ declare function renderPageAsImage(
204
261
  data: DocumentInitParameters['data'],
205
262
  pageNumber: number,
206
263
  options?: {
207
- canvas?: () => Promise<typeof import('canvas')>
208
- /** @default 1 */
264
+ canvasImport?: () => Promise<typeof import('@napi-rs/canvas')>
265
+ /** @default 1.0 */
209
266
  scale?: number
210
267
  width?: number
211
268
  height?: number
@@ -213,13 +270,24 @@ declare function renderPageAsImage(
213
270
  ): Promise<ArrayBuffer>
214
271
  ```
215
272
 
216
- ## FAQ
273
+ **Example**
274
+
275
+ ```ts
276
+ import { definePDFJSModule, renderPageAsImage } from 'unpdf'
217
277
 
218
- ### Why Is `canvas` An Optional Dependency?
278
+ // Use the official PDF.js build
279
+ await definePDFJSModule(() => import('pdfjs-dist'))
219
280
 
220
- The official PDF.js library depends on the `canvas` module for Node.js environments, which [doesn't work inside worker threads](https://github.com/Automattic/node-canvas/issues/1394). That's why `unpdf` ships with a serverless build of PDF.js that mocks the `canvas` module.
281
+ const pdf = await readFile('./dummy.pdf')
282
+ const buffer = new Uint8Array(pdf)
283
+ const pageNumber = 1
221
284
 
222
- However, to render PDF pages as images in Node.js environments, you need to install the `canvas` module. That's why it is a peer dependency.
285
+ const result = await renderPageAsImage(buffer, pageNumber, {
286
+ canvasImport: () => import('@napi-rs/canvas'),
287
+ scale: 2,
288
+ })
289
+ await writeFile('dummy-page-1.png', new Uint8Array(result))
290
+ ```
223
291
 
224
292
  ## License
225
293
 
package/dist/index.cjs CHANGED
@@ -16,24 +16,21 @@ async function getDocumentProxy(data, options = {}) {
16
16
  }
17
17
  async function getResolvedPDFJS() {
18
18
  if (!resolvedModule) {
19
- await resolvePDFJSImports();
19
+ await resolvePDFJSImport();
20
20
  }
21
21
  return resolvedModule;
22
22
  }
23
- async function resolvePDFJSImports(pdfjsResolver, { force = false } = {}) {
24
- if (resolvedModule && !force) {
23
+ async function resolvePDFJSImport(pdfjsResolver, { reload = false } = {}) {
24
+ if (resolvedModule && !reload) {
25
25
  return;
26
26
  }
27
27
  if (pdfjsResolver) {
28
28
  try {
29
29
  resolvedModule = await interopDefault(pdfjsResolver());
30
- if (resolvedModule && "resolvePDFJS" in resolvedModule) {
31
- resolvedModule = await resolvedModule.resolvePDFJS();
32
- }
33
30
  return;
34
31
  } catch (error) {
35
32
  console.error(error);
36
- throw new Error("Resolving failed. Please check the provided configuration.");
33
+ throw new Error("PDF.js could not be resolved. Please check the resolver function.");
37
34
  }
38
35
  }
39
36
  try {
@@ -41,7 +38,7 @@ async function resolvePDFJSImports(pdfjsResolver, { force = false } = {}) {
41
38
  resolvedModule = await resolvePDFJS();
42
39
  } catch (error) {
43
40
  console.error(error);
44
- throw new Error("PDF.js is not available. Please add the package as a dependency.");
41
+ throw new Error("PDF.js package not available. Please install it first.");
45
42
  }
46
43
  }
47
44
  function isPDFDocumentProxy(data) {
@@ -52,6 +49,87 @@ async function interopDefault(m) {
52
49
  return resolved.default || resolved;
53
50
  }
54
51
 
52
+ let canvasModule;
53
+ /**
54
+ * Derived from the PDF.js project by the Mozilla Foundation.
55
+ * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
56
+ * @license Apache-2.0
57
+ */
58
+ class BaseCanvasFactory {
59
+ #enableHWA = false;
60
+ constructor({ enableHWA = false } = {}) {
61
+ this.#enableHWA = enableHWA;
62
+ }
63
+ create(width, height) {
64
+ const canvas = this._createCanvas(width, height);
65
+ return {
66
+ canvas,
67
+ context: canvas.getContext("2d", {
68
+ willReadFrequently: !this.#enableHWA
69
+ })
70
+ };
71
+ }
72
+ reset({ canvas }, width, height) {
73
+ if (!canvas) {
74
+ throw new Error("Canvas is not specified");
75
+ }
76
+ canvas.width = width;
77
+ canvas.height = height;
78
+ }
79
+ destroy(context) {
80
+ if (!context.canvas) {
81
+ throw new Error("Canvas is not specified");
82
+ }
83
+ context.canvas.width = 0;
84
+ context.canvas.height = 0;
85
+ context.canvas = void 0;
86
+ context.context = void 0;
87
+ }
88
+ // eslint-disable-next-line unused-imports/no-unused-vars
89
+ _createCanvas(width, height) {
90
+ throw new Error("Not implemented");
91
+ }
92
+ }
93
+ /**
94
+ * Derived from the PDF.js project by the Mozilla Foundation.
95
+ * @see https://github.com/mozilla/pdf.js/blob/b8de9a372f9bbf7e33adb362eeae5ef1919dba73/src/display/canvas_factory.js#L18
96
+ * @license Apache-2.0
97
+ */
98
+ class DOMCanvasFactory extends BaseCanvasFactory {
99
+ _document;
100
+ constructor({ ownerDocument = globalThis.document, enableHWA = false } = {}) {
101
+ super({ enableHWA });
102
+ this._document = ownerDocument;
103
+ }
104
+ _createCanvas(width, height) {
105
+ const canvas = this._document.createElement("canvas");
106
+ canvas.width = width;
107
+ canvas.height = height;
108
+ return canvas;
109
+ }
110
+ }
111
+ class NodeCanvasFactory extends BaseCanvasFactory {
112
+ constructor({ enableHWA = false } = {}) {
113
+ super({ enableHWA });
114
+ }
115
+ _createCanvas(width, height) {
116
+ if (!canvasModule) {
117
+ throw new Error("@napi-rs/canvas module is not resolved");
118
+ }
119
+ return canvasModule.createCanvas(width, height);
120
+ }
121
+ }
122
+ async function resolveCanvasModule(canvasImport) {
123
+ canvasModule ??= await interopDefault(canvasImport());
124
+ }
125
+ function injectCanvasConstructors() {
126
+ if (!canvasModule)
127
+ return;
128
+ globalThis.DOMMatrix ??= canvasModule.DOMMatrix;
129
+ globalThis.ImageData ??= canvasModule.ImageData;
130
+ globalThis.Path2D ??= canvasModule.Path2D;
131
+ }
132
+
55
133
  async function extractImages$1(data, pageNumber) {
56
134
  const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
57
135
  if (pageNumber < 1 || pageNumber > pdf.numPages) {
@@ -88,74 +166,41 @@ async function extractImages$1(data, pageNumber) {
88
166
  return images;
89
167
  }
90
168
  async function renderPageAsImage$1(data, pageNumber, options = {}) {
91
- const canvasFactory = await createIsomorphicCanvasFactory(options.canvas);
92
- const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data, { canvasFactory });
169
+ const CanvasFactory = await createIsomorphicCanvasFactory(options.canvasImport);
170
+ const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data, { CanvasFactory });
93
171
  const page = await pdf.getPage(pageNumber);
94
172
  if (pageNumber < 1 || pageNumber > pdf.numPages) {
95
173
  throw new Error(`Invalid page number. Must be between 1 and ${pdf.numPages}.`);
96
174
  }
97
- let outputScale = options.scale || 1;
98
- let viewport = page.getViewport({ scale: outputScale });
175
+ const defaultViewport = page.getViewport({ scale: 1 });
176
+ let scale = options.scale || 1;
99
177
  if (options.width) {
100
- outputScale = options.width / viewport.width;
178
+ scale = options.width / defaultViewport.width;
101
179
  } else if (options.height) {
102
- outputScale = options.height / viewport.height;
180
+ scale = options.height / defaultViewport.height;
103
181
  }
104
- if (outputScale !== 1 && outputScale > 0) {
105
- viewport = page.getViewport({ scale: outputScale });
106
- }
107
- const ctx = canvasFactory.create(viewport.width, viewport.height);
182
+ const viewport = page.getViewport({ scale: Math.max(0, scale) });
183
+ const drawingContext = new CanvasFactory().create(viewport.width, viewport.height);
108
184
  await page.render({
109
- canvasContext: ctx.context,
185
+ canvasContext: drawingContext.context,
110
186
  viewport
111
187
  }).promise;
112
- const dataUrl = isBrowser ? ctx.canvas.toDataURL() : ctx.canvas.toDataURL();
188
+ const dataUrl = drawingContext.canvas.toDataURL();
113
189
  const response = await fetch(dataUrl);
114
190
  return await response.arrayBuffer();
115
191
  }
116
- async function createIsomorphicCanvasFactory(canvas) {
117
- const _canvas = canvas ? await interopDefault(canvas()) : void 0;
118
- return {
119
- _createCanvas(width, height) {
120
- if (isBrowser) {
121
- const canvas2 = document.createElement("canvas");
122
- canvas2.width = width;
123
- canvas2.height = height;
124
- return canvas2;
125
- }
126
- if (isNode) {
127
- if (!_canvas) {
128
- throw new Error('Failed to resolve "canvas" package.');
129
- }
130
- return _canvas.createCanvas(width, height);
131
- }
132
- throw new Error("Unsupported environment for canvas creation.");
133
- },
134
- create(width, height) {
135
- const _canvas2 = this._createCanvas(width, height);
136
- const context = _canvas2.getContext(
137
- "2d"
138
- );
139
- return {
140
- canvas: _canvas2,
141
- context
142
- };
143
- },
144
- reset(ctx, width, height) {
145
- if (ctx.canvas) {
146
- ctx.canvas.width = width;
147
- ctx.canvas.height = height;
148
- }
149
- },
150
- destroy(ctx) {
151
- if (ctx.canvas) {
152
- ctx.canvas.width = 0;
153
- ctx.canvas.height = 0;
154
- }
155
- ctx.canvas = void 0;
156
- ctx.context = void 0;
192
+ async function createIsomorphicCanvasFactory(canvasImport) {
193
+ if (isBrowser)
194
+ return DOMCanvasFactory;
195
+ if (isNode) {
196
+ if (!canvasImport) {
197
+ throw new Error('Parameter "canvasImport" is required in Node.js environment.');
157
198
  }
158
- };
199
+ await resolveCanvasModule(canvasImport);
200
+ injectCanvasConstructors();
201
+ return NodeCanvasFactory;
202
+ }
203
+ throw new Error("Unsupported environment for canvas creation.");
159
204
  }
160
205
 
161
206
  async function getMeta$1(data) {
@@ -163,12 +208,12 @@ async function getMeta$1(data) {
163
208
  const meta = await pdf.getMetadata();
164
209
  return {
165
210
  info: meta?.info ?? {},
166
- metadata: meta?.metadata?.getAll() ?? {}
211
+ metadata: { ...meta?.metadata }
167
212
  };
168
213
  }
169
214
 
170
215
  async function extractText$1(data, options = {}) {
171
- const { mergePages = false } = { ...options };
216
+ const { mergePages = false } = options;
172
217
  const pdf = isPDFDocumentProxy(data) ? data : await getDocumentProxy(data);
173
218
  const texts = await Promise.all(
174
219
  Array.from({ length: pdf.numPages }, (_, i) => getPageText(pdf, i + 1))
@@ -184,36 +229,36 @@ async function getPageText(document, pageNumber) {
184
229
  return content.items.filter((item) => item.str != null).map((item) => item.str + (item.hasEOL ? "\n" : "")).join("");
185
230
  }
186
231
 
232
+ async function definePDFJSModule(pdfjs) {
233
+ await resolvePDFJSImport(pdfjs, { reload: true });
234
+ }
187
235
  async function configureUnPDF(options) {
188
- const { pdfjs } = { ...options };
189
- if (pdfjs) {
190
- await resolvePDFJSImports(pdfjs, { force: true });
191
- }
236
+ await resolvePDFJSImport(options.pdfjs, { reload: true });
192
237
  }
193
238
 
194
239
  const getMeta = async (...args) => {
195
- await resolvePDFJSImports();
240
+ await resolvePDFJSImport();
196
241
  return await getMeta$1(...args);
197
242
  };
198
243
  const extractText = async (...args) => {
199
- await resolvePDFJSImports();
244
+ await resolvePDFJSImport();
200
245
  return await extractText$1(...args);
201
246
  };
202
247
  const extractImages = async (...args) => {
203
- await resolvePDFJSImports();
248
+ await resolvePDFJSImport();
204
249
  return await extractImages$1(...args);
205
250
  };
206
251
  const renderPageAsImage = async (...args) => {
207
- await resolvePDFJSImports();
252
+ await resolvePDFJSImport();
208
253
  return await renderPageAsImage$1(...args);
209
254
  };
210
255
 
211
256
  exports.configureUnPDF = configureUnPDF;
212
- exports.createIsomorphicCanvasFactory = createIsomorphicCanvasFactory;
257
+ exports.definePDFJSModule = definePDFJSModule;
213
258
  exports.extractImages = extractImages;
214
259
  exports.extractText = extractText;
215
260
  exports.getDocumentProxy = getDocumentProxy;
216
261
  exports.getMeta = getMeta;
217
262
  exports.getResolvedPDFJS = getResolvedPDFJS;
218
263
  exports.renderPageAsImage = renderPageAsImage;
219
- exports.resolvePDFJSImports = resolvePDFJSImports;
264
+ exports.resolvePDFJSImport = resolvePDFJSImport;