最近在项目中接手了一个比较有挑战性的模块——用斑马打印机将需要打印的内容打印出来。苦苦折腾了两天,总算有所收获,就发到网上来骗骗分数-_-||
项目中使用的打印机型号为GX430t的打印机,接手的时候,自己对于打印机这块儿是眼前一抹黑,啥都不知道。没办法一步步来。
首先尝试使用WPF中的PrintDialog里面的PrintVisual和PrintDocument方法,打印机是一点反应都没有,最后得到的结论是:斑马打印机不支持MS的XPS文档格式,所以使用WPF来排版后进行驱动打印就不要想了,不可能!!!这条路到这里就断了。
然后就想到有没有SDK可以直接进行打印,就找到了斑马打印机的技术支持,还是个妹子,我提了一下,妹子说没有开发包可以用,然后就贴了一个网址给我,网址就是这条:。可能我的水平有限,反正我感觉啰啰嗦嗦一大堆,实际的东西没多少,不过关键点倒是提了出来使用图像或者指令进行打印。我首先想到的是使用指令进行打印,就去找妹子要了Zebra的技术手册,一打开就吓尿了,尼玛1000+页的东西,我只是用一下打印机又不是去帮你们打印机开发驱动,当时心里那个抵触啊。但是没办法,得看呐,就仔细看了一些,找到打印的指令试了试,有东西能打出来,当时感觉挺满足的。唯一比较纠结的就是打印机支持的字体和字体本身的一些设置,比如粗体、斜体等等,资料里面没找到,然后去问了下斑马的技术支持,得知斜体字可以打,但是不清楚有没有对应的指令。没办法,项目里面用的字体多,还有各种斜体神马的,玩WPF的都知道,那么多属性一个个设置下来,光字体类型斑马就搞不定。
既然直接使用指令打印行不通就考虑使用图像打印,图像又跟多媒体挂钩了,尼玛真是够了。因为玩过链接里面的仁兄提到的获取打印模板的命令的方法。就是在安装好打印机驱动后,手动创建一个新的本地端口并在打印机设置中将打印机端口设为新建的端口。使用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;
}