Browse Source

feat(asset-server-plugin): Enable preview image format configuration

Relates to #1650
Michael Bromley 3 years ago
parent
commit
f7c0800201
1 changed files with 76 additions and 13 deletions
  1. 76 13
      packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts

+ 76 - 13
packages/asset-server-plugin/src/sharp-asset-preview-strategy.ts

@@ -29,6 +29,34 @@ interface SharpAssetPreviewConfig {
      * @default 1600
      */
     maxWidth?: number;
+    /**
+     * @description
+     * Set Sharp's options for encoding jpeg files: https://sharp.pixelplumbing.com/api-output#jpeg
+     *
+     * @since 1.7.0
+     */
+    jpegOptions?: sharp.JpegOptions;
+    /**
+     * @description
+     * Set Sharp's options for encoding png files: https://sharp.pixelplumbing.com/api-output#png
+     *
+     * @since 1.7.0
+     */
+    pngOptions?: sharp.PngOptions;
+    /**
+     * @description
+     * Set Sharp's options for encoding webp files: https://sharp.pixelplumbing.com/api-output#webp
+     *
+     * @since 1.7.0
+     */
+    webpOptions?: sharp.WebpOptions;
+    /**
+     * @description
+     * Set Sharp's options for encoding gif files: https://sharp.pixelplumbing.com/api-output#gif
+     *
+     * @since 1.7.0
+     */
+    gifOptions?: sharp.GifOptions;
 }
 
 /**
@@ -37,39 +65,74 @@ interface SharpAssetPreviewConfig {
  * preview images of uploaded binary files. For non-image binaries, a generic "file" icon with the mime type
  * overlay will be generated.
  *
+ * By default, this strategy will produce previews up to maximum dimensions of 1600 x 1600 pixels. The created
+ * preview images will match the input format - so a source file in jpeg format will output a jpeg preview,
+ * a webp source file will output a webp preview, and so on.
+ *
+ * The settings for the outputs will default to Sharp's defaults (https://sharp.pixelplumbing.com/api-output).
+ * However, it is possible to pass your own configurations to control the output of each format:
+ *
+ * ```TypeScript
+ * AssetServerPlugin.init({
+ *   previewStrategy: new SharpAssetPreviewStrategy({
+ *     jpegOptions: { quality: 95 },
+ *     webpOptions: { quality: 95 },
+ *   }),
+ * }),
+ * ```
+ *
  * @docsCategory AssetServerPlugin
  * @docsPage SharpAssetPreviewStrategy
  * @docsWeight 0
  */
 export class SharpAssetPreviewStrategy implements AssetPreviewStrategy {
-    constructor(private config: SharpAssetPreviewConfig) {}
     private readonly defaultConfig: Required<SharpAssetPreviewConfig> = {
         maxHeight: 1600,
         maxWidth: 1600,
+        jpegOptions: {},
+        pngOptions: {},
+        webpOptions: {},
+        gifOptions: {},
     };
+    private readonly config: Required<SharpAssetPreviewConfig>;
 
-    async generatePreviewImage(ctx: RequestContext, mimeType: string, data: Buffer): Promise<Buffer> {
-        const assetType = getAssetType(mimeType);
-        const config = {
+    constructor(config?: SharpAssetPreviewConfig) {
+        this.config = {
             ...this.defaultConfig,
-            ...this.config,
+            ...(config ?? {}),
         };
-        const { maxWidth, maxHeight } = config;
+    }
+
+    async generatePreviewImage(ctx: RequestContext, mimeType: string, data: Buffer): Promise<Buffer> {
+        const assetType = getAssetType(mimeType);
+
+        const { maxWidth, maxHeight } = this.config;
 
         if (assetType === AssetType.IMAGE) {
             try {
-                const image = sharp(data);
+                const image = sharp(data).rotate();
                 const metadata = await image.metadata();
                 const width = metadata.width || 0;
                 const height = metadata.height || 0;
                 if (maxWidth < width || maxHeight < height) {
-                    return image.rotate().resize(maxWidth, maxHeight, { fit: 'inside' }).toBuffer();
+                    image.resize(maxWidth, maxHeight, { fit: 'inside' });
+                }
+                if (mimeType === 'image/svg+xml') {
+                    // Convert the SVG to a raster for the preview
+                    return image.toBuffer();
                 } else {
-                    if (mimeType === 'image/svg+xml') {
-                        // Convert the SVG to a raster for the preview
-                        return image.toBuffer();
-                    } else {
-                        return image.rotate().toBuffer();
+                    switch (metadata.format) {
+                        case 'jpeg':
+                        case 'jpg':
+                            return image.jpeg(this.config.jpegOptions).toBuffer();
+                        case 'png':
+                            return image.png(this.config.pngOptions).toBuffer();
+                        case 'webp':
+                            return image.webp(this.config.webpOptions).toBuffer();
+                        case 'gif':
+                            return image.gif(this.config.jpegOptions).toBuffer();
+                        default:
+                            return image.toBuffer();
                     }
                 }
             } catch (err: any) {