Created
November 14, 2025 15:58
-
-
Save bradmartin333/e0d9889cf46fde0d70080cdf9cb588c0 to your computer and use it in GitHub Desktop.
downscale PNG and print on bluetooth printer from Windows
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
| # /// script | |
| # requires-python = ">=3.11" | |
| # dependencies = [ | |
| # "pillow>=11.2.1", | |
| # ] | |
| # /// | |
| # usage: uv run this_script.py <path_to_png> | |
| import sys | |
| import socket | |
| from PIL import Image | |
| def print_png(file_path): | |
| # Printer config | |
| printer_mac = "00:02:5B:21:28:53" # Your printer's MAC address | |
| printer_width = 384 # pixels, adjust for your printer | |
| # Windows Bluetooth constants | |
| BTPROTO_RFCOMM = 3 | |
| # Open and process image | |
| img = Image.open(file_path) | |
| img = img.convert('1') # 1-bit pixels, black and white | |
| # Resize to printer width, keep aspect ratio | |
| w, h = img.size | |
| new_h = int((printer_width / w) * h) | |
| img = img.resize((printer_width, new_h), Image.LANCZOS) | |
| # Each ESC * line can print up to 24 dots (vertical) at once | |
| rows = img.height | |
| # Connect to printer via Bluetooth socket | |
| try: | |
| # Create Bluetooth RFCOMM socket | |
| sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, BTPROTO_RFCOMM) | |
| print(f"Connecting to {printer_mac}...") | |
| # Connect to printer (port 1 is standard for SPP) | |
| sock.connect((printer_mac, 1)) | |
| print("Connected!") | |
| sock.send(b'\x1B\x33\x00') # Set line spacing | |
| # For each chunk of 24 rows | |
| for y in range(0, rows, 24): | |
| # Prepare 24 rows of data | |
| slice_height = min(24, rows - y) | |
| line_data = bytearray() | |
| for x in range(printer_width): | |
| # For each column, pack 24 bits into 3 bytes (top byte first) | |
| column_bytes = [0, 0, 0] | |
| for b in range(slice_height): | |
| pixel = img.getpixel((x, y + b)) | |
| if pixel == 0: # Black pixel | |
| column_bytes[b // 8] |= (1 << (7 - (b % 8))) | |
| line_data.extend(column_bytes) | |
| # ESC * m nL nH [d1...dk] | |
| m = 33 # 24-dot double-density | |
| nL = printer_width & 0xFF | |
| nH = (printer_width >> 8) & 0xFF | |
| cmd = bytes([0x1B, 0x2A, m, nL, nH]) | |
| sock.send(cmd) | |
| sock.send(bytes(line_data)) | |
| sock.send(bytes([0x0A])) # Line feed | |
| print("Image sent to printer.") | |
| sock.close() | |
| except Exception as e: | |
| import traceback | |
| print(f"Could not print image: {e}") | |
| traceback.print_exc() | |
| if __name__ == "__main__": | |
| if sys.platform != "win32": | |
| print("This script only works on Windows.") | |
| sys.exit(1) | |
| if len(sys.argv) != 2: | |
| print("Usage: python print_png.py <path_to_png>") | |
| else: | |
| print_png(sys.argv[1]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment