exif.htm

戻る

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML LANG="ja">

<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=Shift_JIS">
<TITLE>Exif ビューア</TITLE>
</HEAD>

<BODY BGCOLOR="#CCFFFF" onResize="resize()">
<CENTER ID="back">
<B>Exif ビューア (Firefox 3)</B>
<BR>

<FORM onSubmit="return false">
<DIV STYLE="margin:2px">ファイル(Exif): <INPUT TYPE=FILE ID="file" SIZE=60 onChange="read()"></DIV>
<DIV ID="cont" STYLE="position:relative">

<DIV ID="img_win" STYLE="position:absolute; left:0; top:0; background-color:gray; overflow:auto">
<CANVAS ID="img" WIDTH=1 HEIGHT=1 STYLE="position:absolute; left:0; top:0; visibility:hidden"></CANVAS>
</DIV>
<TABLE ID="size" STYLE="position:absolute; left:0; margin-top:2px"><TR>
<TD VALIGN=TOP NOWRAP>表示サイズ:</TD>
<TD WIDTH=2></TD>
<TD NOWRAP>
<INPUT TYPE=RADIO NAME="scale" ID="fit" CHECKED onClick="scale_fit()"> 表示領域に合わせる<BR>
<INPUT TYPE=RADIO NAME="scale" ID="org" onClick="scale_org()"> 原寸<BR>
<INPUT TYPE=RADIO NAME="scale" ID="shr" onClick="scale_shr()"> 指定した割合で縮小
<INPUT TYPE=TEXT ID="cur_fact" READONLY VALUE="50" SIZE=5 STYLE="border:none; background-color:transparent; font-size:medium; text-align:right">%
<SPAN STYLE="white-space:pre"> </SPAN><INPUT TYPE=BUTTON VALUE="← 設定" onClick="set_factor()" STYLE="padding-left:0; padding-right:0">
<INPUT TYPE=TEXT ID="fact" SIZE=5> %<BR>
</TD>
</TR></TABLE>

<DIV ID="info_win" ALIGN=LEFT STYLE="position:absolute; top:0; background-color:white; overflow:auto">
</DIV>

</DIV>
</FORM>

<SPAN ID="msg" STYLE="position:absolute; left:0; top:0; border-style:double; text-align:center; padding:0.8em 2em; background-color:white; border-color:#CC0000; visibility:hidden">読み込み中...</SPAN>

<IFRAME ID="cvt_uc"  WIDTH=1 HEIGHT=1 FRAMEBORDER=0 SCROLLING=NO STYLE="position:absolute; left:0; top:0; visibility:hidden"></IFRAME>
<IFRAME ID="cvt_gpm" WIDTH=1 HEIGHT=1 FRAMEBORDER=0 SCROLLING=NO STYLE="position:absolute; left:0; top:0; visibility:hidden"></IFRAME>
<IFRAME ID="cvt_gai" WIDTH=1 HEIGHT=1 FRAMEBORDER=0 SCROLLING=NO STYLE="position:absolute; left:0; top:0; visibility:hidden"></IFRAME>

</CENTER>

<SCRIPT LANGUAGE="JavaScript1.5" TYPE="text/javascript">
<!--

function resize() {
  elem_img_win.style.width = elem_img_win.style.height
    = elem_info_win.style.width = elem_info_win.style.height = "1px";
  elem_size.style.top = elem_info_win.style.left = "0";

  var img_win_width;
  if((img_win_width = Math.floor(document.body.scrollWidth * 0.65)) < elem_size.offsetWidth)
    img_win_width = elem_size.offsetWidth;
  var cont_height;
  if((cont_height = document.body.scrollHeight - elem_cont.offsetTop - elem_back.offsetTop)
       <= elem_size.offsetHeight)
    cont_height = elem_size.offsetHeight + 1;
  elem_img_win.style.width = String(img_win_width) + "px";
  elem_img_win.style.height = elem_size.style.top
    = String(cont_height - elem_size.offsetHeight) + "px";
  elem_info_win.style.left = String(img_win_width + 8) + "px";
  var info_win_width;
  if((info_win_width = elem_cont.clientWidth - img_win_width - 8) < 1)
    info_win_width = 1;
  elem_info_win.style.width = String(info_win_width) + "px";
  elem_info_win.style.height = String(cont_height) + "px";

  if(elem_img.style.visibility == "visible") {
    if(elem_fit.checked)  // 表示領域に合わせる
      scale_fit();
    else
      adj_pos();
  }
}

function fact_keypress(e) {
  if(e.which == 13) {  // Enter
    set_factor();
    return;
  }
  // 小数 キー入力チェック
  return (e.which < 0x20 || e.which >= 0x30 && e.which <= 0x39 || e.which == 0x2e || e.ctrlKey || e.metaKey);
}

function read() {
  // ファイル選択ダイアログをキャンセルしたとき,Firefox では change イベントは発生しないが,
  // 他のブラウザでは発生するものもあるので,ファイル選択の有無を一応チェックしておく.
  if(!elem_file.files.length)
    return;
  elem_file.disabled = true;
  // FileUpload コントロールの近くに読み込み中メッセージが表示される場合,この
  // タイミングでメッセージを表示すると,FileUpload コントロールの再描画でメッ
  // セージの表示が潰されることがあるので,FileUpload コントロールの再描画の後
  // でメッセージが表示されるようにタイミングを調整する.
  setTimeout(read2, 0);
}

function read2() {
  elem_img.style.visibility = "hidden";
  elem_img.width = elem_img.height = 1;
  elem_img.style.left = elem_img.style.top = "0";
  elem_img.style.width = elem_img.style.height = "";
  elem_info_win.innerHTML = "";

  with(elem_msg) {
    style.left = String(document.body.scrollLeft + ((document.body.clientWidth  - offsetWidth ) >> 1)) + "px";
    style.top  = String(document.body.scrollTop  + ((document.body.clientHeight - offsetHeight) >> 1)) + "px";
    style.visibility = "visible";
  }
  document.documentElement.style.cursor = "wait";

  elem_info_win.style.overflow = "hidden";  // スクロール バーに前のスクロール量が残ってしまうので,おまじない.
  setTimeout(read3, 0);
}

function read3() {
  elem_info_win.style.overflow = "auto";  // スクロール バーに前のスクロール量が残ってしまうので,おまじない.

  // Firefox 7 では nsIDOMFile オブジェクトの getAsBinary(),getAsDataURL() 等が削除された.
  // 代わりに,Firefox 3.6 で新設された FileReader オブジェクトを使って同様のことができるので,
  // FileReader オブジェクトが使えればそれを使い,使えなければ以前のままの処理とするように修正
  // した.
  if(typeof FileReader == "function") {
    file_reader = new FileReader();
    file_reader.onload = read_fr;
    file_reader.readAsBinaryString(elem_file.files.item(0));
  }
  else {
    file = elem_file.files.item(0).getAsBinary();

    read4();
  }
}

function read_fr() {
  file = file_reader.result;
  file_reader = undefined;

  read4();
}

function read4() {
  if((file.length < 2) ? true : ((get_short_be(file, 0) == 0xffd8/*SOI*/) ? read_JPEG() : read_TIFF())) {
    with(elem_msg) {
      style.visibility = "hidden";
      style.left = style.top = "0";
    }
    document.documentElement.style.cursor = "auto";
    alert("ファイルが Exif 形式でないか、このプログラムでは対応していないバージョンのファイルです。");
    elem_file.disabled = false;
    return;
  }

  put_info();  // 情報表示
}

// 縮小割合設定
function set_factor() {
  var fact = elem_fact.value;
  if(fact.search(/\S/) == -1)  // 未入力
    return;
  if(fact.search(/^\s*\d*\.?\d*\s*$/) == -1) {
    fact = 0;
  }
  else {
    if(isNaN(fact = parseFloat(fact)))
      fact = 0;
    else
      fact = Math.round(fact * 100) / 100;
  }
  if(fact == 0 || fact > 100) {
    alert("縮小割合の入力に誤りがあります。");
    return;
  }

  factor = fact / 100;
  elem_cur_fact.value = String(fact);
  if(elem_shr.checked)
    scale_shr();
}

// 表示領域に合わせる
function scale_fit() {
  if(elem_img.width / elem_img_win.clientWidth > elem_img.height / elem_img_win.clientHeight) {
    elem_img.style.width = (elem_img.width > elem_img_win.clientWidth) ? "100%" : "";
    elem_img.style.height = "";
  }
  else {
    elem_img.style.width = "";
    elem_img.style.height = (elem_img.height > elem_img_win.clientHeight) ? "100%" : "";
  }
  adj_pos();
}

// 原寸
function scale_org() {
  elem_img.style.width = elem_img.style.height = "";
  adj_pos();
}

// 指定した割合で縮小
function scale_shr() {
  elem_img.style.width = Math.round(elem_img.width * factor);
  elem_img.style.height = "";
  adj_pos();
}

// 画像位置調整
function adj_pos() {
  // 一旦位置をゼロにして,スクロール バーの有無を確定する.
  elem_img.style.left = elem_img.style.top = "0";
  // 画像の幅/高さが表示領域より小さければセンタリング
  if(elem_img.offsetWidth < elem_img_win.clientWidth)
    elem_img.style.left = String((elem_img_win.clientWidth - elem_img.offsetWidth) >> 1) + "px";
  if(elem_img.offsetHeight < elem_img_win.clientHeight)
    elem_img.style.top = String((elem_img_win.clientHeight - elem_img.offsetHeight) >> 1) + "px";
}

// **** JPEG ****
function read_JPEG() {
  var exif = false;
  var Y = undefined;
  var X;
  for(var off = 2; ; ) {
    var len;

    if(off + 2 > file.length)
      return -1;
    switch(get_short_be(file, off)) {  // marker
    case 0xffda:  // SOS
      if(!exif)
        return -1;
      if(Y == undefined)
        return -1;

      img_width  = X;
      img_height = Y;

      // JPEG のレンダリングはブラウザに任せる
      elem_src = document.createElement("IMG");
      elem_src.width  = X;
      elem_src.height = Y;
      elem_src.onload = JPEG_loaded;
      // Firefox 7 では nsIDOMFile オブジェクトの getAsBinary(),getAsDataURL() 等が削除された.
      // 代わりに,Firefox 3.6 で新設された FileReader オブジェクトを使って同様のことができるので,
      // FileReader オブジェクトが使えればそれを使い,使えなければ以前のままの処理とするように修正
      // した.
      if(typeof FileReader == "function")
        // Firefox 77 以上では,画像のレンダリングが Exif 情報に従って行われるようになった.
        // そのため,画像の回転/反転が二重に行われてしまうようになった.
        // Exif 情報に従う回転/反転が有効になっている場合は,JPEG ファイルのデータを修正して,
        // ブラウザに Exif 情報を認識させないようにする.
        elem_src.src = "data:image/jpeg;base64," + btoa(
                         (getComputedStyle(document.documentElement)["imageOrientation"] == "from-image")
                           ? file.substr(0, off_id) + "\0" + file.substr(off_id + 1) : file);
      else
        elem_src.src = elem_file.files.item(0).getAsDataURL();

      return 0;

    case 0xffe1:  // APP1
      if(off + 4 > file.length)
        return -1;
      len = get_short_be(file, off + 2);
      if(off + 2 + len > file.length)
        return -1;
      if(len >= 8) {
        if(file.substr(off + 4, 6) == "Exif\0\0") {
          off_id = off + 4;
          if(read_Exif(file.substr(off + 10, len - 8)))
            return -1;
          exif = true;
        }
      }
      break;

    case 0xffc0:  // SOF0
    case 0xffc2:  // SOF2
      if(off + 4 > file.length)
        return -1;
      len = get_short_be(file, off + 2);
      if(off + 2 + len > file.length)
        return -1;
      if(len < 8)
        return -1;
      if(len != 8 + get_byte(file, off + 9) * 3)
        return -1;
      if(get_byte(file, off + 4) != 8)
        return -1;
      Y = get_short_be(file, off + 5);
      X = get_short_be(file, off + 7);
      break;
    }

    if(off + 4 > file.length)
      return -1;
    off += 2 + get_short_be(file, off + 2);
  }
}

