LaravelでPDFからテキストを読み取る(pdftotext)
はじめに
PDFファイルからテキストデータを抽出するツールとして、pdftotextというものがあります。これをLaravelで使うための方法です。
pdftotextは、PHPのライブラリではなくWindowsやMac、Linuxで動作するソフトウェアです。なのでサーバにインストールする必要があります。
VPSなどの場合は簡単にインストールできますが、レンタルサーバの場合はroot権限がないためインストールできません。今回はレンタルサーバでpdftotextを使うための方法も一緒にご紹介します。
pdftotextについて
PDFファイルに関連するツールの内部では、だいたいpdftotextが使われています。暗号化されたPDFファイルにも対応しており、とても優秀なソフトウェアです。
VPSなどの場合
pdftotextは、poppler-utilsというPDF関連ツールに含まれているので、これをサーバにインストールします。
# Ubuntu系 apt install poppler-utils # CentOS系 yum install poppler-utils
続いて、Laravelプロジェクトにpdftotextを使うための便利なライブラリ「spatie/pdf-to-text」をインストール。
composer require spatie/pdf-to-text
以上でpdftotextが使えるようになります。
以下のようなコードで簡単にPDFからテキストを抽出できます。
$text = \Spatie\PdfToText\Pdf::getText([PDFのパス]);
レンタルサーバの場合
レンタルサーバの場合はpoppler-utilsがインストールできませんが、pdftotext自体を任意の場所に置いて、Laravelからコマンドで実行することで動作します。
pdftotextのダウンロード
まずはpdftotextを入手します。
XpdfReaderというツールに含まれているので、以下のサイトからダウンロードします。
「Download the Xpdf command line tools:」というところのLinux用をダウンロードします。
解凍し「bin32」または「bin64」の中の「pdftotext」というファイルを取り出します。pdftotextはこのファイル単体で動作します。
Laravelからpdftotextを実行
PHPなどからコマンドを実行するには、通常exec()
関数を使いますが、今回はLaravelに標準搭載されている「Symfony」の機能を使って、以下のようなコードでコマンドを実行します。
use Symfony\Component\Process\Process; // 前後略 $process = new Process([コマンド]); // コマンド作成 $process->run(); // 実行 $output = $process->getOutput() ?: $process->getErrorOutput(); // 実行結果を受け取る
[コマンド]部分の書き方について
Process()
にコマンドを渡すには、コマンドのスペース部分で区切って配列にして渡します。例えば「echo “Hello world”」の場合なら以下の通り。
$process = new Process(['echo', 'Hello world']);
実際のコード
では、実際にpdftotextを実行してみましょう。
先程入手したpdftotextファイルをLaravelプロジェクト内の好きな場所に置きます。今回は「app」内に「opt」というフォルダを作ってその中に置きました。対象のPDFファイルは「storage/app/pdf/xxx.pdf」にあるものとします。
以下のようなコードでpdftotextを実行し、PDFからテキストを抽出します。
use Symfony\Component\Process\Process; // 前後略 $pdftotext_path = app_path('opt/'); $pdf_path = storage_path('app/pdf/xxx.pdf'); $process = new Process([$pdftotext_path . 'pdftotext', $pdf_path, '-']); // コマンド作成 $process->run(); // 実行 $output = $process->getOutput() ?: $process->getErrorOutput(); // 実行結果を受け取る
pdftotextの実行コマンドについての補足
pdftotext [対象PDFファイル] [出力先textファイル]
通常は、上記のように2つ目の引数に出力先を設定するとテキストファイルが作られますが、「-(ハイフン)」を指定するとファイルは保存されず、テキストとして標準出力されます。今回はこれを使って受け取っています。
パーミッションエラーが出る場合
pdftotextファイルに実行権限を付与します。
chmod 777 app/opt/pdftotext
日本語対応
ここまでは比較的かんたんに実装できましたが、残念ながら日本語が含まれている場合うまく抽出できません。
先程pdftotextを入手したXpdfReaderのサイトで日本語対応のためのファイルが配布されているので、それを使って日本語でも抽出できるようにしていきます。
日本語パッケージのダウンロード
同サイト内の「Download language support packages for Xpdf:」という欄に「Japanese」があるのでダウンロードします。解凍すると「xpdf-japanese」というフォルダができるので、そのまま「app/opt」内に配置します。
パス設定
「xpdf-japanese」の中に「add-to-xpdfrc」というファイルがあるので編集していきます。各ファイルへのパスが設定されているので、今回は以下のように書き換えました。
#----- begin Japanese support package (2011-sep-02) cidToUnicode Adobe-Japan1 "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/Adobe-Japan1.cidToUnicode" unicodeMap ISO-2022-JP "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/ISO-2022-JP.unicodeMap" unicodeMap EUC-JP "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/EUC-JP.unicodeMap" unicodeMap Shift-JIS "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/Shift-JIS.unicodeMap" cMapDir Adobe-Japan1 "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/CMap" toUnicodeDir "/home/account/xxxx.com/[プロジェクトまでのパス]/app/opt/xpdf-japanese/CMap" #fontFileCC Adobe-Japan1 /usr/..../NotoSansCJKjp-Regular.otf #----- end Japanese support package
「/home/account/xxxx.com/[プロジェクトまでのパス]」の部分は各サーバのものを入れてください。(コマンドラインから実行されるので絶対パスで指定します)
実行コマンド
pdftotextの実行コマンドに-cfg
オプションでこの「add-to-xpdfrc」ファイルを指定します。
$process = new Process([$pdftotext_path . 'pdftotext', '-cfg', $pdftotext_path . 'xpdf-japanese/add-to-xpdfrc', $pdf_path, '-']);
これでも上手く読み取れない場合は、PDFファイル内のテキストエンコードを指定するといいかもしれません。例えば、Shift-JISで書かれている場合は、-enc
オプションで「Shift-JIS」を指定。
$process = new Process([$pdftotext_path . 'pdftotext', '-cfg', $pdftotext_path . 'xpdf-japanese/add-to-xpdfrc', '-enc', 'Shift-JIS', $pdf_path, '-']);
以上でテキストが抽出できたと思います。
受け取ったテキストが文字化けしている場合は、以下のようにエンコードを変換します。
$text = mb_convert_encoding($pdfText, 'utf-8', 'Shift-JIS');
まとめ
レンタルサーバでpdftotextを使うのは、なかなか骨の折れる作業になりました。他に良いツールがあれば良いのですが、開発が止まっていたり、暗号化されたPDFに非対応だったりで、この方法以外は見つけられませんでした。こういう時はVPSの自由度の高さに惹かれますが、レンタルサーバはコスパ的にやめられません😅
-
前の記事
LaravelのAPIがCORSエラーを返す意外な原因 2022.08.26
-
次の記事
Laravelのテストコードでcookieをモックする方法 2023.12.19
コメントを書く