65.9K
CodeProject 正在变化。 阅读更多。
Home

Kube 收据打印机

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (9投票s)

2009年3月30日

CPOL

2分钟阅读

viewsIcon

88494

downloadIcon

2746

Kube 收据打印机。

引言

在本文中,我将展示如何使用 XML 文件作为命令文件来驱动一个自定义的 Kube 打印机。具体来说,该项目的目标是使用打印机的原生命令(转义序列)以获得最佳性能,但通过一个简单的 XML 文件来创建布局,而不是使用复杂的固定代码。该打印机可以通过 USB 和 RS232 端口驱动。通过 RS232,也可以读取打印机和纸张状态。

背景

在 Kube 手册中,你可以看到所有支持的命令。我的库目前还没有支持所有命令,但它映射了主要的命令。此外,实现缺失的命令也比较简单。可以打印位图,并且在库内部有转换函数。也支持自定义字符定义。字符串通过将特殊字符(国际字符)转换为正确的转义序列来打印。转换在 XML 文件中定义,因此你可以添加缺失的字符——目前,我只定义了一些西班牙和意大利字符。

Using the Code

该库可以在任何项目中使用。有必要为数据交换设计类(你可以在下面看到一些示例),其中唯一的规则是使用 IList 接口来封装一个数据序列。这是可能的,因为类通过反射被打印引擎读取。

/// <summary>
/// Class to represent a receipt.
/// Receipt contains a Header with more details (legs).
/// </summary>
public class ReceiptData
{
    public string ReceiptID = null;
    public decimal Amount = 0;
    public decimal FinalPrice = 0;
    public decimal PossibleReturn = 0;
    public DateTime PlaceDate = DateTime.MinValue;
    public string UserName = null;
    public string TerminalID = null;
    public string BetType = null;
    public ReceiptLegDataList Legs = null;
    …..
    …..
}
/// <summary>
/// Class to rappresent a receipt leg (detail).
/// </summary>
public class ReceiptLegData
{
    public DateTime EventDate = DateTime.MinValue;
    public string EventDescription = null;
    public string MarketDescription = null;
    public string SelectionDescription = null;
    public decimal Price = 0;
    …..
    …..
}
/// <summary>
/// Class for a List of ReceiptLegData
/// </summary>
public class ReceiptLegDataList : List<ReceiptLegData>
{
}

要使用其中一个定义的模板打印数据很简单,但当然你需要编写 XML 来定义每个模板。要将一系列命令应用于 IList,请使用循环命令。你可以在下面看到一个例子

<Commands Path="xml" Language="IT">
  <!-- Reset -->
  <Command name="Reset"/>
  <!-- Print company logo -->
  <Command name="SetHAlign" Align="Center" />
  <Command name="DefineImage" Filename="image\logo.png"/>
  <Command name="PrintImageDefined" PrintMode="normal"/>
  <Command name="NewLine" />
  <!-- Starting sets and print receiptID (barcode) -->
  <Command name="LineFeed" Line="1" />
  <Command name="SetFont" Font="Large" />
  <Command name="SetLeftMargin" Margin="20" />
  <Command name="SetHAlign" Align="Center" />
  <Command name="SetCharSize" X="2" Y="1" />
  <Command name="PrintString" Mapping="ReceiptID" LineFeed="Yes" />
  <Command name="SetCharSize" X="1" Y="1" />
  <Command name="SetHAlign" Align="Left" />
  <Command name="LineFeed" Line="2" />
  <Command name="SetFont" Font="Small" />
  <Command name="SetLeftMargin" Margin="20" />
  <!-- Print legs info -->
  <Command name="Loop" On="Legs">
    <Command name="PrintString" Mapping="EventDescription" LineFeed="No" />
    <Command name="SetPosition" X="400" />
    <Command name="PrintString" Mapping="EventDate" 
             Format="dd.MM.yy HH:mm" LineFeed="Yes" />
    <Command name="PrintString" Mapping="MarketDescription" LineFeed="Yes" />
    <Command name="PrintString" String="(" LineFeed="No" />
    <Command name="PrintString" Mapping="SelectionDescription" LineFeed="No" />
    <Command name="PrintString" String="): " LineFeed="No" />
    <Command name="SetPosition" X="400" />
    <Command name="PrintString"  Mapping="Price" Format="0.00" LineFeed="No" />
    <Command name="UnitFeed" Unit="2" />
    <Command name="PrintString" String="______________________________
                                        ________________________" LineFeed="Yes" />
  </Command>
  <!-- Print summary info -->
  <Command name="LineFeed" Line="1" />
  <Command name="SetHAlign" Align="Left" />
  <!-- total stake -->
  <Command name="PrintLabel" LabelKey="TotalStake" LineFeed="No" />
  <Command name="SetPosition" X="230" />
  <Command name="PrintMoney" Mapping="Amount" ShowCurrency="Yes" 
           LineFeed="Yes" FixedLen="15" FillOnLeft="true"  />
  <!-- total price -->
  <Command name="PrintLabel" LabelKey="TotalPrice" LineFeed="No" />
  <Command name="SetPosition" X="230" />
  <Command name="PrintMoney" Mapping="FinalPrice" ShowCurrency="No" 
           LineFeed="Yes" FixedLen="11" FillOnLeft="true"  />
  <!-- total price -->
  <Command name="PrintLabel" LabelKey="MaxReturn" LineFeed="No" />
  <Command name="SetPosition" X="230" />
  <Command name="PrintMoney" Mapping="PossibleReturn" ShowCurrency="Yes" 
           LineFeed="Yes" FixedLen="15" FillOnLeft="true" />
  <Command name="LineFeed" Line="1" />
  <!-- place date time -->
  <Command name="SetHAlign" Align="Left" />
  <Command name="PrintLabel" LabelKey="PlaceDate" LineFeed="No" />
  <Command name="PrintString" Mapping="PlaceDate" Format="dd.MM.yy" LineFeed="No" />
  <Command name="PrintLabel" LabelKey="PlaceTime" LineFeed="No" />
  <Command name="PrintString" Mapping="PlaceDate" Format="HH:mm:ss" LineFeed="No" />
  <Command name="PrintString" String=" h" LineFeed="Yes" />
  <!-- company info -->
  <Command name="SetLeftMargin" Margin="60" />
  <Command name="PrintString" String="My Company Info" LineFeed="Yes" />
  <!-- terminal -->
  <Command name="PrintString" String="Sucursal " LineFeed="No" />
  <Command name="PrintString" Mapping="TerminalID" LineFeed="No" />
  <Command name="PrintString" String=" " LineFeed="No" />
  <Command name="PrintLabel" LabelKey="TerminalID" LineFeed="Yes" />
  <Command name="PrintString" String="Apuesta Contrapartida - " LineFeed="No" />
  <Command name="PrintString" Mapping="BetType" LineFeed="Yes" />
  <!-- user -->
  <Command name="SetLeftMargin" Margin="20" />
  <Command name="PrintLabel" LabelKey="User" LineFeed="No" />
  <Command name="PrintString" Mapping="UserName" LineFeed="Yes" />
  <!-- Print receiptID as barcode -->
  <Command name="LineFeed" Line="1" />
  <Command name="SetHAlign" Align="Center" />
  <Command name="PrintBarCode" Mapping="ReceiptID" Font="Large" 
           TextPosition="Down" Height="162" Barcode="CODE93"/>
  <Command name="SetHAlign" Align="Left" />
  <Command name="SetHAlign" Align="Center" />
  <Command name="NewLine" />
  <Command name="LineFeed" Line="3" />
  <!-- Cut paper -->
  <Command name="CutPaper"/>
</Commands>

当你拥有数据的类和布局的模板时,你可以使用这段代码打印票据

