sterepando commited on
Commit
a9f0bdf
·
verified ·
1 Parent(s): 66c8e2e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +177 -9
app.py CHANGED
@@ -1,7 +1,165 @@
1
  import gradio as gr
2
  import base64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- # Встроенный HTML+JS+CSS (код сайта)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  html_content = """
6
  <!DOCTYPE html>
7
  <html lang="ru">
@@ -30,7 +188,7 @@ html_content = """
30
  margin: 0;
31
  padding: 20px;
32
  user-select: none;
33
- overflow-y: auto; /* Скролл внутри iframe, если контент не влазит */
34
  }
35
 
36
  h1 { margin-bottom: 10px; font-weight: 900; }
@@ -40,7 +198,7 @@ html_content = """
40
  gap: 30px;
41
  flex-wrap: wrap;
42
  justify-content: center;
43
- padding-bottom: 50px; /* Отступ снизу для удобства */
44
  }
45
 
46
  .preview-area {
@@ -761,18 +919,12 @@ html_content = """
761
  """
762
 
763
  def create_app():
764
- # Кодируем HTML в base64
765
  encoded_html = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
766
-
767
- # КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: position: fixed.
768
- # Это вырывает iframe из потока документа Gradio, предотвращая бесконечное расширение.
769
- # top: 0, left: 0, width: 100%, height: 100% заставляет его занять всё окно браузера.
770
  iframe_html = f'''
771
  <iframe src="data:text/html;base64,{encoded_html}"
772
  style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; border: none; z-index: 9999;">
773
  </iframe>'''
774
 
775
- # Стили для подавления самого интерфейса Gradio под нашим iframe
776
  global_css = """
777
  <style>
778
  footer {display: none !important;}
@@ -782,9 +934,25 @@ def create_app():
782
  """
783
 
784
  with gr.Blocks(title="Swaga Icon Maker") as demo:
 
785
  gr.HTML(global_css)
786
  gr.HTML(iframe_html)
787
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
  return demo
789
 
790
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  import base64
3
+ import requests
4
+ import io
5
+ import os
6
+ from PIL import Image, ImageDraw, ImageFont, ImageOps
7
+
8
+ # ==========================================
9
+ # 1. ПОДГОТОВКА РЕСУРСОВ (Шрифт)
10
+ # ==========================================
11
+ FONT_URL = "https://github.com/google/fonts/raw/main/ofl/nunito/Nunito-Black.ttf"
12
+ FONT_PATH = "Nunito-Black.ttf"
13
+
14
+ def download_font():
15
+ if not os.path.exists(FONT_PATH):
16
+ print("Скачивание шрифта Nunito...")
17
+ try:
18
+ r = requests.get(FONT_URL)
19
+ with open(FONT_PATH, 'wb') as f:
20
+ f.write(r.content)
21
+ print("Шрифт скачан.")
22
+ except Exception as e:
23
+ print(f"Ошибка скачивания шрифта: {e}")
24
+
25
+ # Скачиваем шрифт при запуске
26
+ download_font()
27
+
28
+ # ==========================================
29
+ # 2. ЛОГИКА ГЕНЕРАЦИИ НА PYTHON (Для API)
30
+ # ==========================================
31
+
32
+ BG_COLOR = (225, 56, 57) # #E13839
33
+ GRADIENT_START = (255, 255, 255) # #FFFFFF
34
+ GRADIENT_END = (255, 172, 199) # #FFACC7
35
+
36
+ def create_gradient_image(width, height):
37
+ """Создает вертикальный градиент."""
38
+ base = Image.new('RGB', (width, height), GRADIENT_START)
39
+ top = Image.new('RGB', (width, height), GRADIENT_END)
40
+ mask = Image.new('L', (width, height))
41
+ mask_data = []
42
+ for y in range(height):
43
+ mask_data.extend([int(255 * (y / height))] * width)
44
+ mask.putdata(mask_data)
45
+ base.paste(top, (0, 0), mask)
46
+ return base
47
+
48
+ def generate_text_icon(text):
49
+ """API: Генерация иконки с адаптивным текстом."""
50
+ if not text:
51
+ return None
52
+
53
+ W, H = 512, 512
54
+ # 1. Фон
55
+ img = Image.new('RGBA', (W, H), BG_COLOR + (255,))
56
+
57
+ # 2. Создание маски текста
58
+ # Начинаем с большого шрифта и уменьшаем, пока не влезет
59
+ font_size = 400
60
+ padding = 40
61
+ max_w = W - (padding * 2)
62
+ max_h = H - (padding * 2)
63
+
64
+ try:
65
+ font = ImageFont.truetype(FONT_PATH, font_size)
66
+ except:
67
+ font = ImageFont.load_default()
68
+
69
+ while font_size > 20:
70
+ try:
71
+ font = ImageFont.truetype(FONT_PATH, font_size)
72
+ except:
73
+ pass # fallback
74
+
75
+ # Используем getbbox для точного измерения
76
+ dummy_draw = ImageDraw.Draw(Image.new('L', (1, 1)))
77
+ bbox = dummy_draw.textbbox((0, 0), text, font=font)
78
+ text_w = bbox[2] - bbox[0]
79
+ text_h = bbox[3] - bbox[1]
80
+
81
+ if text_w <= max_w and text_h <= max_h:
82
+ break
83
+ font_size -= 10
84
+
85
+ # Рисуем текст в маску (белый текст на черном фоне)
86
+ mask_img = Image.new('L', (W, H), 0)
87
+ draw = ImageDraw.Draw(mask_img)
88
+
89
+ # Центрирование
90
+ # Пересчитываем bbox для финального шрифта
91
+ bbox = draw.textbbox((0, 0), text, font=font)
92
+ text_w = bbox[2] - bbox[0]
93
+ text_h = bbox[3] - bbox[1]
94
+ x = (W - text_w) / 2 - bbox[0]
95
+ y = (H - text_h) / 2 - bbox[1] - (bbox[1]/2) # Небольшая коррекция baseline
96
+
97
+ draw.text((x, y), text, font=font, fill=255)
98
+
99
+ # 3. Градиент
100
+ gradient = create_gradient_image(W, H)
101
+
102
+ # 4. Наложение градиента по маске текста на красный фон
103
+ img.paste(gradient, (0, 0), mask_img)
104
+
105
+ return img
106
 
107
+ def generate_image_icon(input_image, scale_percent):
108
+ """API: Генерация иконки из картинки (SVG/PNG) с ресайзом."""
109
+ if input_image is None:
110
+ return None
111
+
112
+ W, H = 512, 512
113
+ # 1. Фон
114
+ final_img = Image.new('RGBA', (W, H), BG_COLOR + (255,))
115
+
116
+ # 2. Обработка входного изображения
117
+ # Если это SVG, Gradio/PIL может сконвертировать его в растр при загрузке,
118
+ # но лучше подавать PNG. PIL откроет как растр.
119
+ if isinstance(input_image, str): # Если путь
120
+ icon = Image.open(input_image).convert("RGBA")
121
+ else: # Если объект
122
+ icon = input_image.convert("RGBA")
123
+
124
+ # Масштабирование
125
+ scale = scale_percent / 100.0
126
+
127
+ # Базовый размер - пытаемся вписать в 300px (как в JS версии) и умножаем на scale
128
+ base_size = 300 * scale
129
+
130
+ # Сохраняем пропорции
131
+ ratio = icon.width / icon.height
132
+ new_w = int(base_size)
133
+ new_h = int(base_size / ratio)
134
+
135
+ icon_resized = icon.resize((new_w, new_h), Image.Resampling.LANCZOS)
136
+
137
+ # Центрирование
138
+ offset_x = (W - new_w) // 2
139
+ offset_y = (H - new_h) // 2
140
+
141
+ # 3. Создаем маску из альфа-канала иконки
142
+ # Если иконка не имеет прозрачности, считаем её сплошной
143
+ if "A" in icon_resized.getbands():
144
+ mask = icon_resized.split()[-1]
145
+ else:
146
+ mask = Image.new('L', icon_resized.size, 255)
147
+
148
+ # 4. Градиент
149
+ gradient = create_gradient_image(W, H)
150
+
151
+ # Вырезаем кусок градиента под размер иконки (чтобы градиент был глобальным на холсте)
152
+ gradient_crop = gradient.crop((offset_x, offset_y, offset_x + new_w, offset_y + new_h))
153
+
154
+ # 5. Наложение
155
+ final_img.paste(gradient_crop, (offset_x, offset_y), mask)
156
+
157
+ return final_img
158
+
159
+
160
+ # ==========================================
161
+ # 3. HTML КОД (Frontend)
162
+ # ==========================================
163
  html_content = """
164
  <!DOCTYPE html>
165
  <html lang="ru">
 
188
  margin: 0;
189
  padding: 20px;
190
  user-select: none;
191
+ overflow-y: auto;
192
  }
193
 
194
  h1 { margin-bottom: 10px; font-weight: 900; }
 
198
  gap: 30px;
199
  flex-wrap: wrap;
200
  justify-content: center;
201
+ padding-bottom: 50px;
202
  }
203
 
204
  .preview-area {
 
919
  """
920
 
921
  def create_app():
 
922
  encoded_html = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
 
 
 
 
923
  iframe_html = f'''
924
  <iframe src="data:text/html;base64,{encoded_html}"
925
  style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; border: none; z-index: 9999;">
926
  </iframe>'''
927
 
 
928
  global_css = """
929
  <style>
930
  footer {display: none !important;}
 
934
  """
935
 
936
  with gr.Blocks(title="Swaga Icon Maker") as demo:
937
+ # 1. ОСНОВНОЙ UI (HTML)
938
  gr.HTML(global_css)
939
  gr.HTML(iframe_html)
940
 
941
+ # 2. API ENDPOINTS (Скрытые)
942
+ with gr.Column(visible=False):
943
+ # API для текста (/run/predict, api_name="text")
944
+ api_text_input = gr.Textbox(label="Text")
945
+ api_text_output = gr.Image(label="Icon")
946
+ btn_text = gr.Button("Generate Text")
947
+ btn_text.click(generate_text_icon, inputs=api_text_input, outputs=api_text_output, api_name="text")
948
+
949
+ # API для иконок (/run/predict, api_name="icons")
950
+ api_icon_input = gr.Image(label="Icon Input", type="pil")
951
+ api_scale_input = gr.Slider(minimum=10, maximum=200, value=100, label="Size %")
952
+ api_icon_output = gr.Image(label="Icon Output")
953
+ btn_icon = gr.Button("Generate Icon")
954
+ btn_icon.click(generate_image_icon, inputs=[api_icon_input, api_scale_input], outputs=api_icon_output, api_name="icons")
955
+
956
  return demo
957
 
958
  if __name__ == "__main__":