function JPEG_loaded() {
  show_img();  // 画像表示
}

// **** TIFF ****
function read_TIFF() {
  if(read_Exif(file))
    return -1;

  // チェック
  switch(img_width = TIFF_tags[I_ImageWidth].value) {
  case undefined:
  case 0:
    return -1;
  }
  switch(img_height = TIFF_tags[I_ImageLength].value) {
  case undefined:
  case 0:
    return -1;
  }
  value = TIFF_tags[I_BitsPerSample].value;
  if(value[0] != 8 || value[1] != 8 || value[2] != 8)
    return -1;
  if(TIFF_tags[I_Compression].value != 1)  // no compression 以外
    return -1;
  if(TIFF_tags[I_SamplesPerPixel].value != 3)
    return -1;
  switch(RowsPerStrip = TIFF_tags[I_RowsPerStrip].value) {
  case undefined:  // Exif の仕様ではデフォルトなし
  case 0:
    return -1;
  }
  if((StripOffsets = TIFF_tags[I_StripOffsets].value) == undefined)
    return -1;
  if((StripByteCounts = TIFF_tags[I_StripByteCounts].value) == undefined)
    return -1;
  if(StripOffsets.length != StripByteCounts.length)
    return -1;

  var pc_chunky;
  switch(TIFF_tags[I_PlanarConfiguration].value) {
  case 1:  // Chunky
    pc_chunky = true;

    if(StripOffsets.length
         != ((RowsPerStrip < img_height) ? Math.floor((img_height + RowsPerStrip - 1) / RowsPerStrip) : 1))
      return -1;
    break;

  case 2:  // Planar
    pc_chunky = false;

    if(StripOffsets.length
         != ((RowsPerStrip < img_height) ? 3 * Math.floor((img_height + RowsPerStrip - 1) / RowsPerStrip) : 3))
      return -1;
    break;

  default:
    return -1;
  }

  var pi_RGB;
  var strip_bytes;
  var ss_422;
  var i;
  switch(TIFF_tags[I_PhotometricInterpretation].value) {
  case 2:  // RGB
    pi_RGB = true;

    if(pc_chunky) {  // Chunky
      row_bytes = img_width * 3;
      strip_bytes = RowsPerStrip * row_bytes;
      for(i = 0; i < StripOffsets.length - 1; i++) {
        if(StripByteCounts[i] != strip_bytes)
          return -1;
      }
      if(StripByteCounts[i] != (img_height - (StripOffsets.length - 1) * RowsPerStrip) * row_bytes)
        return -1;
    }
    else {  // Planar
      if(StripOffsets.length % 3)
        return -1;
      row_bytes = img_width;
      strip_bytes = RowsPerStrip * row_bytes;
      var strips1 = StripOffsets.length / 3;
      for(i = 0; i < StripOffsets.length; i++) {
        if(StripByteCounts[i]
             != (((i + 1) % strips1) ? strip_bytes : (img_height - (strips1 - 1) * RowsPerStrip) * row_bytes))
          return -1;
      }
    }
    break;

  case 6:  // YCbCr
    pi_RGB = false;

    if(!pc_chunky)  // Chunky 以外
      return -1;
    if((value = TIFF_tags[I_YCbCrSubSampling].value) == undefined)
      return -1;
    if(value[0] != 2 || value[1] != 1 && value[1] != 2)  // 4:2:2,4:2:0 以外
      return -1;
    if(!(ss_422 = (value[1] == 1))) {  // 4:2:0
      if(RowsPerStrip < img_height) {
        if(RowsPerStrip % 2)  // RowsPerStrip が奇数
          return -1;
      }
    }
    switch(TIFF_tags[I_YCbCrPositioning].value) {
    case 1:  // centered
      cosited = false;
      break;
    case 2:  // cosited
      cosited = true;
      break;
    default:
      return -1;
    }

    value = TIFF_tags[I_YCbCrCoefficients].value;
    if(!value[0].d || !value[1].d || !value[2].d)
      return -1;
    LR = value[0].n / value[0].d;
    LG = value[1].n / value[1].d;
    LB = value[2].n / value[2].d;
    width_odd = img_width % 2;

    if(ss_422) {  // 4:2:2
      row_bytes = (img_width + width_odd) * 2;
      strip_bytes = RowsPerStrip * row_bytes;
      for(i = 0; i < StripOffsets.length - 1; i++) {
        if(StripByteCounts[i] != strip_bytes)
          return -1;
      }
      if(StripByteCounts[i] != (img_height - (StripOffsets.length - 1) * RowsPerStrip) * row_bytes)
        return -1;
    }
    else {  // 4:2:0
      row_bytes = (img_width + width_odd) * 3;  // 2 行分
      strip_bytes = RowsPerStrip / 2 * row_bytes;
      for(i = 0; i < StripOffsets.length - 1; i++) {
        if(StripByteCounts[i] != strip_bytes)
          return -1;
      }
      if(StripByteCounts[i]
           != (img_height - (StripOffsets.length - 1) * RowsPerStrip + img_height % 2) / 2 * row_bytes)
        return -1;
    }
    break;

  default:
    return -1;
  }

  if((value = TIFF_tags[I_ReferenceBlackWhite].value) == undefined) {
    // JEITA CP-3451A の仕様ではこうなっているが,YCbCr のときのデフォルト値は
    // [0, 255, 128, 255, 128, 255] でなくていいのか?
    RB0 = RB1 = RB2 = 0;
    SC0 = 1;
    SC1 = SC2 = (pi_RGB) ? 1 : 127 / 128;
  }
  else {
    if(!value[0].d || !value[1].d || !value[2].d || !value[3].d || !value[4].d || !value[5].d)
      return -1;
    RB0 = value[0].n / value[0].d;
    RB1 = value[2].n / value[2].d;
    RB2 = value[4].n / value[4].d;
    SC0 = 255 / (value[1].n / value[1].d - RB0);
    var CR = (pi_RGB) ? 255 : 127;
    SC1 = CR / (value[3].n / value[3].d - RB1);
    SC2 = CR / (value[5].n / value[5].d - RB2);
  }

  // 画像
  elem_src = document.createElement("CANVAS");
  elem_src.width  = img_width;
  elem_src.height = img_height;
  src_ctx = elem_src.getContext("2d");
  src_ctx.fillStyle = "rgba(0,0,0,1)";
  src_ctx.fillRect(0, 0, img_width, img_height);
  src_img = src_ctx.getImageData(0, 0, img_width, img_height);
  img_data = src_img.data;

  i_strip = -1;
  if(pi_RGB) {  // RGB
    if(pc_chunky) {  // Chunky
      img_rows = img_height;
      strip_rows = 0;
      i_img = 0;
      setTimeout(TIFF_RGB_Chunky, 0);
    }
    else {  // Planar
      plane = -1;
      img_rows = 0;
      setTimeout(TIFF_RGB_Planar, 0);
    }
  }
  else {  // YCbCr
    SC1_L = SC1 * (2 - 2 * LB);
    SC2_L = SC2 * (2 - 2 * LR);

    img_rows = img_height;
    strip_rows = 0;
    i_img = 0;
    if(ss_422) {  // 4:2:2
      setTimeout(TIFF_YCbCr_422, 0);
    }
    else {  // 4:2:0
      img_stride = img_width * 4;
      sub_width = (img_width + width_odd) / 2;
      prev_Y0 = new Array();
      prev_Y1 = new Array();
      prev_Cb_L = new Array();
      prev_Cr_L = new Array();
      prev_Y0.length = prev_Y1.length = prev_Cb_L.length = prev_Cr_L.length = sub_width;

      setTimeout(TIFF_YCbCr_420, 0);
    }
  }

  return 0;
}

function TIFF_RGB_Chunky() {
  for(var cnt = 100; cnt; cnt--) {
    if(!strip_rows) {
      strip_off = StripOffsets[++i_strip];
      strip_rows = RowsPerStrip;
    }
    for(var off_end = strip_off + row_bytes; strip_off < off_end; strip_off += 3) {
      var w;
      w = Math.round((file.charCodeAt(strip_off    ) - RB0) * SC0);  // R
      img_data[i_img    ] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((file.charCodeAt(strip_off + 1) - RB1) * SC1);  // G
      img_data[i_img + 1] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((file.charCodeAt(strip_off + 2) - RB2) * SC2);  // B
      img_data[i_img + 2] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      i_img += 4;
    }
    if(!--img_rows) {
      TIFF_ready();
      return;
    }
    strip_rows--;
  }
  setTimeout(TIFF_RGB_Chunky, 0);
}

function TIFF_RGB_Planar() {
  for(var cnt = 100; cnt; cnt--) {
    if(!img_rows) {
      if(plane == 2) {
        TIFF_ready();
        return;
      }
      switch(++plane) {
      case 0:
        RB = RB0;
        SC = SC0;
        break;
      case 1:
        RB = RB1;
        SC = SC1;
        break;
      default:
        RB = RB2;
        SC = SC2;
      }
      i_img = plane;
      img_rows = img_height;
      strip_rows = 0;
    }
    if(!strip_rows) {
      strip_off = StripOffsets[++i_strip];
      strip_rows = RowsPerStrip;
    }
    for(var off_end = strip_off + row_bytes; strip_off < off_end; strip_off++) {
      var w = Math.round((file.charCodeAt(strip_off) - RB) * SC);
      img_data[i_img] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      i_img += 4;
    }
    img_rows--;
    strip_rows--;
  }
  setTimeout(TIFF_RGB_Planar, 0);
}

function TIFF_YCbCr_422() {
  for(var cnt = 100; cnt; cnt--) {
    if(!strip_rows) {
      strip_off = StripOffsets[++i_strip];
      strip_rows = RowsPerStrip;
    }
    for(var off_end = strip_off + row_bytes; ; ) {
      var Cb_L = (file.charCodeAt(strip_off + 2) - RB1) * SC1_L;
      var Cr_L = (file.charCodeAt(strip_off + 3) - RB2) * SC2_L;
      var Y;
      var R, B;
      var w;

      Y = (file.charCodeAt(strip_off) - RB0) * SC0;
      w = Math.round(R = Cr_L + Y);  // R
      img_data[i_img    ] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round(B = Cb_L + Y);  // B
      img_data[i_img + 2] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((Y - LB * B - LR * R) / LG);  // G
      img_data[i_img + 1] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      if((strip_off += 4) == off_end && width_odd) {  // 幅が奇数のときの最終カラム
        i_img += 4;
        break;
      }

      Y = (file.charCodeAt(strip_off - 3) - RB0) * SC0;
      if(cosited) {
        if(strip_off < off_end) {  // 最終カラム以外
          Cb_L = (Cb_L + (file.charCodeAt(strip_off + 2) - RB1) * SC1_L) / 2;
          Cr_L = (Cr_L + (file.charCodeAt(strip_off + 3) - RB2) * SC2_L) / 2;
        }
      }
      w = Math.round(R = Cr_L + Y);  // R
      img_data[i_img + 4] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round(B = Cb_L + Y);  // B
      img_data[i_img + 6] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((Y - LB * B - LR * R) / LG);  // G
      img_data[i_img + 5] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      i_img += 8;
      if(strip_off == off_end)  // 最終カラム
        break;
    }
    if(!--img_rows) {
      TIFF_ready();
      return;
    }
    strip_rows--;
  }
  setTimeout(TIFF_YCbCr_422, 0);
}

function TIFF_YCbCr_420() {
  for(var cnt = 50; cnt; cnt--) {
    if(!strip_rows) {
      strip_off = StripOffsets[++i_strip];
      strip_rows = RowsPerStrip;
    }
    var i_prev = 0;
    for(off_end = strip_off + row_bytes; ; ) {
      var Cb_L = (file.charCodeAt(strip_off + 4) - RB1) * SC1_L;
      var Cr_L = (file.charCodeAt(strip_off + 5) - RB2) * SC2_L;
      var Cb_L_p, Cr_L_p;
      var i_img2;
      var Cb_L_2, Cr_L_2;
      var Y;
      var R, B;
      var w;

      if(img_rows < img_height) {
        Cb_L_p = prev_Cb_L[i_prev];
        Cr_L_p = prev_Cr_L[i_prev];
        i_img2 = i_img - img_stride;
        if(cosited) {
          Cb_L_2 = (Cb_L_p + Cb_L) / 2;
          Cr_L_2 = (Cr_L_p + Cr_L) / 2;
        }
        else {
          Cb_L_2 = Cb_L_p;
          Cr_L_2 = Cr_L_p;
        }
        Y = prev_Y0[i_prev];
        w = Math.round(R = Cr_L_2 + Y);  // R
        img_data[i_img2    ] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
        w = Math.round(B = Cb_L_2 + Y);  // B
        img_data[i_img2 + 2] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
        w = Math.round((Y - LB * B - LR * R) / LG);  // G
        img_data[i_img2 + 1] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      }
      Y = (file.charCodeAt(strip_off) - RB0) * SC0;
      w = Math.round(R = Cr_L + Y);  // R
      img_data[i_img    ] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round(B = Cb_L + Y);  // B
      img_data[i_img + 2] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((Y - LB * B - LR * R) / LG);  // G
      img_data[i_img + 1] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      if(img_rows != 1) {  // 高さが奇数のときの最終行以外
        prev_Cb_L[i_prev] = Cb_L;
        prev_Cr_L[i_prev] = Cr_L;
        prev_Y0[i_prev] = (file.charCodeAt(strip_off + 2) - RB0) * SC0;
      }
      if((strip_off += 6) == off_end && width_odd) {  // 幅が奇数のときの最終カラム
        i_img += 4;
        break;
      }

      var Cb_L_n;
      var Cr_L_n;
      var Cb_L_3 = Cb_L;
      var Cr_L_3 = Cr_L;
      if(cosited) {
        if(strip_off < off_end) {  // 最終カラム以外
          Cb_L_n = (file.charCodeAt(strip_off + 4) - RB1) * SC1_L;
          Cr_L_n = (file.charCodeAt(strip_off + 5) - RB2) * SC2_L;
          Cb_L_3 = (Cb_L + Cb_L_n) / 2;
          Cr_L_3 = (Cr_L + Cr_L_n) / 2;
        }
      }
      if(img_rows < img_height) {  // 最初の行以外
        if(cosited) {
          if(strip_off == off_end) {  // 最終カラム
            Cb_L_2 = (Cb_L_p + Cb_L) / 2;
            Cr_L_2 = (Cr_L_p + Cr_L) / 2;
          }
          else {
            Cb_L_2 = (Cb_L_p + prev_Cb_L[i_prev + 1] + Cb_L + Cb_L_n) / 4;
            Cr_L_2 = (Cr_L_p + prev_Cr_L[i_prev + 1] + Cr_L + Cr_L_n) / 4;
          }
        }
        else {
          Cb_L_2 = Cb_L_p;
          Cr_L_2 = Cr_L_p;
        }
        Y = prev_Y1[i_prev];
        w = Math.round(R = Cr_L_2 + Y);  // R
        img_data[i_img2 + 4] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
        w = Math.round(B = Cb_L_2 + Y);  // B
        img_data[i_img2 + 6] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
        w = Math.round((Y - LB * B - LR * R) / LG);  // G
        img_data[i_img2 + 5] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      }
      Y = (file.charCodeAt(strip_off - 5) - RB0) * SC0;
      w = Math.round(R = Cr_L_3 + Y);  // R
      img_data[i_img + 4] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round(B = Cb_L_3 + Y);  // B
      img_data[i_img + 6] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      w = Math.round((Y - LB * B - LR * R) / LG);  // G
      img_data[i_img + 5] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
      if(img_rows != 1)  // 高さが奇数のときの最終行以外
        prev_Y1[i_prev] = (file.charCodeAt(strip_off - 3) - RB0) * SC0;
      i_img += 8;
      if(strip_off == off_end)  // 最終カラム
        break;
      i_prev++;
    }
    i_img += img_stride;
    if((img_rows -= 2) <= 0) {
      if(!img_rows) {  // 高さが偶数
        for(i_prev = 0; i_prev < sub_width; i_prev++) {
          Cb_L = prev_Cb_L[i_prev];
          Cr_L = prev_Cr_L[i_prev];

          Y = prev_Y0[i_prev];
          w = Math.round(R = Cr_L + Y);  // R
          img_data[i_img    ] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          w = Math.round(B = Cb_L + Y);  // B
          img_data[i_img + 2] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          w = Math.round((Y - LB * B - LR * R) / LG);  // G
          img_data[i_img + 1] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          if(i == sub_width - 1 && width_odd)  // 幅が奇数のときの最終カラム
            break;

          Y = prev_Y1[i_prev];
          if(cosited) {
            if(i < sub_width - 1) {  // 最終カラム以外
              Cb_L = (Cb_L + prev_Cb_L[i_prev + 1]) / 2;
              Cr_L = (Cr_L + prev_Cr_L[i_prev + 1]) / 2;
            }
          }
          w = Math.round(R = Cr_L + Y);  // R
          img_data[i_img + 4] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          w = Math.round(B = Cb_L + Y);  // B
          img_data[i_img + 6] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          w = Math.round((Y - LB * B - LR * R) / LG);  // G
          img_data[i_img + 5] = (w < 0) ? 0 : ((w > 255) ? 255 : w);
          i_img += 8;
        }
      }

      prev_Y0 = prev_Y1 = prev_Cb_L = prev_Cr_L = undefined;

      TIFF_ready();
      return;
    }
    strip_rows -= 2;
  }
  setTimeout(TIFF_YCbCr_420, 0);
}

function TIFF_ready() {
  src_ctx.putImageData(src_img, 0, 0);
  src_ctx = undefined;
  src_img = undefined;

  show_img();  // 画像表示
}

// Exif 情報読み込み
function read_Exif(tiff) {
  // Image File Header
  if(tiff.length < 8)
    return -1;
  switch(get_short_be(tiff, 0)) {
  case 0x4949:  // II
    get_value[SHORT] = get_short = get_short_le;
    get_value[LONG]  = get_long  = get_long_le;
    get_slong = get_slong_le;
    break;
  case 0x4d4d:  // MM
    get_value[SHORT] = get_short = get_short_be;
    get_value[LONG]  = get_long  = get_long_be;
    get_slong = get_slong_be;
    break;
  default:
    return -1;
  }
  if(get_short(tiff, 2) != 42)
    return -1;

  var value;

  // 0th IFD
  if(read_IFD(tiff, get_long(tiff, 4), TIFF_tags))
    return -1;

  // チェック
  // 解像度
  value = TIFF_tags[I_XResolution].value;
  if(!value.d)
    return -1;
  resolution = value.n / value.d;
  value = TIFF_tags[I_YResolution].value;
  if(!value.d)
    return -1;
  if(resolution != value.n / value.d)
    return -1;
  // 画像方向
  Orientation = TIFF_tags[I_Orientation].value;
  if(!Orientation || Orientation > 8)
    return -1;

  // Exif IFD
  if(TIFF_tags[I_Exif_IFD].value != undefined) {
    if(read_IFD(tiff, TIFF_tags[I_Exif_IFD].value, Exif_tags))
      return -1;
  }

  // チェック
  // 被写体領域
  if((value = Exif_tags[I_SubjectArea].value) != undefined) {
    if(value.length < 2 || value.length > 4)
      return -1;
  }

  // GPS IFD
  if(TIFF_tags[I_GPS_IFD].value != undefined) {
    if(read_IFD(tiff, TIFF_tags[I_GPS_IFD].value, GPS_tags))
      return -1;
  }

  return 0;
}

// IFD 読み込み
function read_IFD(tiff, off, tags) {
  if(!off || off + 2 > tiff.length)
    return -1;
  var n_dir = get_short(tiff, off);
  off += 2;
  if(off + n_dir * 12 + 4 > tiff.length)
    return -1;

  var i;

  // 初期値(undefined or デフォルト値)
  for(i = 0; i < tags.length; i++)
    tags[i].value = tags[i][3];

  for(; n_dir; n_dir--, off += 12) {
    var tag = get_short(tiff, off);  // Tag

    for(var i_tag = 0; i_tag < tags.length; i_tag++) {
      if(tags[i_tag][0] == tag) {
        // Type
        var type = get_short(tiff, off + 2);
        if(tags[i_tag][1] == SHORT_LONG) {  // SHORT or LONG
          switch(type) {
          case SHORT:
          case LONG:
            break;
          default:
            return -1;
          }
        }
        else {
          if(type != tags[i_tag][1])
            return -1;
        }

        // Count
        var count = get_long(tiff, off + 4);
        if(tags[i_tag][2]) {
          if(count != tags[i_tag][2])
            return -1;
        }

        // Value or Offset
        var val_off = off + 8;
        if(count * field_size[type] > 4) {
          val_off = get_long(tiff, val_off);
          if(val_off + count * field_size[type] > tiff.length)
            return -1;
        }

        switch(type) {
        case ASCII:
        case UNDEFINED:
          tags[i_tag].value = tiff.substr(val_off, count);
          break;

        default:
          if(tags[i_tag][2] == 1) {  // Count
            tags[i_tag].value = get_value[type](tiff, val_off);
          }
          else {
            tags[i_tag].value = new Array();
            tags[i_tag].value.length = count;
            for(i = 0; i < count; i++) {
              tags[i_tag].value[i] = get_value[type](tiff, val_off);
              val_off += field_size[type];
            }
          }
        }

        break;
      }
    }
  }

  return 0;
}

