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()