简体   繁体   English

用 C# WPF 编写漂亮的收据,以便在热敏打印机 POS 上打印

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

I am trying to implement print functionality on one of my project but I am not so good in this kind of work.我正在尝试在我的一个项目中实现打印功能,但我不太擅长这种工作。

I already have connected with my thermal printer and write/print same samples.我已经连接到我的热敏打印机并写入/打印相同的样本。 Now I am trying to find some way to design my receipt to look like receipt from attached image.现在我试图找到某种方法来设计我的收据,使其看起来像来自附加图像的收据。

I have some ideas but I am not sure if they are good for this kind of work, one of them is to try to format my receipt in html and their render html like bitmap(image) and then print it, I already tried this but it looks like I have an loss of quality.我有一些想法,但我不确定它们是否适合这种工作,其中之一是尝试将我的收据格式化为 html 格式,然后将其渲染为 bitmap(image) 之类的 html,然后将其打印出来,我已经尝试过了,但是看起来我的质量有所下降。

If somebody have any other ideas about how I can make receipt look like on from the image please share this info with me I will be very thankful.如果有人对我如何使收据看起来像图像上的任何其他想法,请与我分享此信息,我将非常感激。

This is what I already did to print some samples, for formatting I used graphics.DrawString but I don't think that I can accomplish too much using it.这就是我打印一些样本时所做的,为了格式化我使用了graphics.DrawString但我不认为我可以使用它完成太多工作。

public void Print()
{
    var doc = new PrintDocument();
    doc.PrintPage += new PrintPageEventHandler(ProvideContent);
    doc.Print();
}

public void ProvideContent(object sender, PrintPageEventArgs e)
{
    Graphics graphics = e.Graphics;
    Font font = new Font("Courier New", 10);

    float fontHeight = font.GetHeight();

    int startX = 0;
    int startY = 0;
    int Offset = 20;

    e.PageSettings.PaperSize.Width = 50;
    graphics.DrawString("Welcome to MSST", new Font("Courier New", 8),
                        new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;

    graphics.DrawString("Ticket No:" + "4525554654545",
                new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;


    graphics.DrawString("Ticket Date :" + "21/12/215",
                new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

    Offset = Offset + 20;
    String underLine = "------------------------------------------";

    graphics.DrawString(underLine, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

    Offset = Offset + 20;
    String Grosstotal = "Total Amount to Pay = " + "2566";

    Offset = Offset + 20;
    underLine = "------------------------------------------";
    graphics.DrawString(underLine, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);
    Offset = Offset + 20;

    graphics.DrawString(Grosstotal, new Font("Courier New", 14),
                new SolidBrush(Color.Black), startX, startY + Offset);

}

在此处输入图片说明

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.当有更长的文本列表使用相同的字体和填充时,然后使用 stringbuilder 来构建你的文本会让你的生活变得轻松,这样你就可以通过检查代码来了解它的外观。

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使用之前的 PrintText 类,我们可以存储格式良好的字符串构建器输出

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);
    }
}

在此处输入图片说明

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.由于我们的要求,一些添加图像的代码已在此处注释,您可以添加可以在标题中添加徽标,如您在第二张图像中看到的那样。

在此处输入图片说明

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.如果是这样,您的问题基本上需要您生成位图/pdf 或其他一些图形描述,然后您可以将其发送到打印机。 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.在 photoshop/gimp 或类似软件中测试它。 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.从您的 Photoshop 模型中选择文本坐标。

You can convert the receipt into BitMaps image and use XpsDocuentGenerator class.您可以将收据转换为 BitMaps 图像并使用 XpsDocuentGenerator 类。 Or use open-source library like MigraDocs where you convert it into PDF and send to the Printers或者使用 MigraDocs 等开源库,将其转换为 PDF 并发送到打印机

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM