基於C#在WPF中使用斑馬印表機進行列印
最近在專案中接手了一個比較有挑戰性的模組——用斑馬印表機將需要列印的內容打印出來。苦苦折騰了兩天,總算有所收穫,就發到網上來騙騙分數-_-||
專案中使用的印表機型號為GX430t的印表機,接手的時候,自己對於印表機這塊兒是眼前一抹黑,啥都不知道。沒辦法一步步來。
首先嚐試使用WPF中的PrintDialog裡面的PrintVisual和PrintDocument方法,印表機是一點反應都沒有,最後得到的結論是:斑馬印表機不支援MS的XPS文件格式,所以使用WPF來排版後進行驅動列印就不要想了,不可能!!!這條路到這裡就斷了。
然後就想到有沒有SDK可以直接進行列印,就找到了斑馬印表機的技術支援,還是個妹子,我提了一下,妹子說沒有開發包可以用,然後就貼了一個網址給我,網址就是這條:
既然直接使用指令列印行不通就考慮使用影象列印,影象又跟多媒體掛鉤了,尼瑪真是夠了。因為玩過連結裡面的仁兄提到的獲取列印模板的命令的方法。就是在安裝好印表機驅動後,手動建立一個新的本地埠並在印表機設定中將印表機埠設為新建的埠。使用Zebra的建立模板的軟體建立好你想要的東西,然後列印,就能在你建立的埠檔案中得到你想要的指令序列(其實,模板裡面使用的就是影象列印)。指令序列有了,對照手冊查詢相應的指令就能得到你想要的東西。
這裡說的影象列印並不是我們平時說的點陣圖或者向量圖,手冊裡面說是叫GRF格式的影象,仔細研究了一下,其實就是綴著這麼個名字而已,裡面需要的資料其實就是影象矩陣。而且影象矩陣中的畫素表示法是:一個位元組表示8個畫素,也就是一個bit位(0或1)表示一個畫素的顏色(黑或白)。看到這裡腦子裡有了思路:將要列印的內容進行排版->將排版好的資料轉換成點陣圖->將點陣圖中的資料,根據需要轉換成指令中要求的格式->交給印表機列印。這樣一來就沒有什麼印表機對字型本身的限制了。思路有了,剩下的就是方法。
排版比較簡單,這個玩過自定義控制元件的人都知道,使用DrawingVisual可以構建自己想要的Visual。然而將Visual轉換成點陣圖就難住我了,糾結了一個下午終於從網上找到了一個東西----RenderTargetBitmap。這個類可以將你的Visual轉換成點陣圖。
下面就是將點陣圖資料轉換成指令中的影象資料,咳咳,數學不夠好,在分析資料的時候搞錯了一個地方讓我糾結了好長時間,不過總體來說還是解決了。說一下思路:
- 通過RenderTargetBitmap類的CopyPixels方法將畫素資料拷貝出來。因為這個點陣圖建立的時候只是作為一箇中間的過程,所以格式可以隨便選,我是選擇了PixelFormats.Pbgra32格式,比較簡單。這個格式的影象畫素是用4個位元組表示,依次為:Blue、Green、Red、Alpha。拷貝的時候,作為緩衝區的陣列需要將長度設為畫素數的4倍。
- 畫素拷貝的時候會有一個“跨距”的東西。這個表示的是影象中一行中資料的位元組數,必須為4的倍數。也就是取大於或等於真實值的最小的能夠被4整除的數值。
- 獲取到資料就可以對資料進行整理了,遍歷整個陣列,如果當前畫素的顏色值不為白色或者透明色就將目標陣列中的bit位之一(目標陣列中用bit位表示對應畫素的值)
- 將獲得的陣列轉換成string串,然後將該串插入到指令序列中相應的位置就得到對應的指令。
說到這裡其實說的也差不多了,順便說下,WPF裡面的列印支援真的很強大,給印表機傳遞指令的操作也很簡單,具體見下面的程式碼。
這裡是原始碼:
/// <summary>
/// 獲取繪製Visual的命令
/// </summary>
/// <param name="visual">要獲取的Visual</param>
/// <param name="pixelWidth">畫素寬度</param>
/// <param name="pixelHeight">畫素高度</param>
/// <param name="dpiX">橫向dpi</param>
/// <param name="dpiY">縱向dpi</param>
/// <param name="offsetX">橫座標偏移量,單位為畫素數</param>
/// <param name="offsetY">縱座標偏移量,單位為畫素數</param>
/// <returns></returns>
private string GetPrintZPL(Visual visual, int pixelWidth, int pixelHeight, double dpiX, double dpiY, int offsetX, int offsetY)
{
string ret = string.Empty;
//構建圖片
RenderTargetBitmap bmp = new RenderTargetBitmap(pixelWidth, pixelHeight, dpiX, dpiY, PixelFormats.Pbgra32);
#if TEST //test
DrawingVisual newVisual = new DrawingVisual();
DrawingContext dc = newVisual.RenderOpen();
dc.DrawEllipse(Brushes.Black, new Pen(), new Point(bmp.Width / 2, bmp.Height / 2), bmp.Width / 2, bmp.Height / 2);
dc.Close();
visual = null;
bmp.Render(newVisual);
#else
bmp.Render(visual);
#endif
byte[] datas = new byte[bmp.PixelWidth * bmp.PixelHeight * 4];
bmp.CopyPixels(datas, bmp.PixelWidth * 4, 0);//獲取影象資料
int rowBytes = (pixelWidth + 7) / 8;
byte[] targetDatas = new byte[rowBytes * bmp.PixelHeight];
for (int i = 0; i < bmp.PixelHeight; i++) //資料調整,並將資料
{
for (int j = 0; j < bmp.PixelWidth; j++)
{
byte blue = datas[i * bmp.PixelWidth * 4 + j * 4 + 0];
byte green = datas[i * bmp.PixelWidth * 4 + j * 4 + 1];
byte red = datas[i * bmp.PixelWidth * 4 + j * 4 + 2];
byte alpha = datas[i * bmp.PixelWidth * 4 + j * 4 + 3];
if (blue == 0 && green == 0 && red == 0)
{
if (alpha == 255)//alpha也是0則為透明色
{
byte cur = 1;
cur = (byte)(cur << (7 - j % 8));
targetDatas[i * rowBytes + j / 8] |= cur;
}
}
else
{
if (!(blue == 255 && green == 255 && red == 255 && alpha == 255))//全為255則表示白色
{
byte cur = 1;
cur = (byte)(cur << (7 - j % 8));
targetDatas[i * rowBytes + j / 8] |= cur;
}
}
}
}
ret = string.Format("^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD29^JUS^LRN^CI0^XZ~DG000.GRF,{0},{1},{2}^XA^MMT^PW260^LL0189^LS0^FT0,192^FO{3},{4},^XG000.GRF,1,1^FS^PQ1,0,1,Y^XZ^XA^ID000.GRF^FS^XZ", targetDatas.Length, rowBytes, BitConverter.ToString(targetDatas).Replace("-", string.Empty), offsetX, offsetY);
return ret;
}