pdf-to-png-converter
Version:
Node.js utility to convert PDF file/buffer pages to PNG files/buffers. No build-time compilation required — pre-built native binaries included for all major platforms.
392 lines (291 loc) • 16.3 kB
Markdown
<p align="center">
<strong>🎯 Convert PDF pages to PNG images with no native compilation required</strong>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/pdf-to-png-converter">
<img src="https://img.shields.io/npm/v/pdf-to-png-converter.svg?style=flat-square" alt="npm version">
</a>
<a href="https://www.npmjs.com/package/pdf-to-png-converter">
<img src="https://img.shields.io/npm/dm/pdf-to-png-converter.svg?style=flat-square" alt="npm downloads">
</a>
<a href="https://github.com/dichovsky/pdf-to-png-converter/actions/workflows/test.yml">
<img src="https://github.com/dichovsky/pdf-to-png-converter/actions/workflows/test.yml/badge.svg?branch=main" alt="Tests">
</a>
<a href="https://github.com/dichovsky/pdf-to-png-converter/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/dichovsky/pdf-to-png-converter?style=flat-square" alt="License">
</a>
</p>
---
A high-performance Node.js library for converting PDF files and buffers to PNG images. Perfect for web applications, document processing pipelines, and image generation workflows.
**Key Benefits:**
- ✨ **No Build-Time Compilation** - Pre-built native binaries included via `@napi-rs/canvas`, no `node-gyp` or compiler toolchain required
- 🚀 **High Performance** - Supports parallel page processing
- 🔐 **Encrypted PDFs** - Handle password-protected documents
- 📦 **Lightweight** - Minimal dependencies
- 💪 **TypeScript Support** - Full type definitions included
- 🎨 **Flexible Rendering** - Advanced font and rendering options
> **Note:** `@napi-rs/canvas` ships platform-specific pre-built native binaries (no compilation step). See the [@napi-rs/canvas repository](https://github.com/Brooooooklyn/canvas) for the full list of supported platforms.
## Table of Contents
- [Installation](#installation)
- [Quick Start](#quick-start)
- [CLI Usage](#cli-usage)
- [API Reference](#api-reference)
- [Examples](#examples)
- [Performance Notes](#performance-notes)
- [Output Format](#output-format)
- [Migration Guide](#migration-guide)
- [Project Links](#project-links)
- [License](#license)
---
## Installation
### npm
```sh
npm install pdf-to-png-converter
```
### Yarn
```sh
yarn add pdf-to-png-converter
```
> **Node.js Requirement:** Node.js 22.13 or higher is required.
---
## Quick Start
Convert a PDF file to PNG images in just a few lines:
```javascript
const { pdfToPng } = require('pdf-to-png-converter');
(async () => {
const pngPages = await pdfToPng('document.pdf', {
outputFolder: './output',
});
console.log(`Converted ${pngPages.length} pages`);
})();
```
Or with TypeScript:
```typescript
import { pdfToPng, VerbosityLevel, type PngPageOutput } from 'pdf-to-png-converter';
const pngPages: PngPageOutput[] = await pdfToPng('document.pdf', {
outputFolder: './output',
verbosityLevel: VerbosityLevel.ERRORS, // 0=ERRORS, 1=WARNINGS, 5=INFOS
});
```
> **Existing files are not overwritten.** Disk writes use exclusive-create mode. Re-running a conversion with the same output filenames throws `EEXIST`; clear the target directory or generate unique filenames between runs.
---
You can use the converter directly from the terminal without writing code:
```sh
npx pdf-to-png-converter my-document.pdf --output-folder ./output
```
**Options:**
- `--output-folder <dir>`: Directory to save PNG files. Required for image conversion. Existing files are not overwritten; duplicate output filenames throw `EEXIST`.
- `--viewport-scale <number>`: Scale factor applied to each page viewport.
- `--use-system-fonts`: Attempt to use fonts installed on the host system.
- `--disable-font-face <true|false>`: Do not load embedded fonts.
- `--enable-xfa <true|false>`: Process XFA form data.
- `--pdf-file-password <pwd>`: Password for encrypted PDFs.
- `--pages-to-process <n,m,...>`: Comma-separated list of 1-based page numbers.
- `--verbosity-level <number>`: pdfjs verbosity level (0=errors, 1=warnings, 5=infos).
- `--return-metadata-only`: Return page metadata without rendering images. This prints JSON to stdout and does not require `--output-folder`.
- `--process-pages-in-parallel`: Process pages concurrently.
- `--concurrency-limit <number>`: Maximum number of pages rendered simultaneously.
- `--silent`: Suppress normal output messages unless there is an error.
- `--version`: Show package version.
- `--help`: Show help text.
The CLI has two output modes:
- image conversion: writes PNG files to `--output-folder`
- metadata inspection: prints JSON metadata to stdout with `--return-metadata-only`
If you need in-memory PNG buffers, use the library API (`returnPageContent`) rather than the CLI.
---
## API Reference
### `pdfToPng(input, options?)`
Converts PDF pages to PNG images.
**Parameters:**
| Parameter | Type | Description |
| --------- | ----------------------------------------- | ------------------------------------------------ |
| `input` | `string \| ArrayBufferLike \| Uint8Array` | PDF file path, ArrayBuffer, or Uint8Array/Buffer |
| `options` | `PdfToPngOptions` | Optional configuration object |
**Returns:** `Promise<PngPageOutput[]>` - Array of converted PNG pages
### Options
```typescript
{
// Font & Rendering Options
disableFontFace?: boolean, // Disable font face rendering (default: true)
useSystemFonts?: boolean, // Use system fonts as fallback (default: false)
enableXfa?: boolean, // Render XFA forms (default: true)
// Output Options
outputFolder?: string, // Directory to save PNG files; existing files are not overwritten
outputFileMaskFunc?: (pageNumber: number) => string, // Custom filename function
// Must return a flat filename. "/" is rejected on all platforms;
// "\" is also rejected on Windows.
// Rendering Options
viewportScale?: number, // PNG scale/zoom level (default: 1.0, max: 100)
// Note: large pages can still hit the 100-million-pixel canvas limit
// at scales well below 100. Reduce viewportScale if you get an error.
// Security
pdfFilePassword?: string, // Password for encrypted PDFs
maxInputBytes?: number, // Max input PDF size in bytes (default: 256 * 1024 * 1024)
// Path inputs are stat()'d before reading and non-regular files
// (FIFOs, sockets, /dev/zero) are rejected. Buffer / Uint8Array
// inputs are validated against the same cap by byteLength.
// Processing
pagesToProcess?: number[], // 1-indexed integer pages to convert (e.g., [1, 3, 5])
// Non-integer and <= 0 values throw; pages beyond the PDF length are ignored
processPagesInParallel?: boolean, // Enable parallel processing (default: false)
concurrencyLimit?: number, // Max concurrent pages (parallel) / worker-pool size (worker
// threads): integer 1..16 (default: 4). The upper bound caps
// peak in-flight canvas memory at ~6.4 GiB.
renderInWorkerThreads?: boolean, // Rasterize pages in a pool of worker threads (default: false)
// True multi-core parallelism (pool size = concurrencyLimit); each
// worker loads its own document copy. Pays off on multi-page,
// render-heavy PDFs; identical pixels, ordered results.
// Output Control
returnPageContent?: boolean, // Include PNG buffer in output (default: true)
returnMetadataOnly?: boolean, // Return only page dimensions/rotation without rendering (default: false)
// Logging
verbosityLevel?: VerbosityLevel, // VerbosityLevel.ERRORS | WARNINGS | INFOS (default: ERRORS)
// Use the VerbosityLevel enum for readable values:
// import { VerbosityLevel } from 'pdf-to-png-converter'
}
```
---
```javascript
const { pdfToPng } = require('pdf-to-png-converter');
(async () => {
const pngPages = await pdfToPng('document.pdf', {
outputFolder: './output',
});
console.log(`Successfully converted ${pngPages.length} pages`);
})();
```
```typescript
import { pdfToPng, VerbosityLevel } from 'pdf-to-png-converter';
const pngPages = await pdfToPng('document.pdf', {
// Rendering
viewportScale: 2.0, // 2x zoom for higher resolution
disableFontFace: false, // Use font face rendering
useSystemFonts: true, // Fallback to system fonts
// Output
outputFolder: './pdf-images',
outputFileMaskFunc: (pageNumber) => `page-${String(pageNumber).padStart(3, '0')}.png`,
returnPageContent: true,
// Performance
processPagesInParallel: true,
concurrencyLimit: 8,
// Logging
verbosityLevel: VerbosityLevel.WARNINGS, // Log warnings
});
```
```javascript
const pngPages = await pdfToPng('document.pdf', {
outputFolder: './output',
pagesToProcess: [1, 3, 5], // Only convert first, third, and fifth pages
});
```
```javascript
const pngPages = await pdfToPng('protected.pdf', {
outputFolder: './output',
pdfFilePassword: 'mypassword',
});
```
```javascript
const fs = require('fs');
const { pdfToPng } = require('pdf-to-png-converter');
const pdfBuffer = fs.readFileSync('document.pdf');
const pngPages = await pdfToPng(pdfBuffer, {
outputFolder: './output',
outputFileMaskFunc: (pageNumber) => `page_${pageNumber}.png`,
});
```
```javascript
// Without returning page content (saves memory for large PDFs)
const pngPages = await pdfToPng('large-document.pdf', {
outputFolder: './output',
returnPageContent: false, // Don't keep PNG buffers in memory
processPagesInParallel: true, // Process multiple pages concurrently
concurrencyLimit: 4,
});
// Pages are written to disk, content property will be undefined
pngPages.forEach((page) => {
if (page.kind === 'file') {
console.log(`Saved: ${page.path}`);
}
});
```
```javascript
// Inspect page dimensions and rotation without rendering any images
const pages = await pdfToPng('document.pdf', {
returnMetadataOnly: true,
});
pages.forEach((page) => {
console.log(`Page ${page.pageNumber}: ${page.width}x${page.height}px, rotation=${page.rotation}`);
});
```
This is significantly faster than full rendering and useful for checking page counts, dimensions, or orientation before deciding how to process a document.
---
- **Pipelined processing.** PNG encoding runs on the libuv threadpool and overlaps page rendering, so files may finish writing out of page order even in default (non-parallel) mode. Always consume results via the resolved, page-ordered array rather than directory-watch order.
- **Threadpool sizing.** PNG encodes and disk writes share Node's libuv threadpool (4 threads by default). For parallel file-output workloads on many-core machines, raising it can help: `UV_THREADPOOL_SIZE=8 node app.js`.
- **Strict serial processing.** If you need exactly one page in flight at a time (minimal memory, strict on-disk ordering), use `processPagesInParallel: true` with `concurrencyLimit: 1` — a sliding window of exactly one page.
- **Multi-core rendering.** `processPagesInParallel` interleaves pages on one thread — rasterization itself never runs in parallel. For CPU-bound documents (large embedded images, complex vector art), `renderInWorkerThreads: true` rasterizes pages in a pool of worker threads instead (measured ~3× end-to-end on a 12-page image-heavy document with the default pool of 4). Cost: one PDF copy + one pdf.js instance of memory per worker, plus one extra copy of the PDF retained on the main thread for the duration of the conversion, and a few hundred ms of pool startup per conversion — so prefer it for multi-page, render-heavy work rather than small documents.
---
## Output Format
The `pdfToPng` function returns an array of discriminated page objects. Branch on `kind` before using mode-specific fields:
| `kind` | When returned | `path` | `content` |
| ---------- | -------------------------------- | --------- | ----------------------------------------------- |
| `metadata` | `returnMetadataOnly: true` | `''` | `undefined` |
| `content` | Rendering without `outputFolder` | `''` | PNG `Buffer`, unless `returnPageContent: false` |
| `file` | Rendering with `outputFolder` | File path | PNG `Buffer`, unless `returnPageContent: false` |
All output objects also include `pageNumber`, `name`, `width`, `height`, and `rotation`. `width` and `height` are integer pixel dimensions of the rendered image: a fractional viewport (for example a 595×842 pt A4 page at `viewportScale: 1.5`, i.e. 892.5×1263) is floored to match the bitmap the canvas allocates (892×1263). `returnMetadataOnly` reports the same floored dimensions a render would produce — and, for the same reason, rejects the same unrenderable pages a render would: a `viewportScale` that floors a page to `0` px, or one whose rendered (floored) canvas area exceeds the internal canvas pixel limit, throws the identical error on both paths rather than returning dimensions for a page that cannot be rendered.
```javascript
[
{
kind: 'content',
pageNumber: 1, // Page number in the PDF
name: 'document_page_1.png', // PNG filename
content: Buffer<...>, // PNG image data
// undefined if returnPageContent=false
path: '', // Empty string for in-memory and metadata results
width: 612, // Image width in pixels (integer; floored from viewportScale)
height: 792, // Image height in pixels (integer; floored from viewportScale)
rotation: 0 // Page rotation in degrees: 0, 90, 180, or 270
},
// ... more pages
]
```
```javascript
pngPages.forEach((page) => {
if (page.kind === 'file') {
console.log(page.path);
}
if (page.kind === 'content' && page.content) {
console.log(page.content.byteLength);
}
});
```
---
Version **4.0.0** introduced public and behavioral changes that existing consumers may need to adopt:
1. **`PngPageOutput` is now discriminated.** Branch on `page.kind` before reading `page.path` or assuming `page.content` is present.
2. **`verbosityLevel` is now typed as `VerbosityLevel`.** Prefer `VerbosityLevel.ERRORS`, `VerbosityLevel.WARNINGS`, or `VerbosityLevel.INFOS` instead of raw numeric literals.
3. **Invalid `pagesToProcess` values now throw early.** `0`, negative numbers, and non-integers are rejected immediately; page numbers above the document length are still ignored.
4. **Disk writes are now exclusive-create (`'wx'`).** Re-running the same conversion into the same output filenames now throws `EEXIST`; clear the target directory or generate unique filenames between runs.
See the [changelog](CHANGELOG.md) for the full release history.
---
- [Changelog](CHANGELOG.md)
- [Contributing Guide](CONTRIBUTING.md)
- [Security Policy](SECURITY.md)
- [Issue Tracker](https://github.com/dichovsky/pdf-to-png-converter/issues)
---
MIT © [dichovsky](https://github.com/dichovsky)
In case you want to support my work:
[](https://buymeacoffee.com/dichovsky)