文档管理

文档管理

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 错误时引发

另请参阅

 中文