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

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

我正在尝试在我的一个项目中实现打印功能,但我不太擅长这种工作。

我已经连接了我的热敏打印机和 write/print 相同的样本。 现在我正在尝试找到一些方法来设计我的收据,使其看起来像来自附加图像的收据。

我有一些想法,但我不确定它们是否适合这种工作,其中之一是尝试在 html 中格式化我的收据并像位图一样渲染 html (图片)然后打印出来,我已经试过了,但看起来质量下降了。

如果有人对我如何使收据看起来像图片有任何其他想法,请与我分享此信息,我将非常感激。

这是我打印一些样本时所做的,我使用了格式化 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);

}

我假设您不是在热敏打印机上打印输出,而是在普通的高分辨率位图打印机上打印输出?如果是这样,您的问题基本上需要您生成位图/pdf 或其他一些图形描述,然后您可以将其发送到打印机。由于您提供的示例图像仅包含文本,因此您的任务只是使用坐标和字体大小来布置此文本。然而,您将面临的问题是采购正确的字体并获得绝对正确的间距和字距调整等。这将是我的起点。看看你是否能找到一种看起来像图像中的字体。在 photoshop / gimp 或类似软件中测试它。将您的 termal 照片打印为背景层,然后查看是否可以使前景相同。一旦你可以在那里复制它,就去完成软件。从你的 photoshop 模型中选择文本坐标。

过去这样做时,我将收据分成单独的部分,使用不同的字体或对齐方式,例如页眉、正文、页脚。

我使用以下 class 布局来封装我的打印文本定义。 (你从哪里获得字体以及如何管理它的生命周期取决于你)

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

如果有更长的文本列表使用相同的字体和填充,那么使用 stringbuilder 来构建您的文本会让生活变得轻松,因此您只需检查您的代码就可以直观地了解它的外观。

如果你有静态文本,你可以将它们放在一起:

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

或者,如果数据是动态传递的某个对象,您可以遍历并附加您的格式化文本:

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("================");

输出将如下所示:

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
================

使用 PrintText class 之前我们可以存储格式良好的字符串生成器输出

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

最后在尝试绘制字符串时使用它

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

如果文本打印不正确,您还可以尝试一些不同的图形调整,例如:

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

您可以将收据转换为 BitMaps 图像并使用 XpsDocuentGenerator class。或者使用 MigraDocs 等开源库,将其转换为 PDF 并发送到打印机

我设计了一个简单流畅的收据设计,希望对你有帮助。

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

根据我们的要求,此处添加了一些用于添加图片的代码,您可以在 header 添加您的徽标,如第二张图片所示。