文档管理
文档管理
The Document 类是几乎所有在 Aspose.PDF FOSS for Python 中操作的入口点:创建新 PDF、加载已有 PDF、编辑其页面以及将结果写回。本指南将逐步介绍文档生命周期、页面集合操作、优化、多文件工作流、加密以及你应当处理的异常。
文档生命周期:创建、打开和保存
Document() 在没有参数时创建一个空的内存文档。将文件路径、原始 bytes,或任何可读取的二进制流作为第一个参数传入(或显式调用 load_from())即可加载已有的 PDF。save() 接受路径或可写的二进制流,例如 io.BytesIO。
from aspose_pdf import Document
# Create a new, empty document and add a blank page
doc = Document()
doc.pages.add()
doc.save("hello.pdf")
# Re-open it — a path, bytes, or a binary stream all work
reopened = Document("hello.pdf")
print(reopened.page_count) # 1
# ...or load explicitly onto an existing instance
another = Document()
another.load_from("hello.pdf")
reopened.close()
another.dispose()
doc.dispose()save() 在目标路径已存在时会抛出 FileExistsError,除非你传入 overwrite=True。close() 是 dispose() 的别名;两者都是幂等的,因此多次调用是安全的。
仅实现 PDF 作为保存目标——这是库的核心、完整功能。将 SaveFormat.PPTX 或 DocFormat.HTML 等导出值传递给 save() 会抛出 UnsupportedFeatureException,而不是写入错误标签的文件,因此导出失败总是会显式报错,而不会悄悄失败。
管理页面集合
doc.pages 是一个 PageCollection。它支持 len()、迭代和基于 0 的索引(doc.pages[0]),以及用于结构编辑的 add()、insert(index, page) 和 delete(index)。每个 Page 都公开 index、rect(即 MediaBox)和 rotation。
from aspose_pdf import Document
doc = Document()
doc.pages.add() # page 0
doc.pages.add() # page 1
doc.pages.insert(1, None) # insert a blank page at index 1
print(doc.page_count) # 3
first_page = doc.pages[0]
print(first_page.rect) # (0, 0, 612, 792)
for page in doc.pages:
print(page.index, page.rotation)
doc.pages.delete(1) # remove the page we inserted
doc.save("pages_demo.pdf", overwrite=True)pages.add(page=None) 在未传入参数时会追加一个空白页。pages.insert() 会将超出范围的索引夹到最近的有效位置,而不是抛出异常。
优化与压缩文档
Document.optimize 在一次调用中执行图像/流去重、未使用对象的垃圾回收以及流压缩。传入一个 OptimizationOptions 实例以控制运行的技术;如果省略,则使用标准的清理配置文件。
from aspose_pdf import Document, OptimizationOptions
doc = Document("large_report.pdf")
options = OptimizationOptions()
options.remove_unused_objects = True
options.link_duplicate_streams = True
options.image_compression_quality = 60
options.subset_fonts = True
doc.optimize(options)
doc.save("large_report_optimized.pdf", overwrite=True)optimize_resources() 是 optimize() 的别名。如果只想进行流压缩而不进行结构清理,请直接调用 doc.compress_streams()。
合并、拆分与编辑文件
对于已经打开的文档,Document.merge() 会将其他 Document 实例追加到当前文档中:
from aspose_pdf import Document
base = Document("part1.pdf")
extra = Document("part2.pdf")
base.merge(extra)
base.save("combined.pdf", overwrite=True)对于无需自行打开 Document 的文件对文件工作流,低代码 Merger 与 Splitter 插件接受一个由 FileDataSource 输入和输出构建的 MergeOptions/SplitOptions 对象:
from aspose_pdf import Merger, MergeOptions, Splitter, SplitOptions, FileDataSource
# Merge two files into one
merge_options = MergeOptions()
merge_options.add_input(FileDataSource("part1.pdf"))
merge_options.add_input(FileDataSource("part2.pdf"))
merge_options.add_output(FileDataSource("combined.pdf"))
Merger().process(merge_options)
# Split the result into one file per page
split_options = SplitOptions()
split_options.add_input(FileDataSource("combined.pdf"))
split_options.add_output(FileDataSource("combined_page1.pdf"))
split_options.add_output(FileDataSource("combined_page2.pdf"))
Splitter().process(split_options)PdfFileEditor 提供相同系列的操作,作为一种门面返回 True/False 而不是抛出异常,这对批处理脚本很方便:
from aspose_pdf import PdfFileEditor
with PdfFileEditor() as editor:
ok = editor.concatenate(["part1.pdf", "part2.pdf"], "combined.pdf")
if not ok:
print("concatenate failed:", editor.last_exception)
editor.extract("combined.pdf", "first_page_only.pdf", page_from=1, page_to=1)PdfFileEditor.extract() and .insert() take 基于1的 页码,不同于 PageCollection的0基索引 — 参见 常见问题 如下。
加密与文档安全
Document.encrypt(user_password, owner_password=None, permissions=-4) 对内存中的文档进行加密;decrypt(password) 和 change_passwords(old, new_user, new_owner=None) 逆转或轮换密码。is_encrypted 和 permissions 报告当前状态。
from aspose_pdf import Document
from aspose_pdf.exceptions import PdfSecurityException
doc = Document()
doc.pages.add()
doc.encrypt("user-pass", "owner-pass", permissions=-4)
doc.save("secured.pdf", overwrite=True)
try:
Document("secured.pdf", password="wrong-pass")
except PdfSecurityException as exc:
print("could not open:", exc)
reopened = Document("secured.pdf", password="user-pass")
print(reopened.is_encrypted) # True
reopened.decrypt("user-pass")
reopened.save("unsecured.pdf", overwrite=True)在未提供密码或密码错误的情况下打开加密文档会抛出 PdfSecurityException —— 捕获此类(来自 aspose_pdf.exceptions),而不要假设加载总是成功。
元数据、验证与异常处理
文档元数据位于 doc.info(一个普通的 dict[str, str])上,doc.version / doc.id 揭示 PDF 头部版本和尾部文件标识符。validate()(别名为 check())报告结构完整性;repair() 尝试修复常见问题,例如缺失的页面列表或超出范围的 MediaBox。
from aspose_pdf import Document, PdfLoadLimits
from aspose_pdf.exceptions import AsposePdfException, PdfIOException
doc = Document()
doc.pages.add()
doc.info["Title"] = "Quarterly Report"
doc.info["Author"] = "Reporting Bot"
doc.save("report.pdf", overwrite=True)
# Load untrusted input under an explicit resource-limit policy
safe_limits = PdfLoadLimits(max_input_bytes=50 * 1024 * 1024, max_pages=1000)
try:
untrusted = Document("incoming.pdf", limits=safe_limits)
if not untrusted.validate():
untrusted.repair()
except (AsposePdfException, PdfIOException) as exc:
print("failed to process incoming.pdf:", exc)PdfLoadLimits 对不受信任的文件限制内存和对象数量;当你完全信任来源时,调用 PdfLoadLimits.unlimited() 可关闭所有限制。AsposePdfException 是整个异常层次结构的基类(包括 PdfIOException 和 PdfSecurityException),因此捕获单个 except AsposePdfException 即可捕获所有库抛出的错误。
技巧与最佳实践
- 在完成后,总是对
Document调用dispose()(或close()),或者将其用作短生命周期的局部变量——引擎会在释放之前将已解码的页面内容和图像保存在内存中。 - 在同一次运行中重新写入已创建的路径时,将
overwrite=True传递给save();默认值为False,并会抛出FileExistsError。 - 在发布生成的 PDF 之前优先使用
doc.optimize()——它只需一次调用即可删除未使用的对象并压缩流,通常会显著缩小输出文件大小。 - 每当从不受信任的来源(上传、电子邮件附件、网络抓取)加载 PDF 时,都要显式设置
PdfLoadLimits策略;默认值虽宽松但有限,不能盲目依赖其作为安全边界。 - 在 load/save 调用周围捕获
AsposePdfException(或诸如PdfSecurityException的特定子类),而不是捕获裸Exception——它是库抛出的所有错误的公共基类。
常见问题
| 问题 | 原因 | 修复 |
|---|---|---|
FileExistsError 在 save() | 目标路径已存在,且 overwrite 保持为默认的 False | 传递 save(path, overwrite=True) |
UnsupportedFeatureException 在 save() | 请求了非 PDF 的 save_format(例如 SaveFormat.PPTX、DocFormat.HTML) | 另存为 PDF — 任何其他的 SaveFormat/DocFormat 值都会触发 UnsupportedFeatureException,而不是写入输出 |
PdfSecurityException: Password required for encrypted document | 在没有提供 password 参数的情况下打开了加密的 PDF | 传递 Document(path, password="...") 或调用 load_from(path, password="...") |
在 PageCollection 与 PdfFileEditor 之间的页码出现 off-by-one 偏差 | doc.pages[i] 采用 0 起始;PdfFileEditor.extract()/.insert() 页参数采用 1 起始 | 在两个 API 之间转换时加 1 或减 1 |
IndexError: Page index out of range. 来自 pages.delete() | 传递给 delete() 的索引在集合中不存在 | 删除前请检查 doc.page_count(或 len(doc.pages)) |
FAQ
我需要许可证才能在 Python 中使用 Aspose.PDF FOSS 吗?
不。这是开源(MIT 许可证)版本;无需许可证文件或激活步骤进行配置。
我可以将 Document 导出为 PDF 之外的格式吗?
此版本不支持。save() 仅实现 PDF 输出——传入其他 SaveFormat/DocFormat 值会抛出 UnsupportedFeatureException,而不是生成错误标记的文件。
Document.merge() 与 Merger 插件之间有什么区别?
Document.merge() 将您已经在内存中打开的 Document 实例合并。Merger(配合 MergeOptions 和 FileDataSource)是一个文件到文件的便捷包装器,能够在一次调用中打开、合并并保存——对从不需要中间 Document 对象的简易批处理脚本非常有用。
为什么 pages.insert() 永远不会对超出范围的索引抛出异常?
PageCollection.insert() 将索引限制在有效范围内(负值会变为 0,超出结尾的值会变为 len(doc.pages)),而不是抛出异常,因此插入操作不会仅因索引值而失败。
我该如何安全地从不受信任的来源加载 PDF?
构造一个 PdfLoadLimits,并使用显式上限(max_input_bytes、max_pages、max_objects 等),将其作为 limits= 参数传递给 Document(...) 或 load_from()。每个字段已经默认有限的值,但将它们收紧到预期的输入大小可以减少畸形文件可能消耗的资源。
API Reference 概要
| 类 / 方法 | 描述 |
|---|---|
Document() / Document.load_from | 创建一个空文档或从路径、字节或二进制流加载文档 |
Document.save | 将文档写入路径或可写流(仅限 PDF) |
Document.dispose() / Document.close() | 释放引擎资源;幂等 |
Document.pages | 文档的 PageCollection |
Document.info | 文档元数据作为 dict[str, str] |
Document.optimize / Document.optimize_resources / Document.compress_streams() | 删除未使用的资源并压缩流 |
Document.merge() | 将其他 Document 实例追加到此 |
Document.encrypt / Document.decrypt / Document.change_passwords | 应用、删除或轮换文档密码 |
Document.validate() / Document.check() / Document.repair() | 检查并尝试修复结构完整性 |
PageCollection.add() / .insert() / .delete() / .item() | 结构化页面集合编辑(0 基准) |
Page.rect / Page.rotation / Page.index | 每页几何和位置 |
OptimizationOptions | 由 Document.optimize 使用的细粒度标志 |
MergeOptions / Merger | 文件到文件合并插件 |
SplitOptions / Splitter | 文件对文件的每页输出拆分插件 |
FileDataSource | 插件 API 的基于文件的输入/输出 |
PdfFileEditor | 针对 concatenate()、extract()、insert()、delete()、append() 的外观(基于 1 的页码) |
PdfLoadLimits | 针对不可信输入的不可变资源限制策略 |
AsposePdfException | 库中抛出的所有异常的基类 |
PdfSecurityException | 因缺失或错误的密码以及权限错误而引发 |
PdfIOException | 在 PDF 处理期间的 I/O 错误时引发 |
另请参阅
- API Reference: 完整的类和方法文档
aspose_pdf - 知识库: 任务导向的操作指南
- 产品概述: 功能与能力概述
- 入门 / 安装: 安装与设置
- Aspose.PDF for Python — Enterprise Documentation