about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2020-04-18 15:37:03 +0200
committerGitHub <noreply@github.com>2020-04-18 15:37:03 +0200
commita1ce9cbb67facf705379355b9c8ec19c521b419c (patch)
tree5236f286b15a227ae1370e93257344e2e97e8830 /app
parente12a5635da7de5a1c3b08b2ce420ee8a56ae4aff (diff)
Fix uploaded image orientation in some browsers (#13493)
Some browsers (at least recent Chrome versions) automatically take
image metadata into account when drawing images into a canvas.
There seem to be no way to prevent that behavior, and the only
way to detect it seems to compare with a known image.

See: https://github.com/w3c/csswg-drafts/issues/4666
Solution adapted from: https://github.com/blueimp/JavaScript-Load-Image/commit/1e4df707821a0afcc11ea0720ee403b8759f3881
Diffstat (limited to 'app')
-rw-r--r--app/javascript/mastodon/utils/resize_image.js45
1 files changed, 44 insertions, 1 deletions
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
index 7196dc96b..710a08f7c 100644
--- a/app/javascript/mastodon/utils/resize_image.js
+++ b/app/javascript/mastodon/utils/resize_image.js
@@ -2,6 +2,45 @@ import EXIF from 'exif-js';
 
 const MAX_IMAGE_PIXELS = 1638400; // 1280x1280px
 
+const _browser_quirks = {};
+
+// Some browsers will automatically draw images respecting their EXIF orientation
+// while others won't, and the safest way to detect that is to examine how it
+// is done on a known image.
+// See https://github.com/w3c/csswg-drafts/issues/4666
+// and https://github.com/blueimp/JavaScript-Load-Image/commit/1e4df707821a0afcc11ea0720ee403b8759f3881
+const dropOrientationIfNeeded = (orientation) => new Promise(resolve => {
+  switch (_browser_quirks['image-orientation-automatic']) {
+  case true:
+    resolve(1);
+    break;
+  case false:
+    resolve(orientation);
+    break;
+  default:
+    // black 2x1 JPEG, with the following meta information set:
+    // - EXIF Orientation: 6 (Rotated 90° CCW)
+    const testImageURL =
+      'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
+      'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
+      'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
+      'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
+      'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
+      'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
+    const img = new Image();
+    img.onload = () => {
+      const automatic = (img.width === 1 && img.height === 2);
+      _browser_quirks['image-orientation-automatic'] = automatic;
+      resolve(automatic ? 1 : orientation);
+    };
+    img.onerror = () => {
+      _browser_quirks['image-orientation-automatic'] = false;
+      resolve(orientation);
+    };
+    img.src = testImageURL;
+  }
+});
+
 const getImageUrl = inputFile => new Promise((resolve, reject) => {
   if (window.URL && URL.createObjectURL) {
     try {
@@ -38,7 +77,11 @@ const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
 
   EXIF.getData(img, () => {
     const orientation = EXIF.getTag(img, 'Orientation');
-    resolve(orientation);
+    if (orientation !== 1) {
+      dropOrientationIfNeeded(orientation).then(resolve).catch(() => resolve(orientation));
+    } else {
+      resolve(orientation);
+    }
   });
 });