如何在 Python 环境中裁剪 PDF 页面

在处理扫描件、截图导出的 PDF 或带有大量留白的报告时,我们常常只关心其中的某一部分内容:比如一页里右下角的图表、被白边包围的正文,或者某一栏表格。直接保留整页不仅浪费版面,也会让后续的阅读、打印和归档变得低效。

"裁剪"的本质,是把页面中不需要的区域切掉,只保留我们关注的矩形范围。借助 Spire.PDF,我们可以通过"在新页面上重绘源页内容并做偏移"的方式,精确地按任意矩形裁剪页面,也可以快速裁掉四周的空白边距。下面以具体示例说明几种常用做法。

环境准备

开始之前,先通过 pip 安装 Spire.PDF:

bash 复制代码
pip install Spire.PDF

随后在脚本中引入所需的命名空间。后续所有示例都围绕 PdfDocumentPdfPageBase 展开。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

按指定矩形区域裁剪

最通用的需求是:给定一个矩形(左上角坐标加宽高),把源页中落在这个矩形内的部分单独保存成一个新页面。实现思路是新建一个与目标矩形等大的新页面,再用 DrawTemplate 把源页内容按矩形原点向左上偏移绘制,这样矩形区域就会恰好对齐到新页面的左上角,矩形以外的部分被自然裁掉。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

input_file = "/示例.pdf"
output_file = "/裁剪pdf.pdf"

# 创建一个 PdfDocument 对象并加载 PDF 文件
doc = PdfDocument()
doc.LoadFromFile(input_file)

# 取第一页作为裁剪对象
page = doc.Pages[0]

# 定义裁剪区域:左上角坐标与宽高,单位均为"点"(1 英寸 = 72 点)
crop_x, crop_y = 50.0, 80.0
crop_w, crop_h = 400.0, 500.0

# 新建一个与目标裁剪尺寸一致、无边距的页面
new_doc = PdfDocument()
new_page = new_doc.Pages.Add(SizeF(crop_w, crop_h), PdfMargins(0.0))

# 把源页模板按裁剪原点偏移绘制,使目标区域对齐到新页面左上角
new_page.Canvas.DrawTemplate(page.CreateTemplate(), PointF(-crop_x, -crop_y))

# 保存修改后的 PDF 文件
new_doc.SaveToFile(output_file)
new_doc.Close()
doc.Close()

这里有几个值得注意的点。SizeF 用于描述新页面的尺寸,必须和裁剪矩形保持一致,否则内容会被缩放或留白;PdfMargins(0.0) 确保新页面没有额外边距;而 PointF(-crop_x, -crop_y) 中的负号很关键------它把源页整体向左上平移,让 (crop_x, crop_y) 这个点正好落到新页面的 (0, 0) 处。只要调整这四个数值,就能裁剪出页面上任意位置的子区域。

裁剪掉四周的空白边距

很多 PDF 的页面四周都自带较大的白边,正文只占中间一部分。如果每一页的边距大致均匀,我们可以利用文档的 PageSettings.Margins 直接算出内容区尺寸,再把内容按边距偏移重绘,从而一次性裁掉所有页面的白边。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

input_file = "/示例.pdf"
output_file = "移除页边距.pdf"

# 创建 PdfDocument 对象并加载 PDF 文件
doc = PdfDocument()
doc.LoadFromFile(input_file)

# 获取页边距并创建新的 PdfDocument 对象
new_doc = PdfDocument()
margins = doc.PageSettings.Margins
left, right = margins.Left, margins.Right
top, bottom = margins.Top, margins.Bottom

for i in range(doc.Pages.Count):
    page = doc.Pages.get_Item(i)
    # 新页面尺寸 = 原尺寸减去四周边距
    w = page.Size.Width - left - right
    h = page.Size.Height - top - bottom
    new_page = new_doc.Pages.Add(SizeF(w, h), PdfMargins(0.0))
    # 向左上偏移绘制,从而裁掉边距区域
    new_page.Canvas.DrawTemplate(page.CreateTemplate(), PointF(-left, -top))

# 保存修改后的 PDF 文件
new_doc.SaveToFile(output_file)
new_doc.Close()
doc.Close()

这种方法的核心在于 page.Size 拿到的是包含边距的整页尺寸,而 doc.PageSettings.Margins 描述的是白边的厚度。两者相减得到真正的内容区,再用 PointF(-left, -top) 把内容重新对齐到原点,就完成了"去白边"式的裁剪。它对批量处理多页文档尤其方便,因为循环里每一页都套用同样的逻辑。

读取页面尺寸与裁剪框以确认结果

裁剪前后,我们有时还需要知道页面到底有多大、当前可见区域(裁剪框)落在什么位置。PDF 的每一页都带有 MediaBox(物理尺寸)和 CropBox(当前可见区域)等边界框,PdfPageBase 把它们作为可读属性暴露出来,可以用来核对裁剪是否生效,也能作为下一次裁剪的输入。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

doc = PdfDocument()
doc.LoadFromFile("Sample.pdf")
page = doc.Pages[0]

# MediaBox 是页面物理尺寸,CropBox 是当前可见(裁剪后)区域
print("MediaBox:", page.MediaBox.Width, page.MediaBox.Height)
print("CropBox:", page.CropBox.Width, page.CropBox.Height,
      page.CropBox.X, page.CropBox.Y)
print("实际尺寸:", page.ActualSize.Width, page.ActualSize.Height)
doc.Close()

通过对比裁剪前后的 MediaBoxCropBox(或重绘后新页面的 ActualSize),可以直观地验证页面确实被缩小到了目标区域。如果源文档本身已经设置了 CropBox,它的 (X, Y) 还能直接当作裁剪矩形的起点来用,省去手动测量坐标的步骤。

实用技巧

掌握了裁剪的基本手法后,还可以结合实际场景做进一步扩展。

批量裁剪整个文件夹

把上面的"按矩形裁剪"逻辑包进一个遍历目录的循环,就能对某个文件夹下的所有 PDF 统一裁剪。下面示例用 os 模块批量处理,并为每个输出文件加上前缀避免重名:

python 复制代码
import os
from spire.pdf.common import *
from spire.pdf import *

folder = "./pdfs"
for name in os.listdir(folder):
    if not name.lower().endswith(".pdf"):
        continue
    doc = PdfDocument()
    doc.LoadFromFile(os.path.join(folder, name))
    page = doc.Pages[0]
    new_doc = PdfDocument()
    new_page = new_doc.Pages.Add(SizeF(400.0, 500.0), PdfMargins(0.0))
    new_page.Canvas.DrawTemplate(page.CreateTemplate(), PointF(-50.0, -80.0))
    out = os.path.join(folder, "cropped", f"cropped-{name}")
    new_doc.SaveToFile(out)
    new_doc.Close()
    doc.Close()

用厘米指定裁剪区域

前面示例的坐标都以"点"为单位,阅读和调试不够直观。可以借助 PdfUnitConvertor 把厘米换算成点,让裁剪矩形以更熟悉的尺寸单位来表达:

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

cvtr = PdfUnitConvertor()
# 把 2 厘米换算为点
pt = cvtr.ConvertUnits(2.0, PdfGraphicsUnit.Centimeter, PdfGraphicsUnit.Point)
print("2 厘米 =", pt, "点")

这样在定义 crop_xcrop_ycrop_wcrop_h 时,先统一用厘米描述,再用 ConvertUnits 转成点传入 SizeFPointF,代码可读性会更好,也更不容易算错比例。

小结

本文介绍了使用 Python 裁剪 PDF 页面的两种主要方式:按任意矩形区域精确裁剪,以及裁掉四周统一白边。两者的共同原理都是"新建一个目标尺寸的新页面,再用 DrawTemplate 把源页内容按偏移量重绘",区别在于矩形坐标的来源------前者由我们手动指定,后者从 PageSettings.Margins 自动推导。

再配合 CropBox/MediaBox 的读取做结果核对,以及 osPdfUnitConvertor 等标准工具做批量与单位换算,裁剪流程就能轻松扩展到日常自动化场景。如果你的目标是"隐藏"而非"删除"页外内容,也可以直接调整页面的 CropBox 来定义可见区域,按需选择即可。

相关推荐
威联通网络存储19 分钟前
TS-h2287XU-RP 在医药制造批记录与合规归档场景的部署
python·制造
蓝鸟197426 分钟前
Python Flask + Oracle 接口开发 小白完整版笔记(入参/查库/批量/JSON/避坑)
python·oracle·flask
正在走向自律32 分钟前
从 Python 基础到大模型落地:读《深入浅出 Python 人工智能》,吃透 AI 工程化 CRUD 实战
开发语言·人工智能·python·机器学习·知识脉络
171320330计算机毕设编程34 分钟前
2027计算机毕设五大方向对比&选题推荐
java·ide·python·算法·django·php·推荐算法
Zane199436 分钟前
去重用 set 到底能快多少?实测差距接近三百倍
后端·python
岁月宁静38 分钟前
二、《从零手撸 Agent》 — 聊聊 LLM 的失忆真相
前端·python·agent
0xBADCODE1 小时前
动态DEX加载+反射+DES硬编码密钥:安卓三层逆向实战
android·java·python·安全·网络安全·逆向·ctf
岁月宁静1 小时前
一、《从零手撸 Agent》 我用 10 行代码跑通了第一次大模型调用(顺便踩了 4 个坑)
前端·python·agent
大连好光景1 小时前
【AI Agent案例开发项目01解读】
chrome·python·langchain