#!/usr/bin/env python3 """ Generate Kindred Create splash screen and about images. This script creates branded splash screens with: - Rounded dark rectangle background (Catppuccin Mocha base #1e1e2e) - Kindred logo centered - "Kindred Create" title text - Version string Requirements: pip install Pillow cairosvg Usage: python generate-splash.py [--version VERSION] [--freecad-version FREECAD_VERSION] """ import argparse import os import sys from pathlib import Path try: from PIL import Image, ImageDraw, ImageFont except ImportError: print("Error: Pillow is required. Install with: pip install Pillow") sys.exit(1) try: import cairosvg except ImportError: print("Warning: cairosvg not found. Will try alternative SVG conversion.") cairosvg = None # Catppuccin Mocha colors COLORS = { 'base': '#1e1e2e', 'surface0': '#313244', 'text': '#cdd6f4', 'subtext0': '#a6adc8', 'blue': '#89b4fa', 'lavender': '#b4befe', } def hex_to_rgb(hex_color): """Convert hex color to RGB tuple.""" hex_color = hex_color.lstrip('#') return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) def create_rounded_rectangle(draw, bbox, radius, fill): """Draw a rounded rectangle.""" x1, y1, x2, y2 = bbox draw.rounded_rectangle(bbox, radius=radius, fill=fill) def load_svg_as_image(svg_path, width, height): """Load an SVG file and convert to PIL Image at specified size.""" if cairosvg: import io png_data = cairosvg.svg2png(url=str(svg_path), output_width=width, output_height=height) return Image.open(io.BytesIO(png_data)).convert('RGBA') else: # Fallback: try using inkscape command line import subprocess import tempfile with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp: tmp_path = tmp.name try: subprocess.run([ 'inkscape', '-w', str(width), '-h', str(height), str(svg_path), '-o', tmp_path ], check=True, capture_output=True) img = Image.open(tmp_path).convert('RGBA') return img finally: if os.path.exists(tmp_path): os.unlink(tmp_path) def get_font(size, bold=False): """Get a font, falling back to default if not available.""" font_names = [ '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf' if bold else '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf' if bold else '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', '/usr/share/fonts/TTF/DejaVuSans-Bold.ttf' if bold else '/usr/share/fonts/TTF/DejaVuSans.ttf', ] for font_name in font_names: if os.path.exists(font_name): return ImageFont.truetype(font_name, size) # Fallback to default return ImageFont.load_default() def create_splash(output_path, logo_path, width, height, version, freecad_version, scale=1): """Create a splash screen image.""" # Scale dimensions w = int(width * scale) h = int(height * scale) radius = int(16 * scale) padding = int(30 * scale) # Create image with transparent background img = Image.new('RGBA', (w, h), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) # Draw rounded rectangle background bg_color = hex_to_rgb(COLORS['base']) create_rounded_rectangle(draw, (padding, padding, w - padding, h - padding), radius, bg_color) # Load and place logo logo_max_width = int(300 * scale) logo_max_height = int(150 * scale) try: logo = load_svg_as_image(logo_path, logo_max_width, logo_max_height) # Center logo horizontally, place in upper portion logo_x = (w - logo.width) // 2 logo_y = int(80 * scale) img.paste(logo, (logo_x, logo_y), logo) except Exception as e: print(f"Warning: Could not load logo: {e}") logo_y = int(80 * scale) # Draw "Kindred Create" title title_font = get_font(int(24 * scale), bold=True) title = "Kindred Create" title_bbox = draw.textbbox((0, 0), title, font=title_font) title_width = title_bbox[2] - title_bbox[0] title_x = (w - title_width) // 2 title_y = int(250 * scale) draw.text((title_x, title_y), title, fill=hex_to_rgb(COLORS['text']), font=title_font) # Draw version string version_font = get_font(int(12 * scale)) version_str = f"v{version} (FreeCAD {freecad_version})" version_bbox = draw.textbbox((0, 0), version_str, font=version_font) version_width = version_bbox[2] - version_bbox[0] version_x = (w - version_width) // 2 version_y = title_y + int(35 * scale) draw.text((version_x, version_y), version_str, fill=hex_to_rgb(COLORS['subtext0']), font=version_font) # Save img.save(output_path, 'PNG') print(f"Created: {output_path}") def create_about(output_path, logo_path, width, height, scale=1): """Create an about dialog image.""" # Scale dimensions w = int(width * scale) h = int(height * scale) # Create image img = Image.new('RGBA', (w, h), hex_to_rgb(COLORS['base']) + (255,)) draw = ImageDraw.Draw(img) # Load and place logo logo_max_width = int(200 * scale) logo_max_height = int(100 * scale) try: logo = load_svg_as_image(logo_path, logo_max_width, logo_max_height) logo_x = (w - logo.width) // 2 logo_y = int(30 * scale) img.paste(logo, (logo_x, logo_y), logo) except Exception as e: print(f"Warning: Could not load logo: {e}") # Save img.save(output_path, 'PNG') print(f"Created: {output_path}") def main(): parser = argparse.ArgumentParser(description='Generate Kindred Create splash screens') parser.add_argument('--version', default='0.1.0', help='Kindred Create version') parser.add_argument('--freecad-version', default='1.0.0', help='FreeCAD version') args = parser.parse_args() script_dir = Path(__file__).parent logo_path = script_dir / 'kindred-logo.svg' icons_dir = script_dir.parent.parent / 'src' / 'Gui' / 'Icons' if not logo_path.exists(): print(f"Error: Logo not found at {logo_path}") sys.exit(1) # Create splash screens (600x400 as per spec) create_splash( icons_dir / 'kindredcreatesplash.png', logo_path, 600, 400, args.version, args.freecad_version, scale=1 ) # Create 2x version for HiDPI create_splash( icons_dir / 'kindredcreatesplash_2x.png', logo_path, 600, 400, args.version, args.freecad_version, scale=2 ) # Create about image create_about( icons_dir / 'kindredcreateabout.png', logo_path, 400, 200, scale=1 ) print("\nSplash screen generation complete!") if __name__ == '__main__': main()