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

md2pdf-th

v4.0.3

Published

Markdown to PDF library & CLI — Thai/English, dark theme, TOC, cover, merge, watch, NestJS module

Readme


md2pdf-th is an open-source Markdown-to-PDF converter with first-class Thai language support. It works as a CLI tool, Node.js library, and NestJS module — featuring auto font detection, YAML frontmatter, HTML sanitization, batch conversion, watch mode, web preview, and PDF merging. Powered by Puppeteer via md-to-pdf with pdf-lib for metadata, watermarks, and merging.

Latest Release: v4.0.3 — 19 bug fixes (3 Critical + 5 High + 6 Medium + 5 Low), 12 new regression tests, JSDoc documentation for all functions. See CHANGELOG.md.


Table of contents


What's new in V4

V4 brings a complete rewrite of the core engine with Thai-first design, YAML frontmatter options, built-in templates, PDF watermarking, web preview server, and NestJS integration.

| Area | Change | Impact | |------|--------|--------| | V4.0.3 Bug Audit | 19 bug fixes — frontmatter merge, XSS sanitizer, action.yml RCE, path traversal, watermark API | Stability ⬆️ | | V4.0.2 Concurrency | Deadlock fix, rate limit hardening, path traversal protection | Reliability ⬆️ | | V4.0.0 Rewrite | New core engine, frontmatter options, templates, watermark, web preview, NestJS module | Features ⬆️ |

Critical: Frontmatter merge ignoring CLI flags (_explicitKeys), sanitizeHtml XSS bypass (unclosed tags), action.yml expression injection (RCE)

High: Image serving path traversal, web-ui.html broken import, output path detection, --no-page-numbers + --header conflict, embedFont wrong API

Medium: Regex EOF/sync issues, quoted value parsing, null guard, NestJS content override, CI split

Low: TypeScript types, import path comment, Tabloid dropdown

Plus 12 regression tests and JSDoc for all functions.


Quick start

# Using npx (no install)
npx md2pdf-th document.md

# Global install
npm install -g md2pdf-th
md2pdf-th document.md

# Dark theme + TOC + cover
md2pdf-th --theme dark --toc --cover doc.md

# Use as library
const { md2pdfTh } = require('md2pdf-th');
const pdf = await md2pdfTh({ content: '# สวัสดี', theme: 'dark' });
docker build -t md2pdf-th .
docker run --rm -v $(pwd):/data md2pdf-th /data/doc.md /data/output.pdf

Installation Options

Lightweight install (skip Chromium download)

If you already have Chromium/Chrome on your system:

# Skip Puppeteer's Chromium download (~150MB saved)
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install -g md2pdf-th

# Point to system Chrome
export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
md2pdf-th doc.md

| Platform | System Chrome Path | |----------|-------------------| | Linux (Debian/Ubuntu) | /usr/bin/chromium or /usr/bin/chromium-browser | | macOS | /Applications/Google Chrome.app/Contents/MacOS/Google Chrome | | Windows | C:\Program Files\Google\Chrome\Application\chrome.exe |

Docker (no install needed)

# Build once, reuse
# docker build -t md2pdf-th .
docker run --rm -v $(pwd):/data md2pdf-th /data/doc.md /data/output.pdf

CI environments

# CI environments need --no-sandbox (auto-detected via CI=true env)
CI=true md2pdf-th doc.md

Why md2pdf-th?

The problem

Converting Markdown to PDF with proper Thai language support is painful — most tools break Thai line wrapping, use wrong fonts, or lack professional formatting options. Developers end up fighting with LaTeX or building custom Puppeteer scripts.

The solution

md2pdf-th is a zero-config Markdown to PDF converter that handles Thai and English content out of the box:

  • 🇹🇭 Thai-First Design — Auto-detect Thai content ratio, smart font fallback (Leelawadee → Noto Sans Thai → Tahoma)
  • 🎨 Professional Output — Dark theme, 3 built-in templates (resume, report, invoice), custom fonts
  • 📑 Document Features — TOC, cover page from frontmatter, header/footer, page numbers, watermark
  • ⚡ Developer Experience — CLI, library API, NestJS module, watch mode, web preview, batch conversion
  • 🔒 Security — HTML sanitization (XSS), path traversal protection, auth token for web server
  • 🐳 CI/CD Ready — Docker image, GitHub Action, TypeScript types

