目录
- 从 PDF 中提取文本
- 打开 PDF 文件
- 从页面中提取文本
- 把它放在一起
- 检查你的理解
- 从 PDF 中提取页面
- 使用 PdfFileWriter 类
- 从 PDF 中提取单个页面
- 从 PDF 中提取多个页面
- 检查你的理解
- 连接和合并 PDF
- 使用 PdfFileMerger 类
- 使用 .append() 连接 PDF
- 使用 .merge() 合并 PDF
- 检查你的理解
- 旋转和裁剪 PDF 页面
- 旋转页面
- 裁剪页面
- 检查你的理解
- 加密和解密 PDF
- 加密 PDF
- 解密 PDF
- 检查你的理解
- 从头开始创建 PDF 文件
- 安装报告实验室
- 使用画布类
- 设置页面大小
- 设置字体属性
- 检查你的理解
- 结论:在 Python 中创建和修改 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
通过在终端中运行以下命令来验证安装:
代码语言:javascript复制$ 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:
请特别注意版本信息。在撰写本文时,最新版本PyPDF2
是1.26.0
. 如果您打开了 IDLE,则需要重新启动它才能使用该PyPDF2
软件包。
打开 PDF 文件
让我们首先打开一个 PDF 并阅读有关它的一些信息。您将使用Pride_and_Prejudice.pdf
位于practice_files/
配套存储库文件夹中的文件。
打开IDLE的互动窗口,并导入了PdfFileReader
从类PyPDF2
包:
>>>
代码语言:javascript复制>>> from PyPDF2 import PdfFileReader
要创建PdfFileReader
该类的新实例,您将需要要打开的 PDF 文件的路径。现在让我们使用pathlib
模块来实现:
>>>
代码语言:javascript复制>>> 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
实例:
>>>
代码语言:javascript复制>>> pdf = PdfFileReader(str(pdf_path))
您转换pdf_path
为字符串,因为PdfFileReader
不知道如何从pathlib.Path
对象中读取。
回忆一下第 12 章“文件输入和输出”,在程序终止之前应该关闭所有打开的文件。该PdfFileReader
对象会为您完成所有这些工作,因此您无需担心打开或关闭 PDF 文件!
现在您已经创建了一个PdfFileReader
实例,您可以使用它来收集有关 PDF 的信息。例如,.getNumPages()
返回 PDF 文件中包含的页数:
>>>
代码语言:javascript复制>>> pdf.getNumPages()
234
请注意,它.getNumPages()
是用混合大小写编写的,而不是PEP 8 中推荐的 lower_case_with_underscores 。请记住,PEP 8 是一套指南,而不是规则。就 Python 而言,mixedCase 是完全可以接受的。
注意: PyPDF2
改编自pyPdf
包。pyPdf
写于 2005 年,距 PEP 8 发布仅四年。
当时,许多 Python 程序员正在从混合大小写更常见的语言迁移。
您还可以使用该.documentInfo
属性访问一些文档信息:
>>>
代码语言:javascript复制>>> 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
属性:
>>>
代码语言:javascript复制>>> pdf.documentInfo.title
'Pride and Prejudice, by Jane Austen'
该.documentInfo
对象包含在创建 PDF 时设置的 PDF元数据。
本PdfFileReader
类提供了所有必要的方法和属性,你需要访问数据的PDF文件。让我们探索一下您可以用 PDF 文件做什么以及如何做!
从页面中提取文本
PDF 页面PyPDF2
用PageObject
类表示。您可以使用PageObject
实例与 PDF 文件中的页面进行交互。您无需PageObject
直接创建自己的实例。相反,您可以通过PdfFileReader
对象的.getPage()
方法访问它们。
从单个 PDF 页面中提取文本有两个步骤:
- 找一个
PageObject
有PdfFileReader.getPage()
。 - 使用
PageObject
实例的.extractText()
方法将文本提取为字符串。
Pride_and_Prejudice.pdf
有234
页面。每个页面都有一个介于0
和之间的索引233
。您可以PageObject
通过将页面的索引传递给PdfFileReader.getPage()
:
>>>
代码语言:javascript复制>>> first_page = pdf.getPage(0)
.getPage()
返回一个PageObject
:
>>>
代码语言:javascript复制>>> type(first_page)
<class 'PyPDF2.pdf.PageObject'>
您可以使用以下命令提取页面的文本PageObject.extractText()
:
>>>
代码语言:javascript复制>>> 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 中每一页的文本:
>>>
代码语言:javascript复制>>> for page in pdf.pages:
... print(page.extractText())
...
让我们结合您所学的一切,编写一个程序,从Pride_and_Prejudice.pdf
文件中提取所有文本并将其保存到.txt
文件中。
把它放在一起
在 IDLE 中打开一个新的编辑器窗口并输入以下代码:
代码语言:javascript复制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)
让我们分解一下:
- 首先,您将一个新
PdfFileReader
实例分配给pdf_reader
变量。您还创建了一个Path
指向Pride_and_Prejudice.txt
主目录中文件的新对象并将其分配给output_file_path
变量。 - 接下来,您
output_file_path
以写模式打开并将返回的文件对象分配给.open()
变量output_file
。您在第 12 章“文件输入和输出”中了解到的with
语句确保在块退出时关闭文件。with
- 然后,在
with
块内,使用 .pdf 将 PDF 标题和页数写入文本文件output_file.write()
。 - 最后,您使用
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
:
>>>
代码语言:javascript复制>>> from PyPDF2 import PdfFileWriter
>>> pdf_writer = PdfFileWriter()
PdfFileWriter
对象就像空白的 PDF 文件。您需要先向其中添加一些页面,然后才能将它们保存到文件中。
继续添加一个空白页到pdf_writer
:
>>>
代码语言:javascript复制>>> page = pdf_writer.addBlankPage(width=72, height=72)
在width
和height
参数是必需的,确定单位称为页面的尺寸点。一点等于 1/72 英寸,因此上面的代码将一英寸见方的空白页添加到pdf_writer
.
.addBlankPage()
返回一个新PageObject
实例,代表您添加到的页面PdfFileWriter
:
>>>
代码语言:javascript复制>>> type(page)
<class 'PyPDF2.pdf.PageObject'>
在此示例中,您已将PageObject
返回的实例分配.addBlankPage()
给page
变量,但实际上您通常不需要这样做。也就是说,您通常调用.addBlankPage()
而不将返回值分配给任何东西:
>>>
代码语言:javascript复制>>> pdf_writer.addBlankPage(width=72, height=72)
要将 的内容写入pdf_writer
PDF 文件,请将二进制写入模式的文件对象传递给pdf_writer.write()
:
>>>
代码语言:javascript复制>>> from pathlib import Path
>>> with Path("blank.pdf").open(mode="wb") as output_file:
... pdf_writer.write(output_file)
...
这会在您当前的工作目录中创建一个名为blank.pdf
. 如果您使用 PDF 阅读器(例如 Adobe Acrobat)打开文件,您将看到一个包含一个一英寸见方的空白页面的文档。
技术细节:请注意,您通过将文件对象传递给PdfFileWriter
对象的.write()
方法而不是文件对象的.write()
方法来保存 PDF 文件。
特别是,以下代码将不起作用:
>>>
代码语言:javascript复制>>> with Path("blank.pdf").open(mode="wb") as output_file:
... output_file.write(pdf_writer)
这种方法对许多新程序员来说似乎是倒退的,所以一定要避免这个错误!
PdfFileWriter
对象可以写入新的 PDF 文件,但除了空白页面之外,它们不能从头开始创建新内容。
这似乎是一个大问题,但在许多情况下,您不需要创建新内容。通常,您会使用从使用PdfFileReader
实例打开的 PDF 文件中提取的页面。
注意:您将在下面的“从头开始创建 PDF 文件”部分中了解如何从头开始创建 PDF 文件。
在上面的示例中,使用PyPDF2
以下三个步骤创建新的 PDF 文件:
- 创建一个
PdfFileWriter
实例。 - 向
PdfFileWriter
实例添加一个或多个页面。 - 使用
PdfFileWriter.write()
.
当您学习将页面添加到PdfFileWriter
实例的各种方法时,您会一遍又一遍地看到这种模式。
从 PDF 中提取单个页面
让我们重新审视您在上一节中使用的傲慢与偏见PDF。您将打开 PDF,提取第一页,然后创建一个仅包含单个提取页面的新 PDF 文件。
打开IDLE的交互式窗口和进口PdfFileReader
,并PdfFileWriter
从PyPDF2
还有Path
从类pathlib
模块:
>>>
代码语言:javascript复制>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
现在Pride_and_Prejudice.pdf
用一个PdfFileReader
实例打开文件:
>>>
代码语言:javascript复制>>> # 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的第一页:
>>>
代码语言:javascript复制>>> first_page = input_pdf.getPage(0)
现在创建一个新PdfFileWriter
实例并添加first_page
到它.addPage()
:
>>>
代码语言:javascript复制>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.addPage(first_page)
该.addPage()
方法将一个页面添加到pdf_writer
对象中的页面集,就像.addBlankPage()
. 不同之处在于它需要一个现有的PageObject
.
现在将 的内容写入pdf_writer
一个新文件:
>>>
代码语言:javascript复制>>> 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
,你需要在指标提取的网页1
,2
和3
。
您可以通过导入所需的类并打开 PDF 文件来设置所有内容:
>>>
代码语言:javascript复制>>> 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))
您的目标是提取索引1
、2
和处的页面3
,将它们添加到新PdfFileWriter
实例中,然后将它们写入新的 PDF 文件。
一种方法是循环遍历从 开始到1
结束的数字范围3
,在循环的每一步提取页面并将其添加到PdfFileWriter
实例中:
>>>
代码语言:javascript复制>>> pdf_writer = PdfFileWriter()
>>> for n in range(1, 4):
... page = input_pdf.getPage(n)
... pdf_writer.addPage(page)
...
循环遍历数字1
、2
和3
因为range(1, 4)
不包括右侧端点。在循环的每一步中,当前索引处的页面都被提取.getPage()
并添加到pdf_writer
using 中.addPage()
。
现在pdf_writer
有三页,您可以查看.getNumPages()
:
>>>
代码语言:javascript复制>>> pdf_writer.getNumPages()
3
最后,您可以将提取的页面写入新的 PDF 文件:
>>>
代码语言:javascript复制>>> with Path("chapter1.pdf").open(mode="wb") as output_file:
... pdf_writer.write(output_file)
...
现在,您可以打开chapter1.pdf
当前工作目录中的文件,阅读傲慢与偏见的第一章。
从 PDF 中提取多个页面的另一种方法是利用PdfFileReader.pages
支持切片表示法的事实。让我们使用.pages
而不是循环range
对象重做前面的示例。
首先初始化一个新PdfFileWriter
对象:
>>>
代码语言:javascript复制>>> pdf_writer = PdfFileWriter()
现在循环一个.pages
从索引开始到1
结束的片段4
:
>>>
代码语言:javascript复制>>> for page in input_pdf.pages[1:4]:
... pdf_writer.addPage(page)
...
请记住,切片中的值范围从切片中第一个索引处的项目到但不包括切片中第二个索引处的项目。所以.pages[1:4]
返回一个迭代含指数的网页1
,2
和3
。
最后,将 的内容写入pdf_writer
输出文件:
>>>
代码语言:javascript复制>>> 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
实例中:
>>>
代码语言:javascript复制>>> 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
该类并创建一个新实例:
>>>
代码语言:javascript复制>>> 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/
:
>>>
代码语言:javascript复制>>> 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 文件的可迭代路径。
查看目录中的内容:
>>>
代码语言:javascript复制>>> for path in reports_dir.glob("*.pdf"):
... print(path.name)
...
Expense report 1.pdf
Expense report 3.pdf
Expense report 2.pdf
列出了三个文件的名称,但它们没有按顺序排列。此外,您在计算机输出中看到的文件顺序可能与此处显示的输出不匹配。
通常,.glob()
不保证返回的路径顺序,因此您需要自己对它们进行排序。您可以通过创建一个包含三个文件路径的.sort()
列表然后调用该列表来完成此操作:
>>>
代码语言:javascript复制>>> expense_reports = list(reports_dir.glob("*.pdf"))
>>> expense_reports.sort()
请记住,.sort()
对列表进行就地排序,因此您无需将返回值分配给变量。调用expense_reports
后,列表将按文件名的字母顺序排序.list()
。
要确认排序有效,请expense_reports
再次循环并打印出文件名:
>>>
代码语言:javascript复制>>> 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
类并创建一个新实例:
>>>
代码语言:javascript复制>>> from PyPDF2 import PdfFileMerger
>>> pdf_merger = PdfFileMerger()
现在遍历排序expense_reports
列表中的路径并将它们附加到pdf_merger
:
>>>
代码语言:javascript复制>>> 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()
方法:
>>>
代码语言:javascript复制>>> 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
类并Path
为report.pdf
和toc.pdf
文件创建对象:
>>>
代码语言:javascript复制>>> 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()
:
>>>
代码语言:javascript复制>>> pdf_merger = PdfFileMerger()
>>> pdf_merger.append(str(report_path))
现在其中pdf_merger
有一些页面,您可以在正确的位置将目录 PDF 合并到其中。如果您report.pdf
使用 PDF 阅读器打开文件,您将看到报告的第一页是标题页。第二个是简介,其余页面包含不同的报告部分。
您想在标题页之后和介绍部分之前插入目录。由于 PDF 页面索引以0
in开头PyPDF2
,因此您需要在 index 页面之后0
和 index 页面之前插入目录1
。
为此,请pdf_merger.merge()
使用两个参数调用:
- 整数
1
,指示应插入目录的页的索引 - 包含目录的 PDF 文件路径的字符串
这是它的样子:
>>>
代码语言:javascript复制>>> pdf_merger.merge(1, str(toc_path))
目录 PDF 中的每一页都插入在 index 处的页面之前1
。由于目录 PDF 只有一页,因此它被插入到 index 处1
。当前位于 index 的页面1
然后被转移到 index 2
。当前位于 index 的页面2
被转移到 index 3
,依此类推。
现在将合并的 PDF 写入输出文件:
>>>
代码语言:javascript复制>>> 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 交互窗口中,首先从 导入PdfFileReader
和PdfFileWriter
类PyPDF2
,以及Path
从pathlib
模块中导入类:
>>>
代码语言:javascript复制>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
现在Path
为ugly.pdf
文件创建一个对象:
>>>
代码语言:javascript复制>>> pdf_path = (
... Path.home()
... / "creating-and-modifying-pdfs"
... / "practice_files"
... / "ugly.pdf"
... )
最后,创建新的PdfFileReader
和PdfFileWriter
实例:
>>>
代码语言:javascript复制>>> 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
.
这是它的样子:
>>>
代码语言:javascript复制>>> 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
新文件并检查一切是否正常:
>>>
代码语言:javascript复制>>> with Path("ugly_rotated.pdf").open(mode="wb") as output_file:
... pdf_writer.write(output_file)
...
您现在应该在当前工作目录中有一个名为ugly_rotated.pdf
的ugly.pdf
文件,该文件中的页面全部正确旋转。
您刚才用来旋转ugly.pdf
文件中页面的方法的问题在于,它取决于提前知道哪些页面需要旋转。在实际场景中,浏览整个 PDF 并记下要旋转的页面是不切实际的。
事实上,您可以在没有先验知识的情况下确定哪些页面需要旋转。嗯,有时你可以。
让我们看看如何,从一个新PdfFileReader
实例开始:
>>>
代码语言:javascript复制>>> pdf_reader = PdfFileReader(str(pdf_path))
您需要这样做是因为您PdfFileReader
通过旋转旧实例中的页面来更改它们。因此,通过创建新实例,您将重新开始。
PageObject
实例维护一个包含页面信息的值字典:
>>>
代码语言:javascript复制>>> 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
。
您可以使用下标符号访问/Rotate
键PageObject
,就像在 Pythondict
对象上一样:
>>>
代码语言:javascript复制>>> page = pdf_reader.getPage(0)
>>> page["/Rotate"]
-90
如果您查看 中/Rotate
第二页的键pdf_reader
,您会看到它的值为0
:
>>>
代码语言:javascript复制>>> page = pdf_reader.getPage(1)
>>> page["/Rotate"]
0
这一切意味着 index 处的页面0
具有-90
度数的旋转值。换句话说,它逆时针旋转了九十度。index 处的页面1
的旋转值为0
,因此它根本没有旋转。
如果使用 旋转第一页.rotateClockwise()
,则值/Rotate
将从-90
变为0
:
>>>
代码语言:javascript复制>>> page = pdf_reader.getPage(0)
>>> page["/Rotate"]
-90
>>> page.rotateClockwise(90)
>>> page["/Rotate"]
0
现在您知道如何检查/Rotate
密钥,您可以使用它来旋转ugly.pdf
文件中的页面。
您需要做的第一件事是重新初始化您的pdf_reader
和pdf_writer
对象,以便您重新开始:
>>>
代码语言:javascript复制>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()
现在编写一个循环,循环遍历pdf_reader.pages
可迭代中的页面,检查 的值/Rotate
,如果该值是 ,则旋转页面-90
:
>>>
代码语言:javascript复制>>> for page in pdf_reader.pages:
... if page["/Rotate"] == -90:
... page.rotateClockwise(90)
... pdf_writer.addPage(page)
...
这个循环不仅比第一个解决方案中的循环略短,而且它不依赖于关于哪些页面需要旋转的任何先验知识。您可以使用这样的循环来旋转任何 PDF 中的页面,而无需打开它并查看它。
要完成解决方案,请将 的内容写入pdf_writer
新文件:
>>>
代码语言:javascript复制>>> 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...except
block捕获此异常。
的价值/Rotate
可能并不总是您所期望的。例如,如果您在页面逆时针旋转 90 度的情况下扫描纸质文档,则 PDF 的内容将显示为旋转。但是,/Rotate
键可能具有值0
。
这是许多使处理 PDF 文件令人沮丧的怪癖之一。有时您只需要在 PDF 阅读器程序中打开 PDF 并手动解决问题。
裁剪页面
PDF 的另一个常见操作是裁剪页面。您可能需要这样做以将单个页面拆分为多个页面或仅提取页面的一小部分,例如签名或图形。
例如,该practice_files
文件夹包含一个名为half_and_half.pdf
. 此 PDF 包含汉斯·克里斯蒂安·安徒生 (Hans Christian Andersen) 的《小美人鱼》的一部分。
此 PDF 中的每一页都有两列。让我们将每一页分成两页,每一列一页。
首先,从模块中导入PdfFileReader
和PdfFileWriter
类PyPDF2
和Path
类pathlib
:
>>>
代码语言:javascript复制>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
现在Path
为half_and_half.pdf
文件创建一个对象:
>>>
代码语言:javascript复制>>> pdf_path = (
... Path.home()
... / "creating-and-modifying-pdfs"
... / "practice_files"
... / "half_and_half.pdf"
... )
接下来,创建一个新PdfFileReader
对象并获取 PDF 的第一页:
>>>
代码语言:javascript复制>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> first_page = pdf_reader.getPage(0)
要裁剪页面,您首先需要更多地了解页面的结构。PageObject
像first_page
这样的实例有一个.mediaBox
属性,表示定义页面边界的矩形区域。
.mediaBox
在使用它裁剪页面之前,您可以使用 IDLE 的交互式窗口来探索:
>>>
代码语言:javascript复制>>> first_page.mediaBox
RectangleObject([0, 0, 792, 612])
该.mediaBox
属性返回一个RectangleObject
. 该对象在PyPDF2
包中定义,代表页面上的一个矩形区域。
[0, 0, 792, 612]
输出中的列表定义了矩形区域。前两个数字是矩形左下角的 x 和 y 坐标。第三个和第四个数字分别代表矩形的宽度和高度。所有值的单位都是点,等于 1/72 英寸。
RectangleObject([0, 0, 792, 612])
表示以左下角为原点、宽度为792
11 英寸、高度为 612 磅或 8.5 英寸的矩形区域。这些是横向标准信纸尺寸页面的尺寸,用于The Little Mermaid的示例 PDF 。纵向的信纸大小的 PDF 页面将返回输出RectangleObject([0, 0, 612, 792])
.
一个RectangleObject
有四个属性返回矩形的角的坐标:.lowerLeft
,.lowerRight
,.upperLeft
,和.upperRight
。就像宽度和高度值一样,这些坐标以点为单位。
您可以使用这四个属性来获取 的每个角的坐标RectangleObject
:
>>>
代码语言:javascript复制>>> 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 元组一样使用方括号访问单个坐标:
>>>
代码语言:javascript复制>>> first_page.mediaBox.upperRight[0]
792
>>> first_page.mediaBox.upperRight[1]
612
您可以mediaBox
通过将新元组分配给其属性之一来更改 a 的坐标:
>>>
代码语言:javascript复制>>> first_page.mediaBox.upperLeft = (0, 480)
>>> first_page.mediaBox.upperLeft
(0, 480)
当您更改.upperLeft
坐标时,.upperRight
属性会自动调整以保留矩形形状:
>>>
代码语言:javascript复制>>> first_page.mediaBox.upperRight
(792, 480)
当您更改由RectangleObject
返回的坐标时.mediaBox
,您可以有效地裁剪页面。该first_page
对象现在仅包含新RectangleObject
.
继续将裁剪后的页面写入一个新的 PDF 文件:
>>>
代码语言:javascript复制>>> 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
目的。让我们看看它是如何工作的。
首先,您需要获取 newPdfFileReader
和PdfFileWriter
objects,因为您刚刚更改了第一页pdf_reader
并将其添加到pdf_writer
:
>>>
代码语言:javascript复制>>> pdf_reader = PdfFileReader(str(pdf_path))
>>> pdf_writer = PdfFileWriter()
现在获取 PDF 的第一页:
>>>
代码语言:javascript复制>>> first_page = pdf_reader.getPage(0)
这一次,让我们处理第一页的副本,以便您刚刚提取的页面保持完整。您可以通过copy
从 Python 的标准库中导入模块并使用deepcopy()
来制作页面的副本来做到这一点:
>>>
代码语言:javascript复制>>> import copy
>>> left_side = copy.deepcopy(first_page)
现在您可以在left_side
不更改first_page
. 这样,您可以first_page
稍后使用来提取页面右侧的文本。
现在你需要做一些数学运算。您已经确定需要将 的右上角移动.mediaBox
到页面的顶部中心。为此,您将创建一个新的tuple
,其中第一个组件等于原始值的一半,并将其分配给.upperRight
属性。
首先,获取 .png 右上角的当前坐标.mediaBox
。
>>>
代码语言:javascript复制>>> current_coords = left_side.mediaBox.upperRight
然后创建一个新的,tuple
它的第一个坐标是当前坐标值的一半,第二个坐标与原始坐标相同:
>>>
代码语言:javascript复制>>> new_coords = (current_coords[0] / 2, current_coords[1])
最后,将新坐标分配给.upperRight
属性:
>>>
代码语言:javascript复制>>> left_side.mediaBox.upperRight = new_coords
您现在已经裁剪了原始页面以仅包含左侧的文本!接下来让我们提取页面的右侧。
首先获得一个新副本first_page
:
>>>
代码语言:javascript复制>>> right_side = copy.deepcopy(first_page)
移动.upperLeft
角落而不是.upperRight
角落:
>>>
代码语言:javascript复制>>> right_side.mediaBox.upperLeft = new_coords
这会将左上角设置为与您在提取页面左侧时将右上角移动到的坐标相同的坐标。所以,right_side.mediaBox
现在是一个矩形,其左上角位于页面的顶部中心,右上角位于页面的右上角。
最后,添加left_side
和right_side
页面pdf_writer
并将它们写入一个新的 PDF 文件:
>>>
代码语言:javascript复制>>> 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()
。它有两个主要参数:
user_pwd
设置用户密码。这允许打开和阅读 PDF 文件。owner_pwd
设置所有者密码。这允许在没有任何限制的情况下打开 PDF,包括编辑。
让我们使用.encrypt()
为 PDF 文件添加密码。首先,打开newsletter.pdf
目录中的practice_files
文件:
>>>
代码语言:javascript复制>>> 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
:
>>>
代码语言:javascript复制>>> pdf_writer = PdfFileWriter()
>>> pdf_writer.appendPagesFromReader(pdf_reader)
接下来,添加密码"SuperSecret"
有pdf_writer.encrypt()
:
>>>
代码语言:javascript复制>>> pdf_writer.encrypt(user_pwd="SuperSecret")
当您设置 only 时user_pwd
,owner_pwd
参数默认为相同的字符串。因此,上面的代码行设置了用户和所有者密码。
最后,将加密的 PDF 写入主目录中名为 的输出文件newsletter_protected.pdf
:
>>>
代码语言:javascript复制>>> 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
参数:
>>>
代码语言:javascript复制>>> 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 的路径创建一个新实例:
>>>
代码语言:javascript复制>>> from pathlib import Path
>>> from PyPDF2 import PdfFileReader, PdfFileWriter
>>> pdf_path = Path.home() / "newsletter_protected.pdf"
>>> pdf_reader = PdfFileReader(str(pdf_path))
在解密 PDF 之前,请检查如果您尝试获取第一页会发生什么:
>>>
代码语言:javascript复制>>> 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文件尚未解密。
注意:上面的回溯已被缩短以突出重要部分。您在计算机上看到的回溯会更长。
现在继续解密文件:
>>>
代码语言:javascript复制>>> pdf_reader.decrypt(password="SuperSecret")
1
.decrypt()
返回一个表示解密成功的整数:
0
表示密码错误。1
表示用户密码匹配。2
表示已匹配所有者密码。
解密文件后,您可以访问 PDF 的内容:
>>>
代码语言:javascript复制>>> 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
要开始,你需要安装reportlab
有pip
:
$ 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:usersdaveavenvlibsite-packages
Requires: pillow
Required-by:
在撰写本文时,最新版本reportlab
是 3.5.34。如果您打开了 IDLE,则需要重新启动它才能使用该reportlab
软件包。
使用Canvas
类
用于与创建PDF主界面reportlab
是Canvas
类,它位于在reportlab.pdfgen.canvas
模块。
打开一个新的 IDLE 交互窗口并键入以下内容以导入Canvas
类:
>>>
代码语言:javascript复制>>> from reportlab.pdfgen.canvas import Canvas
创建新Canvas
实例时,您需要提供一个字符串,其中包含您正在创建的 PDF 的文件名。继续Canvas
为文件创建一个新实例hello.pdf
:
>>>
代码语言:javascript复制>>> canvas = Canvas("hello.pdf")
您现在拥有一个Canvas
已分配给变量名称的实例,该实例canvas
与当前工作目录中名为hello.pdf
. hello.pdf
但是,该文件尚不存在。
让我们在 PDF 中添加一些文本。为此,您可以使用.drawString()
:
>>>
代码语言:javascript复制>>> canvas.drawString(72, 72, "Hello, World")
传递的前两个参数用于.drawString()
确定在画布上写入文本的位置。第一个指定与画布左边缘的距离,第二个指定与底部边缘的距离。
传递给的值以.drawString()
点为单位进行测量。由于点等于 1/72 英寸,因此.drawString(72, 72, "Hello, World")
在"Hello, World"
距页面左侧一英寸和距页面底部一英寸处绘制字符串。
要将 PDF 保存到文件,请使用.save()
:
>>>
代码语言:javascript复制>>> canvas.save()
您现在在当前工作目录中有一个名为 .pdf 的 PDF 文件hello.pdf
。你可以用PDF阅读器打开它,看到Hello, World
页面底部的文字!
关于您刚刚创建的 PDF,有几点需要注意:
- 默认页面大小为 A4,这与标准 US letter 页面大小不同。
- 字体默认为 Helvetica,字体大小为 12 磅。
您不会被这些设置所困扰。
设置页面大小
实例化Canvas
对象时,可以使用可选pagesize
参数更改页面大小。此参数接受浮点值元组,以磅为单位表示页面的宽度和高度。
例如,要将页面大小设置为8.5
英寸宽 x11
英寸高,您将创建以下内容Canvas
:
canvas = Canvas("hello.pdf", pagesize=(612.0, 792.0))
(612, 792)
表示一张信纸大小的纸,因为8.5
时间72
是612
,而11
时间72
是792
。
如果将点转换为英寸或厘米的数学计算不是您的菜,那么您可以使用该reportlab.lib.units
模块来帮助您进行转换。该.units
模块包含几个帮助器对象,例如inch
和cm
,可简化您的转换。
继续从模块中导入inch
和cm
对象reportlab.lib.units
:
>>>
代码语言:javascript复制>>> from reportlab.lib.units import inch, cm
现在您可以检查每个对象以查看它们是什么:
>>>
代码语言:javascript复制>>> cm
28.346456692913385
>>> inch
72.0
这两个cm
和inch
的浮点值。它们代表每个单元中包含的点数。inch
是72.0
点,cm
是28.346456692913385
点。
要使用单位,请将单位名称乘以要转换为点的单位数。例如,以下是inch
将页面大小设置为8.5
英寸宽 x11
英寸高的方法:
>>>
代码语言:javascript复制>>> canvas = Canvas("hello.pdf", pagesize=(8.5 * inch, 11 * inch))
通过将元组传递给pagesize
,您可以创建所需的任何大小的页面。但是,该reportlab
软件包具有一些更易于使用的标准内置页面大小。
页面大小位于reportlab.lib.pagesizes
模块中。例如,要将页面大小设置为 letter,您可以LETTER
从pagesizes
模块导入对象,并pagesize
在实例化您的 时将其传递给参数Canvas
:
>>>
代码语言:javascript复制>>> from reportlab.lib.pagesizes import LETTER
>>> canvas = Canvas("hello.pdf", pagesize=LETTER)
如果您检查该LETTER
对象,您会看到它是一个浮点数元组:
>>>
代码语言:javascript复制>>> 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
和字母页面大小创建一个新实例:
>>>
代码语言:javascript复制>>> canvas = Canvas("font-example.pdf", pagesize=LETTER)
然后将字体设置为 Times New Roman,大小为18
磅:
>>>
代码语言:javascript复制>>> canvas.setFont("Times-Roman", 18)
最后,将字符串写入"Times New Roman (18 pt)"
画布并保存:
>>>
代码语言:javascript复制>>> canvas.drawString(1 * inch, 10 * inch, "Times New Roman (18 pt)")
>>> canvas.save()
使用这些设置,文本将被写入距页面左侧一英寸和距底部十英寸的位置。打开font-example.pdf
当前工作目录中的文件并检查它!
默认提供三种字体:
"Courier"
"Helvetica"
"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 文件
在本教程中,您学习了如何使用PyPDF2
和reportlab
包创建和修改 PDF 文件。如果您想学习刚刚看到的示例,请务必单击以下链接下载材料:
下载示例材料: 单击此处获取您将用于了解本教程中创建和修改 PDF 文件的材料。
通过PyPDF2
,您学会了如何:
- 使用该类阅读PDF 文件并提取文本
PdfFileReader
- 使用
PdfFileWriter
该类编写新的 PDF 文件 - 使用类连接和合并PDF 文件
PdfFileMerger
- 旋转和裁剪PDF 页面
- 使用密码加密和解密PDF文件
您还介绍了如何使用该reportlab
包从头开始创建 PDF 文件。你学会了如何:
- 使用
Canvas
类 - 将文本写入一个
Canvas
with.drawString()
- 设置字体和字体大小与
.setFont()
- 更改字体颜色与
.setFillColor()
reportlab
是一个强大的 PDF 创建工具,您只是触及了可能的表面。如果您喜欢从Python Basics: A Practical Introduction to Python 3 中的示例中学到的知识,那么一定要查看本书的其余部分。
快乐编码!