import gradio as gr import base64 import requests import io import os from PIL import Image, ImageDraw, ImageFont, ImageOps # ========================================== # 1. ПОДГОТОВКА РЕСУРСОВ (Шрифт) # ========================================== FONT_URL = "https://github.com/google/fonts/raw/main/ofl/nunito/Nunito-Black.ttf" FONT_PATH = "Nunito-Black.ttf" def download_font(): if not os.path.exists(FONT_PATH): print("Скачивание шрифта Nunito...") try: r = requests.get(FONT_URL) with open(FONT_PATH, 'wb') as f: f.write(r.content) print("Шрифт скачан.") except Exception as e: print(f"Ошибка скачивания шрифта: {e}") # Скачиваем шрифт при запуске download_font() # ========================================== # 2. ЛОГИКА ГЕНЕРАЦИИ НА PYTHON (Для API) # ========================================== BG_COLOR = (225, 56, 57) # #E13839 GRADIENT_START = (255, 255, 255) # #FFFFFF GRADIENT_END = (255, 172, 199) # #FFACC7 def create_gradient_image(width, height): """Создает вертикальный градиент.""" base = Image.new('RGB', (width, height), GRADIENT_START) top = Image.new('RGB', (width, height), GRADIENT_END) mask = Image.new('L', (width, height)) mask_data = [] for y in range(height): mask_data.extend([int(255 * (y / height))] * width) mask.putdata(mask_data) base.paste(top, (0, 0), mask) return base def generate_text_icon(text): """API: Генерация иконки с адаптивным текстом.""" if not text: return None W, H = 512, 512 # 1. Фон img = Image.new('RGBA', (W, H), BG_COLOR + (255,)) # 2. Создание маски текста # Начинаем с большого шрифта и уменьшаем, пока не влезет font_size = 400 padding = 40 max_w = W - (padding * 2) max_h = H - (padding * 2) try: font = ImageFont.truetype(FONT_PATH, font_size) except: font = ImageFont.load_default() while font_size > 20: try: font = ImageFont.truetype(FONT_PATH, font_size) except: pass # fallback # Используем getbbox для точного измерения dummy_draw = ImageDraw.Draw(Image.new('L', (1, 1))) bbox = dummy_draw.textbbox((0, 0), text, font=font) text_w = bbox[2] - bbox[0] text_h = bbox[3] - bbox[1] if text_w <= max_w and text_h <= max_h: break font_size -= 10 # Рисуем текст в маску (белый текст на черном фоне) mask_img = Image.new('L', (W, H), 0) draw = ImageDraw.Draw(mask_img) # Центрирование # Пересчитываем bbox для финального шрифта bbox = draw.textbbox((0, 0), text, font=font) text_w = bbox[2] - bbox[0] text_h = bbox[3] - bbox[1] x = (W - text_w) / 2 - bbox[0] y = (H - text_h) / 2 - bbox[1] - (bbox[1]/2) # Небольшая коррекция baseline draw.text((x, y), text, font=font, fill=255) # 3. Градиент gradient = create_gradient_image(W, H) # 4. Наложение градиента по маске текста на красный фон img.paste(gradient, (0, 0), mask_img) return img def generate_image_icon(input_image, scale_percent): """API: Генерация иконки из картинки (SVG/PNG) с ресайзом.""" if input_image is None: return None W, H = 512, 512 # 1. Фон final_img = Image.new('RGBA', (W, H), BG_COLOR + (255,)) # 2. Обработка входного изображения # Если это SVG, Gradio/PIL может сконвертировать его в растр при загрузке, # но лучше подавать PNG. PIL откроет как растр. if isinstance(input_image, str): # Если путь icon = Image.open(input_image).convert("RGBA") else: # Если объект icon = input_image.convert("RGBA") # Масштабирование scale = scale_percent / 100.0 # Базовый размер - пытаемся вписать в 300px (как в JS версии) и умножаем на scale base_size = 300 * scale # Сохраняем пропорции ratio = icon.width / icon.height new_w = int(base_size) new_h = int(base_size / ratio) icon_resized = icon.resize((new_w, new_h), Image.Resampling.LANCZOS) # Центрирование offset_x = (W - new_w) // 2 offset_y = (H - new_h) // 2 # 3. Создаем маску из альфа-канала иконки # Если иконка не имеет прозрачности, считаем её сплошной if "A" in icon_resized.getbands(): mask = icon_resized.split()[-1] else: mask = Image.new('L', icon_resized.size, 255) # 4. Градиент gradient = create_gradient_image(W, H) # Вырезаем кусок градиента под размер иконки (чтобы градиент был глобальным на холсте) gradient_crop = gradient.crop((offset_x, offset_y, offset_x + new_w, offset_y + new_h)) # 5. Наложение final_img.paste(gradient_crop, (offset_x, offset_y), mask) return final_img # ========================================== # 3. HTML КОД (Frontend) # ========================================== html_content = """ Swaga Icon Maker

MandreIcon Creator

Кликни на стикер для выделения.
Delete = удалить.

Фон (Base Icon)

SVG (Градиентный фон)
Клик или Drop

Стикеры (PNG)
""" def create_app(): encoded_html = base64.b64encode(html_content.encode('utf-8')).decode('utf-8') iframe_html = f''' ''' global_css = """ """ with gr.Blocks(title="Swaga Icon Maker") as demo: # 1. ОСНОВНОЙ UI (HTML) gr.HTML(global_css) gr.HTML(iframe_html) # 2. API ENDPOINTS (Скрытые) with gr.Column(visible=False): # API для текста (/run/predict, api_name="text") api_text_input = gr.Textbox(label="Text") api_text_output = gr.Image(label="Icon") btn_text = gr.Button("Generate Text") btn_text.click(generate_text_icon, inputs=api_text_input, outputs=api_text_output, api_name="text") # API для иконок (/run/predict, api_name="icons") api_icon_input = gr.Image(label="Icon Input", type="pil") api_scale_input = gr.Slider(minimum=10, maximum=200, value=100, label="Size %") api_icon_output = gr.Image(label="Icon Output") btn_icon = gr.Button("Generate Icon") btn_icon.click(generate_image_icon, inputs=[api_icon_input, api_scale_input], outputs=api_icon_output, api_name="icons") return demo if __name__ == "__main__": create_app().launch()