matthewwithanm / django-imagekit

Automated image processing for Django. Currently v4.0
http://django-imagekit.rtfd.org/
BSD 3-Clause "New" or "Revised" License
2.27k stars 275 forks source link

How to preserve exif data with ProcessedImageField #563

Open zoltan-ky opened 1 year ago

zoltan-ky commented 1 year ago

The old options={'keep_exif': True} seems to be gone from imagekit / pilkit / PIL. Now it seems one has to supply the 'exif' data itself from the uploaded source file in options -> params to PIL Image.save() - so how does one do that as it cannot be declared statically as ProcessedImageField options parameter?

epou commented 1 year ago

Here's a workaround I ended up using for facing this issue. Not fancy, I'd prefer doing it by setting an option in the ProcessedImageField. It basically overrides the save method of the original ProcessedImageField by setting the new spec.options dict to contain the same EXIF info as the source image (before processing it)

import os

from django.db.models.fields.files import ImageFieldFile
from django.db import models

from imagekit.models import ProcessedImageField as OriginalProcessedImageField
from imagekit.utils import generate, open_image, suggest_extension

class ProcessedImageExifFieldFile(ImageFieldFile):
    def save(self, name, content, save=True):
        # COPIED FROM OriginalProcessedImageField
        filename, ext = os.path.splitext(name)
        spec = self.field.get_spec(source=content)
        ext = suggest_extension(name, spec.format)
        new_name = f"{filename}{ext}"
        # ADDED CODE START
        if not spec.options:
            spec.options = {}
        if exif := open_image(content).info.get("exif", None):
            spec.options["exif"] = exif        
        # ADDED CODE END
        content = generate(spec)
        return super().save(new_name, content, save)

class ProcessedImageField(OriginalProcessedImageField):
    attr_class = ProcessedImageExifFieldFile

####################

class DummyModel(models.Model)
    image = ProcessedImageField()