// 画像表示
function show_img() {
  var ctx = elem_img.getContext("2d");

  if(Orientation < 5) {
    elem_img.width  = img_width;
    elem_img.height = img_height;

    var m11 = 1, m22 = 1;
    switch(Orientation) {
    case 2:  // 上右
      m11 = -1;
      break;
    case 3:  // 下右
      m11 = m22 = -1;
      break;
    case 4:  // 下左
      m22 = -1;
      break;
    }
    ctx.setTransform(m11, 0, 0, m22, (m11 < 0) ? img_width : 0, (m22 < 0) ? img_height : 0);
  }
  else {
    elem_img.width  = img_height;
    elem_img.height = img_width;

    var m12 = 1, m21 = 1;
    switch(Orientation) {
    case 6:  // 右上
      m21 = -1;
      break;
    case 7:  // 右下
      m12 = m21 = -1;
      break;
    case 8:  // 左下
      m12 = -1;
      break;
    }
    ctx.setTransform(0, m12, m21, 0, (m21 < 0) ? img_height : 0, (m12 < 0) ? img_width : 0);
  }

  ctx.drawImage(elem_src, 0, 0);
  elem_src = undefined;
  if     (elem_fit.checked)  // 表示領域に合わせる
    scale_fit();
  else if(elem_org.checked)  // 原寸
    scale_org();
  else                       // 指定した割合で縮小
    scale_shr();
  elem_img.style.visibility = "visible";

  with(elem_msg) {
    style.visibility = "hidden";
    style.left = style.top = "0";
  }
  document.documentElement.style.cursor = "auto";
  elem_file.disabled = false;
}

