#!/usr/bin/env python3 """ Generate 3D zipper pull STL files with raised text from a list of names using OpenSCAD. This script reads names from a text file and generates individual STL files for each name using the raised text zipper pull OpenSCAD template. Version 2: Improved apostrophe handling - preserves apostrophes in both filenames and text rendering. """ import subprocess import os import sys from pathlib import Path def generate_stl(name, template_file, output_dir): """ Generate an STL file for a given name using OpenSCAD. Args: name: The name to put on the zipper pull template_file: Path to the OpenSCAD template file output_dir: Directory where STL files will be saved """ # Create a safe filename from the name # Keep apostrophes in filename for accurate representation safe_name = "".join(c for c in name if c.isalnum() or c in (' ', '-', '_', "\'")).strip() safe_name = safe_name.replace(' ', '_') output_file = os.path.join(output_dir, f"{safe_name}.stl") # Escape the name for OpenSCAD command line # Need to escape both quotes and apostrophes properly escaped_name = name.replace('\\', '\\\\') # Escape backslashes first escaped_name = escaped_name.replace('"', '\\"') # Escape double quotes escaped_name = escaped_name.replace("'", "\\'") # Escape single quotes/apostrophes # Build the OpenSCAD command # -D sets a variable, -o specifies output file cmd = [ r'C:\Program Files\OpenSCAD\openscad.exe', '-D', f'name="{escaped_name}"', '-o', output_file, template_file ] print(f"Generating: {safe_name}.stl for '{name}'...") try: # Run OpenSCAD result = subprocess.run( cmd, capture_output=True, text=True, check=True ) print(f" [OK] Successfully created {safe_name}.stl") return True except subprocess.CalledProcessError as e: print(f" [ERROR] Error generating {safe_name}.stl") print(f" {e.stderr}") return False except FileNotFoundError: print("Error: OpenSCAD not found. Please install OpenSCAD and ensure it's in your PATH.") print("Download from: https://openscad.org/downloads.html") sys.exit(1) def main(): # Configuration template_file = "zipper_pull_raised_text_template.scad" names_file = "names.txt" output_dir = "zipper-pulls-raised-text-2" # Check if template exists if not os.path.exists(template_file): print(f"Error: Template file '{template_file}' not found!") sys.exit(1) # Check if names file exists if not os.path.exists(names_file): print(f"Error: Names file '{names_file}' not found!") sys.exit(1) # Create output directory if it doesn't exist Path(output_dir).mkdir(exist_ok=True) # Read names from file with open(names_file, 'r', encoding='utf-8') as f: names = [line.strip() for line in f if line.strip()] if not names: print(f"Error: No names found in '{names_file}'") sys.exit(1) print(f"Found {len(names)} name(s) to process") print(f"Output directory: {output_dir}") print("-" * 50) # Generate STL for each name success_count = 0 for name in names: if generate_stl(name, template_file, output_dir): success_count += 1 print("-" * 50) print(f"Complete! Generated {success_count}/{len(names)} STL files") print(f"Files saved in: {output_dir}/") if __name__ == "__main__": main()