1- # -*- coding: utf-8 -*-
2- from __future__ import unicode_literals
3-
4- from io import BytesIO
5- from PIL import Image
6- from resizeimage import resizeimage
7-
81import tinify
92import logging
103import requests
4+ from io import BytesIO
5+ from PIL import Image
6+ from resizeimage import resizeimage
7+ from uuid import uuid4
118
12- from .settings import ( OPTIMIZED_IMAGE_METHOD , TINYPNG_KEY )
9+ from .settings import OPTIMIZED_IMAGE_METHOD , TINYPNG_KEY
1310
1411
1512BACKGROUND_TRANSPARENT = (255 , 255 , 255 , 0 )
@@ -23,10 +20,10 @@ def get_file_extension(file_name):
2320 extension = None
2421
2522 # Get image file extension
26- if file_name .split ('.' )[- 1 ].lower () != ' jpg' :
27- extension = file_name .split ('.' )[- 1 ].upper ()
23+ if file_name .split ("." )[- 1 ].lower () != " jpg" :
24+ extension = file_name .split ("." )[- 1 ].upper ()
2825 else :
29- extension = ' JPEG'
26+ extension = " JPEG"
3027
3128 return extension
3229
@@ -39,11 +36,11 @@ def image_optimizer(image_data, output_size=None, resize_method=None):
3936 """
4037 Optimize an image that has not been saved to a file.
4138 :param `image_data` is image data, e.g from request.FILES['image']
42- :param `output_size` is float pixel scale of image (width, height) or None, e.g : (400, 300)
43- :param `resize_method` is string resize method, choices are: None, "thumbnail", or "cover".
39+ :param `output_size` is float pixel scale of image (width, height) or None, for example : (400, 300) # noqa: E501
40+ :param `resize_method` is string resize method, choices are: None, "thumbnail", or "cover". # noqa: E501
4441 :return optimized image data.
4542 """
46- if OPTIMIZED_IMAGE_METHOD == ' pillow' :
43+ if OPTIMIZED_IMAGE_METHOD == " pillow" :
4744 image = Image .open (image_data )
4845 bytes_io = BytesIO ()
4946
@@ -53,49 +50,108 @@ def image_optimizer(image_data, output_size=None, resize_method=None):
5350 # resize_method. 'thumbnail' is used by default
5451 if output_size is not None :
5552
56- if resize_method not in ('thumbnail' , 'cover' , None ):
57- message = 'optimized_image_resize_method misconfigured, it\' s value must be \' thumbnail\' , \' cover\' or None'
53+ if resize_method not in ("thumbnail" , "cover" , None ):
54+ message = (
55+ "optimized_image_resize_method misconfigured, "
56+ "it's value must be 'thumbnail', 'cover' or None"
57+ )
5858 raise Exception (message )
5959
60- elif resize_method is 'thumbnail' :
61- image = resizeimage .resize_thumbnail (image , output_size , resample = Image .LANCZOS )
60+ elif resize_method == "thumbnail" :
61+ image = resizeimage .resize_thumbnail (
62+ image , output_size , resample = Image .LANCZOS
63+ )
6264
63- elif resize_method is ' cover' :
65+ elif resize_method == " cover" :
6466 image = resizeimage .resize_cover (image , output_size , validate = False )
6567
66- output_image = Image .new ('RGBA' , output_size , BACKGROUND_TRANSPARENT )
67- output_image_center = (int ((output_size [0 ] - image .size [0 ]) / 2 ),
68- int ((output_size [1 ] - image .size [1 ]) / 2 ))
68+ output_image = Image .new (
69+ "RGBA" ,
70+ output_size ,
71+ BACKGROUND_TRANSPARENT ,
72+ )
73+ output_image_center = (
74+ int ((output_size [0 ] - image .size [0 ]) / 2 ),
75+ int ((output_size [1 ] - image .size [1 ]) / 2 ),
76+ )
6977
7078 output_image .paste (image , output_image_center )
7179
7280 else :
73- # If output_size is None the output_image would be the same as source
81+ # If output_size is None the output_image
82+ # would be the same as source
7483 output_image = image
7584
7685 # If the file extension is JPEG, convert the output_image to RGB
77- if extension == ' JPEG' :
78- output_image = output_image .convert (' RGB' )
86+ if extension == " JPEG" :
87+ output_image = output_image .convert (" RGB" )
7988
8089 output_image .save (bytes_io , format = extension , optimize = True )
8190
8291 image_data .seek (0 )
8392 image_data .file .write (bytes_io .getvalue ())
8493 image_data .file .truncate ()
8594
86- elif OPTIMIZED_IMAGE_METHOD == ' tinypng' :
95+ elif OPTIMIZED_IMAGE_METHOD == " tinypng" :
8796 # disable warning info
8897 requests .packages .urllib3 .disable_warnings ()
8998
9099 # just info for people
91100 if any ([output_size , resize_method ]):
92- message = '[django-image-optimizer] "output_size" and "resize_method" only for OPTIMIZED_IMAGE_METHOD="pillow"'
101+ message = (
102+ '[django-image-optimizer] "output_size" and "resize_method" '
103+ 'only for OPTIMIZED_IMAGE_METHOD="pillow"'
104+ )
93105 logging .info (message )
94106
95107 tinify .key = TINYPNG_KEY
96- optimized_buffer = tinify .from_buffer (image_data .file .read ()).to_buffer ()
108+ optimized_buffer = tinify .from_buffer (
109+ image_data .file .read ()
110+ ).to_buffer () # noqa: E501
97111 image_data .seek (0 )
98112 image_data .file .write (optimized_buffer )
99113 image_data .file .truncate ()
100114
101115 return image_data
116+
117+
118+ def crop_image_on_axis (image , width , height , x , y , extension ):
119+ """
120+ function to crop the image using axis (using Pillow).
121+ :param `image` is image data, e.g from request.FILES['image']
122+ :param `width` float width of image
123+ :param `height` float height of image
124+ :param `x` is float x axis
125+ :param `y` is float y axis
126+ :param `extension` is string, e.g: ".png"
127+ """
128+ # Open the passed image
129+ img = Image .open (image )
130+
131+ # Initialise bytes io
132+ bytes_io = BytesIO ()
133+
134+ # crop the image through axis
135+ img = img .crop ((x , y , width + x , height + y ))
136+
137+ # resize the image and optimise it for file size,
138+ # making smaller as possible
139+ img = img .resize ((width , height ), Image .ANTIALIAS )
140+
141+ # This line is optional, for safe side, image name should be unique.
142+ img .name = "{}.{}" .format (uuid4 ().hex , extension )
143+
144+ # If the file extension is JPEG, convert the output_image to RGB
145+ if extension == "JPEG" :
146+ img = image .convert ("RGB" )
147+ img .save (bytes_io , format = extension , optimize = True )
148+
149+ # return the image
150+ image .seek (0 )
151+
152+ # Write back new image
153+ image .file .write (bytes_io .getvalue ())
154+
155+ # truncate the file size
156+ image .file .truncate ()
157+ return image
0 commit comments