• Portretfoto van Maarten Kling
    Maarten Kling

Auto rotate Dexterity images based on EXIF info

Although I’ve been using Dexterity by default in the past years, I still need AT types for auto transforming images based on EXIF information.

When uploading an image to Plone using plone.app.contenttypes it is not automatically rotated like the user would expect. So when uploading a photo made sideways, the user views the photo in windows or mac just fine, almost every program rotates images automatically. But in Plone it suddenly is turned upside down, turned to the left, or right.

In AT there was some really cool stuff to read out your EXIF information and automatically transform images in your plone site.

To do this in Dexterity we can use the code below and trigger it when modified or created event is fired!

The simplified code from:

[https://github.com/plone/Products.ATContentTypes/blob/master/Products/ATContentTypes/lib/imagetransform.py][1]

FLIP_LEFT_RIGHT = 0
FLIP_TOP_BOTTOM = 1
ROTATE_90 = 2
ROTATE_180 = 3
ROTATE_270 = 4
TRANSPOSE_MAP = {
    FLIP_LEFT_RIGHT: (u'Flip around vertical axis'),
    FLIP_TOP_BOTTOM: (u'Flip around horizontal axis'),
    ROTATE_270: (u'Rotate 90 clockwise'),
    ROTATE_180: (u'Rotate 180'),
    ROTATE_90: (u'Rotate 90 counterclockwise'),

}
AUTO_ROTATE_MAP = {
    0: None,
    90: ROTATE_270,
    180: ROTATE_180,
    270: ROTATE_90,
}

ROTATION = {'Horizontal (normal)': 1,
            'Mirrored horizontal': 2,
            'Rotated 180': 3,
            'Mirrored vertical': 4,
            'Mirrored horizontal then rotated 90 CCW': 5,
            'Rotated 90 CW': 6,
            'Mirrored horizontal then rotated 90 CW': 7,
            'Rotated 90 CCW': 8}


def exifRotation(self, event):
    if self.portal_type == 'Image':
        if self.image:

            mirror, rotation = getEXIFOrientation(self)
            transform = None
            if rotation:
                transform = AUTO_ROTATE_MAP.get(rotation, None)
                if transform is not None:
                    transformImage(self, transform)


def getEXIF(self):
    exif_data = exif.process_file(StringIO(self.image.data), debug=False)
    # remove some unwanted elements lik thumb nails
    for key in ('JPEGThumbnail', 'TIFFThumbnail', 'MakerNote JPEGThumbnail'):
        if key in exif_data:
            del exif_data[key]
    return exif_data


def getEXIFOrientation(self):
    """Get the rotation and mirror orientation from the EXIF data

    Some cameras are storing the informations about rotation and mirror in
    the exif data. It can be used for autorotation.
    """
    exif = getEXIF(self)

    mirror = 0
    rotation = 0
    code = exif.get('Image Orientation', None)

    if code is None:
        return (mirror, rotation)

    try:
        code = int(code)
    except ValueError:
        code = ROTATION[code]

    if code in (2, 4, 5, 7):
        mirror = 1
    if code in (1, 2):
        rotation = 0
    elif code in (3, 4):
        rotation = 180
    elif code in (5, 6):
        rotation = 90
    elif code in (7, 8):
        rotation = 270

    return (mirror, rotation)


def transformImage(self, method, REQUEST=None):
    """
    Transform an Image:
        FLIP_LEFT_RIGHT
        FLIP_TOP_BOTTOM
        ROTATE_90 (rotate counterclockwise)
        ROTATE_180
        ROTATE_270 (rotate clockwise)
    """

    image = self.image.data
    image2 = StringIO()

    if image is not None:
        method = int(method)

        img = PIL.Image.open(StringIO(self.image.data))
        del image
        fmt = img.format
        img = img.transpose(method)
        img.save(image2, fmt, quality=88)

        self.image.data = image2.getvalue()

        self.reindexObject()

Now make a simple subscriber action to both modified and objectadded:

<subscriber
  for="plone.app.contenttypes.content.Image
     zope.lifecycleevent.IObjectModifiedEvent"
  handler=".events.exifRotation"
/>
<subscriber
  for="plone.app.contenttypes.content.Image
     zope.lifecycleevent.IObjectAddedEvent"
  handler=".events.exifRotation"
/>

And BAM you’ve got AT powers in you Dexterity based contenttypes and your images are auto rotated just like the user would expect. At the Arnhem sprint I will try and make this a default behavior or at least optional.

More about EXIF information:

https://github.com/plone/Products.ATContentTypes/blob/master/Products/ATContentTypes/thirdparty/exif.py

[1]: https://github.com/plone/Products.ATContentTypes/blob/master/Products/ATContentTypes/lib/imagetransform.py

 

We love code

Cookies

Wij maken gebruik van cookies. Meer hierover lees je in onze Privacy- cookieverklaring.