Created
January 22, 2025 21:04
-
-
Save zvodd/a4691964443463bf8a131006106387a3 to your computer and use it in GitHub Desktop.
Resize SVG folder in/out; 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 | |
import os | |
from pathlib import Path | |
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 process_folder(input_folder, output_folder, width=None, height=None): | |
""" | |
Process all SVG files in the input folder and save resized versions to the output folder. | |
Args: | |
input_folder (str): Path to input folder containing SVG files | |
output_folder (str): Path to output folder for resized SVG files | |
width (float, optional): Desired width | |
height (float, optional): Desired height | |
""" | |
# Create output folder if it doesn't exist | |
Path(output_folder).mkdir(parents=True, exist_ok=True) | |
# Get all SVG files in input folder | |
svg_files = Path(input_folder).glob('**/*.svg') | |
processed_count = 0 | |
error_count = 0 | |
for svg_file in svg_files: | |
# Create relative path for output file | |
rel_path = svg_file.relative_to(input_folder) | |
output_file = Path(output_folder) / rel_path | |
# Create necessary subdirectories | |
output_file.parent.mkdir(parents=True, exist_ok=True) | |
try: | |
resized_svg = resize_svg(svg_file, width, height) | |
with open(output_file, 'w') as f: | |
f.write(resized_svg) | |
processed_count += 1 | |
print(f"Processed: {rel_path}") | |
except Exception as e: | |
print(f"Error processing {rel_path}: {str(e)}", file=sys.stderr) | |
error_count += 1 | |
return processed_count, error_count | |
def main(): | |
parser = argparse.ArgumentParser(description="Batch resize SVG files in a folder.", add_help=False) | |
parser.add_argument("input_folder", help="Input folder containing SVG files") | |
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", required=True, help="Output folder for resized SVGs") | |
parser.add_argument("--help", action="help", help="Show this help message") | |
args = parser.parse_args() | |
try: | |
if not os.path.isdir(args.input_folder): | |
raise ValueError(f"Input path '{args.input_folder}' is not a directory") | |
processed, errors = process_folder( | |
args.input_folder, | |
args.output, | |
args.width, | |
args.height | |
) | |
print(f"\nProcessing complete:") | |
print(f"Successfully processed: {processed} files") | |
if errors > 0: | |
print(f"Errors encountered: {errors} files", file=sys.stderr) | |
sys.exit(1) | |
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