// Load templates
TemplateManager templateManager = new TemplateManager();
templateManager.Init(@"xml\InternationalChars.xml");
templateManager.LoadTemplate(@"xml\TemplateES.xml", "ES", BetReceipt);
templateManager.LoadTemplate(@"xml\CashReportES.xml", "ES", CashReport);
templateManager.LoadTemplate(@"xml\PaidReportES.xml", "ES", PaidReport);
templateManager.LoadTemplate(@"xml\AuthES.xml", "ES", AuthReport);
// Create printer engine (USB mode)
CustomPrinterEngine engine = new CustomPrinterEngine("Custom KUBE 80mm (200dpi)");
// Create printer engine (RS232 mode)
CustomPrinterEngine engine = new CustomPrinterEngine("COM11", 19200, 
      System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
ReceiptData receipt = new ReceiptData();
receipt.BetType = "multiple";
receipt.Amount = 10;
receipt.FinalPrice = (decimal)12.5;
receipt.PlaceDate = DateTime.Now;
receipt.PossibleReturn = 50;
receipt.ReceiptID = "123456789";
receipt.TerminalID = "A010";
receipt.UserName = "Alfredo";
receipt.AddLeg(DateTime.Now, "Roma - Sampdoria", "1X2", "1", 5);
receipt.AddLeg(DateTime.Now.AddDays(3).AddHours(10), "Mìlan - Juventus", 
               "Odd/Even", "Odd", (decimal)2.5);
receipt.AddLeg(DateTime.Now.AddDays(2).AddHours(8), "La Coroña - Real Madrid", 
               "1X2", "2", (decimal)3.1);
engine.DataToPrint = receipt;
engine.Print(templateManager, "ES", BetReceipt);
if(!engine.IsPrinted)
{
  MessageBox.Show("Ticket 1 no printed!");
}

关注点

打印过程由三个部分组成

  1. 第一部分使用 XML 文件创建一个 CommandPrinter 对象列表。
  2. 第二部分将每个 CommandPrinter 解码为字节序列。
  3. 包含所有字节序列的缓冲区被发送到打印机。
private CommandPrinterList GenerateCommandList(string languageKey, 
                           string templateKey, object obj)
{
  // Get template
  XmlDocument xmlTemplate = m_Cache.getTemplate(languageKey, templateKey);
  // Generate command list
  CommandPrinterList commandList = new CommandPrinterList();
  AttributeHashtable attributes = 
    new AttributeHashtable(xmlTemplate.FirstChild.Attributes);
  string parmLanguage = attributes["language"];
  string parmPath = attributes["path"];
  m_LabelMapper = LocalizeLabelMapper.getLanguage(parmPath, parmLanguage);
  commandList.AddRange(ParseChild(xmlTemplate.FirstChild, obj, m_LabelMapper));
  return commandList;
}
private CommandPrinter[] ParseChild(XmlNode node, Object obj, 
        LocalizeLabelMapper htLabels)
{
  CommandPrinterList list = new CommandPrinterList();
  foreach (XmlNode command in node.ChildNodes)
  {
    list.AddRange(ParseCommand(command, obj, htLabels));
  }
  return list.ToArray();
}
private CommandPrinter[] ParseCommandLoop(XmlNode command, 
        Object master, LocalizeLabelMapper htLabels)
{
  AttributeHashtable attributes = new AttributeHashtable(command.Attributes);
  string loopField = attributes["on"];
  System.Reflection.FieldInfo field = master.GetType().GetField(loopField);
  IList iList = (IList)field.GetValue(master);
  CommandPrinterList list = new CommandPrinterList();
  foreach (object obj in iList)
  {
    list.AddRange(ParseChild(command, obj, htLabels));
  }
  return list.ToArray();
}
private CommandPrinter[] ParseCommand(XmlNode command, 
        Object obj, LocalizeLabelMapper htLabels)
{
  if (command is XmlComment)
  {
    return new CommandPrinter[]{ null };
  }
  if (!command.Name.Equals("Command", StringComparison.OrdinalIgnoreCase))
  {
    throw new Exception("Invalid node type. Command element is expected");
  }
  AttributeHashtable attributes = new AttributeHashtable(command.Attributes);
  string cmdName = attributes["name"].ToLower();
  if (cmdName.Equals("loop"))
  {
    return ParseCommandLoop(command, obj, htLabels);
  }
  CommandPrinter commandPrinter = null;
  switch (cmdName)
  {
    case "linefeed":
      commandPrinter = ParseCommandPrintAndFeed(attributes);
      break;
    case "unitfeed":
      commandPrinter = ParseCommandPrintAndUnitFeed(attributes);
      break;
    …..
    …..
    case "papertocut":
      commandPrinter = ParseCommandAlignPaperToCut(attributes);
      break;
    case "reset":
      commandPrinter = ParseCommandReset(attributes);
      break;
  }
  if (commandPrinter == null)
  {
    throw new Exception("Command unkown");
  }
  return new CommandPrinter[]{commandPrinter};
}

CommandPrinter 派生类定义每个 Kube 命令及其可能的参数。也可以打印图像。ImageRasterHelper 将图像转换为打印机的正确字节序列。

public static byte[] ConvertBitmap(Bitmap bitmap, bool bIncludeSize)
{
  int baseIndex = ((bIncludeSize) ? 2 : 0);
  int xSize = (bitmap.Width / 8);
  if (xSize * 8 != bitmap.Width)
  {
    xSize++;
  }
  int ySize = (bitmap.Height / 8);
  if (ySize * 8 != bitmap.Height)
  {
    ySize++;
  }
  if (xSize < 1 || xSize > 255 || ySize < 1 || 
      ySize > 48 || xSize * ySize > 1536)
  {
    throw new Exception("Incorrect size");
  }
  byte[] raw = new byte[xSize * ySize * 8 + ((bIncludeSize) ? 2 : 0)];
  for (int i = 0; i < raw.Length; raw[i++] = 0) ;
  if (bIncludeSize)
  {
    raw[0] = (byte)(xSize & 0x00FF);
    raw[1] = (byte)(ySize & 0x00FF);
  }
  for (int x = 0; x < bitmap.Width; x++)
  {
    for (int y = 0; y < bitmap.Height; y++)
    {
      Color color = bitmap.GetPixel(x, y);
      if (RGBGreatEgual(color, 255, 255, 128)) 
      {
        continue;
      }
      int idx = (ySize * x) + y / 8;
      byte mask = (byte)(0x80 >> (y % 8));
      raw[idx + baseIndex] |= mask;
    }
  }
  return raw;
}

对国际字符的支持在另一个 XML 文件(一个类流 XML 文件)中定义,你可以扩展它以处理打印机支持的所有国际字符。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCharConvert 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CharConvert>
<OrigChar>241</OrigChar>
<InternationalChar>
<char>27</char>
<char>82</char>
<char>7</char>
<char>124</char>
</InternationalChar>
</CharConvert>
…..
…..
<CharConvert>
<OrigChar>176</OrigChar>
<InternationalChar>
<char>27</char>
<char>82</char>
<char>6</char>
<char>91</char>
</InternationalChar>
</CharConvert>
</ArrayOfCharConvert>

也可以定义不同语言的标签。模板中使用的语言通过第一行的语言属性定义,而定义标签的 XML 文件可以根据需要扩展以包含更多标签。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfLocalizeLabelItem 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LocalizeLabelItem>
<key>Money</key>
<value>EUR</value>
</LocalizeLabelItem>
<LocalizeLabelItem>
<key>TotalStake</key>
<value>Totale scommesso:</value>
</LocalizeLabelItem>
...
...
</ArrayOfLocalizeLabelItem>
© . All rights reserved.