JavaScript 操作 PDF 全景指南
在 Web 开发中,我们经常需要在前端或后端处理 PDF 文件,

- 生成 PDF:将订单、报告、发票等数据导出为 PDF 供用户下载。
- 读取/解析 PDF:从用户上传的 PDF 文件中提取文本、图片或表单数据。
- 编辑 PDF:在现有 PDF 上添加水印、合并多个 PDF、填写表单等。
JavaScript 生态为此提供了强大的库,我们将主要围绕以下几个方面展开:
- 客户端生成 PDF (浏览器端)
- 客户端解析 PDF (浏览器端)
- 服务器端生成/操作 PDF (Node.js)
- 最佳实践与注意事项
第一部分:客户端生成 PDF (浏览器端)
在浏览器中生成 PDF,通常是将 HTML 页面或特定 DOM 元素转换为 PDF 文件并触发下载。
推荐库:html2canvas + jspdf (经典组合)
这是一个非常流行且灵活的组合,适合将复杂的 HTML 布局(如报表、海报)转换为 PDF。
安装

通过 CDN 或 npm 安装。
# npm npm install html2canvas jspdf
<!-- CDN --> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
基本用法
将某个 div 元素转换为 PDF。
<!-- 你想要转换的 HTML 内容 -->
<div id="content-to-print" style="padding: 20px; border: 1px solid #ccc;">
<h1>我的 PDF 报告</h1>
<p>这是由 JavaScript 生成的 PDF 文件。</p>
<table>
<tr><th>产品</th><th>价格</th></tr>
<tr><td>苹果</td><td>¥5</td></tr>
<tr><td>香蕉</td><td>¥3</td></tr>
</table>
</div>
<!-- 一个按钮来触发下载 -->
<button id="download-btn">下载 PDF</button>
<script>
document.getElementById('download-btn').addEventListener('click', function() {
const input = document.getElementById('content-to-print');
// 1. 使用 html2canvas 将 DOM 元素转换为 canvas
html2canvas(input).then(canvas => {
// 2. 获取 canvas 的图像数据
const imgData = canvas.toDataURL('image/png');
// 3. 创建 jsPDF 实例
const pdf = new jspdf.jsPDF('p', 'mm', 'a4'); // 'p' (portrait), 'mm' (millimeters), 'a4' (page size)
// 4. 计算图片在 PDF 中的尺寸,使其适应页面
const imgWidth = 210; // A4 宽度
const pageHeight = 295; // A4 高度
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// 5. 将图片添加到 PDF
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// 6. 如果内容超过一页,添加新页
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
// 7. 保存 PDF 文件
pdf.save('my-report.pdf');
});
});
</script>
优点:

- 灵活性高,可以转换任何 HTML 元素。
- 保留了 CSS 样式和布局。
缺点:
- 依赖浏览器,对于非常复杂的 CSS(如
box-shadow,transform)支持可能不完美。 - 性能较差,对于大量内容或复杂页面,转换可能很慢。
- 生成的 PDF 是基于图片的,无法复制文本或搜索。
推荐库:pdf-lib (功能强大的客户端 PDF 编辑)
pdf-lib 是一个更现代、功能更强大的库,它不仅可以创建 PDF,还可以读取、修改和合并现有的 PDF 文件,并且操作的是 PDF 的矢量对象,而不是图片。
安装
npm install pdf-lib
基本用法
示例:在现有 PDF 上添加文字
import { PDFDocument, rgb } from 'pdf-lib';
// 1. 加载一个现有的 PDF 文件 (可以是 ArrayBuffer, Blob 或 URL)
const existingPdfBytes = await fetch('path/to/your/document.pdf').then(res => res.arrayBuffer());
// 2. 创建 PDFDocument 实例
const pdfDoc = await PDFDocument.load(existingPdfBytes);
// 3. 获取第一页
const pages = pdfDoc.getPages();
const firstPage = pages[0];
const { width, height } = firstPage.getSize();
// 4. 在页面上绘制文字
firstPage.drawText('你好,世界!', {
x: 50,
y: height - 50,
size: 30,
color: rgb(0, 0.5, 0), // 绿色
});
// 5. 保存修改后的 PDF
const pdfBytes = await pdfDoc.save();
// 6. 创建一个下载链接并触发下载
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'modified-document.pdf';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
优点:
- 功能极其强大,可以进行 PDF 的深度编辑。
- 操作的是矢量对象,生成的 PDF 质量高,可复制、可搜索。
- 可以合并、拆分、添加表单、签名等。
缺点:
- API 相对复杂,学习曲线比
html2canvas+jspdf陡峭。 - 对于简单的“HTML 转 PDF”需求,有些“杀鸡用牛刀”。
第二部分:客户端解析 PDF (浏览器端)
有时我们需要在浏览器中读取 PDF 文件,例如提取文本或预览。
推荐库:pdfjs-dist (官方推荐)
PDF.js 是 Mozilla 官方维护的 PDF 渲染库,功能非常强大,是浏览器中处理 PDF 的事实标准。
安装
npm install pdfjs-dist
基本用法:提取文本
import { getDocument } from 'pdfjs-dist/build/pdf';
import 'pdfjs-dist/build/pdf.worker.entry'; // 必须引入 worker
async function extractTextFromPdf(file) {
const fileReader = new FileReader();
await new Promise((resolve, reject) => {
fileReader.onload = resolve;
fileReader.onerror = reject;
fileReader.readAsArrayBuffer(file);
});
const typedArray = new Uint8Array(fileReader.result);
// 加载 PDF 文档
const pdf = await getDocument(typedArray).promise;
let fullText = '';
// 遍历每一页
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
// 获取文本内容
const textContent = await page.getTextContent();
// 将文本块拼接成字符串
const pageText = textContent.items.map(item => item.str).join(' ');
fullText += `--- 第 ${i} 页 ---\n${pageText}\n\n`;
}
console.log(fullText);
return fullText;
}
// 使用示例
const fileInput = document.getElementById('pdf-file-input');
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
extractTextFromPdf(file);
}
});
优点:
- 官方维护,稳定可靠。
- 功能全面,可以渲染 PDF、提取文本、获取元数据等。
- 支持高级 PDF 特性。
缺点:
- 库较大,可能会影响页面加载性能。
- 配置稍显复杂(如需要 worker 文件)。
第三部分:服务器端生成/操作 PDF (Node.js)
在服务器端生成 PDF 可以避免客户端的性能问题,并且可以处理更复杂的逻辑,如模板渲染、数据库查询等。
推荐库:puppeteer (基于 Chrome 的自动化)
puppeteer 是一个由 Google 开发的库,可以通过编程控制一个无头 Chrome 浏览器,这是目前生成高质量、可复制的 PDF 的最佳方式之一。
安装
npm install puppeteer
基本用法:将 URL 转为 PDF
const puppeteer = require('puppeteer');
async function generatePdfFromUrl(url, outputPath) {
// 1. 启动浏览器
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 2. 访问 URL
await page.goto(url, { waitUntil: 'networkidle2' });
// 3. 生成 PDF
// viewport 可以控制 PDF 的尺寸
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true, // 打印背景
margin: {
top: '20px',
bottom: '20px',
left: '20px',
right: '20px'
}
});
// 4. 关闭浏览器
await browser.close();
console.log(`PDF 已生成并保存到: ${outputPath}`);
}
// 使用示例
generatePdfFromUrl('https://example.com', 'example.pdf');
从 HTML 字符串生成 PDF
async function generatePdfFromHtml(htmlContent, outputPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 直接设置页面的 HTML 内容
await page.setContent(htmlContent, { waitUntil: 'networkidle0' });
await page.pdf({
path: outputPath,
format: 'A4',
});
await browser.close();
}
// 使用示例
const html = `
<html>
<head><title>My PDF</title></head>
<body style="font-family: sans-serif;">
<h1>服务器生成的 PDF</h1>
<p>这是由 Puppeteer 在 Node.js 中生成的。</p>
</body>
</html>
`;
generatePdfFromHtml(html, 'from-html.pdf');
优点:
- 质量极高:生成的是真正的、可复制的、可搜索的 PDF。
- 功能强大:可以执行 JavaScript,渲染任何现代 Web 技术(React, Vue, WebGL 等)。
- 高度可控:可以模拟用户交互、截图、处理表单等。
缺点:
- 依赖系统中的 Chrome/Chromium,部署环境需要额外配置。
- 启动浏览器有性能开销,不适合高并发的简单任务。
推荐库:pdf-lib (Node.js 环境)
pdf-lib 不仅可以在浏览器中使用,也可以在 Node.js 中使用,非常适合进行 PDF 的修改和合并。
// Node.js 示例
const { PDFDocument, rgb } = require('pdf-lib');
const fs = require('fs');
async function modifyPdf() {
// 读取 PDF 文件
const existingPdfBytes = fs.readFileSync('path/to/document.pdf');
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const pages = pdfDoc.getPages();
const firstPage = pages[0];
const { width } = firstPage.getSize();
firstPage.drawText('添加于服务器', {
x: width / 2 - 50,
y: 50,
size: 20,
color: rgb(1, 0, 0), // 红色
});
const pdfBytes = await pdfDoc.save();
fs.writeFileSync('modified-document.pdf', pdfBytes);
}
modifyPdf();
第四部分:最佳实践与注意事项
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单 HTML 转 PDF | html2canvas + jspdf |
快速实现,无需服务器,适合简单布局。 |
| 高质量、可复制 PDF | puppeteer (服务器端) |
生成 PDF 质量最高,支持所有现代 Web 技术。 |
| 编辑现有 PDF (添加文字/水印/合并) | pdf-lib (客户端或服务器端) |
功能强大,专门为 PDF 操作设计,矢量编辑。 |
| 从网页 URL 生成 PDF | puppeteer (服务器端) |
最可靠的方式,能处理复杂的动态内容。 |
| 在浏览器中预览/提取 PDF | pdfjs-dist |
官方标准,功能全面,兼容性好。 |
| 服务器端生成报表/发票 | puppeteer 或 pdf-lib |
puppeteer 适合模板化页面,pdf-lib 适合基于现有文档修改。 |
重要注意事项
-
CORS (跨域资源共享):
- 在浏览器中使用
pdfjs-dist或puppeteer(如果页面有 CORS 限制) 时,PDF 文件来自不同的域名,服务器必须设置正确的Access-Control-Allow-Origin头,否则浏览器会阻止请求。
- 在浏览器中使用
-
字体问题:
- 当使用
html2canvas或puppeteer时,PDF 中使用了特殊的 Web 字体,需要确保这些字体可以被嵌入或正确加载。puppeteer通常能更好地处理这个问题。 - 使用
pdf-lib时,可以嵌入字体,确保 PDF 在任何设备上都能正确显示。
- 当使用
-
性能:
- 客户端生成 PDF 会消耗大量 CPU 和内存,可能导致页面卡顿或崩溃,对于复杂或大型 PDF,强烈建议使用服务器端方案(如
puppeteer)。
- 客户端生成 PDF 会消耗大量 CPU 和内存,可能导致页面卡顿或崩溃,对于复杂或大型 PDF,强烈建议使用服务器端方案(如
-
安全性:
- 如果用户输入用于生成 PDF 的 HTML 内容,务必进行XSS 防护,防止恶意脚本注入,对 HTML 进行净化处理。
-
puppeteer的部署:- 在生产环境中使用
puppeteer,通常需要配合docker或使用预装的 Chromium 镜像(如puppeteer/chromium)来简化部署。
- 在生产环境中使用
希望这份详细的教程能帮助你选择并掌握 JavaScript 操作 PDF 的最佳方法!
