MD-to-PDF

Convert Markdown files to beautifully formatted PDF documents with syntax highlighting, math support, and auto-generated Table of Contents.

MD-to-PDF

Convert Markdown files to beautifully formatted PDF documents with syntax highlighting, math support, and auto-generated Table of Contents.


Features

  • Table of Contents — Auto-generated, clickable TOC with configurable depth and heading filter
  • TOC Depth Control — Limit TOC entries to any heading level (1–6, default: 3)
  • TOC Heading Filter — Regex-based filter to include only specific headings (default: all headings)
  • Syntax Highlighting — Code blocks with highlight.js support (180+ languages)
  • Math Support — LaTeX equations via MathJax (inline $...$ and block $$...$$)
  • Chrome Auto-Detection — Automatically finds Chrome/Edge/Chromium on Windows, macOS, and Linux
  • Customizable Output — Page format (A4, Letter, Legal, Tabloid, A3, A5), margins, orientation
  • Clean Design — Professional typography (Times New Roman, justified text) optimized for printing
  • Colorful CLI — Chalk-powered terminal output with progress indicators and status icons

Requirements

  • Node.js >= 16.0.0
  • Chrome, Edge, or Chromium browser installed

Installation

# Clone the repository
git clone https://github.com/calculusphile/MD-to-PDF.git
cd md-to-pdf

# Install dependencies
npm install

Global Installation (optional)

npm install -g .
# Now you can use 'md-to-pdf' from anywhere

Quick Start

# Basic conversion
node cli.js input.md

# Specify output file
node cli.js input.md -o output.pdf

# Using npm scripts
npm run convert -- input.md -o output.pdf

CLI Usage

md-to-pdf <input> [options]

Arguments:
  input                      Input markdown file path

Options:
  -v, --version              Display version number
  -o, --output <file>        Output PDF file path
  --chrome-path <path>       Custom Chrome/Chromium executable path
  --no-toc                   Disable Table of Contents generation
  --toc-title <title>        Custom TOC title (default: "Table of Contents")
  --toc-filter <pattern>     Regex pattern to filter TOC headings (default: all headings)
  --toc-depth <depth>        Maximum heading depth for TOC, 1-6 (default: "3")
  -f, --format <format>      Page format: A4, Letter, Legal, Tabloid, A3, A5 (default: "A4")
  -l, --landscape            Use landscape orientation
  --margin-top <margin>      Top margin (default: "25.4mm")
  --margin-bottom <margin>   Bottom margin (default: "25.4mm")
  --margin-left <margin>     Left margin (default: "25.4mm")
  --margin-right <margin>    Right margin (default: "25.4mm")
  -h, --help                 Display help for command

Examples

# Basic conversion
node cli.js notes.md

# Custom output file
node cli.js lecture-notes.md -o final-document.pdf

# Without Table of Contents
node cli.js quick-doc.md --no-toc

# TOC with only Chapter and Section headings
node cli.js book.md --toc-filter "Chapter|Section"

# TOC limited to H1 and H2 only
node cli.js report.md --toc-depth 2

# Include all headings in TOC (match everything)
node cli.js book.md --toc-filter ".*"

# Letter format (US)
node cli.js report.md --format Letter

# Landscape orientation
node cli.js wide-tables.md -l

# Custom margins
node cli.js book.md --margin-top 30mm --margin-bottom 30mm

# Specify Chrome path
node cli.js input.md --chrome-path "C:\Program Files\Google\Chrome\Application\chrome.exe"

Programmatic Usage

const { convertMdToPdf } = require('./lib/converter');

async function main() {
    const result = await convertMdToPdf('input.md', 'output.pdf', {
        format: 'A4',
        noToc: false,
        tocTitle: 'Contents',
        tocFilter: null,     // null = include all headings
        tocMaxDepth: 3,      // include h1, h2, h3
        landscape: false
    });
    
    if (result.success) {
        console.log('PDF generated:', result.outputPath);
    } else {
        console.error('Error:', result.message);
    }
}

main();

Table of Contents Filtering

By default, all headings up to the configured depth are included in the TOC. You can customize this with:

  • --toc-depth <n> — Only include headings up to level n (e.g., --toc-depth 2 includes H1 and H2 only)
  • --toc-filter <pattern> — Regex pattern to match heading text (e.g., "Chapter|Section" includes only headings containing those words)
  • --no-toc — Disable TOC entirely

Supported Markdown Features

  • Headings (H1-H6)
  • Bold, Italic, Strikethrough
  • Ordered and unordered lists
  • Code blocks with syntax highlighting
  • Tables
  • Blockquotes
  • Images & Links
  • Horizontal rules
  • Math equations (LaTeX syntax — inline $...$ and block $$...$$)

Math Support

Inline math: $E = mc^2$

Block math:

$$
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
$$

Code Highlighting

Supports 180+ languages via highlight.js with GitHub-style syntax theme.


Project Structure

md-to-pdf/
├── cli.js              # CLI entry point
├── lib/
│   ├── converter.js    # Core conversion logic
│   └── styles.js       # CSS styles configuration
├── package.json        # Project configuration
├── README.md           # User documentation
├── DEVELOPER.md        # Developer documentation
└── .gitignore          # Git ignore rules

npm Scripts

Script Description
npm start Run CLI (prompts for args)
npm run convert -- <file> Convert a file
npm run legacy Run original super-convert.js
npm run test Test conversion with project.md
npm run help Show CLI help

Tech Stack

  • Node.js — Runtime
  • Puppeteer-core — Headless Chrome PDF rendering
  • marked — Markdown parser
  • marked-highlight — Syntax highlighting integration
  • highlight.js — Code syntax highlighting (180+ languages)
  • MathJax — LaTeX math rendering
  • Commander — CLI argument parsing
  • Chalk — Colorful terminal output

Troubleshooting

Chrome Not Found

If auto-detection fails, specify the path manually:

Windows:

node cli.js input.md --chrome-path "C:\Program Files\Google\Chrome\Application\chrome.exe"

macOS:

node cli.js input.md --chrome-path "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

Linux:

node cli.js input.md --chrome-path "/usr/bin/google-chrome"

PDF Generation Fails

  1. Ensure Chrome/Edge/Chromium is installed
  2. Check that the input markdown file exists
  3. Verify you have write permissions for the output directory