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

extract-pdf

v0.1.151

Published

Convert a PDF (URL or ArrayBuffer) into clean HTML with structural tagging — headings, lists, footnotes, code blocks, bold/italic. Works in Node.js, Cloudflare Workers, and browser environments.

Downloads

23,330

Readme

extract-pdf

When users upload a PDF, they expect an instant chat response, not to wait for 5 min on OCR model.

Instant no-backend-needed javascript to convert a PDF (URL or ArrayBuffer) into clean HTML with structural tagging — headings, lists, footnotes, code blocks, bold/italic, and Table of Contents entries. Works in Node.js, Cloudflare Workers, and browser environments via pdfjs-serverless.

Install

bun add extract-pdf

Usage

import { convertPDFToHTML } from "extract-pdf";

const { html, title, author } = await convertPDFToHTML(
  "https://example.com/paper.pdf",
);
// or pass an ArrayBuffer from fs.readFile / fetch
const { html } = await convertPDFToHTML(buffer, { addPageNumbers: true });

Options

| Option | Default | Description | | ----------------- | ---------------------- | ------------------------------------------------------------------------------------------ | | addPageNumbers | false | Inserts [n] markers at each page boundary | | addCitation | true | Reads PDF metadata and first-page heading to populate title/author in the return value | | method | "ts-block-algorithm" | Parsing engine — "ts-block-algorithm", "liteparse", or "liteparse-wasm" (see below) | | liteParseOptions| {} | Passed through to LiteParse's constructor when method is "liteparse" or "liteparse-wasm" |

Return value

{ html: string, title?: string, author?: string, format: "pdf" }

Parse methods

convertPDFToHTML supports three interchangeable parsing engines via options.method:

| Method | Engine | Environments | OCR | | ------------------------------------ | -------------------------------------------------------------------- | ----------------------------------- | --- | | "ts-block-algorithm" (default) | The pure-TS pipeline documented below (this package) | Node.js, Cloudflare Workers, browser | No | | "liteparse" | LiteParse (native, @llamaindex/liteparse) | Node.js only | Optional | | "liteparse-wasm" | LiteParse (WASM, @llamaindex/liteparse-wasm) | Node.js, Cloudflare Workers, browser | Optional (via callback) |

import { convertPDFToHTML } from "extract-pdf";

const { html } = await convertPDFToHTML(buffer, { method: "liteparse" });

// Or the WASM build, which also runs in browsers and Cloudflare Workers:
const { html } = await convertPDFToHTML(buffer, { method: "liteparse-wasm" });

LiteParse ships a native (napi) addon, so method: "liteparse" only runs in Node.js — it is not bundled into browser or Cloudflare Workers builds. Install it explicitly (bun add @llamaindex/liteparse) since it's an optional dependency; if it isn't installed, convertPDFToHTML returns { error } instead of throwing.

method: "liteparse-wasm" delegates to LiteParse's WebAssembly build instead, which runs anywhere WASM does — browsers, Cloudflare Workers, and Node.js. Install it explicitly (bun add @llamaindex/liteparse-wasm) since it's also an optional dependency; if it isn't installed, convertPDFToHTML returns { error } instead of throwing. The WASM build has no OCR engine built in — pass a liteParseOptions.ocrEngine callback (e.g. backed by tesseract-js) to enable OCR.

By default both LiteParse paths run with OCR disabled (ocrEnabled: false) — matching this package's "instant, no backend" philosophy. Use detectPdfNeedsOcr (below) to decide when a document is worth re-parsing with liteParseOptions: { ocrEnabled: true }.

Detecting whether a PDF needs OCR

Before committing to a full (and potentially slow) OCR parse, detectPdfNeedsOcr runs a cheap, text-layer-only pass and reports whether each page needs OCR or other heavy parsing — useful for routing documents to different pipelines (fast path vs. OCR vs. screenshots vs. a heavier parser like LlamaParse or Docling):

import { detectPdfNeedsOcr, convertPDFToHTML } from "extract-pdf";

const assessment = await detectPdfNeedsOcr(buffer);
// { needsOcr: boolean, pages: PageComplexityStats[], reasons: string[] }

if (!assessment.needsOcr) {
  const { html } = await convertPDFToHTML(buffer, { method: "liteparse" });
} else {
  console.log("Needs OCR:", assessment.reasons); // e.g. ["scanned", "sparse-text"]
  // Route to an OCR-enabled pipeline, e.g.:
  const { html } = await convertPDFToHTML(buffer, {
    method: "liteparse",
    liteParseOptions: { ocrEnabled: true },
  });
}

reasons collects every distinct signal found across pages: "scanned", "no-text", "sparse-text", "embedded-images", "garbled", or "vector-text". Like method: "liteparse", this is Node.js only and requires @llamaindex/liteparse.

Pipeline

The conversion runs a sequential chain of transformations on a ParseResult (pages → items):

Raw pdfjs text spans
  → CalculateGlobalStats   — font heights, distances, format map
  → CompactLines           — merge spans on the same y-line into LineItems
  → RemoveRepetitiveElements — strip recurring page headers/footers
  → VerticalToHorizontal   — rotate vertical character runs
  → DetectTOC              — identify Table of Contents pages, link headings
  → DetectHeaders          — classify items as H1–H6 by font height
  → DetectListItems        — detect bullet/numbered list items
  → GatherBlocks           — group adjacent same-type lines into blocks
  → DetectCodeQuoteBlocks  — mark indented blocks as CODE
  → DetectListLevels       — add indentation for nested list levels
  → ToTextBlocks           — flatten blocks to { category, text } pairs
  → ToHTML                 — render pairs as <p>, <h1>–<h6>, <ul>, <code>

Folder structure

src/
  pdf-to-html.ts          — main entry point (convertPDFToHTML)
  liteparse-to-html.ts    — "liteparse" method (native napi addon)
  liteparse-wasm-to-html.ts — "liteparse-wasm" method (WASM, browser/edge)
  models/                 — data classes: Page, ParseResult, TextItem,
  │                         LineItem, LineItemBlock, Word, BlockType, …
  transforms/
  │  base/                — abstract Transformation, ToLineItem*, ToLineItemBlock*
  │  line-item/           — per-line-item transformations
  │  block/               — per-block transformations
  │  calculate-global-stats.ts
  │  to-text-blocks.ts
  │  to-html.ts
  utils/
     string-functions.ts
     page-item-functions.ts
     page-number-functions.ts