Skip to content

Commit ee95841

Browse files
committed
[dist] Add script to generate icon file from SVG
1 parent 34dd1de commit ee95841

3 files changed

Lines changed: 98 additions & 7 deletions

File tree

dist/generate_ico.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python
2+
"""Generate pyscenedetect.ico from pyscenedetect.svg.
3+
4+
Requires Inkscape (for SVG rasterization) and Pillow (for ICO generation).
5+
"""
6+
7+
import contextlib
8+
import shutil
9+
import subprocess
10+
import sys
11+
import tempfile
12+
from pathlib import Path
13+
14+
from PIL import Image, ImageFilter
15+
16+
# Different raster sizes to include in the ICO file.
17+
SIZES = [16, 24, 32, 48, 64, 128, 256]
18+
19+
# Sharpen smaller sizes to improve ledgibility.
20+
SHARPEN_AMOUNT = {
21+
16: 200,
22+
24: 200,
23+
32: 100,
24+
48: 50,
25+
64: 50,
26+
}
27+
28+
DIST_DIR = Path(__file__).resolve().parent
29+
SVG_PATH = DIST_DIR / "pyscenedetect.svg"
30+
ICO_PATH = DIST_DIR / "pyscenedetect.ico"
31+
32+
33+
def find_inkscape() -> str:
34+
"""Find the Inkscape executable."""
35+
inkscape = shutil.which("inkscape")
36+
if inkscape:
37+
return inkscape
38+
# Common Windows install path
39+
candidate = Path(r"C:\Program Files\Inkscape\bin\inkscape.exe")
40+
if candidate.exists():
41+
return str(candidate)
42+
print("Error: Inkscape not found. Please install it or add it to PATH.", file=sys.stderr)
43+
sys.exit(1)
44+
45+
46+
def render_svg(inkscape: str, svg: Path, output: Path, size: int):
47+
"""Render an SVG to a PNG at the given size using Inkscape."""
48+
subprocess.run(
49+
[inkscape, str(svg), "--export-type=png", f"--export-filename={output}", "-w", str(size), "-h", str(size)],
50+
check=True,
51+
capture_output=True,
52+
)
53+
54+
55+
def render_all_sizes(inkscape: str, work_dir: Path) -> list[Image.Image]:
56+
"""Render the SVG at all icon sizes, applying sharpening where configured."""
57+
images = []
58+
for size in SIZES:
59+
png_path = work_dir / f"icon_{size}.png"
60+
print(f" Rendering {size}x{size}...")
61+
render_svg(inkscape, SVG_PATH, png_path, size)
62+
img = Image.open(png_path).copy()
63+
if size in SHARPEN_AMOUNT:
64+
img = img.filter(ImageFilter.UnsharpMask(radius=0.5, percent=SHARPEN_AMOUNT[size], threshold=0))
65+
print(f" Sharpened {size}x{size} (USM {SHARPEN_AMOUNT[size]}%)")
66+
img.save(png_path)
67+
images.append(img)
68+
return images
69+
70+
71+
def main():
72+
persist_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else None
73+
if persist_dir:
74+
persist_dir.mkdir(parents=True, exist_ok=True)
75+
print(f"Persisting PNGs to: {persist_dir}")
76+
77+
inkscape = find_inkscape()
78+
print(f"Using Inkscape: {inkscape}")
79+
print(f"Input SVG: {SVG_PATH}")
80+
81+
ctx = contextlib.nullcontext(str(persist_dir)) if persist_dir else tempfile.TemporaryDirectory()
82+
with ctx as work:
83+
images = render_all_sizes(inkscape, Path(work))
84+
images[-1].save(ICO_PATH, format="ICO", append_images=images[:-1])
85+
86+
print(f"Output ICO: {ICO_PATH}")
87+
88+
89+
if __name__ == "__main__":
90+
main()

dist/pyscenedetect.ico

20.4 KB
Binary file not shown.

dist/pyscenedetect.svg

Lines changed: 8 additions & 7 deletions
Loading

0 commit comments

Comments
 (0)