pdftotext
Poppler のコマンドラインツールの一つである pdftotext は PDF からテキストを抽出することができます。
通常は入力に PDF ファイルを指定するのですが、マニュアルに "If PDF-file is ´-', it reads the PDF file from stdin." と書かれているように標準入力から PDF データを流し込むことも可能です。
$ man pdftotext
pdftotext(1) General Commands Manual pdftotext(1)
NAME
pdftotext - Portable Document Format (PDF) to text converter (version 3.03)
SYNOPSIS
pdftotext [options] PDF-file [text-file]
DESCRIPTION
Pdftotext converts Portable Document Format (PDF) files to plain text.
Pdftotext reads the PDF file, PDF-file, and writes a text file, text-file. If text-file is not specified, pdftotext converts file.pdf to file.txt. If text-file is ´-', the text is sent to stdout.
If PDF-file is ´-', it reads the PDF file from stdin.
...
と、いうわけで。 Ruby の Open3 を使って pdftotext を起動し、標準入力で PDF ファイルを流し込んで標準出力からテキストを取り出してみました。
Open3
Open3.capture3 は、引数に指定したコマンドを実行し、標準出力、標準エラー、終了ステータスの三つ組を返してくれます。
またオプション :stdin_data を指定すると、その内容を標準入力へ流し込んでくれます。
これらを踏まえて。 実際に PDF をテキストに変換してみます。
ここではサンプルとして、誰でも入手できてかつ無難な内容ということで、気象庁のサイトに掲載されている PDF ファイルを使いました。
PDF データの取得は Net::HTTP.get を使います。
class Net::HTTP (Ruby 3.4 リファレンスマニュアル)
require 'open3' require 'net/http' uri = URI.parse('https://www.jma.go.jp/jma/kishou/know/jishin/joho/pdf/jishin.pdf') body = Net::HTTP.get(uri) out, err, status = Open3.capture3('pdftotext - -', stdin_data: body)
pp out #=> "地震情報\n" + ... pp err #=> "" pp status #=> #<Process::Status: pid 20786 exit 0>
Process::Status
三つ目のオブジェクトは Process::Status のオブジェクトなので、状態を知るために #success? などのメソッドを利用することができます。
status.success?
#=> true
これらをメソッド定義に収め、終了ステータスで判定して例外を生成するようにすると取り回しも楽になりそうです。
module PdfToText
require 'open3' module PdfToText def self.call(pdf) out, err, status = Open3.capture3('pdftotext - -', stdin_data: pdf) raise err unless status.success? out end end
uri = URI.parse('https://www.jma.go.jp/jma/kishou/know/jishin/joho/pdf/jishin.pdf') PdfToText.call(Net::HTTP.get(uri)) #=> "地震情報\n" ...
不適切なデータを渡すと例外を例外を生成します。
PdfToText.call('wrong data') #=> #<RuntimeError:"Syntax Warning: May not be a PDF file (continuing anyway)\n ... >
ここでは横着して RuntimeError に標準エラーの内容を丸ごと載せてだけで済ませていますが、例外の種類を自分で定義することでコマンドの実行に失敗したのかコマンドが失敗を返したのかをきちんと識別できるようにできます。


