Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from fastapi.responses import StreamingResponse
|
3 |
+
from pydantic import BaseModel
|
4 |
+
from PIL import Image, ImageDraw, ImageFont
|
5 |
+
import requests
|
6 |
+
from io import BytesIO
|
7 |
+
import os
|
8 |
+
|
9 |
+
app = FastAPI()
|
10 |
+
|
11 |
+
class WelcomeRequest(BaseModel):
|
12 |
+
username: str
|
13 |
+
avatar_url: str | None = None
|
14 |
+
|
15 |
+
def load_font(size: int = 60) -> ImageFont.FreeTypeFont:
|
16 |
+
font_paths = [
|
17 |
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
18 |
+
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
19 |
+
"/usr/share/fonts/truetype/freefont/FreeSans.ttf",
|
20 |
+
"/System/Library/Fonts/Arial.ttf", # macOS
|
21 |
+
"C:/Windows/Fonts/arial.ttf", # Windows
|
22 |
+
"arial.ttf"
|
23 |
+
]
|
24 |
+
|
25 |
+
for path in font_paths:
|
26 |
+
try:
|
27 |
+
if os.path.exists(path):
|
28 |
+
return ImageFont.truetype(path, size)
|
29 |
+
except Exception as e:
|
30 |
+
print(f"Failed to load font {path}: {e}")
|
31 |
+
continue
|
32 |
+
|
33 |
+
# If no TrueType font found, try to use default but warn
|
34 |
+
print(f"Warning: Using default font, size may not scale properly")
|
35 |
+
try:
|
36 |
+
return ImageFont.load_default(size)
|
37 |
+
except:
|
38 |
+
return ImageFont.load_default()
|
39 |
+
|
40 |
+
@app.post("/generate-welcome")
|
41 |
+
async def generate_welcome(data: WelcomeRequest):
|
42 |
+
try:
|
43 |
+
base_image = Image.open("ChatGPT Image May 27, 2025, 05_24_11 AM.png").convert("RGBA")
|
44 |
+
except FileNotFoundError:
|
45 |
+
return {"error": "base_image.png not found"}
|
46 |
+
|
47 |
+
width, height = base_image.size
|
48 |
+
draw = ImageDraw.Draw(base_image)
|
49 |
+
|
50 |
+
# === Big, centered username font ===
|
51 |
+
username_font_size = 50
|
52 |
+
font = load_font(username_font_size)
|
53 |
+
|
54 |
+
# Debug: Check if font loaded properly
|
55 |
+
print(f"Font loaded: {type(font)}, size: {username_font_size}")
|
56 |
+
|
57 |
+
text_color = (148, 255, 255)
|
58 |
+
|
59 |
+
# === Position elements in lower center area ===
|
60 |
+
username_with_at = f"@{data.username}" # Add @ symbol
|
61 |
+
text_width = draw.textlength(username_with_at, font=font)
|
62 |
+
username_x = (width - text_width) // 2
|
63 |
+
|
64 |
+
# Position username in lower portion of image
|
65 |
+
username_y = height - 120 # 120px from bottom
|
66 |
+
|
67 |
+
# === Optional avatar ===
|
68 |
+
avatar_size = 120
|
69 |
+
if data.avatar_url:
|
70 |
+
try:
|
71 |
+
response = requests.get(data.avatar_url)
|
72 |
+
avatar = Image.open(BytesIO(response.content)).convert("RGBA").resize((avatar_size, avatar_size))
|
73 |
+
|
74 |
+
mask = Image.new("L", (avatar_size, avatar_size), 0)
|
75 |
+
draw_mask = ImageDraw.Draw(mask)
|
76 |
+
draw_mask.ellipse((0, 0, avatar_size, avatar_size), fill=255)
|
77 |
+
|
78 |
+
# Position avatar above the username in lower center
|
79 |
+
avatar_x = width // 2 - avatar_size // 2
|
80 |
+
avatar_y = username_y - avatar_size - 30 # 30px gap above username
|
81 |
+
|
82 |
+
base_image.paste(avatar, (avatar_x, avatar_y), mask)
|
83 |
+
except Exception as e:
|
84 |
+
print("Error loading avatar:", e)
|
85 |
+
|
86 |
+
# === Draw large centered username with @ symbol in lower area ===
|
87 |
+
draw.text((username_x, username_y), username_with_at, fill=text_color, font=font)
|
88 |
+
|
89 |
+
output = BytesIO()
|
90 |
+
base_image.save(output, format='PNG')
|
91 |
+
output.seek(0)
|
92 |
+
return StreamingResponse(output, media_type="image/png")
|