From cbb3660a17f6dd8fa15859abec2f7f3521c9bca2 Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Wed, 26 Mar 2025 06:47:13 -1000 Subject: [PATCH] feat: Convert celebration image generator to SVG format --- generate_celebration_image.py | 204 +++++++++++++++++----------------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/generate_celebration_image.py b/generate_celebration_image.py index 1cc88364a..dee6686ac 100644 --- a/generate_celebration_image.py +++ b/generate_celebration_image.py @@ -1,98 +1,24 @@ import os - -from PIL import Image, ImageDraw, ImageFont +import base64 # --- Configuration --- -WIDTH = 1200 # Twitter card image width -HEIGHT = 675 # Twitter card image height +WIDTH = 1200 # Image width +HEIGHT = 675 # Image height BG_COLOR = "#282a36" # Aider code background color PRIMARY_COLOR = "#14b014" # Aider terminal green TEXT_COLOR = "#FFFFFF" # White for contrast FONT_SIZE_LARGE = 110 FONT_SIZE_MEDIUM = 55 FONT_SIZE_SMALL = 30 -OUTPUT_FILENAME = "aider_30k_stars_celebration.png" +OUTPUT_FILENAME = "aider_30k_stars_celebration.svg" + +# Font families - SVG will try these in order. Ensure viewers have suitable fonts. +FONT_FAMILY_BOLD = "'DejaVu Sans Bold', 'Arial Bold', 'Helvetica Bold', sans-serif-bold, sans-serif" +FONT_FAMILY_REGULAR = "'DejaVu Sans', 'Arial', 'Helvetica', sans-serif" # --- Paths (Adjust if needed) --- # Assumes the script is run from the root of the aider repo -LOGO_PATH = "aider/website/assets/logo.png" # NEEDS TO BE PNG, not SVG! -# Try to find a suitable bold font. Adjust path if necessary, or install one. -# Common locations/names: -FONT_PATHS_BOLD = [ - "DejaVuSans-Bold.ttf", # Common on Linux - "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", - "Arial Bold.ttf", # Common on Windows/macOS - "/System/Library/Fonts/Supplemental/Arial Bold.ttf", - "arialbd.ttf", -] -FONT_PATHS_REGULAR = [ - "DejaVuSans.ttf", - "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", - "Arial.ttf", - "/System/Library/Fonts/Supplemental/Arial.ttf", - "arial.ttf", -] - - -# --- Helper Function to Find Font --- -def find_font(font_paths, default_size): - for path in font_paths: - try: - return ImageFont.truetype(path, default_size) - except IOError: - continue # Try next path - print(f"Warning: Could not find any of the preferred fonts: {font_paths}. Using default.") - # Pillow's default font doesn't support sizing well, return None to handle later - return None - - -# --- Load Fonts --- -font_large = find_font(FONT_PATHS_BOLD, FONT_SIZE_LARGE) -font_medium = find_font(FONT_PATHS_BOLD, FONT_SIZE_MEDIUM) -font_small = find_font(FONT_PATHS_REGULAR, FONT_SIZE_SMALL) - -# Use Pillow's basic default only if absolutely necessary (will look bad) -if not font_large: - font_large = ImageFont.load_default() -if not font_medium: - font_medium = ImageFont.load_default() -if not font_small: - font_small = ImageFont.load_default() - - -# --- Create Base Image --- -image = Image.new("RGB", (WIDTH, HEIGHT), color=BG_COLOR) -draw = ImageDraw.Draw(image) - -# --- Load and Place Logo (Optional) --- -logo_img = None -logo_height = 0 -logo_y_pos = HEIGHT * 0.15 # Start logo about 15% down -try: - if os.path.exists(LOGO_PATH): - logo_img = Image.open(LOGO_PATH) - # Resize logo to fit nicely, maintaining aspect ratio - max_logo_h = 120 - if logo_img.height > max_logo_h: - ratio = max_logo_h / logo_img.height - new_w = int(logo_img.width * ratio) - logo_img = logo_img.resize((new_w, max_logo_h), Image.Resampling.LANCZOS) - - logo_height = logo_img.height - logo_x = (WIDTH - logo_img.width) // 2 - # Paste logo, handling transparency if PNG has alpha channel - if logo_img.mode == "RGBA": - image.paste(logo_img, (logo_x, int(logo_y_pos)), logo_img) - else: - image.paste(logo_img, (logo_x, int(logo_y_pos))) - print(f"Logo loaded from {LOGO_PATH}") - else: - print(f"Info: Logo not found at {LOGO_PATH}, skipping logo.") - logo_y_pos = HEIGHT * 0.1 # Start text higher if no logo -except Exception as e: - print(f"Warning: Could not load or process logo: {e}") - logo_img = None - logo_y_pos = HEIGHT * 0.1 # Start text higher if no logo +LOGO_PATH = "aider/website/assets/logo.svg" # --- Text Content --- line1 = "Thank You!" @@ -100,32 +26,108 @@ line2 = "30,000" line3 = "GitHub Stars" line4 = "github.com/Aider-AI/aider" -# --- Calculate Text Positions --- +# --- Load and Encode Logo --- +logo_data_uri = None +logo_width = 200 # Default width from logo.svg +logo_height = 60 # Default height from logo.svg +try: + if os.path.exists(LOGO_PATH): + with open(LOGO_PATH, "rb") as f: + logo_content = f.read() + encoded_logo = base64.b64encode(logo_content).decode("utf-8") + logo_data_uri = f"data:image/svg+xml;base64,{encoded_logo}" + print(f"Logo loaded and encoded from {LOGO_PATH}") + # Optional: Could parse SVG to get width/height, but using defaults is simpler + else: + print(f"Warning: Logo not found at {LOGO_PATH}, skipping logo.") +except Exception as e: + print(f"Warning: Could not load or encode logo: {e}") + +# --- Calculate Positions --- center_x = WIDTH / 2 -current_y = logo_y_pos + logo_height + 40 # Start text below logo (or top if no logo) +logo_y_pos = HEIGHT * 0.15 +logo_x_pos = center_x - (logo_width / 2) -# --- Draw Text --- -# Line 1: "Thank You!" -draw.text((center_x, current_y), line1, fill=TEXT_COLOR, font=font_medium, anchor="mt") +# Adjust text start based on whether logo is present +text_start_y = logo_y_pos + logo_height + 40 if logo_data_uri else HEIGHT * 0.2 + +current_y = text_start_y + +# Calculate Y positions for each line (using dominant-baseline="middle" for vertical centering) +line1_y = current_y + FONT_SIZE_MEDIUM / 2 current_y += FONT_SIZE_MEDIUM + 30 - -# Line 2: "30,000" -draw.text((center_x, current_y), line2, fill=PRIMARY_COLOR, font=font_large, anchor="mt") +line2_y = current_y + FONT_SIZE_LARGE / 2 current_y += FONT_SIZE_LARGE + 15 - -# Line 3: "GitHub Stars" -draw.text((center_x, current_y), line3, fill=TEXT_COLOR, font=font_medium, anchor="mt") +line3_y = current_y + FONT_SIZE_MEDIUM / 2 current_y += FONT_SIZE_MEDIUM + 60 +line4_y = HEIGHT - FONT_SIZE_SMALL - 30 + FONT_SIZE_SMALL / 2 # Position near bottom -# Line 4: Repo URL (smaller at bottom) -draw.text( - (center_x, HEIGHT - FONT_SIZE_SMALL - 30), line4, fill=TEXT_COLOR, font=font_small, anchor="mb" +# --- Generate SVG Content --- +svg_elements = [] + +# Background +svg_elements.append(f'') + +# Logo +if logo_data_uri: + svg_elements.append( + f'' + ) + +# Text Lines +svg_elements.append( + f'{line1}' +) +svg_elements.append( + f'{line2}' +) +svg_elements.append( + f'{line3}' +) +svg_elements.append( + f'{line4}' ) +# Combine into final SVG +svg_content = f"""\ + + + {''.join(svg_elements)} + +""" + +# --- Save SVG Image --- try: - image.save(OUTPUT_FILENAME) - print(f"Celebration image saved as '{OUTPUT_FILENAME}'") + with open(OUTPUT_FILENAME, "w", encoding="utf-8") as f: + f.write(svg_content) + print(f"Celebration SVG image saved as '{OUTPUT_FILENAME}'") except Exception as e: - print(f"Error saving image: {e}") + print(f"Error saving SVG image: {e}") + +# --- Define Font Families Used in Text Elements --- +# These need to match the font-family attributes used in the tags +FONT_FAMILY_MEDIUM = FONT_FAMILY_BOLD # Using Bold for Medium as per original Pillow logic attempt