«前の日記(2009.04.09 [Thu]) 最新 次の日記(2009.04.26 [Sun])» 編集

Exception Diary


2009.04.18 [Sat]

_ いっとくさんノ質問ニ関スル調査

(注:tdiary-contrib の exifparser には、下記の変更がすでに入っています。)

ちょっと時間が出来たので、いっとくさんから受けていた質問の調査をするため、exifparserを追いかけてみた。結論から言うと、やっぱり携帯(docomo F905i)を通してupしたという画像には、Exifの規約に合わない部分があることを見つけた。で、exifparserを修正して、おかしなデータは無視するようにする。これで、元画像と同じようにExifが表示できるようになった。

    ・exifparser/scan.rb 修正パッチ

    ・パッチ当て済みのscan.rb

以下は追いかけた際のメモ。参考になるかも知れないのでこちらにも上げておく。

_ 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タグのリストに見当たらない。これで、データがおかしいことが決定。

画像の16進ダンプ

で、どうするか。不正なタグを読み飛ばすよう、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。

本日のツッコミ(全1件) [ツッコミを入れる]
_ いっとく (2009.04.19 [Sun] 09:10)

この度は、個人的なトラブルにご対応いただきありがとうございます。<br>無事、Exifが表示されるようになりました。これで安心してモブログが出来ます。<br><br>重ねてお礼申し上げます。<br>ありがとうございました。

[]

2001|01|
2006|04|05|06|07|08|09|10|
2007|06|07|08|09|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|08|09|10|11|12|
2011|01|02|04|06|07|08|
2014|12|
2015|04|

«前の日記(2009.04.09 [Thu]) 最新 次の日記(2009.04.26 [Sun])» 編集