在 Python 中创建和修改 PDF 文件

举报
Yuchuan 发表于 2021/12/13 18:30:39 2021/12/13
【摘要】 在本教程中,您学习了如何使用PyPDF2和reportlab包创建和修改 PDF 文件。如果您想学习刚刚看到的示例,请务必单击以下链接下载材料: 通过PyPDF2,您学会了如何: 使用该类阅读PDF 文件并提取文本PdfFileReader 使用PdfFileWriter该类编写新的 PDF 文件 使用类连接和合并PDF 文件PdfFileMerger 旋转和裁剪PDF 页面 使用密码加密和解密

目录

了解如何在 Python 中创建和修改 PDF 文件非常有用。该PDF,或P ortable d ocument ˚F ORMAT,是最常见的格式在互联网上共享的文件之一。PDF可以在一个文件中包含文本、图像、表格、表单和富媒体。

如此丰富的内容类型会使处理 PDF 变得困难。打开 PDF 文件时,有很多不同类型的数据需要解码!幸运的是,Python 生态系统有一些很棒的包用于读取、操作和创建 PDF 文件。

在本教程中,您将学习如何:

  • 从 PDF 中读取文本
  • 将 PDF拆分为多个文件
  • 连接合并PDF 文件
  • 在 PDF 文件中旋转裁剪页面
  • 使用密码加密解密PDF文件
  • 从头开始创建PDF 文件

注意:本教程改编自Python Basics: A Practical Introduction to Python 3 中的“Creating and Modifying PDF Files”一章。

本书使用 Python 的内置IDLE编辑器来创建和编辑 Python 文件并与 Python shell 交互,因此您将在本教程中偶尔看到对 IDLE 的引用。但是,从您选择的编辑器和环境中运行示例代码应该没有问题。

在此过程中,您将有机会通过跟随示例来加深理解。您可以通过单击以下链接下载示例中使用的材料:

从 PDF 中提取文本

在本节中,您将学习如何阅读 PDF 文件并使用PyPDF2包提取文本。但是,在您执行此操作之前,您需要使用以下命令安装它pip

$ python3 -m pip install PyPDF2

通过在终端中运行以下命令来验证安装:

$ python3 -m pip show PyPDF2
Name: PyPDF2
Version: 1.26.0
Summary: PDF toolkit
Home-page: http://mstamy2.github.com/PyPDF2
Author: Mathieu Fenniak
Author-email: biziqe@mathieu.fenniak.net
License: UNKNOWN
Location: c:\\users\\david\\python38-32\\lib\\site-packages
Requires:
Required-by:

请特别注意版本信息。在撰写本文时,最新版本PyPDF21.26.0. 如果您打开了 IDLE,则需要重新启动它才能使用该PyPDF2软件包。

打开 PDF 文件

让我们首先打开一个 PDF 并阅读有关它的一些信息。您将使用Pride_and_Prejudice.pdf位于practice_files/配套存储库文件夹中的文件。

打开IDLE的互动窗口,并导入PdfFileReader从类PyPDF2包:

>>>
>>> from PyPDF2 import PdfFileReader

要创建PdfFileReader该类的新实例,您将需要要打开的 PDF 文件的路径。现在让我们使用pathlib模块来实现:

>>>
>>> from pathlib import Path
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "Pride_and_Prejudice.pdf"
... )

pdf_path 变量现在包含指向简·奥斯汀的《傲慢与偏见》的 PDF 版本的路径。

注意:您可能需要更改pdf_path以使其与creating-and-modifying-pdfs/计算机上文件夹的位置相对应。

现在创建PdfFileReader实例:

>>>
>>> pdf = PdfFileReader(str(pdf_path))

您转换pdf_path字符串,因为PdfFileReader不知道如何从pathlib.Path对象中读取。

回忆一下第 12 章“文件输入和输出”,在程序终止之前应该关闭所有打开的文件。该PdfFileReader对象会为您完成所有这些工作,因此您无需担心打开或关闭 PDF 文件!

现在您已经创建了一个PdfFileReader实例,您可以使用它来收集有关 PDF 的信息。例如,.getNumPages()返回 PDF 文件中包含的页数:

>>>
>>> pdf.getNumPages()
234

请注意,它.getNumPages()是用混合大小写编写的,而不是PEP 8 中推荐的 lower_case_with_underscores 。请记住,PEP 8 是一套指南,而不是规则。就 Python 而言,mixedCase 是完全可以接受的。

注意: PyPDF2改编自pyPdf包。pyPdf写于 2005 年,距 PEP 8 发布仅四年。

当时,许多 Python 程序员正在从混合大小写更常见的语言迁移。

您还可以使用该.documentInfo属性访问一些文档信息:

>>>
>>> pdf.documentInfo
{'/Title': 'Pride and Prejudice, by Jane Austen', '/Author': 'Chuck',
'/Creator': 'Microsoft® Office Word 2007',
'/CreationDate': 'D:20110812174208', '/ModDate': 'D:20110812174208',
'/Producer': 'Microsoft® Office Word 2007'}

返回的对象.documentInfo看起来像一个字典,但实际上并不是一回事。您可以.documentInfo作为属性访问中的每个项目。

例如,要获取标题,请使用以下.title属性:

>>>
>>> pdf.documentInfo.title
'Pride and Prejudice, by Jane Austen'

.documentInfo对象包含在创建 PDF 时设置的 PDF元数据

PdfFileReader类提供了所有必要的方法和属性,你需要访问数据的PDF文件。让我们探索一下您可以用 PDF 文件做什么以及如何做!

从页面中提取文本

PDF 页面PyPDF2PageObject类表示。您可以使用PageObject实例与 PDF 文件中的页面进行交互。您无需PageObject直接创建自己的实例。相反,您可以通过PdfFileReader对象的.getPage()方法访问它们。

从单个 PDF 页面中提取文本有两个步骤:

  1. 找一个PageObjectPdfFileReader.getPage()
  2. 使用PageObject实例的.extractText()方法将文本提取为字符串。

Pride_and_Prejudice.pdf234页面。每个页面都有一个介于0和之间的索引233。您可以PageObject通过将页面的索引传递给PdfFileReader.getPage()

>>>
>>> first_page = pdf.getPage(0)

.getPage()返回一个PageObject

>>>
>>> type(first_page)
<class 'PyPDF2.pdf.PageObject'>

您可以使用以下命令提取页面的文本PageObject.extractText()

>>>
>>> first_page.extractText()
'\\n \\nThe Project Gutenberg EBook of Pride and Prejudice, by Jane
Austen\\n \\n\\nThis eBook is for the use of anyone anywhere at no cost
and with\\n \\nalmost no restrictions whatsoever.  You may copy it,
give it away or\\n \\nre\\n-\\nuse it under the terms of the Project
Gutenberg License included\\n \\nwith this eBook or online at
www.gutenberg.org\\n \\n \\n \\nTitle: Pride and Prejudice\\n \\n
\\nAuthor: Jane Austen\\n \\n \\nRelease Date: August 26, 2008
[EBook #1342]\\n\\n[Last updated: August 11, 2011]\\n \\n \\nLanguage:
Eng\\nlish\\n \\n \\nCharacter set encoding: ASCII\\n \\n \\n***
START OF THIS PROJECT GUTENBERG EBOOK PRIDE AND PREJUDICE ***\\n \\n
\\n \\n \\n \\nProduced by Anonymous Volunteers, and David Widger\\n
\\n \\n \\n \\n \\n \\n \\nPRIDE AND PREJUDICE \\n \\n \\nBy Jane
Austen \\n \\n\\n \\n \\nContents\\n \\n'

请注意,此处显示的输出已格式化以更适合此页面。您在计算机上看到的输出格式可能不同。

每个PdfFileReader对象都有一个.pages属性,您可以使用该属性按顺序遍历 PDF 中的所有页面。

例如,以下for循环打印傲慢与偏见PDF 中每一页的文本:

>>>
>>> for page in pdf.pages:
...     print(page.extractText())
...

让我们结合您所学的一切,编写一个程序,从Pride_and_Prejudice.pdf文件中提取所有文本并将其保存到.txt文件中。

把它放在一起

在 IDLE 中打开一个新的编辑器窗口并输入以下代码:

from pathlib import Path
from PyPDF2 import PdfFileReader

# Change the path below to the correct path for your computer.
pdf_path = (
    Path.home()
    / "creating-and-modifying-pdfs"
    / "practice-files"
    / "Pride_and_Prejudice.pdf"
)

# 1
pdf_reader = PdfFileReader(str(pdf_path))
output_file_path = Path.home() / "Pride_and_Prejudice.txt"

# 2
with output_file_path.open(mode="w") as output_file:
    # 3
    title = pdf_reader.documentInfo.title
    num_pages = pdf_reader.getNumPages()
    output_file.write(f"{title}\\nNumber of pages: {num_pages}\\n\\n")

    # 4
    for page in pdf_reader.pages:
        text = page.extractText()
        output_file.write(text)

让我们分解一下:

  1. 首先,您将一个新PdfFileReader实例分配给pdf_reader 变量。您还创建了一个Path指向Pride_and_Prejudice.txt主目录中文件的新对象并将其分配给output_file_path变量。

  2. 接下来,您output_file_path以写模式打开并将返回的文件对象分配给.open()变量output_file。您在第 12 章“文件输入和输出”中了解到的with语句确保在块退出时关闭文件。with

  3. 然后,在with块内,使用 .pdf 将 PDF 标题和页数写入文本文件output_file.write()

  4. 最后,您使用for循环遍历 PDF 中的所有页面。在循环中的每一步,下一个都PageObject被分配给page变量。每个页面的文本都被提取出来page.extractText()并写入output_file.

当您保存并运行该程序时,它会在您的主目录中创建一个名为Pride_and_Prejudice.txt包含Pride_and_Prejudice.pdf文档全文的新文件。打开它并检查它!

检查你的理解

展开下面的块以检查您的理解:

练习:从 PDF 打印文本显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:从 PDF 打印文本显示隐藏

准备好后,您可以继续下一部分。

从 PDF 中提取页面

在上一节中,您学习了如何从 PDF 文件中提取所有文本并将其保存到.txt文件中。现在,您将学习如何从现有 PDF 中提取页面或页面范围并将它们保存到新的 PDF。

您可以使用PdfFileWriter来创建新的 PDF 文件。让我们探索这门课并学习使用 .pdf 创建 PDF 所需的步骤PyPDF2

使用PdfFileWriter

PdfFileWriter类用于创建新的PDF文件。在 IDLE 的交互窗口中,导入PdfFileWriter该类并创建一个名为 的新实例pdf_writer

>>>
>>> from PyPDF2 import PdfFileWriter
>>> pdf_writer = PdfFileWriter()

PdfFileWriter对象就像空白的 PDF 文件。您需要先向其中添加一些页面,然后才能将它们保存到文件中。

继续添加一个空白页到pdf_writer

>>>
>>> page = pdf_writer.addBlankPage(width=72, height=72)

widthheight参数是必需的,确定单位称为页面的尺寸。一点等于 1/72 英寸,因此上面的代码将一英寸见方的空白页添加到pdf_writer.

.addBlankPage()返回一个新PageObject实例,代表您添加到的页面PdfFileWriter

>>>
>>> type(page)
<class 'PyPDF2.pdf.PageObject'>

在此示例中,您已将PageObject返回的实例分配.addBlankPage()page变量,但实际上您通常不需要这样做。也就是说,您通常调用.addBlankPage()而不将返回值分配给任何东西:

>>>
>>> pdf_writer.addBlankPage(width=72, height=72)

要将 的内容写入pdf_writerPDF 文件,请将二进制写入模式的文件对象传递给pdf_writer.write()

>>>
>>> from pathlib import Path
>>> with Path("blank.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

这会在您当前的工作目录中创建一个名为blank.pdf. 如果您使用 PDF 阅读器(例如 Adob​​e Acrobat)打开文件,您将看到一个包含一个一英寸见方的空白页面的文档。

技术细节:请注意,您通过将文件对象传递给PdfFileWriter对象的.write()方法而不是文件对象的.write()方法来保存 PDF 文件。

特别是,以下代码将不起作用:

>>>
>>> with Path("blank.pdf").open(mode="wb") as output_file:
...     output_file.write(pdf_writer)

这种方法对许多新程序员来说似乎是倒退的,所以一定要避免这个错误!

PdfFileWriter 对象可以写入新的 PDF 文件,但除了空白页面之外,它们不能从头开始创建新内容。

这似乎是一个大问题,但在许多情况下,您不需要创建新内容。通常,您会使用从使用PdfFileReader实例打开的 PDF 文件中提取的页面。

注意:您将在下面的“从头开始创建 PDF 文件”部分中了解如何从头开始创建 PDF 文件。

在上面的示例中,使用PyPDF2以下三个步骤创建新的 PDF 文件:

  1. 创建一个PdfFileWriter实例。
  2. PdfFileWriter实例添加一个或多个页面。
  3. 使用PdfFileWriter.write().

当您学习将页面添加到PdfFileWriter实例的各种方法时,您会一遍又一遍地看到这种模式。

从 PDF 中提取单个页面

让我们重新审视您在上一节中使用的傲慢与偏见PDF。您将打开 PDF,提取第一页,然后创建一个仅包含单个提取页面的新 PDF 文件。

打开IDLE的交互式窗口和进口PdfFileReader,并PdfFileWriterPyPDF2还有Path从类pathlib模块:

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter

现在Pride_and_Prejudice.pdf用一个PdfFileReader实例打开文件:

>>>
>>> # Change the path to work on your computer if necessary
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "Pride_and_Prejudice.pdf"
... )
>>> input_pdf = PdfFileReader(str(pdf_path))

通过该指数0.getPage()获得一个PageObject代表PDF的第一页:

>>>
>>> first_page = input_pdf.getPage(0)

现在创建一个新PdfFileWriter实例并添加first_page到它.addPage()

>>>
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.addPage(first_page)

.addPage()方法将一个页面添加到pdf_writer对象中的页面集,就像.addBlankPage(). 不同之处在于它需要一个现有的PageObject.

现在将 的内容写入pdf_writer一个新文件:

>>>
>>> with Path("first_page.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

您现在在名为 的当前工作目录中保存了一个新的 PDF 文件first_page.pdf,其中包含Pride_and_Prejudice.pdf文件的封面。挺整洁的!

从 PDF 中提取多个页面

让我们从中提取第一章Pride_and_Prejudice.pdf并将其保存为新的 PDF。

如果Pride_and_Prejudice.pdf使用 PDF 查看器打开,则可以看到第一章位于 PDF 的第二、第三和第四页。由于网页进行索引开始0,你需要在指标提取的网页123

您可以通过导入所需的类并打开 PDF 文件来设置所有内容:

>>>
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
>>> from pathlib import Path
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "Pride_and_Prejudice.pdf"
... )
>>> input_pdf = PdfFileReader(str(pdf_path))

您的目标是提取索引12和处的页面3,将它们添加到新PdfFileWriter实例中,然后将它们写入新的 PDF 文件。

一种方法是循环遍历从 开始到1结束的数字范围3,在循环的每一步提取页面并将其添加到PdfFileWriter实例中:

>>>
>>> pdf_writer = PdfFileWriter()
>>> for n in range(1, 4):
...     page = input_pdf.getPage(n)
...     pdf_writer.addPage(page)
...

循环遍历数字123因为range(1, 4)不包括右侧端点。在循环的每一步中,当前索引处的页面都被提取.getPage()并添加到pdf_writerusing 中.addPage()

现在pdf_writer有三页,您可以查看.getNumPages()

>>>
>>> pdf_writer.getNumPages()
3

最后,您可以将提取的页面写入新的 PDF 文件:

>>>
>>> with Path("chapter1.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

现在,您可以打开chapter1.pdf当前工作目录中的文件,阅读傲慢与偏见的第一章。

从 PDF 中提取多个页面的另一种方法是利用PdfFileReader.pages支持切片表示法的事实。让我们使用.pages而不是循环range对象重做前面的示例。

首先初始化一个新PdfFileWriter对象:

>>>
>>> pdf_writer = PdfFileWriter()

现在循环一个.pages从索引开始到1结束的片段4

>>>
>>> for page in input_pdf.pages[1:4]:
...    pdf_writer.addPage(page)
...

请记住,切片中的值范围从切片中第一个索引处的项目到但不包括切片中第二个索引处的项目。所以.pages[1:4]返回一个迭代含指数的网页123

最后,将 的内容写入pdf_writer输出文件:

>>>
>>> with Path("chapter1_slice.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

现在打开chapter1_slice.pdf当前工作目录中的chapter1.pdf文件,并将其与通过循环range对象创建的文件进行比较。它们包含相同的页面!

有时您需要从 PDF 中提取每一页。您可以使用上面说明的方法来执行此操作,但PyPDF2提供了一个快捷方式。PdfFileWriter实例有一个.appendPagesFromReader()方法,您可以使用该方法从PdfFileReader实例附加页面。

要使用.appendPagesFromReader()PdfFileReader请将实例传递给方法的reader参数。例如,以下代码将傲慢与偏见PDF 中的每一页复制到一个PdfFileWriter实例中:

>>>
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.appendPagesFromReader(pdf_reader)

pdf_writer现在包含中的每一页pdf_reader

检查你的理解

展开下面的块以检查您的理解:

练习:提取 PDF 的最后一页显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:提取 PDF 的最后一页显示隐藏

准备好后,您可以继续下一部分。

连接和合并 PDF

处理 PDF 文件时的两个常见任务是将多个 PDF 连接并合并为一个文件。

当您连接两个或多个 PDF 时,您将文件一个接一个地合并为一个文档。例如,一家公司可能会在月底将多份日报表合并为一份月报表。

合并两个 PDF 也会将 PDF 合并为一个文件。但不是将第二个 PDF 连接到第一个 PDF 的末尾,合并允许您将其插入到第一个 PDF 中的特定页面之后。然后它将插入点之后的所有第一个 PDF 页面推送到第二个 PDF 的末尾。

在本节中,您将学习如何使用PyPDF2包的PdfFileMerger.

使用PdfFileMerger

PdfFileMerger课程与PdfFileWriter您在上一节中学到的课程非常相似。您可以使用这两个类来编写 PDF 文件。在这两种情况下,您都将页面添加到类的实例,然后将它们写入文件。

两者之间的主要区别在于,PdfFileWriter只能将页面附加或连接到编写器中已包含的页面列表的末尾,而PdfFileMerger可以在任何位置插入或合并页面。

继续创建您的第一个PdfFileMerger实例。在 IDLE 的交互窗口中,键入以下代码以导入PdfFileMerger该类并创建一个新实例:

>>>
>>> from PyPDF2 import PdfFileMerger
>>> pdf_merger = PdfFileMerger()

PdfFileMerger对象在第一次实例化时是空的。您需要先向对象添加一些页面,然后才能对其进行任何操作。

有几种方法可以向pdf_merger对象添加页面,使用哪种方法取决于您需要完成的任务:

  • .append()将现有 PDF 文档中的每一页连接到 .pdf 文件中当前页面的末尾PdfFileMerger
  • .merge()在 .pdf 文件中的特定页面之后插入现有 PDF 文档中的所有页面PdfFileMerger

您将在本节中查看这两种方法,从.append().

将 PDF 与 .append()

practice_files/文件夹有一个名为的子目录expense_reports,其中包含名为 Peter Python 的员工的三份费用报告。

Peter 需要将这三个 PDF 连接起来,并将它们作为单个 PDF 文件提交给他的雇主,以便他可以报销一些与工作相关的费用。

您可以首先使用该pathlib模块获取文件夹中Path三个费用报告中每一个的对象列表expense_reports/

>>>
>>> from pathlib import Path
>>> reports_dir = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "expense_reports"
... )

导入Path类后,您需要构建expense_reports/目录的路径。请注意,您可能需要更改上面的代码才能在您的计算机上获得正确的路径。

expense_reports/目录的路径分配给reports_dir变量后,您可以使用它.glob()来获取目录中 PDF 文件的可迭代路径。

查看目录中的内容:

>>>
>>> for path in reports_dir.glob("*.pdf"):
...     print(path.name)
...
Expense report 1.pdf
Expense report 3.pdf
Expense report 2.pdf

列出了三个文件的名称,但它们没有按顺序排列。此外,您在计算机输出中看到的文件顺序可能与此处显示的输出不匹配。

通常,.glob()不保证返回的路径顺序,因此您需要自己对它们进行排序。您可以通过创建一个包含三个文件路径的.sort()列表然后调用该列表来完成此操作:

>>>
>>> expense_reports = list(reports_dir.glob("*.pdf"))
>>> expense_reports.sort()

请记住,.sort()对列表进行就地排序,因此您无需将返回值分配给变量。调用expense_reports后,列表将按文件名的字母顺序排序.list()

要确认排序有效,请expense_reports再次循环并打印出文件名:

>>>
>>> for path in expense_reports:
...     print(path.name)
...
Expense report 1.pdf
Expense report 2.pdf
Expense report 3.pdf

看起来不错!

现在您可以连接三个 PDF。为此,您将使用PdfFileMerger.append(),它需要一个表示 PDF 文件路径的字符串参数。当您调用 时.append(),PDF 文件中的所有页面都会附加到PdfFileMerger对象中的页面集。

让我们看看它的实际效果。首先,导入PdfFileMerger类并创建一个新实例:

>>>
>>> from PyPDF2 import PdfFileMerger
>>> pdf_merger = PdfFileMerger()

现在遍历排序expense_reports列表中的路径并将它们附加到pdf_merger

>>>
>>> for path in expense_reports:
...     pdf_merger.append(str(path))
...

请注意,每个Path在对象expense_reports/被转换成字符串str()之前被传递到pdf_merger.append()

expense_reports/目录中的所有 PDF 文件连接到pdf_merger对象中后,您需要做的最后一件事就是将所有内容写入输出 PDF 文件。PdfFileMerger实例有一个.write()方法,就像PdfFileWriter.write().

以二进制写入模式打开一个新文件,然后将文件对象传递给该pdf_merge.write()方法:

>>>
>>> with Path("expense_reports.pdf").open(mode="wb") as output_file:
...     pdf_merger.write(output_file)
...

您现在在当前工作目录中有一个名为 .pdf 的 PDF 文件expense_reports.pdf。使用 PDF 阅读器打开它,您会在同一个 PDF 文件中找到所有三份费用报告。

合并 PDF .merge()

要合并两个或多个 PDF,请使用PdfFileMerger.merge(). 此方法类似于.append(),不同之处在于您必须指定在输出 PDF 中的哪个位置插入您正在合并的 PDF 中的所有内容。

看一个例子。Goggle, Inc. 准备了一份季度报告,但忘记包含目录。Peter Python 注意到了这个错误,并迅速创建了一个缺少目录的 PDF。现在他需要将该 PDF 合并到原始报告中。

报告 PDF 和目录 PDF 都可以在quarterly_report/文件practice_files夹的子文件夹中找到。报告位于名为 的文件report.pdf中,目录位于名为 的文件中toc.pdf

在 IDLE 的交互窗口中,导入PdfFileMerger类并Pathreport.pdftoc.pdf文件创建对象:

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileMerger
>>> report_dir = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "quarterly_report"
... )
>>> report_path = report_dir / "report.pdf"
>>> toc_path = report_dir / "toc.pdf"

您要做的第一件事是使用以下命令将报告 PDF 附加到新PdfFileMerger实例.append()

>>>
>>> pdf_merger = PdfFileMerger()
>>> pdf_merger.append(str(report_path))

现在其中pdf_merger有一些页面,您可以在正确的位置将目录 PDF 合并到其中。如果您report.pdf使用 PDF 阅读器打开文件,您将看到报告的第一页是标题页。第二个是简介,其余页面包含不同的报告部分。

您想在标题页之后和介绍部分之前插入目录。由于 PDF 页面索引以0in开头PyPDF2,因此您需要在 index 页面之后0和 index 页面之前插入目录1

为此,请pdf_merger.merge()使用两个参数调用:

  1. 整数1,指示应插入目录的页的索引
  2. 包含目录的 PDF 文件路径的字符串

这是它的样子:

>>>
>>> pdf_merger.merge(1, str(toc_path))

目录 PDF 中的每一页都插入在 index 处的页面之前1。由于目录 PDF 只有一页,因此它被插入到 index 处1。当前位于 index 的页面1然后被转移到 index 2。当前位于 index 的页面2被转移到 index 3,依此类推。

现在将合并的 PDF 写入输出文件:

>>>
>>> with Path("full_report.pdf").open(mode="wb") as output_file:
...     pdf_merger.write(output_file)
...

您现在full_report.pdf在当前工作目录中有一个文件。用 PDF 阅读器打开它并检查目录是否插入正确的位置。

连接和合并 PDF 是常见的操作。虽然本节中的示例确实有些人为,但您可以想象一个程序对于合并数千个 PDF 或自动化需要大量时间才能完成的日常任务有多么有用。

检查你的理解

展开下面的块以检查您的理解:

练习:连接两个 PDF显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:连接两个 PDF显示隐藏

准备好后,您可以继续下一部分。

旋转和裁剪 PDF 页面

到目前为止,您已经学习了如何从 PDF 中提取文本和页面,以及如何连接和合并两个或多个 PDF 文件。这些都是 PDF 的常见操作,但PyPDF2还有许多其他有用的功能。

注意:本教程改编自Python Basics: A Practical Introduction to Python 3 中的“Creating and Modifying PDF Files”一章。如果您喜欢正在阅读的内容,请务必查看本书的其余部分

在本节中,您将学习如何旋转和裁剪 PDF 文件中的页面。

旋转页面

您将从学习如何旋转页面开始。对于此示例,您将使用ugly.pdf文件practice_files夹中的文件。该ugly.pdf文件包含汉斯·克里斯蒂安·安徒生 (Hans Christian Andersen) 的《丑小鸭》的可爱版本,只是每个奇数页都逆时针旋转了 90 度。

让我们解决这个问题。在一个新的 IDLE 交互窗口中,首先从 导入PdfFileReaderPdfFileWriterPyPDF2,以及Pathpathlib模块中导入类:

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter

现在Pathugly.pdf文件创建一个对象:

>>>
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "ugly.pdf"
... )

最后,创建新的PdfFileReaderPdfFileWriter实例:

>>>
>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

您的目标是pdf_writer创建一个新的 PDF 文件,其中所有页面的方向都正确。PDF 中的偶数页已经正确定向,但奇数页逆时针旋转了 90 度。

要纠正该问题,您将使用PageObject.rotateClockwise(). 此方法采用以度为单位的整数参数,并将页面顺时针旋转这么多度。例如,.rotateClockwise(90)将 PDF 页面顺时针旋转九十度。

注意:除了.rotateClockwise()PageObject该类还有.rotateCounterClockwise()用于逆时针旋转页面。

有多种方法可以在 PDF 中旋转页面。我们将讨论两种不同的方法。它们都依赖于.rotateClockwise(),但它们采用不同的方法来确定哪些页面被旋转。

第一种技术是遍历 PDF 中页面的索引并检查每个索引是否对应于需要旋转的页面。如果是这样,那么您将调用.rotateClockwise()以旋转页面,然后将页面添加到pdf_writer.

这是它的样子:

>>>
>>> for n in range(pdf_reader.getNumPages()):
...     page = pdf_reader.getPage(n)
...     if n % 2 == 0:
...         page.rotateClockwise(90)
...     pdf_writer.addPage(page)
...

请注意,如果索引是偶数,页面会旋转。这可能看起来很奇怪,因为 PDF 中的奇数页是旋转不正确的页。但是,PDF 中的页码以 开头1,而页面索引以 开头0。这意味着奇数 PDF 页面具有偶数索引。

