Skip to content
Merged
27 changes: 27 additions & 0 deletions packages/payload/src/uploads/cropImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,33 @@ export async function cropImage({
sharpOptions.animated = true
}

const { height: originalHeight, width: originalWidth } = dimensions
const newWidth = Number(widthInPixels)
const newHeight = Number(heightInPixels)

const dimensionsChanged = originalWidth !== newWidth || originalHeight !== newHeight

if (!dimensionsChanged) {
let adjustedHeight = originalHeight

if (fileIsAnimatedType) {
const animatedMetadata = await sharp(
file.tempFilePath || file.data,
sharpOptions,
).metadata()
adjustedHeight = animatedMetadata.pages ? animatedMetadata.height : originalHeight
}

return {
data: file.data,
info: {
height: adjustedHeight,
size: file.size,
width: originalWidth,
},
}
}

const formattedCropData = {
height: Number(heightInPixels),
left: percentToPixel(x, dimensions.width),
Expand Down
2 changes: 1 addition & 1 deletion packages/payload/src/uploads/generateFileData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export const generateFileData = async <T>({
})

// Apply resize after cropping to ensure it conforms to resizeOptions
if (resizeOptions) {
if (resizeOptions && !resizeOptions.withoutEnlargement) {
const resizedAfterCrop = await sharp(croppedImage)
.resize({
fit: resizeOptions?.fit || 'cover',
Expand Down
14 changes: 14 additions & 0 deletions test/uploads/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
relationSlug,
unstoredMediaSlug,
versionSlug,
withoutEnlargeSlug,
} from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
Expand Down Expand Up @@ -490,6 +491,19 @@ export default buildConfigWithDefaults({
staticDir: path.resolve(dirname, './media/enlarge'),
},
},
{
slug: withoutEnlargeSlug,
fields: [],
upload: {
resizeOptions: {
width: 1000,
height: undefined,
fit: 'inside',
withoutEnlargement: true,
},
staticDir: path.resolve(dirname, './media/without-enlarge'),
},
},
{
slug: reduceSlug,
fields: [],
Expand Down
34 changes: 33 additions & 1 deletion test/uploads/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
relationSlug,
withMetadataSlug,
withOnlyJPEGMetadataSlug,
withoutEnlargeSlug,
withoutMetadataSlug,
} from './shared.js'
import { startMockCorsServer } from './startMockCorsServer.js'
Expand Down Expand Up @@ -69,6 +70,7 @@ let uploadsTwo: AdminUrlUtil
let customUploadFieldURL: AdminUrlUtil
let hideFileInputOnCreateURL: AdminUrlUtil
let bestFitURL: AdminUrlUtil
let withoutEnlargementResizeOptionsURL: AdminUrlUtil
let consoleErrorsFromPage: string[] = []
let collectErrorsFromPage: () => boolean
let stopCollectingErrorsFromPage: () => boolean
Expand Down Expand Up @@ -104,6 +106,7 @@ describe('Uploads', () => {
customUploadFieldURL = new AdminUrlUtil(serverURL, customUploadFieldSlug)
hideFileInputOnCreateURL = new AdminUrlUtil(serverURL, hideFileInputOnCreateSlug)
bestFitURL = new AdminUrlUtil(serverURL, 'best-fit')
withoutEnlargementResizeOptionsURL = new AdminUrlUtil(serverURL, withoutEnlargeSlug)

const context = await browser.newContext()
page = await context.newPage()
Expand Down Expand Up @@ -1257,7 +1260,7 @@ describe('Uploads', () => {
})

// without focal point update this generated size was equal to 1736
expect(redDoc.sizes.focalTest.filesize).toEqual(1598)
expect(redDoc.sizes.focalTest.filesize).toEqual(1586)
})

test('should resize image after crop if resizeOptions defined', async () => {
Expand Down Expand Up @@ -1355,6 +1358,35 @@ describe('Uploads', () => {
await expect(page.locator('.file-field .file-details__remove')).toBeHidden()
})

test('should skip applying resizeOptions after updating an image if resizeOptions.withoutEnlargement is true and the original image size is smaller than the dimensions defined in resizeOptions', async () => {
await page.goto(withoutEnlargementResizeOptionsURL.create)

const fileChooserPromise = page.waitForEvent('filechooser')
await page.getByText('Select a file').click()
const fileChooser = await fileChooserPromise
await wait(1000)
await fileChooser.setFiles(path.join(dirname, 'test-image.jpg'))

await page.waitForSelector('button#action-save')
await page.locator('button#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await wait(1000)

await page.locator('.file-field__edit').click()

// no need to make any changes to the image if resizeOptions.withoutEnlargement is actually being respected now
await page.locator('button:has-text("Apply Changes")').click()
await page.waitForSelector('button#action-save')
await page.locator('button#action-save').click()
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await wait(1000)

const resizeOptionMedia = page.locator('.file-meta .file-meta__size-type')

// expect the image to be the original size since the original image is smaller than the dimensions defined in resizeOptions
await expect(resizeOptionMedia).toContainText('800x800')
})

describe('imageSizes best fit', () => {
test('should select adminThumbnail if one exists', async () => {
await page.goto(bestFitURL.create)
Expand Down
1 change: 1 addition & 0 deletions test/uploads/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const mediaSlug = 'media'
export const relationSlug = 'relation'
export const audioSlug = 'audio'
export const enlargeSlug = 'enlarge'
export const withoutEnlargeSlug = 'without-enlarge'
export const focalNoSizesSlug = 'focal-no-sizes'
export const focalOnlySlug = 'focal-only'
export const reduceSlug = 'reduce'
Expand Down
Loading