Repository: svthalia/concrexit

Issue:
Partner page has a wonky layout
### Describe the bug

The local partner info blocks are not nicely aligned, the logo's are different sizes too.

### How to reproduce
<!-- Steps to reproduce the behaviour -->
1. Go to the partner page
2. Scroll down
3. See a wonky layout

### Expected behaviour

The partner page has a nice consistent layout

### Screenshots

![CleanShot 2023-12-06 at 14 50 03](https://github.com/svthalia/concrexit/assets/1576660/48c1ce95-1356-4de3-a04d-0471a29e63bf)


### Additional context
<!-- Add any other context about the problem here. -->




Discussion:

Resolving patch:
diff --git a/website/thaliawebsite/settings.py b/website/thaliawebsite/settings.py
--- a/website/thaliawebsite/settings.py
+++ b/website/thaliawebsite/settings.py
@@ -926,7 +926,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (300, 300),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -936,7 +936,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (600, 600),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -946,7 +946,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (1200, 900),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -974,7 +974,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (900, 900),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -984,7 +984,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (500, 108),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -994,7 +994,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (1000, 215),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -1004,7 +1004,7 @@ def show_toolbar(request):
                 {
                     "PATH": "utils.media.processors.thumbnail",
                     "size": (2000, 430),
-                    "cover": True,
+                    "mode": "cover",
                 },
             ],
         },
@@ -1026,6 +1026,16 @@ def show_toolbar(request):
                 },
             ],
         },
+        "fit_medium_pad": {
+            "FORMAT": "webp",
+            "PROCESSORS": [
+                {
+                    "PATH": "utils.media.processors.thumbnail",
+                    "size": (600, 250),
+                    "mode": "pad",
+                },
+            ],
+        },
         "fit_large": {
             "FORMAT": "webp",
             "PROCESSORS": [
diff --git a/website/utils/media/processors.py b/website/utils/media/processors.py
--- a/website/utils/media/processors.py
+++ b/website/utils/media/processors.py
@@ -42,9 +42,15 @@ def process_upload(image: DaVinciImage, **kwargs):
 def thumbnail(image: DaVinciImage, **kwargs):
     """Thumbnail an image to at most the given size.
 
-    If `kwargs["cover"]`, the image is cropped to the aspect ratio of the given size.
+    If `mode="contain"` (the default), the image is resized to fit within the given size.
+    The original aspect ratio is preserved, so the image may be smaller than specified.
+
+    If `mode="cover"`, the image is cropped to the aspect ratio of the given size.
     The thumbnails are saved to WebP format with lossy compression.
 
+    If `mode="pad"`, the image is resized to fit within the given size.
+    Padding is added to the edges to make the image the exact size specified.
+
     Warning: for django-thumbnails to save the image with the right filename,
     the '<size>.FORMAT' key in settings.THUMBNAILS_SIZES must be set to 'webp'.
 
@@ -52,12 +58,16 @@ def thumbnail(image: DaVinciImage, **kwargs):
     pil_image = image.get_pil_image()
 
     size = kwargs["size"]
-    if not kwargs.get("cover", False):
-        ratio = min(a / b for a, b in zip(size, pil_image.size))
-        size = tuple(int(ratio * x) for x in pil_image.size)
-
-    if size[0] != pil_image.size[0] and size[1] != pil_image.size[1]:
-        pil_image = ImageOps.fit(pil_image, size, Image.Resampling.LANCZOS)
+    mode = kwargs.get("mode", "contain")
+
+    match mode:
+        case "contain":
+            pil_image = ImageOps.contain(pil_image, size, Image.Resampling.LANCZOS)
+        case "cover":
+            pil_image = ImageOps.fit(pil_image, size, Image.Resampling.LANCZOS)
+        case "pad":
+            pil_image = pil_image.convert("RGBA")
+            pil_image = ImageOps.pad(pil_image, size, Image.Resampling.LANCZOS)
 
     image.set_pil_image(pil_image)
     image.format = "WEBP"


Test patch: