繁体   English   中英

struct.error:unpack需要长度为16的字符串参数

[英]struct.error: unpack requires a string argument of length 16

使用pdfminer(pdf2txt.py)处理PDF 文件(2.pdf)时收到以下错误:

pdf2txt.py 2.pdf 

Traceback (most recent call last):
  File "/usr/local/bin/pdf2txt.py", line 115, in <module>
    if __name__ == '__main__': sys.exit(main(sys.argv))
  File "/usr/local/bin/pdf2txt.py", line 109, in main
    interpreter.process_page(page)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdfinterp.py", line 832, in process_page
    self.render_contents(page.resources, page.contents, ctm=ctm)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdfinterp.py", line 843, in render_contents
    self.init_resources(resources)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdfinterp.py", line 347, in init_resources
    self.fontmap[fontid] = self.rsrcmgr.get_font(objid, spec)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdfinterp.py", line 195, in get_font
    font = self.get_font(None, subspec)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdfinterp.py", line 186, in get_font
    font = PDFCIDFont(self, spec)
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdffont.py", line 654, in __init__
    StringIO(self.fontfile.get_data()))
  File "/usr/local/lib/python2.7/dist-packages/pdfminer/pdffont.py", line 375, in __init__
    (name, tsum, offset, length) = struct.unpack('>4sLLL', fp.read(16))
struct.error: unpack requires a string argument of length 16

虽然类似的文件(1.pdf)不会导致问题。

我找不到有关错误的任何信息。 我在pdfminer GitHub存储库中添加了一个问题 ,但它仍未得到答复。 有人可以向我解释为什么会这样吗? 我该怎么做才能解析2.pdf


更新 :直接从GitHub存储库安装pdfminer后,我收到类似于BytesIO而不是StringIO错误。

    $ pdf2txt.py 2.pdf 
Traceback (most recent call last):
  File "/home/danil/projects/python/pdfminer-source/env/bin/pdf2txt.py", line 116, in <module>
    if __name__ == '__main__': sys.exit(main(sys.argv))
  File "/home/danil/projects/python/pdfminer-source/env/bin/pdf2txt.py", line 110, in main
    interpreter.process_page(page)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdfinterp.py", line 839, in process_page
    self.render_contents(page.resources, page.contents, ctm=ctm)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdfinterp.py", line 850, in render_contents
    self.init_resources(resources)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdfinterp.py", line 356, in init_resources
    self.fontmap[fontid] = self.rsrcmgr.get_font(objid, spec)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdfinterp.py", line 204, in get_font
    font = self.get_font(None, subspec)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdfinterp.py", line 195, in get_font
    font = PDFCIDFont(self, spec)
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdffont.py", line 665, in __init__
    BytesIO(self.fontfile.get_data()))
  File "/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdffont.py", line 386, in __init__
    (name, tsum, offset, length) = struct.unpack('>4sLLL', fp.read(16))
struct.error: unpack requires a string argument of length 16

TL; DR

感谢@mkl和@hynecker的额外信息...有了这个我可以确认这是pdfminer和你的PDF中的错误。 每当pdfminer尝试获取嵌入的文件流(例如字体定义)时,它就会在endobj之前拾取文件中的最后一个。 遗憾的是,并非所有PDF都严格添加结束标记,因此pdfminer应该具有弹性。

快速解决此问题

我已经创建了一个补丁 - 它已经在github上作为pull请求提交了。 请参阅https://github.com/euske/pdfminer/pull/159

详细诊断

正如其他答案中所提到的,你看到这个的原因是你没有从流中获得预期的字节数,因为pdfminer正在解压缩数据。 但为什么?

正如您在堆栈跟踪中看到的那样,pdfminer(正确地)发现它有一个要处理的CID字体。 然后,它继续将嵌入的字体文件处理为TrueType字体(在pdffont.py )。 它尝试通过读出一组二进制表来解析关联的流(流ID 18)。

这对于2.pdf不起作用,因为它有一个文本流。 你可以通过运行dumppdf -b -i 18 2.pdf来看到这一点。 我在这里开始:

/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0
>> def /CMapName /Adobe-Identity-UCS def
...

垃圾输入,垃圾输出......这是你的文件或pdfminer中的错误吗? 好吧,其他读者可以处理它的事实让我怀疑。

再挖一点,我看到这个流流ID 17 相同 ,它是ToUnicode字段的cmap。 快速浏览PDF规范可以看出这些不一样。

进一步深入研究代码,我发现所有流都获得了相同的数据。 哎呀! 这是错误。 原因似乎与这个PDF缺少一些结束标签这一事实有关 - 正如@hynecker所指出的那样。

修复是为每个流返回正确的数据。 任何其他修复只是吞下错误将导致错误的数据被用于所有流,因此,例如,不正确的字体定义。

我相信附加的补丁会解决你的问题,一般来说应该是安全的。

我在源代码中修复了你的问题,然后我尝试使用你的文件2.pdf来确保它有效。

pdffont.py文件中我替换了:

class TrueTypeFont(object):

    class CMapNotFound(Exception):
        pass

    def __init__(self, name, fp):
        self.name = name
        self.fp = fp
        self.tables = {}
        self.fonttype = fp.read(4)
        (ntables, _1, _2, _3) = struct.unpack('>HHHH', fp.read(8))
        for _ in xrange(ntables):
            (name, tsum, offset, length) = struct.unpack('>4sLLL', fp.read(16))
            self.tables[name] = (offset, length)
        return

这样:

class TrueTypeFont(object):

    class CMapNotFound(Exception):
        pass

    def __init__(self, name, fp):
        self.name = name
        self.fp = fp
        self.tables = {}
        self.fonttype = fp.read(4)
        (ntables, _1, _2, _3) = struct.unpack('>HHHH', fp.read(8))
        for _ in xrange(ntables):
            fp_bytes = fp.read(16)
            if len(fp_bytes) < 16:
                break
            (name, tsum, offset, length) = struct.unpack('>4sLLL', fp_bytes)
            self.tables[name] = (offset, length)
        return

说明

@Nabeel Ahmed是对的

foramt string> 4sLLL需要16字节大小的缓冲区,正确指定fp.read以一次读取16个字节。

因此,问题只能在于它正在读取的缓冲流,即特定PDF文件的内容。

在代码中我们看到fp.read(16)是在循环中进行的,没有任何检查。因此,我们不确定它是否成功读取了所有内容。 例如,它可以达到EOF

为了避免这个问题,我只是在出现这种问题时break for循环。

    for _ in xrange(ntables):
        fp_bytes = fp.read(16)
        if len(fp_bytes) < 16:
            break

在任何常规情况下,它都不应该改变任何东西。

我会尝试在github上做一个pull请求,但是我甚至不确定它会被接受所以我建议你现在做一个猴子补丁并修改你的/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdffont.py文件。

这实际上是一个无效的PDF,因为在三个间接对象之后存在一些缺少的关键字endobj (对象5,18和22)

PDF文件中间接对象的定义应包括其对象编号和世代号(由空格分隔),后跟关键字objendobj之间括起来的对象的值。 PDF参考中的第7.3.10章)

示例2.pdf是一个简单的PDF 1.3版本,它使用简单的无压缩交叉引用和未压缩的对象分隔符。 通过grep命令和一般文件查看器可以很容易地找到失败,即PDF有22个间接对象。 模式“obj”正好被正确找到22次(在字符串对象或流中从不小心,幸运的是为了简单起见),但关键字endobj缺失了三次。

$ grep --binary-files=text -B1 -A2 -E " obj|endobj" 2.pdf
...
18 0 obj
<< /Length 451967/Length1 451967/Filter [/FlateDecode] >> 
stream
...
endstream                 % # see the missing "endobj" here
17 0 obj
<< /Length 12743 /Filter [/FlateDecode] >> 
stream
...
endstream
endobj
...

类似地,对象5在对象1之前没有endobj ,并且对象22在对象21之前没有endobj

众所周知,PDF中的断开交叉引用通常可以通过obj / endobj关键字重建(参见PDF参考,第C.2章)如果交叉引用正确,某些应用程序可能反之亦然修复丢失的endobj,但它没有书面建议。

最后一条错误消息告诉您很多:

文件“/usr/local/lib/python2.7/dist-packages/pdfminer/pdffont.py”,第375行,

init (name,tsum,offset,length)= struct.unpack('> 4sLLL',fp.read(16))struct.error:unpack需要一个长度为16的字符串参数

您可以轻松地调试正在发生的事情,例如,将必要的调试语句准确地放在pdffont.py文件中。 我的猜测是你的pdf内容有一些特别之处。 通过抛出错误消息的方法名称 - TrueTypeFont判断,与字体类型存在一些不兼容性。

首先解释一下您获得异常的声明:

struct.unpack('>4sLLL', fp.read(16))

其中的概要是:

struct.unpack(fmt, buffer)

该方法根据格式字符串 fmt unpack ,从缓冲区buffer (可能早先由pack(fmt, ...)打包)解pack(fmt, ...) 结果是一个元组,即使它只包含一个项目。 缓冲区的大小(以字节为单位)必须与格式所需的大小相匹配,如calcsize()所示。

最常见的情况是,使用的格式错误的字节数( 16 )( >4sLLL ) - 例如,对于期望4个字节的格式,您指定了3个字节:

(name, tsum, offset, length) = struct.unpack('BH', fp.read(3))

为此,你会得到

struct.error: unpack requires a string argument of length 4

原因 - 格式struct('BH')需要4个字节,即当我们使用'BH'格式打包时,它将占用4个字节的内存。 这里有个很好的解释。


为了进一步澄清 - 让我们看看>4sLLL格式字符串。 要验证unpack的大小,请指望缓冲区(您从PDF文件中读取的字节数)。 从文档引用:

缓冲区的大小(以字节为单位)必须与格式所需的大小相匹配,如calcsize()所示。

>>> import struct 
>>> struct.calcsize('>4sLLL')
16
>>> 

到目前为止,我们可以说声明没有错:

(name, tsum, offset, length) = struct.unpack('>4sLLL', fp.read(16))

>4sLLL string >4sLLL需要16字节大小的缓冲区,正确指定fp.read以一次读取16个字节。

因此,问题只能在于它正在读取的缓冲流,即特定PDF文件的内容。


可能是一个错误 - 根据这个评论

这是@euske上游PDFminer中的一个错误。似乎有补丁,所以它应该是一个简单的修复。 除此之外,我还需要加强pdf解析,这样我们就不会从失败的解析中出错

我将编辑一个问题,我发现这里有一些有用的东西 - 解决方案或补丁。

如果在应用Peter的补丁后仍然遇到一些结构错误,尤其是在一个脚本的运行中解析许多文件时(使用os.listdir),请尝试将资源管理器缓存更改为false。

rsrcmgr = PDFResourceManager(caching=False)

在应用上述解决方案后,它帮助我摆脱了其余的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM