[Rails] MiniMagickでPDFのページ数を取得するときはフォントエラーに注意!

こんにちは。@kazzです。

アドベントカレンダー以来の登場です。明けましておめでとうございます。

今回は、みんな大好きImageMagickMiniMagickの調査を行いました。
私はImageMagickもMiniMagickもちっともわからないのですが、実際にどんな感じで調査行ったのか?ということを赤裸々に記事にしてみました。

問題の発生

PDFファイルのページ数をカウントして保存するという機能を持っているシステムがすでにリリースされています。
先日、実際のPDFファイルのページ数と、保存しているPDFのページ数とが違う!!というご連絡を頂きました。

実装の確認

PDFのページ数カウントはMiniMagickを使うと実現できるようです。実際に以下のようなコードが実装されていました。

MiniMagick::Image.open(filepath).pages.count

MiniMagickは画像の作成・変換のライブラリです。上のコードのようにPDFも取り扱えます。RMagickよりも軽量でよく使用されているとのことです。

Magickという名前で画像を取り扱うライブラリなので、内部的にはImageMagickに依存します。というより、ImageMagickをRubyで取り扱うことのできるライブラリがMiniMagickといった所だと思います。

そういえばこのシステムの開発環境作りでImageMagickのセットアップが面倒だった気がしたなぁ。

再現確認

問題のでるPDFファイルを拝領して再現確認してみるのですが、私のローカル環境では正しくページ数が取得できています

再現しないとはどういうことだ?PDFに相違はない。ソースコードにも相違はない。ということは…環境に違いがあるからか?とすればマズい!ローカルで再現できなければ改修もできない!!そもそも調査しても原因が特定しきれないかも。

おそるおそるステージング環境でRailsConsoleで上記のコードを実行してみます。

ステージング環境だとページ数が増えてる!環境差異問題確定だ!バンザイ!!

(短い人生でした。三日とろろ美味しうございました。@kazzさんの新連載にご期待ください)

原因調査

いやいや俺たちの冒険はここからですよ。

再現する環境が確定していてそれが本番のようなシビアな環境ではないので、こんなところであきらめている場合ではありません。

再現環境での確認

まずはImageMagickのバージョンを確認

ぐーぐるせんせいありがとうございます。

$ convert -version

あれ。。。私のローカル環境と同じだ。

実際にやってみる

ではImageMagick的にPDFを画像に変換してどんな画像がでるか見てみましょう。

$ convert file.pdf out.jpg

あれ?変換された画像ファイル数とPDFのページ数が同じだ?

ImageMagickは悪い子じゃないっぽいぞ(ちょっと安心)。

じゃあ悪い子はMiniMagickかなぁ。

MiniMaigckの調査

MiniMagickのソースを見てみる

ということで、MiniMagick::Image#pages のソースを見てみました。

def layers
  layers_count = identify.lines.count
  layers_count.times.map do |idx|
    MiniMagick::Image.new("#{path}[#{idx}]")
  end
end
alias pages layers

GitHub もご参照ください。

なるほど。identify.lines.countが取得できるページ数になるのか。

実際にRailsConsoleでやってみた

> puts *MiniMagick::Image.open(filepath).identify.lines
Can't find the font file /usr/share/fonts/truetype/xxxxx.ttf
Can't find the font file /usr/share/fonts/truetype/xxxxx.ttf
Can't find the font file /usr/share/fonts/truetype/xxxxx.ttf
(略)
/tmp/mini_magick20170113-26897-fmh4es.pdf[0] PDF 522x733 (略)
/tmp/mini_magick20170113-26897-fmh4es.pdf[1] PDF 522x733 (略)
.
.

なるほど。Can’t find うんぬん… も #lines の結果(配列)に含まれるから…
うほー。フォントが見つからないエラー文言がページ数としてカウントされるってことじゃないか!

ということは identity コマンド(今知った)はどうなるのかな?

$ identity file.pdf

おなじく、フォントが見つからないエラーが標準出力されていました。

  • 実は標準エラーにWarningの出力もでていましたが、影響しないようなので省略してます
  • どんなフォントが入ってなかったかはお察しください

原因判明

対象となるPDFで使用されているフォントがシステムに存在しない場合、MiniMagickはページ数のカウントを誤ることがある

ということがわかりました。

なんとMiniMagickのissueに同じ問題のissueが上がってました(ぐーぐるせん(略

しかもご親切にも、Workaroundだが、.verbose を使うと回避できるよとも書いてあります。神だ。

というか、MiniMagickはverboseコマンドの標準出力の内容を食べてるのか(意外とへちょいきがする)

対応内容

ということで、以下の対応をすれば良いことがわかりました。

  • 足りないと言われたフォントはシステムにインストールする
  • 今後PDFにどんなフォントが使われるかなんて知る由もないのでとりあえずページ数取得ロジックに.verbose をつけておく
    • ※神issueの最初の報告が2016年8月なので、MiniMagick側のバグとしてFIXされるのかもしれません

最後に

ImageMagickもMiniMagickも全然知らなかった私ではありましたが、落ち着いてひとつずつ見ていくことでなんとか解決に導くことができました。

…さて、誤ったページ数を修正するバッチを書かないと。

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

kazz

Javaエンジニアで生きてきましたがこのたびRailsエンジニアになれました。 オブジェクト指向中毒者のおっさんです。

kazzの書いた記事

週刊Railsウォッチ

インフラ

BigBinary記事より

ActiveSupport探訪シリーズ