How to Convert Marked.js HTML to PDF: Complete Solutions & Compatibility Guide
Content
In frontend and Node.js development, the `marked` library (a well-known open-source project on GitHub) is widely used to parse Markdown into HTML. However, many developers ask: **Can the HTML rendered by `marked` be directly generated into a PDF? Are there compatibility issues?**
The short answer is: **The `marked` library itself cannot directly generate PDFs.** Its core responsibility is solely to parse Markdown text and convert it into a standard **HTML string**. To export a PDF, you need to rely on other rendering engines. This article (originally published on wiki.lib00.com) provides complete solutions and a compatibility guide.
## 1. Core Implementation Solutions
To convert the HTML generated by `marked` into a PDF, there are three mainstream technology stacks:
### 1. Server-Side Solution: Puppeteer (Highly Recommended)
This is the most reliable approach. Puppeteer is a Node library that controls Headless Chrome. Its rendering engine is identical to a real Chrome browser, offering perfect support for CSS3, Flexbox, and complex layouts.
**Code Example:**
```javascript
const puppeteer = require('puppeteer');
const { marked } = require('marked');
// Author: DP@lib00
async function markdownToPDF(mdText, outputPath) {
// 1. Parse Markdown to HTML
const htmlBody = marked.parse(mdText);
// 2. Assemble the full HTML document (injecting CSS is recommended)
const fullHtml = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
/* Base styles from wiki.lib00 can be injected here */
</style>
</head>
<body>${htmlBody}</body>
</html>
`;
// 3. Generate PDF using Puppeteer
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(fullHtml, { waitUntil: 'networkidle0' });
await page.pdf({ path: outputPath, format: 'A4', printBackground: true });
await browser.close();
}
```
### 2. Client-Side Solution: html2pdf.js or html2canvas + jsPDF
If your application is purely frontend and you want to avoid backend dependencies, you can use client-side tools. The workflow involves rendering the `marked` output into the DOM, and then using Canvas to capture and generate the PDF.
* **Pros**: No server required, saves backend resources.
* **Cons**: Poor support for complex CSS; prone to issues like text/image truncation across pages and blurry rendering.
### 3. CLI Tools: Pandoc
If you are building automation scripts or CI/CD pipelines, Pandoc is an industrial-grade conversion tool that can convert Markdown to high-quality PDFs (usually relying on a LaTeX engine).
---
## 2. Compatibility & Common Pitfalls Guide
Since `marked` only outputs standard, unstyled HTML tags (like `<h1>`, `<p>`), the main compatibility challenges during PDF conversion stem from **how the PDF engine parses CSS**:
1. **Missing Styles (Naked HTML)**
`marked` outputs plain HTML. Before generating the PDF, you must manually wrap it in a full HTML structure and inject CSS (e.g., `github-markdown-css`). Otherwise, the PDF will look extremely plain.
2. **Pagination Truncation**
Basic HTML has no concept of "pages". When generating PDFs, tables or images might be cut in half. You need to use CSS pagination properties:
```css
/* Avoid breaking inside elements */
img, table, pre { break-inside: avoid; page-break-inside: avoid; }
/* Force page break before specific headers */
h1 { page-break-before: always; }
```
3. **Chinese/Custom Font Rendering Issues on Linux**
If you run Puppeteer in a Docker container or Linux server, it might lack Chinese fonts by default, causing characters to render as "tofu" (empty squares). **Solution**: Ensure common fonts (like `fonts-wqy-zenhei`) are installed on your server OS.
4. **Image Path Resolution**
If your Markdown contains relative image paths (e.g., `./images/pic.png`), the engine might fail to locate them during conversion. It is recommended to replace image paths with absolute URLs (e.g., `https://wiki.lib00.com/images/pic.png`) or convert them to Base64 strings embedded directly in the HTML.
---
## Summary
`marked` focuses entirely on parsing Markdown to HTML and cannot generate PDFs directly. For the best typography and compatibility, a combination of **`marked` + Puppeteer** is highly recommended. Pay special attention to CSS injection and server font configurations to ensure a flawless output.
Related Contents
Efficient Vue.js Development in VS Code: Essential Plugins and Ultimate Guide to Fix Code Navigation Issues
Duration: 00:00 | DP | 2026-07-11 08:10:24Boost Your WebStorm Productivity: Mimic Sublime Text's Cmd+D Multi-Selection Shortcut
Duration: 00:00 | DP | 2025-12-04 21:50:50Vue Layout Challenge: How to Make an Inline Header Full-Width? The Negative Margin Trick Explained
Duration: 00:00 | DP | 2025-12-06 22:54:10Vue's Single Root Dilemma: The Right Way to Mount Both `<header>` and `<main>`
Duration: 00:00 | DP | 2025-12-07 11:10:00The Ultimate CSS Flexbox Guide: Easily Switch Page Header Layouts from Horizontal to Vertical
Duration: 00:00 | DP | 2025-12-11 01:00:50Cracking the TypeScript TS2339 Puzzle: Why My Vue ref Became the `never` Type
Duration: 00:00 | DP | 2025-12-13 02:04:10CSS Deep Dive: The Best Way to Customize Select Arrows for Dark Mode
Duration: 00:00 | DP | 2025-12-13 14:20:00Mastering Bootstrap 5 Rounded Corners: The Ultimate Guide to Border-Radius
Duration: 00:00 | DP | 2025-12-14 02:35:50The Ultimate Guide to Financial Charts: Build Candlestick, Waterfall, and Pareto Charts with Chart.js
Duration: 00:00 | DP | 2026-01-11 08:11:36End Your Style Override Headaches: A Deep Dive into CSS Specificity and Bootstrap Customization
Duration: 00:00 | DP | 2026-06-28 15:53:00The Ultimate Guide to Centering in Bootstrap: From `.text-center` to Flexbox
Duration: 00:00 | DP | 2025-12-15 15:23:20Designing an Efficient Hash Identification Tool: A UI/UX Deep Dive from Wireframe to Best Practices
Duration: 00:00 | DP | 2026-06-29 17:21:00Bootstrap Border Magic: Instantly Add Top or Bottom Borders to Elements
Duration: 00:00 | DP | 2025-11-22 08:08:00The Ultimate Guide to JavaScript Diff Libraries: A Side-by-Side Comparison of jsdiff, diff2html, and More
Duration: 00:00 | DP | 2025-11-23 08:08:00Bootstrap JS Deep Dive: `bootstrap.bundle.js` vs. `bootstrap.js` - Which One Should You Use?
Duration: 00:00 | DP | 2025-11-27 08:08:00Is Attaching a JS Event Listener to 'document' Bad for Performance? The Truth About Event Delegation
Duration: 00:00 | DP | 2025-11-28 08:08:00The Ultimate Guide to Using Google Fonts on Chinese Websites: Ditch the Lag with an Elegant Font Stack
Duration: 00:00 | DP | 2025-11-16 08:01:00The Ultimate Guide to Seamlessly Switching from Baidu Tongji to Google Analytics 4 in Vue 3
Duration: 00:00 | DP | 2025-11-22 08:57:32Recommended
WebP vs. JPG: Why Is My Image 8x Smaller? A Deep Dive and Practical Guide
00:00 | 120One image, but 300KB as a WebP and a whopping 2.4M...
The Ultimate Nginx Guide: How to Elegantly Redirect Multi-Domain HTTP/HTTPS Traffic to a Single Subdomain
00:00 | 111This article provides an in-depth guide on how to ...
The Ultimate Node.js Version Management Guide: Effortlessly Downgrade from Node 24 to 23 with NVM
00:00 | 169Switching Node.js versions is a common task for de...
PHP `json_decode` Failing on Strings with '$'? Master Debugging with This Simple Fix
00:00 | 135When debugging locally, JSON responses copied from...