Exception Diary
2009.04.18 [Sat]
_ いっとくさんノ質問ニ関スル調査
(注:tdiary-contrib の exifparser には、下記の変更がすでに入っています。)
ちょっと時間が出来たので、いっとくさんから受けていた質問の調査をするため、exifparserを追いかけてみた。結論から言うと、やっぱり携帯(docomo F905i)を通してupしたという画像には、Exifの規約に合わない部分があることを見つけた。で、exifparserを修正して、おかしなデータは無視するようにする。これで、元画像と同じようにExifが表示できるようになった。
以下は追いかけた際のメモ。参考になるかも知れないのでこちらにも上げておく。
_ exifparser修正までの作業メモ
exifparserがExceptionを上げているところまでは分かっているので、view_exif のrescue節に、Exceptionのbacktraceを表示するようにしてみた。
rescue exp = ($!).to_s + "<br>" ($!).backtrace.each do |btinfo| exp += btinfo exp += "<br>" end return exp end
結果は以下の通り。scan.rbのextendを呼んでいる部分で、Moduleでなければいけない引数がnilだと言っている。
wrong argument type nil (expected Module) /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:229:in `extend' /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:229:in `scan_IFD' /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:221:in `upto' /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:221:in `scan_IFD' /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:94:in `scan' /usr/local/lib/site_ruby/1.8/exifparser.rb:107:in `initialize' /usr/local/lib/site_ruby/1.8/exifparser.rb:105:in `open' /usr/local/lib/site_ruby/1.8/exifparser.rb:105:in `initialize' (plugin/recent_image.rb):341:in `new' (plugin/recent_image.rb):341:in `view_exif' (TDiary::Plugin#eval_src):51:in `eval_src'
extend というメソッドを呼んでいるのは、exifparser/scan.rb の229行目、←の部分。
def scan_IFD(tagTable, ifdname) num_dirs = decode_ushort(fin_read_n(2)) 1.upto(num_dirs) { curpos_tag = @fin.pos tag = parseTagID(fin_read_n(2)) tagclass = Tag.find(tag.hex, tagTable) unit, formatter = Tag::Format::Unit[decode_ushort(fin_read_n(2))] count = decode_ulong(fin_read_n(4)) tagdata = fin_read_n(4) obj = tagclass.new(tag, ifdname, count) obj.extend formatter, @byteOrder_module ←(※)ココ obj.pos = curpos_tag if unit * count > 4 curpos = @fin.pos begin @fin.pos = @tiffHeader0 + decode_ulong(tagdata) obj.dataPos = @fin.pos obj.data = fin_read_n(unit*count) ensure @fin.pos = curpos end else
Object.extend は、引数で指定したモジュールのインスタンスメソッドを self の特異メソッドとして追加する場合に使う。従って、formatter、@byteOrder_module は両方ともModuleでないといけないのに、どちらかnilだってことか。なるほど。
まず、@byteOrder_module を決めているところのコードはscan.rbの以下の部分。
# # get byte order # case tiff_header[0,2] when "MM" @byteOrder_module = Utils::Decode::Motorola when "II" @byteOrder_module = Utils::Decode::Intel else raise RuntimeError, "Unknown byte order" end self.extend @byteOrder_module @result[:offset_IFD0] = decode_ulong(tiff_header[4..-1])
データがMotorola形式(Big Endien)かIntel形式(Little Endien)かを表すコードがtiffヘッダに入っていて、それを読み取って決めているところらしい。ここに問題があるなら、RuntimeErrorがraiseされることになるから、現象と合わない。よって、formatter の方がnilでありそうだ、ということが分かる。
では、formatterはどこで代入されるのか? 以下の行に注目。
unit, formatter = Tag::Format::Unit[decode_ushort(fin_read_n(2))]
Tag::Format::Unit のコードは以下の通り。
# # maps number to size of one unit and the # corresponding formatter (defined below) module. # module Format Unit = { 1 => [1, ::Exif::Tag::Formatter::UByte], 2 => [1, ::Exif::Tag::Formatter::Ascii], 3 => [2, ::Exif::Tag::Formatter::UShort], 4 => [4, ::Exif::Tag::Formatter::ULong], 5 => [8, ::Exif::Tag::Formatter::URational], #6 => [1, ::Exif::Tag::Formatter::SByte], 7 => [1, ::Exif::Tag::Formatter::Undefined], 8 => [2, ::Exif::Tag::Formatter::SShort], 9 => [4, ::Exif::Tag::Formatter::SLong], 10 => [8, ::Exif::Tag::Formatter::SRational], #11 => [4, Exif::Formatter::SFloat], #12 => [8, Exif::Formatter::DFloat] } end
なるほど、decode_ushort(fin_read_n(2)) が 1,2,3,4,5,7,8,9,10のいずれかでないといけないところが、いずれにも当てはまらない値だ、ということか。
では、実際にはいくつになってしまっているのだろう。formatterがnilなら、Exceptionを上げるよう、scan_IFDにデバッグ・コードを追加してみる。
tagclass = Tag.find(tag.hex, tagTable) ifd = decode_ushort(fin_read_n(2)) unit, formatter = Tag::Format::Unit[ifd] if formatter == nil raise Exception.new(%Q[scan_IFD formatter = nil: ifd = #{sprintf("%04x", ifd)}]) end count = decode_ulong(fin_read_n(4))
以下の通りの結果になった。
500 Internal Server Error scan_IFD formatter = nil: ifd = 0000 (Exception) /usr/local/lib/site_ruby/1.8/exifparser/scan.rb:228:in `scan_IFD' ...
ここで、jpegファイルの構造の仕様をネットで探して読んでみる。Tag::Format::Unit は、InformationDirectory(IFD)のデータ・タイプを見て、マッチするデコーダModuleを返すHash tableになっているようだ、ということが分かった。要はこのデータ・タイプが0になっているのが悪い、と。だんだん分かってきた。
次に、実際にjpegデータを16進ダンプして見てみる。ビンゴ。最初のIFD(IFD0)の5番目のタグのtypeが確かに0x0000になっている。Tagそのものも0x0001になっていて、Exifタグのリストに見当たらない。これで、データがおかしいことが決定。
で、どうするか。不正なタグを読み飛ばすよう、scan.rb に1行追加。
unit, formatter = Tag::Format::Unit[decode_ushort(fin_read_n(2))] count = decode_ulong(fin_read_n(4)) tagdata = fin_read_n(4) next if formatter == nil # <- ココを追加 obj = tagclass.new(tag, ifdname, count)
これで成功。
GR DIGITAL 2 , 5.9mm, F2.4, 1/30sec., ISO100, -0.3EV
元画像と同じようにExifが表示されるようになった。Image GalleryのViewerモードでもOK。
この度は、個人的なトラブルにご対応いただきありがとうございます。<br>無事、Exifが表示されるようになりました。これで安心してモブログが出来ます。<br><br>重ねてお礼申し上げます。<br>ありがとうございました。