diff --git a/src/collections/config/schema.ts b/src/collections/config/schema.ts index d72e140b83..de03bba879 100644 --- a/src/collections/config/schema.ts +++ b/src/collections/config/schema.ts @@ -184,6 +184,14 @@ const collectionSchema = joi.object().keys({ format: joi.string(), options: joi.object(), }), + trimOptions: joi.alternatives().try( + joi.object().keys({ + format: joi.string(), + options: joi.object(), + }), + joi.string(), + joi.number(), + ), }), joi.boolean(), ), diff --git a/src/uploads/generateFileData.ts b/src/uploads/generateFileData.ts index 70376dd4bf..5a7a2eaf9b 100644 --- a/src/uploads/generateFileData.ts +++ b/src/uploads/generateFileData.ts @@ -56,7 +56,7 @@ export const generateFileData = async ({ }; } - const { staticDir, imageSizes, disableLocalStorage, resizeOptions, formatOptions } = collectionConfig.upload; + const { staticDir, imageSizes, disableLocalStorage, resizeOptions, formatOptions, trimOptions } = collectionConfig.upload; let staticPath = staticDir; if (staticDir.indexOf('/') !== 0) { @@ -85,7 +85,7 @@ export const generateFileData = async ({ if (fileIsAnimated) sharpOptions.animated = true; - if (fileSupportsResize && (resizeOptions || formatOptions)) { + if (fileSupportsResize && (resizeOptions || formatOptions || trimOptions)) { if (file.tempFilePath) { sharpFile = sharp(file.tempFilePath, sharpOptions); } else { @@ -99,6 +99,9 @@ export const generateFileData = async ({ if (formatOptions) { sharpFile = sharpFile.toFormat(formatOptions.format, formatOptions.options); } + if (trimOptions) { + sharpFile = sharpFile.trim(trimOptions); + } } if (isImage(file.mimetype)) { diff --git a/src/uploads/imageResizer.ts b/src/uploads/imageResizer.ts index d7701bb1b6..4e9b667faf 100644 --- a/src/uploads/imageResizer.ts +++ b/src/uploads/imageResizer.ts @@ -77,6 +77,10 @@ export default async function resizeAndSave({ resized = resized.toFormat(desiredSize.formatOptions.format, desiredSize.formatOptions.options); } + if (desiredSize.trimOptions) { + resized = resized.trim(desiredSize.trimOptions); + } + const bufferObject = await resized.toBuffer({ resolveWithObject: true, }); diff --git a/src/uploads/types.ts b/src/uploads/types.ts index c5f1480b85..a118ddfb6c 100644 --- a/src/uploads/types.ts +++ b/src/uploads/types.ts @@ -41,9 +41,16 @@ export type ImageUploadFormatOptions = { options?: Parameters[1] } +/** + * Params sent to the sharp trim() function + * @link https://sharp.pixelplumbing.com/api-resize#trim + */ +export type ImageUploadTrimOptions = Parameters[0] + export type ImageSize = ResizeOptions & { name: string formatOptions?: ImageUploadFormatOptions + trimOptions?: ImageUploadTrimOptions /** * @deprecated prefer position */ @@ -64,6 +71,7 @@ export type IncomingUploadType = { resizeOptions?: ResizeOptions /** Options for original upload file only. For sizes, set each formatOptions individually. */ formatOptions?: ImageUploadFormatOptions + trimOptions?: ImageUploadTrimOptions } export type Upload = { @@ -77,6 +85,7 @@ export type Upload = { handlers?: any[] resizeOptions?: ResizeOptions; formatOptions?: ImageUploadFormatOptions + trimOptions?: ImageUploadTrimOptions } export type File = { diff --git a/test/uploads/config.ts b/test/uploads/config.ts index b5867c0203..c9c1ce4277 100644 --- a/test/uploads/config.ts +++ b/test/uploads/config.ts @@ -135,6 +135,39 @@ export default buildConfig({ }, fields: [], }, + { + slug: 'media-trim', + upload: { + staticURL: '/media-trim', + staticDir: './media-trim', + mimeTypes: ['image/png', 'image/jpg', 'image/jpeg'], + trimOptions: 0, + imageSizes: [ + { + name: 'trimNumber', + width: 1024, + height: undefined, + trimOptions: 0, + }, + { + name: 'trimString', + width: 1024, + height: undefined, + trimOptions: 0, + }, + { + name: 'trimOptions', + width: 1024, + height: undefined, + trimOptions: { + background: '#000000', + threshold: 50, + }, + }, + ], + }, + fields: [], + }, { slug: 'unstored-media', upload: {