UNPKG

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
# pdf-to-png-converter <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. --- ## CLI Usage 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' } ``` --- ## Examples ### Basic Usage ```javascript const { pdfToPng } = require('pdf-to-png-converter'); (async () => { const pngPages = await pdfToPng('document.pdf', { outputFolder: './output', }); console.log(`Successfully converted ${pngPages.length} pages`); })(); ``` ### Advanced Configuration ```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 }); ``` ### Convert Specific Pages ```javascript const pngPages = await pdfToPng('document.pdf', { outputFolder: './output', pagesToProcess: [1, 3, 5], // Only convert first, third, and fifth pages }); ``` ### Handle Encrypted PDFs ```javascript const pngPages = await pdfToPng('protected.pdf', { outputFolder: './output', pdfFilePassword: 'mypassword', }); ``` ### Convert from Buffer ```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`, }); ``` ### Memory-Efficient Processing ```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}`); } }); ``` ### Get Page Metadata Only ```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. --- ## Performance Notes - **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); } }); ``` --- ## Migration Guide 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. --- ## Project Links - [Changelog](CHANGELOG.md) - [Contributing Guide](CONTRIBUTING.md) - [Security Policy](SECURITY.md) - [Issue Tracker](https://github.com/dichovsky/pdf-to-png-converter/issues) --- ## License MIT © [dichovsky](https://github.com/dichovsky) ## Buy Me A Coffee In case you want to support my work: [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/dichovsky)