Writing nice receipt in C# WPF for printing on thermal printer POS Writing nice receipt in C# WPF for printing on thermal printer POS wpf wpf

Writing nice receipt in C# WPF for printing on thermal printer POS


In the past when doing this I split up the receipt into separate parts that used different fonts or alignments such as Header, Body, Footer.

I used the following class layout to encapsulate my printed text definition.(where you get the Font from and how you manage its lifetime is up to you)

public class PrintText{    public PrintText(string text, Font font) : this(text, font, new StringFormat()) {}    public PrintText(string text, Font font, StringFormat stringFormat)    {        Text = text;        Font = font;        StringFormat = stringFormat;    }    public string Text { get; set; }    public Font Font { get; set; }    /// <summary> Default is horizontal string formatting </summary>    public StringFormat StringFormat { get; set; }}

When there are longer lists of texts using the same font & padding then using a stringbuilder to build up your text makes life easy so you get a visual of how it will look just from inspecting your code.

If you had static text you can fit it all together as so:

var sb = new StringBuilder();sb.AppendLine("Start of receipt");sb.AppendLine("================");sb.AppendLine("Item 1");sb.AppendLine("Item 2");sb.AppendLine("================");

Or if the data is a bit dynamic pass in some object you can iterate over and append your formatted text:

private class ReceiptItem{    public string Name { get; set; }    public decimal Cost { get; set; }    public int Amount { get; set; }    public int Discount { get; set; }    public decimal Total { get { return Cost * Amount; } }}
const int FIRST_COL_PAD = 20;const int SECOND_COL_PAD = 7;const int THIRD_COL_PAD = 20;var sb = new StringBuilder();sb.AppendLine("Start of receipt");sb.AppendLine("================");foreach (var item in receiptItems){    sb.Append(item.Name.PadRight(FIRST_COL_PAD));    var breakDown = item.Amount > 0 ? item.Amount + "x" + item.Cost : string.Empty;    sb.Append(breakDown.PadRight(SECOND_COL_PAD));    sb.AppendLine(string.Format("{0:0.00} A", item.Total).PadLeft(THIRD_COL_PAD));    if (item.Discount > 0)    {        sb.Append(string.Format("DISCOUNT {0:D2}%", item.Discount).PadRight(FIRST_COL_PAD + SECOND_COL_PAD));        sb.Append(string.Format("{0:0.00} A", -(item.Total / 100 * item.Discount)).PadLeft(THIRD_COL_PAD));        sb.AppendLine();    }}sb.AppendLine("================");

The output will look like:

Start of receipt================Joes Food           1x10      10.00 ADISCOUNT 10%                  -1.00 AFun Facts           1x20      20.00 ADISCOUNT 15%                  -3.00 ABag of Sand         7x40     280.00 A================

Using the PrintText class earlier we can store our nicely formatted string builder output

var printText = new PrintText(sb.ToString(), new Font("Monospace Please...", 8));

Then finally use that when attempting to draw the string

var layoutArea = new SizeF(AvailableWidth, 0);SizeF stringSize = g.MeasureString(printText.Text, printText.Font, layoutArea, printText.StringFormat);RectangleF rectf = new RectangleF(new PointF(), new SizeF(AvailableWidth, stringSize.Height));g.DrawString(printText.Text, printText.Font, Brushes.Black, rectf, printText.StringFormat);

You can also play around with a few different graphical tweaks if the text doesn't print quite right such as:

g.SmoothingMode = SmoothingMode.AntiAlias;g.InterpolationMode = InterpolationMode.HighQualityBicubic;g.PixelOffsetMode = PixelOffsetMode.HighQuality;


I have designed a simple and smooth receipt design, i hope it will help you.

public class PrintJob{    private PrintDocument PrintDocument;    private Graphics graphics;    private Order order { set; get; }    private Shop shop { set; get; }    private int InitialHeight = 360;    public PrintJob(Order order, Shop shop)    {        this.order = order;        this.shop = shop;        AdjustHeight();    }    private void AdjustHeight()    {        var capacity = 5 * order.ItemTransactions.Capacity;        InitialHeight += capacity;        capacity = 5 * order.DealTransactions.Capacity;        InitialHeight += capacity;    }    public void Print(string printername)    {        PrintDocument = new PrintDocument();        PrintDocument.PrinterSettings.PrinterName = printername;        PrintDocument.PrintPage += new PrintPageEventHandler(FormatPage);        PrintDocument.Print();    }    void DrawAtStart(string text, int Offset)    {        int startX = 10;        int startY = 5;        Font minifont = new Font("Arial", 5);        graphics.DrawString(text, minifont,                 new SolidBrush(Color.Black), startX + 5, startY + Offset);    }    void InsertItem(string key, string value, int Offset)    {        Font minifont = new Font("Arial", 5);        int startX = 10;        int startY = 5;        graphics.DrawString(key, minifont,                     new SolidBrush(Color.Black), startX + 5, startY + Offset);        graphics.DrawString(value, minifont,                 new SolidBrush(Color.Black), startX + 130, startY + Offset);    }    void InsertHeaderStyleItem(string key, string value, int Offset)    {        int startX = 10;        int startY = 5;        Font itemfont = new Font("Arial", 6, FontStyle.Bold);        graphics.DrawString(key, itemfont,                     new SolidBrush(Color.Black), startX + 5, startY + Offset);        graphics.DrawString(value, itemfont,                 new SolidBrush(Color.Black), startX + 130, startY + Offset);    }    void DrawLine(string text, Font font, int Offset, int xOffset)    {        int startX = 10;        int startY = 5;        graphics.DrawString(text, font,                 new SolidBrush(Color.Black), startX + xOffset, startY + Offset);    }    void DrawSimpleString(string text, Font font, int Offset, int xOffset)    {        int startX = 10;        int startY = 5;        graphics.DrawString(text, font,                 new SolidBrush(Color.Black), startX + xOffset, startY + Offset);    }    private void FormatPage(object sender, PrintPageEventArgs e)    {        graphics = e.Graphics;        Font minifont = new Font("Arial", 5);        Font itemfont = new Font("Arial", 6);        Font smallfont = new Font("Arial", 8);        Font mediumfont = new Font("Arial", 10);        Font largefont = new Font("Arial", 12);        int Offset = 10;        int smallinc = 10, mediuminc = 12, largeinc = 15;        //Image image = Resources.logo;        //e.Graphics.DrawImage(image, startX + 50, startY + Offset, 100, 30);        //graphics.DrawString("Welcome to HOT AND CRISPY", smallfont,        //      new SolidBrush(Color.Black), startX + 22, startY + Offset);        Offset = Offset + largeinc + 10;        String underLine = "-------------------------------------";        DrawLine(underLine, largefont, Offset, 0);        Offset = Offset + mediuminc;        DrawAtStart("Invoice Number: " + order.Invoice, Offset);        if (!String.Equals(order.Customer.Address, "N/A"))        {            Offset = Offset + mediuminc;            DrawAtStart("Address: " + order.Customer.Address, Offset);        }        if (!String.Equals(order.Customer.Phone, "N/A"))        {            Offset = Offset + mediuminc;            DrawAtStart("Phone # : " + order.Customer.Phone, Offset);        }        Offset = Offset + mediuminc;        DrawAtStart("Date: " + order.Date, Offset);        Offset = Offset + smallinc;        underLine = "-------------------------";        DrawLine(underLine, largefont, Offset, 30);        Offset = Offset + largeinc;        InsertHeaderStyleItem("Name. ", "Price. ", Offset);        Offset = Offset + largeinc;        foreach (var itran in order.ItemTransactions)        {            InsertItem(itran.Item.Name + " x " + itran.Quantity, itran.Total.CValue, Offset);            Offset = Offset + smallinc;        }        foreach (var dtran in order.DealTransactions)        {            InsertItem(dtran.Deal.Name, dtran.Total.CValue, Offset);            Offset = Offset + smallinc;            foreach (var di in dtran.Deal.DealItems)            {                InsertItem(di.Item.Name + " x " + (dtran.Quantity * di.Quantity), "", Offset);                Offset = Offset + smallinc;            }        }        underLine = "-------------------------";        DrawLine(underLine, largefont, Offset, 30);        Offset = Offset + largeinc;        InsertItem(" Net. Total: ", order.Total.CValue, Offset);        if (!order.Cash.Discount.IsZero())        {            Offset = Offset + smallinc;            InsertItem(" Discount: ", order.Cash.Discount.CValue, Offset);        }        Offset = Offset + smallinc;        InsertHeaderStyleItem(" Amount Payable: ", order.GrossTotal.CValue, Offset);        Offset = Offset + largeinc;        String address = shop.Address;        DrawSimpleString("Address: " + address, minifont, Offset, 15);        Offset = Offset + smallinc;        String number = "Tel: " + shop.Phone1 + " - OR - " + shop.Phone2;        DrawSimpleString(number, minifont, Offset, 35);        Offset = Offset + 7;        underLine = "-------------------------------------";        DrawLine(underLine, largefont, Offset, 0);        Offset = Offset + mediuminc;        String greetings = "Thanks for visiting us.";        DrawSimpleString(greetings, mediumfont, Offset, 28);        Offset = Offset + mediuminc;        underLine = "-------------------------------------";        DrawLine(underLine, largefont, Offset, 0);        Offset += (2 * mediuminc);        string tip = "TIP: -----------------------------";        InsertItem(tip, "", Offset);        Offset = Offset + largeinc;        string DrawnBy = "Meganos Softwares: 0312-0459491 - OR - 0321-6228321";        DrawSimpleString(DrawnBy, minifont, Offset, 15);    }}

enter image description here

Some code to add the image has been commented here due to our requirements you can add can add your logo at header, as you can see in second image.

enter image description here


i am assuming you are not printing the output on a thermal printer but a normal high res bitmap capable printer? if so your problem basically requires you to generate a bitmap / pdf or some other graphics description that you can then send to the printer. as the example image you supplied just contains text then your task is simply to layout this text with coordinates and font sizes. the issue you will face, however is sourcing the right fonts and getting the spacing and the kerning etc absolutely right. this would be my starting point. see if you can find a font that looks like the one in the image. test it in photoshop / gimp or similar. make your photo of the termal print a background layer, then see if you can get the foreground to the be same. once you can copy it there, go and finish the software. pick the text coordinates from your photoshop mockup.