157 lines
4.7 KiB
Python
157 lines
4.7 KiB
Python
# -*- encoding: utf-8 -*-
|
|
|
|
from io import BytesIO
|
|
|
|
Path = None
|
|
random = None
|
|
subprocess = None
|
|
ImageCaptcha = None
|
|
ImageFilter = None
|
|
ImageOps = None
|
|
Image = None
|
|
|
|
|
|
def ensure_imported_Path():
|
|
global Path
|
|
from pathlib import Path
|
|
|
|
|
|
def ensure_imported_ImageFilter():
|
|
global ImageFilter
|
|
from PIL import ImageFilter
|
|
|
|
|
|
def ensure_imported_ImageOps():
|
|
global ImageOps
|
|
from PIL import ImageOps
|
|
|
|
|
|
def ensure_imported_ImageCaptcha():
|
|
global ImageCaptcha
|
|
from captcha.image import ImageCaptcha
|
|
|
|
|
|
def ensure_imported_Image():
|
|
global Image
|
|
from PIL.Image import Image
|
|
|
|
|
|
def ensure_imported_random():
|
|
global random
|
|
import random
|
|
|
|
|
|
def ensure_imported_subprocess():
|
|
global subprocess
|
|
import subprocess
|
|
|
|
|
|
class AbstractImageCaptchaGenerator:
|
|
'Abstract class for generators for image captcha'
|
|
|
|
def generate(self, secret: str, retry: int = 5) -> 'BytesIO':
|
|
'Creates an captcha image with given secret'
|
|
raise NotImplementedError(
|
|
'This method should be overridden on the child class')
|
|
|
|
def generate_bytes(self, secret: str, retry: int = 5) -> bytes:
|
|
'Default implementation for unwrapping BytesIO\'s contents'
|
|
return self.generate(secret, retry).getvalue()
|
|
|
|
|
|
class ImageCaptchaGenerator(AbstractImageCaptchaGenerator):
|
|
'Generates image captcha using current process'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
'Initializes protected values'
|
|
super().__init__(*args, **kwargs)
|
|
self._image_captcha = _gen_image_captcha()
|
|
ensure_imported_random()
|
|
ensure_imported_Image()
|
|
ensure_imported_ImageFilter()
|
|
ensure_imported_ImageOps()
|
|
|
|
def generate(self, secret: str, retry: int = 5) -> BytesIO:
|
|
im: Image = self._image_captcha.generate_image(secret)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.SMOOTH_MORE)
|
|
im = im.copy()
|
|
# if random.random() < 0.25:
|
|
# im = im.filter(ImageFilter.BLUR)
|
|
# im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.FIND_EDGES)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.CONTOUR)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.EMBOSS)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.DETAIL)
|
|
im = im.copy()
|
|
if random.random() < 0.25:
|
|
im = im.filter(ImageFilter.SHARPEN)
|
|
im = im.copy()
|
|
if random.random() < 0.5:
|
|
im = ImageOps.invert(im)
|
|
im = im.copy()
|
|
out = BytesIO()
|
|
im.save(out, format='PNG')
|
|
out.seek(0)
|
|
return out
|
|
|
|
|
|
class ImageCaptchaSubprocessGenerator(AbstractImageCaptchaGenerator):
|
|
'Generates image captcha on a subprocess'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
'Initializes protected values'
|
|
super().__init__(*args, **kwargs)
|
|
ensure_imported_subprocess()
|
|
|
|
def generate(self, secret: str, retry: int = 5) -> BytesIO:
|
|
result = subprocess.run(['python3', 'manage.py', 'gencaptcha', secret],
|
|
stdin=None,
|
|
stdout=subprocess.PIPE,
|
|
stderr=None,
|
|
text=False,
|
|
)
|
|
if result.returncode:
|
|
if retry > 0:
|
|
return self.generate(secret, retry - 1)
|
|
else:
|
|
result.check_returncode()
|
|
# print(result.stdout[:10])
|
|
out = BytesIO(result.stdout)
|
|
out.seek(0)
|
|
return out
|
|
|
|
|
|
def _gen_image_captcha() -> 'ImageCaptcha':
|
|
'Generates ImageCaptcha object'
|
|
ensure_imported_Path()
|
|
ensure_imported_ImageCaptcha()
|
|
image_captcha_fonts = [
|
|
str(Path('webproj/fonts/Roboto/Roboto-Regular.ttf').absolute()),
|
|
str(Path('webproj/fonts/Fondamento/Fondamento-Regular.ttf').absolute()),
|
|
str(Path('webproj/fonts/Josefin_Sans/static/JosefinSans-Medium.ttf').absolute()),
|
|
str(Path('webproj/fonts/Lora/static/Lora-Medium.ttf').absolute()),
|
|
str(Path('webproj/fonts/Quicksand/static/Quicksand-Regular.ttf').absolute()),
|
|
str(Path('webproj/fonts/Work_Sans/static/WorkSans-Medium.ttf').absolute()),
|
|
]
|
|
|
|
image_captcha = ImageCaptcha(300, 150,
|
|
fonts=image_captcha_fonts,
|
|
font_sizes=[36, 40, 44, 48, 54, 60, 66, 72])
|
|
|
|
for _ in image_captcha.truefonts:
|
|
pass
|
|
|
|
return image_captcha
|