// 情報表示
// Firefox 57 からは,"data" URL が同一オリジン ポリシーにより制限されるようになったため,
// コード変換に "data" URL を使うことができなくなった.
// 代わりに,FileReader と Blob のコンストラクタが実装されているバージョンでは FileReader
// を使うようにした.
function put_info() {
  var value, value2;
  var unit;
  var str, str2;
  var n, m;
  var off;
  var uc_src = undefined;
  var gpm_src = undefined;
  var gai_src = undefined;
  var rtn;
  var i;

  info = "<TABLE CELLSPACING=0 STYLE='white-space:pre; margin:0; padding:4px 0'>";

  // 画像の幅/高さ
  info_item("サイズ", String(img_width) + " x " + String(img_height));

  // 解像度
  unit = ResolutionUnit_str[TIFF_tags[I_ResolutionUnit].value];
  info_item("解像度", (unit == undefined) ? "不明" : String(resolution) + " " + unit);

  // 作成日時
  if((value = TIFF_tags[I_DateTime].value) != undefined)
    info_item("作成日時", DateTime(value, (TIFF_tags[I_Exif_IFD].value == undefined)
                                            ? undefined : Exif_tags[I_SubsecTime].value));

  // タイトル
  if((value = TIFF_tags[I_ImageDescription].value) != undefined)
    info_item("タイトル", ascii(value));

  // メーカー
  if((value = TIFF_tags[I_Make].value) != undefined)
    info_item("メーカー", ascii(value));

  // モデル
  if((value = TIFF_tags[I_Model].value) != undefined)
    info_item("モデル", ascii(value));

  // ソフトウェア
  if((value = TIFF_tags[I_Software].value) != undefined)
    info_item("使用ソフトウェア", ascii(value));

  // アーティスト
  if((value = TIFF_tags[I_Artist].value) != undefined)
    info_item("作者", ascii(value));

  // 撮影著作権者/編集著作権者
  if((value = TIFF_tags[I_Copyright].value) != undefined) {
    str = "";
    str2 = "";
    if((i = value.indexOf("\0")) != -1) {
      if((str = value.substr(0, i)) == " ")
        str = "";
      var i2;
      if((i2 = value.indexOf("\0", i + 1)) != -1)
        str2 = value.substr(i + 1, i2 - i - 1);
    }
    if(str.length)
      info_item("撮影著作権者", info_text(str));
    if(str2.length)
      info_item("編集著作権者", info_text(str2));
  }

  // 付属情報
  if(TIFF_tags[I_Exif_IFD].value != undefined) {
    info_item("<BR>", "");

    // 色空間情報
    if((value = Exif_tags[I_ColorSpace].value) != undefined) {
      str = ColorSpace_str[value];
      info_item("色空間情報", (str == undefined) ? "不明" : str);
    }

    // 再生ガンマ
    if((value = Exif_tags[I_Gamma].value) != undefined)
      info_item("再生ガンマ", rational(value, 3, undefined));

    // メーカー ノート
    if((value = Exif_tags[I_MakerNote].value) != undefined)
      info_item("メーカー ノート", "(" + String(value.length) + " バイト)");

    // ユーザ コメント
    if((value = Exif_tags[I_UserComment].value) != undefined) {
      rtn = conv_text(value, "info_uc");
      info_item("ユーザ コメント", rtn.txt);
      if(rtn.src != undefined) {
        if(fr_uc == undefined)
          elem_cvt_uc.onload = uc_loaded;
        uc_src = rtn.src;
      }
    }

    // 関連音声ファイル
    if((value = Exif_tags[I_RelatedSoundFile].value) != undefined)
      info_item("関連音声ファイル", ascii(value));

    // 原画像データの生成日時
    if((value = Exif_tags[I_DateTimeOriginal].value) != undefined)
      info_item("原画像データ生成日時", DateTime(value, Exif_tags[I_SubsecTimeOriginal].value));

    // ディジタル データの作成日時
    if((value = Exif_tags[I_DateTimeDigitized].value) != undefined)
      info_item("ディジタル データ作成日時", DateTime(value, Exif_tags[I_SubsecTimeDigitized].value));

    // 露出時間
    if((value = Exif_tags[I_ExposureTime].value) != undefined)
      info_item("露出時間",
                (value.d) ? ((value.n == 1) ? "1/" + String(value.d)
                                            : rational(value, 6, undefined)) + " 秒"
                          : "不明");

    // Fナンバー
    if((value = Exif_tags[I_FNumber].value) != undefined)
      info_item("F値", rational(value, 1, undefined));

    // 露出プログラム
    value = Exif_tags[I_ExposureProgram].value;
    info_item("露出プログラム", ((value > 8) ? "不明" : ExposureProgram_str[value]));

    // スペクトル感度
    if((value = Exif_tags[I_SpectralSensitivity].value) != undefined)
      info_item("スペクトル感度", ascii(value));

    // ISO スピード レート
    if((value = Exif_tags[I_ISOSpeedRatings].value) != undefined) {
      str = "";
      for(i = 0; i < value.length; i++) {
        if(i)
          str += ", ";
        str += String(value[i]);
      }
      info_item("ISO スピード レート", str);
    }

    // 光電変換関数
    if((value = Exif_tags[I_OECF].value) != undefined) {
      if(value.length < 4) {
        str = "不明";
      }
      else {
        n = get_short(value, 0);
        m = get_short(value, 2);
        off = 4;
        str = "<TABLE CELLSPACING=0 CELLPADDING=0 STYLE='white-space:pre'><TR>";
        for(i = n; i; i--) {
          if((value2 = get_asciiz(value, off)) == undefined)
            break;
          str += "<TD STYLE='padding-right:1em'>" + info_text(value2) + "</TD>";
          off += value2.length + 1;
        }
        if(i) {
          str = "不明";
        }
        else {
          if(off + n * m * 8 == value.length) {
            str += "</TR>";
            for(; m; m--) {
              str += "<TR>";
              for(i = n; i; i--) {
                str += "<TD STYLE='padding-right:1em'>"
                         + rational(get_srational(value, off), 3, undefined)
                         + "</TD>";
                off += 8;
              }
              str += "</TR>";
            }
            str += "</TABLE>";
          }
          else {
            str = "不明";
          }
        }
      }
      info_item("光電変換関数", str);
    }

    // シャッター スピード
    if((value = Exif_tags[I_ShutterSpeedValue].value) != undefined)
      info_item("シャッター スピード", rational(value, 3, "APEX"));

    // 絞り値
    if((value = Exif_tags[I_ApertureValue].value) != undefined)
      info_item("絞り値", rational(value, 3, "APEX"));

    // 輝度値
    if((value = Exif_tags[I_BrightnessValue].value) != undefined)
      info_item("輝度値", (value.n == -1/*FFFFFFFF.H*/) ? "不明" : rational(value, 3, "APEX"));

    // 露光補正値
    if((value = Exif_tags[I_ExposureBiasValue].value) != undefined)
      info_item("露出補正値", rational(value, 3, "APEX"));

    // レンズ最小F値
    if((value = Exif_tags[I_MaxApertureValue].value) != undefined)
      info_item("レンズ最小F値", rational(value, 3, "APEX"));

    // 被写体距離
    if((value = Exif_tags[I_SubjectDistance].value) != undefined) {
      switch(value.n) {
      case 0:
        str = "不明";
        break;
      case 4294967295:  // FFFFFFFF.H
        str = "∞";
        break;
      default:
        str = rational(value, 3, "m");
      }
      info_item("被写体距離", str);
    }

    // 測光方式
    info_item("測光方式",
              ((str = MeteringMode_str[Exif_tags[I_MeteringMode].value]) == undefined) ? "不明" : str);

    // 光源
    info_item("光源",
              ((str = LightSource_str[Exif_tags[I_LightSource].value]) == undefined) ? "不明" : str);

    // フラッシュ
    if((value = Exif_tags[I_Flash].value) != undefined) {
      str = ((value & 0x01) ? "ストロボ発光" : "ストロボ発光せず") + "<BR>";
      switch(value & 0x06) {
      case 0x00:
        str += "ストロボのリターン検出機能なし";
        break;
      case 0x04:
        str += "ストロボのリターン検出されず";
        break;
      case 0x06:
        str += "ストロボのリターン検出";
        break;
      default:
        str += "ストロボのリターン状態不明";
      }
      str += "<BR>";
      switch(value & 0x18) {
      case 0x08:
        str += "強制発光モード";
        break;
      case 0x10:
        str += "強制非発光モード";
        break;
      case 0x18:
        str += "自動発光モード";
        break;
      default:
        str += "ストロボ モード不明";
      }
      str += "<BR>";
      str += ((value & 0x20) ? "ストロボ機能なし" : "ストロボ機能あり") + "<BR>";
      str += (value & 0x40) ? "赤目軽減あり" : "赤目軽減なし、または赤目モード不明";
      info_item("フラッシュ", str);
    }

    // レンズ焦点距離
    if((value = Exif_tags[I_FocalLength].value) != undefined)
      info_item("レンズ焦点距離", rational(value, 1, "mm"));

    // 被写体領域
    if((value = Exif_tags[I_SubjectArea].value) != undefined) {
      str = "X: " + String(value[0]) + ", Y: " + String(value[1]);
      switch(value.length) {
      case 3:
        str += ", 直径: " + String(value[2]);
        break;
      case 4:
        str += ", 幅: " + String(value[2]) + ", 高さ: " + String(value[3]);
        break;
      }
      info_item("被写体領域", str);
    }

    // フラッシュ強度
    if((value = Exif_tags[I_FlashEnergy].value) != undefined)
      info_item("フラッシュ強度", rational(value, 1, "BCPS"));

    // 空間周波数応答
    if((value = Exif_tags[I_SpatialFrequencyResponse].value) != undefined) {
      if(value.length < 4) {
        str = "不明";
      }
      else {
        n = get_short(value, 0);
        m = get_short(value, 2);
        off = 4;
        str = "<TABLE CELLSPACING=0 CELLPADDING=0 STYLE='white-space:pre'><TR>";
        for(i = n; i; i--) {
          if((value2 = get_asciiz(value, off)) == undefined)
            break;
          str += "<TD STYLE='padding-right:1em'>" + info_text(value2) + "</TD>";
          off += value2.length + 1;
        }
        if(i) {
          str = "不明";
        }
        else {
          if(off + n * m * 8 == value.length) {
            str += "</TR>";
            for(; m; m--) {
              str += "<TR>";
              for(i = n; i; i--) {
                str += "<TD STYLE='padding-right:1em'>"
                         + rational(get_rational(value, off), 3, undefined)
                         + "</TD>";
                off += 8;
              }
              str += "</TR>";
            }
            str += "</TABLE>";
          }
          else {
            str = "不明";
          }
        }
      }
      info_item("空間周波数応答", str);
    }

    // 焦点面解像度単位
    unit = ResolutionUnit_str[Exif_tags[I_FocalPlaneResolutionUnit].value];
    // 焦点面の幅の解像度
    if((value = Exif_tags[I_FocalPlaneXResolution].value) != undefined)
      info_item("焦点面の幅の解像度",
                (unit == undefined) ? "不明" : rational(value, 1, unit));
    // 焦点面の高さの解像度
    if((value = Exif_tags[I_FocalPlaneYResolution].value) != undefined)
      info_item("焦点面の高さの解像度",
                (unit == undefined) ? "不明" : rational(value, 1, unit));

    // 被写体位置
    if((value = Exif_tags[I_SubjectLocation].value) != undefined)
      info_item("被写体位置", "X: " + String(value[0]) + ", Y: " + String(value[1]));

    // 露出インデックス
    if((value = Exif_tags[I_ExposureIndex].value) != undefined)
      info_item("露出インデックス", rational(value, 1, undefined));

    // センサ方式
    if((value = Exif_tags[I_SensingMethod].value) != undefined)
      info_item("センサ方式", ((str = SensingMethod_str[value]) == undefined) ? "不明" : str);

    // ファイル ソース
    info_item("ファイル ソース",
              ((value = Exif_tags[I_FileSource].value.charCodeAt()) > 3) ? "不明" : FileSource_str[value]);

    // シーン タイプ
    info_item("シーン タイプ",
              (Exif_tags[I_SceneType].value.charCodeAt() == 1) ? "直接撮影された画像" : "不明");

    // CFA パターン
    if((value = Exif_tags[I_CFAPattern].value) != undefined) {
      if(value.length < 4) {
        str = "不明";
      }
      else {
        n = get_short(value, 0);
        m = get_short(value, 2);
        if(4 + n * m == value.length) {
          off = 4;
          str = "<TABLE CELLSPACING=0 CELLPADDING=0 STYLE='white-space:pre'>";
          for(; m; m--) {
            str += "<TR>";
            for(i = n; i; i--) {
              value2 = get_byte(value, off);
              str += "<TD STYLE='padding-right:1em'>" + ((value2 > 6) ? "不明" : CFA_str[value2])
                       + "</TD>";
              off++;
            }
            str += "</TR>";
          }
          str += "</TABLE>";
        }
        else {
          str = "不明";
        }
      }
      info_item("CFA パターン", str);
    }

    // 個別画像処理
    info_item("個別画像処理",
              ((value = Exif_tags[I_CustomRendered].value) > 1) ? "不明" : CustomRendered_str[value]);

    // 露出モード
    if((value = Exif_tags[I_ExposureMode].value) != undefined)
      info_item("露出モード", (value > 2) ? "不明" : ExposureMode_str[value]);

    // ホワイト バランス
    if((value = Exif_tags[I_WhiteBalance].value) != undefined)
      info_item("ホワイト バランス", (value > 1) ? "不明" : WhiteBalance_str[value]);

    // ディジタル ズーム倍率
    if((value = Exif_tags[I_DigitalZoomRatio].value) != undefined)
      info_item("ディジタル ズーム倍率",
                (value.n) ? rational(value, 1, undefined) : "ディジタル ズーム未使用");

    // 35mm 換算レンズ焦点距離
    if((value = Exif_tags[I_FocalLengthIn35mmFilm].value) != undefined)
      info_item("35mm 換算レンズ焦点距離", (value) ? String(value) + " mm" : "不明");

    // 撮影シーン タイプ
    info_item("撮影シーン タイプ",
              ((value = Exif_tags[I_SceneCaptureType].value) > 3) ? "不明" : SceneCaptureType_str[value]);

    // ゲイン制御
    if((value = Exif_tags[I_GainControl].value) != undefined)
      info_item("ゲイン制御", (value > 4) ? "不明" : GainControl_str[value]);

    // 撮影コントラスト
    info_item("撮影コントラスト",
              ((value = Exif_tags[I_Contrast].value) > 2) ? "不明" : Contrast_str[value]);

    // 撮影彩度
    info_item("撮影彩度", ((value = Exif_tags[I_Saturation].value) > 2) ? "不明" : Saturation_str[value]);

    // 撮影シャープネス
    info_item("撮影シャープネス",
              ((value = Exif_tags[I_Sharpness].value) > 2) ? "不明" : Sharpness_str[value]);

    // 撮影条件記述情報
    if((value = Exif_tags[I_DeviceSettingDescription].value) != undefined) {
      if(value.length < 4) {
        str = "";
      }
      else {
        n = get_short(value, 0);
        m = get_short(value, 2);

        off = 4;
        str = "<TABLE CELLSPACING=0 CELLPADDING=0 STYLE='white-space:pre'>";
        for(; m; m--) {
          str += "<TR>";
          for(i = n; i; i--) {
            if(off + 2 > value.length)
              break;
            // JEITA CP-3451A の仕様では Signature を含むことになっているが,Signature のバイト オーダーが
            // TIFF ヘッダのバイト オーダーと違ったら,Signature のバイト オーダーに従うのだろうか?
            var get_short_w;
            switch(get_short_be(value, off)) {
            case 0xfeff:  // big endian
              get_short_w = get_short_be;
              off += 2;
              break;
            case 0xfffe:  // little endian
              get_short_w = get_short_le;
              off += 2;
              break;
            default:  // Signature が無いときは TIFF ヘッダのバイト オーダーに従う
              get_short_w = get_short;
            }
            str2 = "";
            for(; ; ) {
              if(off + 2 > value.length) {
                value2 = true;
                break;
              }
              value2 = get_short_w(value, off);
              off += 2;
              if(!value2)
                break;
              str2 += String.fromCharCode(value2);
            }
            if(value2)
              break;
            str += "<TD STYLE='padding-right:1em'>" + info_text(str2) + "</TD>";
          }
          if(i)
            break;
          str += "</TR>";
        }
        if(m)
          str = "";
        else
          str += "</TABLE>";
      }
      info_item("撮影条件記述情報", str);
    }

    // 被写体距離レンジ
    if((value = Exif_tags[I_SubjectDistanceRange].value) != undefined)
      info_item("被写体距離レンジ", ((str = SubjectDistanceRange_str[value]) == undefined) ? "不明" : str);

    // 画像ユニーク ID
    if((value = Exif_tags[I_ImageUniqueID].value) != undefined)
      info_item("画像ユニーク ID", ascii(value));
  }

  // GPS
  if(TIFF_tags[I_GPS_IFD].value != undefined) {
    info_item("<BR>", "");

    // 緯度,経度
    str = "";
    if((value = GPS_tags[I_GPSLatitudeRef].value) != undefined
         && (value2 = GPS_tags[I_GPSLatitude].value) != undefined) {
      unit = LatitudeRef_str[ascii(value)];
      if(unit != undefined && value2[0].d && value2[1].d && value2[2].d) {
        str = unit + " " + String(value2[0].n / value2[0].d) + "度";
        if(value2[0].d == 1) {
          str += String(value2[1].n / value2[1].d) + "分";
          if(value2[1].d == 1)
            str += String(value2[2].n / value2[2].d) + "秒";
        }
      }
      else {
        str = "緯度不明";
      }
    }
    if((value = GPS_tags[I_GPSLongitudeRef].value) != undefined
         && (value2 = GPS_tags[I_GPSLongitude].value) != undefined) {
      if(str.length)
        str += "<BR>";
      unit = LongitudeRef_str[ascii(value)];
      if(unit != undefined && value2[0].d && value2[1].d && value2[2].d) {
        str += unit + " " + String(value2[0].n / value2[0].d) + "度";
        if(value2[0].d == 1) {
          str += String(value2[1].n / value2[1].d) + "分";
          if(value2[1].d == 1)
            str += String(value2[2].n / value2[2].d) + "秒";
        }
      }
      else {
        str += "経度不明";
      }
    }
    if(str.length)
      info_item("経緯度", str);

    // 高度
    if((value = GPS_tags[I_GPSAltitude].value) != undefined) {
      switch(GPS_tags[I_GPSAltitudeRef].value) {
      case 0:
        str = "";
        break;
      case 1:
        str = "-";
        break;
      default:
        str = undefined;
      }
      info_item("高度", (str == undefined || !value.d) ? "不明" : str + rational(value, 1, "m"));
    }

    // GPS 時間(原子時計の時間)
    if((value = GPS_tags[I_GPSTimeStamp].value) != undefined) {
      if(!value[0].d || !value[1].d || !value[2].d) {
        str = "不明";
      }
      else {
        str = String(value[0].n / value[0].d) + "時";
        if(value[0].d == 1) {
          str += String(value[1].n / value[1].d) + "分";
          if(value[1].d == 1)
            str += String(value[2].n / value[2].d) + "秒";
        }
        str += " UTC";
      }
      info_item("GPS 時間", str);
    }

    // 測位に使った衛星信号
    if((value = GPS_tags[I_GPSSatellites].value) != undefined)
      info_item("測位に用いた衛星信号", ascii(value));

    // GPS 受信機の状態
    if((value = GPS_tags[I_GPSStatus].value) != undefined) {
      str = GPSStatus_str[ascii(value)];
      info_item("GPS 受信機の状態", (str == undefined) ? "不明" : str);
    }

    // GPS の測位方法
    if((value = GPS_tags[I_GPSMeasureMode].value) != undefined) {
      str = GPSMeasureMode_str[ascii(value)];
      info_item("GPS の測位方法", (str == undefined) ? "不明" : str);
    }

    // 測位の信頼性
    if((value = GPS_tags[I_GPSDOP].value) != undefined)
      info_item("測位の信頼性", rational(value, 1, undefined));

    // 速度
    if((value = GPS_tags[I_GPSSpeed].value) != undefined) {
      unit = GPSSpeedRef_str[ascii(GPS_tags[I_GPSSpeedRef].value)];
      info_item("速度", (unit == undefined) ? "不明" : rational(value, 1, unit));
    }

    // 進行方向
    if((value = GPS_tags[I_GPSTrack].value) != undefined) {
      unit = DirectionRef_str[ascii(GPS_tags[I_GPSTrackRef].value)];
      info_item("進行方向",
                (unit == undefined || !value.d)
                  ? "不明" : unit + " " + (value.n / value.d).toFixed(2) + " 度");
    }

    // 撮影した画像の方向
    if((value = GPS_tags[I_GPSImgDirection].value) != undefined) {
      unit = DirectionRef_str[ascii(GPS_tags[I_GPSImgDirectionRef].value)];
      info_item("撮影した画像の方向",
                (unit == undefined || !value.d)
                  ? "不明" : unit + " " + (value.n / value.d).toFixed(2) + " 度");
    }

    // 測位に用いた地図データ
    if((value = GPS_tags[I_GPSMapDatum].value) != undefined)
      info_item("測地系", ascii(value));

    // 目的地の緯度,目的地の経度
    str = "";
    if((value = GPS_tags[I_GPSDestLatitudeRef].value) != undefined
         && (value2 = GPS_tags[I_GPSDestLatitude].value) != undefined) {
      unit = LatitudeRef_str[ascii(value)];
      if(unit != undefined && value2[0].d && value2[1].d && value2[2].d) {
        str = unit + " " + String(value2[0].n / value2[0].d) + "度";
        if(value2[0].d == 1) {
          str += String(value2[1].n / value2[1].d) + "分";
          if(value2[1].d == 1)
            str += String(value2[2].n / value2[2].d) + "秒";
        }
      }
      else {
        str = "緯度不明";
      }
    }
    if((value = GPS_tags[I_GPSDestLongitudeRef].value) != undefined
         && (value2 = GPS_tags[I_GPSDestLongitude].value) != undefined) {
      if(str.length)
        str += "<BR>";
      unit = LongitudeRef_str[ascii(value)];
      if(unit != undefined && value2[0].d && value2[1].d && value2[2].d) {
        str += unit + " " + String(value2[0].n / value2[0].d) + "度";
        if(value2[0].d == 1) {
          str += String(value2[1].n / value2[1].d) + "分";
          if(value2[1].d == 1)
            str += String(value2[2].n / value2[2].d) + "秒";
        }
      }
      else {
        str += "経度不明";
      }
    }
    if(str.length)
      info_item("目的地の経緯度", str);

    // 目的地の方角
    if((value = GPS_tags[I_GPSDestBearing].value) != undefined) {
      unit = DirectionRef_str[ascii(GPS_tags[I_GPSDestBearingRef].value)];
      info_item("目的地の方角",
                (unit == undefined || !value.d)
                  ? "不明" : unit + " " + (value.n / value.d).toFixed(2) + " 度");
    }

    // 目的地までの距離
    if((value = GPS_tags[I_GPSDestDistance].value) != undefined) {
      unit = GPSDestDistanceRef_str[ascii(GPS_tags[I_GPSDestDistanceRef].value)];
      info_item("目的地までの距離", (unit == undefined) ? "不明" : rational(value, 1, unit));
    }

    // 測位方式の名称
    if((value = GPS_tags[I_GPSProcessingMethod].value) != undefined) {
      rtn = conv_text(value, "info_gpm");
      info_item("測位方式", rtn.txt);
      if(rtn.src != undefined) {
        if(fr_uc == undefined)
          elem_cvt_gpm.onload = gpm_loaded;
        gpm_src = rtn.src;
      }
    }

    // 測位地点の名称
    if((value = GPS_tags[I_GPSAreaInformation].value) != undefined) {
      rtn = conv_text(value, "info_gai");
      info_item("測位地点", rtn.txt);
      if(rtn.src != undefined) {
        if(fr_uc == undefined)
          elem_cvt_gai.onload = gai_loaded;
        gai_src = rtn.src;
      }
    }

    // GPS 日付
    if((value = GPS_tags[I_GPSDateStamp].value) != undefined) {
      var dt = value.match(/(\d{4}):(\d{2}):(\d{2})/);
      if(dt == null) {
        str = "不明";
      }
      else {
        if(dt[2].charAt(0) == "0")
          dt[2] = dt[2].substr(1);
        if(dt[3].charAt(0) == "0")
          dt[3] = dt[3].substr(1);
        str = dt[1] + "年" + dt[2] + "月" + dt[3] + "日 UTC";
      }
      info_item("GPS 日付", str);
    }

    // GPS 補正測位
    if((value = GPS_tags[I_GPSDifferential].value) != undefined)
      info_item("GPS 補正測位", (value > 1) ? "不明" : GPSDifferential_str[value]);
  }

  info += "</TABLE>";
  elem_info_win.innerHTML = info;
  if(uc_src != undefined) {
    if(fr_uc == undefined)
      elem_cvt_uc.src = uc_src;
    else
      fr_uc.readAsText(uc_src.blb, uc_src.enc);
  }
  if(gpm_src != undefined) {
    if(fr_uc == undefined)
      elem_cvt_gpm.src = gpm_src;
    else
      fr_gpm.readAsText(gpm_src.blb, gpm_src.enc);
  }
  if(gai_src != undefined) {
    if(fr_uc == undefined)
      elem_cvt_gai.src = gai_src;
    else
      fr_gai.readAsText(gai_src.blb, gai_src.enc);
  }
}

