textoverimage1 / app.py
kavehtaheri's picture
Update app.py
19de2fe verified
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import arabic_reshaper
from bidi.algorithm import get_display
import requests
from io import BytesIO
import os
# Your COLOR_MAP dictionary remains the same
COLOR_MAP = {
"Black": (0, 0, 0),
"White": (255, 255, 255),
"Gray": (128, 128, 128),
"Red": (255, 0, 0),
"Blue": (0, 0, 255),
}
# --- REVISED AND FINAL FUNCTION ---
def overlay_text_on_image(persian_text, url, upload, username, text_color):
# --- Stage 1: Image Loading (No changes) ---
if url and url.strip():
if not url.startswith(('http://', 'https://')):
url = 'https://' + url
try:
response = requests.get(url)
response.raise_for_status()
img = Image.open(BytesIO(response.content))
except Exception as e:
raise ValueError(f"Failed to load image from URL: {e}")
elif upload:
img = Image.open(upload)
else:
raise ValueError("Please provide either an image URL or upload an image.")
img = img.resize((1080, 1080), Image.LANCZOS)
draw = ImageDraw.Draw(img)
width, height = img.size
# --- Stage 2: Font and Color Setup (No changes) ---
font_path = "Vazir.ttf"
if not os.path.exists(font_path):
print("Warning: Vazirmatn font not found. Falling back to default.")
possible_fonts = [
"Vazir.ttf",
"/usr/share/fonts/truetype/noto/NotoNaskhArabic-Regular.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
]
font_path = next((f for f in possible_fonts if os.path.exists(f)), "DejaVuSans.ttf")
selected_color = COLOR_MAP.get(text_color, (0, 0, 0))
lines = [line for line in persian_text.split('\n') if line.strip()]
reshaped_lines = [arabic_reshaper.reshape(line) for line in lines]
# --- Stage 3: UNIFORM FONT SIZE CALCULATION (No changes in logic) ---
initial_font_size = 80
max_width = width * 0.90
uniform_font_size = initial_font_size
longest_line = ""
if reshaped_lines:
temp_font = ImageFont.truetype(font_path, uniform_font_size)
longest_line = max(reshaped_lines, key=lambda line: draw.textlength(line, font=temp_font))
while uniform_font_size > 20:
font = ImageFont.truetype(font_path, uniform_font_size)
if draw.textlength(longest_line, font=font) <= max_width:
break
uniform_font_size -= 2
final_font = ImageFont.truetype(font_path, uniform_font_size)
# --- Stage 4: Text Block Height Calculation and Centering ---
line_spacing = 20
total_text_height = 0
line_heights = []
for line in reshaped_lines:
# *** THE ONLY CHANGE IS ON THIS LINE ***
# Replaced getbbox with textbbox for older Pillow compatibility
line_bbox = draw.textbbox((0, 0), line, font=final_font)
line_height = line_bbox[3] - line_bbox[1]
line_heights.append(line_height)
total_text_height += line_height
total_text_height += (len(reshaped_lines) - 1) * line_spacing if len(reshaped_lines) > 1 else 0
y_start = (height - total_text_height) / 2
# --- Stage 5: Drawing the Text ---
current_y = y_start
for i, line in enumerate(reshaped_lines):
x_center = width / 2
line_y_center = current_y + line_heights[i] / 2
draw.text(
(x_center, line_y_center),
line,
font=final_font,
fill=selected_color,
anchor="mm"
)
current_y += line_heights[i] + line_spacing
# --- Stage 6: Username Drawing (No changes) ---
username_font_size = 40
username_font = ImageFont.truetype(font_path, username_font_size)
display_username = get_display(arabic_reshaper.reshape(f"@{username}"))
username_x = width / 2
username_y = height - 50
draw.text((username_x, username_y), display_username, font=username_font, fill=selected_color, anchor="mb")
return img
# --- Your Gradio Interface Code (No changes needed here) ---
with gr.Blocks() as demo:
gr.Markdown("# Persian Quote Overlay for Instagram Posts")
gr.Markdown("Paste an image URL from another HF Space or upload an image, enter Persian text (quote, supports multi-line), choose text color, and username. The app overlays the text centered with dynamic font sizing and places the username at the bottom center.")
with gr.Row():
persian_text = gr.Textbox(label="Persian Quote Text (Multi-line supported)", lines=5)
image_input = gr.Textbox(label="Image URL (e.g., from another HF Space)")
username = gr.Textbox(label="Channel Username (e.g., mychannel)")
with gr.Row():
text_color = gr.Dropdown(label="Text Color", choices=list(COLOR_MAP.keys()), value="Black")
image_upload = gr.Image(label="Optional: Upload Image (if not using URL)", type="filepath")
output_image = gr.Image(label="Output Image")
submit_btn = gr.Button("Generate Overlay")
submit_btn.click(overlay_text_on_image,
inputs=[persian_text, image_input, image_upload, username, text_color],
outputs=output_image)
demo.launch()