name: pdf-manipulation description: 操作PDF文件,包括合并、拆分、提取、涂黑、转换和安全工作流。
PDF操作技能
使用免费的命令行工具和库合并、拆分、提取、涂黑和转换PDF文件。覆盖文档自动化工作流的常见PDF操作。
何时使用
- 将多个PDF合并为一个文档
- 将大型PDF拆分为单独文件或页面范围
- 提取文本、图像或特定页面
- 涂黑敏感信息
- 添加水印、密码或元数据
- 将PDF转换为图像或其他格式
所需工具
- pdftk — PDF操作的瑞士军刀(合并、拆分、旋转、加密)
- qpdf — PDF转换和加密(线性化、解密、修复)
- pdftotext / pdfimages — poppler-utils的一部分(提取文本和图像)
- ghostscript (gs) — 高级PDF处理、压缩和转换
安装
# Ubuntu/Debian
sudo apt-get install pdftk qpdf poppler-utils ghostscript
# macOS (Homebrew)
brew install pdftk-java qpdf poppler ghostscript
# For Node.js: npm i pdf-lib (纯JS,无系统依赖)
# For Python: pip install PyPDF2 pypdf
技能
合并PDF
# 使用pdftk(保留书签、表单)
pdftk file1.pdf file2.pdf file3.pdf cat output merged.pdf
# 使用ghostscript(更好的压缩)
gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=merged.pdf file1.pdf file2.pdf file3.pdf
# 使用qpdf(保留结构)
qpdf --empty --pages file1.pdf file2.pdf file3.pdf -- merged.pdf
Node.js (pdf-lib):
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
async function mergePDFs(files, output) {
const mergedPdf = await PDFDocument.create();
for (const file of files) {
const pdfBytes = fs.readFileSync(file);
const pdf = await PDFDocument.load(pdfBytes);
const pages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
pages.forEach(page => mergedPdf.addPage(page));
}
const mergedBytes = await mergedPdf.save();
fs.writeFileSync(output, mergedBytes);
}
// mergePDFs(['file1.pdf', 'file2.pdf'], 'merged.pdf');
拆分PDF(按页面或范围)
# 将每个页面拆分为单独文件
pdftk input.pdf burst output page_%02d.pdf
# 提取特定页面(例如,第1-5页和第10页)
pdftk input.pdf cat 1-5 10 output subset.pdf
# 使用qpdf提取页面范围
qpdf input.pdf --pages . 1-5 -- output.pdf
# 每N页拆分(例如,每2页)
pdftk input.pdf burst
# 然后手动合并或脚本处理
Node.js (pdf-lib):
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
async function extractPages(inputPath, pages, outputPath) {
const pdfBytes = fs.readFileSync(inputPath);
const pdfDoc = await PDFDocument.load(pdfBytes);
const newPdf = await PDFDocument.create();
for (const pageNum of pages) {
const [page] = await newPdf.copyPages(pdfDoc, [pageNum - 1]);
newPdf.addPage(page);
}
const newBytes = await newPdf.save();
fs.writeFileSync(outputPath, newBytes);
}
// extractPages('input.pdf', [1, 3, 5], 'output.pdf');
提取文本
# 提取所有文本(保留布局)
pdftotext input.pdf output.txt
# 提取原始文本(无布局)
pdftotext -raw input.pdf output.txt
# 提取特定页面
pdftotext -f 1 -l 5 input.pdf output.txt
# 使用qpdf + pdftotext
pdftotext -layout input.pdf -
Node.js (pdf-parse):
const fs = require('fs');
const pdf = require('pdf-parse');
async function extractText(filePath) {
const dataBuffer = fs.readFileSync(filePath);
const data = await pdf(dataBuffer);
return data.text;
}
// extractText('input.pdf').then(console.log);
提取图像
# 从PDF提取所有图像
pdfimages -all input.pdf output_prefix
# 输出:output_prefix-000.png、output_prefix-001.jpg等
# 仅提取JPEG
pdfimages -j input.pdf output_prefix
涂黑/移除页面
# 移除特定页面(例如,移除第2-4页)
pdftk input.pdf cat 1 5-end output redacted.pdf
# 仅保留特定页面
pdftk input.pdf cat 1-10 20-30 output selected.pdf
添加密码保护
# 使用密码加密PDF
pdftk input.pdf output secured.pdf user_pw mypassword
# 移除密码
pdftk secured.pdf input_pw mypassword output unlocked.pdf
# 使用qpdf(AES-256)
qpdf --encrypt userpass ownerpass 256 -- input.pdf output.pdf
Node.js (pdf-lib):
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
async function encryptPDF(inputPath, password, outputPath) {
const pdfBytes = fs.readFileSync(inputPath);
const pdfDoc = await PDFDocument.load(pdfBytes);
const encryptedBytes = await pdfDoc.save({
userPassword: password,
ownerPassword: password
});
fs.writeFileSync(outputPath, encryptedBytes);
}
旋转页面
# 将所有页面顺时针旋转90度
pdftk input.pdf cat 1-endright output rotated.pdf
# 旋转特定页面
pdftk input.pdf cat 1-5 6right 7-end output rotated.pdf
# 选项:right(90°)、left(270°)、down(180°)
压缩/减小文件大小
# 使用ghostscript(调整质量)
gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook \
-dNOPAUSE -dQUIET -dBATCH -sOutputFile=compressed.pdf input.pdf
# 质量设置:
# /screen - 低质量(72 dpi)
# /ebook - 中等(150 dpi)
# /printer - 高(300 dpi)
# /prepress - 最高(300 dpi,保留颜色)
# 使用qpdf(无损压缩)
qpdf --linearize --object-streams=generate input.pdf compressed.pdf
将PDF转换为图像
# 将每个页面转换为PNG(300 DPI)
pdftoppm -png -r 300 input.pdf output_prefix
# 输出:output_prefix-1.png、output_prefix-2.png等
# 转换为JPEG
pdftoppm -jpeg -r 150 input.pdf output_prefix
# 使用ImageMagick(替代方案)
convert -density 300 input.pdf output_%03d.png
添加水印
# 在每个页面上叠加watermark.pdf
pdftk input.pdf stamp watermark.pdf output watermarked.pdf
# 背景水印(在内容后面)
pdftk input.pdf background watermark.pdf output watermarked.pdf
# 仅水印特定页面
pdftk input.pdf multistamp watermark.pdf output watermarked.pdf
获取PDF元数据
# 使用pdftk
pdftk input.pdf dump_data
# 使用qpdf
qpdf --show-object=1 input.pdf
# 使用pdfinfo(poppler-utils)
pdfinfo input.pdf
多操作脚本(Node.js)
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
class PDFHelper {
static async merge(files, output) {
const merged = await PDFDocument.create();
for (const file of files) {
const pdf = await PDFDocument.load(fs.readFileSync(file));
const pages = await merged.copyPages(pdf, pdf.getPageIndices());
pages.forEach(p => merged.addPage(p));
}
fs.writeFileSync(output, await merged.save());
}
static async split(input, ranges, output) {
const pdf = await PDFDocument.load(fs.readFileSync(input));
const newPdf = await PDFDocument.create();
const pages = await newPdf.copyPages(pdf, ranges);
pages.forEach(p => newPdf.addPage(p));
fs.writeFileSync(output, await newPdf.save());
}
static async info(input) {
const pdf = await PDFDocument.load(fs.readFileSync(input));
return {
pages: pdf.getPageCount(),
title: pdf.getTitle(),
author: pdf.getAuthor(),
creator: pdf.getCreator()
};
}
}
module.exports = PDFHelper;
代理提示
您具备PDF操作技能。当用户请求PDF操作时:
1. 检测操作:合并、拆分、提取(文本/图像/页面)、涂黑、压缩、加密、旋转、水印或获取信息。
2. 使用适当的工具:
- pdftk用于合并、拆分、旋转、加密、水印
- pdftotext/pdfimages用于提取
- ghostscript用于压缩
- qpdf用于修复和高级操作
3. 在处理前始终验证输入文件是否存在。
4. 对于脚本,优先使用pdf-lib(Node.js)或PyPDF2(Python)以确保可移植性。
5. 以JSON格式返回结构化输出(文件路径、元数据、文本)。
最佳实践
- 验证PDF:在处理前使用
qpdf --check input.pdf。 - 保留元数据:尽可能使用pdftk或pdf-lib,避免使用ghostscript进行简单操作。
- 使用适当的压缩 — ghostscript的
/ebook设置是大多数情况下的良好平衡。 - 安全 — 如果用户提供密码,在处理前始终移除密码;切勿记录密码。
- 大文件 — 对于100+页的PDF,分块处理或使用流API。
常见工作流
发票处理
# 1. 提取文本以解析
pdftotext invoice.pdf invoice.txt
# 2. 仅提取第一页(摘要)
pdftk invoice.pdf cat 1 output summary.pdf
# 3. 压缩以归档
gs -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dBATCH -dNOPAUSE -q \
-sOutputFile=invoice_compressed.pdf invoice.pdf
批量处理
# 合并目录中的所有PDF
pdftk *.pdf cat output combined.pdf
# 将目录中的每个PDF拆分为单独页面
for f in *.pdf; do
pdftk "$f" burst output "${f%.pdf}_page_%02d.pdf"
done
# 从所有PDF提取文本
for f in *.pdf; do
pdftotext "$f" "${f%.pdf}.txt"
done
故障排除
- 损坏的PDF:使用
qpdf --check然后qpdf input.pdf --replace-input修复。 - 加密的PDF:首先使用
qpdf --decrypt --password=PASS input.pdf output.pdf移除密码。 - 大文件大小:使用ghostscript压缩或移除不需要的嵌入字体/图像。
- 缺少字体:安装
fonts-liberation或msttcorefonts包。
另请参阅
- anonymous-file-upload.md — 匿名上传处理后的PDF。
- using-web-scraping.md — 抓取网页并转换为PDF。