So, long story short: jimp correctly reads images rotated via exif orientation property and rearranges the pixels as if the exif/orientation property didn't exist, but then also stores the old exif property instead of resetting it to 1 as it should for it to be displayed properly on every device.
The simplest solution I was able to implement was using exif-auto-rotate to rotate the image pixels and reset the exif property on the frontend before uploading the (base64 encoded) image to the backend:
import Rotator from 'exif-auto-rotate';
// ...
const [file] = e.target.files;
const image = await Rotator.createRotatedImageAsync(file, "base64")
.catch(err => {
if (err === "Image is NOT have a exif code" || err === "Image is NOT JPEG") {
// just return base64 encoded image if image is not jpeg or contains no exif orientation property
return toBase64(file)
}
// reject if other error
return Promise.reject(err)
});
If you need to do this on the backend then you are probably better off using jpeg-autorotate with buffers as suggested here:
const fileIn = fs.readFileSync('input.jpg')
const jo = require('jpeg-autorotate')
const {buffer} = await jo.rotate(fileIn, {quality: 30})
const image = await Jimp.read(buffer)
More info on browser-based exif orientation issues:
EXIF Orientation Handling Is a Ghetto