71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
import os
|
|
from io import BytesIO
|
|
|
|
from django.conf import settings
|
|
from django.utils.functional import cached_property
|
|
from PIL import Image
|
|
|
|
from ckeditor_uploader import utils
|
|
|
|
|
|
THUMBNAIL_SIZE = getattr(settings, "CKEDITOR_THUMBNAIL_SIZE", (75, 75))
|
|
|
|
|
|
class PillowBackend:
|
|
def __init__(self, storage_engine, file_object):
|
|
self.file_object = file_object
|
|
self.storage_engine = storage_engine
|
|
|
|
@cached_property
|
|
def is_image(self):
|
|
try:
|
|
Image.open(
|
|
BytesIO(self.file_object.read())
|
|
).verify() # verify closes the file
|
|
return True
|
|
except OSError:
|
|
return False
|
|
finally:
|
|
self.file_object.seek(0)
|
|
|
|
def _compress_image(self, image):
|
|
quality = getattr(settings, "CKEDITOR_IMAGE_QUALITY", 75)
|
|
image = image.resize(image.size, Image.ANTIALIAS).convert("RGB")
|
|
image_tmp = BytesIO()
|
|
image.save(image_tmp, format="JPEG", quality=quality, optimize=True)
|
|
return image_tmp
|
|
|
|
def save_as(self, filepath):
|
|
if not self.is_image:
|
|
saved_path = self.storage_engine.save(filepath, self.file_object)
|
|
return saved_path
|
|
|
|
image = Image.open(self.file_object)
|
|
|
|
should_compress = getattr(settings, "CKEDITOR_FORCE_JPEG_COMPRESSION", False)
|
|
is_animated = hasattr(image, "is_animated") and image.is_animated
|
|
if should_compress and not is_animated:
|
|
file_object = self._compress_image(image)
|
|
filepath = f"{os.path.splitext(filepath)[0]}.jpg"
|
|
saved_path = self.storage_engine.save(filepath, file_object)
|
|
else:
|
|
file_object = self.file_object
|
|
saved_path = self.storage_engine.save(filepath, self.file_object)
|
|
|
|
if not is_animated:
|
|
self.create_thumbnail(file_object, saved_path)
|
|
return saved_path
|
|
|
|
def create_thumbnail(self, file_object, file_path):
|
|
thumbnail_filename = utils.get_thumb_filename(file_path)
|
|
thumbnail_io = BytesIO()
|
|
# File object after saving e.g. to S3 can be closed.
|
|
try:
|
|
image = Image.open(file_object).convert("RGBA")
|
|
except ValueError:
|
|
file_object = self.storage_engine.open(file_path)
|
|
image = Image.open(file_object).convert("RGBA")
|
|
image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)
|
|
image.save(thumbnail_io, format="PNG", optimize=True)
|
|
return self.storage_engine.save(thumbnail_filename, thumbnail_io)
|