diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..1c7d1b2 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index e5cb33c..d19cbaf 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ wheels/ participants.csv cert.png CrimsonText-Bold.ttf +certificates/ +cert.jpg diff --git a/jpeg.py b/jpeg.py new file mode 100644 index 0000000..f3ccc74 --- /dev/null +++ b/jpeg.py @@ -0,0 +1,87 @@ +import csv +from PIL import Image, ImageDraw, ImageFont +import os +import concurrent.futures +from functools import partial +import time + +def generate_certificate(template_path, font_path, name, output_path, index, font_size=50, text_color="black", y_offset=0): + # Open the certificate template image + with Image.open(template_path) as image: + # For JPG compatibility, convert to RGB instead of RGBA + image = image.convert("RGB") + draw = ImageDraw.Draw(image) + + # Load the specified font at the desired size + font = ImageFont.truetype(font_path, font_size) + + # Image dimensions + image_width, image_height = image.size + + # Calculate the bounding box for the text + bbox = draw.textbbox((0, 0), name, font=font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + + # Calculate coordinates for centered text + x = (image_width - text_width) / 2 + y = (image_height - text_height) / 2 + y_offset + + # Draw the text onto the certificate image + draw.text((x, y), name, fill=text_color, font=font) + + # Save the image as JPG + output_filename = os.path.join(output_path, f"certificate_{index:03d}.jpg") + image.save(output_filename, quality=95) # Set JPG quality + print(f"Saved certificate for {name} as {output_filename}") + +def process_certificate(row, i, template_path, font_path, output_folder, font_size, text_color, y_offset): + name = row["name"].upper() + generate_certificate(template_path, font_path, name, output_folder, i, font_size, text_color, y_offset) + +def main(): + start_time = time.time() + template_path = "cert.jpg" # Certificate template image filename (JPG format) + font_path = "CrimsonText-Bold.ttf" # Path to the font file (TTF or OTF) + csv_file = "participants.csv" # CSV file containing the name list + output_folder = "certificates" # Output folder for generated certificates + font_size = 92 + text_color = "black" + y_offset = -82 + + # Optimal number of workers + cpu_count = os.cpu_count() + max_workers = min(32, (cpu_count + 4) if cpu_count is not None else 4) + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + # Read all data from CSV at once + with open(csv_file, newline='', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile) + participants = list(reader) + + # Create a partial function with all the fixed parameters + process_func = partial( + process_certificate, + template_path=template_path, + font_path=font_path, + output_folder=output_folder, + font_size=font_size, + text_color=text_color, + y_offset=y_offset + ) + + # Process certificates in parallel + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit all tasks + futures = [executor.submit(process_func, row, i) for i, row in enumerate(participants, start=1)] + + # Wait for all to complete + concurrent.futures.wait(futures) + + elapsed_time = time.time() - start_time + print(f"Completed generating {len(participants)} certificates in {elapsed_time:.2f} seconds") + +if __name__ == "__main__": + main() diff --git a/main.py b/main.py deleted file mode 100644 index bc27ad4..0000000 --- a/main.py +++ /dev/null @@ -1,61 +0,0 @@ -import csv -from PIL import Image, ImageDraw, ImageFont - -def generate_certificate(template_path, font_path, output_folder, name, font_size=50, text_color="black", y_offset=0): - # Open the certificate template image - with Image.open(template_path) as image: - # Convert image to RGBA if not already (to support transparency if needed) - image = image.convert("RGBA") - draw = ImageDraw.Draw(image) - - # Load the Inter Semi Bold font at the desired size - font = ImageFont.truetype(font_path, font_size) - - # Get image dimensions - image_width, image_height = image.size - - # Determine the size of the text to be drawn - # Note: textsize may not be pixel-perfect for all fonts; textbbox is available in newer Pillow versions. - text_width, text_height = draw.textsize(name, font=font) - - # Calculate coordinates for centered text - x = (image_width - text_width) / 2 - # Adjust y coordinate; here we center vertically then apply optional y_offset (e.g., for slight nudging) - y = (image_height - text_height) / 2 + y_offset - - # Draw the name onto the certificate image - draw.text((x, y), name, fill=text_color, font=font) - - return image - -def main(): - # File paths and settings -- update these as needed - template_path = "cert" # Path to your certificate image template - font_path = "CrimsonText-Bold.ttf" # Path to the Inter Semi Bold TTF file - csv_file = "names.csv" # Path to the CSV file containing names - output_folder = "certificates" # Folder to save generated certificate images - font_size = 50 # Adjust font size based on your template - text_color = "black" # Text color; change as necessary - y_offset = 0 # Adjust vertical offset if the text is not in the desired location - - # Create output folder if it doesn't exist - import os - if not os.path.exists(output_folder): - os.makedirs(output_folder) - - # Read names from CSV - with open(csv_file, newline='', encoding='utf-8') as csvfile: - reader = csv.DictReader(csvfile) - for i, row in enumerate(reader, start=1): - # Get name from first column (index 0) - name = row[0] # First column contains the names - # Generate a personalized certificate image with the name - certificate_image = generate_certificate(template_path, font_path, output_folder, name, font_size, text_color, y_offset) - - # Save the certificate with a unique filename (you could also include the name in the filename) - output_filename = os.path.join(output_folder, f"certificate_{i:03d}.png") - certificate_image.save(output_filename) - print(f"Saved certificate for {name} as {output_filename}") - -if __name__ == "__main__": - main() diff --git a/png.py b/png.py new file mode 100644 index 0000000..101d8a5 --- /dev/null +++ b/png.py @@ -0,0 +1,86 @@ +import csv +from PIL import Image, ImageDraw, ImageFont +import os +import concurrent.futures +from functools import partial +import time + +def generate_certificate(template_path, font_path, name, output_path, index, font_size=50, text_color="black", y_offset=0): + # Open the certificate template image + with Image.open(template_path) as image: + image = image.convert("RGBA") + draw = ImageDraw.Draw(image) + + # Load the specified font at the desired size + font = ImageFont.truetype(font_path, font_size) + + # Image dimensions + image_width, image_height = image.size + + # Calculate the bounding box for the text + bbox = draw.textbbox((0, 0), name, font=font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + + # Calculate coordinates for centered text + x = (image_width - text_width) / 2 + y = (image_height - text_height) / 2 + y_offset + + # Draw the text onto the certificate image + draw.text((x, y), name, fill=text_color, font=font) + + # Save the image directly + output_filename = os.path.join(output_path, f"certificate_{index:03d}.png") + image.save(output_filename) + print(f"Saved certificate for {name} as {output_filename}") + +def process_certificate(row, i, template_path, font_path, output_folder, font_size, text_color, y_offset): + name = row["name"].upper() + generate_certificate(template_path, font_path, name, output_folder, i, font_size, text_color, y_offset) + +def main(): + start_time = time.time() + template_path = "cert.png" # Certificate template image filename + font_path = "CrimsonText-Bold.ttf" # Path to the font file (TTF or OTF) + csv_file = "participants.csv" # CSV file containing the name list + output_folder = "certificates" # Output folder for generated certificates + font_size = 92 + text_color = "black" + y_offset = -82 + + # Optimal number of workers + cpu_count = os.cpu_count() + max_workers = min(32, (cpu_count + 4) if cpu_count is not None else 4) + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + # Read all data from CSV at once + with open(csv_file, newline='', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile) + participants = list(reader) + + # Create a partial function with all the fixed parameters + process_func = partial( + process_certificate, + template_path=template_path, + font_path=font_path, + output_folder=output_folder, + font_size=font_size, + text_color=text_color, + y_offset=y_offset + ) + + # Process certificates in parallel + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + # Submit all tasks + futures = [executor.submit(process_func, row, i) for i, row in enumerate(participants, start=1)] + + # Wait for all to complete + concurrent.futures.wait(futures) + + elapsed_time = time.time() - start_time + print(f"Completed generating {len(participants)} certificates in {elapsed_time:.2f} seconds") + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..48af26f --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 1 +requires-python = ">=3.12" + +[[package]] +name = "img-cert-maker" +version = "0.1.0" +source = { virtual = "." }