なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

ImageMagickがInvalid UTF-8 sequenceを吐き出す

最近Rubyばっかり書いてる気がする。世の中わからないことでいっぱいだ。

convert -version

$ convert -version | grep Version
Version: ImageMagick 6.9.0-0 Q16 x86_64 2015-02-03 http://www.imagemagick.org

困ったこと

ImageMagickがちょっとくらいおかしな文字を流してきたところで大抵の場合は問題がないんだけど、 つい先日serverspecでImageMagickの対応しているフォーマットをテストしようと下のようなテストコードを書いたら、 Invalid UTF-8 sequenceで落ちてしまい大変だった。

describe command("convert -list format") do
  let(:disable_sudo) { true }

  its { should match(/#{Regexp.escape('PNG24*')}/) }
end

検証

example.rb

stdout = `convert -list format`
p /PNG/.match stdout

結果:

example.rb:2:in `match': invalid byte sequence in UTF-8 (ArgumentError)
        from example.rb:2:in `<main>'

ですよねー。convert -list formatInvalid UTF-8 sequenceを吐いているのだ。

Workaround

とりあえず、ぱっと思いついた方法は二つで、ServerSpec::Type::Command#stdoutを書き換えるか、 itsをやめてstdoutから不正なUTF-8文字を取り除いてsubjectにセットするか。 どっちでも簡単にできるけど、ServerSpec::Type::Command#stdoutを書き換えるのは何だか大げさな気がするので、 今回はstdoutから不正な文字を取り除く方法で対応することにした。

module Serverspec::Type
  class Command < Base
    def stdout
      command_result.stdout.scrub
    end

    def stderr
      command_result.stderr.scrub
    end
end

まあ、こんなことをしてもいいんですが・・・。

describe command("convert -list format") do
  let(:disable_sudo) { true }

  subject { described_class.stdout.scrub }

  it { should match(/#{Regexp.escape('PNG24*')}/) }
end

こっちのほうがいいかな。

described_classdescribe対象のインスタンスServerspec::Type::Commandが取れるので、そのstdoutを持ってきます。 後は、subjectにstdout.scrubをセットしておしまい。

なお、String#scrubはRuby2.1.0で追加されたメソッドなので、2.1より前のバージョンではstring-scrub gemを入れる必要がある。

そもそも

ImageMagickのconvertコマンドが標準出力に書き出しているInvalid UTF-8 sequenceとは何なのか、ImageMagickソースコードをちょっと追ってみました。 String#scrubには不正な文字を置き換える機能があるので、どの文字が不正なのか調べてみるとどうやらDJVUに怪しい?が・・・。

ということで、coders/djvu.cを見てみると、RegisterDJVUImage関数でentry->description=AcquireString("Déjà vu");というコードを見つけました。 AcquireStringはmemcpyしているだけなので、元の文字が壊れている気がする・・・。

最後に

追記

UTF-8ではなくlatin 1なのではという話がありました。iconvとかで変換する・・・とか、いかがでしょう?