Rendering Markdown PDF
Overview
Markdown to PDF with Mermaid is a visual export task, not a plain file conversion. Render diagrams, style them consistently, disable print headers, then verify the PDF pages visually.
When to Use
Use for:
- Markdown docs containing Mermaid diagrams
- PDF export where diagrams are blank, oversized, clipped, or colorless
- Chrome/Puppeteer exports with unwanted page headers/footers
- report/document exports where tables and diagrams must stay readable
Do not use for plain text Markdown without diagrams.
Core Workflow
Preflight
- Count Mermaid blocks in source.
- Create a temp build dir.
- Never edit source Markdown.
Render Mermaid explicitly
- Use
mmdc or browser-side Mermaid.
- Apply a theme config with colors, font, and white background.
- Set Mermaid
flowchart.htmlLabels=false for PDF exports. Chrome PDF can drop foreignObject labels, causing blank boxes.
- Wrap each diagram in a controlled container.
Build HTML with print CSS
- Set
@page { margin: 0.55in; }.
- Add a designed Table of Contents near the top for long documents. Generate it from headings in the temporary build, not by editing source Markdown. Links are preferred when supported.
- Add PDF outline/bookmarks for Preview/sidebar navigation. This is separate from visible TOC content. Use document headings, usually title + major sections.
- Do not leave the TOC as raw Markdown bullets with awkward hyperlink styling. Render it as a polished
nav.toc, ordered hierarchy, or table-style list with clean spacing, muted section numbers, and normal link text.
- Use readable body width and table styles.
- Start major sections on new pages. For docs where
## are major sections, use h2 { break-before: page; } and exempt the first major section if needed. For true H1 sectioned docs, use h1 { break-before: page; } except the document title.
- Set diagram CSS:
max-width: 100%
height: auto
max-height: 7in
page-break-inside: avoid
Print with headers disabled
- Prefer Puppeteer/CDP
Page.printToPDF with displayHeaderFooter: false.
- Do not rely on Chrome CLI defaults.
Verify
- Confirm output PDF exists and has pages.
- Rasterize PDF pages with
pdftoppm or inspect screenshots.
- Check diagram pages for blank placeholders.
- Verify rendered diagram count equals Mermaid block count.
- Check no page URL/date/title headers or footers exist.
Quick Reference
| Symptom |
Fix |
| Blank Mermaid block |
Render with mmdc; verify SVG non-empty before PDF |
| Blank Mermaid node labels |
Set flowchart.htmlLabels=false; avoid SVG foreignObject labels |
| Diagram too large |
Strip fixed SVG dimensions; CSS max width/height |
| Mermaid colors gone |
Use explicit Mermaid theme config and white background |
| Headers/footers on every page |
Use Puppeteer displayHeaderFooter:false |
| Tables overflow |
CSS table-layout:auto, smaller font, horizontal-safe widths |
| Major sections run together |
Add print CSS page breaks on h1/h2, excluding title |
| Long doc hard to navigate |
Generate TOC from headings in temp HTML/PDF build |
| Preview sidebar index missing |
Add PDF outline/bookmarks from headings; visible TOC alone is not enough |
| TOC looks like bullet links |
Replace raw ul bullets with styled nav.toc, ordered hierarchy, or table-style rows |
| Source got changed |
Stop. Restore source. Use temp build artifacts only |
Navigation Requirements
Long PDFs need two navigation layers:
- Visible TOC page/section inside PDF content.
- PDF outline/bookmarks visible in Preview, Acrobat, browser sidebars, and other PDF readers.
Do not treat these as interchangeable. A clickable TOC page does not create Preview sidebar entries.
Visible TOC Style
For long PDFs, add a Table of Contents that looks intentional:
- Place TOC after the document title and any short prefatory note.
- Use heading text from source, but generate TOC in temp HTML/build artifacts only.
- Prefer a
nav.toc block with title Table of Contents.
- Avoid default Markdown bullets. If using
ul/ol, set list-style: none and create visual hierarchy with spacing/indentation.
- Avoid ugly blue underlined links. Use document text color, subtle hover/print styling, and no text decoration in print.
- Make each entry feel like one clean row: section number or level marker, title, optional page/anchor affordance.
- Include top-level sections by default. Include second-level sections only when document is short enough or nested indentation stays readable.
- Keep TOC compact: no more than two pages unless user asks for exhaustive navigation.
Example CSS pattern:
.toc {
margin: 1.5rem 0 2rem;
padding: 1rem 1.25rem;
border: 1px solid #d8e0ee;
border-radius: 12px;
background: #f8fbff;
}
.toc h2 {
margin: 0 0 0.75rem;
break-before: auto;
}
.toc ol {
list-style: none;
margin: 0;
padding: 0;
}
.toc li {
margin: 0.35rem 0;
line-height: 1.35;
}
.toc a {
color: #162033;
text-decoration: none;
}
.toc .toc-level-2 {
margin-left: 1rem;
color: #526173;
font-size: 0.95em;
}
PDF Outline / Bookmark Requirements
Add document outline entries from headings:
- Include document title.
- Include major sections by default: usually
h1 title and h2 sections, or h1 sections when a doc uses multiple top-level headings.
- Include
h3 only when nesting stays readable.
- Keep labels clean: remove Markdown numbering artifacts if duplicated, trim whitespace, decode HTML entities.
- Bookmarks must jump to correct section pages, not just page 1.
- Prefer PDF generators that emit outlines natively. If using Puppeteer/Chrome and it does not emit outlines, postprocess the PDF with a PDF library that can add outlines/bookmarks.
- If outline creation is unsupported by the available toolchain, say so explicitly in the output report instead of implying the TOC covers it.
Validation options:
# If mutool is available
mutool show output.pdf outline
# If qpdf is available, inspect JSON for outline data
qpdf --json output.pdf | rg -i 'outline|bookmark|title'
Validation Commands
# Count diagrams in Markdown
rg -n '^```mermaid' input.md
# Check PDF page count
pdfinfo output.pdf | rg '^Pages:'
# Rasterize for visual inspection
mkdir -p /tmp/pdf-pages
pdftoppm -png -r 120 output.pdf /tmp/pdf-pages/page
open /tmp/pdf-pages
Recommended Toolchain
Preferred:
Markdown -> temp Markdown/HTML -> Mermaid SVG -> styled HTML -> Puppeteer PDF
Use:
mmdc for Mermaid SVGs
pandoc or Python Markdown for HTML
- Puppeteer/Chrome CDP for PDF
pdfinfo and pdftoppm for verification
Common Mistakes
- Assuming successful PDF creation means diagrams rendered.
- Trusting Chrome CLI
--print-to-pdf without checking headers/footers.
- Letting SVG intrinsic dimensions control PDF layout.
- Leaving Mermaid source blocks after failed rendering.
- Allowing Mermaid
foreignObject HTML labels in PDF exports. They may show in browser but print as blank boxes.
- Verifying only the first page.
- Forgetting that many Markdown docs use
## as major sections after one document # title.
- Leaving auto-generated TOC as raw bullet points with blue underlined links.
- Assuming a visible TOC creates PDF bookmarks. Preview sidebar index requires PDF outline entries.
- Editing the source Markdown to make PDF conversion easier.
Required Output Report
After conversion, report:
- Output path
- File size
- Page count
- Mermaid block count vs rendered SVG count
- Whether headers/footers are absent
- Whether any diagram pages were visually checked
- Whether major sections start on new pages
- Whether TOC was generated
- Whether PDF outline/bookmarks were generated and how verified
- Toolchain used
1---2name: markdown-to-pdf3description: Use when exporting Markdown reports to PDF, especially when Mermaid diagrams render blank, oversized, lose color, tables overflow, a TOC or PDF outline is needed, or Chrome/Puppeteer adds unwanted page headers and footers.4license: Apache-2.05---67# Rendering Markdown PDF89## Overview1011Markdown to PDF with Mermaid is a visual export task, not a plain file conversion. Render diagrams, style them consistently, disable print headers, then verify the PDF pages visually.1213## When to Use1415Use for:16- Markdown docs containing Mermaid diagrams17- PDF export where diagrams are blank, oversized, clipped, or colorless18- Chrome/Puppeteer exports with unwanted page headers/footers19- report/document exports where tables and diagrams must stay readable2021Do not use for plain text Markdown without diagrams.2223## Core Workflow24251. **Preflight**26 - Count Mermaid blocks in source.27 - Create a temp build dir.28 - Never edit source Markdown.29302. **Render Mermaid explicitly**31 - Use `mmdc` or browser-side Mermaid.32 - Apply a theme config with colors, font, and white background.33 - Set Mermaid `flowchart.htmlLabels=false` for PDF exports. Chrome PDF can drop `foreignObject` labels, causing blank boxes.34 - Wrap each diagram in a controlled container.35363. **Build HTML with print CSS**37 - Set `@page { margin: 0.55in; }`.38 - Add a designed Table of Contents near the top for long documents. Generate it from headings in the temporary build, not by editing source Markdown. Links are preferred when supported.39 - Add PDF outline/bookmarks for Preview/sidebar navigation. This is separate from visible TOC content. Use document headings, usually title + major sections.40 - Do not leave the TOC as raw Markdown bullets with awkward hyperlink styling. Render it as a polished `nav.toc`, ordered hierarchy, or table-style list with clean spacing, muted section numbers, and normal link text.41 - Use readable body width and table styles.42 - Start major sections on new pages. For docs where `##` are major sections, use `h2 { break-before: page; }` and exempt the first major section if needed. For true H1 sectioned docs, use `h1 { break-before: page; }` except the document title.43 - Set diagram CSS:44 - `max-width: 100%`45 - `height: auto`46 - `max-height: 7in`47 - `page-break-inside: avoid`48494. **Print with headers disabled**50 - Prefer Puppeteer/CDP `Page.printToPDF` with `displayHeaderFooter: false`.51 - Do not rely on Chrome CLI defaults.52535. **Verify**54 - Confirm output PDF exists and has pages.55 - Rasterize PDF pages with `pdftoppm` or inspect screenshots.56 - Check diagram pages for blank placeholders.57 - Verify rendered diagram count equals Mermaid block count.58 - Check no page URL/date/title headers or footers exist.5960## Quick Reference6162| Symptom | Fix |63|---|---|64| Blank Mermaid block | Render with `mmdc`; verify SVG non-empty before PDF |65| Blank Mermaid node labels | Set `flowchart.htmlLabels=false`; avoid SVG `foreignObject` labels |66| Diagram too large | Strip fixed SVG dimensions; CSS max width/height |67| Mermaid colors gone | Use explicit Mermaid theme config and white background |68| Headers/footers on every page | Use Puppeteer `displayHeaderFooter:false` |69| Tables overflow | CSS `table-layout:auto`, smaller font, horizontal-safe widths |70| Major sections run together | Add print CSS page breaks on `h1`/`h2`, excluding title |71| Long doc hard to navigate | Generate TOC from headings in temp HTML/PDF build |72| Preview sidebar index missing | Add PDF outline/bookmarks from headings; visible TOC alone is not enough |73| TOC looks like bullet links | Replace raw `ul` bullets with styled `nav.toc`, ordered hierarchy, or table-style rows |74| Source got changed | Stop. Restore source. Use temp build artifacts only |7576## Navigation Requirements7778Long PDFs need two navigation layers:79801. **Visible TOC page/section** inside PDF content.812. **PDF outline/bookmarks** visible in Preview, Acrobat, browser sidebars, and other PDF readers.8283Do not treat these as interchangeable. A clickable TOC page does not create Preview sidebar entries.8485### Visible TOC Style8687For long PDFs, add a Table of Contents that looks intentional:8889- Place TOC after the document title and any short prefatory note.90- Use heading text from source, but generate TOC in temp HTML/build artifacts only.91- Prefer a `nav.toc` block with title `Table of Contents`.92- Avoid default Markdown bullets. If using `ul`/`ol`, set `list-style: none` and create visual hierarchy with spacing/indentation.93- Avoid ugly blue underlined links. Use document text color, subtle hover/print styling, and no text decoration in print.94- Make each entry feel like one clean row: section number or level marker, title, optional page/anchor affordance.95- Include top-level sections by default. Include second-level sections only when document is short enough or nested indentation stays readable.96- Keep TOC compact: no more than two pages unless user asks for exhaustive navigation.9798Example CSS pattern:99100```css101.toc {102 margin: 1.5rem 0 2rem;103 padding: 1rem 1.25rem;104 border: 1px solid #d8e0ee;105 border-radius: 12px;106 background: #f8fbff;107}108.toc h2 {109 margin: 0 0 0.75rem;110 break-before: auto;111}112.toc ol {113 list-style: none;114 margin: 0;115 padding: 0;116}117.toc li {118 margin: 0.35rem 0;119 line-height: 1.35;120}121.toc a {122 color: #162033;123 text-decoration: none;124}125.toc .toc-level-2 {126 margin-left: 1rem;127 color: #526173;128 font-size: 0.95em;129}130```131132### PDF Outline / Bookmark Requirements133134Add document outline entries from headings:135136- Include document title.137- Include major sections by default: usually `h1` title and `h2` sections, or `h1` sections when a doc uses multiple top-level headings.138- Include `h3` only when nesting stays readable.139- Keep labels clean: remove Markdown numbering artifacts if duplicated, trim whitespace, decode HTML entities.140- Bookmarks must jump to correct section pages, not just page 1.141- Prefer PDF generators that emit outlines natively. If using Puppeteer/Chrome and it does not emit outlines, postprocess the PDF with a PDF library that can add outlines/bookmarks.142- If outline creation is unsupported by the available toolchain, say so explicitly in the output report instead of implying the TOC covers it.143144Validation options:145146```bash147# If mutool is available148mutool show output.pdf outline149150# If qpdf is available, inspect JSON for outline data151qpdf --json output.pdf | rg -i 'outline|bookmark|title'152```153154## Validation Commands155156```bash157# Count diagrams in Markdown158rg -n '^```mermaid' input.md159160# Check PDF page count161pdfinfo output.pdf | rg '^Pages:'162163# Rasterize for visual inspection164mkdir -p /tmp/pdf-pages165pdftoppm -png -r 120 output.pdf /tmp/pdf-pages/page166open /tmp/pdf-pages167```168169## Recommended Toolchain170171Preferred:172173```text174Markdown -> temp Markdown/HTML -> Mermaid SVG -> styled HTML -> Puppeteer PDF175```176177Use:178- `mmdc` for Mermaid SVGs179- `pandoc` or Python Markdown for HTML180- Puppeteer/Chrome CDP for PDF181- `pdfinfo` and `pdftoppm` for verification182183## Common Mistakes184185- Assuming successful PDF creation means diagrams rendered.186- Trusting Chrome CLI `--print-to-pdf` without checking headers/footers.187- Letting SVG intrinsic dimensions control PDF layout.188- Leaving Mermaid source blocks after failed rendering.189- Allowing Mermaid `foreignObject` HTML labels in PDF exports. They may show in browser but print as blank boxes.190- Verifying only the first page.191- Forgetting that many Markdown docs use `##` as major sections after one document `#` title.192- Leaving auto-generated TOC as raw bullet points with blue underlined links.193- Assuming a visible TOC creates PDF bookmarks. Preview sidebar index requires PDF outline entries.194- Editing the source Markdown to make PDF conversion easier.195196## Required Output Report197198After conversion, report:199200- Output path201- File size202- Page count203- Mermaid block count vs rendered SVG count204- Whether headers/footers are absent205- Whether any diagram pages were visually checked206- Whether major sections start on new pages207- Whether TOC was generated208- Whether PDF outline/bookmarks were generated and how verified209- Toolchain used