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

pdf-to-md-ts

v0.1.5

Published

Convert PDF documents to Markdown — browser & Node.js compatible

Readme

pdf-to-md-ts

Convert PDF documents to clean Markdown — works in browser and Node.js.

A fast, dependency-light PDF-to-Markdown converter powered by Mozilla's pdf.js. No external APIs, no server calls — everything runs locally.


Installation

npm install pdf-to-md-ts

Quick Start

Browser

import { pdfToMarkdown } from 'pdf-to-md-ts';

// From a file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput!.files![0];
const data = await file.arrayBuffer();

const markdown = await pdfToMarkdown(data);
console.log(markdown);

Node.js

import { pdfToMarkdown } from 'pdf-to-md-ts';
import { readFile } from 'node:fs/promises';

const data = await readFile('document.pdf');
const markdown = await pdfToMarkdown(data.buffer);

console.log(markdown);

API

pdfToMarkdown(data, options?)

Converts PDF data to a Markdown string.

Parameters

| Param | Type | Description | |-------|------|-------------| | data | ArrayBuffer \| Uint8Array | The PDF file contents | | options.signal | AbortSignal | Optional AbortSignal to cancel conversion | | options.pages | number[] | Optional 1-indexed page numbers to include, e.g. [1, 3, 5, 6, 7] | | options.headingSizes | HeadingSizes | Optional custom font-size thresholds for heading detection, e.g. { h1: 18, h2: 14 } |

Returns

Promise<string> — The converted Markdown text.

Errors

Throws on invalid PDF data. Throws AbortError (or Error in older Node.js) when cancelled via AbortSignal.

Types

import type { PdfToMarkdownOptions, TxtItem, Row, FontStyle } from 'pdf-to-md-ts';

Features

| Feature | Description | |---------|-------------| | H1–H6 | Heading detection by font size with bold verification | | Bold | **text** via glyph width analysis (>3% wider than body) | | Italic | *text* via glyph width analysis (>15% narrower than body) | | Bold+Italic | ***text*** (mixed formatting detected automatically) | | Lists | Ordered (1.) and unordered (-) with X-position nesting | | Tables | GFM pipe tables via column alignment analysis | | Blockquotes | > indented text blocks with mixed content support | | Code Blocks | Fenced ``` blocks for monospaced text regions | | Inline Code | `code` for monospaced inline text | | Underline | <u>text</u> via PDF annotations and graphic line detection | | Overline | <span style="text-decoration: overline">text</span> via graphic line detection | | Strikethrough | ~~text~~ via PDF annotations and graphic line detection | | Hyperlinks | Detected from PDF link annotations | | Cancellable | Pass an AbortSignal to cancel long conversions |


How It Works

PDF bytes → pdf.js text extraction → Layout analysis → mdast AST → Markdown string
  1. Text Extraction — pdf.js parses the PDF and extracts every text glyph with position, font name, font size, and glyph width.

  2. Layout Analysis — Glyphs are grouped into rows by Y-position, then processed through a detection pipeline:

    • Code blocks → monospaced font runs
    • Headings → font-size thresholds + bold verification
    • Lists → bullet/number patterns with indentation nesting
    • Tables → column position alignment
    • Blockquotes → X-position indentation
    • Paragraphs → merged consecutive non-special rows
  3. Decoration Detection — Horizontal graphic lines near text are analyzed to detect text decorations:

    • Lines above the text → overline
    • Lines through the middle → strikethrough
    • Lines below the baseline → underline
    • PDF annotations (Underline, StrikeOut) are also parsed for decoration detection
  4. Inline Formatting — A statistical width-per-character (WPC) analysis compares glyph widths across fonts:

    • Body text (most common font) → plain
    • Wider glyphs (>3%) → bold
    • Narrower glyphs (>15%) → italic
    • Between both → bold+italic
  5. Serialization — The resulting mdast tree is serialized to GFM-compatible Markdown.


Environment Support

| Environment | Status | Notes | |-------------|--------|-------| | Browser (ESM) | ✅ | pdf.js worker configured automatically | | Node.js 18+ | ✅ | Uses pdf.js legacy build (built-in worker) | | Node.js 16 | ✅ | Requires --experimental-fetch flag | | Deno | ⚠️ | Requires polyfill for document.createElement | | Bun | ⚠️ | Requires canvas polyfill for worker |


Limitations

  • Embedded images are not extracted — only text content is converted.
  • Scanned/image PDFs require OCR (not included).
  • Complex layouts (multi-column, rotated text, custom fonts) may produce suboptimal results.
  • H6 headings (10pt) are detected only when using a bold font — table cells at the same size are correctly excluded.

License

MIT