在 JavaScript (Node.js) 中生成 PDF 报告
创建 PDF 文档是 Web 开发中最常见的任务之一。报告、发票、证书、文档——所有这些都需要一个可靠的服务器端生成工具。在 Node.js 生态系统中,有几个强大的库可以灵活地生成 PDF 文件。在本文中,我们将详细分析三种主要方法:PDFKit(直接编程)、Puppeteer(HTML 渲染)和 jsPDF(轻量级方案)。
为什么要在服务器上生成 PDF?
- 动态内容——来自数据库的数据、计算、图表。
- 安全性——访问控制和版本管理。
- 自动化——按计划批量发送报告。
- 统一格式——在所有设备上正确显示。
1. PDFKit——对文档的完全控制
PDFKit 是从零开始创建 PDF 最流行的库。它不需要浏览器,仅使用 JavaScript 对象工作。您可以控制每个元素:文本、图像、矢量图形、字体。
安装
npm install pdfkit示例:带表格的简单报告
const PDFDocument = require('pdfkit');const fs = require('fs');
const doc = new PDFDocument();
doc.pipe(fs.createWriteStream('report.pdf'));
// 标题doc.fontSize(24).text('月度报告', { align: 'center' });doc.moveDown();
// 副标题doc.fontSize(14).text('周期:2025 年 1 月', { align: 'center' });doc.moveDown(2);
// 表格(模拟)doc.fontSize(12).font('Helvetica-Bold');doc.text('商品', 50, 150);doc.text('数量', 250, 150);doc.text('价格', 400, 150);
doc.font('Helvetica');const data = [ { item: '笔记本电脑', qty: 15, price: 75000 }, { item: '显示器', qty: 30, price: 25000 }, { item: '键盘', qty: 100, price: 1500 }];
let y = 170;data.forEach(row => { doc.text(row.item, 50, y); doc.text(String(row.qty), 250, y); doc.text(String(row.price), 400, y); y += 20;});
// 总计doc.moveDown();doc.font('Helvetica-Bold');doc.text(`总计:${data.reduce((sum, r) => sum + r.qty * r.price, 0)} 卢布`, 50, y + 20);
doc.end();PDFKit 的功能:
- 矢量图形(线条、矩形、圆形)。
- 嵌入字体(TrueType、OpenType)。
- 支持 Unicode 和西里尔字母。
- 创建链接和注释。
2. Puppeteer——将 HTML 渲染为 PDF
如果您已经有报告 HTML 模板(例如,基于 Bootstrap),Puppeteer 是理想的选择。它启动无头 Chrome 浏览器,并将页面转换为 PDF,保留所有样式、字体和 JavaScript 动画。
安装
npm install puppeteer示例:转换 HTML 模板
const puppeteer = require('puppeteer');
async function generatePDF() { const browser = await puppeteer.launch(); const page = await browser.newPage();
// 带 CSS 的 HTML const html = ` <!DOCTYPE html> <html> <head> <style> body { font-family: Arial; padding: 40px; } h1 { color: #2c3e50; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ccc; padding: 8px; } </style> </head> <body> <h1>销售报告</h1> <table> <tr><th>月份</th><th>金额</th></tr> <tr><td>一月</td><td>450,000 卢布</td>