Created
August 18, 2026 14:34
-
-
Save wpyoga/ceabddd187b8854a7e2bf0617b7dce0f to your computer and use it in GitHub Desktop.
Vibe-coded script to normalize an SVG file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import argparse | |
| import math | |
| import xml.etree.ElementTree as ET | |
| from svgelements import SVG | |
| def calculate_bbox(svg): | |
| """ | |
| Calculate the bounding box of drawable SVG content. | |
| Returns: | |
| (xmin, ymin, xmax, ymax) | |
| """ | |
| bbox = None | |
| for element in svg.elements(): | |
| # Ignore elements that are explicitly hidden. | |
| try: | |
| if ( | |
| element.values.get("visibility") == "hidden" | |
| or element.values.get("display") == "none" | |
| ): | |
| continue | |
| except AttributeError: | |
| pass | |
| element_bbox = element.bbox() | |
| # try: | |
| # element_bbox = element.bbox() | |
| # except (AttributeError, TypeError, ValueError): | |
| # continue | |
| if element_bbox is None: | |
| continue | |
| if not all(math.isfinite(value) for value in element_bbox): | |
| print(f"Not all the bounds are finite: {element_bbox}") | |
| continue | |
| xmin, ymin, xmax, ymax = element_bbox | |
| if bbox is None: | |
| bbox = [xmin, ymin, xmax, ymax] | |
| else: | |
| bbox[0] = min(bbox[0], xmin) | |
| bbox[1] = min(bbox[1], ymin) | |
| bbox[2] = max(bbox[2], xmax) | |
| bbox[3] = max(bbox[3], ymax) | |
| if bbox is None: | |
| raise ValueError("SVG contains no drawable geometry") | |
| return tuple(bbox) | |
| def normalize_viewbox(input_filename, output_filename, padding=0): | |
| """ | |
| Replace the SVG's viewBox with the bounding box of its artwork. | |
| The SVG is parsed with svgelements only for geometry calculation. | |
| The actual SVG document is parsed and written using ElementTree. | |
| """ | |
| svg = SVG.parse(input_filename) | |
| xmin, ymin, xmax, ymax = calculate_bbox(svg) | |
| xmin -= padding | |
| ymin -= padding | |
| xmax += padding | |
| ymax += padding | |
| width = xmax - xmin | |
| height = ymax - ymin | |
| if width <= 0 or height <= 0: | |
| raise ValueError(f"Invalid bounding box: " f"{xmin}, {ymin}, {xmax}, {ymax}") | |
| ET.register_namespace("", "http://www.w3.org/2000/svg") | |
| # Parse the ORIGINAL SVG document. | |
| tree = ET.parse(input_filename) | |
| root = tree.getroot() | |
| # Change only the semantic SVG attribute. | |
| root.set( | |
| "viewBox", | |
| f"{xmin:g} {ymin:g} {width:g} {height:g}", | |
| ) | |
| root.set("width", f"{width:g}") | |
| root.set("height", f"{height:g}") | |
| # Write the original XML structure back out. | |
| tree.write( | |
| output_filename, | |
| encoding="utf-8", | |
| xml_declaration=True, | |
| ) | |
| print(f"Input: {input_filename}") | |
| print(f"BBox: {xmin:g} {ymin:g} {xmax:g} {ymax:g}") | |
| print(f"New size: {width:g} × {height:g}") | |
| print(f"New viewBox:{xmin:g} {ymin:g} {width:g} {height:g}") | |
| print(f"Output: {output_filename}") | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("input") | |
| parser.add_argument("output") | |
| args = parser.parse_args() | |
| normalize_viewbox(args.input, args.output) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment