精美的PDFRW库的扩展,该库添加了内容流的处理(以及其中引用的所有对象,例如图像,字体等),同时使其尽可能简单。
那么,当有诸如PYPDF之类的全面PDF库时,为什么还要扩展PDFRW呢?这样想:大多数PDF库都试图提供简化常见PDF处理任务的功能。这些库非常擅长,但是有时会出现一个需要新功能的任务。现在,如果您是在这种情况下的开发人员,则必须开始研究这些库的大量源代码。
PDFRW的方法是不同的:让我们尽可能地解析PDF,同时使结果尽可能简单。并不是一个坏主意,尤其是考虑到PDF对象模型(这只是一堆字典,其某些价值是引用其他字典)非常适合映射到标准的Python词典中。最重要的是, PDFRW在另一个出色的软件包背后实现了一个想法,以使遍历PDF对象变得更简单:例如,可以这样做的访问页面字体:
for fontName , fontDict in page . Resources . Font . items ():
( do something )下一步是尝试解析字典流 - 这些是词典中包含有趣内容的特殊条目:文本,图像,矢量图形等。PDFRWpdfrw不做任何事情,它并不是故意的:根据其Unix型哲学,它做得很好,并且没有做得多。它产生的数据结构已完成:它包含所有PDF,足以容纳任何可能的处理任务。而且它非常简单,因此开发人员可以立即开始编码,花更多的时间享受Adobe的Magnum Opus中的段落,而没有一个 - 学习另一个复杂的库。而且,实际上, PDFRW是通过使用它来帮助学习PDF语法的理想工具!
好的,如果您到了这一点,那么您很可能想知道:那么,我要去哪里?具体:如何解析字典流?这是PDFRWX可以提供帮助的地方:它可以解析字典流并做其他有用的事情。但是首先是
PDFRWX首先尝试遵守上面概述的PDFRW哲学。为此,它添加了一个观察结果,即在许多PDF处理任务中,大多数时间都花在开发软件解决方案上,而不是运行它。这导致了设计选择:
现在,我们已经准备好查看PDFRWX的作用以及它如何实现:
此示例/示例pdfstream.py读取示例。
from pdfrw import PdfReader , PdfWriter , PdfArray
from pdfrwx . pdffilter import PdfFilter
from pdfrwx . pdfstreamparser import PdfStream
toArray = lambda obj : obj if isinstance ( obj , PdfArray )
else PdfArray ([ obj ]) if obj != None else PdfArray ()
pdfIn = PdfReader ( 'example.pdf' )
pdfOut = PdfWriter ( 'example-out.pdf' )
for page in pdfIn . pages :
contentsArray = toArray ( page . Contents )
for contents in contentsArray :
stream = PdfFilter . uncompress ( contents ). stream
treeIn = PdfStream . stream_to_tree ( stream )
treeOut = []
for leaf in treeIn :
cmd , args = leaf [: 2 ]
if cmd != 'BT' : treeOut . append ( leaf )
contents . stream = PdfStream . tree_to_stream ( treeOut )
contents . Filter = None
pdfOut . addPage ( page )
pdfOut . write ()如您所见,代码在页面上运行,然后在每个页面的内容上运行,然后将内容取消压缩,因为它可以被压缩,然后将内容流解析为树中,然后将BT/ET文本块删除,然后将所得的树解析回流。上面的每一行代码的含义应清楚地清楚,除了toarray lambda函数:它之所以存在,是因为PDF中的页面可以包含多个内容字典,在这种情况下,page.contents是PDFarray,并且其每个元素都必须单独处理。因此,toarray lambda函数通过将页面上的页面转换为不符合阵列的pdfarray带有一个元素。
还必须注意其他一些事情。首先,请注意,为了完成任务,代码仅使用PDFRWX :PDFFILTER和PDFSTREAM的两个新类,每个函数都来自每个函数。其次,解析的树I只是以下琐碎格式的嵌套标准python列表:
[
['q', []],
['cm', ['1','0','0','1','0','0']],
...
['BT', [], [ /a tree list of text operators/ ]],
...
['Q', []]
]
因此,树列表的每个叶子(元素)本身就是2或3个元素的列表:前两个元素是命令和参数列表(如果命令没有参数,则为一个空列表),而第三个可选参数则存在于叶子是命令块的情况下,在这种情况下,是构成块的树列表。通过设计,只有两种类型的块:BT/ET文本块,该块在解析树中使用命令名称为“ BT”,而BI/ID/EI内联图像块,该块在解析树中使用命令名称为“ BI”。请注意,在原始的PDF流中,只有一系列命令。为了方便起见,它是PDFStream解析器在其输出中创建这些块。为了进一步熟悉流解析器的输出的结构,请在调用解析器后立即插入诸如Pprint(Treein)之类的命令。
还请注意,托瓦雷功能多么有用,尚未在模块中实现,因此您每次进行解析时都必须对其进行编码。这听起来可能很奇怪,但这是相同的设计原理的结果:模块只是解析了流。取决于开发人员对其他所有内容进行编码。
PDFStream Parser类是在Pure Python中实施的,借助David Beazley的受欢迎的(和Pure Python!)Sly Parser Generator Library的帮助,仅使用了约300行代码。出于好奇:解析器使用两个解析器状态(即两个不同语法的解析器,它们在操作时会切换在它们之间),一个用于解析PDF字面字符串(即括号中的字符串),另一个用于其他所有内容。是的,为了使字面字符串支持编码括号作为字符串的一部分,PDF字面字符串的格式已经变得如此复杂,以至于需要单独的解析器才能解析。因此,如果出于某种原因(速度?),您将想使用另一个解析器发电机库实现流解析器,请确保它支持Parser状态的堆栈。
解决字体地狱。文档即将推出,请继续关注。
支持的过滤器:
在图像导出中,厌倦了Adobe自己的产品错误?然后,您来对地方了!旨在成为最准确的PDF图像操纵类。文档即将推出,请继续关注。
支持的编解码器(编码/解码):
支持的颜色空间:
加:
该模块目前非常可用,并且已通过多种测试进行运行,以确保它可以执行正确的操作。但是,它绝不靠近alpha:界面尚未完全完成。此外,错误处理被打破(可能很快就可以修复,并类似于在PDFRW中处理错误的方式)。所以,自负没有风险,只是不要指望它具有生产质量。