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>ありがとうございました。