With some LLM help, this works a treat. No need to pre-download fonts to the printers. Custom fonts galore!

As a test, it dumps the Z64 encoded data to the console, and sends to a printer on port 9100.

import os
import binascii
import socket
import zlib
import base64
from typing import Optional

def font_to_zpl(dir_path: str, font_name: str) -> str:
    """
    Convert a font file to a ZPL command string with Z64 encoding.
    
    Args:
        dir_path (str): Directory path where the font file is located
        font_name (str): Name of the font file (e.g., 'comic.ttf')
    
    Returns:
        str: ZPL command string containing the font data
    """
    # Construct the full file path
    file_path = os.path.join(dir_path, font_name)
    
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"Font file '{font_name}' not found in '{dir_path}'")

    with open(file_path, 'rb') as file:
        font_bytes = file.read()
    
    # Compress the data using zlib's DEFLATE (LZ77) algorithm
    compressed_data = zlib.compress(font_bytes)

    # Encode the compressed data in Base64
    base64_encoded_data = base64.b64encode(compressed_data).decode('utf-8')

    # Calculate CRC32 of the Base64-encoded data
    crc_value = zlib.crc32(base64_encoded_data.encode('utf-8')) & 0xFFFF
    crc_hex = f"{crc_value:04X}".lower()  # Format as 4 lowercase hex digits

    # Create the Z64-encoded font data string with CRC
    encoded_data_with_crc = f":Z64:{base64_encoded_data}:{crc_hex}"

    # Get the original font size (in bytes) for the `~DY` command
    original_size = len(font_bytes)

    # Construct the ZPL command to send to the printer
    zpl = (
        f"^XA\n"
        f"~DUR:{font_name.upper()},{original_size},{encoded_data_with_crc}\n"
        f"^FB1200,1,,C,"
        f"^CW1,R:{font_name.upper()}\n"
        f"^FO20,20"
        f"^A@N,40,,R:{font_name.upper()}\n"
        f"^FDThis is {font_name}!^FS\n"
        f"^XZ"
    )
    
    return zpl

def send_to_device(host: str, port: int, data: str, timeout: Optional[float] = 5.0) -> bool:
    """
    Send data to a device over TCP.
    
    Args:
        host (str): The IP address or hostname of the device
        port (int): The port number (typically 9100 for printers)
        data (str): The data to send
        timeout (float, optional): Socket timeout in seconds. Defaults to 5.0
    
    Returns:
        bool: True if successful, False if failed
    """
    try:
        # Create a TCP socket
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            # Set timeout
            sock.settimeout(timeout)
            
            # Connect to the device
            sock.connect((host, port))
            
            # Send the data
            sock.sendall(data.encode('utf-8'))
            
            return True
            
    except socket.timeout:
        print(f"Error: Connection to {host}:{port} timed out")
        return False
    except ConnectionRefusedError:
        print(f"Error: Connection to {host}:{port} was refused")
        return False
    except Exception as e:
        print(f"Error sending data to {host}:{port}: {str(e)}")
        return False

def main():
    # Configuration
    dir_path = r"C:\Users\CarlFarrington\envs\zpltest\ttf"  # Replace with your directory path
    font_name = "AndaleM.ttf"
    device_ip = "192.168.1.47"  # Replace with your device's IP address
    device_port = 9100
    
    try:
        # Generate ZPL command
        zpl_command = font_to_zpl(dir_path, font_name)
        
        # Print the command (for debugging)
        print("Generated ZPL command:")
        print(zpl_command)
        
        print("\nSending to device...")
        # Send to device
        if send_to_device(device_ip, device_port, zpl_command):
            print(f"Successfully sent to {device_ip}:{device_port}")
        else:
            print("Failed to send data to device")
            
    except FileNotFoundError as e:
        print(e)
    except Exception as e:
        print(f"Error: {str(e)}")

if __name__ == "__main__":
    main()