CLI reference

md2pdf-th <file.md> [output.pdf]
md2pdf-th <file1.md> <file2.md> ...          # batch convert
md2pdf-th [options] <file.md>

Options

| Option | Description | |--------|-------------| | --css <path> | Custom CSS file path | | --outdir, -o <dir> | Output directory | | --no-page-numbers | Disable page numbers | | --theme <light\|dark> | Color theme (default: light) | | --toc | Generate Table of Contents | | --watch | Watch mode — reconvert on file change | | --merge | Merge multiple PDFs into one | | --cover | Add cover page from frontmatter | | --header <text> | Custom header text | | --footer <text> | Custom footer text | | --format <size> | Page size: A3, A4, A5, Letter, Legal, Tabloid | | --font <name> | Custom font family | | --template <name> | Built-in template: resume, report, invoice | | --watermark <text> | Diagonal watermark text | | --output-filename <pattern> | Output filename pattern: {name}, {date}, {time}, {timestamp} | | --concurrency <n> | Batch concurrency limit 1-32 (default: 4) | | --serve | Start web preview server | | --port <port> | Server port (default: 3000) | | --html-only | Export HTML instead of PDF (lightweight, no Puppeteer) | | --timeout <ms> | Conversion timeout in milliseconds (default: 60000) | | --version, -v | Show version | | --help, -h | Show help |

Examples

# Single file
md2pdf-th doc.md

# Dark theme
md2pdf-th --theme dark doc.md

# TOC + cover page
md2pdf-th --toc --cover doc.md

# Custom header/footer
md2pdf-th --header "Company" --footer "Confidential" doc.md

# Letter format with Georgia font
md2pdf-th --format Letter --font Georgia doc.md

# Merge multiple files
md2pdf-th --merge doc1.md doc2.md doc3.md

# Watch mode
md2pdf-th --watch doc.md

# Web preview
md2pdf-th --serve --port 8080 doc.md

# Resume template
md2pdf-th --template resume resume.md

# Report with watermark
md2pdf-th --template report --watermark "DRAFT" report.md

# Custom output filename pattern
md2pdf-th --output-filename "{name}-{date}" -o ./pdfs *.md

# Batch convert
md2pdf-th -o ./pdfs *.md

Library API

Use as a Node.js library — returns PDF Buffer:

const { md2pdfTh } = require('md2pdf-th');

// From markdown string → Buffer
const pdfBuffer = await md2pdfTh({ content: '# สวัสดี' });

// From file → Buffer
const pdfBuffer = await md2pdfTh({ inputPath: 'doc.md' });

// From file → save to disk
await md2pdfTh({ inputPath: 'doc.md', outputPath: 'out.pdf' });

// With options
const pdfBuffer = await md2pdfTh({
  content: '# Hello',
  theme: 'dark',
  toc: true,
  cover: true,
  headerText: 'My Company',
  footerText: 'Confidential',
  format: 'Letter',
  font: 'Georgia',
  lang: 'th',
  template: 'report',
  watermark: 'DRAFT',
});

Frontmatter (for --cover & options)

---
title: My Document Title
author: John Doe
date: 2024-01-01
tags: report, quarterly
description: Quarterly report
theme: dark
toc: true
cover: true
format: A4
template: report
watermark: DRAFT
headerText: Company Name
footerText: Confidential
---

# Content starts here

All frontmatter options: title, author, date, tags, description, theme, toc, cover, format, template, watermark, headerText, footerText, noPageNumbers, font, lang

Custom CSS

Default stylesheet is bundled. Override with --css or css option:

md2pdf-th --css my-style.css doc.md
await md2pdfTh({ content: '# Hello', css: 'h1 { color: red; }' });

NestJS integration

import { Md2PdfModule, Md2PdfService } from 'md2pdf-th/nestjs';

@Module({
  imports: [Md2PdfModule.forRoot({ theme: 'dark' })],
})
class AppModule {}

@Injectable()
class ReportService {
  constructor(private md2pdf: Md2PdfService) {}

