CrossEscPos.Transports
1.2.0
dotnet add package CrossEscPos.Transports --version 1.2.0
NuGet\Install-Package CrossEscPos.Transports -Version 1.2.0
<PackageReference Include="CrossEscPos.Transports" Version="1.2.0" />
<PackageVersion Include="CrossEscPos.Transports" Version="1.2.0" />
<PackageReference Include="CrossEscPos.Transports" />
paket add CrossEscPos.Transports --version 1.2.0
#r "nuget: CrossEscPos.Transports, 1.2.0"
#:package CrossEscPos.Transports@1.2.0
#addin nuget:?package=CrossEscPos.Transports&version=1.2.0
#tool nuget:?package=CrossEscPos.Transports&version=1.2.0
ESC/POS Receipt Printer Emulator
π¨οΈ This app emulates a networked receipt printer to test your ESC/POS commands against.
About
- Cross-platform application (Avalonia 12 + SkiaSharp + .NET 10), runs on Windows, macOS and Linux
- Listens for ESC/POS commands over TCP/IP and (optionally) a serial port
- Logs commands and visually represents the resulting receipt(s)
- Renders 1D barcodes and 2D codes (QR, PDF417, DataMatrix, Aztec), bit images, and page mode
- Answers status queries (
DLE EOT,GS r, Automatic Status Back) β simulate paper-out, cover, drawer, offline and error states from the Printer state panel - Signals buzzer / cash-drawer events with a sound and on-screen toast
- Configure the TCP listen address/port and serial port live from the UI
- Export rendered tickets to PNG β all in one image, or one file per cut
- It support different text formattings in the same line, although a few combinations were tested.
Cross-platform fork. This project began as a cross-platform port of roydejong/EscPosEmulator (originally a Windows/WPF app). It has been migrated to Avalonia + SkiaSharp + .NET 10 so it runs on Windows, macOS and Linux, and extended with barcode/QR rendering and a serial transport. All credit for the original emulator goes to the upstream author.
π· This is an unfinished experiment. Use at your own risk and keep your expectations low. π
Download
Pre-built, self-contained apps (no .NET install required) are published on the Releases page:
| Platform | Artifact |
|---|---|
| Windows (x64) | CrossEscPos-win-x64.zip |
| Linux (x64) | CrossEscPos-linux-x64.tar.gz |
| macOS (Intel) | CrossEscPos-osx-x64.zip (.app bundle) |
| macOS (Apple Silicon) | CrossEscPos-osx-arm64.zip (.app bundle) |
Releases are produced by the Release GitHub Actions workflow on
each v* tag.
macOS first launch. The
.appis ad-hoc signed but not notarized (no paid Apple Developer ID). macOS quarantines anything downloaded from the internet, so on first launch you may see "CrossEscPos is damaged and can't be opened" (especially on Apple Silicon). Clear the quarantine flag once, then open it:xattr -dr com.apple.quarantine /path/to/CrossEscPos.app open /path/to/CrossEscPos.app(Right-click β Open also works once the quarantine is cleared.)
Built with
- .NET 10 Β· Avalonia 12 Β· SkiaSharp (rendering) Β· CommunityToolkit.Mvvm (MVVM)
- ZXing.Net (1D barcodes) Β· QRCoder (QR codes) Β· Ardalis.SmartEnum (class-based enums) Β· System.IO.Ports (serial)
- ESC-POS-.NET (the Monitor test client) Β· LibUsbDotNet (direct USB printing)
Supported commands
β οΈ Support is currently limited to only a subset of ESC/POS. Even the commands listed here may only be partially implemented.
- Raw Text
- LF: Line feed
- CR: Carriage return
- ESC Commands:
- Initialize printer (
ESC @) - Toggle italic (
ESC 4/ESC 5) [possibly deprecated?] - Select font (
ESC M) - Select charset (
ESC R) - Select character table (
ESC t) - Select justification (
ESC a) - Select line spacing (
ESC 2/ESC 3) - Toggle emphasis (
ESC E) - Toggle underline (
ESC -) - Set print text mode (
ESC !) - Full cut (
ESC m) - Partial cut (
ESC i) - Print and feed n lines (
ESC d) - Print and feed paper (
ESC J) - Generate pulse / kick cash drawer (
ESC p m t1 t2) - Select character code table (
ESC t) β PC437/850/852/858/860/863/865/866/1252 remapped to Unicode - Beeper (
ESC ( A) - Bit image (
ESC *) β 8-dot and 24-dot inline raster - Page mode: select page / standard mode (
ESC L/ESC S), print area (ESC W), direction (ESC T), absolute position (ESC $) - User-defined characters (
ESC &/ESC %/ESC ?) β parsed & stored
- Initialize printer (
- Control characters:
- Buzzer / beeper (
BEL, 0x07) - Form feed (
FF) β prints the page in page mode - Cancel (
CAN) β cancels page data
- Buzzer / beeper (
- DLE (real-time) Commands:
- Real-time status (
DLE EOT n, n=1-4) - Real-time request / recover (
DLE ENQ) - Real-time cash-drawer pulse (
DLE DC4 1 m t)
- Real-time status (
- FS Commands:
- Print stored logo (
FS p n m) - Auto cut (
FS } 0x60 n)
- Print stored logo (
- GS Commands:
- Select character size
- Select cut mode and cut paper
- Paper eject (
GS e n [m t]) - Print raster image (
GS v 0 [m xL xH yL yH ...pixels]) - Print 1D barcode (
GS k) β UPC-A/E, EAN-13/8, CODE39, CODE93, CODE128, ITF, CODABAR (both function A & B forms) - Set barcode height / module width (
GS h/GS w) - Select HRI text position / font (
GS H/GS f) - Print 2D symbols (
GS ( k) β QR Code (cn=49), PDF417 (cn=48), DataMatrix (cn=54), Aztec (cn=55) - Status / transmit-back: paper & drawer status (
GS r), printer ID (GS I), Automatic Status Back (GS a) - Download bit image: define (
GS *) and print (GS /) - Set motion units (
GS P), absolute/relative vertical position (GS $/GS \) - Config (accepted/ignored): user setup (
GS ( E), print control (GS ( K), response request (GS ( H)
The emulator is bidirectional: status commands (DLE EOT, GS r, GS I) and Automatic Status
Back reply to the host over the same TCP/serial connection, driven by the Printer state panel
(right side) where you can simulate paper-out/near-end, cover open, cash-drawer open/closed,
offline, and error conditions. Like a real device, the emulator refuses to print while it isn't
ready (out of paper, cover open, offline, or in an error state) and shows a notification instead.
Barcodes and QR codes render inline on the receipt, with optional HRI text:
Not yet implemented
A few things remain partial or unimplemented:
- Page-mode coordinate system β page mode buffers output and rasterizes it on
FF, but absolute/relative positioning (ESC $,GS $,GS \) and print direction (ESC T) are accepted as no-ops rather than fully positioned. - User-defined glyph substitution β
ESC &glyphs are parsed and stored, but the inline text renderer still draws the font glyph rather than the custom bitmap. - MaxiCode / GS1 DataBar / Composite 2D symbologies (
GS ( kcn=50/51/52). - Graphics commands
GS ( L/GS 8 L(NV/raster graphics store-and-print) and otherESC *-family densities. - Real-time
DLE DC4functions other than the cash-drawer pulse (power-off, recover-and-cancel, buzzer). - Katakana / CJK code pages render as missing glyphs since the bundled Latin font has no such glyphs.
Contributions welcome β new commands follow the simple BaseCommand pattern in
EscPos/Commands and are registered in
EscPosInterpreter.RegisterCommands.
Connecting
The emulator accepts ESC/POS data over two transports. Both can be changed live from the UI (left panel): pick a TCP listen address and port and Start/Stop the listener, or select a serial port + baud and Open/Close it (β³ refreshes the port list). The environment variables below set the initial values at startup:
| Variable | Default | Meaning |
|---|---|---|
ESCPOS_LISTEN_ADDRESS |
0.0.0.0 |
Initial TCP bind address (0.0.0.0 = all interfaces, 127.0.0.1 = localhost). |
ESCPOS_TCP_PORT |
9100 |
Initial TCP listen port. Set to off / 0 to start with TCP stopped. |
ESCPOS_SERIAL_PORT |
(unset) | Serial device to auto-open (e.g. /dev/ttys004, COM3). Unset = serial closed. |
ESCPOS_SERIAL_BAUD |
9600 |
Serial baud rate. |
ESCPOS_DEBUG_DUMP |
(off) | Set to 1 to dump every received payload to last_* files. |
ESCPOS_RENDER_BACKEND |
skia |
Render backend: skia (default) or imagesharp (managed). The --backend arg overrides it. |
Examples:
# (run is shorthand for: dotnet run --project src/CrossEscPos.App.Desktop)
dotnet run --project src/CrossEscPos.App.Desktop # TCP only, port 9100
ESCPOS_TCP_PORT=9200 dotnet run --project src/CrossEscPos.App.Desktop # TCP on 9200
ESCPOS_SERIAL_PORT=/dev/ttys004 dotnet run --project src/CrossEscPos.App.Desktop # TCP 9100 + serial
ESCPOS_TCP_PORT=off ESCPOS_SERIAL_PORT=COM3 dotnet run --project src/CrossEscPos.App.Desktop # serial only
Pick the render backend (for A/B testing the two rendering libraries) with --backend skia
(default) or --backend imagesharp (the managed, no-native backend). The active backend is shown in
the window title bar.
dotnet run --project src/CrossEscPos.App.Desktop -- --backend imagesharp # managed ImageSharp backend
dotnet run --project src/CrossEscPos.App.Desktop -- --backend skia # default SkiaSharp backend
The status panel shows the active TCP endpoint and serial port.
Testing serial without hardware (app-to-app on one machine)
You don't need a USB serial adapter. Create a virtual serial bridge β a pair of linked ports β then point the emulator at one end and your POS application (or a shell) at the other. Bytes written to one end appear on the other.
macOS / Linux β using socat (brew install socat /
apt install socat). A helper script is included:
./scripts/serial-bridge.sh
# It prints a linked pair, e.g.:
# PORT A (emulator): /dev/ttys004
# PORT B (your app): /dev/ttys005
# Leave it running.
Then, in two more terminals:
# Terminal 2 β run the emulator on port A
ESCPOS_SERIAL_PORT=/dev/ttys004 dotnet run --project src/CrossEscPos.App.Desktop
# Terminal 3 β send a receipt from "another app" on port B
cat test_receipt.txt > /dev/ttys005
# β¦or from your own program, just open /dev/ttys005 like a normal serial port
# (9600 8N1) and write ESC/POS bytes to it.
The receipt appears in the emulator window. Because the interpreter is stateful across reads, fragmented serial writes (commands split across packets) are handled correctly.
Windows β install com0com and create a linked pair
(e.g. COM3 β COM4). Run the emulator with ESCPOS_SERIAL_PORT=COM3 and have your application
write to COM4.
Monitor (built-in test client)
Sending test jobs is the monitor's job β the emulator is the device, the monitor is the POS-side client that drives it over the wire (just like a real application would). Click Open monitorβ¦ to launch a second window (built on ESC-POS-.NET) and pick a transport:
- TCP/IP β connect to the emulator's listener (or any networked printer).
- Serial β pick a port + baud; pairs with the emulator's serial transport via a virtual port bridge.
- USB β print directly to a real USB printer selected from the connected-device list (by
VID:PID), via libusb. This is send-only (no status), and needs native libusb installed
(macOS
brew install libusb, Debian/Ubuntuapt install libusb-1.0-0; bundled on Windows) and the OS not already holding the device.
It then lets you exercise the target without writing any code:
- Print a sample receipt, all 1D barcodes, or QR / PDF417 / DataMatrix / Aztec.
- Send the full feature test receipt, open the cash drawer, buzz, or cut.
- Watch the printer status the emulator reports back: toggle paper-out / cover / drawer / offline in the Printer state panel and the monitor's status display updates live (via Automatic Status Back), confirming the emulator's status responses are wire-correct. When the printer isn't ready, the emulator drops the job and shows a notification, just like real hardware.
Toggling the emulator's Printer state panel pushes status to the monitor in real time β here the printer reports paper low and a recoverable error, so the monitor shows Not ready:
Exporting tickets
Each cut (ESC i / ESC m / GS V) starts a new receipt β a "page". The Export buttons in the
left panel save the rendered tickets as PNG:
- Export all (single image) β stacks every receipt into one tall PNG (a save dialog).
- Export each cut (folder) β writes one
receipt_NNN.pngper cut into a chosen folder.
Architecture & packages
The emulator is split into layered, independently-publishable packages so the rendering backend is
swappable and the core runs headless (and in the browser). The namespace is unified under
CrossEscPos.* (organized by feature/directory, not by package), so types resolve across assemblies.
| Package | Namespace(s) | Role | Depends on |
|---|---|---|---|
CrossEscPos.Abstractions |
CrossEscPos, CrossEscPos.Graphics |
Backend-agnostic rendering + printer contracts (IReceiptCanvas, IReceiptImage, IReceiptImageFactory, ITypefaceProvider, IImageEncoder, IReceiptPrintable, IPrinterResponder) |
β |
CrossEscPos.Core |
CrossEscPos.Emulator, CrossEscPos.EscPos, β¦ |
Headless ESC/POS interpreter, printer state machine, receipt document model, barcode/QR generation. ESC/POS code maps (2D families, code tables, barcode systems, status requests) are behaviour-carrying SmartEnums, not magic-number switches |
Abstractions, QRCoder, ZXing.Net, Ardalis.SmartEnum |
CrossEscPos.Rendering.Skia |
CrossEscPos.Rendering.Skia |
The default render backend (SkiaSharp). Swap it for another IReceiptImageFactory/ITypefaceProvider/IImageEncoder |
Abstractions, SkiaSharp |
CrossEscPos.Rendering.ImageSharp |
CrossEscPos.Rendering.ImageSharp |
A 100% managed render backend (ImageSharp) β no native dependency, so it runs in Blazor WASM without a native relink. Same output as the Skia backend | Abstractions, SixLabors.ImageSharp.Drawing |
CrossEscPos.Transports |
CrossEscPos.Transports |
TCP / serial / USB transports (desktop only) | Core, System.IO.Ports, LibUsbDotNet, ESC-POS-.NET |
CrossEscPos.Controls |
CrossEscPos.Controls |
Reusable Avalonia controls (ReceiptView, PrinterStatePanel) β host apps consume these. Backend-agnostic (no SkiaSharp dependency) |
Core, Avalonia |
Core carries no UI and no graphics-backend dependency, so the library works headless or in WASM.
The host (desktop, browser, or your own app) is the composition root: it picks a backend and injects it.
var imageFactory = new SkiaImageFactory();
var typefaces = new SkiaTypefaceProvider();
var printer = new ReceiptPrinter(PaperConfiguration.Default, imageFactory, typefaces);
printer.FeedEscPos(escPosBytes); // byte[] β ESC/POS is binary
using var image = printer.CurrentReceipt.Render(); // IReceiptImage
new SkiaImageEncoder().EncodePng(image, outputStream);
π¦ Package usage guides: docs/packages/ β getting started, the core
emulator, rendering & custom backends, the Avalonia controls, the transports, and a
WASM JavaScript interop for rendering ESC/POS to PNG from any web
project (no .NET on the caller's side).
Building & running
Requires the .NET 10 SDK (and the wasm-tools workload for the browser head:
dotnet workload install wasm-tools).
# Desktop app (Windows / macOS / Linux)
dotnet run --project src/CrossEscPos.App.Desktop
# Headless: ESC/POS bytes -> PNG, no UI, no Avalonia
dotnet run --project samples/CrossEscPos.Headless -- test_receipt.txt out.png
# Browser (Avalonia WASM head): launches a dev server and opens the app
dotnet run --project src/CrossEscPos.App.Browser
# Blazor web emulator: pick the render engine (managed ImageSharp by default), render ESC/POS
# in the browser, and drive the printer state β the managed-backend showcase
dotnet run --project samples/CrossEscPos.Web
# Tests (unit + headless UI)
dotnet test CrossEscPos.slnx
The Blazor web app (samples/CrossEscPos.Web) renders receipts entirely
in the browser and lets you switch the render engine at runtime β ImageSharp (managed) by default
(no native relink) or SkiaSharp (native). It mirrors the desktop ReceiptView / PrinterStatePanel
as Razor components. Paste ESC/POS text, upload a file, or load the bundled sample.
- Windows / macOS: no extra setup β native rendering libraries ship with the Avalonia packages.
- Linux: install the usual font/render native deps if they are missing, e.g.
sudo apt install libfontconfig1 libfreetype6(Debian/Ubuntu).
Testing
Two test projects under tests/:
CrossEscPos.Core.Testsβ emulation coverage. ESC/POS sequences are fed through the interpreter and the observable results (rendered draws, printer state, host responses, events) asserted: text and control characters, styling (emphasis/italic/size/justification), cutting and feeding, 1D/2D barcodes and bit images, status/transmit-back (DLE EOT,GS r,GS I,GS a), cash drawer and buzzer, real-time commands, code pages, page mode and not-ready handling β plus exactStatusByteBuilderbit layouts and theSmartEnumcode maps. Rendering goes to a synthetic backend, so the suite runs with no SkiaSharp (proving the core is headless).CrossEscPos.Controls.Testsβ headless Avalonia UI tests for the controls (two-way state binding, receipt image rendering).
dotnet test CrossEscPos.slnx
Fonts & license
Receipt text is rendered with JetBrains Mono, embedded in the
CrossEscPos.Rendering.Skia package (under
src/CrossEscPos.Rendering.Skia/Assets/Fonts/) so output
is identical across platforms and works in the browser sandbox (no file IO). JetBrains Mono is licensed
under the SIL Open Font License 1.1; the full license text is included at
src/CrossEscPos.Rendering.Skia/Assets/Fonts/OFL.txt. Per the OFL, the font is redistributed here under its
original license and "JetBrains Mono" is a trademark of JetBrains s.r.o. To swap in a different
monospace font, replace the receipt-mono*.ttf files (and keep its license alongside).
Emulated printer
This program emulates a printer with the following specifications:
- 80mm paper width
- 72mm printing width
- 180x180dpi
- ASCII Font A/B: 12x24 pixels
- Automatic line feed
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- CrossEscPos.Core (>= 1.2.0)
- ESCPOS_NET (>= 3.0.0)
- LibUsbDotNet (>= 3.0.224)
- SixLabors.ImageSharp (>= 2.1.13)
- System.IO.Ports (>= 10.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.0 | 48 | 7/7/2026 |