1. 什么是 PyMuPDF? #
PyMuPDF 是 Python 中最强大的 PDF 处理库之一,它是 MuPDF 引擎的 Python 绑定。虽然导入时使用 fitz(这是历史原因),但库名是 PyMuPDF。它可以处理 PDF、XPS、EPUB 等多种格式,功能强大且性能优秀。
为什么要用 PyMuPDF?
想象一下,你需要从 PDF 中提取文本、提取图片、合并多个 PDF 文件,或者创建新的 PDF 文档。手动操作太慢且容易出错,而 PyMuPDF 提供了强大的 Python API,可以自动化完成这些任务。
PyMuPDF 的优势:
- 功能强大:支持文本提取、图片提取、PDF 创建、页面编辑等多种操作
- 性能优秀:基于 C 语言实现,处理速度快
- 精确度高:可以精确提取文本位置、图片坐标等信息
- 易于使用:API 设计简洁直观,文档完善
- 跨平台:Windows、macOS、Linux 都可以使用
前置知识补充:
- PDF(可移植文档格式):PDF 是一种用于展示文档的文件格式,可以包含文本、图片、表格等多种内容。PDF 文件可以在不同设备和系统上保持一致的显示效果。
- 页面(Page):PDF 文档由多个页面组成,每个页面是独立的,类似于书籍中的一页。
- 坐标系统:PDF 使用坐标系统来定位元素。通常以页面左下角为原点 (0, 0),X 轴向右,Y 轴向上。
- 元数据(Metadata):PDF 文件可以包含元数据信息,如标题、作者、创建日期等。
为什么导入名是
fitz?:这是历史原因。PyMuPDF 的开发者最初使用fitz作为导入名,这个名称来源于 MuPDF 的一个渲染引擎。虽然库名是 PyMuPDF,但导入时仍然使用import fitz。
2. 环境准备 #
在学习 PyMuPDF 之前,需要确保你的 Python 环境已经准备好。PyMuPDF 支持 Python 3.7 及以上版本。
2.1 检查 Python 版本 #
在安装之前,先检查一下你的 Python 版本是否符合要求。
# Windows PowerShell:查看 Python 版本
python --version# macOS 终端:查看 Python 版本
python3 --version2.2 安装 PyMuPDF #
PyMuPDF 可以直接通过 pip 安装。安装过程会自动下载所需的依赖包。
# 说明:Windows PowerShell 安装 PyMuPDF
# 先升级 pip 到最新版本,确保能正常安装依赖
python -m pip install --upgrade pip
# 安装 PyMuPDF 库
python -m pip install PyMuPDF# 说明:macOS / Linux 终端安装 PyMuPDF
# 先升级 pip 到最新版本
python3 -m pip install --upgrade pip
# 安装 PyMuPDF 库
python3 -m pip install PyMuPDF网络加速提示:如果从 PyPI 下载较慢,可以使用国内镜像:
- Windows:
python -m pip install PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple- macOS:
python3 -m pip install PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple
安装问题提示:
- 如果安装失败,可能是因为网络问题。可以尝试使用镜像源,或者检查网络连接。
- macOS 用户如果遇到权限问题,可以在命令前加
sudo(不推荐),或者使用虚拟环境。- 如果遇到编译错误,可能需要安装系统依赖。大多数情况下,pip 会自动处理这些依赖。
2.3 验证安装 #
安装完成后,验证一下是否安装成功。
# -*- coding: utf-8 -*-
# 说明:验证 PyMuPDF 是否安装成功
# 说明:导入 fitz 模块(PyMuPDF 的导入名)
# 虽然库名是 PyMuPDF,但导入时使用 fitz
import fitz
# 说明:打印 PyMuPDF 版本信息
# version 属性返回库的版本号
print(f"PyMuPDF 版本:{fitz.version}")
# 说明:测试创建一个空的 PDF 文档
# fitz.open() 不传参数时创建一个新的空文档
doc = fitz.open()
# 说明:创建一个新页面
# new_page() 方法在文档末尾添加一个新页面
# 参数可以指定页面大小,不传参使用默认大小(A4)
page = doc.new_page()
# 说明:在页面上插入文本
# insert_text() 方法在指定位置插入文本
# 参数:(x, y) 坐标,文本内容,字体大小
page.insert_text((72, 72), "Hello PyMuPDF!", fontsize=16)
# 说明:保存文档到文件
# save() 方法将文档保存到指定的文件路径
doc.save("test.pdf")
# 说明:关闭文档
# close() 方法关闭文档,释放资源
doc.close()
# 说明:打印结果,验证安装是否成功
print("安装成功!已创建测试文件:test.pdf")
# 说明:如果没有报错并创建了 test.pdf 文件,说明安装成功3. 核心概念 #
在使用 PyMuPDF 之前,需要理解几个核心概念。这些概念是理解 PyMuPDF 的基础。
3.1 Document(文档对象) #
Document 是 PDF 文档的对象表示,相当于整个 PDF 文件。你可以通过 fitz.open() 打开已有的 PDF 文件,或创建新的 PDF 文档。
3.2 Page(页面对象) #
Page 是 PDF 文档中的一个页面,类似于书籍中的一页。每个 Page 对象可以独立操作,如提取文本、插入内容等。
3.3 Rect(矩形对象) #
Rect 用于表示页面上的一个矩形区域,用于定位和裁剪。它由四个值组成:左、上、右、下坐标。
3.4 坐标系统 #
PyMuPDF 使用 PDF 的坐标系统:以页面左下角为原点 (0, 0),X 轴向右,Y 轴向上。这与常见的图像坐标系统(左上角为原点)不同。
4. 快速体验:打开和读取 PDF #
让我们通过一个最简单的例子来快速体验 PyMuPDF 的强大功能。
4.1 打开 PDF 文档 #
| 方法/属性 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
fitz.open() |
打开 PDF 文档 | filename:文件路径(可选,不传参创建新文档) |
Document 对象 |
.page_count |
获取文档的页数 | 无(属性) | 整数 |
.metadata |
获取文档的元数据 | 无(属性) | 字典 |
doc[index] |
通过索引访问页面 | index:页面索引(从 0 开始) |
Page 对象 |
.rect |
获取页面的矩形区域 | 无(属性) | Rect 对象 |
.close() |
关闭文档 | 无 | 无 |
# -*- coding: utf-8 -*-
# 说明:快速体验 PyMuPDF - 打开和读取 PDF 文档
# 说明:导入 fitz 模块
# 虽然库名是 PyMuPDF,但导入时使用 fitz
import fitz
# 说明:打开一个 PDF 文档
# fitz.open() 函数打开指定的 PDF 文件
# 返回 Document 对象
doc = fitz.open("example.pdf")
# 说明:获取文档的页数
# page_count 属性返回文档的总页数
page_count = doc.page_count
print(f"PDF 文档总页数:{page_count}")
# 说明:获取文档的元数据
# metadata 属性返回文档的元数据字典
# 包含标题、作者、创建日期等信息
metadata = doc.metadata
print("\n文档元数据:")
for key, value in metadata.items():
print(f" {key}:{value}")
# 说明:遍历每一页并获取页面信息
# 可以通过索引访问页面(从 0 开始)
print("\n页面信息:")
for page_number in range(doc.page_count):
# 说明:通过索引获取页面对象
# doc[0] 表示第一页,doc[1] 表示第二页
page = doc[page_number]
# 说明:获取页面的矩形区域
# rect 属性返回页面的矩形区域(包含宽度和高度)
rect = page.rect
print(f" 第 {page_number + 1} 页:")
print(f" 宽度:{rect.width:.2f} 点")
print(f" 高度:{rect.height:.2f} 点")
# 说明:使用完毕后关闭文档
# close() 方法关闭文档,释放资源
# 这是一个好习惯,可以避免内存泄漏
doc.close()
print("\n文档已关闭")注意:上面的代码需要当前目录中存在
example.pdf文件。如果没有这个文件,代码会报错。在实际使用中,请替换为你自己的 PDF 文件路径。
5. 文本提取:从 PDF 中提取文本 #
PyMuPDF 可以精确提取 PDF 中的文本,包括完整文本、文本块和单词级别的信息。本节介绍如何提取文本。
5.1 提取完整文本 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.get_text() |
提取页面的文本内容 | textpage:是否使用 TextPage 对象(可选) |
字符串 |
# -*- coding: utf-8 -*-
# 说明:演示如何提取 PDF 页面的完整文本
# 说明:导入 fitz 模块
import fitz
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
# 使用索引 0 访问第一页
page = doc[0]
# 说明:提取页面的完整文本
# get_text() 方法提取页面上的所有文本
# 返回一个字符串,包含页面上的所有文本内容
text = page.get_text()
# 说明:打印提取的文本
print("第一页的文本内容:")
print(text)
# 说明:提取所有页面的文本
print("\n所有页面的文本:")
for page_number in range(doc.page_count):
# 说明:获取每一页
page = doc[page_number]
# 说明:提取该页的文本
text = page.get_text()
# 说明:打印页号和文本(只显示前 100 个字符)
print(f"\n第 {page_number + 1} 页(前 100 个字符):")
print(text[:100] if text else "(无文本内容)")
# 说明:关闭文档
doc.close()5.2 提取文本块 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.get_text() |
提取页面的文本,支持多种格式 | format:文本格式("text"、"blocks"、"words"、"dict" 等) |
列表或字符串 |
# -*- coding: utf-8 -*-
# 说明:演示如何提取文本块(包含位置信息)
# 说明:导入 fitz 模块
import fitz
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
page = doc[0]
# 说明:提取文本块
# get_text("blocks") 返回文本块列表
# 每个文本块是一个元组:(x0, y0, x1, y1, "文本内容", 块号, 类型)
# x0, y0, x1, y1 是文本块的坐标(矩形区域)
blocks = page.get_text("blocks")
# 说明:遍历文本块并打印信息
print("文本块信息:")
for idx, block in enumerate(blocks):
# 说明:block 是一个元组,包含文本块的信息
# block[:4] 是坐标(x0, y0, x1, y1)
# block[4] 是文本内容
x0, y0, x1, y1 = block[:4]
text_content = block[4]
print(f"\n文本块 {idx + 1}:")
print(f" 坐标:({x0:.2f}, {y0:.2f}) 到 ({x1:.2f}, {y1:.2f})")
print(f" 内容:{text_content[:50]}...") # 只显示前 50 个字符
# 说明:关闭文档
doc.close()5.3 提取单词级别的信息 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.get_text("words") |
提取单词级别的信息 | 无 | 列表,每个元素是 (x0, y0, x1, y1, "单词", 块号, 行号, 词号) |
# -*- coding: utf-8 -*-
# 说明:演示如何提取单词级别的信息(包含位置)
# 说明:导入 fitz 模块
import fitz
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
page = doc[0]
# 说明:提取单词级别的信息
# get_text("words") 返回单词列表
# 每个单词是一个元组:(x0, y0, x1, y1, "单词", 块号, 行号, 词号)
# x0, y0, x1, y1 是单词的坐标
words = page.get_text("words")
# 说明:打印前 10 个单词的信息
print("前 10 个单词的信息:")
for idx, word in enumerate(words[:10]):
# 说明:word 是一个元组,包含单词的信息
# word[:4] 是坐标(x0, y0, x1, y1)
# word[4] 是单词内容
x0, y0, x1, y1 = word[:4]
word_text = word[4]
print(f"\n单词 {idx + 1}:{word_text}")
print(f" 坐标:({x0:.2f}, {y0:.2f}) 到 ({x1:.2f}, {y1:.2f})")
# 说明:统计单词数量
print(f"\n总共找到 {len(words)} 个单词")
# 说明:关闭文档
doc.close()6. 图片提取:从 PDF 中提取图片 #
PyMuPDF 可以提取 PDF 中嵌入的图片,并保存为 PNG 或 JPG 格式。本节介绍如何提取图片。
6.1 提取并保存图片 #
| 方法/属性 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.get_images() |
获取页面上的所有图片 | 无 | 列表,每个元素是 (xref, smask, width, height, bpc, colorspace, alt.colorspace, name, filter, referencer) |
fitz.Pixmap() |
创建图片对象 | doc:文档对象,xref:图片的交叉引用编号 |
Pixmap 对象 |
.n |
获取颜色通道数 | 无(属性) | 整数 |
.alpha |
是否有透明通道 | 无(属性) | 整数(0 或 1) |
.save() |
保存图片到文件 | filename:文件路径 |
无 |
# -*- coding: utf-8 -*-
# 说明:演示如何从 PDF 中提取图片
# 说明:导入 fitz 模块和 os 模块(用于文件操作)
import fitz
import os
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
page = doc[0]
# 说明:获取页面上的所有图片
# get_images() 方法返回页面上的所有图片信息
# 返回一个列表,每个元素包含图片的元数据
images = page.get_images()
# 说明:打印图片数量
print(f"第一页找到 {len(images)} 张图片")
# 说明:遍历所有图片并保存
for index, img in enumerate(images):
# 说明:img 是一个元组,包含图片的信息
# img[0] 是图片的交叉引用编号(xref),用于获取图片数据
xref = img[0]
# 说明:使用 Pixmap 对象提取图片
# Pixmap(doc, xref) 从文档中提取指定编号的图片
# 返回一个 Pixmap 对象,包含图片的像素数据
pix = fitz.Pixmap(doc, xref)
# 说明:检查图片格式
# pix.n 是颜色通道数(3=RGB, 4=RGBA, 1=灰度)
# pix.alpha 是透明通道标志(0=无透明,1=有透明)
# pix.n - pix.alpha < 4 表示是标准格式(RGB 或灰度),可以直接保存
if pix.n - pix.alpha < 4:
# 说明:保存图片为 PNG 格式
# save() 方法将图片保存到文件
# 文件名包含页号和图片索引
output_path = f"page0_img{index}.png"
pix.save(output_path)
print(f" 图片 {index + 1} 已保存:{output_path}")
else:
# 说明:如果图片格式特殊(如 CMYK),需要转换为 RGB
# 创建一个新的 RGB 图片
pix_rgb = fitz.Pixmap(fitz.csRGB, pix)
output_path = f"page0_img{index}.png"
pix_rgb.save(output_path)
print(f" 图片 {index + 1} 已保存(已转换):{output_path}")
# 说明:释放转换后的图片对象
pix_rgb = None
# 说明:释放图片对象,释放内存
# 这是重要的步骤,可以避免内存泄漏
pix = None
# 说明:提取所有页面的图片
print("\n提取所有页面的图片:")
for page_number in range(doc.page_count):
page = doc[page_number]
images = page.get_images()
for img_index, img in enumerate(images):
xref = img[0]
pix = fitz.Pixmap(doc, xref)
if pix.n - pix.alpha < 4:
output_path = f"page{page_number}_img{img_index}.png"
pix.save(output_path)
print(f" 第 {page_number + 1} 页,图片 {img_index + 1}:{output_path}")
pix = None
# 说明:关闭文档
doc.close()
print("\n图片提取完成")7. 创建和编辑 PDF:创建新文档和修改现有文档 #
PyMuPDF 不仅可以读取 PDF,还可以创建新的 PDF 文档和修改现有文档。本节介绍如何创建和编辑 PDF。
7.1 创建新 PDF 并添加文本 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
fitz.open() |
创建新的空文档 | 无 | Document 对象 |
.new_page() |
在文档末尾添加新页面 | width、height:页面大小(可选) |
Page 对象 |
.insert_text() |
在页面上插入文本 | point:坐标 (x, y),text:文本内容,fontsize:字体大小(可选),color:颜色(可选) |
无 |
.save() |
保存文档到文件 | filename:文件路径 |
无 |
.close() |
关闭文档 | 无 | 无 |
# -*- coding: utf-8 -*-
# 说明:演示如何创建新的 PDF 文档并添加文本
# 说明:导入 fitz 模块
import fitz
# 说明:创建一个新的空 PDF 文档
# fitz.open() 不传参数时创建一个新的空文档
doc = fitz.open()
# 说明:在文档中添加一个新页面
# new_page() 方法在文档末尾添加一个新页面
# 不传参数时使用默认大小(A4,约 595 x 842 点)
page = doc.new_page()
# 说明:在页面上插入文本
# insert_text() 方法在指定位置插入文本
# 参数:(x, y) 坐标,文本内容,字体大小(可选)
# 坐标单位是点(point),72 点 = 1 英寸
# (72, 72) 表示距离左边缘和底边缘各 1 英寸的位置
page.insert_text((72, 72), "Hello PyMuPDF!", fontsize=16)
# 说明:插入更多文本(中文)
# 可以多次调用 insert_text() 添加多个文本
page.insert_text((72, 100), "欢迎使用 PyMuPDF!", fontsize=14)
# 说明:插入带颜色的文本
# color 参数设置文本颜色,格式为 (r, g, b),值范围 0-1
# (1, 0, 0) 表示红色
page.insert_text((72, 128), "这是红色文本", fontsize=12, color=(1, 0, 0))
# 说明:添加多个页面
for i in range(2, 4):
# 说明:创建新页面
new_page = doc.new_page()
# 说明:在新页面上添加内容
new_page.insert_text((72, 72), f"这是第 {i} 页", fontsize=16)
# 说明:保存文档到文件
# save() 方法将文档保存到指定的文件路径
# 如果文件已存在,会被覆盖
doc.save("hello.pdf")
# 说明:关闭文档
# close() 方法关闭文档,释放资源
doc.close()
print("PDF 文档已创建:hello.pdf")
print("文档包含 3 页,每页都有文本内容")7.2 修改现有 PDF(添加文本和绘制) #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.draw_rect() |
在页面上绘制矩形 | rect:矩形区域,color:填充颜色(可选),width:边框宽度(可选),fill:是否填充(可选) |
无 |
.draw_line() |
在页面上绘制直线 | p1:起点坐标,p2:终点坐标,color:线条颜色(可选),width:线条宽度(可选) |
无 |
fitz.Rect() |
创建矩形对象 | x0, y0, x1, y1:矩形的四个坐标 |
Rect 对象 |
# -*- coding: utf-8 -*-
# 说明:演示如何修改现有 PDF(添加文本和绘制图形)
# 说明:导入 fitz 模块
import fitz
# 说明:打开现有的 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
page = doc[0]
# 说明:在页面上插入文本(添加到现有内容上)
page.insert_text((100, 100), "这是添加的文本", fontsize=12, color=(0, 0, 1))
# 说明:绘制一个矩形
# fitz.Rect(x0, y0, x1, y1) 创建一个矩形对象
# x0, y0 是左下角坐标,x1, y1 是右上角坐标
rect = fitz.Rect(50, 50, 200, 150)
# 说明:draw_rect() 方法绘制矩形
# color 参数设置颜色,(1, 0, 0) 表示红色
# fill 参数设置为 True 表示填充矩形
# width 参数设置边框宽度
page.draw_rect(rect, color=(1, 0, 0), fill=True, width=2)
# 说明:绘制一条直线
# draw_line() 方法绘制直线
# p1 是起点坐标,p2 是终点坐标
# color 参数设置线条颜色,(0, 1, 0) 表示绿色
# width 参数设置线条宽度
page.draw_line((100, 200), (300, 300), color=(0, 1, 0), width=3)
# 说明:插入带背景色的文本(通过绘制矩形实现)
text_rect = fitz.Rect(250, 250, 400, 280)
page.draw_rect(text_rect, color=(1, 1, 0), fill=True) # 黄色背景
page.insert_text((260, 260), "带背景的文本", fontsize=10, color=(0, 0, 0))
# 说明:保存修改后的文档
# 保存为新文件,避免覆盖原文件
doc.save("modified.pdf")
# 说明:关闭文档
doc.close()
print("修改后的 PDF 已保存:modified.pdf")8. 合并和拆分 PDF:批量处理 PDF 文件 #
在实际应用中,你可能需要合并多个 PDF 文件或拆分一个大的 PDF 文件。本节介绍如何合并和拆分 PDF。
8.1 合并多个 PDF 文件 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.insert_pdf() |
将另一个 PDF 的页面插入到当前文档 | source:源文档对象,from_page:起始页(可选),to_page:结束页(可选),start_at:插入位置(可选) |
无 |
# -*- coding: utf-8 -*-
# 说明:演示如何合并多个 PDF 文件
# 说明:导入 fitz 模块和 os 模块
import fitz
import os
# 说明:创建一个新的空 PDF 文档用于合并
# fitz.open() 不传参数时创建新文档
merged = fitz.open()
# 说明:要合并的 PDF 文件列表
pdf_files = ["doc1.pdf", "doc2.pdf", "doc3.pdf"]
# 说明:遍历每个 PDF 文件
for pdf_file in pdf_files:
# 说明:检查文件是否存在
if os.path.exists(pdf_file):
# 说明:打开要合并的 PDF 文件
source_doc = fitz.open(pdf_file)
# 说明:将该 PDF 的所有页面插入到合并文档中
# insert_pdf() 方法将源文档的页面插入到当前文档
# 不指定 from_page 和 to_page 时,插入所有页面
merged.insert_pdf(source_doc)
# 说明:关闭源文档
source_doc.close()
print(f"已添加:{pdf_file}({source_doc.page_count} 页)")
else:
print(f"文件不存在:{pdf_file}")
# 说明:保存合并后的文档
merged.save("merged.pdf")
# 说明:关闭合并文档
merged.close()
print(f"\n合并完成!合并后的文档共 {merged.page_count} 页")
print("合并后的文件:merged.pdf")注意:上面的代码需要当前目录中存在
doc1.pdf、doc2.pdf、doc3.pdf文件。在实际使用中,请替换为你自己的 PDF 文件路径。
8.2 拆分 PDF 文件 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.insert_pdf() |
将另一个 PDF 的页面插入到当前文档 | source:源文档对象,from_page:起始页,to_page:结束页 |
无 |
# -*- coding: utf-8 -*-
# 说明:演示如何拆分 PDF 文件(按页拆分)
# 说明:导入 fitz 模块
import fitz
# 说明:打开要拆分的 PDF 文件
source = fitz.open("big.pdf")
# 说明:遍历每一页,将每一页保存为独立的 PDF
for page_number in range(source.page_count):
# 说明:创建一个新的空文档
target = fitz.open()
# 说明:将源文档的当前页插入到新文档
# insert_pdf() 方法的 from_page 和 to_page 参数指定页面范围
# from_page=i, to_page=i 表示只插入第 i 页(从 0 开始)
target.insert_pdf(source, from_page=page_number, to_page=page_number)
# 说明:保存为单独的 PDF 文件
# 文件名包含页码(从 1 开始)
output_filename = f"page_{page_number + 1}.pdf"
target.save(output_filename)
# 说明:关闭新文档
target.close()
print(f"已保存:{output_filename}")
# 说明:关闭源文档
source.close()
print(f"\n拆分完成!共拆分为 {source.page_count} 个文件")注意:上面的代码需要当前目录中存在
big.pdf文件。在实际使用中,请替换为你自己的 PDF 文件路径。
9. PDF 转图片:将 PDF 页面转换为图片 #
PyMuPDF 可以将 PDF 页面转换为图片(PNG 或 JPG),这对于预览 PDF 内容或创建缩略图非常有用。
9.1 PDF 页面转图片 #
| 方法/类 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
fitz.Matrix() |
创建变换矩阵(用于缩放) | sx, sy:X 和 Y 方向的缩放因子 |
Matrix 对象 |
.get_pixmap() |
将页面渲染为图片对象 | matrix:变换矩阵(可选) |
Pixmap 对象 |
.save() |
保存图片到文件 | filename:文件路径 |
无 |
# -*- coding: utf-8 -*-
# 说明:演示如何将 PDF 页面转换为图片
# 说明:导入 fitz 模块和 os 模块
import fitz
import os
# 说明:定义转换函数
def pdf_to_images(pdf_path: str, output_dir: str = "output_images", dpi: int = 150):
"""
将 PDF 的每一页转换为图片
参数:
pdf_path (str): PDF 文件路径
output_dir (str): 输出目录
dpi (int): 图片分辨率(每英寸点数)
"""
# 说明:创建输出目录(如果不存在)
# os.makedirs() 创建目录,exist_ok=True 表示如果目录已存在不报错
os.makedirs(output_dir, exist_ok=True)
# 说明:打开 PDF 文档
doc = fitz.open(pdf_path)
# 说明:遍历每一页
for page_number in range(doc.page_count):
# 说明:获取当前页
page = doc[page_number]
# 说明:创建变换矩阵(用于缩放)
# Matrix(sx, sy) 创建变换矩阵
# dpi / 72 是将分辨率从点(point)转换为像素(pixel)
# 72 点是 1 英寸,如果 dpi=150,则 150/72 就是缩放因子
matrix = fitz.Matrix(dpi / 72, dpi / 72)
# 说明:将页面渲染为图片对象
# get_pixmap() 方法将页面渲染为 Pixmap 对象
# matrix 参数指定缩放比例
pix = page.get_pixmap(matrix=matrix)
# 说明:保存图片
# save() 方法将图片保存为文件
# 文件名包含页码(从 1 开始)
output_path = os.path.join(output_dir, f"page_{page_number + 1}.png")
pix.save(output_path)
print(f"已保存:{output_path}")
# 说明:释放图片对象,释放内存
pix = None
# 说明:关闭文档
doc.close()
print(f"\n转换完成!共转换 {doc.page_count} 页")
# 说明:调用函数进行转换
# 如果文件存在,进行转换
if os.path.exists("document.pdf"):
pdf_to_images("document.pdf", "output_images", dpi=150)
else:
# 说明:创建一个示例 PDF 用于演示
print("创建示例 PDF...")
doc = fitz.open()
page = doc.new_page()
page.insert_text((72, 72), "示例 PDF 文档", fontsize=20)
doc.save("document.pdf")
doc.close()
# 说明:转换示例 PDF
pdf_to_images("document.pdf", "output_images", dpi=150)10. 搜索和高亮:在 PDF 中搜索文本并添加高亮 #
PyMuPDF 可以在 PDF 中搜索文本,并添加高亮、下划线等注释。这对于标注重要内容非常有用。
10.1 搜索文本并添加高亮 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.search_for() |
在页面中搜索文本 | text:要搜索的文本 |
列表,包含匹配的矩形区域 |
.add_highlight_annot() |
添加高亮注释 | rect:要高亮的矩形区域 |
Annotation 对象 |
.add_underline_annot() |
添加下划线注释 | rect:要添加下划线的矩形区域 |
Annotation 对象 |
.update() |
更新注释(应用更改) | 无 | 无 |
# -*- coding: utf-8 -*-
# 说明:演示如何在 PDF 中搜索文本并添加高亮
# 说明:导入 fitz 模块
import fitz
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:要搜索的文本
search_text = "重要内容"
# 说明:遍历每一页,搜索并高亮文本
for page_number in range(doc.page_count):
# 说明:获取当前页
page = doc[page_number]
# 说明:在页面中搜索文本
# search_for() 方法在页面中搜索指定的文本
# 返回一个列表,包含所有匹配文本的矩形区域
areas = page.search_for(search_text)
# 说明:如果有匹配的文本
if areas:
print(f"第 {page_number + 1} 页找到 {len(areas)} 处匹配")
# 说明:遍历每个匹配的区域
for rect in areas:
# 说明:添加高亮注释
# add_highlight_annot() 方法添加高亮注释
# 参数是文本所在的矩形区域
highlight = page.add_highlight_annot(rect)
# 说明:设置高亮颜色(可选)
# set_colors() 方法设置注释的颜色
# stroke 参数设置高亮颜色(RGB 格式,值范围 0-1)
highlight.set_colors(stroke=[1, 1, 0]) # 黄色高亮
# 说明:更新注释,应用更改
# update() 方法应用注释更改
highlight.update()
# 说明:添加下划线注释(可选)
# add_underline_annot() 方法添加下划线注释
underline = page.add_underline_annot(rect)
underline.update()
else:
print(f"第 {page_number + 1} 页未找到匹配文本")
# 说明:保存修改后的文档
doc.save("annotated.pdf")
# 说明:关闭文档
doc.close()
print("\n高亮完成!已保存到:annotated.pdf")10.2 添加文本注释 #
| 方法 | 功能说明 | 常用参数 | 返回值 |
|---|---|---|---|
.add_text_annot() |
添加文本注释 | point:注释位置坐标,text:注释文本,icon:图标类型(可选) |
Annotation 对象 |
.add_rect_annot() |
添加矩形注释 | rect:矩形区域 |
Annotation 对象 |
.set_colors() |
设置注释的颜色 | stroke:边框颜色(可选),fill:填充颜色(可选) |
无 |
# -*- coding: utf-8 -*-
# 说明:演示如何添加文本注释和矩形注释
# 说明:导入 fitz 模块
import fitz
# 说明:打开 PDF 文档
doc = fitz.open("example.pdf")
# 说明:获取第一页
page = doc[0]
# 说明:添加文本注释
# add_text_annot() 方法在指定位置添加文本注释
# 参数:位置坐标 (x, y),注释文本,图标类型
# icon="Note" 表示使用便签图标
annot = page.add_text_annot((100, 100), "这是注释内容", icon="Note")
# 说明:设置注释颜色
# set_colors() 方法设置注释的颜色
# stroke 参数设置边框颜色(RGB 格式)
annot.set_colors(stroke=[0, 0, 1]) # 蓝色边框
# 说明:更新注释
annot.update()
# 说明:添加矩形注释
# fitz.Rect(x0, y0, x1, y1) 创建矩形区域
# add_rect_annot() 方法添加矩形注释
rect = fitz.Rect(50, 50, 150, 100)
rect_annot = page.add_rect_annot(rect)
# 说明:设置矩形注释的颜色
# stroke 参数设置边框颜色
rect_annot.set_colors(stroke=[1, 0, 0]) # 红色边框
# 说明:更新矩形注释
rect_annot.update()
# 说明:保存修改后的文档
doc.save("annot_with_rect.pdf")
# 说明:关闭文档
doc.close()
print("注释已添加!已保存到:annot_with_rect.pdf")11. 实战案例:PDF 处理工具 #
下面是一个完整的实战案例,综合运用前面学到的知识,创建一个简单的 PDF 处理工具。
| 方法/属性 | 功能说明 |
|---|---|
fitz.open() |
打开或创建 PDF 文档 |
.page_count |
获取页数 |
.get_text() |
提取文本 |
.get_images() |
获取图片 |
.new_page() |
创建新页面 |
.insert_text() |
插入文本 |
.save() |
保存文档 |
.close() |
关闭文档 |
# -*- coding: utf-8 -*-
# 说明:实战案例 - PDF 处理工具
# 综合运用前面学到的所有知识,创建一个 PDF 处理工具
# 说明:导入所需的库
import fitz
import os
from datetime import datetime
def pdf_info(pdf_path: str):
"""
显示 PDF 文件的基本信息
参数:
pdf_path (str): PDF 文件路径
"""
# 说明:打开 PDF 文档
doc = fitz.open(pdf_path)
# 说明:获取基本信息
print(f"文件名:{pdf_path}")
print(f"页数:{doc.page_count}")
print(f"文件大小:{os.path.getsize(pdf_path) / 1024:.2f} KB")
# 说明:获取元数据
print("\n元数据:")
for key, value in doc.metadata.items():
if value:
print(f" {key}:{value}")
# 说明:统计文本和图片
total_text_length = 0
total_images = 0
for page_number in range(doc.page_count):
page = doc[page_number]
text = page.get_text()
total_text_length += len(text)
images = page.get_images()
total_images += len(images)
print(f"\n统计信息:")
print(f" 总文本长度:{total_text_length} 个字符")
print(f" 总图片数:{total_images} 张")
# 说明:关闭文档
doc.close()
def extract_all_text(pdf_path: str, output_path: str):
"""
提取 PDF 中的所有文本并保存到文件
参数:
pdf_path (str): PDF 文件路径
output_path (str): 输出文件路径
"""
# 说明:打开 PDF 文档
doc = fitz.open(pdf_path)
# 说明:提取所有文本
all_text = []
for page_number in range(doc.page_count):
page = doc[page_number]
text = page.get_text()
all_text.append(f"=== 第 {page_number + 1} 页 ===\n{text}\n")
# 说明:保存到文件
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n".join(all_text))
# 说明:关闭文档
doc.close()
print(f"文本已提取到:{output_path}")
def create_watermarked_pdf(source_path: str, watermark_text: str, output_path: str):
"""
创建带水印的 PDF
参数:
source_path (str): 源 PDF 文件路径
watermark_text (str): 水印文本
output_path (str): 输出文件路径
"""
# 说明:打开源 PDF
doc = fitz.open(source_path)
# 说明:遍历每一页,添加水印
for page_number in range(doc.page_count):
page = doc[page_number]
# 说明:获取页面中心位置
rect = page.rect
center_x = rect.width / 2
center_y = rect.height / 2
# 说明:在页面中心插入水印文本
# color 参数设置文本颜色为浅灰色
# 可以调整透明度
page.insert_text(
(center_x - 50, center_y),
watermark_text,
fontsize=20,
color=(0.8, 0.8, 0.8) # 浅灰色
)
# 说明:保存带水印的 PDF
doc.save(output_path)
# 说明:关闭文档
doc.close()
print(f"带水印的 PDF 已保存到:{output_path}")
# 说明:主程序入口
if __name__ == "__main__":
# 说明:创建一个示例 PDF 用于演示
print("创建示例 PDF...")
doc = fitz.open()
page = doc.new_page()
page.insert_text((72, 72), "这是示例 PDF 文档", fontsize=16)
page.insert_text((72, 100), "包含重要内容", fontsize=12)
doc.save("example_demo.pdf")
doc.close()
print("示例 PDF 已创建:example_demo.pdf\n")
# 说明:显示 PDF 信息
print("=" * 50)
print("PDF 信息:")
print("=" * 50)
pdf_info("example_demo.pdf")
# 说明:提取文本
print("\n" + "=" * 50)
print("提取文本:")
print("=" * 50)
extract_all_text("example_demo.pdf", "extracted_text.txt")
# 说明:创建带水印的 PDF
print("\n" + "=" * 50)
print("创建带水印的 PDF:")
print("=" * 50)
create_watermarked_pdf("example_demo.pdf", "水印文本", "watermarked.pdf")
print("\n处理完成!")12. 常见问题与排查 #
在使用 PyMuPDF 时,可能会遇到一些常见问题。本节提供解决方案。
12.1 文件不存在错误 #
问题:打开 PDF 文件时提示文件不存在。
解决方案:
# -*- coding: utf-8 -*-
# 说明:演示如何处理文件不存在的情况
# 说明:导入 fitz 模块和 os 模块
import fitz
import os
# 说明:方法1:使用 try-except 捕获异常
pdf_path = "example.pdf"
try:
# 说明:尝试打开 PDF 文件
doc = fitz.open(pdf_path)
print(f"成功打开:{pdf_path}")
doc.close()
except FileNotFoundError:
# 说明:如果文件不存在,捕获异常
print(f"错误:文件 {pdf_path} 不存在")
except Exception as e:
# 说明:捕获其他可能的错误
print(f"错误:{e}")
# 说明:方法2:先检查文件是否存在
pdf_path = "example.pdf"
if os.path.exists(pdf_path):
# 说明:文件存在,可以安全打开
doc = fitz.open(pdf_path)
print(f"成功打开:{pdf_path}")
doc.close()
else:
# 说明:文件不存在,提示用户
print(f"错误:文件 {pdf_path} 不存在")
print("请检查文件路径是否正确")12.2 内存管理问题 #
问题:处理大文件时内存占用过高。
解决方案:
# -*- coding: utf-8 -*-
# 说明:演示如何正确处理大文件,避免内存泄漏
# 说明:导入 fitz 模块
import fitz
def process_large_pdf(pdf_path: str):
"""
逐页处理大 PDF 文件(推荐方式)
参数:
pdf_path (str): PDF 文件路径
"""
# 说明:打开 PDF 文档
doc = fitz.open(pdf_path)
# 说明:逐页处理,避免一次性加载所有页面
for page in doc:
# 说明:处理当前页
text = page.get_text()
# 说明:处理文本(这里只是示例)
# 在实际应用中,可以对文本进行分析、提取等操作
print(f"处理第 {page.number + 1} 页,文本长度:{len(text)}")
# 说明:如果需要处理图片,也要及时释放
images = page.get_images()
for img in images:
xref = img[0]
pix = fitz.Pixmap(doc, xref)
# 说明:处理图片...
# 处理完后立即释放
pix = None
# 说明:处理完成后关闭文档
doc.close()
print("处理完成")
# 说明:调用函数
try:
process_large_pdf("large.pdf")
except FileNotFoundError:
print("文件不存在,创建示例文件...")
# 说明:创建示例文件用于演示
doc = fitz.open()
for i in range(5):
page = doc.new_page()
page.insert_text((72, 72), f"第 {i+1} 页", fontsize=16)
doc.save("large.pdf")
doc.close()
process_large_pdf("large.pdf")12.3 中文显示问题 #
问题:提取的中文文本显示乱码。
解决方案:
# -*- coding: utf-8 -*-
# 说明:演示如何处理中文文本提取
# 说明:导入 fitz 模块
import fitz
# 说明:打开包含中文的 PDF
doc = fitz.open("chinese.pdf")
# 说明:提取文本
for page_number in range(doc.page_count):
page = doc[page_number]
# 说明:方法1:直接提取文本(通常可以正确处理中文)
text = page.get_text()
# 说明:如果出现乱码,可能需要指定编码
# 大多数情况下,PyMuPDF 会自动处理编码
# 说明:保存文本时指定 UTF-8 编码
if text:
print(f"第 {page_number + 1} 页文本:")
print(text[:100]) # 只显示前 100 个字符
# 说明:关闭文档
doc.close()
print("\n提示:")
print(" - 如果中文显示乱码,确保文件保存时使用 UTF-8 编码")
print(" - 使用 open(file, 'w', encoding='utf-8') 打开文件进行写入")