npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@captain-sdk/pdf-parser

v0.4.0

Published

Fast, self-contained PDF layout extraction (Rust + PDFium via WebAssembly). Parses PDFs into structured text, tables (as HTML), and figure placements with normalized bounding boxes, fully in-process: no network, no temp files.

Readme

@captain-sdk/pdf-parser

Fast, self-contained PDF layout extraction for Node.js. A Rust + PDFium engine compiled to WebAssembly turns a PDF into structured blocks: text, tables (as HTML), and figure placements, each with a normalized bounding box. Everything runs in-process: the PDF bytes are handed straight to WebAssembly and never leave your process. No network calls, no temp files, no external service.

Requirements

  • Node.js >= 18.3

Install

npm install @captain-sdk/pdf-parser

Quick start

const fs = require('fs');
const { initParser, parsePdf } = require('@captain-sdk/pdf-parser');

(async () => {
  // Load the engine once at startup (see "Performance" below). Optional but recommended.
  await initParser();

  const result = await parsePdf(fs.readFileSync('document.pdf'));

  console.log(result.pages);          // number of pages
  console.log(result.has_text_layer); // true if the PDF carries real text
  console.log(result.producer);       // e.g. "Billing System 2.0"
  console.log(result.blocks.length);  // extracted layout blocks
})();

ESM:

import { initParser, parsePdf } from '@captain-sdk/pdf-parser';

Output shape

interface ParseResult {
  pages: number;
  has_text_layer: boolean;
  producer: string | null;
  blocks: Block[];
}

interface Block {
  type: string;      // see block types below
  content: string;   // text, or HTML for tables, or "" for figures
  page: number;      // 1-indexed page
  left: number;      // bounding box, all normalized 0..1 from the top-left
  top: number;
  width: number;
  height: number;
}

parsePdf accepts a Uint8Array, Buffer, or ArrayBuffer. It rejects with an Error when the bytes are not a PDF the engine can open, so you can show a clean message instead of crashing.

Block types

  • Text, Header, Section Header, List Item, Key Value, Footer — text regions, classified by role. content is the text.
  • Tablecontent is HTML (<table><tr><td>...). Both ruled (lined) and unruled (whitespace-aligned) tables are reconstructed.
  • Figure — marks where a graphic sits, with a bounding box. content is empty: figures are located, not rasterized, and there is no OCR. Treat the count as approximate (vector art and raster images can both surface as figures).

Performance

The first call loads ~6 MB of WebAssembly and pays PDFium's one-time init, so it is the slow one (up to a couple of seconds cold). Every parse after that is fast (a few milliseconds for a simple page, ~100–200 ms for a dense multi-page doc).

Call initParser() once at startup so your first real parsePdf is not the call that eats the init cost. If you skip it, parsePdf initializes lazily on first use.

Bundlers and serverless

This package ships two runtime assets it loads from disk at call time: pdfium/pdfium.wasm and lib/globeparse_wasm_bg.wasm. In a plain Node process (node app.js) this just works.

Bundlers (webpack, esbuild, Next.js) and serverless packagers (AWS Lambda, Vercel) often do not trace or copy .wasm and sibling assets automatically. If you bundle or deploy and see a "file not found" or "PDFium engine" error at runtime, the asset folders were not included in your build. Fixes:

  • Keep the package external so it loads from node_modules at runtime:

    • esbuild: --external:@captain-sdk/pdf-parser
    • Next.js: add @captain-sdk/pdf-parser to serverExternalPackages
    • webpack: mark it in externals
  • Or copy the assets into your deployment and point the parser at them:

    await initParser({ assetsDir: '/var/task/pdfium' }); // where you copied pdfium/*

    assetsDir also works as the second argument to parsePdf(bytes, { assetsDir }).

PII masking (basic, off by default)

{ maskPii: true } runs a fast, pattern-based redaction over the common fields on standardized telecom bills (names, phone and account numbers, addresses, TINs). It is a lightweight local pre-filter, not a comprehensive or compliance-grade PII engine: it is tuned to specific bill formats and will miss values it has no pattern for (arbitrary names, non-standard phone or account formats, other document types).

For production-grade PII coverage, run the parsed text through a dedicated PII service such as Google Cloud DLP, or use Captain's server-side masking at index time.

Turn it on inline with the maskPii flag when parsing:

const { parsePdf, parsePdfBatch } = require('@captain-sdk/pdf-parser');

const result = await parsePdf(bytes, { maskPii: true });      // sanitized result
const batch = await parsePdfBatch(items, { maskPii: true });  // each entry sanitized

Or mask standalone with the maskPii primitive, so you can run your own processing first (or benchmark against another masker) and sanitize after:

const { maskPii } = require('@captain-sdk/pdf-parser');

const masked = maskPii(result);        // a ParseResult in, a sanitized ParseResult out
const maskedText = maskPii(someText);  // also accepts a raw string

maskPii returns a sanitized copy and never mutates its input.

Pass types to mask only some entity types (default masks all):

maskPii(result, { types: ['PHONE_NUMBER', 'ACCOUNT_NUMBER'] });

Values are replaced with Presidio-style angle-bracket tokens. The types and their tokens: PERSON<PERSON>, EMAIL_ADDRESS<EMAIL_ADDRESS>, PHONE_NUMBER<PHONE_NUMBER>, LOCATION<LOCATION>, ACCOUNT_NUMBER<ACCOUNT_NUMBER>, TIN<TIN>, INVOICE_NUMBER<INVOICE_NUMBER>, BARCODE<BARCODE>. Charge descriptions, amounts, dates, plan names, and generic labels are preserved.

Licensing

MIT (see LICENSE). The bundled PDFium engine is distributed under its own BSD-3-Clause license, included as PDFIUM-LICENSE.