|
|
|
|
|
import re |
|
from io import BytesIO |
|
import base64 |
|
from typing import Union |
|
|
|
from .typing import ImageType |
|
|
|
try: |
|
from PIL import Image |
|
except ImportError: |
|
Image = None |
|
|
|
from .errors import MissingRequirementsError |
|
|
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'} |
|
|
|
def is_allowed_extension(filename: str) -> bool: |
|
""" |
|
Checks if the given filename has an allowed extension. |
|
""" |
|
return '.' in filename and \ |
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS |
|
|
|
def is_data_uri_an_image(data_uri: str) -> bool: |
|
""" |
|
Checks if the given data URI represents an image. |
|
""" |
|
if not re.match(r'data:image/(\w+);base64,', data_uri): |
|
raise ValueError("Invalid data URI image.") |
|
image_format = re.match(r'data:image/(\w+);base64,', data_uri).group(1).lower() |
|
if image_format not in ALLOWED_EXTENSIONS and image_format != "svg+xml": |
|
raise ValueError("Invalid image format (from MIME type).") |
|
return True |
|
|
|
def extract_data_uri(data_uri: str) -> bytes: |
|
""" |
|
Extracts the binary data from the given data URI. |
|
""" |
|
data = data_uri.split(",")[1] |
|
return base64.b64decode(data) |
|
|
|
def to_data_uri(image: str) -> str: |
|
""" |
|
Validates and returns the data URI for an image. |
|
""" |
|
is_data_uri_an_image(image) |
|
return image |
|
|
|
def to_image(image: str) -> ImageType: |
|
""" |
|
Converts a base64-encoded image to bytes. |
|
""" |
|
if Image is None: |
|
raise MissingRequirementsError('Install "pillow" package for image processing.') |
|
is_data_uri_an_image(image) |
|
return extract_data_uri(image) |
|
|
|
class MissingRequirementsError(Exception): |
|
pass |
|
|
|
class ImageResponse: |
|
def __init__(self, url: str, alt: str): |
|
self.url = url |
|
self.alt = alt |
|
|