Printing a receipt in English is usually easy.
Printing a reliable Arabic receipt across different thermal printers is not.
The difficulty is not Flutter itself. It is the combination of printer firmware, character encodings, paper width, command languages, Bluetooth/USB transport, bitmap rendering, and the fact that two printers that claim ESC/POS compatibility may still behave differently.
Why Arabic text is hard
Many low-cost thermal printers were designed around limited code pages.
Arabic introduces several problems:
- Right-to-left layout.
- Connected letter shaping.
- Mixed Arabic and English.
- Arabic numerals and Latin numbers.
- Different code-page support between vendors.
- Alignment differences.
- Fonts that do not exist on the device.
- Firmware that interprets the same bytes differently.
If you simply send a UTF-8 string, some printers produce question marks, separated letters, reversed words, or nothing useful.
Strategy 1: Native printer text commands
When the printer supports Arabic correctly, sending text commands is the fastest option.
Advantages:
- Small payload.
- Fast printing.
- Sharp text.
- Efficient Bluetooth usage.
Disadvantages:
- Printer-specific code pages.
- Inconsistent shaping.
- Limited fonts.
- Difficult mixed-language layouts.
This approach is excellent when you control the hardware model and can certify the exact configuration.
Strategy 2: Render the receipt as a bitmap
For unknown or inconsistent printers, bitmap printing is often the most reliable Arabic strategy.
The application renders the receipt exactly as it should appear, including:
- Arabic shaping.
- RTL layout.
- Logos.
- Tables.
- Totals.
- QR codes.
- Mixed language.
Then it sends the raster image to the printer.
The printer no longer needs to understand Arabic. It only needs to print pixels.
The trade-off is performance.
Large bitmaps mean larger payloads, more memory, slower Bluetooth transfer, and sometimes overheating or buffer problems on cheap printers.
Strategy 3: Hybrid receipts
A useful compromise is hybrid printing:
- Use native printer commands for separators, feeds, cuts, and simple English/numeric content.
- Rasterize only the Arabic or complex sections.
This can reduce transfer size while preserving visual correctness.
ESC/POS is not the only world
Some label printers use TSPL or vendor-specific command sets.
That means a production printing layer should not spread ESC/POS commands throughout business code.
I prefer a printer abstraction with capabilities such as:
- printText
- printImage
- printQr
- feed
- cut
- setAlignment
- setDensity
Adapters translate those operations into ESC/POS, TSPL, or a custom protocol.
The invoice feature should not know which printer language is underneath it.
What I test
A printer integration is not finished when one receipt works once.
I test:
- 58mm and 80mm widths.
- Long Arabic product names.
- Mixed Arabic/English lines.
- Very large invoices.
- Discounts and taxes.
- Reconnect after Bluetooth drop.
- Reprinting.
- Rapid consecutive receipts.
- Low-end printers with small buffers.
- Different Android vendors.
- Bitmap chunking.
- Paper feed and cut behavior.
Printing is hardware integration, so it needs hardware-level testing.
The engineering lesson
When software touches physical devices, “standards compliant” does not mean “behaves identically.”
Build capability detection where possible, isolate printer protocols, keep diagnostics, and always have a fallback rendering strategy.
For Arabic business software, printing is not a small UI feature. It is part of the core transaction workflow.