Introduction
Typography is an important component in t-shirt design — quotes, meme text, brand names, stylized lettering. AI not only creates images but also has to generate beautiful text, with the right font style, and place it in the right position on the shirt.
1. Typography Challenges in AI
Why is text generation difficult?
Stable Diffusion (SDXL) + text:
❌ Chữ bị sai spelling ("COFEE" thay vì "COFFEE")
❌ Chữ bị méo, khó đọc
❌ Font không consistent
❌ Chữ bị mirror/reverse
Giải pháp: KHÔNG dùng Diffusion model để render text trực tiếp.
→ Dùng text rendering engine riêng + composite lên design.
2. Text Generation Pipeline
User Input
├── "Tạo áo với quote motivational"
│
├── LLM generate quote text
│ └── "HUSTLE HARDER"
│
├── Font recommendation engine
│ └── Bold sans-serif, uppercase
│
├── Text rendering (Pillow/Cairo)
│ └── High-res text image (transparent)
│
├── Style transfer (optional)
│ └── Neon glow, shadow, gradient
│
└── Composite onto design
└── Auto-placement + position
3. AI Text Content Generator
class TextContentGenerator:
"""AI generate text content cho áo thun"""
async def generate_text(
self,
category: str,
style: str = "default",
language: str = "en",
) -> list[str]:
prompt = f"""
Generate 5 short text/quotes for a {style} style t-shirt.
Category: {category}
Language: {language}
Rules:
- Maximum 5 words per line
- Maximum 2 lines
- ALL CAPS preferred for impact
- Catchy, memorable, trendy
- No offensive content
Return as JSON array of strings.
"""
response = await self.llm.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
)
return json.loads(
response.choices[0].message.content
)["texts"]
4. Font Recommendation Engine
class FontRecommender:
"""Gợi ý font phù hợp theo style design"""
FONT_STYLES = {
"cyberpunk": [
{"name": "Orbitron", "weight": "Bold", "case": "upper"},
{"name": "Rajdhani", "weight": "SemiBold", "case": "upper"},
],
"minimal": [
{"name": "Montserrat", "weight": "Light", "case": "upper"},
{"name": "Futura", "weight": "Medium", "case": "mixed"},
],
"streetwear": [
{"name": "Impact", "weight": "Regular", "case": "upper"},
{"name": "Bebas Neue", "weight": "Regular", "case": "upper"},
],
"vintage": [
{"name": "Playfair Display", "weight": "Bold", "case": "mixed"},
{"name": "Lora", "weight": "Regular", "case": "mixed"},
],
"gaming": [
{"name": "Press Start 2P", "weight": "Regular", "case": "upper"},
{"name": "Audiowide", "weight": "Regular", "case": "upper"},
],
"japanese": [
{"name": "Noto Sans JP", "weight": "Black", "case": "mixed"},
{"name": "M PLUS 1p", "weight": "Bold", "case": "mixed"},
],
}
def recommend(self, design_style: str) -> list[dict]:
return self.FONT_STYLES.get(
design_style, self.FONT_STYLES["minimal"]
)
5. Text Rendering Engine
from PIL import Image, ImageDraw, ImageFont
class TextRenderer:
"""Render text thành image cho composite"""
def render(
self,
text: str,
font_name: str,
font_size: int,
color: str = "#FFFFFF",
effects: list[str] | None = None,
) -> Image.Image:
# Load font
font = ImageFont.truetype(
f"fonts/{font_name}.ttf", font_size
)
# Calculate text size
bbox = font.getbbox(text)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# Create canvas with padding
padding = font_size // 4
canvas = Image.new(
"RGBA",
(text_width + padding * 2, text_height + padding * 2),
(0, 0, 0, 0)
)
draw = ImageDraw.Draw(canvas)
draw.text(
(padding, padding),
text, font=font,
fill=color,
)
# Apply effects
if effects:
for effect in effects:
canvas = self._apply_effect(canvas, effect)
return canvas
def _apply_effect(
self, img: Image.Image, effect: str
) -> Image.Image:
if effect == "neon_glow":
return self._neon_glow(img)
elif effect == "drop_shadow":
return self._drop_shadow(img)
elif effect == "outline":
return self._outline(img)
elif effect == "gradient":
return self._gradient_fill(img)
return img
def _neon_glow(self, img: Image.Image) -> Image.Image:
"""Hiệu ứng neon glow cho text"""
from PIL import ImageFilter
import numpy as np
# Create glow layer
glow = img.filter(ImageFilter.GaussianBlur(radius=10))
glow = glow.filter(ImageFilter.GaussianBlur(radius=5))
# Brighten glow
glow_array = np.array(glow)
glow_array[:, :, :3] = np.clip(
glow_array[:, :, :3] * 1.5, 0, 255
).astype(np.uint8)
glow = Image.fromarray(glow_array)
# Composite: glow behind text
result = Image.alpha_composite(glow, img)
return result
6. Auto-Placement
class TextPlacer:
"""Tự động đặt text vào vị trí phù hợp trên design"""
def auto_place(
self,
design: Image.Image,
text_image: Image.Image,
position: str = "auto",
) -> Image.Image:
if position == "auto":
position = self._find_best_position(
design, text_image
)
positions = {
"top_center": self._place_top_center,
"bottom_center": self._place_bottom_center,
"center": self._place_center,
"arc_top": self._place_arc_top,
"arc_bottom": self._place_arc_bottom,
}
placer = positions.get(position, self._place_center)
return placer(design, text_image)
def _find_best_position(
self,
design: Image.Image,
text_image: Image.Image,
) -> str:
"""AI-detect vùng trống tốt nhất cho text"""
# Phân tích content density theo vùng
regions = self._analyze_regions(design)
# Text đặt ở vùng ít content nhất
least_dense = min(regions, key=lambda r: r["density"])
return least_dense["position"]
Summary
AI Typography System:
- Text generation — LLM creates quotes, meme text, slogans
- Font recommendation — font suggestion according to design style
- Text rendering — Pillow/Cairo renders high quality text
- Effects — neon glow, shadow, outline, gradient
- Auto-placement — finds suitable empty areas on the design
The next article begins Part 4: AI Personalization — the AI system learns each user's aesthetic taste.