Skip to content

v2.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 28 Oct 21:22
· 1 commit to main since this release

unpdf 2.0 is here! 🎉

This major release extracts every image on a page, adds page inspection and more rendering formats, and trims the API. unpdf now ships as ESM only and requires Node.js 24.

🔥 What's New

  • Complete image extraction – extractImages returns inline images and image masks too, as RGBA pixels or as PNG with format: 'png', each with its position on the page in bounds.
  • Page inspection – inspectPages measures text and image coverage per page, e.g. to tell scanned pages from born-digital ones.
  • JPEG and WebP rendering – renderPageAsImage takes format: 'png' | 'jpeg' | 'webp' and a quality.
  • CMaps and standard fonts in Node.js – with pdfjs-dist installed, both load from that package, so CJK text without embedded mappings extracts correctly.
  • Typed metadata – getMeta types the /Info dictionary as PDFInfo.

🚀 How to Upgrade

pnpm add unpdf@^2

unpdf 2 requires Node.js 24 or later. CommonJS builds are gone – replace require('unpdf') with import. The steps below cover every other breaking change.

1. Define PDF.js Without await

definePDFJSModule stores the loader and loads PDF.js on first use. configureUnPDF and resolvePDFJSImport are removed.

- await configureUnPDF({ pdfjs: () => import('pdfjs-dist/legacy/build/pdf.mjs') })
+ definePDFJSModule(() => import('pdfjs-dist/legacy/build/pdf.mjs'))

A loader that fails now throws on the first call of another method. To load PDF.js up front, call await getResolvedPDFJS() where you used resolvePDFJSImport().

2. Flatten extractLinks for All Links

links holds one array per page.

  const { links } = await extractLinks(pdf)
+ const allLinks = links.flat()

3. Use the Uint8Array From renderPageAsImage

  const image = await renderPageAsImage(pdf, 1, {
    canvasImport: () => import('@napi-rs/canvas'),
  })
- await writeFile('page.png', new Uint8Array(image))
+ await writeFile('page.png', image)

A scale, width, or height of 0 now throws instead of falling back to a scale of 1. createIsomorphicCanvasFactory is removed – each document carries PDF.js' own canvas factory as pdf.canvasFactory.

4. Read Extracted Images as RGBA

ExtractedImageObject is now ExtractedImage. data always holds four bytes per pixel, so channels is gone, and key is null for small inline images.

  const [image] = await extractImages(pdf, 1)
  await sharp(image.data, {
-   raw: { width: image.width, height: image.height, channels: image.channels },
+   raw: { width: image.width, height: image.height, channels: 4 },
  }).toFile('image.png')

Or drop sharp and pass { format: 'png' }. A page may now return more images than before, since inline images and image masks count, or fewer, since images inside soft masks and hidden layers are skipped.

5. Check the getMeta Types

info is typed as PDFInfo and metadata as Metadata | null – the runtime values are unchanged. Non-standard /Info entries sit in info.Custom, a Map.

- const title = metadata.get('dc:title')
+ const title = metadata?.get('dc:title')

6. Expect Standard Fonts in Node.js Renders

With pdfjs-dist installed, getDocumentProxy sets useSystemFonts: false in Node.js and draws non-embedded standard fonts with PDF.js' own font files instead of the host's. To keep the host fonts, pass useSystemFonts: true to getDocumentProxy.


   🚨 Breaking Changes

   🚀 Features

   🐞 Bug Fixes

   🏎 Performance

    View changes on GitHub