vendure-ecommerce/vendure

EXIF Orientation lost during on-the-fly image resize

Closed this issue · 0 comments

Describe the bug
Some images appear rotated because EXIF Orientation data is lost during the resize on fly process for source images.

To Reproduce

  1. Steps to reproduce the behaviour:
  2. Download this image: https://api.pinelab.studio/assets/source/35/cr2.jpeg
  3. View it on your computer. It should have the correct orientation.
  4. Upload this image to Vendure.
  5. Open it using the Vendure source URL. The image is fine because the browser uses EXIF data to rotate it during rendering.
  6. Add resize query parameters to the source URL (e.g., /cr2.jpeg?mode=crop&w=640&h=640&q=85).
  7. The image is now rotated.

Source: https://api.pinelab.studio/assets/source/35/cr2.jpeg
Source with resize query: https://api.pinelab.studio/assets/source/35/cr2.jpeg?mode=crop&w=640&h=640&q=85

Expected behavior
The image orientation should remain consistent with the original, even after resizing.

Environment:

  • @vendure/core version: v3.0.3
  • Nodejs: v20.11
  • Database: postgres

Additional context

  • This issue affects only source images. Previews have EXIF data applied during generation and are saved with the correct orientation. The browser handles EXIF rotation during rendering for source images, but resizing removed this data, causing incorrect orientation.

  • A similar problem with preview images was solved by adding sharp.rotate(). You can see the details in #1548.

  • It looks like the issue is happening in this part of the code. The logic there doesn’t seem to account for the EXIF orientation when resizing source images.

    const image = sharp(originalImage);
    try {
    await applyFormat(image, imageFormat, quality);
    } catch (e: any) {
    Logger.error(e.message, loggerCtx, e.stack);
    }
    if (fpx && fpy && targetWidth && targetHeight && mode === 'crop') {
    const metadata = await image.metadata();
    if (metadata.width && metadata.height) {
    const xCenter = fpx * metadata.width;
    const yCenter = fpy * metadata.height;
    const { width, height, region } = resizeToFocalPoint(
    { w: metadata.width, h: metadata.height },
    { w: targetWidth, h: targetHeight },
    { x: xCenter, y: yCenter },
    );
    return image.resize(width, height).extract(region);
    }
    }
    return image.resize(targetWidth, targetHeight, options);
    }