function info_item(nam, val) {
  info += "<TR><TD VALIGN=TOP STYLE='padding:0 4px'>" + nam
            + "</TD><TD VALIGN=TOP STYLE='padding:0 4px'>" + val + "</TD></TR>";
}

function ascii(value) {
  var len = value.indexOf("\0");
  return info_text((len == -1) ? value : value.substr(0, len));
}

function rational(value, digits, unit) {
  if(value.d) {
    var str = (value.n / value.d).toFixed(digits);
    for(var i = str.length - 1; str[i] != "."; i--) {
      if(str[i] != "0") {
        i++;
        break;
      }
    }
    str = str.substr(0, i);
    if(unit != undefined)
      str += " " + unit;
    return str;
  }
  return "不明";
}

function DateTime(value_dt, value_sub) {
  var dt = value_dt.match(/(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})/);
  if(dt == null)
    return "不明";
  if(dt[2].charAt(0) == "0")
    dt[2] = dt[2].substr(1);
  if(dt[3].charAt(0) == "0")
    dt[3] = dt[3].substr(1);
  if(dt[4].charAt(0) == "0")
    dt[4] = dt[4].substr(1);
  if(dt[5].charAt(0) == "0")
    dt[5] = dt[5].substr(1);
  if(dt[6].charAt(0) == "0")
    dt[6] = dt[6].substr(1);
  var str = dt[1] + "年" + dt[2] + "月" + dt[3] + "日 " + dt[4] + "時" + dt[5] + "分" + dt[6];
  if(value_sub != undefined) {
    var sub;
    if((sub = ascii(value_sub)).length)
      str += "." + sub;
  }
  return str + "秒";
}

// コード変換
// コード変換に "data" URL を使うことができなくなったバージョンへの対応で,
// FileReader を使う処理を追加した.
function conv_text(value, id) {
  if(value.length < 8)
    return {txt:""};

  var str;
  var bin;
  var i;
  switch(value.substr(0, 8)) {
  case "ASCII\0\0\0":  // ASCII
    return {txt:info_text(value.substr(8))};

  case "JIS\0\0\0\0\0":  // JIS
    // JEITA CP-3451A の仕様では「JIS X0208-1990」としか書かれていない.
    // JIS X 0201 のラテン文字集合等と組み合わせて使うのか等,詳細は不明.
    // ここでは単純に,ISO-2022-JP エンコーディングであるものとして扱うことにする.
    if(fr_uc == undefined) {
      str = "";
      for(i = 8; i < value.length; i++)
        str += "%" + ("0" + value.charCodeAt(i).toString(16)).substr(-2);
      return {txt:"<SPAN ID='" + id + "'></SPAN>", src:"data:text/plain;charset=ISO-2022-JP," + str};
    }
    bin = new Uint8Array(value.length - 8);
    for(i = 8; i < value.length; i++)
      bin[i - 8] = value.charCodeAt(i);
    // Firefox 15 より前のバージョンでは,Blob のコンストラクタに TypedArray を渡すとエラーになる.
    // ArrayBuffer なら OK なので,ArrayBuffer を取得して渡している.
    return {txt:"<SPAN ID='" + id + "'></SPAN>", src:{blb:new Blob([bin.buffer]), enc:"ISO-2022-JP"}};

  case "UNICODE\0":  // Unicode
    str = "";
    for(i = 8; i + 1 < value.length; i += 2)
      str += String.fromCharCode(get_short(value, i));
    return {txt:info_text(str)};

  case "\0\0\0\0\0\0\0\0":  // Undefined
    // PC に依存.ここではシフト JIS と仮定.
    if(fr_uc == undefined) {
      str = "";
      for(i = 8; i < value.length; i++)
        str += "%" + ("0" + value.charCodeAt(i).toString(16)).substr(-2);
      return {txt:"<SPAN ID='" + id + "'></SPAN>", src:"data:text/plain;charset=Shift_JIS," + str};
    }
    bin = new Uint8Array(value.length - 8);
    for(i = 8; i < value.length; i++)
      bin[i - 8] = value.charCodeAt(i);
    // Firefox 15 より前のバージョンでは,Blob のコンストラクタに TypedArray を渡すとエラーになる.
    // ArrayBuffer なら OK なので,ArrayBuffer を取得して渡している.
    return {txt:"<SPAN ID='" + id + "'></SPAN>", src:{blb:new Blob([bin.buffer]), enc:"Shift_JIS"}};
  }

  return {txt:""};
}

function uc_loaded() {
  document.getElementById("info_uc").innerHTML = info_text(this.contentDocument.body.textContent);
};

