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.
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):
print://with/some/parameterswhere 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:
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
The first and the second ones are usually processed by the default browser, while the third one is processed by the default mail client.
regedit tool and open the
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
HKEY_CLASSES_ROOT section. Default parameter type should be set to
and the value to
URL:print. Add another
REG_SZ parameter named
URL Protocol with no value.
Finally, create the
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 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.
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
Programclass with the
Mainmethod, the application entry point. This method has
argsparameter. As we have mentioned above, URL will be passed into this parameter, so we will get something like
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
PrintDocument class, set’s dimensions to millimeters (and calculates printing area width too), so all the positions and sizes
PrintPage method are in millimeters. It has the protected
that has been already setup. The calculated printing area width is available using the protected
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.
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.
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.