こんにちは。学生アルバイトのohkiです。スクレイピングをしていると、欲しいデータがexcelやPDFファイル形式になっていて、パースに苦戦することはありませんか?(excelファイルの詳しいパース方法はこちら)その中でも特に面倒なものが画像を含むPDFファイルではないでしょうか?画像を含んでいるとexcelに変換することができず、OCR(Optical Character Recognition)などを使わない限り、基本的にパースができません(具体例はこちら)。せめて、PDFファイルが画像を含んでいることだけでも分かっておきたいものです。そこで、今回はPDFファイル内に画像が含まれているかどうかの確認方法を紹介します。

PDFとは

はじめに、PDF(Portable Document Format)とは、アドビが開発及び提唱する電子上の文書に関するファイルフォーマットです。特徴として、特定の環境に左右されずに全ての環境でほぼ同様の状態で文章や画像等を閲覧できることが挙げられます。

今回は、以下の画像を含むpdfを例に、pdfからの画像検出を考えていきたいと思います。

PDFのオブジェクトを辿る

まずは、PDFのオブジェクトを参照する方法です。PDFは、ファイルをエディタで開いてみるとわかりますが、バイナリデータが入っていて、多くのオブジェクトなどで構成されています。特に大きく分けて4つの部分に分かれています。

  • ヘッダー
  • ファイルの先頭部分には”%”で始まるコメントが書かれています。 今回は、”%PDF-1.3″と書かれています。 これは、このファイルがPDFファイルであるということと、PDF仕様のバージョンを表します。

    %PDF-1.3
    
  • 相互参照テーブル
  • xrefとその下の数字列が並んだ行は相互参照(cross reference)と呼ばれ、ファイル内の各オブジェクトの位置を一覧化したものです。 相互参照は、3列の項目が複数行並ぶ表形式になっています。 これで、特定のID番号を持つオブジェクトの開始位置がわかるようになっています。

    xref
    0 16
    0000000000 65535 f 
    0000019648 00000 n 
    後略
    
  • トレーラー
  • 特殊なオブジェクトを読み取るためのもので、相互参照テーブルの開始位置や、ファイル内に格納されたメタデータの位置を保持します。 ファイルの最終行を示す”%%EOF”もここに記載されます。 “startxref”の下の数字は、ファイルの先頭から”xref”の”x”の文字の位置までのバイト数で示したものです。

    trailer
    << /Size 16 /Root 15 0 R /Info 1 0 R /ID [ 
     ] >>
    startxref
    19970
    %%EOF
    
  • 本体とオブジェクト
  • 本体部分には複数のオブジェクトが定義されています。 “4 0 obj”から”endobj”までがオブジェクトの定義です。 オブジェクトとは、文書構造、各ページの内容、フォント定義、画像データなど、文書を構成する様々な要素です。 objの前の二つの数字はそれぞれ、オブジェクトの番号と世代番号を表します。 世代番号は、オブジェクトが更新された時に値が増加し、基本的には0です。

    4 0 obj
    << /Length 5 0 R /Filter /FlateDecode >>
    stream
    中略
    endstream
    endobj
    

    画像を含んでいる場合、以下のようなimageオブジェクトを含んでいます。これを目印に画像の検出をすることができます。

    #画像オブジェクト
    9 0 obj
    << /Length 10 0 R /Type /XObject /Subtype /Image /Width 1000 /Height 185 /Interpolate
    true /ColorSpace 8 0 R /SMask 11 0 R /BitsPerComponent 8 /Filter /FlateDecode >>
    stream
    中略
    endstream
    endobj
    

Popplerコマンドで検出

次に、Popplerコマンドを用いた方法です。Popplerとは、PDFドキュメントの閲覧に用いられるフリーのプログラミングライブラリです。

まず、下記のようにインストールします

#homebrew
$ brew install poppler
#ubuntu
$ sudo apt install poppler-utils poppler-data

今回は、PDFファイルに画像を含んでいるかを確認するため、Popplerコマンドのひとつであるpdfimagesを使います。使い方は以下のようになります。

#pdfimages (-f 最初のページ番号 -l 最後のページ番号) ファイル名
$ pdfimages -list test.pdf 
page   num  type   width height color comp bpc  enc interp  object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
   1     0 image    1000   185  icc     3   8  image  yes        9  0   100   100 10.1K 1.9%
   1     1 smask    1000   185  gray    1   8  image  yes        9  0   100   100 5349B 2.9%
#画像が含まれていない場合
$ pdfimages -list no_image_test.pdf 
page   num  type   width height color comp bpc  enc interp  object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------

このように、PDFファイル内に画像が含まれていることがわかります。

まとめ

今回はpdfファイル内の画像検出の方法を紹介しました。これをきっかけに、pdfファイルの構造や、他にもあるpopplerのメソッドに興味を持っていただければと思います。

Pocket