md-to-pdf-renderer
v0.2.6
Published
CLI to render Markdown files to print-ready PDF with optional HTML output, Mermaid, KaTeX, TOC, footnotes, and GitHub-style callouts
Downloads
81
Maintainers
Readme
md-to-pdf-renderer
md-to-pdf-renderer is a standalone Node.js tool that converts Markdown documents into print-ready PDF files, with optional HTML output.
It is designed for documentation export workflows where the same Markdown source should be rendered consistently with:
- A4-oriented PDF layout
- Mermaid diagram rendering
- Syntax-highlighted code blocks and plain-text blocks
- Tables, blockquotes, and general document formatting
- Task lists, footnotes, and GitHub-style callouts
[[TOC]]placeholder based table of contents- Inline and block math rendering with KaTeX
- Optional manifest generation for produced PDFs
Quickstart For Humans And Agents
Quickstart for humans and agents:
npx md-to-pdf-rendererThis will render all of the markdown files in the current directory and write the output to the current directory.
Fastest successful path:
npx md-to-pdf-renderer --input fixtures/readme-showcase --output out --log-fileExpected output:
out/rendering-showcase.pdfout/render.log
Tool contract:
- Input is the top-level
*.mdfiles in the--inputdirectory. - Output is
*.pdfin--output, or in the current working directory when--outputis omitted. - A manifest
README.mdis only written when--manifestis provided. - Intermediate HTML files are only written when
--html <dir>is provided. - For automation, prefer passing both
--inputand--outputexplicitly instead of relying on defaults. - The command exits with a non-zero status when the input directory is missing, empty, or when Mermaid rendering fails.
- On Linux ARM boards, prefer a system Chromium or Chrome path via
--chrome-pathorPUPPETEER_EXECUTABLE_PATH.
Preview
Example source: fixtures/readme-showcase/rendering-showcase.md
Generated output:
fixtures/readme-showcase-output/rendering-showcase.pdffixtures/readme-showcase-output/html/rendering-showcase.html
The preview HTML above was generated with --html fixtures/readme-showcase-output/html.
Preview images from the latest showcase render:
What it does
When you run the renderer:
- It scans the input directory for top-level
*.mdfiles. - It converts each Markdown file into rendered HTML in memory.
- It opens that HTML in a bundled Puppeteer-managed browser.
- It renders Mermaid blocks before printing.
- It writes the final PDF files.
- It optionally writes intermediate HTML files when
--html <dir>is set. - It optionally creates a
README.mdmanifest inside the PDF output directory when--manifestis set.
Requirements
- Node.js
npm installfor this tool directory- No separate Chrome installation is required by default
- On Apple Silicon Macs, use an arm64 build of Node.js. If Puppeteer warns about degraded performance,
node -p "process.arch"should printarm64. - Network access for Mermaid ESM loading from jsDelivr at render time
- Linux ARM boards may need a system Chromium or Chrome executable instead of Puppeteer's bundled browser
Dependencies
markdown-itmarkdown-it-footnotemarkdown-it-task-listshighlight.jskatexmermaidpuppeteer
Install
From the tool directory:
npm installUsage
Quick start with npx:
npx md-to-pdf-rendererThis writes output files into the current working directory by default.
Single file:
npx md-to-pdf-renderer --input docs/guide.mdSingle file with a custom PDF name:
npx md-to-pdf-renderer --input docs/guide.md --output-file guide-v2.pdfSingle file with a custom output directory:
npx md-to-pdf-renderer --input docs/guide.md --output outputWrite a manifest too:
npx md-to-pdf-renderer --input input --output output --manifestShow CLI help:
npx md-to-pdf-renderer --helpOverride the built-in CSS:
npx md-to-pdf-renderer --input input --output output --css styles/print.cssnpx md-to-pdf-renderer --input input --output output --paper-size A4 --orientation portrait --log-fileSave intermediate HTML too:
npx md-to-pdf-renderer --input input --output output --html output/htmlLinux ARM example:
npx md-to-pdf-renderer --input input --output output --chrome-path /usr/bin/chromiummacOS Chrome example:
npx md-to-pdf-renderer --input input --output output --chrome-path "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"Run from the repository root:
node src/render-pdfs.mjs --input input --output output --paper-size A4 --orientation portraitCLI options
| Option | Description | Default |
| --------------- | ------------------------------------------------------------------------ | ---------------------------- |
| --input | Directory or Markdown file to render | Current working directory |
| --output | Directory where PDF files are written | Current working directory |
| --output-file | PDF file name for single-file input only | Source file name with .pdf |
| --html | Also write intermediate HTML files to this directory | Disabled |
| --manifest | Also write <output>/README.md manifest | Disabled |
| --css | Use an existing CSS file path or inline CSS text, appended after the built-in styles | Disabled |
| --paper-size | Print paper size such as A4, Letter, Legal, A3, or 210mm 297mm | A4 |
| --orientation | Print orientation: portrait or landscape | portrait |
| --font-size | Overall font size preset: xs, s, m, l, lg, or xl | m |
| --log-file | Write progress logs to <output>/render.log | Disabled |
| --chrome-path | Optional path to a custom Chrome or Chromium executable | Bundled Puppeteer browser |
Apple Silicon macOS performance note
If Puppeteer prints a degraded-performance warning on an Apple Silicon Mac, the usual cause is running an x64 build of Node.js under Rosetta. That makes Chrome run through Rosetta too, which can slow PDF rendering down significantly.
Use an arm64 build of Node.js and confirm with:
node -p "process.arch"It should print arm64.
Output structure
The tool generates:
<output>/*.pdf<output>/README.mdonly when--manifestis enabled<output>/render.logwhen--log-fileis enabled<html>/*.htmlonly when--html <dir>is enabled
Example:
input/
01-overview.md
02-architecture.md
output/
01-overview.pdf
02-architecture.pdf
render.log
output/html/
01-overview.html
02-architecture.htmlProgrammatic API
Minimal examples with only the essential options:
import {
renderHtmlToPdf,
renderMarkdownFileToPdf,
renderMarkdownStringToPdf,
renderMarkdownToHtml,
} from 'md-to-pdf-renderer';
const html = await renderMarkdownToHtml({
markdown: '# Hello\n\n[[TOC]]',
});
const pdf = await renderHtmlToPdf({
html,
});
const stringResult = await renderMarkdownStringToPdf({
markdown: '# In Memory\n\nHello from a variable.',
});
const fileResult = await renderMarkdownFileToPdf({
inputFile: 'docs/guide.md',
});
console.log(pdf); // Uint8Array
console.log(stringResult.file.pdf); // Uint8Array
console.log(fileResult.file.pdf); // Uint8ArrayAvailable exports:
renderHtmlToPdf(options)renders HTML to PDF bytes without writing files.renderMarkdownFileToPdf(options)renders one Markdown file to PDF bytes without writing files.renderMarkdownStringToPdf(options)renders Markdown content from a string to PDF bytes without writing files.renderMarkdownToHtml(options)renders a single Markdown string to HTML without writing files.
The library API is intentionally memory-oriented. If you want files on disk, use the CLI.
The PDF-producing APIs return an object, not raw bytes directly. The actual PDF binary is in result.file.pdf.
Example:
const result = await renderMarkdownStringToPdf({
markdown: '# Hello\n\nRendered in memory.',
fileName: 'hello.md',
});
console.log(result.fileName); // 'hello.md'
console.log(result.file.title); // 'Hello'
console.log(result.file.pdfName); // 'hello.pdf'
console.log(result.file.html); // rendered HTML string
console.log(result.file.pdf); // Uint8ArrayIf you only need the PDF bytes, destructure them:
const {
file: { pdf },
} = await renderMarkdownStringToPdf({
markdown: '# Hello',
});
console.log(pdf); // Uint8ArrayIf your source data starts as an object instead of a Markdown string, convert it to Markdown first and then read the PDF bytes from result.file.pdf:
import { renderMarkdownStringToPdf } from 'md-to-pdf-renderer';
const report = {
title: 'Weekly Report',
summary: 'Build is stable and release prep has started.',
items: [
'Completed PDF renderer refactor',
'Added memory-based API',
'Updated CLI defaults',
],
};
function reportToMarkdown(data) {
return [
`# ${data.title}`,
'',
data.summary,
'',
'## Highlights',
'',
...data.items.map((item) => `- ${item}`),
].join('\n');
}
const result = await renderMarkdownStringToPdf({
markdown: reportToMarkdown(report),
fileName: 'weekly-report.md',
outputFileName: 'weekly-report.pdf',
cssPath: './styles/print.css',
});
console.log(result.file.pdf); // Uint8ArrayrenderMarkdownFileToPdf(options) options:
| Field | Type | Default | Description |
| ------------------------------- | --------------- | ---------------------------- | ------------------------------------------------------------------ |
| cwd | string | process.cwd() | Base path used to resolve relative options |
| inputFile / input | string | Required | Markdown file to render |
| outputFileName / outputFile | string | Source file name with .pdf | Custom PDF file name for returned metadata |
| cssPath / css | string | Disabled | Existing CSS file path or inline CSS text appended after the built-in styles |
| paperSize | string | A4 | Paper size such as A4, Letter, Legal, A3, or 210mm 297mm |
| orientation | string | portrait | Page orientation: portrait or landscape |
| fontSizePreset / fontSize | string | m | Overall font size preset: xs, s, m, l, lg, or xl |
| chromePath | string | null | Auto-detect | Custom Chrome or Chromium executable |
renderMarkdownFileToPdf() return shape:
| Field | Type | Description |
| ----------------- | ------------ | -------------------------------------- |
| inputFile | string | Absolute input file path |
| file.title | string | Resolved document title |
| file.fileName | string | Source Markdown file name |
| file.pdfName | string | Output PDF file name used in metadata |
| file.pdf | Uint8Array | PDF binary data |
| file.html | string | Rendered HTML used to generate the PDF |
| file.sourcePath | string | Absolute source Markdown file path |
renderMarkdownStringToPdf(options) options:
| Field | Type | Default | Description |
| ------------------------------- | --------------- | ------------------------------- | ------------------------------------------------------------------ |
| markdown | string | Required | Markdown source to render from memory |
| title | string | First # Heading or Document | HTML document title |
| fileName / name | string | document.md | Virtual Markdown file name used for output naming |
| cwd | string | process.cwd() | Base path used to resolve relative options |
| baseDir / inputDir | string | . | Base directory for relative asset links |
| baseHref | string | Derived from baseDir | Explicit <base href> value |
| outputFileName / outputFile | string | Virtual file name with .pdf | Custom PDF file name for returned metadata |
| cssPath / css | string | Disabled | Existing CSS file path or inline CSS text appended after the built-in styles |
| paperSize | string | A4 | Paper size such as A4, Letter, Legal, A3, or 210mm 297mm |
| orientation | string | portrait | Page orientation: portrait or landscape |
| fontSizePreset / fontSize | string | m | Overall font size preset: xs, s, m, l, lg, or xl |
| chromePath | string | null | Auto-detect | Custom Chrome or Chromium executable |
renderMarkdownStringToPdf() return shape:
| Field | Type | Description |
| ----------------- | ------------ | --------------------------------------------------------- |
| fileName | string | Virtual Markdown file name |
| file.title | string | Resolved document title |
| file.fileName | string | Virtual Markdown file name again inside the file metadata |
| file.pdfName | string | Output PDF file name used in metadata |
| file.pdf | Uint8Array | PDF binary data |
| file.html | string | Rendered HTML used to generate the PDF |
| file.sourcePath | null | Always null for in-memory Markdown |
renderHtmlToPdf(options) options:
| Field | Type | Default | Description |
| --------------- | --------------- | --------------- | ------------------------------------ |
| html | string | Required | HTML document to render |
| documentLabel | string | document.html | Label used in render errors |
| chromePath | string | null | Auto-detect | Custom Chrome or Chromium executable |
renderMarkdownToHtml(options) options:
| Field | Type | Default | Description |
| ---------------------- | -------- | ------------------------------- | --------------------------------------------------- |
| markdown | string | Required | Markdown source to render |
| title | string | First # Heading or Document | HTML document title |
| cwd | string | process.cwd() | Base path used to resolve relative options |
| baseDir / inputDir | string | . | Base directory for relative asset links |
| baseHref | string | Derived from baseDir | Explicit <base href> value |
| cssPath / css | string | Disabled | Existing CSS file path or inline CSS text appended after the built-in styles |
| paperSize | string | A4 | Paper size such as A4, Letter, or 210mm 297mm |
| orientation | string | portrait | Page orientation: portrait or landscape |
| fontSizePreset / fontSize | string | m | Overall font size preset: xs, s, m, l, lg, or xl |
Rendering notes
- The title of each document is taken from the first Markdown
# Headingwhen available. - If no top-level heading exists, the file name is converted into a readable title.
- Mermaid fences using ````mermaid` are rendered as diagrams.
- Standard fenced code blocks are syntax highlighted when the language is known, with automatic highlighting as a fallback.
- Code fences using ````text` are rendered with a plain text oriented layout.
- Task lists using
- [x]and- [ ]are rendered with checkbox styling. - Footnotes using
[^name]syntax are rendered at the end of the document. - GitHub-style callouts such as
> [!NOTE]and> [!WARNING]are rendered as callout cards. [[TOC]]is replaced with a generated table of contents linking to document headings.- Inline math using
$...$and block math using$$...$$are rendered with KaTeX. - The generated PDFs use print CSS and support
--paper-sizeplus--orientation. --font-size,fontSizePreset, andfontSizechange the overall typography scale, including Mermaid diagram text.--css,cssPath, andcssaccept either an existing CSS file path or inline CSS text for overriding the built-in styles.- Render progress is always printed to the console.
- Intermediate HTML files are skipped by default and are only persisted when
--html <dir>is passed. <output>/README.mdis only written when--manifestis enabled.<output>/render.logis only written when--log-fileis enabled.- Mermaid rendering errors fail the command instead of silently producing a broken diagram in the PDF.
- Missing, empty, or invalid input directories fail with a clear error message.
- On Linux ARM, the bundled Puppeteer browser may be unusable, so pass
--chrome-pathor setPUPPETEER_EXECUTABLE_PATH.
License
MIT
