Printer Driver

This year I had the pleasure of working with some very dated receipt printers for work. Printing loyalty coupons for a wide variety of cash register systems was quite a challenge. But along the way I really started to like this little old printing protocol that could.

To my complete surprise even today, many cash registers run Windows, and as such our loyalty integration was running on Windows as well. To keep installation as easy as possible it had to be written natively or in a framework that comes preinstalled. You can say what you want about .NET Framework 4.5. But applications written in it install and run on pretty much any Windows system you will find, so that’s what we target.

Printers

So with that out of the way, let’s talk thermal receipt printers.

Thermal Receipt Printer

You have probably seen these before. They are magical, don’t require ink, you can put in meters of cheap paper at a time, and they are fast too.

And more of these than I would have believed are connected with serial cables, or to be more precise, RS-232. And why wouldn’t they? Sending a couple of lines of text doesn’t take a lot of data, so bandwidth in the order of 10k bits per second is fine. But the first time we encountered these, we were printing to them from normal Windows drivers.

Since these printers have very limited support for graphics and fonts, the Windows solution is to rasterize full documents and send them over the wire as bitmaps. As you can imagine, that is not fast, and indeed, we quickly found ourselves waiting 10 seconds or longer for printing simple receipts.

ESC/POS

After testing a couple of .NET frameworks for printing QR codes, I finally realized that all of them were either incompatible with .NET 4.5 or just plain incorrect with very limited compatibility. However, the Java world was set up better, and after testing with some printers, it became clear that escpos-coffee was miles ahead of any .NET library. They were battle-tested, compatible with all our printers, printed graphics correctly, and had tests to boot.

A little aside on the protocol itself. There is really not all that much to it, since it is 100% ASCII text. That means sending plain text down a COM port with an ESC/POS printer attached will straight up print that text. From there, they essentially took the idea of progressive enhancement to the maximum. The right combination of hidden ASCII control characters, like the ESC character that is lending its name to the protocol, will change fonts, underline, indent, or even encode full bitmaps with or without dithering.

The full specification is quite far out of scope, but using that wonderful Java library, a hello world can look like this:

escpos = new EscPos(new PrinterOutputStream(printService));

Style title = new Style()
        .setFontSize(Style.FontSize._3, Style.FontSize._3)
        .setJustification(EscPosConst.Justification.Center);

Style subtitle = new Style(escpos.getStyle())
        .setBold(true)
        .setUnderline(Style.Underline.OneDotThick);
Style bold = new Style(escpos.getStyle())
        .setBold(true);

escpos.writeLF(title,"My Market")
        .feed(3)
        .write("Client: ")
        .writeLF(subtitle, "John Doe")
        .feed(3)
        .writeLF("Cup of coffee                      $1.00")
        .writeLF("Botle of water                     $0.50")
        .writeLF("----------------------------------------")
        .feed(2)
        .writeLF(bold,
                  "TOTAL                              $1.50")
        .writeLF("----------------------------------------")
        .feed(8)
        .cut(EscPos.CutMode.FULL);


escpos.close();

escpos-sharp

So naturally, I stole it.

Based on their excellent work, with some automatic code transformation, and some manual cleanup, I transformed the library into a modern .NET package. All of the tests are carried over; it works just as well in practice. It works with modern and very old .NET and has all the CI/CD you could ever hope for.

That sample above translates to this in C#, which does feel very faithful to me:

using var result = new MemoryStream();
using var escpos = new EscPos(result);

Style title = new Style()
    .SetFontSize(Style.FontSize._3, Style.FontSize._3)
    .SetJustification(Justification.Center);

Style subtitle = new Style(escpos.GetStyle())
    .SetBold(true)
    .SetUnderline(Style.Underline.OneDotThick);
Style bold = new Style(escpos.GetStyle()).SetBold(true);

escpos
    .WriteLF(title, "My Market")
    .Feed(3)
    .Write("Client: ")
    .WriteLF(subtitle, "John Doe")
    .Feed(3)
    .WriteLF("Cup of coffee                      $1.00")
    .WriteLF("Botle of water                     $0.50")
    .WriteLF("----------------------------------------")
    .Feed(2)
    .WriteLF(bold, "TOTAL                              $1.50")
    .WriteLF("----------------------------------------")
    .Feed(8)
    .Cut(EscPos.CutMode.FULL);

This has been working great for us, and I am hoping the next person fortunate enough to stumble into the wonderful world of legacy .NET and ESC/POS printing has an easier time.

If you are that person, please find the EscPosSharp GitHub, and the EscPosSharp NuGet.

Check Out the Source!