From c06d9009a2f0caff4557384179078b4e45edfb8d Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Fri, 25 Feb 2022 14:55:42 +0100 Subject: [PATCH] Cover cropping. --- .../migrations/0003_auto_20220225_1431.py | 40 +++++++++++++++++++ .../migrations/0004_populate_use_file.py | 26 ++++++++++++ src/cover/models.py | 40 ++++++++++++++++++- src/cover/templates/cover/image_detail.html | 10 ++--- 4 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 src/cover/migrations/0003_auto_20220225_1431.py create mode 100644 src/cover/migrations/0004_populate_use_file.py diff --git a/src/cover/migrations/0003_auto_20220225_1431.py b/src/cover/migrations/0003_auto_20220225_1431.py new file mode 100644 index 00000000..0fc38a07 --- /dev/null +++ b/src/cover/migrations/0003_auto_20220225_1431.py @@ -0,0 +1,40 @@ +# Generated by Django 3.1.13 on 2022-02-25 14:31 + +import cover.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cover', '0002_auto_20191002_1224'), + ] + + operations = [ + migrations.AddField( + model_name='image', + name='cut_bottom', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='image', + name='cut_left', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='image', + name='cut_right', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='image', + name='cut_top', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='image', + name='use_file', + field=models.ImageField(default='', storage=cover.models.OverwriteStorage(), upload_to='cover/use', verbose_name='file for use'), + preserve_default=False, + ), + ] diff --git a/src/cover/migrations/0004_populate_use_file.py b/src/cover/migrations/0004_populate_use_file.py new file mode 100644 index 00000000..0ae52956 --- /dev/null +++ b/src/cover/migrations/0004_populate_use_file.py @@ -0,0 +1,26 @@ +# Generated by Django 3.1.13 on 2022-02-25 14:31 + +from django.db import migrations + + +def populate_use_file(apps, schema_editor): + Image = apps.get_model('cover', 'Image') + for img in Image.objects.all(): + img.use_file.save( + "%d.jpg" % img.id, + img.file + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('cover', '0003_auto_20220225_1431'), + ] + + operations = [ + migrations.RunPython( + populate_use_file, + migrations.RunPython.noop + ) + ] diff --git a/src/cover/models.py b/src/cover/models.py index c60efe18..60f1bca5 100644 --- a/src/cover/models.py +++ b/src/cover/models.py @@ -1,6 +1,7 @@ # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # +from io import BytesIO from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage from django.db import models @@ -9,6 +10,7 @@ from django.dispatch import receiver from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from django.contrib.sites.models import Site +from PIL import Image as PILImage from cover.utils import URLOpener @@ -27,7 +29,20 @@ class Image(models.Model): source_url = models.URLField(verbose_name=_('source URL'), null=True, blank=True) download_url = models.URLField(unique=True, verbose_name=_('image download URL'), null=True, blank=True) file = models.ImageField( - upload_to='cover/image', storage=OverwriteStorage(), editable=True, verbose_name=_('file')) + upload_to='cover/image', + storage=OverwriteStorage(), + verbose_name=_('file') + ) + use_file = models.ImageField( + upload_to='cover/use', + storage=OverwriteStorage(), + editable=True, + verbose_name=_('file for use') + ) + cut_top = models.IntegerField(default=0, ) + cut_bottom = models.IntegerField(default=0) + cut_left = models.IntegerField(default=0) + cut_right = models.IntegerField(default=0) class Meta: verbose_name = _('cover image') @@ -36,6 +51,29 @@ class Image(models.Model): def __str__(self): return u"%s - %s" % (self.author, self.title) + def save(self, **kwargs): + img = self.file + if self.cut_top or self.cut_bottom or self.cut_left or self.cut_right: + img = PILImage.open(img) + img = img.crop(( + self.cut_left, + self.cut_top, + img.size[0] - self.cut_right, + img.size[1] - self.cut_bottom, + )) + buffer = BytesIO() + img.save( + buffer, + format='JPEG', + ) + img = ContentFile(buffer.getvalue()) + self.use_file.save( + "%d.jpg" % self.pk, + img, + save=False + ) + super().save(**kwargs) + def get_absolute_url(self): return reverse('cover_image', args=[self.id]) diff --git a/src/cover/templates/cover/image_detail.html b/src/cover/templates/cover/image_detail.html index 85fda98f..ca273ed4 100644 --- a/src/cover/templates/cover/image_detail.html +++ b/src/cover/templates/cover/image_detail.html @@ -10,12 +10,8 @@
- +
{{ object.title }} by {{ object.author }}, {% if object.license_url %}{% endif %} @@ -52,7 +48,7 @@ -- 2.20.1