diff options
Diffstat (limited to 'deluge/scripts/create_icons.py')
-rwxr-xr-x | deluge/scripts/create_icons.py | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/deluge/scripts/create_icons.py b/deluge/scripts/create_icons.py new file mode 100755 index 0000000..479505c --- /dev/null +++ b/deluge/scripts/create_icons.py @@ -0,0 +1,201 @@ +#!/usr/bin/python3 +# +# Create Deluge PNG icons from SVG +# +# Required image tools: +# * rsvg-convert +# * convert (ImageMagik) +# * oxipng +# * pngquant +# +import shutil +import subprocess +from dataclasses import dataclass, field +from pathlib import Path + + +@dataclass +class IconPack: + name: str + dir: Path + icon_sizes: list[int] + panel_sizes: list[int] + ico_sizes: list[int] + pixmaps_dir: Path = field(init=False) + theme_dir: Path = field(init=False) + theme_svg: Path = field(init=False) + theme_pngs: dict[int, Path] = field(init=False) + logo_svg: Path = field(init=False) + logo_ico: Path = field(init=False) + logo_png: Path = field(init=False) + + def __post_init__(self): + self.pixmaps_dir = self.dir / 'pixmaps' + self.logo_svg = self.pixmaps_dir / f'{self.name}.svg' + self.logo_ico = self.pixmaps_dir / f'{self.name}.ico' + self.logo_png = self.pixmaps_dir / f'{self.name}.png' + + self.theme_dir = self.dir / 'icons' / 'hicolor' + self.theme_svg = self.theme_dir / 'scalable' / 'apps' / f'{self.name}.svg' + self.theme_pngs = self.create_theme_pngs_paths( + self.name, self.icon_sizes, self.theme_dir + ) + + @staticmethod + def create_theme_pngs_paths(name, icon_sizes, out_dir): + return { + size: out_dir / f'{size}x{size}' / 'apps' / f'{name}.png' + for size in icon_sizes + } + + +@dataclass +class WebIconPack: + name: str + dir: Path + icon_sizes: list[int] + favicon_sizes: list[int] + icons_dir: Path = field(init=False) + touch: Path = field(init=False) + favicon: Path = field(init=False) + + def __post_init__(self): + self.icons_dir = self.dir / 'icons' + self.touch = self.icons_dir / f'{self.name}-apple-180.png' + self.favicon = self.icons_dir / 'favicon.ico' + + +def convert_svg_to_png(svg_file, png_file, size, background_color=None): + rsvg_options = [ + '-w', + str(size), + '-h', + str(size), + '-o', + png_file, + ] + rsvg_options + ['-b', {background_color}] if background_color else [] + + subprocess.run(['rsvg-convert'] + rsvg_options + [svg_file], check=True) + + +def compress_png(png_file): + subprocess.run( + ['pngquant', '--quality=70-95', '--ext', '.png', '--force', png_file], + check=True, + ) + subprocess.run(['oxipng', png_file], check=True) + + +def create_panel_icons(icon_pack, sizes): + for size in sizes: + app_png = icon_pack[size] + panel_png = app_png.with_name(f'{app_png.stem}-panel.png') + shutil.copyfile(app_png, panel_png) + + +def create_hicolor_icons(svg_icon, icon_pack): + """Convert SVG icon to hicolor PNG icons.""" + for size, png_file in icon_pack.items(): + png_file.parent.mkdir(parents=True, exist_ok=True) + convert_svg_to_png(svg_icon, png_file, size) + compress_png(png_file) + + +def create_ico_icon(icon_pack, sizes, ico_file): + infiles = [icon_pack[size] for size in sizes] + ico_file.parent.mkdir(parents=True, exist_ok=True) + + subprocess.run(['convert', *infiles, ico_file], check=True) + + +def create_hicolor_svg(src_svg, dest_svg): + dest_svg.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(src_svg, dest_svg) + + +def create_mini_icons(pixmaps_dir): + pixmap_svgs = pixmaps_dir.glob('*.svg') + + for svg_file in pixmap_svgs: + png_file = pixmaps_dir / f'{svg_file.stem}16.png' + convert_svg_to_png(svg_file, png_file, 16) + compress_png(png_file) + + +def create_logo(deluge_png, pixmap_png): + pixmap_png.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(deluge_png, pixmap_png) + + +def create_web_status_icons(src_dir: Path, dest_dir: Path): + """Web UI status icons from 16px icons.""" + pngs_16px = src_dir.glob('*16.png') + dest_dir.mkdir(parents=True, exist_ok=True) + for path in pngs_16px: + if path.stem.startswith('tracker'): + continue + new_name = path.stem.replace('16', '') + '.png' + shutil.copyfile(path, dest_dir / new_name) + + +def create_touch_icon(svg_file, png_file, size): + """Web icons with background color for Apple or Android""" + png_file.parent.mkdir(parents=True, exist_ok=True) + convert_svg_to_png(svg_file, png_file, size, background_color='#599EEE') + compress_png(png_file) + + +def create_web_icons(app_pngs, sizes, dest_dir): + dest_dir.mkdir(parents=True, exist_ok=True) + for size in sizes: + app_png = app_pngs[size] + web_png = dest_dir / f'{app_png.stem}-{size}.png' + shutil.copyfile(app_png, web_png) + + +def main(): + data_dir = Path.cwd() / 'deluge' / 'ui' / 'data' + if not data_dir.is_dir(): + exit(f'No path to UI data dir: {data_dir}') + + # Create Deluge UI icons + icon_pack_sizes = [16, 22, 24, 32, 36, 48, 64, 72, 96, 128, 192, 256, 512] + panel_icon_sizes = [16, 22, 24] + ico_icon_sizes = [16, 32, 48, 64, 128, 256] + ui_icons = IconPack( + name='deluge', + dir=data_dir, + icon_sizes=icon_pack_sizes, + panel_sizes=panel_icon_sizes, + ico_sizes=ico_icon_sizes, + ) + + # Theme icons for GTK + create_hicolor_icons(ui_icons.logo_svg, ui_icons.theme_pngs) + create_hicolor_svg(ui_icons.logo_svg, ui_icons.theme_svg) + create_mini_icons(ui_icons.pixmaps_dir) + # Panel icon for systray + create_panel_icons(ui_icons.theme_pngs, ui_icons.panel_sizes) + + # Deluge logos + create_ico_icon(ui_icons.theme_pngs, ui_icons.ico_sizes, ui_icons.logo_ico) + create_logo(ui_icons.theme_pngs[48], ui_icons.logo_png) + + # Web UI Icons + web_icon_sizes = [32, 192, 512] + favicon_sizes = [16, 32, 48] + web_icons = WebIconPack( + name='deluge', + dir=data_dir / '..' / 'web', + icon_sizes=web_icon_sizes, + favicon_sizes=favicon_sizes, + ) + create_web_icons(ui_icons.theme_pngs, web_icons.icon_sizes, web_icons.icons_dir) + create_web_status_icons(ui_icons.pixmaps_dir, web_icons.icons_dir) + create_touch_icon(ui_icons.logo_svg, web_icons.touch, 180) + create_ico_icon(ui_icons.theme_pngs, web_icons.favicon_sizes, web_icons.favicon) + + +if __name__ == '__main__': + main() |