summaryrefslogtreecommitdiff
path: root/postgresqleu/util/fields.py
diff options
context:
space:
mode:
authorMagnus Hagander2022-06-26 19:31:44 +0000
committerMagnus Hagander2022-06-26 22:04:44 +0000
commite4d50c8012b5403aa1e8a2341ab77ce4182cdaac (patch)
tree2f9bad5f7fdfec66970c9e86bc299d2990a37769 /postgresqleu/util/fields.py
parent897738b31e86ba297285d232aa4b21409fc25e66 (diff)
Support higher resolution speaker photos
This adds a new field to the speaker model supporting 512x512 sized speaker photos. It also changes the speaker form (as well as the backend form) to accept any size image and resize it into the bounding box of 512x512. There is no reason to force the speaker to do that manually, computers are good at such things. The rezied image will be padded as necessary to the full 512x512 with transparent background, which also means the storage format of the new photos will be PNG in order to support that transparency. Whenever the photo is updated, a downscaled copy will automatically be generated that's 128x128 for backwards compatibilty as well as thumbnailing. There is no interface to independently edit this lower resolution photo. Fixes #15
Diffstat (limited to 'postgresqleu/util/fields.py')
-rw-r--r--postgresqleu/util/fields.py41
1 files changed, 33 insertions, 8 deletions
diff --git a/postgresqleu/util/fields.py b/postgresqleu/util/fields.py
index ae41061a..88b381b0 100644
--- a/postgresqleu/util/fields.py
+++ b/postgresqleu/util/fields.py
@@ -2,7 +2,9 @@ from django.db import models
from django.core.exceptions import ValidationError
from .forms import ImageBinaryFormField, PdfBinaryFormField
-from PIL import ImageFile
+import io
+
+from PIL import Image, ImageFile
from postgresqleu.util.magic import magicdb
@@ -19,7 +21,8 @@ class ImageBinaryField(models.Field):
empty_values = [None, b'']
def __init__(self, max_length, *args, **kwargs):
- self.max_resolution = kwargs.pop('max_resolution', None)
+ self.resolution = kwargs.pop('resolution', None)
+ self.auto_scale = kwargs.pop('auto_scale', False)
super(ImageBinaryField, self).__init__(*args, **kwargs)
self.max_length = max_length
@@ -65,12 +68,34 @@ class ImageBinaryField(models.Field):
except Exception as e:
raise ValidationError("Could not parse image: %s" % e)
- if img.format.upper() != 'JPEG':
- raise ValidationError("Only JPEG files are allowed")
-
- if self.max_resolution:
- if img.size[0] > self.max_resolution[0] or img.size[1] > self.max_resolution[1]:
- raise ValidationError("Maximum image size is {}x{}".format(*self.max_resolution))
+ if img.format.upper() not in ('JPEG', 'PNG'):
+ raise ValidationError("Only JPEG or PNG files are allowed")
+
+ if self.resolution:
+ if img.size[0] != self.resolution[0] or img.size[1] != self.resolution[1]:
+ if self.auto_scale:
+ scale = min(
+ float(self.resolution[0]) / float(img.size[0]),
+ float(self.resolution[1]) / float(img.size[1]),
+ )
+ newimg = img.resize(
+ (int(img.size[0] * scale), int(img.size[1] * scale)),
+ Image.BICUBIC,
+ )
+ saver = io.BytesIO()
+ if newimg.size[0] != newimg.size[1]:
+ # This is not a square, so we have to roll it again
+ centeredimg = Image.new('RGBA', self.resolution)
+ centeredimg.paste(newimg, (
+ (self.resolution[0] - newimg.size[0]) // 2,
+ (self.resolution[1] - newimg.size[1]) // 2,
+ ))
+ centeredimg.save(saver, format='PNG')
+ else:
+ newimg.save(saver, format="PNG")
+ value = saver.getvalue()
+ else:
+ raise ValidationError("Image size must be {}x{}".format(*self.resolution))
return value