#!/usr/bin/env python3 """Background (centered layout) + theme.txt + preview.png""" import argparse import os from psd_tools import PSDImage from PIL import Image, ImageDraw, ImageFont parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('psd', help='layered PSD with the menu background') parser.add_argument('out', help='output directory') parser.add_argument('--bar', default=None, help='selected-row image (default: /sel_bar.png)') parser.add_argument('--font', default='Montserrat-SemiBold.ttf', help='TTF used for the preview text') args = parser.parse_args() SRC = args.psd T = args.out os.makedirs(T, exist_ok=True) KEEP_HEADERS = {'главное меню', 'paravis flash', '© paravis.ru'} DROP_PIXELS = {'shape and color'} # frame of the right "additional menu" psd = PSDImage.open(SRC) def walk(layer): for ch in layer: nm = (ch.name or '').strip().lower() if ch.kind == 'group': walk(ch) elif ch.kind == 'type': if nm not in KEEP_HEADERS: ch.visible = False elif ch.kind == 'pixel': if nm in DROP_PIXELS: ch.visible = False for layer in psd: nm = (layer.name or '') if nm.startswith('ТЕКСТ_БЕЗ_ОБВОДКИ'): layer.visible = False elif nm.startswith('ВЫДЕЛЕНЫЕ'): layer.visible = False else: walk(layer) bg = psd.composite().convert('RGB') bg.save(os.path.join(T, 'background.png')) print('background:', bg.size) # ---------------------------------------------------------------- geometry LEFT, TOP, WIDTH = 202, 100, 620 ITEM_H, SPACING = 46, 4 ROWS = [ ('Super Grub2 (поиск и запуск ОС)', '(1)'), ('Поиск и запуск Windows 7-11', '(2)'), ('Установка Windows 7, 10, 11 x64', '(3)'), ('Ventoy (ISO-образы)', '(4)'), ('Kaspersky Rescue Disk 18', '(5)'), None, ('Windows 10, 11 PE Sergei Strelec', '(6)'), ('Утилиты для диагностики', '(7)'), ('Восстановление Windows MSDaRT', '(8)'), ('Запуск / Установка Linux', '(9)'), None, ('Перезагрузка', '(R)'), ('Выключение', '(S)'), ] HEIGHT = len(ROWS) * (ITEM_H + SPACING) theme = f'''# Paravis Flash - GRUB2 theme (main menu) desktop-image: "background.png" desktop-color: "#e9e9e9" title-text: "" terminal-font: "Unifont Regular 16" + boot_menu {{ left = {LEFT} top = {TOP} width = {WIDTH} height = {HEIGHT} item_font = "Montserrat SemiBold 17" item_color = "#2b3a55" selected_item_color = "#ffffff" item_height = {ITEM_H} item_spacing = {SPACING} item_padding = 26 item_icon_space = 0 icon_width = 0 icon_height = 0 selected_item_pixmap_style = "sel_*.png" scrollbar = false }} ''' open(os.path.join(T, 'theme.txt'), 'w', encoding='utf-8').write(theme) print('theme.txt written') # ---------------------------------------------------------------- preview bar_path = args.bar or os.path.join(T, 'sel_bar.png') bar = Image.open(bar_path).convert('RGBA').resize((WIDTH, ITEM_H)) font = ImageFont.truetype(args.font, 17) img = bg.copy().convert('RGBA') d = ImageDraw.Draw(img) col = max(len(r[0]) for r in ROWS if r) for i, row in enumerate(ROWS): y = TOP + i * (ITEM_H + SPACING) if row is None: continue label, num = row if i == 0: img.alpha_composite(bar, (LEFT, y)) color = (255, 255, 255) if i == 0 else (43, 58, 85) d.text((LEFT + 26, y + ITEM_H / 2), label, font=font, fill=color, anchor='lm') d.text((LEFT + WIDTH - 22, y + ITEM_H / 2), num, font=font, fill=color, anchor='rm') img.convert('RGB').save(os.path.join(T, 'preview.png')) print('preview.png written')