Skip to content

Commit

Permalink
fix: calculates correct aspect ratio dimensions on sharp based files (#…
Browse files Browse the repository at this point in the history
…8510)

Fixes #8317 

Sharp based images are auto-oriented based on the EXIF data i.e.
`.rotate()`.

This can be problematic when resizing images as the
`originalAspectRatio` calculation we do in the `imageResizer` can become
incorrect if the files dimensions are rotated from sharp.

For example, uploading an `ios` based image with dimensions of 3024 x
4032 will be auto rotated to 4032 x 3024 because the exif data gives the
image an orientation of `6` - which means it needs to be rotated 90
degrees clockwise.

As a result, the original aspect ratio goes from being `0.75` to
`1.3333` - which is incorrect.

This PR preserves the original aspect ratio to properly resize images
based on the original dimensions - not the sharp based dimensions.
  • Loading branch information
PatrikKozak authored Oct 8, 2024
1 parent f2284f3 commit 9d05b82
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 26 deletions.
39 changes: 13 additions & 26 deletions packages/payload/src/uploads/imageResizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,31 +191,6 @@ const getImageResizeAction = ({
return hasFocalPoint ? 'resizeWithFocalPoint' : 'resize'
}

/**
* Check if the image should be passed directly to sharp without payload adjusting properties.
*
* @param resizeConfig - object containing the requested dimensions and resize options
* @param original - the original image size
* @returns true if the image should passed directly to sharp
*/
const applyPayloadAdjustments = (
{ fit, height, width, withoutEnlargement, withoutReduction }: ImageSize,
original: ProbedImageSize,
) => {
if (fit === 'contain' || fit === 'inside') return false
if (!isNumber(height) && !isNumber(width)) return false

const targetAspectRatio = width / height
const originalAspectRatio = original.width / original.height
if (originalAspectRatio === targetAspectRatio) return false

const skipEnlargement = withoutEnlargement && (original.height < height || original.width < width)
const skipReduction = withoutReduction && (original.height > height || original.width > width)
if (skipEnlargement || skipReduction) return false

return true
}

/**
* Sanitize the resize config. If the resize config has the `withoutReduction`
* property set to true, the `fit` and `position` properties will be set to `contain`
Expand Down Expand Up @@ -302,6 +277,18 @@ export default async function resizeAndTransformImageSizes({
const sharpBase: Sharp | undefined = sharp(file.tempFilePath || file.data, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
const originalImageMeta = await sharpBase.metadata()

let adjustedDimensions = { ...dimensions }

// Images with an exif orientation of 5, 6, 7, or 8 are auto-rotated by sharp
// Need to adjust the dimensions to match the original image
if ([5, 6, 7, 8].includes(originalImageMeta.orientation)) {
adjustedDimensions = {
...dimensions,
height: dimensions.width,
width: dimensions.height,
}
}

const resizeImageMeta = {
height: extractHeightFromImage(originalImageMeta),
width: originalImageMeta.width,
Expand All @@ -324,7 +311,7 @@ export default async function resizeAndTransformImageSizes({
if (resizeAction === 'resizeWithFocalPoint') {
let { height: resizeHeight, width: resizeWidth } = imageResizeConfig

const originalAspectRatio = dimensions.width / dimensions.height
const originalAspectRatio = adjustedDimensions.width / adjustedDimensions.height

// Calculate resizeWidth based on original aspect ratio if it's undefined
if (resizeHeight && !resizeWidth) {
Expand Down
12 changes: 12 additions & 0 deletions test/uploads/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ describe('uploads', () => {
await saveDocAndAssert(page)
})

test('should properly create IOS file upload', async () => {
await page.goto(mediaURL.create)

await page.setInputFiles('input[type="file"]', path.resolve(__dirname, './ios-image.jpeg'))

const filename = page.locator('.file-field__filename')

await expect(filename).toHaveValue('ios-image.jpeg')

await saveDocAndAssert(page)
})

test('should create animated file upload', async () => {
await page.goto(animatedTypeMediaURL.create)

Expand Down
Binary file added test/uploads/ios-image.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9d05b82

Please sign in to comment.