#!/usr/bin/env python3 # # Prusa Slicer as of v2.7 remove headers before jpg and convert PNG to JPG. # # This script has been developed for E3S1PROFORKBYTT by Thomas Toka. # # ------------------------------------------------------------------------------ import sys import base64 import math from PIL import Image from io import BytesIO import os import platform if platform.system() == "Darwin": print("Running on macOS") script_directory = os.path.dirname(os.path.abspath(__file__)) source_file = sys.argv[1] source_file = os.path.join(script_directory, source_file) if not os.path.exists(source_file): print(f"The file '{source_file}' does not exist.") sys.exit(1) else: print(f"The file '{source_file}' exists.") else: print("Not running on macOS") def main(source_file): # Read the entire G-code file into memory with open(source_file, "r", encoding='utf-8') as f: lines = f.readlines() # Remove any empty lines and lines that are just a semicolon lines = [line for line in lines if line.strip() and not line == ';\n'] # Extract additional information filament_used_m, filament_used_g, filament_diameter, filament_density, layer_height = "0", "0", "0", "0", "0" layers = 0 for line in lines: if line.startswith("; filament used [mm] ="): filament_used_mm_values = [float(value.strip()) for value in line.split("=")[1].strip().split(',')] filament_used_m = round(sum(filament_used_mm_values) / 1000, 2) # Convert mm to meters if filament_used_m > 0: filament_used_m = math.ceil(filament_used_m) else: filament_used_m = 0 elif line.startswith("; filament used [g] ="): filament_used_g_values = [float(value.strip()) for value in line.split("=")[1].strip().split(',')] filament_used_g = round(sum(filament_used_g_values), 2) if filament_used_g > 0: filament_used_g = math.ceil(filament_used_g) else: filament_used_g = 0 elif line.startswith("; filament_diameter ="): filament_diameter_values = [float(value.strip()) for value in line.split("=")[1].strip().split(',')] filament_diameter = round(sum(filament_diameter_values) / len(filament_diameter_values), 2) filament_diameter = "{:.2f}".format(filament_diameter) elif line.startswith("; filament_density ="): filament_density_values = [float(value.strip()) for value in line.split("=")[1].strip().split(',')] filament_density = round(sum(filament_density_values) / len(filament_density_values), 2) # Calculate the median filament_density = "{:.2f}".format(filament_density) elif line.startswith("; layer_height ="): layer_height_values = [float(value.strip()) for value in line.split("=")[1].strip().split(',')] layer_height = round(sum(layer_height_values) / len(layer_height_values), 2) # Calculate the median layer_height = "{:.2f}".format(layer_height) elif line.startswith(";AFTER_LAYER_CHANGE"): layers += 1 filament_used_m_per_layer = filament_used_m / max(layers, 1) # Avoid division by zero remaining_filament_m = filament_used_m filament_used_g_per_layer = filament_used_g / max(layers, 1) # Avoid division by zero remaining_filament_g = filament_used_g m117_added = 0 # Counter for added M117 commands # Counting AFTER_LAYER_CHANGE occurrences after_layer_change_count = sum(';AFTER_LAYER_CHANGE' in line for line in lines) - 1 # Find the thumbnail start and end lines thumbnail_start, thumbnail_end = None, None for i, line in enumerate(lines): if '; thumbnail begin' in line: thumbnail_start = i elif '; thumbnail end' in line: thumbnail_end = i break if thumbnail_start is not None and thumbnail_end is not None: # Extract and decode the PNG data original_png_data = "".join(lines[thumbnail_start + 1:thumbnail_end]).replace("; ", "") png_data_bytes = base64.b64decode(original_png_data) image = Image.open(BytesIO(png_data_bytes)) image = image.convert("RGB") # Encode the image as JPEG buffer = BytesIO() image.save(buffer, format="JPEG") image_jpg_data = buffer.getvalue() # Base64 encode the JPEG data image_jpg_base64 = base64.b64encode(image_jpg_data).decode('utf-8') # Split the base64 string into formatted lines max_line_length = 79 - len("; ") injected_jpg_data = ["; " + image_jpg_base64[i:i + max_line_length] for i in range(0, len(image_jpg_base64), max_line_length)] # Replace the old PNG lines with the new JPEG lines lines[thumbnail_start + 1:thumbnail_end] = [line + "\n" for line in injected_jpg_data] # Update the '; thumbnail_JPG begin' line using 'after_layer_change_count' start_line_number = 1 end_line_number = start_line_number + len(injected_jpg_data) + 1 lines[thumbnail_start] = f'; thumbnail_JPG begin 250x250 {len(image_jpg_data)} {start_line_number} {end_line_number} {filament_used_m} {filament_used_g} {layer_height} {filament_diameter} {filament_density} {after_layer_change_count}\n' # Insert 'M117 Pxxx Qxxx' before each ';AFTER_LAYER_CHANGE' layer_number = 0 i = 0 while i < len(lines): if lines[i].startswith(';AFTER_LAYER_CHANGE'): if layer_number == 0: m117_line = "M117 L1 M{} G{} Z{} Q{}".format(math.ceil(remaining_filament_m), math.ceil(remaining_filament_g), layers, layer_height) else: m117_line = "M117 L{} M{} G{}".format(layer_number + 1, math.ceil(remaining_filament_m), math.ceil(remaining_filament_g)) lines.insert(i, m117_line + '\n') remaining_filament_m -= filament_used_m_per_layer remaining_filament_g -= filament_used_g_per_layer m117_added += 1 # Increment counter layer_number += 1 i += 1 i += 1 if m117_added > 0: print("Added {} M117 commands.".format(m117_added)) else: print("No M117 commands were added. Check the G-code for ';AFTER_LAYER_CHANGE' markers.") # Remove the '; generated by PrusaSlicer' line and any leading semicolons or empty lines lines = [line for line in lines if line.strip() and not line.startswith('; generated by PrusaSlicer')] # Write the modified content back to the original file with open(source_file, "w", encoding='utf-8') as f: f.writelines(lines) if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python3 script.py ") sys.exit(1) if platform.system() == "Darwin": main(source_file) else: main(sys.argv[1])