MiniMagickで画像を作る方法とデバッグプリント

MiniMagickで画像を作る方法をついも忘れてしまって、そのたびに調べなおすことになるのでメモしておく。

MiniMagick::Tool::Convert.new do |c|
  c.size '300x300'
  c << 'canvas:#FFFFFF'
  c << '-font' << 'VL-PGothic-Regular'
  c << '-fill' << '#000000'
  c << '-pointsize' << '14'
  c << '-gravity' << 'North-West'
  c << '-annotate' << '+10+10' << 'hoge ほげ'
  c << 'test.png'
end

いつもMiniMagick::Image.newから始めようとしてはまる。

こういうAPI的な面以外でもMiniMagickがなんだか難しいなと感じる。それはその内部でどのようにImageMagickコマンドを実行しているのか、いまひとつ分かりにくいためではないかと思う。

特に少し古めのバージョンだと例外も起きずに終了しているのに出力ファイルができないとか、例外が起きたものの何が問題なのか分からないといったことがままある。一応、MiniMagickにデバッグログを出力させることはできるのだが、これも十分とはいえない。

たとえば、あえてImageMagickを異常終了させるスクリプトを用意する。

require 'mini_magick'

# デバッグログを出力させる
if MiniMagick.logger.respond_to?(:level=)
  MiniMagick.logger.level = Logger::DEBUG
else
  MiniMagick.debug = true
end

# 時間制限を設定する
Process.setrlimit(:CPU, 1, 1)

# 時間制限を超過するような大きな画像を作成する
MiniMagick::Tool::Convert.new do |c|
  c.size '4096x4096'
  c << 'tile:logo:'
  c << '-font' << 'VL-PGothic-Regular'
  c << '-fill' << '#000000'
  c << '-pointsize' << '14'
  c << '-gravity' << 'North-West'
  c << '-annotate' << '+10+10' << 'hoge ほげ'
  c << 'test.png'
end

これを実行すると次のようにエラー終了する。(処理時間でシグナルを受けるようにしているので環境によってはエラーにならない。)

$ ruby test.rb
D, [2017-12-09T16:02:21.762056 #88432] DEBUG -- : [1.10s] convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill #000000 -pointsize 14 -gravity North-West -annotate +10+10 hoge ほげ test.png
/Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/shell.rb:17:in `run': `convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill #000000 -pointsize 14 -gravity North-West -annotate +10+10 hoge ほげ test.png` failed with error: (MiniMagick::Error)
    from /Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/tool.rb:92:in `call'
    from /Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/tool.rb:40:in `new'
    from test.rb:19:in `<main>'

これはMiniMagick 4.8.0の挙動だが、4.2あたりだと次のようになる。

$ ruby test.rb
[0.01s] convert -help
[1.09s] convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill #000000 -pointsize 14 -gravity North-West -annotate +10+10 hoge ほげ test.pn

どちらもtest.pngが作成されるのだが、その内容は途中までしか出力されていない。

出力が途中までなのは、この場合、意図通りなので問題ない。が、これらの状況から原因にたどりつくのはなかなか難しい。そこで、ImageMagickコマンドの実行をトレースするコードを作ってみた。

これを用いるとこのような出力を得られる。

$ ruby test.rb
=== execute_open3 ================= command (2017-12-09T16:11:10+09:00)
convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill \#000000 -pointsize 14 -gravity North-West -annotate \+10\+10 hoge\ \ほ\げ test.png
--- execute_open3 ----------------- status
#<Process::Status: pid 93588 SIGABRT (signal 6)>
=== execute_open3 ================= finish (2017-12-09T16:11:11+09:00)

D, [2017-12-09T16:11:11.490649 #93575] DEBUG -- : [1.10s] convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill #000000 -pointsize 14 -gravity North-West -annotate +10+10 hoge ほげ test.png
/Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/shell.rb:17:in `run': `convert -size 4096x4096 tile:logo: -font VL-PGothic-Regular -fill #000000 -pointsize 14 -gravity North-West -annotate +10+10 hoge ほげ test.png` failed with error: (MiniMagick::Error)
    from /Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/tool.rb:92:in `call'
    from /Users/akira/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/mini_magick-4.8.0/lib/mini_magick/tool.rb:40:in `new'
    from test.rb:19:in `<main>'

MiniMagickによって実行されたconvertコマンドが、シグナルを受けて終了したことを示している。

この例のような妙な状況というのはそう頻繁に起きるわけではないし、もう少し一般的なImageMagickコマンドがエラー終了するようなケースではきちんと例外を起こしてくれることが多い。そのため、こういうトレースをしたくなる場面はそうとう限られてくる。のだが、実際にこういう感じのエラーが起きるようになると、何度コードを見ても分からなくて困ったりするのよね……。