Arabic on ESC/POS thermal printers: a reference for reliable receipts and kitchen tickets
Whitepaper · RAWA Engineering · rawaerp.com · runnable example: github.com/rawaerp/arabic-thermal-printer-escpos-guide
Problem
Arabic receipts that print as ?????, as disconnected letters, in reversed word order, or that start well and degrade
halfway are the most common hardware complaint in Arabic-market POS deployments. They look like printer faults; they are
four distinct software problems.
Root causes
- No Arabic code page on the printer model, or none selected.
- No shaping. Arabic letters take different forms depending on their neighbours; printers do not join them.
- No bidirectional reordering. Right-to-left text with embedded numbers needs the Unicode bidi algorithm.
- Receive-buffer overrun. A cheap network printer has a small buffer; one large job can overflow it.
Fixing 1–3 in text mode means different handling for every printer brand supported.
Architecture: render on the host, send raster in bands
- Shape and reorder the text on the host (
arabic-reshaper+python-bidi, or the browser's own text engine). - Draw the receipt onto a canvas exactly as wide as the paper in dots — 576 for an 80 mm head, 384 for 58 mm, always a multiple of 8.
- Binarise and encode as ESC/POS raster
GS v 0, in bands of 128 rows, each band self-contained. - Network printers: one short TCP connection per band to port 9100, ~50 ms pause. Idle sockets to cheap printers drop; per-job connections are boring and reliable.
- Frame the job:
ESC @first; optional drawer pulseESC p 0 25 250;LF LF LFthenGS V 66 0(feed and partial cut).
The host decides what is on the paper, so the same bytes print the same on every printer. The trade-offs are real and stated: more bytes per receipt (tens of KB) and slower Bluetooth links — hence compact receipts.
Operational rules
- Never fall back silently to a browser print dialog. On failure show an on-screen error with retry; keep the sale.
- Queue per printer so two terminals never interleave bytes on one printer.
- Static IP for network printers and the same subnet without client isolation; verify with
telnet <ip> 9100. - Kitchen tickets use the same raster path, which is why Arabic dish names and modifiers print reliably.
Reference implementation
A symptoms → causes → fixes table, a network checklist and a runnable example that works without a printer
(--out receipt.bin --png preview.png) are open source: github.com/rawaerp/arabic-thermal-printer-escpos-guide.
RAWA prints receipts and kitchen tickets this way over network, USB and Bluetooth on standard 80 mm ESC/POS printers.
Hardware: https://rawaerp.com/guide/pos-hardware-jordan
Common questions
Why do Arabic receipts print as question marks or disconnected letters?
Four distinct causes: no Arabic code page on the printer, no letter shaping, no bidirectional reordering, and receive-buffer overrun on cheap network printers.
What image width should I use?
The printable width in dots as a multiple of 8: 576 for an 80 mm head and 384 for a 58 mm head.
Why send the image in bands?
A cheap network printer has a small receive buffer; sending 128-row bands, one short connection per band to port 9100 with a short pause, avoids overruns and half-printed receipts.