PDF操作Skill pdf-manipulation

PDF操作技能专注于自动化处理PDF文件,支持合并、拆分、提取文本和图像、加密保护、压缩优化等常见操作,适用于文档管理、发票处理和批量工作流。关键词:PDF处理、文档自动化、命令行工具、Node.js、Python、ghostscript、pdftk。

DevOps 0 次安装 0 次浏览 更新于 3/22/2026

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-liberationmsttcorefonts包。

另请参阅