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 A
DISCOUNT 10% -1.00 A
Fun Facts 1x20 20.00 A
DISCOUNT 15% -3.00 A
Bag 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);
}
}