Files
create/resources/branding/generate-splash.py
forbes-0023 f71decca08
Some checks failed
Build and Test / build (pull_request) Failing after 19m15s
fix(splash): skip runtime title/version draw and use mantle background
The splash PNG already has the title and version baked in by
generate-splash.py, but SplashScreen::splashImage() was drawing them
again at position (6,75) which fell outside the rounded background
rectangle (starts at 30,30). Skip runtime text rendering for Kindred
Create while keeping the dev build warning.

Also switch the splash background from Catppuccin Mocha base (#1e1e2e)
to mantle (#181825) for a darker appearance and regenerate assets.
2026-02-12 10:08:23 -06:00

232 lines
6.9 KiB
Python
Executable File

#!/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 = {
"mantle": "#181825",
"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["mantle"])
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()