How to Print From a Web Page to a POS Printer

Dmitry Sikorsky
6 min readApr 7, 2020

--

Currently I’m working on the new project (basically, this is one of the reasons I write so few articles now). It is food delivery automation system (I own the pizza delivery company located in Kyiv, my hometown; some kind of hobby). It contains the point of sale applications, one of them is web-based. And, as any other POS application, it must print receipts using a thermal POS printer.

As you may know, while it is still possible to print a web page content using any kind of printer (and you basically can get perfect results if you need just to print an A4 document and if you know how to write correct CSS), printing something using a POS printer can turn into a real nightmare. It is rather difficult even when you have direct access to a printer, but you don’t have such access directly from a web page, so that might be tricky. You can’t get rid of browser’s print dialog (there are some workarounds, but still), you can’t send raw commands to a printer, can’t use cutter when you need it, you even can’t control margins and sizes in a normal way.

In the previous version of our system we used to use QZ Tray product to print from JavaScript. It worked with no problems. All you need to do is to buy it (otherwise it will display a bunch of warning windows), install client Java application, write some JavaScript on your web page, and it works. Really good and powerful solution.

But this time (basically because we are going to build a cloud-based product) I decided that I can’t rely on the third-party (expensive) products and must have full control over everything. Our solution should support most of the POS printers and I don’t want to spend much time learning printer control languages (EPL, ZPL, TSPL-EZ, and other abbreviations). Also, while I can’t avoid the need to install a client software on each computer that needs to be able to send anything to a printer, I need to make this software and installation process as simple as possible.

So, I decided to go with the following solution (currently I’ve tested it only on Windows, but as we checked, it should be possible to use it on Linux and Mac as well):

  • register custom protocol in the Windows registry (let’s say it is named print) to be processed by our client application;
  • write the client application that will handle print requests from the web pages;
  • add links with URLs like print://with/some/parameters where we need to send something to a printer.

Now I can confirm that this is a working, fast, and very simple solution, I really like it. And it allows to work with the printers on their native languages or using the unified solutions like the built-in .NET PrintDocument class. I’ve chosen the second option for now (to get it working ASAP as I wrote above). The only disadvantage of this solution (which I can see for now) is that printing is done using the Graphics class, so printer prints everything as image, and its built-in fonts are not used, so text is a bit blurry, not so clear as when it is printed using the command language. But still, receipts look good as you can see.

Registering Custom Protocol in the Windows Registry

Ok, let’s start from the first step. Windows (Linux and Mac as well) allows to specify which application should process URLs with a specific protocol. For example, I’m sure you all know http, https, and mailto protocols. The first and the second ones are usually processed by the default browser, while the third one is processed by the default mail client. Run regedit tool and open the HKEY_CLASSES_ROOT section. Then, navigate to the mailto subsection and expand its content:

I’ve selected the main parameter which specifies the application that processes this protocol. Now, when we see how it is done (or using some additional information), we can create our own print protocol.

Create new subsection named print inside the HKEY_CLASSES_ROOT section. Default parameter type should be set to REG_SZ and the value to URL:print. Add another REG_SZ parameter named URL Protocol with no value. Finally, create the EditFlags REG_DWORD parameter with 2 as the value (otherwise your protocol won’t work in Microsoft Edge).

Inside our print section create subsection named shell. Default parameter type should be set to REG_SZ and the value to open. Create open subsection inside the shell one. Default REG_SZ parameter has no value. Finally, inside the open subsection create the command one. Its default parameter type should be set to REG_SZ and the value to "C:\\[Path to your application]\\print.exe" "%1". (Please note that we need this "%1" after the path, otherwise our application won’t get the URL as an argument.) That’s all.

I have created a reg file for you, so you can just import it.

Custom Protocol Handling Application

In my case I’ve decided to write a Windows Forms desktop application because I need users to be able to select a printer, to adjust their receipts, choose logo file etc. In our case we will go with a simple console application to make it simpler to demonstrate the idea.

So please create new Windows Console application:

It already contains the Program class with the Main method, the application entry point. This method has args parameter. As we have mentioned above, URL will be passed into this parameter, so we will get something like print://xxx/yyy/zzz.

Now we will write 2 simple classes (and one more utility class). The first one will be abstract and will contain some simple logic that we can reuse to print different documents including one method to print text in columns. It uses the standard .NET PrintDocument class, set’s dimensions to millimeters (and calculates printing area width too), so all the positions and sizes in the PrintPage method are in millimeters. It has the protected Print method that allows to specify the printer name (it should be taken from the configuration instead) and method that will do the printing job. That method will just receive the Graphics object that has been already setup. The calculated printing area width is available using the protected width field.

The second one will inherit our base class and prints simple receipt (all the data will be hardcoded to make it simple). As you can see, we use our method for printing text in columns. That method calculates size of the rendered text according to the available space (printing area width multiplied by the value of the RelativeWidth property of each column), prints the text and returns height of the highest column.

Put it all Together

Now press Windows+R, enter print://1234, and press Enter.

We can see that our application has successfully printed our demo receipt. (And it correctly parsed and printed the fake order ID we had specified in the URL.) But. Our POS printer has cutter, but it didn’t cut the receipt, and this is very bad, especially if you have to print hundreds of times per day. Fortunately, POS printer driver allows us to specify that printer should cut the paper after each document is printed:

If you try to execute the command again, you will see, that now everything works as expected and printer cuts the paper.

Conclusions

As I have mentioned, this is quick and working solution, but it allows replace PrintDocument class with the raw printer commands to get full control over the printing process if you need that. But I like how simple it is to send print commands to the application and process them.

I have created the demo project for this post on GitHub. Please, feel free to ask any questions or share your ideas how to make this approach work better.

--

--

Dmitry Sikorsky

Senior software engineer, technical lead, system architect. Owner and CEO at UBRAINIANS.