Cover cropping.
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 25 Feb 2022 13:55:42 +0000 (14:55 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 25 Feb 2022 13:55:42 +0000 (14:55 +0100)
src/cover/migrations/0003_auto_20220225_1431.py [new file with mode: 0644]
src/cover/migrations/0004_populate_use_file.py [new file with mode: 0644]
src/cover/models.py
src/cover/templates/cover/image_detail.html

diff --git a/src/cover/migrations/0003_auto_20220225_1431.py b/src/cover/migrations/0003_auto_20220225_1431.py
new file mode 100644 (file)
index 0000000..0fc38a0
--- /dev/null
@@ -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 (file)
index 0000000..0ae5295
--- /dev/null
@@ -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
+        )
+    ]
index c60efe1..60f1bca 100644 (file)
@@ -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])
 
index 85fda98..ca273ed 100644 (file)
 
 <div style="float: right; margin-bottom:1em;">
 
-<a href="{{ object.file.url }}"><img
-        src="{% thumbnail object.file "565x833" as thumb %}
-                {{ thumb.url }}
-             {% empty %}
-                {{ object.file.url }}
-             {% endthumbnail %}" />
+<a href="{{ object.use_file.url }}"><img style="max-width: 565px; max-height: 833px"
+        src="{{ object.use_file.url }}?{{ object.cut_top }}.{{ object.cut_bottom }}.{{ object.cut_left }}.{{ object.cut_right }}" />
     </a>
 <br/><a href="{{ object.source_url }}">{{ object.title }}</a> by {{ object.author }},
     {% if object.license_url %}<a href="{{ object.license_url }}">{% endif %}
@@ -52,7 +48,7 @@
 
 
 <textarea style="width:100%" rows="5">
-&lt;dc:relation.coverImage.url xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.file.url|build_absolute_uri:request }}&lt;/dc:relation.coverImage.url>
+&lt;dc:relation.coverImage.url xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.use_file.url|build_absolute_uri:request }}&lt;/dc:relation.coverImage.url>
 &lt;dc:relation.coverImage.attribution xmlns:dc="http://purl.org/dc/elements/1.1/">{% if object.title %}{{ object.title }}, {% endif %}{% if object.author %}{{ object.author }}, {% endif %}{{ object.license_name }}&lt;/dc:relation.coverImage.attribution>
 &lt;dc:relation.coverImage.source xmlns:dc="http://purl.org/dc/elements/1.1/">{{ object.get_full_url }}&lt;/dc:relation.coverImage.source>
 </textarea>