アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

JavaScript だけで EXIF を読む 2

先週 JavaScript だけで EXIF を読むという記事を書いたのだが、万里小路さんのコメントによりサンプルとして公開していたスクリプトにバグがあることが判明した。

具体的には文字列値として埋め込まれているデータをそのまま読み込むため NULL 文字などが入っていると文字化けが発生する。スクリプトの元ネタである Jacob Seidelin 氏のスクリプトでもこの部分の処理は一緒なので同じ問題が起きると思う。問題の処理は binaryajax.js にあり、以下のように実装されている。

this.getStringAt = function( offset, length )
{
  var value = [];
  for( var i = offset, j = 0; i < offset + length; ++i, ++j )
  {
    value[ j ] = String.fromCharCode( this.getByteAt(
    i) );
  }

  return value.join( "" );
};

文字列値として取得されるデータは文字コードが埋め込まれている。6 行目の this.getByteAt(i) という部分は整数値を返し String.fromCharCode() はそれを文字化する。データの文字列化という観点から見ると正しい処理なのだが NULL 文字のように表示不能なものまで含むので人間が読むものとしては不適切である。

そこで、以下のように変更してみた。

this.getStringAt = function( offset, length )
{
  var value = [];
  var max   = offset + length;
  for( var i = offset, j = 0; i < max; ++i, ++j )
  {
    // 0x00 ~ 0x1F は制御コードなので無視する
    var data = this.getByteAt( i );
    if( data < 32 ) { continue; }

    value[ j ] = String.fromCharCode( data );
  }

  return value.join( "" );
};

重要な変更点は 7 ~ 9 行目となる。文字コード 0x000x1F は制御コードで表示されないだろうから無視するようにした。この変更を行ったスクリプトとサンプルを以下に公開する。ライセンスは前回のサンプルと同様に Mozilla Public License とする。

備考

MP3 の ID3v2 タグなどもそうだがファイルの先頭や途中に記述するタイプのメタデータは、データの更新が発生するたびにファイル全体の書き換えが必要となる。これは非効率なので以下のように対策されることがある。

  • メターデータの領域にパディング ( 余白 ) をもたせておく
  • 余白には無効値として NULL を入れる
  • データが文字列値の場合、一般的な処理系ならば自動的に文字列終端として切り捨てられる
  • データが数値でも、文字列処理すれば余白がなくなるので、それを数値化すればよい。数値系メタデータの場合、たいがいは固定幅だと思われるが...
  • データの書き換えが発生したとき、データ長がパディングの範囲内なら他の部分の伸長は発生しないので、部分更新で済む

前回の記事のコメントでは Cannon のカメラで撮影した写真の Model などに NULL 文字が入っているとのことだった。推測になるが、これは上記のようなパディングとして入れているのではなかろうか?