這幾天完成了Prowill PD-S326 Plus收據機的研究與輸出格式化工作,除了安裝外沒有甚麼特別的問題。這裡的程式沒有採用EPSON ESC指令集來控制,而是採用Windows標準的印表機驅動來撰寫,會這樣做的目的是為了日後好維護相容性,否則用ESC指令集當更換硬體時,大部分程式碼都要改寫了,這完全不符合我的偷懶原則。
這台機器安裝在Windows 10下,驅動程式安裝完成後,其實你只會看到一個陌生的裝置,叫做「USB Printer」,這台裝置並不是真的印表機,所以Windows根本不認得它。因此,你必須要使用手動「新增印表機」的功能,自己把這台新的印表機串接到USB001之類的通訊埠,這樣才會完成驅動一台「Prowill 32xx Series」標準的Windows印表機,以上是安裝過程的分享。
這台機器我對它的輸出格式的期望是,我想要有一個企業LOGO圖,然後收據名稱,然後是要能自動無限新增的明細列表,然後是總金額,然後在印製一些相關資訊以及廣告文字。
我把這些需求包裝成一個類別如下:(PDS326Plus.cs)
/// <summary> /// 這個類別是針對發票收據列印機而設計的列印類別 /// 廠牌:Prowill /// 型號:PD-S326 Plus /// </summary> namespace Slashview.Hardware.Print { /// <summary> /// PD-S326 Plus發票收據列印機主要類別檔案 /// </summary> class PDS326Plus { /// <summary> /// Logo圖檔路徑 /// 建議不要亂更換圖檔,因為這種低接的機器不若一般事務印表機那麼好調校。如果真的要更換,盡量以600 X 160像素、300 DPI為準。 /// </summary> public string cLogoPath { get; set; } /// <summary> /// 品項名稱以及金額的列舉 /// </summary> public System.Collections.Generic.List<Slashview.Hardware.Print.PDS326PlusData> oItemsList { get; set; } /// <summary> /// 所有品項的加總後之金額 /// </summary> public int iTotalAccount { get; set; } /// <summary> /// 系統資訊 /// (是哪個系統來調用這次的列印?) /// </summary> public string cSystemName { get; set; } /// <summary> /// 作業資訊 /// (是從哪一台KIOSK列印出來的?如果是一般電腦,那這邊的資訊可以寫入是由哪個承辦人員操作的。) /// </summary> public string cOperateInfo { get; set; } /// <summary> /// 廣告字串 /// (活動廣告用途,如果給空字串,那麼紙張就不會輸出任何的字語。) /// </summary> public string cAdWords { get; set; } /// <summary> /// 主要建構子 /// </summary> public PDS326Plus() { } /// <summary> /// 啟動列印程序 /// </summary> public void Print() { /**** 檢查與調整必要資訊 ****/ //檢查是否有品項可以列印,如果沒有半個品項就踢出 if (oItemsList == null || oItemsList.Count == 0) { throw new System.Exception("沒有任何的項次資訊可供列印。"); } //檢查是否超過金額欄位最大列印長度(包含正負符號、Comma符號) int iPrintLimitLength = 7; foreach (Slashview.Hardware.Print.PDS326PlusData oItem in oItemsList) { if (string.Format("{0:n0}", oItem.iPrice).Length > iPrintLimitLength) { throw new System.Exception(string.Format("品項內所提供之金額,含正負號、千位逗號後,已經超過最大列印極限{0}個字。", iPrintLimitLength.ToString())); } } if (string.Format("{0:n0}", iTotalAccount).Length > iPrintLimitLength) { throw new System.Exception(string.Format("總金額所提供之金額,含正負號、千位逗號後,已經超過最大列印極限{0}個字。", iPrintLimitLength.ToString())); } //檢查是否有沒設定的參數,並適時的調整為空字串 if (string.IsNullOrWhiteSpace(cSystemName)) { cSystemName = ""; } if (string.IsNullOrWhiteSpace(cOperateInfo)) { cOperateInfo = ""; } if (string.IsNullOrWhiteSpace(cAdWords)) { cAdWords = ""; } /**** 準備列印 ****/ System.Drawing.Printing.PrintDocument oDocument = new System.Drawing.Printing.PrintDocument(); oDocument.DefaultPageSettings.PaperSize = new System.Drawing.Printing.PaperSize("3InchPaper", 290, int.MaxValue); oDocument.PrintPage += setDocumentContent; //delegate給setDocumentContent oDocument.Print(); } /// <summary> /// 製作文件內容程序 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void setDocumentContent(object sender, System.Drawing.Printing.PrintPageEventArgs e) { //定義相關變數 float fPageWidth = 280F; //紙張最大可列印寬度(以三英吋熱感應紙為基準;290 * 2.54 = 736mm) float fPageHeight = 270F; //紙張最大可列印高度(以國家規定電子發票之高度為基準;280 * 2.54 = 711mm,加上預設退紙高度,大約可以勉強等於法規電子發票的870mm高度) float fPrintX = 0F; //當前列印X軸(被用來作為左邊界位移) float fPrintY = 0F; //當前列印Y軸(必須隨時被記載) float fVertical = 20F; //垂直行距 float fPrintX_Item = 0F; //品項(表格用) float fPrintX_Price = 216F; //金額(表格用) System.Drawing.Font oFontHeader = new System.Drawing.Font("微軟正黑體", 14F); System.Drawing.Font oFontContent = new System.Drawing.Font("微軟正黑體", 10F); System.Drawing.Font oFontContentSmall = new System.Drawing.Font("微軟正黑體", 8F, System.Drawing.FontStyle.Bold); System.Drawing.Brush oBrush = System.Drawing.Brushes.Black; //最佳化繪圖輸出 e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; //繪製輸出可視邊界(用來調整版面用) //e.Graphics.DrawRectangle(Pens.Aqua, new Rectangle(1, 1, 280, 270)); //以離線的方式讀取並列印LOGO圖檔,以免系統將檔案咬死 System.Drawing.Image oLogo; using (var oTemp = new System.Drawing.Bitmap(cLogoPath)) { var oTemp2 = new System.Drawing.Bitmap(oTemp); oTemp2.SetResolution(400F, 400F); oLogo = oTemp2; } e.Graphics.DrawImage(oLogo, new System.Drawing.PointF(64F, fPrintY)); fPrintY += fVertical * 2; //繪製單據抬頭 e.Graphics.DrawString("電子交易收據憑單", oFontHeader, oBrush, 59F, fPrintY); fPrintY += fVertical * 1.4F; //繪製表格標題 e.Graphics.DrawString("品項名稱", oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Item, fPrintY, fPrintX_Price - fPrintX_Item, fVertical)); e.Graphics.DrawString("金額", oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Price, fPrintY, fPageWidth - fPrintX_Price, fVertical), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Far }); fPrintY += fVertical * 1.15F; //繪製表格(上)分隔線 e.Graphics.DrawLine(new System.Drawing.Pen(oBrush, 1), fPrintX, fPrintY, 280, fPrintY); fPrintY += fVertical * 0.25F; //繪製明細金額 foreach (Slashview.Hardware.Print.PDS326PlusData oItem in oItemsList) { e.Graphics.DrawString(oItem.cName, oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Item, fPrintY, fPrintX_Price - fPrintX_Item, fVertical)); e.Graphics.DrawString(string.Format("{0:n0}", oItem.iPrice), oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Price, fPrintY, fPageWidth - fPrintX_Price, fVertical), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Far }); fPrintY += fVertical; } //繪製表格(下)分隔線 fPrintY += fVertical * 0.25F; e.Graphics.DrawLine(new System.Drawing.Pen(oBrush, 1), fPrintX, fPrintY, 280, fPrintY); fPrintY += fVertical * 0.25F; //繪製結算金額 e.Graphics.DrawString("總收費金額:NT$", oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Item, fPrintY, fPrintX_Price - fPrintX_Item, fVertical), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Far }); e.Graphics.DrawString(string.Format("{0:n0}", iTotalAccount), oFontContent, oBrush, new System.Drawing.RectangleF(fPrintX_Price, fPrintY, fPageWidth - fPrintX_Price, fVertical), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Far }); fPrintY += fVertical * 1.2F; //系統日期(單子的列印時間日期) e.Graphics.DrawString(string.Format("列印時間:{0}", System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")), oFontContentSmall, oBrush, fPrintX, fPrintY); fPrintY += fVertical; //系統資訊(哪一個系統列印出這張單子) e.Graphics.DrawString(string.Format("系統資訊:{0}", cSystemName), oFontContentSmall, oBrush, fPrintX, fPrintY); fPrintY += fVertical; //輸出機器資訊(例如KIOSK編號) e.Graphics.DrawString(string.Format("作業資訊:{0}", cOperateInfo), oFontContentSmall, oBrush, fPrintX, fPrintY); fPrintY += fVertical; //廣告區塊 e.Graphics.DrawString(cAdWords, oFontContentSmall, oBrush, new System.Drawing.RectangleF(fPrintX, fPrintY, fPageWidth - fPrintX, e.Graphics.MeasureString(cAdWords, oFontContentSmall).Height * 205F)); } } /// <summary> /// 傳入給PD-S326 Plus發票收據列印機的資料定義檔案 /// Object Rrelational Mapping(ORM) /// </summary> class PDS326PlusData { /// <summary> /// 品項名稱 /// </summary> public string cName { get; set; } /// <summary> /// 品項之金額 /// </summary> public int iPrice { get; set; } } }
類別完成後,接下來只剩主控端的呼叫驗證了,以下是調用的範例程式碼:
private void button2_Click(object sender, EventArgs e) { //寫入資料 Slashview.Hardware.Print.PDS326Plus oPrinter = new Slashview.Hardware.Print.PDS326Plus() { cLogoPath = @"D:\Slashview\Sample.png", oItemsList = new System.Collections.Generic.List<Slashview.Hardware.Print.PDS326PlusData> { new Slashview.Hardware.Print.PDS326PlusData() { cName = "麵包", iPrice = 12345 }, new Slashview.Hardware.Print.PDS326PlusData() { cName = "手續費", iPrice = 10 }, new Slashview.Hardware.Print.PDS326PlusData() { cName = "感光紙費", iPrice = 1 }, }, iTotalAccount = 12356, cSystemName = "真好吃麵包坊收銀系統", cOperateInfo = "天母分店", cAdWords = "即日起,凡持有真好吃麵包坊貴賓卡之消費者,來電均可贈送真難吃麵包乙顆。" }; //送出列印 oPrinter.Print(); }Prowill PD-S326Plus PD-S326+ 發票機 收據機 ReceiptPrinters C# .NetFramework