Created
January 22, 2025 20:50
-
-
Save zvodd/5c90cda2b44254510d8dbbaa5cf3f1b7 to your computer and use it in GitHub Desktop.
Resize SVG; via viewbox and corresponding transform element wrap
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 xml.etree.ElementTree as ET | |
import argparse | |
import sys | |
def resize_svg(input_file, width=None, height=None): | |
""" | |
Resize an SVG by wrapping its contents in a transform that scales to new dimensions | |
while preserving aspect ratio. | |
Args: | |
input_file (str): Path to input SVG file | |
width (float, optional): Desired width | |
height (float, optional): Desired height | |
Returns: | |
str: Modified SVG content as string | |
""" | |
# Parse the SVG file | |
tree = ET.parse(input_file) | |
root = tree.getroot() | |
# Get the viewBox attribute | |
viewbox = root.get('viewBox') | |
if not viewbox: | |
raise ValueError("SVG must have a viewBox attribute") | |
# Parse viewBox values | |
vb_min_x, vb_min_y, vb_width, vb_height = map(float, viewbox.split()) | |
# Calculate actual width and height accounting for negative origins | |
orig_width = abs(vb_width) | |
orig_height = abs(vb_height) | |
# If only one dimension is provided, use it for both | |
if width is None and height is None: | |
raise ValueError("At least one of width or height must be specified") | |
elif width is None: | |
width = height | |
elif height is None: | |
height = width | |
# Calculate scale factors | |
scale_x = width / orig_width | |
scale_y = height / orig_height | |
# Create a new root element with the new dimensions | |
new_root = ET.Element('svg') | |
new_root.set('xmlns', 'http://www.w3.org/2000/svg') | |
new_root.set('width', str(width)) | |
new_root.set('height', str(height)) | |
# Calculate new viewBox values | |
new_vb_width = width | |
new_vb_height = height | |
new_vb_min_x = vb_min_x * scale_x | |
new_vb_min_y = vb_min_y * scale_y | |
new_root.set('viewBox', f"{new_vb_min_x} {new_vb_min_y} {new_vb_width} {new_vb_height}") | |
# Create a group to hold the transformed content | |
group = ET.SubElement(new_root, 'g') | |
group.set('transform', f'scale({scale_x} {scale_y})') | |
# Move all original content to the transformed group | |
for child in list(root): | |
group.append(child) | |
# Convert to string | |
return ET.tostring(new_root, encoding='unicode') | |
def main(): | |
# Using add_help=False to avoid conflict with -h argument | |
parser = argparse.ArgumentParser(description="Resize SVG file.", add_help=False) | |
parser.add_argument("input_file", help="Input SVG file") | |
parser.add_argument("-w", "--width", type=float, help="New width") | |
parser.add_argument("-t", "--height", type=float, help="New height") # Using -t instead of -h | |
parser.add_argument("-o", "--output", default="-", help="Output file (default: stdout)") | |
parser.add_argument("--help", action="help", help="Show this help message") | |
args = parser.parse_args() | |
try: | |
resized_svg = resize_svg(args.input_file, args.width, args.height) | |
if args.output == "-": | |
print(resized_svg) | |
else: | |
with open(args.output, "w") as f: | |
f.write(resized_svg) | |
print(f"Successfully resized SVG to {args.width}x{args.height}") | |
except Exception as e: | |
print(f"Error: {e}", file=sys.stderr) | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment