node-poppler
An asynchronous Node.js wrapper around the Poppler PDF rendering utilities for converting, splitting, merging, and inspecting PDF files.
Repository Health
Technical Analysis
node-poppler wraps the Poppler command-line utilities (pdftocairo, pdftohtml, pdftoppm, pdftops, pdftotext, pdfinfo, pdfimages, pdffonts, pdfattach, pdfdetach, pdfseparate, pdfunite) behind a single async/await-friendly Poppler class, so Node.js applications can convert, inspect, and manipulate PDF files without shelling out to child_process themselves.
Each Poppler binary is exposed as a class method (pdfToCairo, pdfToHtml, pdfToText, pdfToPpm, pdfToPs, pdfInfo, pdfImages, pdfFonts, pdfAttach, pdfDetach, pdfSeparate, pdfUnite) that accepts either a file path or an in-memory Buffer, validates its options object against a per-binary schema (including the minimum Poppler version an option requires), and returns a promise that resolves with stdout or rejects with a descriptive Error. This removes the need to hand-build CLI argument arrays or parse raw stderr codes.
It ships as CommonJS with bundled TypeScript type definitions, and on Windows automatically falls back to an optional node-poppler-win32 dependency that bundles prebuilt binaries, while Linux/macOS users install poppler-utils themselves.
What You Get
- A single
Popplerclass exposing one method per Poppler binary (pdfToCairo, pdfToHtml, pdfToPpm, pdfToPs, pdfToText, pdfInfo, pdfImages, pdfFonts, pdfAttach, pdfDetach, pdfSeparate, pdfUnite) - Accepts either a file path or an in-memory Buffer as input, with automatic stdin piping for Buffers
- Per-option validation against each binary’s accepted arguments, including minimum/maximum Poppler version checks so unsupported flags fail fast with a clear error
- AbortSignal support on every method for cancelling in-flight conversions
- Bundled TypeScript type definitions and full JSDoc for editor autocomplete without a separate @types package
- Automatic Windows binary resolution via the optional node-poppler-win32 dependency, falling back to a user-supplied binary path or PATH lookup on Linux/macOS
Common Use Cases
- Converting uploaded PDFs to PNG/JPEG thumbnails for a document management or preview UI
- Extracting plain text from PDFs for full-text search indexing or LLM ingestion pipelines
- Merging multiple generated PDF reports into a single downloadable file
- Splitting a multi-page PDF into individual per-page files for downstream processing
- Reading PDF metadata (page count, dimensions, permissions) before running expensive operations
- Extracting or attaching embedded files inside PDF containers
Under The Hood
Architecture
index.js exports a single Poppler class holding private fields for each resolved binary path plus two caches (#binVersions, #acceptedOptions). Every public method (pdfToCairo, pdfToText, pdfInfo, etc.) follows the same two-stage pipeline: parseOptions() validates the caller’s options object against a per-binary schema (checking type and, via semver comparison, whether the installed Poppler version actually supports the flag) and produces a CLI argument array terminated with an end-of-options -- marker; execBinary() then spawns the binary, pipes a Buffer over stdin or lets it read a filepath directly, and collects stdout/stderr concurrently with Promise.all. This is a facade over child_process — no caller ever touches spawn directly — so a change to execBinary()’s contract would ripple through all twelve public methods that depend on it.
Tech Stack
Plain JavaScript with JSDoc-driven types (TypeScript declarations are emitted separately via tsc -p tsconfig.build.json; no build step is needed to run the library, since main points straight at src/index.js). Runtime dependencies are deliberately minimal: camelcase formats keys for pdfInfo’s JSON output, ice-barrage deep-freezes the accepted-options maps, and semver powers the minVersion/maxVersion gating. An optional dependency, node-poppler-win32, supplies prebuilt Windows binaries so Windows users skip a separate poppler-utils install. Tooling is a modern JS stack: ESLint plus Prettier for style, Jest for tests with coverage thresholds enforced directly in package.json, commitlint for conventional commits, and licensee for dependency license auditing.
Code Quality
The test suite exceeds 1,600 lines and mocks child_process to exercise specific exit codes, stdout/stderr combinations, and AbortSignal cancellation, while also running real fixture PDFs through the binaries for integration-level coverage. Error handling is explicit rather than swallowed: known Poppler exit codes map to human-readable messages via a lookup table, and invalid options throw immediately with a joined list of specific messages. Naming is consistent camelCase mirroring each binary’s long-form flags, private class fields prevent external mutation of resolved binary paths, and CI runs lint, typecheck, and coverage-gated tests on every push alongside CodeQL and OSSF Scorecard supply-chain scanning.
API Design
The most distinctive choice is per-option, per-binary-version validation: each option schema carries an explicit minimum (and sometimes maximum) supported Poppler version, so calling a flag introduced in a newer Poppler release than what’s installed throws a specific, actionable error rather than failing silently or surfacing a cryptic native CLI error. Combined with transparent Buffer/stdin support and the -- end-of-options guard against hyphen-prefixed filenames being misread as flags, the API absorbs several common child_process footguns that thinner CLI wrappers leave to the caller — a refinement of an established wrapper pattern rather than a wholly new capability.