7

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
4

6 に答える 6

5

TL; DR

追加情報を提供してくれた @mkl と @hynecker に感謝します。これで、これが pdfminer と PDF のバグであることを確認できます。pdfminer が埋め込みファイル ストリーム (フォント定義など) を取得しようとするたびに、ファイル内のendobj. 悲しいことに、すべての PDF が厳密に終了タグを追加するわけではないため、pdfminer はこれに対して回復力があるはずです。

この問題のクイックフィックス

パッチを作成しました - これは github でプル リクエストとして送信されました。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のバグですか?まあ、他の読者がそれを処理できるという事実は、私を疑わせました。

もう少し調べてみると、このストリームは、フィールドの cmap であるストリーム ID 17と同じであるToUnicodeことがわかります。PDF 仕様をざっと見てみると、これらが同じではないことがわかります。

コードをさらに掘り下げると、すべてのストリームが同じデータを取得していることがわかります。おっとっと!これがバグです。原因は、@hynecker が指摘したように、この PDF にいくつかの終了タグが欠落しているという事実に関連しているようです。

修正は、ストリームごとに正しいデータを返すことです。エラーを飲み込むためのその他の修正を行うと、すべてのストリームに不正なデータが使用され、たとえば、フォント定義が正しくなくなります。

添付のパッチで問題が解決され、一般的に安全に使用できると思います。

于 2016-10-27T23:39:27.190 に答える
4

ソースコードの問題を修正し、ファイルが機能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

説明

@ナビール・アーメドは正しかった

フォーマット文字列 >4sLLL には 16 バイト サイズのバッファが必要です。これは、一度に 16 バイトを読み取るために fp.read に正しく指定されています。

したがって、問題は、読み取っているバッファ ストリーム、つまり特定の PDF ファイルのコンテンツにのみ発生する可能性があります。

コードではfp.read(16)、チェックなしでループで作成されていることがわかります。したがって、すべてを正常に読み取れたかどうかはわかりません。たとえば、に達する可能性がありますEOF

この問題を回避するためbreakに、この種の問題が発生したときに for ループから抜け出しました。

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

通常のケースでは、とにかく何も変更しないでください。

github でプル リクエストを試みますが、それが受け入れられるかどうかさえわからないので、とりあえずモンキー パッチを適用して、今すぐ/home/danil/projects/python/pdfminer-source/env/local/lib/python2.7/site-packages/pdfminer/pdffont.pyファイルを変更することをお勧めします。

于 2016-10-27T11:40:32.453 に答える
4

これは実際には無効な PDF です。これは、3 つの間接オブジェクトの後にいくつかのキーワードendobjが欠落しているためです。(オブジェクト 5、18、および 22)

PDF ファイル内の間接オブジェクトの定義は、オブジェクト番号と世代番号 (空白で区切られている) で構成され、その後にキーワードobjendobjで囲まれたオブジェクトの値が続きます。( PDFリファレンスの7.3.10章)

例の 2.pdf は、単純な非圧縮相互参照と非圧縮オブジェクト区切りを使用する単純な PDF 1.3 バージョンです。この障害は、grep コマンドと、PDF に 22 個の間接オブジェクトがある一般的なファイル ビューアーによって簡単に見つけることができます。パターン " obj" は正確に 22 回 (文字列オブジェクトやストリーム内で偶発的に見つかることはありません。幸いなことに単純化されています)、キーワードendobjが 3 回欠落しています。

$ 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 の欠落をおそらく逆に修正しますが、書面によるアドバイスではありません。

于 2016-10-30T02:16:50.483 に答える
2

最後のエラー メッセージは多くのことを示しています。

ファイル「/usr/local/lib/python2.7/dist-packages/pdfminer/pdffont.py」の 375 行目

init (名前、ツム、オフセット、長さ) = struct.unpack('>4sLLL', fp.read(16)) struct.error: unpack には長さ 16 の文字列引数が必要です

たとえば、必要なデバッグ ステートメントを正確にpdffont.pyファイルに入れることで、何が起こっているかを簡単にデバッグできます。私の推測では、あなたの pdf コンテンツには何か特別なものがあると思います。エラー メッセージをスローするメソッド名 - TrueTypeFont- から判断すると、フォント タイプとの互換性がありません。

于 2016-10-25T08:53:20.020 に答える
2

例外が発生しているステートメントの説明から始めましょう。

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

あらすじは次のとおりです。

struct.unpack(fmt, buffer)

メソッドは、フォーマット文字列に従って (おそらく以前に によってパックされた)unpackバッファからアンパックします。アイテムが 1 つだけ含まれている場合でも、結果はタプルになります。バッファのバイト単位のサイズは、calcsize() によって反映されるように、フォーマットで必要なサイズと一致する必要があります。bufferpack(fmt, ...) fmt

最も一般的なケースは、16使用されている形式 ( )のバイト数 ( ) が間違っている>4sLLLことです。たとえば、4 バイトを想定している形式に対して、3 バイトを指定しました。

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

このためにあなたは得るでしょう

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

その理由 - フォーマット構造体 ('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には、一度に 16 バイトを読み取るために fp.read に正しく指定された 16 バイト サイズのバッファーが必要です。

したがって、問題は、読み取っているバッファ ストリーム、つまり特定の PDF ファイルのコンテンツにのみ発生する可能性があります。


バグの可能性があります-このコメントによると:

これは、@euske による上流の PDFminer のバグです。これにはパッチがあるようで、簡単に修正できるはずです。これを超えて、失敗した解析からエラーが発生しないように、pdf 解析を強化する必要もあります。

ここに追加するのに役立つ何かが見つかったら、質問を編集します-解決策またはパッチ。

于 2016-10-25T14:17:30.100 に答える
1

Peter のパッチを適用した後も構造体エラーが発生する場合、特に 1 つのスクリプトの実行 (os.listdir を使用) で多数のファイルを解析する場合は、リソース マネージャーのキャッシュを false に変更してみてください。

rsrcmgr = PDFResourceManager(caching=False)

上記の解決策を適用した後、残りのエラーを取り除くのに役立ちました。

于 2016-11-03T14:17:07.293 に答える