Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import io | |
| import os | |
| from PIL import Image, ImageDraw, ImageFont | |
| from pathlib import Path | |
| import gdown | |
| # Create fonts directory if it doesn't exist | |
| os.makedirs("fonts", exist_ok=True) | |
| # Dictionary of fonts to download - add Persian fonts here | |
| fonts = { | |
| "Arial Bold": "https://github.com/matomo-org/travis-scripts/raw/master/fonts/Arial.ttf", | |
| "Vazir": "https://github.com/rastikerdar/vazir-font/raw/master/dist/Vazir.ttf", | |
| "Sahel": "https://github.com/rastikerdar/sahel-font/raw/master/dist/Sahel.ttf", | |
| "Samim": "https://github.com/rastikerdar/samim-font/raw/master/dist/Samim.ttf" | |
| } | |
| # Download fonts | |
| for font_name, font_url in fonts.items(): | |
| font_filename = font_name.replace(" ", "-") + ".ttf" | |
| font_path = Path(f"fonts/{font_filename}") | |
| if not font_path.exists(): | |
| try: | |
| font_data = requests.get(font_url).content | |
| with open(font_path, "wb") as f: | |
| f.write(font_data) | |
| print(f"Downloaded font: {font_name}") | |
| except Exception as e: | |
| print(f"Error downloading font {font_name}: {e}") | |
| # Fallback to system fonts if needed | |
| system_fonts = [ | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", | |
| "/usr/share/fonts/TTF/DejaVuSans-Bold.ttf", | |
| "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf" | |
| ] | |
| def download_image_from_gdrive(url): | |
| """Download image from Google Drive""" | |
| try: | |
| # Extract file ID from Google Drive URL | |
| file_id = url.split("/d/")[1].split("/view")[0] | |
| output = "background_image.jpg" | |
| # Download the file | |
| gdown.download(f"https://drive.google.com/uc?id={file_id}", output, quiet=False) | |
| # Open the image | |
| img = Image.open(output) | |
| return img | |
| except Exception as e: | |
| print(f"Error downloading image: {e}") | |
| # Return a default image if download fails | |
| return Image.new('RGB', (1200, 800), (255, 239, 188)) | |
| def wrap_text(text, font, max_width): | |
| """Wrap text to fit within a given width""" | |
| lines = [] | |
| paragraphs = text.split('\n') | |
| for paragraph in paragraphs: | |
| words = paragraph.split() | |
| current_line = [] | |
| current_width = 0 | |
| for word in words: | |
| word_width = font.getbbox(f"{word} ")[2] | |
| if current_width + word_width <= max_width: | |
| current_line.append(word) | |
| current_width += word_width | |
| else: | |
| if current_line: | |
| lines.append(' '.join(current_line)) | |
| current_line = [word] | |
| current_width = font.getbbox(f"{word} ")[2] | |
| if current_line: | |
| lines.append(' '.join(current_line)) | |
| return lines | |
| def create_quote_image(image_url, quote_text, title_text="", username="", | |
| font_type="Vazir", quote_font_size=60, title_font_size=70, | |
| username_font_size=40, text_color="#333333", | |
| text_position="center", title_position="top", | |
| username_position="bottom"): | |
| """Create an image with text overlays""" | |
| # Download image from Google Drive | |
| img = download_image_from_gdrive(image_url) | |
| width, height = img.size | |
| draw = ImageDraw.Draw(img) | |
| # Parse text color | |
| try: | |
| if text_color.startswith("#"): | |
| r = int(text_color[1:3], 16) | |
| g = int(text_color[3:5], 16) | |
| b = int(text_color[5:7], 16) | |
| text_color = (r, g, b) | |
| except: | |
| text_color = (51, 51, 51) # Default to dark gray | |
| # Load fonts | |
| try: | |
| font_filename = font_type.replace(" ", "-") + ".ttf" | |
| font_path = f"fonts/{font_filename}" | |
| quote_font = ImageFont.truetype(font_path, quote_font_size) | |
| title_font = ImageFont.truetype(font_path, title_font_size) | |
| username_font = ImageFont.truetype(font_path, username_font_size) | |
| except Exception as e: | |
| print(f"Error loading font: {e}") | |
| # Try system fonts as fallback | |
| for system_font in system_fonts: | |
| if os.path.exists(system_font): | |
| quote_font = ImageFont.truetype(system_font, quote_font_size) | |
| title_font = ImageFont.truetype(system_font, title_font_size) | |
| username_font = ImageFont.truetype(system_font, username_font_size) | |
| break | |
| else: | |
| quote_font = ImageFont.load_default() | |
| title_font = ImageFont.load_default() | |
| username_font = ImageFont.load_default() | |
| # Add title if provided | |
| if title_text: | |
| max_title_width = width * 0.9 | |
| title_lines = wrap_text(title_text, title_font, max_title_width) | |
| title_height = len(title_lines) * (title_font_size * 1.2) | |
| if title_position == "top": | |
| title_y = height * 0.05 # 5% from top | |
| else: | |
| title_y = (height - title_height) / 2 # Center | |
| for i, line in enumerate(title_lines): | |
| line_width = title_font.getbbox(line)[2] | |
| x = (width - line_width) / 2 | |
| y = title_y + i * (title_font_size * 1.2) | |
| draw.text((x, y), line, fill=text_color, font=title_font) | |
| # Add main quote text | |
| max_text_width = width * 0.8 | |
| quote_lines = wrap_text(quote_text, quote_font, max_text_width) | |
| quote_height = len(quote_lines) * (quote_font_size * 1.2) | |
| if text_position == "center": | |
| quote_y = (height - quote_height) / 2 | |
| elif text_position == "top": | |
| quote_y = height * 0.05 | |
| if title_text and title_position == "top": | |
| quote_y += title_height + height * 0.05 # Add space after title | |
| else: # bottom | |
| quote_y = height - quote_height - height * 0.15 | |
| for i, line in enumerate(quote_lines): | |
| line_width = quote_font.getbbox(line)[2] | |
| x = (width - line_width) / 2 | |
| y = quote_y + i * (quote_font_size * 1.2) | |
| draw.text((x, y), line, fill=text_color, font=quote_font) | |
| # Add username/author if provided | |
| if username: | |
| if username_position == "bottom": | |
| username_y = height - username_font_size * 2 | |
| elif username_position == "top": | |
| username_y = height * 0.05 | |
| if title_text and title_position == "top": | |
| username_y += title_height + height * 0.03 | |
| else: # After quote | |
| username_y = quote_y + quote_height + username_font_size | |
| username_width = username_font.getbbox(username)[2] | |
| username_x = (width - username_width) / 2 | |
| draw.text((username_x, username_y), username, fill=text_color, font=username_font) | |
| return img | |
| def generate_quote_image(image_url, quote_text, title_text, username, | |
| font_type, quote_font_size, title_font_size, | |
| username_font_size, text_color, text_position, | |
| title_position, username_position): | |
| """Generate a quote image with all customizations""" | |
| print("Start processing") | |
| # Validate inputs | |
| if not image_url or image_url.strip() == "": | |
| return None, "Please provide a valid Google Drive image URL" | |
| if not quote_text or quote_text.strip() == "": | |
| quote_text = "Please enter a quote to generate an image." | |
| # Create image | |
| try: | |
| image = create_quote_image( | |
| image_url=image_url, | |
| quote_text=quote_text, | |
| title_text=title_text, | |
| username=username, | |
| font_type=font_type, | |
| quote_font_size=quote_font_size, | |
| title_font_size=title_font_size, | |
| username_font_size=username_font_size, | |
| text_color=text_color, | |
| text_position=text_position, | |
| title_position=title_position, | |
| username_position=username_position | |
| ) | |
| print("Processing complete") | |
| return image, "Image generated successfully!" | |
| except Exception as e: | |
| print(f"Error generating image: {e}") | |
| return None, f"Error: {str(e)}" | |
| # Create Gradio interface | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Custom Quote Image Generator") | |
| gr.Markdown(""" | |
| This tool generates beautiful quote images using your own background image from Google Drive. | |
| Enter your quote, customize the text properties, and click 'Generate Image'. | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| image_url = gr.Textbox( | |
| label="Google Drive Image URL", | |
| placeholder="https://drive.google.com/file/d/12QImAkg9A5_yRr-YwjJiFNznYF9ymybS/view?usp=drive_link" | |
| ) | |
| with gr.Tabs(): | |
| with gr.TabItem("Main Quote"): | |
| quote_input = gr.Textbox( | |
| label="Enter your quote", | |
| placeholder="May the tears you cried in 2024 water the seeds you plant for 2025.", | |
| lines=3 | |
| ) | |
| quote_font_size = gr.Slider( | |
| label="Quote Font Size", | |
| minimum=20, | |
| maximum=120, | |
| value=60, | |
| step=1 | |
| ) | |
| text_position = gr.Radio( | |
| label="Quote Position", | |
| choices=["center", "top", "bottom"], | |
| value="center" | |
| ) | |
| with gr.TabItem("Title"): | |
| title_input = gr.Textbox( | |
| label="Title (optional)", | |
| placeholder="Inspirational Quote", | |
| lines=1 | |
| ) | |
| title_font_size = gr.Slider( | |
| label="Title Font Size", | |
| minimum=20, | |
| maximum=120, | |
| value=70, | |
| step=1 | |
| ) | |
| title_position = gr.Radio( | |
| label="Title Position", | |
| choices=["top", "center"], | |
| value="top" | |
| ) | |
| with gr.TabItem("Author/Username"): | |
| username_input = gr.Textbox( | |
| label="Author/Username (optional)", | |
| placeholder="@aestheticdailyquote" | |
| ) | |
| username_font_size = gr.Slider( | |
| label="Author Font Size", | |
| minimum=10, | |
| maximum=80, | |
| value=40, | |
| step=1 | |
| ) | |
| username_position = gr.Radio( | |
| label="Author Position", | |
| choices=["bottom", "top"], | |
| value="bottom" | |
| ) | |
| font_type = gr.Dropdown( | |
| label="Font Type", | |
| choices=list(fonts.keys()), | |
| value="Vazir" | |
| ) | |
| text_color = gr.ColorPicker( | |
| label="Text Color", | |
| value="#333333" | |
| ) | |
| generate_btn = gr.Button("Generate Image", variant="primary") | |
| with gr.Column(): | |
| image_output = gr.Image(type="pil", label="Generated Quote Image") | |
| status_output = gr.Textbox(label="Status") | |
| generate_btn.click( | |
| fn=generate_quote_image, | |
| inputs=[ | |
| image_url, quote_input, title_input, username_input, | |
| font_type, quote_font_size, title_font_size, | |
| username_font_size, text_color, text_position, | |
| title_position, username_position | |
| ], | |
| outputs=[image_output, status_output] | |
| ) | |
| # Example for demonstration | |
| gr.Examples( | |
| examples=[ | |
| [ | |
| "https://drive.google.com/file/d/12QImAkg9A5_yRr-YwjJiFNznYF9ymybS/view?usp=drive_link", | |
| "May the tears you cried in 2024 water the seeds you plant for 2025.", | |
| "Inspirational Quote", | |
| "@aestheticdailyquote", | |
| "Vazir", | |
| 60, 70, 40, | |
| "#333333", | |
| "center", | |
| "top", | |
| "bottom" | |
| ] | |
| ], | |
| inputs=[ | |
| image_url, quote_input, title_input, username_input, | |
| font_type, quote_font_size, title_font_size, | |
| username_font_size, text_color, text_position, | |
| title_position, username_position | |
| ], | |
| outputs=[image_output, status_output], | |
| fn=generate_quote_image | |
| ) | |
| gr.Markdown(""" | |
| ## How to Use | |
| 1. Paste a Google Drive image URL (make sure it's publicly accessible) | |
| 2. Enter your quote, title, and author information | |
| 3. Customize font type, size, color, and positions | |
| 4. Click 'Generate Image' | |
| ## Supported Persian Fonts | |
| - Vazir | |
| - Sahel | |
| - Samim | |
| """) | |
| # Launch the app | |
| demo.launch() | |