1#!/usr/bin/env python3
2"""
3Generate arch-logo.png for Limine bootloader wallpaper.
4
5Renders the Arch ASCII logo with the same cyan→slate-blue gradient used in
6hyprlock.conf, on a deep navy background. Output is sized for 1920x1080.
7
8Usage: python3 gen-limine-logo.py [--out path/to/arch-logo.png]
9"""
10
11import sys
12import os
13import argparse
14from PIL import Image, ImageDraw, ImageFont
15
16LOGO_LINES = [
17 " -`",
18 " .o+`",
19 " `ooo/",
20 " `+oooo:",
21 " `+oooooo:",
22 " -+oooooo+:",
23 " `/:-:++oooo+:",
24 " `/++++/+++++++:",
25 " `/++++++++++++++:",
26 " `/+++ooooooooooooo/`",
27 " ./ooosssso++osssssso+`",
28 " .oossssso-````/ossssss+`",
29 " -osssssso. :ssssssso.",
30 " :osssssss/ osssso+++.",
31 " /ossssssss/ +ssssooo/-",
32 " `/ossssso+/:- -:/+osssso+-",
33 " `+sso+:-` `.-/+oso:",
34 " `++:. `-/+/",
35 " .` `",
36]
37
38# Gradient: cyan at top → slate-blue at bottom (matching hyprlock.conf)
39GRADIENT_COLORS = [
40 (0, 212, 255), # line 0 - bright cyan
41 (0, 212, 255), # line 1
42 (0, 212, 255), # line 2
43 (0, 212, 255), # line 3
44 (0, 196, 239), # line 4
45 (0, 180, 223), # line 5
46 (34, 164, 207), # line 6
47 (68, 148, 191), # line 7
48 (85, 136, 204), # line 8
49 (85, 128, 192), # line 9
50 (80, 120, 180), # line 10
51 (74, 123, 167), # line 11
52 (74, 123, 167), # line 12
53 (90, 122, 154), # line 13
54 (96, 122, 148), # line 14
55 (101, 122, 144), # line 15
56 (106, 122, 140), # line 16
57 (110, 122, 136), # line 17
58 (112, 128, 144), # line 18
59]
60
61# Background: deep navy from hyprlock
62BG_COLOR = (5, 10, 20)
63
64FONT_SIZE = 36
65LINE_SPACING = 42 # px between lines
66PADDING = 40 # px padding around the logo content
67
68FONT_PATH = "/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Thin.ttf"
69FONT_PATH_FALLBACK = "/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Regular.ttf"
70
71
72def main():
73 parser = argparse.ArgumentParser()
74 parser.add_argument("--out", default=os.path.join(os.path.dirname(__file__), "arch-logo.png"))
75 args = parser.parse_args()
76
77 # Load font
78 font_path = FONT_PATH if os.path.exists(FONT_PATH) else FONT_PATH_FALLBACK
79 try:
80 font = ImageFont.truetype(font_path, FONT_SIZE)
81 except Exception as e:
82 print(f"Failed to load font {font_path}: {e}", file=sys.stderr)
83 sys.exit(1)
84
85 # Measure block dimensions using a throw-away draw surface
86 tmp = Image.new("RGB", (4000, 2000), BG_COLOR)
87 tmp_draw = ImageDraw.Draw(tmp)
88 widths = [tmp_draw.textbbox((0, 0), line, font=font)[2] for line in LOGO_LINES]
89 max_w = max(widths)
90 total_h = len(LOGO_LINES) * LINE_SPACING
91
92 # Canvas is just the logo + padding — Limine centers it on screen
93 img_w = int(max_w) + PADDING * 2
94 img_h = int(total_h) + PADDING * 2
95
96 img = Image.new("RGB", (img_w, img_h), BG_COLOR)
97 draw = ImageDraw.Draw(img)
98
99 for i, line in enumerate(LOGO_LINES):
100 color = GRADIENT_COLORS[i] if i < len(GRADIENT_COLORS) else GRADIENT_COLORS[-1]
101 y = PADDING + i * LINE_SPACING
102 draw.text((PADDING, y), line, font=font, fill=color)
103
104 # Quantize to a small palette (no dithering) — cuts file size dramatically
105 # while preserving the gradient colors faithfully at boot resolution.
106 img = img.quantize(colors=32, dither=Image.Dither.NONE)
107 img.save(args.out, "PNG", optimize=True)
108 size_kb = os.path.getsize(args.out) / 1024
109 print(f"Generated: {args.out} ({img_w}x{img_h}, {size_kb:.1f}K)")
110
111
112if __name__ == "__main__":
113 main()