How to Print From a Web Page to a POS Printer
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.