Skip to content

Commit

Permalink
fix exif orientation bug
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasCARPi committed Apr 13, 2024
1 parent 8c4d334 commit e7f3419
Showing 1 changed file with 107 additions and 158 deletions.
265 changes: 107 additions & 158 deletions src/dropzone.js
Original file line number Diff line number Diff line change
Expand Up @@ -871,15 +871,16 @@ export default class Dropzone extends Emitter {
resizeMimeType === "image/jpg"
) {
// Now add the original EXIF information
resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL);
resizedDataURL = restoreExif(file.dataURL, resizedDataURL);
}
return callback(Dropzone.dataURItoBlob(resizedDataURL));
}
}
},
true
);
}

createThumbnail(file, width, height, resizeMethod, fixOrientation, callback) {
createThumbnail(file, width, height, resizeMethod, fixOrientation, callback, ignoreExif = false) {
let fileReader = new FileReader();

fileReader.onload = () => {
Expand All @@ -899,7 +900,9 @@ export default class Dropzone extends Emitter {
height,
resizeMethod,
fixOrientation,
callback
callback,
undefined,
ignoreExif,
);
};

Expand Down Expand Up @@ -951,7 +954,8 @@ export default class Dropzone extends Emitter {
resizeMethod,
fixOrientation,
callback,
crossOrigin
crossOrigin,
ignoreExif = false,
) {
// Not using `new Image` here because of a bug in latest Chrome versions.
// See https://github.com/enyo/dropzone/pull/226
Expand Down Expand Up @@ -1064,7 +1068,12 @@ export default class Dropzone extends Emitter {
img.onerror = callback;
}

return (img.src = file.dataURL);
var dataURL = file.dataURL;
if (ignoreExif) {
dataURL = removeExif(dataURL);
}

return (img.src = dataURL);
}

// Goes through the queue and processes files if there aren't too many already.
Expand Down Expand Up @@ -1989,165 +1998,105 @@ var drawImageIOSFix = function (ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) {
return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio);
};

// Based on MinifyJpeg
// Inspired by MinifyJpeg
// Source: http://www.perry.cz/files/ExifRestorer.js
// http://elicon.blog57.fc2.com/blog-entry-206.html
class ExifRestore {
static initClass() {
this.KEY_STR =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
}

static encode64(input) {
let output = "";
let chr1 = undefined;
let chr2 = undefined;
let chr3 = "";
let enc1 = undefined;
let enc2 = undefined;
let enc3 = undefined;
let enc4 = "";
let i = 0;
while (true) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output =
output +
this.KEY_STR.charAt(enc1) +
this.KEY_STR.charAt(enc2) +
this.KEY_STR.charAt(enc3) +
this.KEY_STR.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
if (!(i < input.length)) {
break;
}
}
return output;
}
function removeExif(origFileBase64) {

var marker = 'data:image/jpeg;base64,';

if (!origFileBase64.startsWith(marker)) {
return origFileBase64;
}

var origFile = window.atob(origFileBase64.slice(marker.length));

if (!origFile.startsWith("\xFF\xD8\xFF")) {
return origFileBase64;
}

// loop through the JPEG file segments and copy all but Exif segments into the filtered file.
var head = 0;
var filteredFile = "";
while (head < origFile.length) {

if (origFile.slice(head, head+2) == "\xFF\xDA") {
// this is the start of the image data, we don't expect exif data after that.
filteredFile += origFile.slice(head);
break;
} else if (origFile.slice(head, head+2) == "\xFF\xD8") {
// this is the global start marker.
filteredFile += origFile.slice(head, head+2);
head += 2;
} else {
// we have a segment of variable size.
var length = origFile.charCodeAt(head + 2) * 256 + origFile.charCodeAt(head + 3);
var endPoint = head + length + 2;
var segment = origFile.slice(head, endPoint);
if (!segment.startsWith("\xFF\xE1")) {
filteredFile += segment;
}
head = endPoint;
}
}

return marker + window.btoa(filteredFile);
}

function restoreExif(origFileBase64, resizedFileBase64) {

var marker = 'data:image/jpeg;base64,';

static restore(origFileBase64, resizedFileBase64) {
if (!origFileBase64.match("data:image/jpeg;base64,")) {
if (!(origFileBase64.startsWith(marker) && resizedFileBase64.startsWith(marker))) {
return resizedFileBase64;
}
let rawImage = this.decode64(
origFileBase64.replace("data:image/jpeg;base64,", "")
);
let segments = this.slice2Segments(rawImage);
let image = this.exifManipulation(resizedFileBase64, segments);
return `data:image/jpeg;base64,${this.encode64(image)}`;
}
}

static exifManipulation(resizedFileBase64, segments) {
let exifArray = this.getExifArray(segments);
let newImageArray = this.insertExif(resizedFileBase64, exifArray);
let aBuffer = new Uint8Array(newImageArray);
return aBuffer;
}
var origFile = window.atob(origFileBase64.slice(marker.length));

static getExifArray(segments) {
let seg = undefined;
let x = 0;
while (x < segments.length) {
seg = segments[x];
if ((seg[0] === 255) & (seg[1] === 225)) {
return seg;
}
x++;
}
return [];
}

static insertExif(resizedFileBase64, exifArray) {
let imageData = resizedFileBase64.replace("data:image/jpeg;base64,", "");
let buf = this.decode64(imageData);
let separatePoint = buf.indexOf(255, 3);
let mae = buf.slice(0, separatePoint);
let ato = buf.slice(separatePoint);
let array = mae;
array = array.concat(exifArray);
array = array.concat(ato);
return array;
}

static slice2Segments(rawImageArray) {
let head = 0;
let segments = [];
while (true) {
var length;
if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 218)) {
break;
}
if ((rawImageArray[head] === 255) & (rawImageArray[head + 1] === 216)) {
head += 2;
} else {
length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3];
let endPoint = head + length + 2;
let seg = rawImageArray.slice(head, endPoint);
segments.push(seg);
head = endPoint;
}
if (head > rawImageArray.length) {
break;
}
}
return segments;
}

