Coverage for website/utils/media/processors.py: 63.64%

32 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1from da_vinci import Image as DaVinciImage 

2from PIL import Image, ImageOps 

3 

4 

5def process_upload(image: DaVinciImage, **kwargs): 

6 """Process an incoming image. 

7 

8 - Rotate and flip the image based on EXIF data. 

9 - Resize the image to the maximum allowed size if it is larger. 

10 - Convert the image to RGB colors, or keep RGBA for PNGs. 

11 

12 Warning: for django-thumbnails to save the image with the right filename, the 

13 format must also be set on the '<size>.FORMAT' key in settings.THUMBNAILS_SIZES. 

14 """ 

15 # Get the PIL image out of the DaVinci image wrapper, 

16 # so we can manipulate it more completely. 

17 pil_image = image.get_pil_image() 

18 

19 # Rotate and flip the image based on EXIF 

20 # data, so the image is stored upright. 

21 pil_image = ImageOps.exif_transpose(pil_image) 

22 

23 # Resize the image to at most the maximum allowed size. 

24 pil_image.thumbnail(kwargs["size"]) 

25 

26 if kwargs["format"] == "jpg": 26 ↛ 30line 26 didn't jump to line 30 because the condition on line 26 was always true

27 # Store in RGB colors. 

28 pil_image = pil_image.convert("RGB") 

29 image.format = "JPEG" 

30 elif kwargs["format"] == "png": 

31 image.format = "PNG" 

32 if pil_image.mode not in ("RGBA", "RGB"): 

33 pil_image = pil_image.convert("RGB") 

34 else: 

35 raise ValueError(f"Unsupported format: {kwargs['format']}") 

36 

37 # Put the PIL image back into the wrapper. 

38 image.set_pil_image(pil_image) 

39 return image 

40 

41 

42def thumbnail(image: DaVinciImage, **kwargs): 

43 """Thumbnail an image to at most the given size. 

44 

45 If `mode="contain"` (the default), the image is resized to fit within the given size. 

46 The original aspect ratio is preserved, so the image may be smaller than specified. 

47 

48 If `mode="cover"`, the image is cropped to the aspect ratio of the given size. 

49 The thumbnails are saved to WebP format with lossy compression. 

50 

51 If `mode="pad"`, the image is resized to fit within the given size. 

52 Padding is added to the edges to make the image the exact size specified. 

53 

54 Warning: for django-thumbnails to save the image with the right filename, 

55 the '<size>.FORMAT' key in settings.THUMBNAILS_SIZES must be set to 'webp'. 

56 

57 """ 

58 pil_image = image.get_pil_image() 

59 

60 size = kwargs["size"] 

61 mode = kwargs.get("mode", "contain") 

62 

63 match mode: 

64 case "contain": 

65 pil_image = ImageOps.contain(pil_image, size, Image.Resampling.LANCZOS) 

66 case "cover": 66 ↛ 68line 66 didn't jump to line 68 because the pattern on line 66 always matched

67 pil_image = ImageOps.fit(pil_image, size, Image.Resampling.LANCZOS) 

68 case "pad": 

69 pil_image = pil_image.convert("RGBA") 

70 pil_image = ImageOps.pad(pil_image, size, Image.Resampling.LANCZOS) 

71 

72 image.set_pil_image(pil_image) 

73 image.format = "WEBP" 

74 image.quality = 90 

75 

76 return image