function gpm_loaded() {
  document.getElementById("info_gpm").innerHTML = info_text(this.contentDocument.body.textContent);
};

function gai_loaded() {
  document.getElementById("info_gai").innerHTML = info_text(this.contentDocument.body.textContent);
};

function fr_uc_loaded() {
  document.getElementById("info_uc").innerHTML = info_text(fr_uc.result);
};

function fr_gpm_loaded() {
  document.getElementById("info_gpm").innerHTML = info_text(fr_gpm.result);
};

function fr_gai_loaded() {
  document.getElementById("info_gai").innerHTML = info_text(fr_gai.result);
};

function info_text(text) {
  elem_esc.textContent = text.replace(/[\x01-\x1f]/g, " ").replace(/ *$/, "");
  return elem_esc.innerHTML;
}

// BYTE 取得
function get_byte(data, off) {
  return data.charCodeAt(off);
}

// SHORT 取得
var get_short;

// big endian の SHORT 取得
function get_short_be(data, off) {
  return (data.charCodeAt(off) << 8) | data.charCodeAt(off + 1);
}

// little endian の SHORT 取得
function get_short_le(data, off) {
  return (data.charCodeAt(off + 1) << 8) | data.charCodeAt(off);
}

// LONG 取得
var get_long;

// big endian の LONG 取得
function get_long_be(data, off) {
  return data.charCodeAt(off) * 0x1000000
           + ((data.charCodeAt(off + 1) << 16) | (data.charCodeAt(off + 2) << 8) | data.charCodeAt(off + 3));
}

// little endian の LONG 取得
function get_long_le(data, off) {
  return data.charCodeAt(off + 3) * 0x1000000
           + ((data.charCodeAt(off + 2) << 16) | (data.charCodeAt(off + 1) << 8) | data.charCodeAt(off));
}

// SLONG 取得
var get_slong;

// big endian の SLONG 取得
function get_slong_be(data, off) {
  return (data.charCodeAt(off) << 24) | (data.charCodeAt(off + 1) << 16)
           | (data.charCodeAt(off + 2) << 8) | data.charCodeAt(off + 3);
}

// little endian の SLONG 取得
function get_slong_le(data, off) {
  return (data.charCodeAt(off + 3) << 24) | (data.charCodeAt(off + 2) << 16)
           | (data.charCodeAt(off + 1) << 8) | data.charCodeAt(off);
}

// RATIONAL 取得
function get_rational(data, off) {
  return {n:get_long(data, off), d:get_long(data, off + 4)};
}

// SRATIONAL 取得
function get_srational(data, off) {
  return {n:get_slong(data, off), d:get_slong(data, off + 4)};
}

// null 終端 ASCII 文字列取得
function get_asciiz(data, off) {
  var str = "";
  for(; ; ) {
    var c;
    if((c = data.charAt(off)) == "\0")
      return str;
    str += c;
    if(++off == data.length)
      return undefined;
  }
}

//----------------------------------------------------------

  // TIFF field types
  BYTE       =  1;
  ASCII      =  2;
  SHORT      =  3;
  LONG       =  4;
  RATIONAL   =  5;
//SBYTE      =  6;
  UNDEFINED  =  7;
//SSHORT     =  8;
//SLONG      =  9;
  SRATIONAL  = 10;
