Skip to content

Instantly share code, notes, and snippets.

@zvodd
Created January 22, 2025 20:50
Show Gist options
  • Save zvodd/5c90cda2b44254510d8dbbaa5cf3f1b7 to your computer and use it in GitHub Desktop.
Save zvodd/5c90cda2b44254510d8dbbaa5cf3f1b7 to your computer and use it in GitHub Desktop.
Resize SVG; via viewbox and corresponding transform element wrap
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