static decode64(input) {
let output = "";
let chr1 = undefined;
let chr2 = undefined;
let chr3 = "";
let enc1 = undefined;
let enc2 = undefined;
let enc3 = undefined;
let enc4 = "";
let i = 0;
let buf = [];
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
let base64test = /[^A-Za-z0-9\+\/\=]/g;
if (base64test.exec(input)) {
console.warn(
"There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."
);
}
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (true) {
enc1 = this.KEY_STR.indexOf(input.charAt(i++));
enc2 = this.KEY_STR.indexOf(input.charAt(i++));
enc3 = this.KEY_STR.indexOf(input.charAt(i++));
enc4 = this.KEY_STR.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
buf.push(chr1);
if (enc3 !== 64) {
buf.push(chr2);
}
if (enc4 !== 64) {
buf.push(chr3);
}
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
if (!(i < input.length)) {
break;
}
}
return buf;
}
if (!origFile.startsWith("\xFF\xD8\xFF")) {
return resizedFileBase64;
}

// Go through the JPEG file segments one by one and collect any Exif segments we find.
var head = 0;
var exifData = "";
while (head < origFile.length) {

if (origFile.slice(head, head+2) == "\xFF\xDA") {
// this is the start of the image data, we don't expect exif data after that.
break;
} else if (origFile.slice(head, head+2) == "\xFF\xD8") {
// this is the global start marker.
head += 2;
} else {
// we have a segment of variable size.
var length = origFile.charCodeAt(head + 2) * 256 + origFile.charCodeAt(head + 3);
var endPoint = head + length + 2;
var segment = origFile.slice(head, endPoint);
if (segment.startsWith("\xFF\xE1")) {
exifData += segment;
}
head = endPoint;
}

}

if (exifData == "") {
return resizedFileBase64;
}

var resizedFile = window.atob(resizedFileBase64.slice(marker.length));

if (!resizedFile.startsWith("\xFF\xD8\xFF")) {
return resizedFileBase64;
}

// The first file segment is always header information so insert the Exif data as second segment.
var splitPoint = 4 + resizedFile.charCodeAt(4) * 256 + resizedFile.charCodeAt(5)
resizedFile = resizedFile.slice(0, splitPoint) + exifData + resizedFile.slice(splitPoint);

return marker + window.btoa(resizedFile);
}
ExifRestore.initClass();

function __guard__(value, transform) {
return typeof value !== "undefined" && value !== null
Expand Down

0 comments on commit e7f3419

Please sign in to comment.