# app/image.py 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 # Handle absence of PIL 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