11#!/usr/bin/env python
2- """Generate pyscenedetect.ico from pyscenedetect.svg .
2+ """Generate pyscenedetect.ico and logo PNGs from SVG sources .
33
44Requires Inkscape (for SVG rasterization) and Pillow (for ICO generation).
55"""
1010import sys
1111import tempfile
1212from pathlib import Path
13+ from typing import NamedTuple
1314
1415from PIL import Image , ImageFilter
1516
17+
18+ class LogoOutput (NamedTuple ):
19+ path : Path
20+ width : int
21+ height : int
22+ source : Path
23+
1624# Colors matching the SVG design
1725BG = (224 , 232 , 240 , 255 ) # #e0e8f0
1826FG = (42 , 53 , 69 , 255 ) # #2a3545
3139SHARPEN_RADIUS = 0.5
3240
3341DIST_DIR = Path (__file__ ).resolve ().parent
42+ REPO_DIR = DIST_DIR .parent
3443LOGO_DIR = DIST_DIR / "logo"
3544ICO_PATH = DIST_DIR / "pyscenedetect.ico"
3645
46+ LOGO_SVG = LOGO_DIR / "pyscenedetect-logo.svg"
47+ LOGO_BG_SVG = LOGO_DIR / "pyscenedetect-logo-bg.svg"
48+
49+ # Heights match the natural SVG aspect ratio (1024x480).
50+ # _small outputs use the -bg variant (background included).
51+ LOGO_OUTPUTS : list [LogoOutput ] = [
52+ LogoOutput (REPO_DIR / "docs" / "_static" / "pyscenedetect_logo.png" , 900 , 422 , LOGO_SVG ),
53+ LogoOutput (REPO_DIR / "docs" / "_static" / "pyscenedetect_logo_small.png" , 300 , 141 , LOGO_BG_SVG ),
54+ LogoOutput (REPO_DIR / "website" / "pages" / "img" / "pyscenedetect_logo.png" , 640 , 300 , LOGO_BG_SVG ),
55+ LogoOutput (REPO_DIR / "website" / "pages" / "img" / "pyscenedetect_logo_small.png" , 462 , 217 , LOGO_SVG ),
56+ ]
57+
3758SVG_FOR_SIZE : dict [int , Path ] = {
3859 24 : LOGO_DIR / "pyscenedetect-24.svg" ,
3960 32 : LOGO_DIR / "pyscenedetect-32.svg" ,
@@ -88,15 +109,24 @@ def find_inkscape() -> str:
88109 sys .exit (1 )
89110
90111
91- def render_svg (inkscape : str , svg : Path , output : Path , size : int ):
92- """Render an SVG to a PNG at the given size using Inkscape."""
112+ def render_svg (inkscape : str , svg : Path , output : Path , width : int , height : int ):
113+ """Render an SVG to a PNG at the given dimensions using Inkscape."""
93114 subprocess .run (
94- [inkscape , str (svg ), "--export-type=png" , f"--export-filename={ output } " , "-w" , str (size ), "-h" , str (size )],
115+ [inkscape , str (svg ), "--export-type=png" , f"--export-filename={ output } " , "-w" , str (width ), "-h" , str (height )],
95116 check = True ,
96117 capture_output = True ,
97118 )
98119
99120
121+ def render_logos (inkscape : str ):
122+ """Render the logo SVG to all required PNG outputs."""
123+ print ("Rendering logo PNGs..." )
124+ for entry in LOGO_OUTPUTS :
125+ print (f" { entry .path .relative_to (REPO_DIR )} ({ entry .width } x{ entry .height } ) [source: { entry .source .name } ]..." )
126+ render_svg (inkscape , entry .source , entry .path , entry .width , entry .height )
127+ print (f" Done ({ len (LOGO_OUTPUTS )} files)." )
128+
129+
100130def render_all_sizes (inkscape : str , work_dir : Path ) -> list [Image .Image ]:
101131 """Render the SVG at all icon sizes, applying sharpening where configured."""
102132 images = []
@@ -109,7 +139,7 @@ def render_all_sizes(inkscape: str, work_dir: Path) -> list[Image.Image]:
109139 else :
110140 svg_path = SVG_FOR_SIZE [size ]
111141 print (f" Rendering { size } x{ size } using { svg_path .name } ..." )
112- render_svg (inkscape , svg_path , png_path , size )
142+ render_svg (inkscape , svg_path , png_path , size , size )
113143 img = Image .open (png_path ).copy ()
114144 if size in SHARPEN_AMOUNT :
115145 img = img .filter (ImageFilter .UnsharpMask (radius = SHARPEN_RADIUS , percent = SHARPEN_AMOUNT [size ], threshold = 0 ))
@@ -135,6 +165,7 @@ def main():
135165 images [- 1 ].save (ICO_PATH , format = "ICO" , append_images = images [:- 1 ])
136166
137167 print (f"Output ICO: { ICO_PATH } " )
168+ render_logos (inkscape )
138169
139170
140171if __name__ == "__main__" :
0 commit comments