web-pdf-downloader
v1.0.3
Published
Generate and download PDFs from HTML/URLs with perfect page break handling. Fixes dashboard printing issues, prevents content cutoff, and handles tables, cards, and complex layouts elegantly.
Maintainers
Readme
web-pdf-downloader
Generate and download PDFs from HTML/URLs with perfect page break handling. Fixes dashboard printing issues, prevents content cutoff, and handles tables, cards, and complex layouts elegantly.
✨ Features
- 🎯 Perfect Page Breaks - Prevents content cutoff and repetition across pages
- 📊 Dashboard-Friendly - Handles cards, widgets, and dashboard elements without breaking
- 📋 Smart Table Handling - Tables break elegantly across pages with headers preserved
- 🖼️ Image Protection - Images never break across pages
- 🎨 Background Support - Preserves colors, backgrounds, and styling
- ⚡ Flexible - Works with HTML strings or URLs
- 🌐 Universal - Works in Node.js, Express, Next.js API routes, and browser environments
- 🔧 Customizable - Full control over PDF options (format, margins, etc.)
- 📦 Lightweight - Uses
puppeteer-core(you control the Chrome binary)
🚀 Quick Start
Installation
npm install web-pdf-downloaderBasic Usage (Node.js)
import { generatePDF } from 'web-pdf-downloader';
import fs from 'fs';
// From HTML string
const pdfBuffer = await generatePDF({
html: '<h1>Hello World</h1><p>This is a PDF!</p>',
executablePath: '/path/to/chrome', // or 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' on Windows
});
// Save to file
fs.writeFileSync('output.pdf', pdfBuffer);
// From URL
const pdfBuffer = await generatePDF({
url: 'https://example.com',
executablePath: '/path/to/chrome',
});
fs.writeFileSync('output.pdf', pdfBuffer);Browser Usage
import { downloadPDF } from 'web-pdf-downloader';
// Download PDF directly in browser
await downloadPDF({
html: '<h1>My Dashboard</h1>',
executablePath: '/path/to/chrome', // Required even in browser (for server-side generation)
filename: 'dashboard.pdf',
});📖 API Reference
generatePDF(options: PDFGenerationOptions): Promise<Uint8Array>
Generates a PDF buffer from HTML or URL.
Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| html | string | Yes* | Raw HTML content to convert |
| url | string | Yes* | URL to load and convert |
| executablePath | string | Yes | Path to Chrome/Chromium executable |
| puppeteerArgs | string[] | No | Additional Puppeteer launch arguments |
| waitForSelector | string | No | CSS selector to wait for before generating PDF |
| waitForTimeout | number | No | Timeout in milliseconds to wait for content |
| pdfOptions | Partial<PuppeteerPDFOptions> | No | Custom PDF options (format, margins, etc.) |
* Either html or url must be provided (not both).
PDF Options
You can customize PDF generation by passing pdfOptions:
const pdfBuffer = await generatePDF({
html: '<h1>Custom PDF</h1>',
executablePath: '/path/to/chrome',
pdfOptions: {
format: 'Letter', // 'A4', 'Letter', etc.
margin: {
top: '10mm',
right: '10mm',
bottom: '10mm',
left: '10mm',
},
printBackground: true,
displayHeaderFooter: true,
headerTemplate: '<div>Header</div>',
footerTemplate: '<div>Footer</div>',
},
});downloadPDF(options: PDFDownloadOptions): Promise<void>
Downloads a PDF directly in the browser. Same options as generatePDF, plus:
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| filename | string | No | Filename for download (default: "document.pdf") |
🎨 Page Break Control
The package automatically handles page breaks intelligently, but you can also control them manually:
Automatic Protection
The following elements are automatically protected from breaking across pages:
- Cards (
.card,.dashboard-card,[class*="card"]) - Headings (
h1,h2,h3, etc.) - Images (
img,svg,canvas) - Charts and graphs (
.chart,[class*="chart"]) - Tables rows and cells
- Lists items
- Code blocks
Manual Control
Add CSS classes to control page breaks:
<!-- Force a page break -->
<div class="page-break">Content before break</div>
<div>Content after break</div>
<!-- Prevent breaking inside -->
<div class="no-break">
This entire section will stay together
</div>
<!-- Prevent breaking before -->
<div class="no-break-before">
This won't start on a new page alone
</div>
<!-- Prevent breaking after -->
<div class="no-break-after">
This won't end on a page alone
</div>🔧 Advanced Examples
Next.js API Route
// pages/api/generate-pdf.ts
import { generatePDF } from 'web-pdf-downloader';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { html } = req.body;
try {
const pdfBuffer = await generatePDF({
html,
executablePath: process.env.CHROME_PATH || '/usr/bin/google-chrome',
});
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename="document.pdf"');
res.send(Buffer.from(pdfBuffer));
} catch (error) {
res.status(500).json({ error: 'PDF generation failed' });
}
}Express Server
import express from 'express';
import { generatePDF } from 'web-pdf-downloader';
const app = express();
app.post('/api/pdf', async (req, res) => {
const { html } = req.body;
try {
const pdfBuffer = await generatePDF({
html,
executablePath: process.env.CHROME_PATH || '/usr/bin/google-chrome',
pdfOptions: {
format: 'A4',
margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' },
},
});
res.contentType('application/pdf');
res.send(Buffer.from(pdfBuffer));
} catch (error) {
res.status(500).json({ error: 'PDF generation failed' });
}
});
app.listen(3000);Waiting for Dynamic Content
// Wait for a specific element to appear
const pdfBuffer = await generatePDF({
url: 'https://example.com/dashboard',
executablePath: '/path/to/chrome',
waitForSelector: '.dashboard-loaded', // Wait for this selector
waitForTimeout: 2000, // Additional 2 seconds wait
});Custom Puppeteer Arguments
const pdfBuffer = await generatePDF({
html: '<h1>PDF</h1>',
executablePath: '/path/to/chrome',
puppeteerArgs: [
'--disable-web-security',
'--disable-features=IsolateOrigins',
],
});🐛 Troubleshooting
Finding Chrome Executable Path
Windows:
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
// or
executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'macOS:
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'Linux:
executablePath: '/usr/bin/google-chrome'
// or
executablePath: '/usr/bin/chromium-browser'Common Issues
Issue: "executablePath is required"
- Make sure you provide the correct path to Chrome/Chromium executable
- Use absolute paths, not relative paths
Issue: Content is cut off
- The package automatically handles this, but ensure your HTML has proper structure
- Use
.no-breakclass for elements that must stay together - Check that images have proper
max-widthCSS
Issue: Tables breaking incorrectly
- Tables are automatically handled, but ensure table headers are in
<thead>tags - Use
page-break-inside: avoidon specific rows if needed
Issue: Slow generation
- Increase
waitForTimeoutif content takes time to load - Use
waitForSelectorto wait for specific elements instead of full page load
📝 License
MIT © Muhammad Muneeb
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📚 Related
- Puppeteer - The underlying technology
- Puppeteer PDF Options - Full PDF options reference
Made with ❤️ for perfect PDF generation
