unpdf 1.9.0 → 2.0.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 +97 -280
- package/dist/index.d.mts +123 -130
- package/dist/index.mjs +439 -246
- package/dist/pdfjs.mjs +6 -6
- package/package.json +14 -30
- package/dist/index.cjs +0 -462
- package/dist/index.d.cts +0 -192
- package/dist/pdfjs.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# unpdf
|
|
2
2
|
|
|
3
|
-
Utilities for PDF extraction and rendering across all JavaScript runtimes – Node.js,
|
|
3
|
+
Utilities for PDF extraction and rendering across all JavaScript runtimes – Node.js, Bun, the browser, and serverless environments like Cloudflare Workers. Especially useful for AI applications that need to summarize or analyze PDF documents.
|
|
4
4
|
|
|
5
5
|
Ships with a serverless build of Mozilla's [PDF.js](https://github.com/mozilla/pdf.js), optimized for edge environments. If you're coming from [`pdf-parse`](https://www.npmjs.com/package/pdf-parse), `unpdf` is a modern, actively maintained alternative with broader runtime support.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- 🏗️ Works in Node.js,
|
|
10
|
-
- 🪭 Includes serverless build of PDF.js ([`unpdf/pdfjs`](
|
|
11
|
-
- 💬 Extract [text](#extract-text
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- 🏗️ Works in Node.js 24+, Bun, browsers, and serverless environments ([Cloudflare Workers example](./examples/cloudflare))
|
|
10
|
+
- 🪭 Includes serverless build of PDF.js ([`unpdf/pdfjs`](#how-it-works))
|
|
11
|
+
- 💬 Extract [text](#extract-text), [images as PNG](#extract-images-as-png), links, and metadata – no native dependencies
|
|
12
|
+
- 🖼️ [Render pages as PNG, JPEG, or WebP](#render-a-page-as-an-image)
|
|
13
|
+
- 🔍 [Spot scanned pages](#spot-scanned-pages) before sending them to OCR
|
|
14
|
+
- 🧱 Opt-in to a [custom PDF.js build](#custom-pdfjs-build)
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -22,379 +23,195 @@ pnpm add unpdf
|
|
|
22
23
|
npm install unpdf
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
##
|
|
26
|
+
## Use Cases
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
Every method takes binary PDF data (`Uint8Array`, `ArrayBuffer`) or a document proxy from [`getDocumentProxy`](#getdocumentproxy). Pass a proxy to run several methods on one document without parsing it again.
|
|
29
|
+
|
|
30
|
+
### Extract Text
|
|
28
31
|
|
|
29
32
|
```ts
|
|
30
33
|
import { extractText, getDocumentProxy } from 'unpdf'
|
|
31
34
|
|
|
32
|
-
// Fetch a PDF from the web or load it from the file system
|
|
33
35
|
const buffer = await fetch('https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf')
|
|
34
36
|
.then(res => res.arrayBuffer())
|
|
35
37
|
|
|
36
38
|
const pdf = await getDocumentProxy(new Uint8Array(buffer))
|
|
37
39
|
const { totalPages, text } = await extractText(pdf, { mergePages: true })
|
|
38
|
-
|
|
39
|
-
console.log(`Total pages: ${totalPages}`)
|
|
40
|
-
console.log(text)
|
|
41
40
|
```
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
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.
|
|
42
|
+
Without `mergePages`, `text` holds one string per page. For coordinates and font sizes, e.g. to detect tables, use [`extractTextItems`](#extracttextitems).
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
> PDF.js v5.x uses `Promise.withResolvers`, which may not be supported in all environments, such as Node < 22. Consider using the bundled serverless build, which includes a polyfill, or use an older version of PDF.js.
|
|
44
|
+
### Extract Images as PNG
|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
PNG encoding needs no canvas and no native dependency, so it also runs in Cloudflare Workers:
|
|
51
47
|
|
|
52
48
|
```ts
|
|
53
|
-
import {
|
|
49
|
+
import { writeFile } from 'node:fs/promises'
|
|
50
|
+
import { extractImages } from 'unpdf'
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
await definePDFJSModule(() => import('pdfjs-dist'))
|
|
52
|
+
const images = await extractImages(pdf, 1, { format: 'png' })
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const { text } = await extractText(pdf)
|
|
54
|
+
for (const [index, image] of images.entries())
|
|
55
|
+
await writeFile(`image-${index + 1}.png`, image.data)
|
|
61
56
|
```
|
|
62
57
|
|
|
63
|
-
|
|
58
|
+
Without `format`, `data` holds raw RGBA pixels. Each image carries its `bounds` on the page.
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
In browsers, a document proxy decodes some images into bitmaps, which `extractImages` skips. Pass binary data instead, or create the proxy with `isOffscreenCanvasSupported: false` and `isImageDecoderSupported: false`.
|
|
66
61
|
|
|
67
|
-
|
|
62
|
+
### Render a Page as an Image
|
|
63
|
+
|
|
64
|
+
Browsers render on a DOM canvas, which a Web Worker lacks. Node.js and Bun need [`@napi-rs/canvas`](https://github.com/Brooooooklyn/canvas), passed as `canvasImport`. Serverless runtimes like Cloudflare Workers have no canvas.
|
|
68
65
|
|
|
69
66
|
```ts
|
|
70
|
-
import {
|
|
67
|
+
import { renderPageAsImage } from 'unpdf'
|
|
71
68
|
|
|
72
|
-
const
|
|
69
|
+
const image = await renderPageAsImage(pdf, 1, {
|
|
70
|
+
canvasImport: () => import('@napi-rs/canvas'),
|
|
71
|
+
scale: 2,
|
|
72
|
+
format: 'jpeg',
|
|
73
|
+
quality: 0.8,
|
|
74
|
+
})
|
|
73
75
|
```
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
> If no other PDF.js build was defined, the serverless build will always be used.
|
|
77
|
+
### Spot Scanned Pages
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
`inspectPages` measures text and images per page – where to draw the line between a text page and a scan depends on your documents:
|
|
79
80
|
|
|
80
81
|
```ts
|
|
81
|
-
import {
|
|
82
|
-
import { getResolvedPDFJS } from 'unpdf'
|
|
82
|
+
import { inspectPages } from 'unpdf'
|
|
83
83
|
|
|
84
|
-
const {
|
|
85
|
-
const data = await readFile('./dummy.pdf')
|
|
86
|
-
const document = await getDocument(new Uint8Array(data)).promise
|
|
84
|
+
const { pages } = await inspectPages(pdf)
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
// Hidden OCR layers count as invisible text, so subtract them.
|
|
87
|
+
const scannedPageNumbers = pages
|
|
88
|
+
.map((page, index) => ({ ...page, pageNumber: index + 1 }))
|
|
89
|
+
.filter(page => page.textLength - page.invisibleTextLength < 20 && page.imageCoverage > 0.8)
|
|
90
|
+
.map(page => page.pageNumber)
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
### Process Untrusted PDFs
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
> The serverless PDF.js bundle is built from PDF.js v6.4.299. It requires the global `Iterator` constructor (Safari 18.4+); without it, importing the bundle throws.
|
|
95
|
-
|
|
96
|
-
Heart and soul of this package is the [`pdfjs.rolldown.config.ts`](./pdfjs.rolldown.config.ts) file. It uses [Rolldown](https://rolldown.rs/) to bundle PDF.js into a single file for serverless environments. The key techniques:
|
|
95
|
+
PDF.js does not execute scripts embedded in PDFs, but resource limits are your job:
|
|
97
96
|
|
|
98
|
-
- **
|
|
99
|
-
- **
|
|
100
|
-
- **
|
|
97
|
+
- **Image decoding:** `maxImageSize` is unlimited by default – pass e.g. `getDocumentProxy(data, { maxImageSize: 16_777_216 })` (~16 MP) so a single declared image can't allocate gigabytes.
|
|
98
|
+
- **Page fan-out:** `extractText`, `extractTextItems`, `extractLinks`, and `inspectPages` process all pages in one call – check `pdf.numPages` against a limit first.
|
|
99
|
+
- **Timeouts:** with the serverless build, parsing runs on your event loop (no worker) – race extraction calls against a timeout.
|
|
101
100
|
|
|
102
101
|
## API
|
|
103
102
|
|
|
104
|
-
|
|
103
|
+
`data` is binary PDF data or a `PDFDocumentProxy` throughout.
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
### `getDocumentProxy`
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
Creates a `PDFDocumentProxy` with `useSystemFonts: true`. In Node.js with `pdfjs-dist` installed, it uses that package's standard fonts instead of system fonts, and its CJK character maps (`cMapUrl`). Cloudflare Workers have no local `pdfjs-dist` – to extract text from CJK fonts without embedded mappings, pass `cMapUrl: 'https://unpkg.com/pdfjs-dist@6.4.299/cmaps/'`, `cMapPacked: true`, and `useWorkerFetch: true`. Your options override the defaults.
|
|
109
108
|
|
|
110
109
|
```ts
|
|
111
|
-
function
|
|
110
|
+
function getDocumentProxy(data: Uint8Array | ArrayBuffer, options?: DocumentInitParameters): Promise<PDFDocumentProxy>
|
|
112
111
|
```
|
|
113
112
|
|
|
114
|
-
### `
|
|
115
|
-
|
|
116
|
-
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.
|
|
113
|
+
### `extractText`
|
|
117
114
|
|
|
118
|
-
|
|
115
|
+
Extracts the text of every page, or one string with `mergePages: true` – line breaks kept, at most one blank line in a row.
|
|
119
116
|
|
|
120
117
|
```ts
|
|
121
|
-
function
|
|
118
|
+
function extractText(data, options?: { mergePages?: boolean }): Promise<{ totalPages: number, text: string | string[] }>
|
|
122
119
|
```
|
|
123
120
|
|
|
124
|
-
### `
|
|
125
|
-
|
|
126
|
-
Creates a `PDFDocumentProxy` from binary PDF data. Every extraction method accepts either raw data or an existing proxy – use this when you want to reuse one document across multiple calls.
|
|
127
|
-
|
|
128
|
-
Applies sensible defaults: `useSystemFonts: true`; in Node.js additionally `disableFontFace: true`, plus `standardFontDataUrl` and `cMapUrl`/`cMapPacked` resolved from the local `pdfjs-dist` package for standard font and CJK character map support (see the font rendering tip in [`renderPageAsImage`](#renderpageasimage)).
|
|
121
|
+
### `extractTextItems`
|
|
129
122
|
|
|
130
|
-
|
|
123
|
+
Extracts positioned text items per page: `str`, `x` and `y` in PDF space (origin bottom-left), `width`, `height`, `fontSize`, `fontFamily`, `dir`, `hasEOL`.
|
|
131
124
|
|
|
132
125
|
```ts
|
|
133
|
-
function
|
|
134
|
-
data: DocumentInitParameters['data'],
|
|
135
|
-
options?: DocumentInitParameters,
|
|
136
|
-
): Promise<PDFDocumentProxy>
|
|
126
|
+
function extractTextItems(data): Promise<{ totalPages: number, items: StructuredTextItem[][] }>
|
|
137
127
|
```
|
|
138
128
|
|
|
139
|
-
### `
|
|
140
|
-
|
|
141
|
-
Extracts metadata from a PDF. If `parseDates` is set to `true`, the date properties will be parsed into `Date` objects.
|
|
129
|
+
### `extractLinks`
|
|
142
130
|
|
|
143
|
-
|
|
131
|
+
Extracts the URLs of link annotations per page.
|
|
144
132
|
|
|
145
133
|
```ts
|
|
146
|
-
function
|
|
147
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
148
|
-
options?: {
|
|
149
|
-
parseDates?: boolean
|
|
150
|
-
},
|
|
151
|
-
): Promise<{
|
|
152
|
-
info: Record<string, any>
|
|
153
|
-
metadata: Record<string, any>
|
|
154
|
-
}>
|
|
134
|
+
function extractLinks(data): Promise<{ totalPages: number, links: string[][] }>
|
|
155
135
|
```
|
|
156
136
|
|
|
157
|
-
### `
|
|
158
|
-
|
|
159
|
-
Extracts all text from a PDF. If `mergePages` is set to `true`, the text of all pages will be merged into a single string with line breaks preserved and at most one blank line in a row. Otherwise, an array of strings for each page will be returned.
|
|
137
|
+
### `extractImages`
|
|
160
138
|
|
|
161
|
-
|
|
139
|
+
Extracts the images painted on a page – image XObjects, inline images, and image masks – as RGBA pixels or PNG bytes, each with `width`, `height`, and `bounds` in PDF space.
|
|
162
140
|
|
|
163
141
|
```ts
|
|
164
|
-
function
|
|
165
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
166
|
-
options?: {
|
|
167
|
-
mergePages?: boolean
|
|
168
|
-
}
|
|
169
|
-
): Promise<{
|
|
170
|
-
totalPages: number
|
|
171
|
-
text: string | string[]
|
|
172
|
-
}>
|
|
142
|
+
function extractImages(data, pageNumber: number, options?: { format?: 'rgba' | 'png' }): Promise<ExtractedImage[]>
|
|
173
143
|
```
|
|
174
144
|
|
|
175
|
-
### `
|
|
176
|
-
|
|
177
|
-
Extracts text with layout information – one array of positioned items per page. Useful when plain text is not enough and you need coordinates, font sizes, or reading direction, e.g. for table detection or positional parsing.
|
|
145
|
+
### `renderPageAsImage`
|
|
178
146
|
|
|
179
|
-
|
|
147
|
+
Renders a page as image bytes, or as a data URL with `toDataURL: true`. `width` or `height` scale the page to that size.
|
|
180
148
|
|
|
181
149
|
```ts
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
fontFamily: string
|
|
192
|
-
/** Text direction: `"ltr"`, `"rtl"`, or `"ttb"`. */
|
|
193
|
-
dir: string
|
|
194
|
-
/** Whether the text item is followed by a line break. */
|
|
195
|
-
hasEOL: boolean
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function extractTextItems(
|
|
199
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
200
|
-
): Promise<{
|
|
201
|
-
totalPages: number
|
|
202
|
-
items: StructuredTextItem[][]
|
|
203
|
-
}>
|
|
150
|
+
function renderPageAsImage(data, pageNumber: number, options?: {
|
|
151
|
+
canvasImport?: () => Promise<typeof import('@napi-rs/canvas')>
|
|
152
|
+
scale?: number // Default: 1
|
|
153
|
+
width?: number
|
|
154
|
+
height?: number
|
|
155
|
+
format?: 'png' | 'jpeg' | 'webp' // Default: 'png'
|
|
156
|
+
quality?: number // From 0 to 1, for JPEG and WebP
|
|
157
|
+
toDataURL?: boolean
|
|
158
|
+
}): Promise<Uint8Array | string>
|
|
204
159
|
```
|
|
205
160
|
|
|
206
|
-
### `
|
|
207
|
-
|
|
208
|
-
Extracts all links from a PDF document, including hyperlinks and external URLs.
|
|
209
|
-
|
|
210
|
-
**Type Declaration**
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
function extractLinks(
|
|
214
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
215
|
-
): Promise<{
|
|
216
|
-
totalPages: number
|
|
217
|
-
links: string[]
|
|
218
|
-
}>
|
|
219
|
-
```
|
|
161
|
+
### `inspectPages`
|
|
220
162
|
|
|
221
|
-
|
|
163
|
+
Measures each page: `textLength`, `invisibleTextLength` (text drawn invisibly, as by OCR layers), `imageCount`, and `imageCoverage` (share of the page covered by images, from 0 to 1).
|
|
222
164
|
|
|
223
165
|
```ts
|
|
224
|
-
|
|
225
|
-
import { extractLinks, getDocumentProxy } from 'unpdf'
|
|
226
|
-
|
|
227
|
-
// Load a PDF file
|
|
228
|
-
const buffer = await readFile('./document.pdf')
|
|
229
|
-
const pdf = await getDocumentProxy(new Uint8Array(buffer))
|
|
230
|
-
|
|
231
|
-
// Extract all links from the PDF
|
|
232
|
-
const { totalPages, links } = await extractLinks(pdf)
|
|
233
|
-
|
|
234
|
-
console.log(`Total pages: ${totalPages}`)
|
|
235
|
-
console.log(`Found ${links.length} links:`)
|
|
236
|
-
for (const link of links) console.log(link)
|
|
166
|
+
function inspectPages(data): Promise<{ totalPages: number, pages: PageInspection[] }>
|
|
237
167
|
```
|
|
238
168
|
|
|
239
|
-
### `
|
|
240
|
-
|
|
241
|
-
Extracts images from a specific page of a PDF document, including necessary metadata such as width, height, and calculated color channels. Works with both the serverless and official PDF.js build.
|
|
169
|
+
### `getMeta`
|
|
242
170
|
|
|
243
|
-
|
|
171
|
+
Extracts the `/Info` dictionary and the XMP metadata (`null` without). `parseDates: true` turns the dates into `Date` objects.
|
|
244
172
|
|
|
245
173
|
```ts
|
|
246
|
-
|
|
247
|
-
data: Uint8ClampedArray
|
|
248
|
-
width: number
|
|
249
|
-
height: number
|
|
250
|
-
channels: 1 | 3 | 4
|
|
251
|
-
key: string
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function extractImages(
|
|
255
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
256
|
-
pageNumber: number,
|
|
257
|
-
): Promise<ExtractedImageObject[]>
|
|
174
|
+
function getMeta(data, options?: { parseDates?: boolean }): Promise<{ info: PDFInfo, metadata: Metadata | null }>
|
|
258
175
|
```
|
|
259
176
|
|
|
260
|
-
|
|
177
|
+
### `definePDFJSModule`
|
|
261
178
|
|
|
262
|
-
|
|
263
|
-
> 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.
|
|
179
|
+
Sets the PDF.js build every other method uses. It loads on first use.
|
|
264
180
|
|
|
265
181
|
```ts
|
|
266
|
-
|
|
267
|
-
import sharp from 'sharp'
|
|
268
|
-
import { extractImages, getDocumentProxy } from 'unpdf'
|
|
269
|
-
|
|
270
|
-
async function extractPdfImages() {
|
|
271
|
-
const buffer = await readFile('./document.pdf')
|
|
272
|
-
const pdf = await getDocumentProxy(new Uint8Array(buffer))
|
|
273
|
-
|
|
274
|
-
// Extract images from page 1
|
|
275
|
-
const imagesData = await extractImages(pdf, 1)
|
|
276
|
-
console.log(`Found ${imagesData.length} images on page 1`)
|
|
277
|
-
|
|
278
|
-
// Process each image with sharp (optional)
|
|
279
|
-
let totalImagesProcessed = 0
|
|
280
|
-
for (const imgData of imagesData) {
|
|
281
|
-
const imageIndex = ++totalImagesProcessed
|
|
282
|
-
|
|
283
|
-
await sharp(imgData.data, {
|
|
284
|
-
raw: {
|
|
285
|
-
width: imgData.width,
|
|
286
|
-
height: imgData.height,
|
|
287
|
-
channels: imgData.channels
|
|
288
|
-
}
|
|
289
|
-
})
|
|
290
|
-
.png()
|
|
291
|
-
.toFile(`image-${imageIndex}.png`)
|
|
292
|
-
|
|
293
|
-
console.log(`Saved image ${imageIndex} (${imgData.width}x${imgData.height}, ${imgData.channels} channels)`)
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
extractPdfImages().catch(console.error)
|
|
182
|
+
function definePDFJSModule(pdfjs: () => Promise<PDFJS>): void
|
|
298
183
|
```
|
|
299
184
|
|
|
300
|
-
### `
|
|
301
|
-
|
|
302
|
-
To render a PDF page as an image, you can use the `renderPageAsImage` method. This method will return an `ArrayBuffer` of the rendered image. It can also return a data URL (`string`) if `toDataURL` option is set to `true`.
|
|
303
|
-
|
|
304
|
-
> [!NOTE]
|
|
305
|
-
> This method will only work in Node.js and browser environments.
|
|
306
|
-
|
|
307
|
-
In order to use this method, make sure to meet the following requirements:
|
|
308
|
-
|
|
309
|
-
- Use the official PDF.js build (see [Official or Legacy PDF.js Build](#official-or-legacy-pdfjs-build)).
|
|
310
|
-
- 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.
|
|
311
|
-
|
|
312
|
-
> [!TIP]
|
|
313
|
-
> In Node.js, `getDocumentProxy` automatically sets `disableFontFace: true` and resolves `standardFontDataUrl` from your local `pdfjs-dist` package for correct font rendering. To customize this behavior, pass your own options:
|
|
314
|
-
>
|
|
315
|
-
> ```ts
|
|
316
|
-
> const pdf = await getDocumentProxy(buffer, {
|
|
317
|
-
> disableFontFace: false,
|
|
318
|
-
> standardFontDataUrl: 'https://unpkg.com/pdfjs-dist@latest/standard_fonts/',
|
|
319
|
-
> })
|
|
320
|
-
> ```
|
|
185
|
+
### `getResolvedPDFJS`
|
|
321
186
|
|
|
322
|
-
|
|
187
|
+
Returns the resolved PDF.js module – the serverless build, unless `definePDFJSModule` set another – to call the PDF.js API directly.
|
|
323
188
|
|
|
324
189
|
```ts
|
|
325
|
-
function
|
|
326
|
-
data: DocumentInitParameters['data'] | PDFDocumentProxy,
|
|
327
|
-
pageNumber: number,
|
|
328
|
-
options?: {
|
|
329
|
-
canvasImport?: () => Promise<typeof import('@napi-rs/canvas')>
|
|
330
|
-
/** @default 1.0 */
|
|
331
|
-
scale?: number
|
|
332
|
-
width?: number
|
|
333
|
-
height?: number
|
|
334
|
-
toDataURL?: boolean
|
|
335
|
-
},
|
|
336
|
-
): Promise<ArrayBuffer | string>
|
|
190
|
+
function getResolvedPDFJS(): Promise<PDFJS>
|
|
337
191
|
```
|
|
338
192
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
```ts
|
|
342
|
-
import { readFile, writeFile } from 'node:fs/promises'
|
|
343
|
-
import { definePDFJSModule, renderPageAsImage } from 'unpdf'
|
|
193
|
+
## Custom PDF.js Build
|
|
344
194
|
|
|
345
|
-
|
|
346
|
-
await definePDFJSModule(() => import('pdfjs-dist'))
|
|
347
|
-
|
|
348
|
-
const pdf = await readFile('./dummy.pdf')
|
|
349
|
-
const buffer = new Uint8Array(pdf)
|
|
350
|
-
const pageNumber = 1
|
|
351
|
-
|
|
352
|
-
const result = await renderPageAsImage(buffer, pageNumber, {
|
|
353
|
-
canvasImport: () => import('@napi-rs/canvas'),
|
|
354
|
-
scale: 2,
|
|
355
|
-
})
|
|
356
|
-
await writeFile('dummy-page-1.png', new Uint8Array(result))
|
|
357
|
-
```
|
|
195
|
+
`unpdf` uses its serverless build unless you define another one, e.g. to pin a different PDF.js version. Install `pdfjs-dist` and call `definePDFJSModule` before any other method:
|
|
358
196
|
|
|
359
197
|
```ts
|
|
360
|
-
import {
|
|
361
|
-
import { definePDFJSModule, renderPageAsImage } from 'unpdf'
|
|
362
|
-
|
|
363
|
-
await definePDFJSModule(() => import('pdfjs-dist'))
|
|
198
|
+
import { definePDFJSModule } from 'unpdf'
|
|
364
199
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const pageNumber = 1
|
|
200
|
+
definePDFJSModule(() => import('pdfjs-dist/legacy/build/pdf.mjs'))
|
|
201
|
+
```
|
|
368
202
|
|
|
369
|
-
|
|
370
|
-
canvasImport: () => import('@napi-rs/canvas'),
|
|
371
|
-
scale: 2,
|
|
372
|
-
toDataURL: true,
|
|
373
|
-
})
|
|
203
|
+
In Node.js, import the legacy build as shown. The default `pdfjs-dist` entry targets current browsers – it fails on Node.js 24 and warns on newer versions. `renderPageAsImage` still requires `canvasImport`, though PDF.js draws with the `@napi-rs/canvas` that `pdfjs-dist` installs as an optional dependency.
|
|
374
204
|
|
|
375
|
-
|
|
376
|
-
<html lang="en">
|
|
377
|
-
<head>
|
|
378
|
-
<meta charset="UTF-8">
|
|
379
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
380
|
-
<title>Dummy Page</title>
|
|
381
|
-
</head>
|
|
382
|
-
<body>
|
|
383
|
-
<img alt="Example Page" src="${result}">
|
|
384
|
-
</body>
|
|
385
|
-
</html>`
|
|
386
|
-
|
|
387
|
-
await writeFile('dummy-page-1.html', html)
|
|
388
|
-
```
|
|
205
|
+
## How It Works
|
|
389
206
|
|
|
390
|
-
|
|
207
|
+
> [!NOTE]
|
|
208
|
+
> The serverless PDF.js bundle is built from PDF.js v6.4.299. It requires the global `Iterator` constructor (Safari 18.4+); without it, importing the bundle throws.
|
|
391
209
|
|
|
392
|
-
|
|
210
|
+
Heart and soul of this package is the [`pdfjs.rolldown.config.ts`](./pdfjs.rolldown.config.ts) file. It uses [Rolldown](https://rolldown.rs/) to bundle PDF.js into a single file for serverless environments. The key techniques:
|
|
393
211
|
|
|
394
|
-
- **
|
|
395
|
-
- **
|
|
396
|
-
- **
|
|
397
|
-
- The raw `getDocument` from `unpdf/pdfjs` applies none of these defaults.
|
|
212
|
+
- **String replacements** strip browser-specific references from the PDF.js source.
|
|
213
|
+
- **Worker inlining** embeds the PDF.js worker directly into the main bundle, since serverless runtimes can't load separate worker files.
|
|
214
|
+
- **Global polyfills** fill in newer APIs like `Promise.try` and `Map.prototype.getOrInsert` for runtimes that lack them, plus `FinalizationRegistry` for Cloudflare Workers.
|
|
398
215
|
|
|
399
216
|
## License
|
|
400
217
|
|