  async generateReport() {
    const pdf = await this.md2pdf.convertFromContent('# Report');
    return pdf; // Buffer
  }
}
Md2PdfModule.forRootAsync({
  useFactory: (config: ConfigService) => config.get('md2pdf'),
  inject: [ConfigService],
})

API reference

md2pdfTh(options)Promise<Buffer>

| Option | Type | Default | Description | |--------|------|---------|-------------| | content | string | — | Markdown content string (alternative to inputPath) | | inputPath | string | — | Path to .md file (alternative to content) | | outputPath | string | — | Output PDF path. If omitted, returns Buffer | | css | string | — | Custom CSS string | | cssPath | string | — | Path to custom CSS file | | theme | 'light'\|'dark' | 'light' | Color theme | | toc | boolean | false | Generate Table of Contents | | cover | boolean | false | Add cover page from frontmatter | | headerText | string | — | Custom header text | | footerText | string | — | Custom footer text | | format | string | 'A4' | Page size: A3, A4, A5, Letter, Legal, Tabloid | | font | string | — | Custom font family | | noPageNumbers | boolean | false | Disable page numbers | | lang | 'th'\|'en' | 'th' | Language hint for font selection | | template | string | — | Built-in template: resume, report, invoice | | watermark | string | — | Diagonal watermark text | | outputFilename | string | — | Output filename pattern ({name}, {date}, {time}, {timestamp}) |

mergePdfBuffers(buffers)Promise<Buffer>

Merge multiple PDF buffers into one.

sanitizeHtml(content)string

Strip dangerous HTML (script, iframe, SVG, event handlers, etc.).

parseFrontmatter(content)FrontmatterMeta

Parse YAML frontmatter. Returns { title, author, date, tags, description, theme, toc, cover, format, ... }.


Docker

# Build
docker build -t md2pdf-th .

# Convert
docker run --rm -v $(pwd):/data md2pdf-th /data/doc.md /data/output.pdf

# With options
docker run --rm -v $(pwd):/data md2pdf-th --template report --toc /data/doc.md

GitHub Action

Use in your CI pipeline:

- uses: teeprakorn1/md2pdf-th@v4
  with:
    markdown-file: report.md
    output-file: report.pdf
    theme: dark
    format: A4
    toc: true
    template: report
    watermark: DRAFT
- run: npm install -g md2pdf-th
- run: md2pdf-th --toc --cover report.md

Project structure

md2pdf-th/
├── md2pdf.js                    # CLI entry point
├── lib/
│   ├── md2pdf-core.js           # Core engine (sanitize, TOC, cover, watermark, metadata)
│   └── nestjs/
│       ├── md2pdf.module.js     # NestJS module (forRoot, forRootAsync)
│       ├── md2pdf.service.js    # NestJS service wrapper
│       └── index.js             # NestJS entry point
├── types/
│   ├── index.d.ts               # TypeScript definitions (core)
│   └── nestjs.d.ts              # TypeScript definitions (NestJS)
├── templates/
│   ├── resume.css               # Resume template
│   ├── report.css               # Report template
│   └── invoice.css              # Invoice template
├── style.css                    # Light theme (CSS vars only)
├── style-dark.css               # Dark theme (CSS vars only)
├── style-base.css               # Shared layout rules
├── web-ui.html                  # Web UI (drag & drop)
├── md/                          # Example markdown files
├── test/
│   ├── unit.test.js             # Unit tests (55 tests)
│   ├── nestjs.test.js           # NestJS module/service tests (8 tests)
│   └── md2pdf.test.js           # Integration tests (19 tests)
├── action.yml                   # GitHub Action definition
├── Dockerfile                   # Docker with Chromium + Thai fonts
└── package.json                 # npm package config

Requirements

  • Node.js >= 16.0.0
  • Chromium (auto-installed by Puppeteer via md-to-pdf)

Documentation

  • CHANGELOG.md — Version history and bug fixes
  • USAGE.md — Extended usage guide, environment variables, troubleshooting
  • CODEBASE.md — Architecture, file map, module responsibilities
  • DEPLOY.md — Deployment guide (npm, Docker, CI/CD, NestJS)

Contributing

See CONTRIBUTING.md for guidelines.

Security

See SECURITY.md for vulnerability reporting.

Code of conduct

See CODE_OF_CONDUCT.md.

License

Apache-2.0 © Teepakorn Kumvong