如何使用 Python 解码 PDF 中的二进制数组

How to decode a binary array in a PDF using Python

我正在尝试解码 PDF 中的流对象。为了提供示例,我使用 word 创建了一个简单的 PDF。这是它的样子:

我已阅读此处找到的 PDF 的一些基本规范:PDF Specs。如果您使用基本文本编辑器(Windows 的记事本)打开 PDF,您可以看到该 PDF 是一个结构化的面向对象文件。这是“你好 World.pdf”的开头部分在文本编辑器中的样子:

%PDF-1.7
%µµµµ
1 0 obj
<</Type/Catalog/Pages 2 0 R/Lang(en-US) /StructTreeRoot 10 0 R/MarkInfo<</Marked true>>/Metadata 20 0 R/ViewerPreferences 21 0 R>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[ 3 0 R] >>
endobj
3 0 obj
<</Type/Page/Parent 2 0 R/Resources<</Font<</F1 5 0 R>>/ExtGState<</GS7 7 0 R/GS8 8 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 612 792] /Contents 4 0 R/Group<</Type/Group/S/Transparency/CS/DeviceRGB>>/Tabs/S/StructParents 0>>
endobj
4 0 obj
<</Filter/FlateDecode/Length 172>>

通过一些阅读,我可以稍后破译这是什么意思;但是,我无法理解流对象的结构。这是 PDF 中的流对象的样子:

4 0 obj
<</Filter/FlateDecode/Length 172>>
stream
x¥=Â0÷@þÃ`¤1P:ôêPPt7kªXÿ?K½áå÷{¤iÐÛ2ËH!?²JC"ñ×X±£¤]àNIî(    Ö
J   Á])Q¾'¡`´:VÄ    ÜÍ÷ê£Aÿò?ÑOi5§ÛðuÃðàË[Öz°q¸ð3ÜÊ/(ùIéXDúi"ð;ª)ðýÚ65
endstream
endobj

我希望能够将它变成可读的东西,以便我可以提取字符串对象。此 PDF 中应该有一个包含文本“Hello World”的字符串对象。它位于这些流对象之一中的某个位置,但在解码构成它们的二进制数组时我不知道从哪里开始。

我在角色映射和相关方面没有太多经验。压缩是否参与其中?如果是这样,我该如何处理?我希望能够使用 python 的标准库进行解码。如果像解码这样简单的事情需要某种包,那么你能告诉我从哪里开始以及这个过程是如何工作的吗?

我知道 PDF 的结构会随着它的编码和压缩而变化,但现在我只想知道如何解码这个特定的文件。

从@KJ 的评论中,我能够看到 PDFMinor 如何解压缩 PDF 流。在此处查看第 248 行:PDFMinor Source

对于这个简单的 PDF,PDF 流有一个“FlateDecode”过滤器。显然,python 内置的 zlib 模块能够解压这个二进制数组。只需几行简单的代码,您就可以解码:

代码段

import zlib

with open('Hello World.pdf', 'rb') as f:
    content = f.read()

stream_bytes = content[539:699]  # This is where the stream array is
data = zlib.decompress(stream_bytes)
print(data.decode())

输出

"C:\Users\gmbra\OneDrive\Desktop\Python Programs\Practice and Learning 2\venv\Scripts\python.exe" "C:/Users/gmbra/OneDrive/Desktop/Python Programs/Practice and Learning 2/test.py"
 /P <</MCID 0>> BDC q
0.00000912 0 612 792 re
W* n
BT
/F1 10.98 Tf
1 0 0 1 72 709.5 Tm
/GS7 gs
0 g
/GS8 gs
0 G
[(Hello Wo)-2(rld)] TJ
ET
Q
q
0.00000912 0 612 792 re
W* n
BT
/F1 10.98 Tf
1 0 0 1 125.42 709.5 Tm
0 g
0 G
[( )] TJ
ET
Q
 EMC 

在那里您可以看到字符串对象“Hello World”,但我不确定它为什么乱序。如果有人可以向我解释,请发表评论!现在只需添加一个简单的正则表达式,就可以提取字符串。我将这些行添加到上面的代码片段中:

import re

matches = re.findall(r'\((.+?)\)', data.decode())
print(''.join(matches).strip())

它输出:

Hello World