Utiliser exif.js pour inverser l'orientation appliquée par canva.drawImage() (#73872) #154

Merged
bdauvergne merged 1 commits from wip/73872-Reorientation-d-une-image-suite into main 2023-03-17 00:07:33 +01:00
3 changed files with 1165 additions and 40 deletions

View File

@ -87,6 +87,7 @@ class HTTPResponse(quixote.http_response.HTTPResponse):
'jquery.js', 'jquery.js',
'jquery-ui.js', 'jquery-ui.js',
'jquery.iframe-transport.js', 'jquery.iframe-transport.js',
'exif.js',
'jquery.fileupload.js', 'jquery.fileupload.js',
] ]
) )

1064
wcs/qommon/static/js/exif.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -255,49 +255,109 @@ $.WcsFileUpload = {
var original_image_64 = e.target.result; var original_image_64 = e.target.result;
var img = document.createElement("img"); var img = document.createElement("img");
img.onload = function() { img.onload = function() {
var canvas = document.createElement("canvas"); var adapt_image = function(orientation) {
var ctx = canvas.getContext('2d'); /*
ctx.drawImage(img, 0, 0); * 1 = Horizontal (normal)
* 2 = Mirror horizontal
var MAX_WIDTH = 2000; * 3 = Rotate 180
var MAX_HEIGHT = 2000; * 4 = Mirror vertical
var width = img.width; * 5 = Mirror horizontal and rotate 270 CW
var height = img.height; * 6 = Rotate 90 CW
* 7 = Mirror horizontal and rotate 90 CW
if (width > height && width > MAX_WIDTH) { * 8 = Rotate 270 CW */
height *= MAX_WIDTH / width; var canvas = document.createElement("canvas");
width = MAX_WIDTH;
} else if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
if (img.width != width || img.height != height) {
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height); ctx.drawImage(img, 0, 0);
var new_image_64 = canvas.toDataURL('image/jpeg', 0.95);
var blob = null; var MAX_WIDTH = 2000;
if (data.files[0].type == 'image/jpeg') { var MAX_HEIGHT = 2000;
blob = ExifRestorer.restore_as_blob(original_image_64, new_image_64); var width = img.width;
blob.name = data.files[0].name; var height = img.height;
} else {
// adapted from dataURItoBlob, from if (width > height && width > MAX_WIDTH) {
// https://stackoverflow.com/questions/12168909/blob-from-dataurl#12300351 height *= MAX_WIDTH / width;
var byteString = atob(new_image_64.split(',')[1]); width = MAX_WIDTH;
var mimeString = new_image_64.split(',')[0].split(':')[1].split(';')[0] } else if (height > MAX_HEIGHT) {
var ab = new ArrayBuffer(byteString.length); width *= MAX_HEIGHT / height;
var ia = new Uint8Array(ab); height = MAX_HEIGHT;
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
blob = new Blob([ab], {type: mimeString});
blob.name = data.files[0].name + '.jpg';
} }
data.files[0] = blob; if (img.width != width || img.height != height) {
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
switch (orientation) {
case 1:
break;
case 2:
ctx.translate(width, 0);
ctx.scale(-1, 1);
break;
case 3:
ctx.translate(width, height);
ctx.rotate(180 / 180 * Math.PI);
break;
case 4:
ctx.translate(0, height);
ctx.scale(1, -1);
break;
case 5:
canvas.width = height;
canvas.height = width;
ctx.rotate(90 / 180 * Math.PI);
ctx.scale(1, -1);
break;
case 6:
canvas.width = height;
canvas.height = width;
ctx.rotate(-90 / 180 * Math.PI);
ctx.translate(-width, 0);
break;
case 7:
canvas.width = height;
canvas.height = width;
ctx.rotate(270 / 180 * Math.PI);
ctx.translate(-width, height);
ctx.scale(1, -1);
break;
case 8:
canvas.width = height;
canvas.height = width;
ctx.translate(height, 0);
ctx.rotate(-270 / 180 * Math.PI);
break;
}
ctx.drawImage(img, 0, 0, width, height);
var new_image_64 = canvas.toDataURL('image/jpeg', 0.95);
var blob = null;
if (data.files[0].type == 'image/jpeg') {
blob = ExifRestorer.restore_as_blob(original_image_64, new_image_64);
Review

Ça vaudrait peut-être le coup de faire ce test avant de transformer l'image : le standard PNG a intégré les métadonnées Exif depuis 2017, bien que ça ne semble pas supporté par mon navigateur (https://zpl.fi/exif-orientation-in-different-formats/) et que donc je doute très fort qu'exif.js le supporte. Je n'ai pas pu le vérifier : quand je télécharge une image PNG elle est convertie en JPEG quelque part que j'ai pas trouvé avant d'exécuter ce code. Si la conversion PNG => JPG est voulue et systématique en amont, on pourrait carrément dégager le test.

Ça vaudrait peut-être le coup de faire ce test avant de transformer l'image : le standard PNG a intégré les métadonnées Exif depuis 2017, bien que ça ne semble pas supporté par mon navigateur (https://zpl.fi/exif-orientation-in-different-formats/) et que donc je doute très fort qu'exif.js le supporte. Je n'ai pas pu le vérifier : quand je télécharge une image PNG elle est convertie en JPEG quelque part que j'ai pas trouvé avant d'exécuter ce code. Si la conversion PNG => JPG est voulue et systématique en amont, on pourrait carrément dégager le test.
Review

Non t'as raison j'ai complètement changer la logique du truc niveau filtrage des images jpeg, je vais reprendre ça.

Non t'as raison j'ai complètement changer la logique du truc niveau filtrage des images jpeg, je vais reprendre ça.
Review

Voilà j'ai sorti le gros de la fonction dans adapt_image et EXIF.getData n'est plus appelé que sur des images supposément jpeg.

Voilà j'ai sorti le gros de la fonction dans adapt_image et EXIF.getData n'est plus appelé que sur des images supposément jpeg.
blob.name = data.files[0].name;
} else {
// adapted from dataURItoBlob, from
// https://stackoverflow.com/questions/12168909/blob-from-dataurl#12300351
var byteString = atob(new_image_64.split(',')[1]);
var mimeString = new_image_64.split(',')[0].split(':')[1].split(';')[0]
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
blob = new Blob([ab], {type: mimeString});
blob.name = data.files[0].name + '.jpg';
}
data.files[0] = blob;
}
$(base_widget).find('.file-button').trigger('wcs:image-blob', data.files[0]);
return $.WcsFileUpload.upload(base_widget, data);
};
if (data.files[0].type == 'image/jpeg') {
EXIF.getData(img, function () {
var orientation = +EXIF.getTag(this, "Orientation");
adapt_image(orientation);
});
} else {
adapt_image(0);
} }
$(base_widget).find('.file-button').trigger('wcs:image-blob', data.files[0]);
return $.WcsFileUpload.upload(base_widget, data);
} }
img.src = e.target.result; img.src = e.target.result;
} }