PDF文件读取/写入库
项目描述
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中创建只包含部分页面的新PDF。
unspread.py 从2合1PDF中分离出页面。
watermark.py 在PDF的所有页面上添加水印PDF图像。
rl1/4up.py 另一个4合1示例,使用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中添加一些无意义的元数据后输出。
《alter.py》示例修改PDF中的一个元数据项,并将结果写入新的PDF。
一个区别是,因为《cat》正在创建新的PDF结构,而《alter》正在尝试修改现有的PDF结构,所以《alter》产生的PDF(以及《watermark.py》)应该更忠于原始文件(除了期望的更改)。
例如,《alter.py》的导航应该保持不变,而《cat.py》将会被删除。
2.2.3 旋转和复制
如果您想打印类似小册子但需要螺旋装订的东西,您要么得做一些复杂的重新排列,要么就浪费一半的纸张。
例如,《print_two.py》示例程序将使PDF的每一页在每个输出纸上都有两个并排的副本。
但每隔一页都会翻转,这样您就可以打印双面打印,页面会正确对齐,并且预先整理。
2.2.4 图形流解析概念验证
脚本 copy.py 展示了读取 PDF 文件并使用 decodegraphics.py 模块通过 reportlab 画布尝试将相同信息写入新 PDF 的简单示例。如果你了解 reportlab,就知道如果你能够忠实地将 PDF 渲染到 reportlab 画布,你几乎可以用它做任何想做的事情。这种低级操作只有在真正需要时才应该进行。decodegraphics 更多的是一种概念验证,而不是其他任何东西。对于大多数情况,只需使用示例/rl1/booklet.py 示例中所示的 Form XObject 功能即可。
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 的 'indirect' 属性。当写入 PDF 文件时,如果你创建了任意数据,你只需确保在每个循环中至少有一个对象上的 'indirect' 属性评估为 True,以断开循环引用。
另一个不与常规 Python 完全对应的 PDF 文件概念是“流”。流是与无格式数据块相关联的字典。pdfrw 通过在子类化的字典上放置特殊属性来处理流。
4.3 使用模型
pdfrw的用法模型将大多数对象视为字符串(在写入文件时,它会使用它们的字符串表示形式)。两个主要例外是PdfArray对象和PdfDict对象。
PdfArray是列表的子类,具有两个特殊功能。首先,一个“间接”属性允许PdfArray以间接PDF对象的形式写入。其次,pdfrw按需读取文件,因此PdfArray会根据需要了解并解析对其他间接对象的引用。
PdfDict是具有间接属性和按需引用解析功能的字典的子类。子类化的IndirectPdfDict会将间接属性自动设置为True)。
但是,PdfDict还有一个可选的关联流。流对象默认为None,但如果您将流分配给字典,它将自动设置字典的PDF /Length属性。
最后,由于PdfDict实例是通过PdfName对象索引的(这些对象始终以“/”开头),并且大多数(所有?)标准Adobe 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格式允许使用任意复杂的嵌套字典来描述页面顺序。列表中的每个条目都是文件中某个页面的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只需要这些。您可能还需要阅读Adobe PDF参考手册,以了解需要将什么内容放入PDF中,但至少您不必担心实际构建它和获取文件偏移量。
4.3.3 内存中操作 PDF
就大部分而言,pdfrw试图对PDF文件的内容保持中立,并支持它们作为容器,但要进行有用的工作,需要稍微高级一些的功能,因此pdfrw努力了解容器的内容。例如
PDF页面。pdfrw足够了解在读取的PDF文件中找到页面,并将一系列页面写回到新的PDF文件中。
表单XObjects。pdfrw可以将任何页面或页面上的矩形转换为Form XObject,适用于在另一个PDF文件中使用。它对这些内容足够了解,可以执行缩放、旋转和定位。
reportlab对象。pdfrw可以从其内部对象格式递归创建一组reportlab对象。这使得,例如,Form XObjects可以在reportlab中使用,这样您就可以在构建新的PDF时重用现有PDF文件中的内容。
示例代码目录中有几个示例演示了这些功能。
4.3.4 缺失的功能
即使作为纯PDF容器库,pdfrw也有一些不足。它目前不支持
大多数压缩/解压缩过滤器
加密
pdftk是一个非常好的命令行工具,可以将您的PDF转换为去除加密和解压缩。然而,在大多数情况下,您可以在不实际去除压缩的情况下完成许多有用的工作,因为只有PDF中的某些元素实际上是压缩的。
5 库内部
5.1 简介
pdfrw目前由19个模块组成,分为一个主包和一个子包。
__init__.py 模块执行了从子模块导入一些主要属性的常规操作,而 errors.py 模块支持日志记录和异常生成。
5.2 PDF 对象模型支持
objects 子包包含每个PDF文件中存在的基本对象类型的内部表示的模块,该子包中的 objects/__init__.py 模块只是将它们收集起来,使它们对主 pdfrw 包可用。
所有PDF对象类共有的一项特性是包含一个‘indirect’属性。如果‘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)
将属性访问映射到字典本身(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 为您构造一个。在 examples 目录中有一个这样的例子(alter.py)。
5.5 高级特性
buildxobj.py 包含从页面或页面上的矩形构建表单 XObjects 的函数。这些可以在新 PDF 中重复使用,基本上就像它们是图像一样。
buildxobj会仔细缓存任何使用过的页面,以确保它们只出现在输出中一次。
toreportlab.py提供了makerl函数,该函数可以将pdfrw对象转换为可以与reportlab一起使用的格式。它通常与buildxobj结合使用,以便在使用reportlab时能够重用现有PDF的部分。
pagemerge.py建立在buildxobj的基础之上。它包含用于使用其他页面中的一个或多个矩形创建新页面(或覆盖现有页面)的类。有一些示例展示了它的使用,例如水印、缩放、4-up输出、将每页分成两页等。
findobjs.py包含可以找到PDF文件中特定类型对象的代码。extract.py示例使用此模块创建一个新的PDF,将源PDF中的每个图像和Form XObject放置在其自己的页面上,例如,以便与其他示例或与reportlab一起轻松重用。
5.6 其他
compress.py和uncompress.py包含压缩和解压缩函数。目前支持很少的过滤器,因此如果您需要解压缩(或,就这一点而言,解密)PDF文件的能力,那么像pdftk这样的外部工具可能是个好选择。
py23_diffs.py包含帮助管理Python 2和Python 3之间差异的代码。
6 测试
与pdfrw相关的测试需要大量PDF文件,这些文件不包括在库中。
要运行测试
从github.com/pmaupin/pdfrw下载或克隆完整包
切换到测试目录,然后克隆包github.com/pmaupin/static_pdfs到子目录(也命名为static_pdfs)。
现在可以使用unittest、py.test或nose从该目录运行测试。
github使用travisci,并使用py.test运行测试
7 其他库
7.1 纯 Python
-
如果您想编程生成任意PDF,那么reportlab是必须的软件。
-
在某些方面,pyPdf功能非常全面。它可以进行解压缩和解密,并且似乎对至少某些类型的PDF文件中的项目了解很多。相比之下,pdfrw对特定PDF文件功能(如元数据)的了解较少,但专注于尝试为将PDF文件容器语法映射到Python提供一个更Pythonic的API,并且(据我所知)具有更简单、更好的PDF文件解析器。pdfrw的Form XObject功能意味着在许多情况下,它实际上不需要解压缩对象——它们可以保持压缩状态。
-
pdftools感觉很大,我在试图弄清楚它如何组合在一起时睡着了,但许多人已经用它做了有用的事情。
-
我的理解是,当我在构建pdfrw时,pagecatcher会做我想做的任何事情。但我预算为零,所以我从未有幸体验过pagecatcher。然而,我确实使用并喜欢reportlab(开源,来自制作pagecatcher的人),因此我确信pagecatcher很棒,文档更完善,功能更全面,比pdfrw更好。
-
这看起来像是一个有用、积极开发的程序。它相当大,因为它的目标是积极理解完整的PDF文档。从网站
“PDFMiner 是一套程序,用于提取和分析 PDF 文档中的文本数据。与其它 PDF 相关工具不同,它允许获取页面中文本的确切位置,以及其他额外信息,例如字体信息或标尺线。它包含一个 PDF 转换器,可以将 PDF 文件转换为其他文本格式(如 HTML)。它具有可扩展的 PDF 解析器,可用于其他目的,而不仅仅是文本分析。”
7.2 非纯 Python 库
7.3 其他工具
8 发布信息
修订版本
0.4 – 发布于 2017 年 9 月 18 日
将 Python 3.6 添加到测试矩阵
为 PDF 中的文本字符串添加了正确的 Unicode 支持
buildxobj 的修复允许在某些情况下更好地支持从压缩页面创建 XObjects
为 Python 3+ 修复了压缩问题
添加了新的 subset_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 补丁
对日志记录的更改允许与其他软件包更好地共存
修复了“from pdfrw import *”
新的 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 版本之间的二进制匹配结果,已将核心移植并测试在 Python3 上。
仍然只提供有限的对压缩的支持,并且不支持加密或较新的 PDF 功能。(pdftk 有用,可以将 PDF 放在 pdfrw 可以使用的格式。)
0.1 – 2012 年发布到 PyPI。支持 Python 2.5 - 2.7
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装软件包 的更多信息。
源代码发行版
构建发行版
pdfrw-0.4.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 0dc0494a0e6561b268542b28ede2280387c2728114f117d3bb5d8e4787b93ef4 |
|
MD5 | eaf97243d3634cac954527904dcdecae |
|
BLAKE2b-256 | 6196cbde98b96115d10694010e584d00d39f7b97905e1c8303e6ffbb84080e6a |
pdfrw-0.4-py2.py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 758289edaa3b672e9a1a67504be73c18ec668d4e5b9d5ac9cbc0dc753d8d196b |
|
MD5 | 76f4e1c495fc279e4c0a933e4a09c7d2 |
|
BLAKE2b-256 | c084af442c4458756bb0c0d2424102d1200616f3ff9b82c48aaa130e08549bf6 |