116 lines
2.8 KiB
Python
116 lines
2.8 KiB
Python
# coding: utf-8
|
|
|
|
import re
|
|
import os
|
|
import unicodedata
|
|
import math
|
|
|
|
import six
|
|
from django.utils.module_loading import import_string
|
|
|
|
from filebrowser.settings import STRICT_PIL, NORMALIZE_FILENAME, CONVERT_FILENAME
|
|
from filebrowser.settings import VERSION_PROCESSORS
|
|
|
|
if STRICT_PIL:
|
|
from PIL import Image
|
|
else:
|
|
try:
|
|
from PIL import Image
|
|
except ImportError:
|
|
import Image
|
|
|
|
|
|
def convert_filename(value):
|
|
"""
|
|
Convert Filename.
|
|
"""
|
|
|
|
if NORMALIZE_FILENAME:
|
|
chunks = value.split(os.extsep)
|
|
normalized = []
|
|
for v in chunks:
|
|
v = unicodedata.normalize('NFKD', six.text_type(v)).encode('ascii', 'ignore').decode('ascii')
|
|
v = re.sub(r'[^\w\s-]', '', v).strip()
|
|
normalized.append(v)
|
|
|
|
if len(normalized) > 1:
|
|
value = '.'.join(normalized)
|
|
else:
|
|
value = normalized[0]
|
|
|
|
if CONVERT_FILENAME:
|
|
value = value.replace(" ", "_").lower()
|
|
|
|
return value
|
|
|
|
|
|
def path_strip(path, root):
|
|
if not path or not root:
|
|
return path
|
|
path = os.path.normcase(path)
|
|
root = os.path.normcase(root)
|
|
if path.startswith(root):
|
|
return path[len(root):]
|
|
return path
|
|
|
|
|
|
_default_processors = None
|
|
|
|
|
|
def process_image(source, processor_options, processors=None):
|
|
"""
|
|
Process a source PIL image through a series of image processors, returning
|
|
the (potentially) altered image.
|
|
"""
|
|
global _default_processors
|
|
if processors is None:
|
|
if _default_processors is None:
|
|
_default_processors = [import_string(name) for name in VERSION_PROCESSORS]
|
|
processors = _default_processors
|
|
image = source
|
|
for processor in processors:
|
|
image = processor(image, **processor_options)
|
|
return image
|
|
|
|
|
|
def scale_and_crop(im, width=None, height=None, opts='', **kwargs):
|
|
"""
|
|
Scale and Crop.
|
|
"""
|
|
x, y = [float(v) for v in im.size]
|
|
width = float(width or 0)
|
|
height = float(height or 0)
|
|
|
|
if (x, y) == (width, height):
|
|
return im
|
|
|
|
if 'upscale' not in opts:
|
|
if (x < width or not width) and (y < height or not height):
|
|
return im
|
|
|
|
if width:
|
|
xr = float(width)
|
|
else:
|
|
xr = float(x * height / y)
|
|
if height:
|
|
yr = float(height)
|
|
else:
|
|
yr = float(y * width / x)
|
|
|
|
if 'crop' in opts:
|
|
r = max(xr / x, yr / y)
|
|
else:
|
|
r = min(xr / x, yr / y)
|
|
|
|
if r < 1.0 or (r > 1.0 and 'upscale' in opts):
|
|
im = im.resize((int(math.ceil(x * r)), int(math.ceil(y * r))), resample=Image.ANTIALIAS)
|
|
|
|
if 'crop' in opts:
|
|
x, y = [float(v) for v in im.size]
|
|
ex, ey = (x - min(x, xr)) / 2, (y - min(y, yr)) / 2
|
|
if ex or ey:
|
|
im = im.crop((int(ex), int(ey), int(ex + xr), int(ey + yr)))
|
|
return im
|
|
|
|
scale_and_crop.valid_options = ('crop', 'upscale')
|