Skip to content

Instantly share code, notes, and snippets.

@unreal79
Created February 4, 2026 22:47
Show Gist options
  • Select an option

  • Save unreal79/cdb75c86b88a12f9fa0f210cdaa7c2f7 to your computer and use it in GitHub Desktop.

Select an option

Save unreal79/cdb75c86b88a12f9fa0f210cdaa7c2f7 to your computer and use it in GitHub Desktop.
# !/usr/bin/env python3
# Convert SVG files to centered PNGs of fixed size.
# Licensed under CC0-1.0
#
# Using resvg_py lib: https://github.com/baseplate-admin/resvg-py
# pip install resvg_py
import resvg_py
from pathlib import Path
import xml.etree.ElementTree as ET
WIDTH = 256
HEIGHT = 256
DIR = Path(__file__).parent
# DIR = Path(__file__).parent / "BLACK" / "SVG"
def _parse_length(value: str | None) -> float | None:
if not value:
return None
for suffix in ("px", "pt", "cm", "mm", "in"):
if value.endswith(suffix):
value = value[: -len(suffix)]
break
try:
return float(value)
except ValueError:
return None
def prepare_svg(svg_text: str) -> str:
"""Normalize SVG to have viewBox and target size for centered fit."""
root = ET.fromstring(svg_text)
view_box = root.attrib.get("viewBox") or root.attrib.get("viewbox")
width_attr = root.attrib.get("width")
height_attr = root.attrib.get("height")
if view_box:
parts = view_box.replace(",", " ").split()
if len(parts) != 4:
raise ValueError("Invalid viewBox format; expected four numbers.")
try:
# Validate numeric values even though we only need the attribute to exist.
_ = list(map(float, parts))
except ValueError as exc:
raise ValueError("viewBox values must be numeric.") from exc
else:
vb_width = _parse_length(width_attr)
vb_height = _parse_length(height_attr)
if vb_width and vb_height:
root.set("viewBox", f"0 0 {vb_width} {vb_height}")
# If we still don't have geometry, bail out early.
if not root.attrib.get("viewBox"):
raise ValueError("SVG missing viewBox/width/height; cannot center content.")
root.set("width", str(WIDTH))
root.set("height", str(HEIGHT))
if "preserveAspectRatio" not in root.attrib:
root.set("preserveAspectRatio", "xMidYMid meet")
return ET.tostring(root, encoding="unicode")
def convert_SVG2PNG_centered(svg_dir: Path) -> None:
"""Convert all SVG files in svg_dir to PNGs with the same basename."""
if not svg_dir.is_dir():
raise FileNotFoundError(f"Directory not found: {svg_dir}")
for svg_path in sorted(svg_dir.glob("*.svg")):
png_path = svg_path.with_suffix(".png")
svg_text = svg_path.read_text(encoding="utf-8")
prepared_svg = prepare_svg(svg_text)
png_bytes: bytes = resvg_py.svg_to_bytes(
svg_string=prepared_svg,
width=WIDTH,
height=HEIGHT,
background=None,
)
png_path.write_bytes(png_bytes)
print(f"Converted {svg_path.name} -> {png_path.name}")
if __name__ == "__main__":
convert_SVG2PNG_centered(DIR)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment