$npx -y skills add agentscope-ai/QwenPaw --skill pdf-zh当用户需要对PDF文件进行任何操作时,请使用此技能。包括从 PDF 中读取或提取文本/表格、合并多个 PDF、拆分 PDF、旋转页面、添加水印、创建新PDF、填写PDF表单、加密/解密 PDF、提取图片,以及对扫描版 PDF 进行 OCR 使其可搜索。如果用户提到 .pdf 文件或要求生成 PDF,请使用此技能。
| 1 | > **重要:** 所有 `scripts/` 路径均相对于此技能目录。 |
| 2 | > 运行方式:`cd {this_skill_dir} && python scripts/...` |
| 3 | > 或使用 `execute_shell_command` 的 `cwd` 参数。 |
| 4 | |
| 5 | # PDF 处理指南 |
| 6 | |
| 7 | ## 前置要求 |
| 8 | |
| 9 | - **pypdf**:核心 PDF 读写功能 |
| 10 | - **pdfplumber**:文本和表格提取 |
| 11 | - **reportlab**:PDF 创建 |
| 12 | - **pdftotext** (poppler-utils):命令行文本提取 |
| 13 | - **pdftoppm** (poppler-utils):PDF 转图片 |
| 14 | - **qpdf**:PDF 操作(合并、拆分、旋转、解密) |
| 15 | |
| 16 | ## 概述 |
| 17 | |
| 18 | 本指南涵盖了使用 Python 库和命令行工具进行 PDF 处理的基本操作。有关高级功能、JavaScript 库和详细示例,请参阅 REFERENCE.md。如果需要填写 PDF 表单,请阅读 FORMS.md 并按照其中的说明操作。 |
| 19 | |
| 20 | ## 快速入门 |
| 21 | |
| 22 | ```python |
| 23 | from pypdf import PdfReader, PdfWriter |
| 24 | |
| 25 | # 读取 PDF |
| 26 | reader = PdfReader("document.pdf") |
| 27 | print(f"Pages: {len(reader.pages)}") |
| 28 | |
| 29 | # 提取文本 |
| 30 | text = "" |
| 31 | for page in reader.pages: |
| 32 | text += page.extract_text() |
| 33 | ``` |
| 34 | |
| 35 | ## Python 库 |
| 36 | |
| 37 | ### pypdf - 基本操作 |
| 38 | |
| 39 | #### 合并 PDF |
| 40 | ```python |
| 41 | from pypdf import PdfWriter, PdfReader |
| 42 | |
| 43 | writer = PdfWriter() |
| 44 | for pdf_file in ["doc1.pdf", "doc2.pdf", "doc3.pdf"]: |
| 45 | reader = PdfReader(pdf_file) |
| 46 | for page in reader.pages: |
| 47 | writer.add_page(page) |
| 48 | |
| 49 | with open("merged.pdf", "wb") as output: |
| 50 | writer.write(output) |
| 51 | ``` |
| 52 | |
| 53 | #### 拆分 PDF |
| 54 | ```python |
| 55 | reader = PdfReader("input.pdf") |
| 56 | for i, page in enumerate(reader.pages): |
| 57 | writer = PdfWriter() |
| 58 | writer.add_page(page) |
| 59 | with open(f"page_{i+1}.pdf", "wb") as output: |
| 60 | writer.write(output) |
| 61 | ``` |
| 62 | |
| 63 | #### 提取元数据 |
| 64 | ```python |
| 65 | reader = PdfReader("document.pdf") |
| 66 | meta = reader.metadata |
| 67 | print(f"Title: {meta.title}") |
| 68 | print(f"Author: {meta.author}") |
| 69 | print(f"Subject: {meta.subject}") |
| 70 | print(f"Creator: {meta.creator}") |
| 71 | ``` |
| 72 | |
| 73 | #### 旋转页面 |
| 74 | ```python |
| 75 | reader = PdfReader("input.pdf") |
| 76 | writer = PdfWriter() |
| 77 | |
| 78 | page = reader.pages[0] |
| 79 | page.rotate(90) # 顺时针旋转 90 度 |
| 80 | writer.add_page(page) |
| 81 | |
| 82 | with open("rotated.pdf", "wb") as output: |
| 83 | writer.write(output) |
| 84 | ``` |
| 85 | |
| 86 | ### pdfplumber - 文本和表格提取 |
| 87 | |
| 88 | #### 提取带布局的文本 |
| 89 | ```python |
| 90 | import pdfplumber |
| 91 | |
| 92 | with pdfplumber.open("document.pdf") as pdf: |
| 93 | for page in pdf.pages: |
| 94 | text = page.extract_text() |
| 95 | print(text) |
| 96 | ``` |
| 97 | |
| 98 | #### 提取表格 |
| 99 | ```python |
| 100 | with pdfplumber.open("document.pdf") as pdf: |
| 101 | for i, page in enumerate(pdf.pages): |
| 102 | tables = page.extract_tables() |
| 103 | for j, table in enumerate(tables): |
| 104 | print(f"Table {j+1} on page {i+1}:") |
| 105 | for row in table: |
| 106 | print(row) |
| 107 | ``` |
| 108 | |
| 109 | #### 高级表格提取 |
| 110 | ```python |
| 111 | import pandas as pd |
| 112 | |
| 113 | with pdfplumber.open("document.pdf") as pdf: |
| 114 | all_tables = [] |
| 115 | for page in pdf.pages: |
| 116 | tables = page.extract_tables() |
| 117 | for table in tables: |
| 118 | if table: # 检查表格是否为空 |
| 119 | df = pd.DataFrame(table[1:], columns=table[0]) |
| 120 | all_tables.append(df) |
| 121 | |
| 122 | # 合并所有表格 |
| 123 | if all_tables: |
| 124 | combined_df = pd.concat(all_tables, ignore_index=True) |
| 125 | combined_df.to_excel("extracted_tables.xlsx", index=False) |
| 126 | ``` |
| 127 | |
| 128 | ### reportlab - 创建 PDF |
| 129 | |
| 130 | #### 基本 PDF 创建 |
| 131 | ```python |
| 132 | from reportlab.lib.pagesizes import letter |
| 133 | from reportlab.pdfgen import canvas |
| 134 | |
| 135 | c = canvas.Canvas("hello.pdf", pagesize=letter) |
| 136 | width, height = letter |
| 137 | |
| 138 | # 添加文本 |
| 139 | c.drawString(100, height - 100, "Hello World!") |
| 140 | c.drawString(100, height - 120, "This is a PDF created with reportlab") |
| 141 | |
| 142 | # 添加线条 |
| 143 | c.line(100, height - 140, 400, height - 140) |
| 144 | |
| 145 | # 保存 |
| 146 | c.save() |
| 147 | ``` |
| 148 | |
| 149 | #### 创建多页 PDF |
| 150 | ```python |
| 151 | from reportlab.lib.pagesizes import letter |
| 152 | from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak |
| 153 | from reportlab.lib.styles import getSampleStyleSheet |
| 154 | |
| 155 | doc = SimpleDocTemplate("report.pdf", pagesize=letter) |
| 156 | styles = getSampleStyleSheet() |
| 157 | story = [] |
| 158 | |
| 159 | # 添加内容 |
| 160 | title = Paragraph("Report Title", styles['Title']) |
| 161 | story.append(title) |
| 162 | story.append(Spacer(1, 12)) |
| 163 | |
| 164 | body = Paragraph("This is the body of the report. " * 20, styles['Normal']) |
| 165 | story.append(body) |
| 166 | story.append(PageBreak()) |
| 167 | |
| 168 | # 第 2 页 |
| 169 | story.append(Paragraph("Page 2", styles['Heading1'])) |
| 170 | story.append(Paragraph("Content for page 2", styles['Normal'])) |
| 171 | |
| 172 | # 构建 PDF |
| 173 | doc.build(story) |
| 174 | ``` |
| 175 | |
| 176 | #### 下标和上标 |
| 177 | |
| 178 | **重要**:切勿在 ReportLab PDF 中使用 Unicode 下标/上标字符(₀₁₂₃₄₅₆₇₈₉、⁰¹²³⁴⁵⁶⁷⁸⁹)。内置字体不包含这些字形,会导致它们渲染为黑色方块。 |
| 179 | |
| 180 | 请改用 ReportLab 在 Paragraph 对象中的 XML 标记标签: |
| 181 | ```python |
| 182 | from reportlab.platypus import Paragraph |
| 183 | from reportlab.lib.styles import getSampleStyleSheet |
| 184 | |
| 185 | styles = getSampleStyleSheet() |
| 186 | |
| 187 | # 下标:使用 <sub> 标签 |
| 188 | chemical = Paragraph("H<sub>2</sub>O", styles['Normal']) |
| 189 | |
| 190 | # 上标:使用 <super> 标签 |
| 191 | squared = Paragraph("x<super>2</super> + y<super>2</super>", styles['Normal']) |
| 192 | ``` |
| 193 | |
| 194 | 对于使用 canvas 绘制的文本(非 Paragraph 对象),请手动调整字体大小和位置,而不是使用 Unicode 下标/上标。 |
| 195 | |
| 196 | ## 命令行工具 |
| 197 | |
| 198 | ### pdftotext (poppler-utils) |
| 199 | ```bash |
| 200 | # 提取文本 |
| 201 | pdftotext input.pdf output.txt |
| 202 | |
| 203 | # 保留布局提取文本 |
| 204 | pdftotext -layout input.pdf output.txt |
| 205 | |
| 206 | # 提取指定页面 |
| 207 | pdftotext -f 1 -l 5 input.pdf output.txt # 第 1-5 页 |
| 208 | ``` |
| 209 | |
| 210 | ### qpdf |
| 211 | ```bash |
| 212 | # 合并 PDF |
| 213 | qpdf --empty --pages file1.pdf file2.pdf -- merged.pdf |
| 214 | |
| 215 | # 拆分页面 |
| 216 | qpdf input.pdf --pages . 1-5 -- pages1-5.pdf |
| 217 | qpdf input.pdf --pages . 6-10 -- pages6-10.pdf |
| 218 | |
| 219 | # 旋转页面 |
| 220 | qpdf input.pdf output.pdf --rotate |