Python: From ESC/P Serial to BMP image

45 Views Asked by At

I receive an ESC/P protocol from a device via a serial interface. The device is actually intended to be connected to a dot matrix printer. Since we do not want to use printers, I am currently working on a solution to receive the stream with a software and save it in a BMP file for further use. I have been searching the web for a long time for a library for Python or other solutions on this topic and have unfortunately not found anything. So I read up on the ESC/P protocol and tried to write my own Python routine. In case others have the same problem, I'll post my solution here. I would be very happy about feedback or suggestions for improvement.

    from PIL import Image
    import struct, io
    
    def parse_ESCP_bytes(data):
        image_list = []
    
        while data:
            # Search for the start character (ESC) and the ASCII asterisk
            start_index = data.find(b'\x1b*')
            
            if start_index != -1:
                # Extract the number of columns with the low and high bytes
                num_columns_low, num_columns_high = struct.unpack('>BB', data[start_index+3:start_index+5])
                num_columns = (num_columns_high << 8) + num_columns_low
                
                # Read all bytes of the image data
                image_data = struct.unpack('>' + 'B' * num_columns, data[start_index + 5:start_index + 5 + num_columns])
                
                # The image data has the following format:
                # [0, 7, 15, ...] each 8-bit value represents a column with 8 rows. 
                # 0 = No pixels, 7 = pixels 6, 7, 8 of this column are set (counted from the top).
                # Thus, for each image data row we read, 8 new image rows are added to our list
                for i in range(7, -1, -1):
                    image_list.append([col >> i & 1 for col in image_data])
                
                # Update the data for the next iteration
                data = data[start_index + 5 + num_columns + 2:]
    
                # Control characters for DPI or paper feed or carriage return are not considered here.
            else:
                break
        
        # Define the width and height of the pixel matrix
        width, height = len(image_list), len(image_list[0])
    
        # Create the binary black and white image
        img = Image.new('1', (width, height))
    
        # Set the pixels based on the pixel matrix
        img.putdata([int(pixel) for row in image_list for pixel in row])
    
        img_byte_arr = io.BytesIO()
        img.save(img_byte_arr, format='BMP')
        return img_byte_arr.getvalue()
    
    with open("ESCP", "rb") as file:
        raw_serial = file.read()
    
    image_bytes = parse_ESCP_bytes(raw_serial)
    
    with open("output.bmp", 'wb') as file:
        file.write(image_bytes)
    
0

There are 0 best solutions below