如果这让你头晕目眩,别担心!即使在处理这样的事情多年之后,专业程序员仍然会被这些事情绊倒!

注意:当你执行for上面的循环时,你会在 IDLE 的交互窗口中看到一堆输出。那是因为.rotateClockwise()返回一个PageObject实例。

您现在可以忽略此输出。当您从 IDLE 的编辑器窗口执行程序时,此输出将不可见。

现在您已经旋转了 PDF 中的所有页面,您可以将内容写入pdf_writer新文件并检查一切是否正常:

>>>
>>> with Path("ugly_rotated.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

您现在应该在当前工作目录中有一个名为ugly_rotated.pdfugly.pdf文件,该文件中的页面全部正确旋转。

您刚才用来旋转ugly.pdf文件中页面的方法的问题在于,它取决于提前知道哪些页面需要旋转。在实际场景中,浏览整个 PDF 并记下要旋转的页面是不切实际的。

事实上,您可以在没有先验知识的情况下确定哪些页面需要旋转。嗯,有时你可以。

让我们看看如何,从一个新PdfFileReader实例开始:

>>>
>>> pdf_reader = PdfFileReader(str(pdf_path))

您需要这样做是因为您PdfFileReader通过旋转旧实例中的页面来更改它们。因此,通过创建新实例,您将重新开始。

PageObject 实例维护一个包含页面信息的值字典:

>>>
>>> pdf_reader.getPage(0)
{'/Contents': [IndirectObject(11, 0), IndirectObject(12, 0),
IndirectObject(13, 0), IndirectObject(14, 0), IndirectObject(15, 0),
IndirectObject(16, 0), IndirectObject(17, 0), IndirectObject(18, 0)],
'/Rotate': -90, '/Resources': {'/ColorSpace': {'/CS1':
IndirectObject(19, 0), '/CS0': IndirectObject(19, 0)}, '/XObject':
{'/Im0': IndirectObject(21, 0)}, '/Font': {'/TT1':
IndirectObject(23, 0), '/TT0': IndirectObject(25, 0)}, '/ExtGState':
{'/GS0': IndirectObject(27, 0)}}, '/CropBox': [0, 0, 612, 792],
'/Parent': IndirectObject(1, 0), '/MediaBox': [0, 0, 612, 792],
'/Type': '/Page', '/StructParents': 0}

哎呀!与所有看起来毫无意义的东西混合在一起的是一个名为 的键/Rotate,您可以在上面的第四行输出中看到它。该键的值为-90

您可以使用下标符号访问/RotatePageObject,就像在 Pythondict对象上一样:

>>>
>>> page = pdf_reader.getPage(0)
>>> page["/Rotate"]
-90

如果您查看 中/Rotate第二页的键pdf_reader,您会看到它的值为0

>>>
>>> page = pdf_reader.getPage(1)
>>> page["/Rotate"]
0

这一切意味着 index 处的页面0具有-90度数的旋转值。换句话说,它逆时针旋转了九十度。index 处的页面1的旋转值为0,因此它根本没有旋转。

如果使用 旋转第一页.rotateClockwise(),则值/Rotate将从-90变为0

>>>
>>> page = pdf_reader.getPage(0)
>>> page["/Rotate"]
-90
>>> page.rotateClockwise(90)
>>> page["/Rotate"]
0

现在您知道如何检查/Rotate密钥,您可以使用它来旋转ugly.pdf文件中的页面。

您需要做的第一件事是重新初始化您的pdf_readerpdf_writer对象,以便您重新开始:

>>>
>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

现在编写一个循环,循环遍历pdf_reader.pages可迭代中的页面,检查 的值/Rotate,如果该值是 ,则旋转页面-90

>>>
>>> for page in pdf_reader.pages:
...     if page["/Rotate"] == -90:
...         page.rotateClockwise(90)
...     pdf_writer.addPage(page)
...

这个循环不仅比第一个解决方案中的循环略短,而且它不依赖于关于哪些页面需要旋转的任何先验知识。您可以使用这样的循环来旋转任何 PDF 中的页面,而无需打开它并查看它。

要完成解决方案,请将 的内容写入pdf_writer新文件:

>>>
>>> with Path("ugly_rotated2.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

现在,您可以ugly_rotated2.pdf在当前工作目录中打开该文件,并将其与ugly_rotated.pdf之前生成的文件进行比较。它们应该看起来相同。

注意:关于/Rotate密钥的一个警告词:它不能保证存在于页面上。

如果/Rotate键不存在,则通常意味着页面尚未旋转。然而,这并不总是一个安全的假设。

如果 aPageObject没有/Rotate键,则KeyError当您尝试访问它时会引发a 。您可以使用try...exceptblock捕获此异常

的价值/Rotate可能并不总是您所期望的。例如,如果您在页面逆时针旋转 90 度的情况下扫描纸质文档,则 PDF 的内容将显示为旋转。但是,/Rotate键可能具有值0

这是许多使处理 PDF 文件令人沮丧的怪癖之一。有时您只需要在 PDF 阅读器程序中打开 PDF 并手动解决问题。

裁剪页面

PDF 的另一个常见操作是裁剪页面。您可能需要这样做以将单个页面拆分为多个页面或仅提取页面的一小部分,例如签名或图形。

例如,该practice_files文件夹包含一个名为half_and_half.pdf. 此 PDF 包含汉斯·克里斯蒂安·安徒生 (Hans Christian Andersen) 的《小美人鱼》的一部分

此 PDF 中的每一页都有两列。让我们将每一页分成两页,每一列一页。

首先,从模块中导入PdfFileReaderPdfFileWriterPyPDF2Pathpathlib

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter

现在Pathhalf_and_half.pdf文件创建一个对象:

>>>
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "half_and_half.pdf"
... )

接下来,创建一个新PdfFileReader对象并获取 PDF 的第一页:

>>>
>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> first_page = pdf_reader.getPage(0)

要裁剪页面,您首先需要更多地了解页面的结构。PageObjectfirst_page这样的实例有一个.mediaBox属性,表示定义页面边界的矩形区域。

.mediaBox在使用它裁剪页面之前,您可以使用 IDLE 的交互式窗口来探索:

>>>
>>> first_page.mediaBox
RectangleObject([0, 0, 792, 612])

.mediaBox属性返回一个RectangleObject. 该对象在PyPDF2包中定义,代表页面上的一个矩形区域。

[0, 0, 792, 612]输出中的列表定义了矩形区域。前两个数字是矩形左下角的 x 和 y 坐标。第三个和第四个数字分别代表矩形的宽度和高度。所有值的单位都是点,等于 1/72 英寸。

RectangleObject([0, 0, 792, 612])表示以左下角为原点、宽度为79211 英寸、高度为 612 磅或 8.5 英寸的矩形区域。这些是横向标准信纸尺寸页面的尺寸,用于The Little Mermaid的示例 PDF 。纵向的信纸大小的 PDF 页面将返回输出RectangleObject([0, 0, 612, 792]).

一个RectangleObject有四个属性返回矩形的角的坐标:.lowerLeft.lowerRight.upperLeft,和.upperRight。就像宽度和高度值一样,这些坐标以点为单位。

您可以使用这四个属性来获取 的每个角的坐标RectangleObject

>>>
>>> first_page.mediaBox.lowerLeft
(0, 0)
>>> first_page.mediaBox.lowerRight
(792, 0)
>>> first_page.mediaBox.upperLeft
(0, 612)
>>> first_page.mediaBox.upperRight
(792, 612)

每个属性返回一个tuple包含指定角坐标的值。您可以像访问任何其他 Python 元组一样使用方括号访问单个坐标:

>>>
>>> first_page.mediaBox.upperRight[0]
792
>>> first_page.mediaBox.upperRight[1]
612

您可以mediaBox通过将新元组分配给其属性之一来更改 a 的坐标:

>>>
>>> first_page.mediaBox.upperLeft = (0, 480)
>>> first_page.mediaBox.upperLeft
(0, 480)

当您更改.upperLeft坐标时,.upperRight属性会自动调整以保留矩形形状:

>>>
>>> first_page.mediaBox.upperRight
(792, 480)

当您更改由RectangleObject返回的坐标时.mediaBox,您可以有效地裁剪页面。该first_page对象现在仅包含新RectangleObject.

继续将裁剪后的页面写入一个新的 PDF 文件:

>>>
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.addPage(first_page)
>>> with Path("cropped_page.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

如果您cropped_page.pdf在当前工作目录中打开该文件,您将看到页面的顶部已被删除。

您将如何裁剪页面以便仅显示页面左侧的文本?您需要将页面的水平尺寸切成两半。您可以通过更改对象的.upperRight坐标来实现此.mediaBox目的。让我们看看它是如何工作的。

首先,您需要获取 newPdfFileReaderPdfFileWriterobjects,因为您刚刚更改了第一页pdf_reader并将其添加到pdf_writer

>>>
>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()

现在获取 PDF 的第一页:

>>>
>>> first_page = pdf_reader.getPage(0)

这一次,让我们处理第一页的副本,以便您刚刚提取的页面保持完整。您可以通过copy从 Python 的标准库中导入模块并使用deepcopy()来制作页面的副本来做到这一点:

>>>
>>> import copy
>>> left_side = copy.deepcopy(first_page)

现在您可以在left_side不更改first_page. 这样,您可以first_page稍后使用来提取页面右侧的文本。

现在你需要做一些数学运算。您已经确定需要将 的右上角移动.mediaBox到页面的顶部中心。为此,您将创建一个新的tuple,其中第一个组件等于原始值的一半,并将其分配给.upperRight属性。

首先,获取 .png 右上角的当前坐标.mediaBox

>>>
>>> current_coords = left_side.mediaBox.upperRight

然后创建一个新的,tuple它的第一个坐标是当前坐标值的一半,第二个坐标与原始坐标相同:

>>>
>>> new_coords = (current_coords[0] / 2, current_coords[1])

最后,将新坐标分配给.upperRight属性:

>>>
>>> left_side.mediaBox.upperRight = new_coords

您现在已经裁剪了原始页面以仅包含左侧的文本!接下来让我们提取页面的右侧。

首先获得一个新副本first_page

>>>
>>> right_side = copy.deepcopy(first_page)

移动.upperLeft角落而不是.upperRight角落:

>>>
>>> right_side.mediaBox.upperLeft = new_coords

这会将左上角设置为与您在提取页面左侧时将右上角移动到的坐标相同的坐标。所以,right_side.mediaBox现在是一个矩形,其左上角位于页面的顶部中心,右上角位于页面的右上角。

最后,添加left_sideright_side页面pdf_writer并将它们写入一个新的 PDF 文件:

>>>
>>> pdf_writer.addPage(left_side)
>>> pdf_writer.addPage(right_side)
>>> with Path("cropped_pages.pdf").open(mode="wb") as output_file:
...     pdf_writer.write(output_file)
...

现在cropped_pages.pdf用 PDF 阅读器打开文件。您应该看到一个包含两页的文件,第一页包含来自原始第一页左侧的文本,第二页包含来自原始右侧的文本。

检查你的理解

展开下面的块以检查您的理解:

练习:旋转 PDF 中的页面显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:在 PDF 中旋转页面显示隐藏

加密和解密 PDF

有时 PDF 文件受密码保护。使用该PyPDF2软件包,您可以处理加密的 PDF 文件以及为现有 PDF 添加密码保护。

注意:本教程改编自Python Basics: A Practical Introduction to Python 3 中的“Creating and Modifying PDF Files”一章。如果您喜欢正在阅读的内容,请务必查看本书的其余部分

加密 PDF

您可以使用实例的.encrypt()方法为PDF 文件添加密码保护PdfFileWriter()。它有两个主要参数:

  1. user_pwd设置用户密码。这允许打开和阅读 PDF 文件。
  2. owner_pwd设置所有者密码。这允许在没有任何限制的情况下打开 PDF,包括编辑。

让我们使用.encrypt()为 PDF 文件添加密码。首先,打开newsletter.pdf目录中的practice_files文件:

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
>>> pdf_path = (
...     Path.home()
...     / "creating-and-modifying-pdfs"
...     / "practice_files"
...     / "newsletter.pdf"
... )
>>> pdf_reader = PdfFileReader(str(pdf_path))

现在创建一个新PdfFileWriter实例并向其中添加页面pdf_reader

>>>
>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.appendPagesFromReader(pdf_reader)

接下来,添加密码"SuperSecret"pdf_writer.encrypt()

>>>
>>> pdf_writer.encrypt(user_pwd="SuperSecret")

当您设置 only 时user_pwdowner_pwd参数默认为相同的字符串。因此,上面的代码行设置了用户和所有者密码。

最后,将加密的 PDF 写入主目录中名为 的输出文件newsletter_protected.pdf

>>>
>>> output_path = Path.home() / "newsletter_protected.pdf"
>>> with output_path.open(mode="wb") as output_file:
...     pdf_writer.write(output_file)

当您使用 PDF 阅读器打开 PDF 时,系统会提示您输入密码。输入"SuperSecret"以打开 PDF。

如果您需要为 PDF 设置单独的所有者密码,则将第二个字符串传递给owner_pwd参数:

>>>
>>> user_pwd = "SuperSecret"
>>> owner_pwd = "ReallySuperSecret"
>>> pdf_writer.encrypt(user_pwd=user_pwd, owner_pwd=owner_pwd)

在此示例中,用户密码为"SuperSecret",所有者密码为"ReallySuperSecret"

当您使用密码加密 PDF 文件并尝试打开它时,您必须提供密码才能查看其内容。这种保护扩展到在 Python 程序中读取 PDF。接下来,让我们看看如何使用 .pdf 文件解密 PDF 文件PyPDF2

解密 PDF

要解密加密的 PDF 文件,请使用实例的.decrypt()方法PdfFileReader

.decrypt()有一个名为的参数password,您可以使用它来提供解密密码。您在打开 PDF 时拥有的权限取决于您传递给password参数的参数。

让我们打开newsletter_protected.pdf您在上一节中创建的加密文件并用于对其PyPDF2进行解密。

首先,PdfFileReader使用受保护的 PDF 的路径创建一个新实例:

>>>
>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
>>> pdf_path = Path.home() / "newsletter_protected.pdf"
>>> pdf_reader = PdfFileReader(str(pdf_path))

在解密 PDF 之前,请检查如果您尝试获取第一页会发生什么:

>>>
>>> pdf_reader.getPage(0)
Traceback (most recent call last):
  File "/Users/damos/github/realpython/python-basics-exercises/venv/
  lib/python38-32/site-packages/PyPDF2/pdf.py", line 1617, in getObject
    raise utils.PdfReadError("file has not been decrypted")
PyPDF2.utils.PdfReadError: file has not been decrypted

一个PdfReadError例外被引发,通知您的PDF文件尚未解密。

注意:上面的回溯已被缩短以突出重要部分。您在计算机上看到的回溯会更长。

现在继续解密文件:

>>>
>>> pdf_reader.decrypt(password="SuperSecret")
1

.decrypt() 返回一个表示解密成功的整数:

  • 0 表示密码错误。
  • 1 表示用户密码匹配。
  • 2 表示已匹配所有者密码。

解密文件后,您可以访问 PDF 的内容:

>>>
>>> pdf_reader.getPage(0)
{'/Contents': IndirectObject(7, 0), '/CropBox': [0, 0, 612, 792],
'/MediaBox': [0, 0, 612, 792], '/Parent': IndirectObject(1, 0),
'/Resources': IndirectObject(8, 0), '/Rotate': 0, '/Type': '/Page'}

现在,您可以根据自己的喜好提取文本并裁剪或旋转页面!

检查你的理解

展开下面的块以检查您的理解:

练习:加密 PDF显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:加密 PDF显示隐藏

从头开始创建 PDF 文件

PyPDF2包非常适合阅读和修改现有的 PDF 文件,但它有一个主要限制:您不能使用它来创建新的 PDF 文件。在本节中,您将使用ReportLab 工具包从头开始生成 PDF 文件。

ReportLab 是用于创建 PDF 的全功能解决方案。有一个需要花钱使用的商业版本,但也有一个功能有限的开源版本。

注意:本节并不是对 ReportLab 的详尽介绍,而是可能的示例。

有关更多示例,请查看 ReportLab 的代码片段页面

安装 reportlab

要开始,你需要安装reportlabpip

$ python3 -m pip install reportlab

您可以使用以下命令验证安装pip show

$ python3 -m pip show reportlab
Name: reportlab
Version: 3.5.34
Summary: The Reportlab Toolkit
Home-page: http://www.reportlab.com/
Author: Andy Robinson, Robin Becker, the ReportLab team
        and the community
Author-email: reportlab-users@lists2.reportlab.com
License: BSD license (see license.txt for details),
         Copyright (c) 2000-2018, ReportLab Inc.
Location: c:\users\davea\venv\lib\site-packages
Requires: pillow
Required-by:

在撰写本文时,最新版本reportlab是 3.5.34。如果您打开了 IDLE,则需要重新启动它才能使用该reportlab软件包。

使用Canvas

用于与创建PDF主界面reportlabCanvas类,它位于在reportlab.pdfgen.canvas模块。

打开一个新的 IDLE 交互窗口并键入以下内容以导入Canvas类:

>>>
>>> from reportlab.pdfgen.canvas import Canvas

创建新Canvas实例时,您需要提供一个字符串,其中包含您正在创建的 PDF 的文件名。继续Canvas为文件创建一个新实例hello.pdf

>>>
>>> canvas = Canvas("hello.pdf")

您现在拥有一个Canvas已分配给变量名称的实例,该实例canvas与当前工作目录中名为hello.pdfhello.pdf但是,该文件尚不存在。

让我们在 PDF 中添加一些文本。为此,您可以使用.drawString()

>>>
>>> canvas.drawString(72, 72, "Hello, World")

传递的前两个参数用于.drawString()确定在画布上写入文本的位置。第一个指定与画布左边缘的距离,第二个指定与底部边缘的距离。

传递给的值以.drawString()点为单位进行测量。由于点等于 1/72 英寸,因此.drawString(72, 72, "Hello, World")"Hello, World"距页面左侧一英寸和距页面底部一英寸处绘制字符串。

要将 PDF 保存到文件,请使用.save()

>>>
>>> canvas.save()

您现在在当前工作目录中有一个名为 .pdf 的 PDF 文件hello.pdf。你可以用PDF阅读器打开它,看到Hello, World页面底部的文字!

关于您刚刚创建的 PDF,有几点需要注意:

  1. 默认页面大小为 A4,这与标准 US letter 页面大小不同。
  2. 字体默认为 Helvetica,字体大小为 12 磅。

您不会被这些设置所困扰。

设置页面大小

实例化Canvas对象时,可以使用可选pagesize参数更改页面大小。此参数接受浮点值元组,以磅为单位表示页面的宽度和高度。

例如,要将页面大小设置为8.5英寸宽 x11英寸高,您将创建以下内容Canvas

canvas = Canvas("hello.pdf", pagesize=(612.0, 792.0))

(612, 792)表示一张信纸大小的纸,因为8.5时间72612,而11时间72792

如果将点转换为英寸或厘米的数学计算不是您的菜,那么您可以使用该reportlab.lib.units模块来帮助您进行转换。该.units模块包含几个帮助器对象,例如inchcm,可简化您的转换。

继续从模块中导入inchcm对象reportlab.lib.units

>>>
>>> from reportlab.lib.units import inch, cm

现在您可以检查每个对象以查看它们是什么:

>>>
>>> cm
28.346456692913385
>>> inch
72.0

这两个cminch的浮点值。它们代表每个单元中包含的点数。inch72.0点,cm28.346456692913385点。

要使用单位,请将单位名称乘以要转换为点的单位数。例如,以下是inch将页面大小设置为8.5英寸宽 x11英寸高的方法:

>>>
>>> canvas = Canvas("hello.pdf", pagesize=(8.5 * inch, 11 * inch))

通过将元组传递给pagesize,您可以创建所需的任何大小的页面。但是,该reportlab软件包具有一些更易于使用的标准内置页面大小。

页面大小位于reportlab.lib.pagesizes模块中。例如,要将页面大小设置为 letter,您可以LETTERpagesizes模块导入对象,并pagesize在实例化您的 时将其传递给参数Canvas

>>>
>>> from reportlab.lib.pagesizes import LETTER
>>> canvas = Canvas("hello.pdf", pagesize=LETTER)

如果您检查该LETTER对象,您会看到它是一个浮点数元组:

>>>
>>> LETTER
(612.0, 792.0)

reportlab.lib.pagesize模块包含许多标准页面大小。以下是一些尺寸:

Page Size Dimensions
A4 210 mm x 297 mm
LETTER 8.5 in x 11 in
LEGAL 8.5 in x 14 in
TABLOID 11 in x 17 in

除此之外,该模块还包含所有ISO 216 标准纸张尺寸的定义

设置字体属性

您还可以在将文本写入Canvas.

要更改字体和字体大小,您可以使用.setFont(). 首先,Canvas使用文件名font-example.pdf和字母页面大小创建一个新实例:

>>>
>>> canvas = Canvas("font-example.pdf", pagesize=LETTER)

然后将字体设置为 Times New Roman,大小为18磅:

>>>
>>> canvas.setFont("Times-Roman", 18)

最后,将字符串写入"Times New Roman (18 pt)"画布并保存:

>>>
>>> canvas.drawString(1 * inch, 10 * inch, "Times New Roman (18 pt)")
>>> canvas.save()

使用这些设置,文本将被写入距页面左侧一英寸和距底部十英寸的位置。打开font-example.pdf当前工作目录中的文件并检查它!

默认提供三种字体:

  1. "Courier"
  2. "Helvetica"
  3. "Times-Roman"

每种字体都有粗体和斜体变体。以下是 中可用的所有字体变体的列表reportlab

  • "Courier"
  • "Courier-Bold
  • "Courier-BoldOblique"
  • "Courier-Oblique"
  • "Helvetica"
  • "Helvetica-Bold"
  • "Helvetica-BoldOblique"
  • "Helvetica-Oblique"
  • "Times-Bold"
  • "Times-BoldItalic
  • "Times-Italic"
  • "Times-Roman"

您还可以使用 设置字体颜色.setFillColor()。在以下示例中,您将创建一个名为 的蓝色文本 PDF 文件font-colors.pdf

from reportlab.lib.colors import blue
from reportlab.lib.pagesizes import LETTER
from reportlab.lib.units import inch
from reportlab.pdfgen.canvas import Canvas

canvas = Canvas("font-colors.pdf", pagesize=LETTER)

# Set font to Times New Roman with 12-point size
canvas.setFont("Times-Roman", 12)

# Draw blue text one inch from the left and ten
# inches from the bottom
canvas.setFillColor(blue)
canvas.drawString(1 * inch, 10 * inch, "Blue text")

# Save the PDF file
canvas.save()

blue是从reportlab.lib.colors模块导入的对象。该模块包含几种常见颜色。可以在reportlab源代码中找到完整的颜色列表。

本节中的示例重点介绍了使用Canvas对象的基础知识。但你只是触及了表面。使用reportlab,您可以从头开始创建表格、表单,甚至是高质量的图形!

ReportLab的用户手册中包含的如何从头开始生成PDF文档的例子太多了。如果您有兴趣了解有关使用 Python 创建 PDF 的更多信息,这是一个很好的起点。

检查你的理解

展开下面的块以检查您的理解:

练习:从头开始创建 PDF显示隐藏

您可以展开下面的块以查看解决方案:

解决方案:从头开始创建 PDF显示隐藏

准备好后,您可以继续下一部分。

结论:在 Python 中创建和修改 PDF 文件

在本教程中,您学习了如何使用PyPDF2reportlab包创建和修改 PDF 文件。如果您想学习刚刚看到的示例,请务必单击以下链接下载材料:

下载示例材料: 单击此处获取您将用于了解本教程中创建和修改 PDF 文件的材料

通过PyPDF2,您学会了如何:

  • 使用该类阅读PDF 文件并提取文本PdfFileReader
  • 使用PdfFileWriter该类编写新的 PDF 文件
  • 使用类连接合并PDF 文件PdfFileMerger
  • 旋转裁剪PDF 页面
  • 使用密码加密解密PDF文件

您还介绍了如何使用该reportlab包从头开始创建 PDF 文件。你学会了如何:

  • 使用Canvas
  • 文本写入一个Canvaswith.drawString()
  • 设置字体字体大小.setFont()
  • 更改字体颜色.setFillColor()

reportlab是一个强大的 PDF 创建工具,您只是触及了可能的表面。如果您喜欢从Python Basics: A Practical Introduction to Python 3 中的示例中学到的知识,那么一定要查看本书的其余部分

快乐编码!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200