//FLOAT      = 11;
//DOUBLE     = 12;
  SHORT_LONG = 0x10000;  // SHORT or LONG

  field_size = [0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8];

  // 値取得関数テーブル
  get_value = [
    undefined,
    get_byte,
    undefined,
    undefined,  // get_short(endian 確定後に設定)
    undefined,  // get_long (endian 確定後に設定)
    get_rational,
    undefined,
    undefined,
    undefined,
    undefined,
    get_srational,
    undefined,
    undefined
  ];

  // TIFF Rev.6.0 IFD
  TIFF_tags = [
  //   Tag    Type     Count  デフォルト
  // A. 画像データの構成に関するタグ
    [  256, SHORT_LONG,   1                   ],  // ImageWidth
    [  257, SHORT_LONG,   1                   ],  // ImageLength
    [  258, SHORT,        3, [8, 8, 8]        ],  // BitsPerSample
    [  259, SHORT,        1                   ],  // Compression
    [  262, SHORT,        1                   ],  // PhotometricInterpretation
    [  274, SHORT,        1, 1                ],  // Orientation
    [  277, SHORT,        1, 3                ],  // SamplesPerPixel
    [  284, SHORT,        1, 1                ],  // PlanarConfiguration
    [  530, SHORT,        2                   ],  // YCbCrSubSampling
    [  531, SHORT,        1, 1                ],  // YCbCrPositioning
    [  282, RATIONAL,     1, {n:72, d:1}      ],  // XResolution
    [  283, RATIONAL,     1, {n:72, d:1}      ],  // YResolution
    [  296, SHORT,        1, 2                ],  // ResolutionUnit
  // B. 画像の記録位置に関するタグ
    [  273, SHORT_LONG,   0                   ],  // StripOffsets
    [  278, SHORT_LONG,   1                   ],  // RowsPerStrip
    [  279, SHORT_LONG,   0                   ],  // StripByteCounts
                                                  // JPEGInterchangeFormat
                                                  // JPEGInterchangeFormatLength
  // C. 画像データの特性に関するタグ
    [  301, SHORT,      768                   ],  // TransferFunction  Count: 3 * 256
    [  318, RATIONAL,     2                   ],  // WhitePoint
    [  319, RATIONAL,     6                   ],  // PrimaryChromaticities
    [  529, RATIONAL,     3, [{n:299, d:1000},
                              {n:587, d:1000},
                              {n:114, d:1000}]],  // YCbCrCoefficients
    [  532, RATIONAL,     6                   ],  // ReferenceBlackWhite
  // D. その他のタグ
    [  306, ASCII,       20                   ],  // DateTime
    [  270, ASCII,        0                   ],  // ImageDescription
    [  271, ASCII,        0                   ],  // Make
    [  272, ASCII,        0                   ],  // Model
    [  305, ASCII,        0                   ],  // Software
    [  315, ASCII,        0                   ],  // Artist
    [33432, ASCII,        0                   ],  // Copyright

  // Exif プライベート タグ
    [34665, LONG,         1                   ],  // Exif IFD
    [34853, LONG,         1                   ]   // GPS IFD
  ];

  I_ImageWidth = 0;
  I_ImageLength = 1;
  I_BitsPerSample = 2;
  I_Compression = 3;
  I_PhotometricInterpretation = 4;
  I_Orientation = 5;
  I_SamplesPerPixel = 6;
  I_PlanarConfiguration = 7;
  I_YCbCrSubSampling = 8;
  I_YCbCrPositioning = 9;
  I_XResolution = 10;
  I_YResolution = 11;
  I_ResolutionUnit = 12;
  I_StripOffsets = 13;
  I_RowsPerStrip = 14;
  I_StripByteCounts = 15;
  I_YCbCrCoefficients = 19;
  I_ReferenceBlackWhite = 20;
  I_DateTime = 21;
  I_ImageDescription = 22;
  I_Make = 23;
  I_Model = 24;
  I_Software = 25;
  I_Artist = 26
  I_Copyright = 27;
  I_Exif_IFD = 28;
  I_GPS_IFD = 29;

  // Exif IFD
  Exif_tags = [
  //  Tag     Type    Count デフォルト
  // A. バージョンに関するタグ
    [36864, UNDEFINED,   4, "0221"],  // Exif バージョン
    [40960, UNDEFINED,   4, "0100"],  // 対応フラッシュピックス バージョン
  // B. 画像データの特性に関するタグ
    [40961, SHORT,       1        ],  // 色空間情報
    [42240, RATIONAL,    1        ],  // 再生ガンマ
  // C. 構造に関するタグ
    [37121, UNDEFINED,   4        ],  // 各コンポーネントの意味
    [37122, RATIONAL,    1        ],  // 画像圧縮モード
    [40962, SHORT_LONG,  1        ],  // 実効画像幅
    [40963, SHORT_LONG,  1        ],  // 実効画像高さ
  // D. ユーザ情報に関するタグ
    [37500, UNDEFINED,   0        ],  // メーカ ノート
    [37510, UNDEFINED,   0        ],  // ユーザ コメント
  // E. 関連ファイル情報に関するタグ
    [40964, ASCII,      13        ],  // 関連音声ファイル
  // F. 日時に関するタグ
    [36867, ASCII,      20        ],  // 原画像データの生成日時
    [36868, ASCII,      20        ],  // ディジタル データの作成日時
    [37520, ASCII,       0        ],  // DateTime のサブセック
    [37521, ASCII,       0        ],  // DateTimeOriginal のサブセック
    [37522, ASCII,       0        ],  // DateTimeDigitized のサブセック
  // G. 撮影条件に関するタグ
    [33434, RATIONAL,    1        ],  // 露出時間
    [33437, RATIONAL,    1        ],  // Fナンバー
    [34850, SHORT,       1, 0     ],  // 露出プログラム
    [34852, ASCII,       0        ],  // スペクトル感度
    [34855, SHORT,       0        ],  // ISO スピード レート
    [34856, UNDEFINED,   0        ],  // 光電変換関数
    [37377, SRATIONAL,   1        ],  // シャッター スピード
    [37378, RATIONAL,    1        ],  // 絞り値
    [37379, SRATIONAL,   1        ],  // 輝度値
    [37380, SRATIONAL,   1        ],  // 露光補正値
    [37381, RATIONAL,    1        ],  // レンズ最小F値
    [37382, RATIONAL,    1        ],  // 被写体距離
    [37383, SHORT,       1, 0     ],  // 測光方式
    [37384, SHORT,       1, 0     ],  // 光源
    [37385, SHORT,       1        ],  // フラッシュ
    [37386, RATIONAL,    1        ],  // レンズ焦点距離
    [37396, SHORT,       0        ],  // 被写体領域  Count: 2 〜 4
    [41483, RATIONAL,    1        ],  // フラッシュ強度
    [41484, UNDEFINED,   0        ],  // 空間周波数応答
    [41486, RATIONAL,    1        ],  // 焦点面の幅の解像度
    [41487, RATIONAL,    1        ],  // 焦点面の高さの解像度
    [41488, SHORT,       1, 2     ],  // 焦点面解像度単位
    [41492, SHORT,       2        ],  // 被写体位置
    [41493, RATIONAL,    1        ],  // 露出インデックス
    [41495, SHORT,       1        ],  // センサ方式
    [41728, UNDEFINED,   1, "\x03"],  // ファイル ソース
    [41729, UNDEFINED,   1, "\x01"],  // シーン タイプ
    [41730, UNDEFINED,   0        ],  // CFA パターン
    [41985, SHORT,       1, 0     ],  // 個別画像処理
    [41986, SHORT,       1        ],  // 露出モード
    [41987, SHORT,       1        ],  // ホワイト バランス
    [41988, RATIONAL,    1        ],  // ディジタル ズーム倍率
    [41989, SHORT,       1        ],  // 35mm 換算レンズ焦点距離
    [41990, SHORT,       1, 0     ],  // 撮影シーン タイプ
    [41991, SHORT,       1        ],  // ゲイン制御
    [41992, SHORT,       1, 0     ],  // 撮影コントラスト
    [41993, SHORT,       1, 0     ],  // 撮影彩度
    [41994, SHORT,       1, 0     ],  // 撮影シャープネス
    [41995, UNDEFINED,   0        ],  // 撮影条件記述情報
    [41996, SHORT,       1        ],  // 被写体距離レンジ
  // H. その他のタグ
    [42016, ASCII,      33        ]   // 画像ユニーク ID
  ];

  I_ColorSpace = 2;
  I_Gamma = 3;
  I_MakerNote = 8;
  I_UserComment = 9;
  I_RelatedSoundFile = 10;
  I_DateTimeOriginal = 11;
  I_DateTimeDigitized = 12;
  I_SubsecTime = 13;
  I_SubsecTimeOriginal = 14;
  I_SubsecTimeDigitized = 15;
  I_ExposureTime = 16;
  I_FNumber = 17;
  I_ExposureProgram = 18;
  I_SpectralSensitivity = 19;
  I_ISOSpeedRatings = 20;
  I_OECF = 21;
  I_ShutterSpeedValue = 22;
  I_ApertureValue = 23;
  I_BrightnessValue = 24;
  I_ExposureBiasValue = 25;
  I_MaxApertureValue = 26;
  I_SubjectDistance = 27;
  I_MeteringMode = 28;
  I_LightSource = 29;
  I_Flash = 30;
  I_FocalLength = 31;
  I_SubjectArea = 32;
  I_FlashEnergy = 33;
  I_SpatialFrequencyResponse = 34;
  I_FocalPlaneXResolution = 35;
  I_FocalPlaneYResolution = 36;
  I_FocalPlaneResolutionUnit = 37;
  I_SubjectLocation = 38;
  I_ExposureIndex = 39;
  I_SensingMethod = 40;
  I_FileSource = 41;
  I_SceneType = 42;
  I_CFAPattern = 43;
  I_CustomRendered = 44;
  I_ExposureMode = 45;
  I_WhiteBalance = 46;
  I_DigitalZoomRatio = 47;
  I_FocalLengthIn35mmFilm = 48;
  I_SceneCaptureType = 49;
  I_GainControl = 50;
  I_Contrast = 51;
  I_Saturation = 52;
  I_Sharpness = 53;
  I_DeviceSettingDescription = 54;
  I_SubjectDistanceRange = 55;
  I_ImageUniqueID = 56;

  // GPS IFD
  GPS_tags = [
  // Tag   Type   Count  デフォルト
  // A. GPS に関するタグ
    [ 0, BYTE,       4, [2, 2, 0, 0]],  // GPS タグのバージョン
    [ 1, ASCII,      2              ],  // 北緯(N) or 南緯(S)
    [ 2, RATIONAL,   3              ],  // 緯度(数値)
    [ 3, ASCII,      2              ],  // 東経(E) or 西経(W)
    [ 4, RATIONAL,   3              ],  // 経度(数値)
    [ 5, BYTE,       1, 0           ],  // 高度の基準
    [ 6, RATIONAL,   1              ],  // 高度(数値)
    [ 7, RATIONAL,   3              ],  // GPS 時間(原子時計の時間)
    [ 8, ASCII,      0              ],  // 測位に使った衛星信号
    [ 9, ASCII,      2              ],  // GPS 受信機の状態
    [10, ASCII,      2              ],  // GPS の測位方法
    [11, RATIONAL,   1              ],  // 測位の信頼性
    [12, ASCII,      2, "K"         ],  // 速度の単位
    [13, RATIONAL,   1              ],  // 速度(数値)
    [14, ASCII,      2, "T"         ],  // 進行方向の単位
    [15, RATIONAL,   1              ],  // 進行方向(数値)
    [16, ASCII,      2, "T"         ],  // 撮影した画像の方向の単位
    [17, RATIONAL,   1              ],  // 撮影した画像の方向(数値)
    [18, ASCII,      0              ],  // 測位に用いた地図データ
    [19, ASCII,      2              ],  // 目的地の北緯(N) or 南緯(S)
    [20, RATIONAL,   3              ],  // 目的地の緯度(数値)
    [21, ASCII,      2              ],  // 目的地の東経(E) or 西経(W)
    [22, RATIONAL,   3              ],  // 目的地の経度(数値)
    [23, ASCII,      2, "T"         ],  // 目的地の方角の単位
    [24, RATIONAL,   1              ],  // 目的の方角(数値)
    [25, ASCII,      2, "K"         ],  // 目的地までの距離の単位
    [26, RATIONAL,   1              ],  // 目的地までの距離(数値)
    [27, UNDEFINED,  0              ],  // 測位方式の名称
    [28, UNDEFINED,  0              ],  // 測位地点の名称
    [29, ASCII,     11              ],  // GPS 日付
    [30, SHORT,      1              ]   // GPS 補正測位
  ];

  I_GPSLatitudeRef = 1;
  I_GPSLatitude = 2;
  I_GPSLongitudeRef = 3;
  I_GPSLongitude = 4;
  I_GPSAltitudeRef = 5;
  I_GPSAltitude = 6;
  I_GPSTimeStamp = 7;
  I_GPSSatellites = 8;
  I_GPSStatus = 9;
  I_GPSMeasureMode = 10;
  I_GPSDOP = 11;
  I_GPSSpeedRef = 12;
  I_GPSSpeed = 13;
  I_GPSTrackRef = 14;
  I_GPSTrack = 15;
  I_GPSImgDirectionRef = 16;
  I_GPSImgDirection = 17;
  I_GPSMapDatum = 18;
  I_GPSDestLatitudeRef = 19;
  I_GPSDestLatitude = 20;
  I_GPSDestLongitudeRef = 21;
  I_GPSDestLongitude = 22;
  I_GPSDestBearingRef = 23;
  I_GPSDestBearing = 24;
  I_GPSDestDistanceRef = 25;
  I_GPSDestDistance = 26;
  I_GPSProcessingMethod = 27;
  I_GPSAreaInformation = 28;
  I_GPSDateStamp = 29;
  I_GPSDifferential = 30;

  ResolutionUnit_str = [];
  ResolutionUnit_str[2] = "DPI"; ResolutionUnit_str[3] = "ドット/cm";
  ColorSpace_str = {1:"sRGB", 0xffff:"Uncalibrated"};
  ExposureProgram_str = ["", "マニュアル", "通常プログラム", "絞り優先", "シャッター優先",
                         "creative プログラム", "action プログラム", "人物モード", "風景モード"];
  MeteringMode_str = [undefined, "平均", "中央重点", "スポット", "マルチ スポット", "分割測光", "部分測光"];
  MeteringMode_str[255] = "その他";
  LightSource_str = [];
  LightSource_str[1] = "昼光"; LightSource_str[2] = "蛍光灯"; LightSource_str[3] = "タングステン";
  LightSource_str[4] = "フラッシュ";
  LightSource_str[9] = "晴天"; LightSource_str[10] = "曇天"; LightSource_str[11] = "日陰";
  LightSource_str[12] = "昼光色蛍光灯"; LightSource_str[13] = "昼白色蛍光灯"; LightSource_str[14] = "白色蛍光灯";
  LightSource_str[15] = "温白色蛍光灯";
  LightSource_str[17] = "標準光 A"; LightSource_str[18] = "標準光 B"; LightSource_str[19] = "標準光 C";
  LightSource_str[20] = "D55"; LightSource_str[21] = "D65"; LightSource_str[22] = "D75"; LightSource_str[23] = "D50";
  LightSource_str[24] = "ISO studio tungsten";
  LightSource_str[255] = "その他";
  SensingMethod_str = [undefined, "", "単板カラー センサ", "2 板カラー センサ", "3 板カラー センサ",
                       "色順次カラー センサ", undefined, "3 線リニア センサ", "色順次リニア センサ"];
  FileSource_str = ["その他", "透過型スキャナ", "反射型スキャナ", "DSC"];
  CFA_str = ["RED", "GREEN", "BLUE", "CYAN", "MAGENTA", "YELLOW", "WHITE"];
  CustomRendered_str = ["通常処理", "特殊処理"];
  ExposureMode_str = ["自動", "マニュアル", "オート ブラケット"];
  WhiteBalance_str = ["自動", "マニュアル"];
  SceneCaptureType_str = ["標準", "風景", "人物", "夜景"];
  GainControl_str = ["なし", "弱い増感", "強い増感", "弱い減感", "強い減感"];
  Contrast_str = ["標準", "軟調", "硬調"];
  Saturation_str = ["標準", "低彩度", "高彩度"];
  Sharpness_str = ["標準", "弱い", "強い"];
  SubjectDistanceRange_str = [undefined, "マクロ", "近景", "遠景"];
  LatitudeRef_str = {"N":"北緯", "S":"南緯"};
  LongitudeRef_str = {"E":"東経", "W":"西経"};
  GPSStatus_str = {"A":"測位中", "V":"未測位(中断中)"};
  GPSMeasureMode_str = {"2":"2 次元測位中", "3":"3 次元測位中"};
  GPSSpeedRef_str = {"K":"km/h", "M":"mi/h", "N":"ノット"};
  DirectionRef_str = {"T":"真方位", "M":"磁気方位"};
  GPSDestDistanceRef_str = {"K":"km", "M":"mi", "N":"海里"};
  GPSDifferential_str = ["単独測位", "Differential 補正測位"];

  elem_back = document.getElementById("back");
  elem_file = document.getElementById("file");
  elem_cont = document.getElementById("cont");
  elem_img_win = document.getElementById("img_win");
  elem_img = document.getElementById("img");
  elem_size = document.getElementById("size");
  elem_fit = document.getElementById("fit");
  elem_org = document.getElementById("org");
  elem_shr = document.getElementById("shr");
  elem_cur_fact = document.getElementById("cur_fact");
  elem_fact = document.getElementById("fact");
  elem_info_win = document.getElementById("info_win");
  elem_msg = document.getElementById("msg");
  elem_cvt_uc = document.getElementById("cvt_uc");
  elem_cvt_gpm = document.getElementById("cvt_gpm");
  elem_cvt_gai = document.getElementById("cvt_gai");
  elem_esc = document.createElement("SPAN");

  factor = 0.5;  // 縮小割合 初期値 50%

  resize();

  elem_fact.onkeypress = fact_keypress;

  if(typeof Blob == "function") {
    fr_uc = new FileReader();
    fr_uc.onload = fr_uc_loaded;
    fr_gpm = new FileReader();
    fr_gpm.onload = fr_gpm_loaded;
    fr_gai = new FileReader();
    fr_gai.onload = fr_gai_loaded;
  }
  else {
    fr_uc = undefined;
  }

  document.forms[0].reset();  // ページを再ロードしたときのため

//-->
</SCRIPT>

</BODY>

</HTML>