tmegos blog

I love school idols / Web developer

Chrome 81、Safari 13.1からExifのOrientationが考慮されるようになりましたね

みなさんの環境では、以下の画像はどのように見えますか?
タイトルの通りですが、Chrome 81、Safari 13.1からjpegExif、Orientationの値を考慮して画像が表示されるようになったようです

See the Pen exif orientation by megos (@megos) on CodePen.

各ブラウザの動き

f:id:tmegos:20200409201641p:plain
Firefox 74.0.1

f:id:tmegos:20200409201531p:plain
Safari 13.1

f:id:tmegos:20200409201945p:plain
Chromium 75

f:id:tmegos:20200409202116p:plain
Chrome 81

ブラウザに関わらず画像の向きを正しく表示する

blueimp-load-imageの2.29.0以上を使うことでExifのOrientationを考慮して画像を読み込むことができます
2.29.0未満の場合、
画像読み込み → blueimp-load-image(回転1回目) → htmlに表示(回転2回目)と二重に回転した画像が表示されます…

issue報告からすぐ対応してくださった作者様に感謝…!

github.com

以下は駄文です。私がいろいろとハマったことを書いていきます

2020/03/31

携わっているWebアプリで画像が回転するという報告が上がる(iOS13系)

2020/04/01

自分のiPhone(iOS13.4)で試す。再現しない(回転しない)

MacChrome(80)で試す。再現した(回転する)

iPhoneシミュレータ12.4、13.4でそれぞれ試す
12.4 → 回転する
13.4 → 回転しない

ここまでで、jpegExif orientationが影響していることがわかる
orientationを考慮して画像を読み込むことができるblueimp-load-imageを入れる

すると、iOS13.4で画像が二重に回転することがわかる

2020/04/02

いろいろ試してみる

全て失敗

Exifの情報が残っているのでは?という推論から、
orientationの値を取得 → orientationを0でクリア → 先程取得した値をblueimp-load-imageに投げる
という方法を考える

Exifの情報を読み込むライブラリはあっても、書き込むものは無さそうだった
今回はorientationだけでいいので、Exifの仕様を読んで理解するという謎の時間が発生

qiita.com http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf dsas.blog.klab.org hp.vector.co.jp

Qiitaの記事を元にバイナリを書き換えるコードを作成

const getAndRemoveOrientation = buffer => {
  const dataView = new DataView(buffer)
  let app1Start = 2
  if (dataView.getUint16(app1Start) === 0xffe0) {
    // Have APP0
    app1Start += dataView.getUint16(4) + 2
  }
  if (dataView.getUint16(app1Start) !== 0xffe1) {
    // Noting APP1
    return { orientation: 0, buffer }
  }
  const littleEndian = dataView.getUint16(app1Start + 10) === 0x4949
  const fieldSizeStart = 18 + app1Start
  const fieldEntrySize = 12
  const ORIENTATION = 0x0112
  const valueOffset = 8
  const fieldSize = dataView.getUint16(fieldSizeStart, littleEndian)
  for (let i = 0; i < fieldSize; i++) {
    const start = fieldSizeStart + 2 + i * fieldEntrySize
    const tag = dataView.getUint16(start, littleEndian)
    if (tag === ORIENTATION) {
      const orientation = dataView.getUint16(start + valueOffset, littleEndian)
      // NOTE: Clear orientation tag because it rotation twice in Safari 13.1
      dataView.setUint16(start + valueOffset, 0, littleEndian)
      return { orientation, buffer: dataView.buffer }
    }
  }
  return { orientation: 0, buffer }
}

バイナリを書き換えることで二重回転することなく表示することができた!
(´-`).。oO(本当にこんな対応でいいのか?)

2020/04/03

Safariの13.1のみで発生しているかも?と思ってWebKit Bugzillaへ。ビンゴなissueを発見

bugs.webkit.org

ということはバイナリを書き換えるような大掛かりなことをしなくても if (safari version => 13.1)みたいな分岐だけ入れたほうがいいのか悩む…

2020/04/08

blueimp-load-imageにissueが上がっているのを見かける
WebKit Bugzillaを見ていたので、ライブラリのバグじゃないしなぁと思ってissue作るのをためらっていたんですよね

これをみて、よさそうなワークアラウンドと、Chrome 81でも同じ動きをするということがわかる
ここまで調べて方針が決まったので、残りは明日やろうと一旦離れる

{ orientation: getComputedStyle(document.body).imageOrientation !== 'from-image' }

github.com

2020/04/09

続きをしようと思って再びissueを見たら二重回転しないようにライブラリが更新されていた
yarnしてあっけなく終了。お疲れさまでした。時間かかりすぎた。