""" Printer font & symbol test script. Usage (inside Docker): python print_test.py [IP] [PORT] Defaults to 10.98.20.25:9100 """ import sys PRINTER_IP = sys.argv[1] if len(sys.argv) > 1 else "10.98.20.25" PRINTER_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 9100 from escpos.printer import Network def _gr(text: str) -> bytes: return text.encode('cp737', errors='replace') def _open(): p = Network(PRINTER_IP, PRINTER_PORT, timeout=10) p._raw(b'\x1b\x40') # ESC @ reset p._raw(b'\x1b\x74\x1d') # CP737 Greek code page return p def _t(p, text: str): p._raw(_gr(text)) def _divider(p, char="-", width=48): p._raw(b'\x1b\x61\x00') _t(p, char * width + "\n") def _center(p): p._raw(b'\x1b\x61\x01') def _left(p): p._raw(b'\x1b\x61\x00') # ── ESC ! n byte reference ────────────────────────────────────────────────── # Bit 0 → underline (not tested, minor) # Bit 1 → double-strike (bold) # Bit 3 → double-height # Bit 4 → double-width # Bit 5 → delete-line # Bit 7 → bold (ESC E alias in some models) # Common combos used here: # 0x00 = normal # 0x08 = double-height only (48 chars wide) # 0x10 = double-height only (alt bit) (48 chars wide) # 0x18 = double-height + bold # 0x20 = double-width only (24 chars wide) # 0x30 = double-width + double-height (24 chars wide) # 0x38 = double-width + double-height + bold # 0x48 = double-height (bit 6 combo — some printers) MODES = [ (0x00, "Normal (0x00)"), (0x08, "Double-height bit3 (0x08)"), (0x10, "Double-height bit4 (0x10)"), (0x18, "Double-height + Bold (0x18)"), (0x20, "Double-width (0x20)"), (0x30, "Double-width + Double-height (0x30)"), (0x38, "Double-width + Double-height + Bold (0x38)"), ] # ── Section 1 — Font sizes & styles, English ─────────────────────────────── def section_english(p): _center(p) p._raw(b'\x1b\x21\x38') _t(p, "=== FONT SIZES (EN) ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") for code, label in MODES: _left(p) _t(p, f"[{label}]\n") p._raw(bytes([0x1b, 0x21, code])) _t(p, "TEST PRINT Hello World Abc123\n") p._raw(b'\x1b\x21\x00') # bold on/off via ESC E _t(p, " -> bold ON: ") p._raw(b'\x1b\x45\x01') p._raw(bytes([0x1b, 0x21, code])) _t(p, "Bold Sample\n") p._raw(b'\x1b\x45\x00') p._raw(b'\x1b\x21\x00') _t(p, "\n") _divider(p) p._raw(b'\n') # ── Section 2 — Font sizes & styles, Greek ───────────────────────────────── def section_greek(p): _center(p) p._raw(b'\x1b\x21\x38') _t(p, "=== FONT SIZES (GR) ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") for code, label in MODES: _left(p) _t(p, f"[{label}]\n") p._raw(bytes([0x1b, 0x21, code])) _t(p, "ΔΟΚΙΜΑΣΤΙΚΗ ΕΚΤΥΠΩΣΗ\n") _t(p, "δοκιμαστικη εκτυπωση\n") # lowercase p._raw(b'\x1b\x21\x00') p._raw(b'\x1b\x45\x01') p._raw(bytes([0x1b, 0x21, code])) _t(p, "Bold: Καλημερα Κοσμε\n") p._raw(b'\x1b\x45\x00') p._raw(b'\x1b\x21\x00') _t(p, "\n") _divider(p) p._raw(b'\n') # ── Section 3 — All printable ASCII symbols ──────────────────────────────── def section_ascii_symbols(p): _center(p) p._raw(b'\x1b\x21\x18') # double-height bold for header _t(p, "=== ASCII SYMBOLS ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") _left(p) # Printable ASCII 0x20 – 0x7E, 16 per line chars = [chr(c) for c in range(0x20, 0x7F)] line = "" for i, ch in enumerate(chars): line += ch + " " if (i + 1) % 24 == 0: _t(p, line + "\n") line = "" if line: _t(p, line + "\n") _t(p, "\n") _t(p, "Notable:\n") _t(p, " Bullets : * + - # @ ! ? > < | / \\ ^ ~ _\n") _t(p, " Framing : [ ] { } ( ) = : ; , . \"\n") _t(p, " Currency : $ %\n") _divider(p) p._raw(b'\n') # ── Section 4 — CP737 extended chars (0x80–0xFF) ─────────────────────────── def section_extended(p): _center(p) p._raw(b'\x1b\x21\x18') _t(p, "=== CP737 EXTENDED (0x80-FF) ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") _left(p) _t(p, "Hex offset rows x16 columns\n\n") for row in range(8): # 0x80, 0x90 ... 0xF0 base = 0x80 + row * 16 row_bytes = bytes(range(base, base + 16)) label = f"0x{base:02X}: " p._raw(_gr(label)) p._raw(row_bytes) p._raw(b'\n') _t(p, "\n") _t(p, "Key CP737 specials:\n") specials = [ (0xB3, "─ thin horiz line"), (0xC4, "─ double line"), (0xBA, "│ vert line"), (0xBB, "┐ corner"), (0xBC, "┘ corner"), (0xC9, "╔ corner"), (0xCA, "╩ junction"), (0xCB, "╦ junction"), (0xCC, "╠ junction"), (0xCD, "═ double horiz"), (0xCE, "╬ cross"), (0xC8, "╚ corner"), (0xBB, "╗ corner"), (0xBC, "╝ corner"), (0xDB, "█ full block"), (0xDC, "▄ lower block"), (0xDF, "▀ upper block"), (0xB0, "░ light shade"), (0xB1, "▒ medium shade"), (0xB2, "▓ dark shade"), (0xF8, "° degree"), (0xF9, "· middle dot"), (0xFA, "· bullet dot"), (0xFB, "√ check / tick"), (0xFE, "■ filled square"), ] for code, desc in specials: row_bytes = bytes([code, 0x20]) # char + space p._raw(row_bytes) _t(p, f" {desc}\n") _divider(p) p._raw(b'\n') # ── Section 5 — Underline ────────────────────────────────────────────────── def section_underline(p): _center(p) p._raw(b'\x1b\x21\x18') _t(p, "=== UNDERLINE TEST ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") _left(p) # ESC - n : underline 0=off, 1=thin, 2=thick for ul in [1, 2]: p._raw(bytes([0x1b, 0x2d, ul])) _t(p, f"Underline mode {ul}: TEST PRINT Abc123\n") p._raw(b'\x1b\x2d\x00') # off _t(p, "\n") _divider(p) p._raw(b'\n') # ── Section 6 — Inverted / white-on-black ───────────────────────────────── def section_invert(p): _center(p) p._raw(b'\x1b\x21\x18') _t(p, "=== INVERT (white-on-black) ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") _left(p) # GS B n — invert p._raw(b'\x1d\x42\x01') _t(p, " INVERTED TEXT SAMPLE \n") p._raw(b'\x1d\x42\x00') _t(p, "Normal text after invert\n") _t(p, "\n") _divider(p) p._raw(b'\n') # ── Section 7 — QR Code sample ──────────────────────────────────────────── def section_qr(p): _center(p) p._raw(b'\x1b\x21\x18') _t(p, "=== QR CODE SAMPLE ===\n") p._raw(b'\x1b\x21\x00') _divider(p, "=") data = b"https://pos.test" # GS ( k — QR store data store_len = len(data) + 3 p._raw(b'\x1d\x28\x6b' + bytes([store_len & 0xFF, (store_len >> 8) & 0xFF, 0x31, 0x50, 0x30]) + data) # GS ( k — set size (module=6) p._raw(b'\x1d\x28\x6b\x03\x00\x31\x43\x06') # GS ( k — error correction level M p._raw(b'\x1d\x28\x6b\x03\x00\x31\x45\x31') # GS ( k — print p._raw(b'\x1d\x28\x6b\x03\x00\x31\x51\x30') _t(p, "\nhttps://pos.test\n\n") _divider(p) p._raw(b'\n') # ── Main ─────────────────────────────────────────────────────────────────── def main(): print(f"Connecting to {PRINTER_IP}:{PRINTER_PORT} ...") # ---- PAGE 1: English fonts ---- p = _open() section_english(p) p._raw(b'\n\n\n') p.cut() p.close() print("Page 1 sent (English fonts)") # ---- PAGE 2: Greek fonts ---- p = _open() section_greek(p) p._raw(b'\n\n\n') p.cut() p.close() print("Page 2 sent (Greek fonts)") # ---- PAGE 3: Symbols & special chars ---- p = _open() section_ascii_symbols(p) section_extended(p) p._raw(b'\n\n\n') p.cut() p.close() print("Page 3 sent (ASCII + CP737 extended)") # ---- PAGE 4: Underline + Invert + QR ---- p = _open() section_underline(p) section_invert(p) section_qr(p) p._raw(b'\n\n\n') p.cut() p.close() print("Page 4 sent (underline / invert / QR)") print("Done — 4 pages printed.") if __name__ == "__main__": main()