generated from exteraGramPlugins/pluginBase
[no bump] Move technical docs to docs/, add icon and PNG export tool
Publish plugin / publish (push) Successful in 4s
Publish plugin / publish (push) Successful in 4s
README now carries the icon, TL;DR, description and features; the technical sections live in docs/. tools/icon_png.py renders images/icon.svg to a square PNG (512x512 by default, --resolution N). plugin.json points at the plugin's own icon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2e4075a03
commit
b86cc8a1c8
9 files changed
+412
-218
No files matched your search
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Renders images/icon.svg to a square PNG (512x512 by default).
|
||||
|
||||
Stdlib only: the actual rasterising is delegated to the first renderer found,
|
||||
in this order: the cairosvg Python package, rsvg-convert (librsvg),
|
||||
ImageMagick (magick/convert), Inkscape. Install any one of them.
|
||||
|
||||
Usage:
|
||||
tools/icon_png.py # images/icon.svg -> images/icon.png, 512x512
|
||||
tools/icon_png.py --resolution 1024 # 1024x1024
|
||||
tools/icon_png.py other.svg -o out.png # explicit input/output
|
||||
|
||||
The output is always PNG, always <resolution>x<resolution>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_SVG = REPO_ROOT / "images" / "icon.svg"
|
||||
DEFAULT_RESOLUTION = 512
|
||||
|
||||
|
||||
def fail(message: str) -> "None":
|
||||
print(f"error: {message}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def cli_command(svg: Path, png: Path, size: int) -> Optional[List[str]]:
|
||||
"""Returns the command line for the first installed CLI renderer, if any."""
|
||||
if shutil.which("rsvg-convert"):
|
||||
return ["rsvg-convert", "-w", str(size), "-h", str(size), "-f", "png",
|
||||
"-o", str(png), str(svg)]
|
||||
magick = shutil.which("magick") or shutil.which("convert")
|
||||
if magick:
|
||||
# A high density rasterises the vector sharply before scaling down;
|
||||
# "!" forces the exact size.
|
||||
return [magick, "-background", "none", "-density", "1200", str(svg),
|
||||
"-resize", f"{size}x{size}!", f"png:{png}"]
|
||||
if shutil.which("inkscape"):
|
||||
return ["inkscape", str(svg), "--export-type=png", f"--export-filename={png}",
|
||||
"-w", str(size), "-h", str(size)]
|
||||
return None
|
||||
|
||||
|
||||
def render(svg: Path, png: Path, size: int) -> str:
|
||||
"""Renders svg into png at size x size, returning the renderer's name."""
|
||||
try:
|
||||
import cairosvg # type: ignore[import-not-found]
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
cairosvg.svg2png(url=str(svg), write_to=str(png),
|
||||
output_width=size, output_height=size)
|
||||
return "cairosvg"
|
||||
|
||||
command = cli_command(svg, png, size)
|
||||
if command is None:
|
||||
fail("no SVG renderer found; install one of: cairosvg (pip), "
|
||||
"rsvg-convert (librsvg), ImageMagick, Inkscape")
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
fail(f"{Path(command[0]).name} failed:\n{result.stderr.strip()}")
|
||||
return Path(command[0]).name
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Render an SVG icon to a square PNG.")
|
||||
parser.add_argument("svg", nargs="?", type=Path, default=DEFAULT_SVG,
|
||||
help="input SVG (default: images/icon.svg)")
|
||||
parser.add_argument("-r", "--resolution", type=int, default=DEFAULT_RESOLUTION,
|
||||
help=f"width and height in pixels (default: {DEFAULT_RESOLUTION})")
|
||||
parser.add_argument("-o", "--output", type=Path,
|
||||
help="output PNG (default: the input path with a .png suffix)")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.resolution <= 0:
|
||||
fail("--resolution must be a positive integer")
|
||||
if not args.svg.is_file():
|
||||
fail(f"{args.svg} not found")
|
||||
png = args.output or args.svg.with_suffix(".png")
|
||||
if png.suffix.lower() != ".png":
|
||||
fail(f"output must be a .png file, got {png}")
|
||||
png.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
renderer = render(args.svg, png, args.resolution)
|
||||
print(f"{png} ({args.resolution}x{args.resolution}, via {renderer})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user