Some checks failed
Build and Test / build (pull_request) Failing after 19m15s
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.
232 lines
6.9 KiB
Python
Executable File
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()
|