Skip to main content

PDF 文件阅读器/编写器库

项目描述

作者

帕特里克·莫平

<nav class="contents" id="contents" role="doc-toc">

内容

</nav>

1简介

pdfrw是一个 Python 库和实用程序,用于读取和写入 PDF 文件:

  • 0.4 版经过测试,可在 Python 2.6、2.7、3.3、3.4、3.5 和 3.6 上运行

  • 操作包括子集、合并、旋转、修改元数据等。

  • 最快的纯 Python PDF 解析器

  • 多年来一直被印刷商用于印前生产

  • 可与 rst2pdf 配合使用,忠实再现矢量图

  • 可以单独使用,也可以与reportlab结合使用, 以在新 PDF 中重用现有 PDF

  • 许可许可

pdfrw 将忠实地再现矢量格式而不进行光栅化,因此自 2010 年 3 月以来,rst2pdf 包默认使用 pdfrw 处理 PDF 和 SVG 图像。

pdfrw 也可以与 reportlab 结合使用,以便在使用 reportlab 创建的新 PDF 中重复使用现有 PDF 的部分内容。

2例子

该库附带了几个示例,显示了使用和不使用 reportlab 的操作。

2.1所有例子

示例目录有一些使用该库的脚本。请注意,如果这些示例不适用于您的 PDF,您应该先尝试使用 pdftk 解压缩和/或解密它们。

  • 4up.py将缩小页面并在每个输出页面上放置 4 个页面。

  • alter.py显示了修改元数据的示例,而不改变 PDF 的结构。

  • booklet.py显示了一个创建适合打印和折叠的 2-up 输出的示例(例如在小报尺寸的纸张上)。

  • cat.py显示了将多个 PDF 连接在一起的示例。

  • extract.py将从现有的 PDF 中提取图像和表单 XObjects(嵌入页面),以使它们更易于使用和从新的 PDF 中引用(例如,使用 reportlab 或 rst2pdf)。

  • poster.py增加了 PDF 的大小,因此可以将其打印为海报。

  • print_two.py允许通过在打印后将 8.5 X 11” 纸张切开来创建 8.5 X 5.5” 小册子。

  • rotate.py旋转 PDF 中的所有或选定页面。

  • subset.py仅使用原始页面的子集创建新 PDF。

  • unspread.py采用 2-up PDF,并拆分页面。

  • watermark.py在 PDF 的所有页面上方或下方添加水印 PDF 图像。

  • rl1/4up.py另一个 4up 示例,使用 reportlab 画布进行输出。

  • rl1/booklet.py另一个小册子示例,使用 reportlab 画布进行输出。

  • rl1/subset.py另一个子集示例,使用 reportlab 画布进行输出。

  • rl1/platypus_pdf_template.py另一个水印示例,使用 reportlab 画布并为文档生成输出。由用户 asannes 提供。

  • rl2用于解析图形的实验代码。需要工作。

  • subset_booklets.py展示了一个以更专业和实用的方式创建完整的可打印 pdf 版本的示例(请查看 http://www.wikihow.com/Bind-a-Book

2.2选例注释

2.2.1重新组织页面并将它们放在一起

带有精美打印机和/或 Acrobat 完整副本的打印机可以轻松地将您的小型 PDF 转换为小册子(例如,在单个 11" x 17" 上打印 4 个字母大小的页面)。

但这假设有几件事,包括人员知道如何操作硬件和软件。booklet.py可让您将 PDF 转换为预先格式化的小册子,以减少他们搞砸的机会。

2.2.2添加或修改元数据

cat.py示例将在命令行接受多个输入文件,在将一些无意义的元数据添加到输出 PDF 文件之后,将它们连接起来并输出到 output.pdf。

alter.py示例更改PDF中的单个元数据项,并将结果写入新 PDF。

一个区别是,由于cat正在创建一个新的 PDF 结构,而alter正在尝试修改现有的 PDF 结构,因此由 alter(以及 watermark.py)生成的 PDF应该更忠实于原始文件(除了所需的变化)。

例如,alter.py 导航应该保持不变,而 cat.py 将被剥离。

2.2.3旋转加倍

如果您想打印像小册子一样的东西,但需要螺旋装订,您要么必须进行一些花哨的重新排列,要么只浪费一半的纸张。

例如,print_two.py示例程序将在每个输出表上制作 PDF 的每一页的两个并排副本。

但是,每隔一页翻转一次,这样您就可以进行双面打印,并且页面将正确排列并进行预分页。

2.2.4图形流解析概念证明

copy.py脚本显示了一个阅读PDF的简单示例,并使用 decodegraphics.py 模块尝试通过 reportlab 画布将相同的信息写入新的 PDF。(如果您了解reportlab,就会知道如果您可以忠实地将PDF 渲染到reportlab 画布上,那么您几乎可以对PDF 执行任何您想要的操作。)只有在您确实需要时才应该进行这种低级操作至。decodegraphics 不仅仅是一个概念证明,而不是其他任何东西。在大多数情况下,只需使用 Form XObject 功能,如示例/rl1/booklet.py 演示中所示。

3 pdfrw 哲学

3.1核心库

pdfrw 库部分的理念是提供直观的函数来读取、操作和编写 PDF 文件。抽象层之间应该有最小的泄漏,尽管完成有用的工作会使“纯”功能分离变得困难。

该库支持的一个关键概念是使用 Form XObjects,它允许将一个 PDF 的片段轻松嵌入到另一个 PDF 中。

向库添加核心支持通常是经过仔细和深思熟虑的,以免因太多特殊情况而将其弄乱。

有很多格式不正确的 PDF 漂浮在各处;在某些情况下会添加对这些的支持。该决定通常基于 acroread 和 okular 对 PDF 的处理方式;如果他们可以正确显示它们,那么最终 pdfrw 也应该如此,如果它不太困难或成本不高的话。

欢迎投稿;一位用户贡献了一些解压缩过滤器和处理 PDF 1.5 流对象的能力。显然有用的其他功能包括额外的解压缩过滤器、处理受密码保护的 PDF 的能力以及输出线性化 PDF 的能力。

3.2例子

这些示例的理念是提供展示 pdfrw 功能的小型、易于理解的示例。

4个PDF文件和Python

4.1简介

一般来说,PDF 文件在概念上可以很好地映射到 Python。要考虑的主要对象是:

  • 字符串。大多数东西都是字符串。这些也经常自然分解成

  • 令牌列表。可以组合令牌以创建更高级别的对象,例如

  • 数组

  • 字典

  • 内容流(可以是更多的令牌流)

4.2困难

将 PDF 文件映射到 Python 的明显主要困难是“间接对象”的 PDF 文件概念。间接对象​​提供了允许从多个包含对象引用单个数据的效率,但可能更重要的是,间接对象提供了一种在映射任意数据结构时解决循环对象引用的鸡和蛋问题的方法到文件。为了展平循环引用,间接对象被引用而不是直接包含在另一个对象中。PDF 文件具有用于定位间接对象的全局机制,并且它们都有两个参考编号(参考编号和“生成”编号,以防您想附加到 PDF 文件而不是重写整个文件)。

pdfrw 在读取 PDF 文件时自动处理间接引用。当 pdfrw 遇到间接 PDF 文件对象时,它创建的相应 Python 对象将具有值为 True 的“间接”属性。在编写 PDF 文件时,如果您创建了任意数据,您只需通过在每个循环中至少在一个对象上放置一个名为“indirect”的属性来确保循环引用被分解。

另一个不能完全映射到常规 Python 的 PDF 文件概念是“流”。流是字典,每个字典都有一个关联的未格式化数据块。pdfrw 通过在子类字典上放置一个特殊属性来处理流。

4.3使用模式

pdfrw 的使用模型将大多数对象视为字符串(将它们写入文件时采用字符​​串表示形式)。两个主要的例外是 PdfArray 对象和 PdfDict 对象。

PdfArray 是列表的子类,具有两个特殊功能。首先,“间接”属性允许将 PdfArray 作为间接 PDF 对象写出。其次,pdfrw 会延迟读取文件,因此 PdfArray 会根据需要了解并解析对其他间接对象的引用。

PdfDict 是 dict 的子类,它也具有间接属性和惰性引用解析。(并且子类 IndirectPdfDict 间接自动设置为 True)。

但是 PdfDict 也有一个可选的关联流。流对象默认为无,但如果您将流分配给字典,它将自动为字典设置 PDF /Length 属性。

最后,由于 PdfDict 实例由 PdfName 对象(始终以 / 开头)进行索引,并且由于大多数(全部?)标准 Adob​​e PdfName 对象使用格式为“/CamelCase”的名称,因此允许通过对象属性访问字典元素是有意义的访问以及对象索引访问。因此 PdfDict 对象的使用通常是通过属性访问,尽管可以通过字典索引查找来访问非标准名称(尽管仍然带有前导斜杠)。

4.3.1阅读 PDF

PdfReader 对象是 PdfDict 的子类,它允许轻松访问整个文档:

>>> from pdfrw import PdfReader
>>> x = PdfReader('source.pdf')
>>> x.keys()
['/Info', '/Size', '/Root']
>>> x.Info
{'/Producer': '(cairo 1.8.6 (http://cairographics.org))',
 '/Creator': '(cairo 1.8.6 (http://cairographics.org))'}
>>> x.Root.keys()
['/Type', '/Pages']

Info、Size 和 Root 从 PDF 文件的预告片中检索。

除了树形结构之外,pdfrw 还创建了一个名为pages的特殊属性,即文档中所有页面的列表。pdfrw 创建pages属性作为用户的简化,因为 PDF 格式允许任意复杂的嵌套字典来描述页面顺序。pages列表中的每个条目都是文件中某个页面的 PdfDict 对象,按顺序排列。

>>> len(x.pages)
1
>>> x.pages[0]
{'/Parent': {'/Kids': [{...}], '/Type': '/Pages', '/Count': '1'},
 '/Contents': {'/Length': '11260', '/Filter': None},
 '/Resources': ... (Lots more stuff snipped)
>>> x.pages[0].Contents
{'/Length': '11260', '/Filter': None}
>>> x.pages[0].Contents.stream
'q\n1 1 1 rg /a0 gs\n0 0 0 RG 0.657436
  w\n0 J\n0 j\n[] 0.0 d\n4 M q' ... (Lots more stuff snipped)

4.3.2编写 PDF

如您所见,深入研究 PDF 文档非常容易。但是什么时候该写出来呢?

>>> from pdfrw import PdfWriter
>>> y = PdfWriter()
>>> y.addpage(x.pages[0])
>>> y.write('result.pdf')

这就是创建新 PDF 所需的全部内容。您可能仍需要阅读 Adob​​e PDF 参考手册以了解 PDF 中需要包含哪些内容 但至少您不必费力实际构建它并获得正确的文件偏移量。

4.3.3在内存中操作 PDF

在大多数情况下,pdfrw 试图对 PDF 文件的内容不可知,并将它们作为容器来支持,但是为了做有用的工作,需要更高级别的东西,所以 pdfrw 努力理解一些关于容器。例如:

  • PDF 页面。pdfrw 知道的足够多,可以在您读入的 PDF 文件中找到页面,并将一组页面写回新的 PDF 文件。

  • 形成 XObject。pdfrw 可以获取页面上的任何页面或矩形,并将其转换为 Form XObject,适合在另一个 PDF 文件中使用。它对这些有足够的了解以执行缩放、旋转和定位。

  • 报告实验室对象。pdfrw 可以从其内部对象格式递归地创建一组 reportlab 对象。例如,这允许在 reportlab 中使用 Form XObjects,以便您在使用 reportlab 构建新 PDF 时可以重用现有 PDF 文件中的内容。

示例代码目录中有几个示例演示了这些功能。

4.3.4缺失特征

即使作为一个纯 PDF 容器库,pdfrw 也有点短。目前不支持:

  • 大多数压缩/解压缩过滤器

  • 加密

pdftk是一个很棒的命令行工具,可以转换您的 PDF 以消除加密和压缩。但是,在大多数情况下,您可以对 PDF 进行很多有用的工作,而无需实际删除压缩,因为实际上只有 PDF 中的某些元素会被压缩。

5图书馆内部

5.1简介

pdfrw目前由 19 个模块组成,分为一个主包和一个子包。

__init.py__模块通常从一些子模块导入一些主要属性,errors.py 模块支持日志记录和异常生成。

5.2 PDF对象模型支持

objects子包包含一个模块,用于 PDF 文件中存在的各种基本对象的每种内部表示,该包中的objects /__init__.py模块只是将它们收集起来并提供给主 pdfrw包裹。

所有 PDF 对象类的一个共同特点是包含一个“间接”属性。如果 'indirect' 存在并且计算结果为 True,那么当对象被写出时,它被写成间接对象。也就是说,它在 PDF 文件中是可寻址的,并且可以被任意数量(包括零)的容器对象引用。这种间接对象功能通过允许从多个页面引用字体等对象来节省 PDF 文件中的空间,并且还允许 PDF 文件包含内部循环引用。例如,当每个页面对象在其字典中都有一个“父”对象时,就会使用后一种功能。

5.2.1普通对象

objects/pdfobject.py模块包含 PdfObject 类,它是 str 的子类,并且是未由其他对象明确表示的任何 PDF 文件元素的包罗万象的对象,如下所述。

5.2.2命名对象

objects/pdfname.py模块包含 PdfName 单例对象,该对象将通过在斜杠前面添加一个字符串将其转换为 PDF 名称。可以通过调用它或获取属性来使用它,例如:

PdfName.Rotate == PdfName('Rotate') == PdfObject('/Rotate')

在上面的示例中,从 PdfName 返回的对象与从 PdfObject 返回的对象之间存在细微差别。PdfName 对象实际上是“BasePdfName”类的对象。这很重要,因为只有这些可以用作 PdfDict 对象中的键。

5.2.3字符串对象

objects/pdfstring.py 模块包含 PdfString 类,它是 str 的子类,用于表示 PDF 文件中的编码字符串。该类具有字符串的编码和解码方法。

5.2.4数组对象

objects/pdfarray.py 模块包含 PdfArray 类,它是 list 的子类,用于表示 PDF 文件中的数组。可以使用常规列表代替,但使用 PdfArray 类允许设置间接属性,并且还允许以对 pdfrw 透明的方式代理未解析的间接对象(尚未读入)客户。

5.2.5字典对象

objects/pdfdict.py 模块包含 PdfDict 类,它是 dict 的子类,用于表示 PDF 文件中的字典。可以使用常规 dict 代替,但 PdfDict 类更符合 PDF 文件的要求:

  • 未解析的间接对象的透明(从库客户端的角度)代理

  • 为不存在的键返回 None(如 dict.get)

  • 属性访问到 dict 本身的映射 (pdfdict.Foo == pdfdict[NameObject('Foo')])

  • 自动管理内容字典的以下流和 /Length 属性

  • 间接属性

  • 可以设置其他属性以供图书馆和/或其客户的私人内部使用。

  • 支持在父词典中搜索 PDF“可继承”属性。

如果 PdfDict 在 PDF 文件中有关联的数据流,则通过“stream”(全部小写)属性访问该流。在 PdfDict 上设置流属性也会自动设置 /Length 属性。如果这不是所需的(例如,如果流被压缩),则可以使用 _stream(带有下划线的同名)将流与 PdfDict 相关联,而无需设置长度。

要在字典上设置私有属性(不会写入新的 PDF 文件),请使用 'private' 属性:

mydict.private.foo = 1

一旦设置了属性,就可以直接作为字典的属性访问它:

foo = mydict.foo

PDF 页面的某些属性是“可继承的”。也就是说,它们可能属于父字典(或父字典的父字典等)。“可继承”属性允许轻松发现这些:

mediabox = mypage.inheritable.MediaBox

5.2.6代理对象

objects/pdfindirect.py 模块包含 PdfIndirect 类,它是尚未从文件中读取和解析的 PDF 对象的非透明代理对象。尽管这些在库中是不透明的,但客户端代码永远不会看到其中之一——它们存在于 PdfArray 和 PdfDict 容器类型中,但在返回给这些类型的客户端之前会被解析。

5.3文件读取、标记化和解析

pdfreader.py 包含 PdfReader 类,它可以读取 PDF 文件(或传递文件对象或已读取的字符串)并解析它。它使用 tokens.py 中的 PdfTokens 类进行 低级标记化。

PdfReader 类通常不会解析到容器中(例如,在内容流中)。在 examples/rl2 子目录中有一个这样做的概念证明,但这很慢,开发不完善,对大多数应用程序没有用处。

PdfReader 类的一个实例是 PdfDict 的一个实例——确切地说,是 PDF 文件的预告字典。它将在其上设置一个名为“pages”的私有属性,该属性是一个包含文件中所有页面的列表。

实例化 PdfReader 对象时,有一些选项可用于解压缩文件中的所有对象。pdfrw 目前没有很多解压选项,所以这并不是很有用,除非在压缩对象流的特定情况下。

此外,还没有解密选项。如果您有加密或高度压缩的 PDF 文件,您可能会发现在它们上使用其他程序(如 pdftk)可以使它们被 pdfrw 读取。

通常,对象是从文件中延迟读取的,但压缩对象流目前并非如此——所有这些都在 PdfReader 实例化时解压缩并读入。

5.4文件输出

pdfwriter.py 包含 PdfWriter 类,它可以创建和输出 PDF 文件。

创建和使用此类时有几个选项可用。

在最简单的情况下,实例化 PdfWriter 的一个实例,然后从一个或多个源文件(或以编程方式创建)将页面添加到其中,然后调用 write 方法将结果转储到文件中。

如果你有一个源 PDF 并且不想太破坏它的结构,那么你可以将它的预告片直接传递给 PdfWriter 而不是让 PdfWriter 为你构建一个。在示例目录中有一个示例(alter.py)。

5.5高级功能

buildxobj.py 包含从页面或页面上的矩形构建表单 XObject 的函数。这些可以在新的 PDF 中重复使用,就像它们是图像一样。

buildxobj 小心地缓存任何使用的页面,以便它只出现在输出中一次。

toreportlab.py 提供了 makerl 函数,它将 pdfrw 对象转换为可以与reportlab一起使用的格式。它通常与 buildxobj 结合使用,以便在使用 reportlab 时能够重用现有 PDF 的部分内容。

pagemerge.py建立在 buildxobj 奠定的基础之上。它包含使用来自其他页面的一个或多个矩形创建新页面(或覆盖现有页面)的类。有一些例子展示了它用于水印、缩放、4-up 输出、将每页分成 2 等。

findobjs.py包含可以在 PDF 文件中查找特定类型对象的代码。extract.py 示例使用此模块创建一个新的 PDF,将源 PDF 中的每个图像和 Form XObject 放置到其自己的页面上,例如,便于与其他一些示例或 reportlab 重用。

5.6杂项

compress.pyuncompress.py 包含压缩和解压缩函数。目前支持的过滤器很少,因此如果您需要能够解压缩(或就此而言,解密)PDF 文件,像 pdftk 这样的外部工具可能会很好。

py23_diffs.py包含有助于管理 Python 2 和 Python 3 之间差异的代码。

6测试

与 pdfrw 相关的测试需要大量的 PDF,这些 PDF 不随库一起分发。

要运行测试:

  • 从 github.com/pmaupin/pdfrw 下载或克隆完整的包

  • cd 进入测试目录,然后将包 github.com/pmaupin/static_pdfs 克隆到一个子目录(也称为 static_pdfs)。

  • 现在可以使用 unittest、py.test 或 nose 从该目录运行测试。

  • travisci 在 github 上使用,并使用 py.test 运行测试

7其他图书馆

7.1纯 Python

  • 报告实验室

    如果您想以编程方式生成任意 PDF,reportlab 是必备软件。

  • pyPDF

    pyPdf 在某些方面功能非常全面。它可以进行解压缩和解密,并且似乎对至少某些 PDF 文件中的项目了解很多。相比之下,pdfrw 对特定的 PDF 文件功能(如元数据)知之甚少,但专注于尝试拥有更多 Pythonic API 来将 PDF 文件容器语法映射到 Python,并且(IMO)拥有更简单更好的 PDF 文件解析器。pdfrw 的 Form XObject 功能意味着,在许多情况下,它实际上不需要解压缩对象——它们可以保持压缩状态。

  • pdf工具

    pdftools 感觉很大,我睡着了,试图弄清楚它是如何组合在一起的,但许多其他人已经用它做了有用的事情。

  • 寻呼机

    我的理解是,当我构建 pdfrw 时,pagecatcher 会做我想要的。但是我的预算为零,所以我从来没有体验过 pagecatcher 的乐趣。但是,我确实使用并且喜欢reportlab(开源,来自制作 pagecatcher 的人),所以我确信 pagecatcher 很棒,比 pdfrw 有更好的文档记录并且功能更全面。

  • pdf矿工

    这看起来像是一个有用的、积极开发的程序。它相当大,但它正在尝试主动理解完整的 PDF 文档。从网站:

    “PDFMiner 是一套帮助提取和分析 PDF 文档文本数据的程序。与其他 PDF 相关工具不同,它允许获取页面中文本的确切位置,以及其他额外信息,例如字体信息或格线。它包括一个 PDF 转换器,可以将 PDF 文件转换为其他文本格式(例如 HTML)。它有一个可扩展的 PDF 解析器,可用于其他目的,而不是文本分析。”

7.2非纯Python库

  • pyPoppler可以读取 PDF 文件。

  • pycairo可以编写 PDF 文件。

  • PyMuPDF对 PDF、(Open)XPS、CBZ 和 EPUB 的高性能渲染

7.3其他工具

  • pdftk是用于基本 PDF 操作的出色命令行工具。它对pdfrw的补充非常好,支持很多pdfrw不能做的解密、解压等操作。

  • MuPDF是一个免费的顶级性能 PDF、(Open)XPS、CBZ 和 EPUB 渲染库,还附带一些命令行工具。其中之一mutool与 pdftk 有很大的重叠 - 除了它快 10 倍。

8发布信息

修订:

0.4 – 2017 年 9 月 18 日发布

  • Python 3.6 添加到测试矩阵

  • 添加了对 PDF 中文本字符串的适当 unicode 支持

  • buildxobj 修复允许在某些情况下更好地支持从压缩页面创建表单 XObject

  • Python 3+ 的压缩修复

  • 新的子集_booklets.py 示例

  • 修复了压缩对象流中未压缩索引的错误

  • 修复了区分压缩对象流优先对象的错误

  • 为一些无效的 PDF 添加了更好的错误报告(例如,当阅读超过文件末尾时)

  • 在编写 PDF 时更好地清理旧书签信息,以删除悬空引用

  • 重构 pdfwriter,包括更新 API,以允许未来增强增量写入等功能

  • 小分词器加速

  • 修复了一些 flate 解压器错误

  • 增加了压缩和解压测试

  • 添加了新的 unicode 处理测试

  • PdfReader.readpages() 递归错误(问题 #92)已修复。

  • 添加了初始加密过滤器支持

0.3 – 2016 年 10 月 19 日发布。

  • Python 3.5 添加到测试矩阵

  • 在 Python 3.x 下更好地支持内存中 PDF 文件类对象

  • 添加了一些 pagemerge 和 Unicode 补丁

  • 对日志记录的更改允许与其他包更好地共存

  • 修复“从 pdfrw 导入 *”

  • 新的 fancy_watermark.py 示例展示了 pagemerge.py 的功能

  • metadata.py 示例重命名为 cat.py

0.2 – 2015 年 6 月 21 日发布。支持 Python 2.6、2.7、3.3 和 3.4。

  • 修复了几个错误

  • 新的回归测试功能性地测试了数十个 PDF 的核心,还测试了示例。

  • 通过往返几个困难的文件并观察不同 Python 版本的二进制匹配结果,Core 已在 Python3 上进行了移植和测试。

  • 仍然只支持最低限度的压缩,不支持加密或更新的 PDF 功能。(pdftk 对于将 PDF 放入 pdfrw 可以使用的形式很有用。)

0.1 – 2012 年发布到 PyPI。支持 Python 2.5 - 2.7

下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

pdfrw-0.4.tar.gz (95.4 kB 查看哈希)

已上传 source

内置分布

pdfrw-0.4-py2.py3-none-any.whl (69.5 kB 查看哈希

已上传 3 5