Печать чеков на термопринтере, по-русски.
Библиотека закрывает то, обо что обычно бьются при первой же попытке напечатать чек не на латинице: кириллицу в кодировке принтера, моноширинную раскладку под ширину ленты, QR, штрихкоды, картинки и доставку байт на сетевой принтер.
У текстовой части нет обязательных зависимостей — только стандартная библиотека Python.
from escpos_ru import Receipt, Printer, Charset
r = Receipt(width=32, charset=Charset.CP866)
r.rule()
r.bold_line("КУХНЯ / ПИЦЦА", align="center")
r.rule()
r.left_right("Заказ №1043", "12:40")
r.rule("-")
r.item(1, "Пицца Пепперони 33 см", 2, note="без лука")
r.rule("-")
r.left_right("Позиций:", "7")
r.feed(2).cut()
print(r.preview()) # посмотреть глазами
Printer.network("192.168.1.50").send(r) # напечатать================================
КУХНЯ / ПИЦЦА
================================
Заказ №1043 12:40
--------------------------------
1. Пицца Пепперони 33 см x2
> без лука
--------------------------------
Позиций: 7
================================
Потому что основная боль не в отправке байтов, а в кириллице.
Термопринтер не знает Unicode. Он печатает байты по таблице, которую вы ему
указали командой ESC t n. Отправите UTF-8 — получите кракозябры. Отправите
CP866, не выбрав страницу 17, — получите другие кракозябры. А если модель
кириллицу не поддерживает вообще, честный выход один: транслитерация.
Здесь это три режима, и переключаются они одной строкой:
Charset |
Команда принтеру | Когда |
|---|---|---|
CP866 |
ESC t 17 |
большинство моделей с поддержкой кириллицы |
CP1251 |
ESC t 46 |
часть моделей, Windows-совместимые |
TRANSLIT |
— | принтер кириллицу не умеет: Пицца → Picca |
ASCII |
— | гарантированно безопасный вывод |
Receipt(width=32, charset=Charset.CP866) # байты CP866 + ESC t 17
Receipt(width=32, charset=Charset.TRANSLIT) # латиница, страница не выбираетсяТранслитерация удлиняет текст: Лимонад Дюшес 0.5 (17 знаков) превращается
в Limonad Dyushes 0.5 (19). Строка, которая идеально влезала в 32 колонки
по-русски, на принтере уезжает на две позиции.
Поэтому раскладка здесь считается в том виде, в котором текст уйдёт на бумагу, а не в исходном. И перед печатью чек можно проверить:
problems = r.check()
# ['строка 12 длиннее 32: 34 — "3. Limonad Dyushes 0.5 x3"']
r.check(strict=True) # или сразу исключениемcheck() ловит три вещи: строки длиннее ширины ленты, табуляции (ломают
моноширинность) и символы, которых нет в выбранной кодировке.
preview() возвращает текст чека без управляющих команд — ровно то, что
окажется на бумаге. Это позволяет отлаживать макет, не подходя к железу,
и писать тесты на раскладку.
python -m escpos_ru # демонстрационный чек в двух кодировкахМодуль escpos_ru.layout — чистые функции «строки на входе, строки на
выходе», без байтов и без железа:
from escpos_ru import layout
layout.rule(32, "=") # '================================'
layout.center("ИТОГО", 32)
layout.left_right("Сумма:", "1300 руб", 32)
layout.wrap("Позвонить за 10 минут", 32, indent=" ")
layout.item(1, "Пицца", 2, width=32, note="без лука")
layout.money(1300) # '1300 руб'Строка позиции сама переносит длинное название, прижимает количество вправо и выравнивает продолжения по началу названия:
1. Пицца Пепперони 33 см x2
> без лука, соус отдельно
2. Сырный борт x2
r.qr("https://example.com/order/1043", module_size=5, ec="M")
r.barcode("4600000000000", kind="EAN13", height=80)
r.image("logo.png", dither=False) # dither=False для чёткой графикиQR и штрихкод собираются вручную по спецификации (GS ( k, GS k) — без
сторонних генераторов. Поддержаны CODE128, CODE39, EAN13, EAN8, UPC-A,
ITF, CODABAR, CODE93.
Картинки — единственное место, где нужен Pillow:
pip install "escpos-ru[image]"Printer.network("192.168.1.50", port=9100) # сетевой, сырой поток
Printer.file("out/tickets") # в папку — отладка и страховка
Printer.windows("XP-80C") # драйвер Windows (pywin32)
Printer.serial("COM3", baudrate=9600) # COM-порт (pyserial)Транспорт никогда не роняет заказ: send() возвращает результат, а не
бросает исключение. Если сеть отвалилась, а указан fallback_dir, чек
уезжает в файл, и вы об этом узнаете из результата:
p = Printer.network("192.168.1.50", retries=2, fallback_dir="out/tickets")
res = p.send(r)
# SendResult(ok=True, transport='file(fallback)', where='out/tickets/...',
# error='192.168.1.50:9100 недоступен: ...')Зависимостей нет, ставить ничего не нужно:
python -m unittest discover -s tests -t .
# Ran 111 tests in 0.9s — OKТесты проверяют точные байтовые последовательности команд, раскладку на кириллице, поведение транслитерации и то, что чек не теряется при обрыве связи.
pip install -e . # или PYTHONPATH=. перед запуском
python examples/kitchen_ticket.py # кухонный чек по цехам
python examples/client_receipt.py # чек клиенту с ценами и скидкойОба примера только печатают предпросмотр в консоль и ничего не отправляют на принтер.
A receipt-printing library for ESC/POS thermal printers with first-class Cyrillic support — the thing that usually breaks on the first non-Latin receipt.
- Charset handling: CP866 (
ESC t 17), CP1251 (ESC t 46), plus a transliteration fallback for printers with no Cyrillic code page at all. - Layout in the printed alphabet: transliteration widens text
(
Лимонад Дюшес→Limonad Dyushes), so column math is done on what will actually hit the paper.receipt.check()catches overflow, tabs and unencodable characters before you waste paper. - Zero dependencies for text, QR codes and barcodes — pure standard library. Pillow only for images, pywin32 only for the Windows driver, pyserial only for COM ports.
- Preview without hardware:
receipt.preview()returns the plain-text receipt, so layout can be unit-tested. - Transports never lose a ticket:
send()returns a result instead of raising, and falls back to a file queue when the network printer is down.
from escpos_ru import Receipt, Printer, Charset
r = Receipt(width=32, charset=Charset.CP866)
r.bold_line("KITCHEN", align="center")
r.left_right("Order #1043", "12:40")
r.item(1, "Pepperoni pizza 33 cm", 2, note="no onion")
r.feed(2).cut()
Printer.network("192.168.1.50").send(r)MIT licensed.
MIT. См. LICENSE.