Ricky01anjay's picture
Update app.py
9131604 verified
import io
import os
import urllib.parse
from flask import Flask, request, send_file, render_template_string
from PIL import Image, ImageFont, ImageDraw, ImageFilter
from pilmoji import Pilmoji
app = Flask(__name__)
# ==========================================
# UI HTML (Mobile Friendly)
# ==========================================
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brat Generator</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f9;
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
width: 100%;
max-width: 400px;
box-sizing: border-box;
}
h2 { text-align: center; margin-top: 0; color: #333; }
textarea {
width: 100%;
height: 100px;
padding: 12px;
box-sizing: border-box;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
resize: none;
margin-bottom: 15px;
}
button {
width: 100%;
background-color: #111;
color: white;
border: none;
padding: 14px;
font-size: 16px;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s;
}
button:hover { background-color: #333; }
.result-container {
margin-top: 20px;
text-align: center;
}
img {
max-width: 100%;
border: 1px solid #eaeaea;
border-radius: 4px;
display: block;
margin: 0 auto;
}
.download-btn {
display: inline-block;
margin-top: 15px;
color: #111;
text-decoration: none;
font-weight: bold;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h2>Brat Generator ⬜</h2>
<textarea id="inputText" placeholder="Ini adalah\ncontoh\nbrat😭">Ini adalah&#10;contoh&#10;brat😭</textarea>
<button onclick="generateImage()">Buat Gambar</button>
<div class="result-container">
<img id="resultImg" src="/generate?text=Ini%20adalah%0Acontoh%0Abrat%F0%9F%98%AD" alt="Hasil Render">
<br>
<a id="downloadLink" class="download-btn" href="/generate?text=Ini%20adalah%0Acontoh%0Abrat%F0%9F%98%AD" download="brat.png">⬇️ Download Gambar</a>
</div>
</div>
<script>
function generateImage() {
const text = document.getElementById('inputText').value;
const encodedText = encodeURIComponent(text);
const url = `/generate?text=${encodedText}`;
document.getElementById('resultImg').src = url;
document.getElementById('downloadLink').href = url;
}
</script>
</body>
</html>
"""
# ==========================================
# Fungsi Pencari Font Fleksibel
# ==========================================
def get_font(size):
# Mencari Arial atau font mirip Arial di berbagai OS
font_paths = [
"/usr/share/fonts/truetype/msttcorefonts/Arial.ttf", # Ubuntu/Linux Arial
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", # Linux Fallback
"C:\\Windows\\Fonts\\arial.ttf", # Windows
"/System/Library/Fonts/Supplemental/Arial.ttf", # Mac OS
]
for path in font_paths:
if os.path.exists(path):
try:
return ImageFont.truetype(path, size)
except:
continue
return ImageFont.load_default()
# ==========================================
# Algoritma Auto-Scaling yang Diperbaiki (Struktur Tetap Terjaga)
# ==========================================
def get_optimal_font_and_text(text, max_width, max_height, line_spacing=10):
# BATASAN UKURAN FONT:
# Dimulai dari 75px (tidak 180px) agar kalimat seperti "Ini adalah"
# tidak terpecah menjadi 2 baris karena font terlalu besar.
max_font_size = 75
min_font_size = 15
# Dummy image untuk menghitung text bounding box
temp_img = Image.new('RGB', (1, 1))
draw = ImageDraw.Draw(temp_img)
for size in range(max_font_size, min_font_size, -1):
font = get_font(size)
lines = []
# Menghormati tombol "Enter" yang diketik user
for paragraph in text.split('\n'):
words = paragraph.split(' ')
current_line = []
for word in words:
test_line = ' '.join(current_line + [word]) if current_line else word
if font.getlength(test_line) <= max_width:
current_line.append(word)
else:
if current_line:
lines.append(' '.join(current_line))
current_line = [word]
else:
lines.append(word)
current_line = []
if current_line:
lines.append(' '.join(current_line))
wrapped_text = '\n'.join(lines)
# Cek apakah blok teks (termasuk spasi antar baris) muat di layar
bbox = draw.multiline_textbbox((0, 0), wrapped_text, font=font, align="center", spacing=line_spacing)
text_w = bbox[2] - bbox[0]
text_h = bbox[3] - bbox[1]
if text_w <= max_width and text_h <= max_height:
return font, wrapped_text
return font, wrapped_text
# ==========================================
# Routes
# ==========================================
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
@app.route('/generate')
def generate():
text = request.args.get('text', 'Ini adalah\ncontoh\nbrat😭').strip()
if not text:
text = "Ini adalah\ncontoh\nbrat😭"
# Konfigurasi Canvas 1:1 (512x512)
img_size = 512
# Padding diperbesar (60px) agar area teks tidak terlalu menempel ke pinggir layar
padding = 60
line_spacing = 15 # Jarak antar baris teks
# Warna Canvas (Putih Murni) & Warna Teks (Abu-abu Gelap, mencegah kontras berlebih)
bg_color = (255, 255, 255)
text_color = (40, 40, 40)
img = Image.new('RGB', (img_size, img_size), color=bg_color)
max_text_width = img_size - (padding * 2)
max_text_height = img_size - (padding * 2)
# Dapatkan font size ideal yang menjaga struktur kalimat
font, wrapped_text = get_optimal_font_and_text(text, max_text_width, max_text_height, line_spacing)
# Rendering Teks dengan Pilmoji
with Pilmoji(img) as pilmoji:
pilmoji.text(
(img_size // 2, img_size // 2),
wrapped_text,
fill=text_color,
font=font,
anchor="mm", # Fix posisi benar-benar di tengah absolut
align="center", # Rata tengah untuk tiap baris
spacing=line_spacing, # Spasi antar baris
emoji_scale_factor=1.05 # Skala emoji sedikit dikecilkan agar seimbang
)
# =============== EFEK BLUR (LOW QUALITY VIBE) =================
# Radius 0.5 memberi efek 'soft' persis kompresi gambar WhatsApp/Meme
img = img.filter(ImageFilter.GaussianBlur(radius=0.5))
# ==============================================================
# Output memori
img_io = io.BytesIO()
img.save(img_io, 'PNG')
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)