博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#并口热敏小票打印机打印位图
阅读量:5911 次
发布时间:2019-06-19

本文共 7129 字,大约阅读时间需要 23 分钟。

原文:

最近一直在研究并口小票打印机打印图片问题,这也是第一次和硬件打交道,不过还好,最终成功了。

 

这是DEMO的窗体:

 

下面是打印所需要调用的代码:

class LptControl    {        private string LptStr = "lpt1";        public LptControl(string l_LPT_Str)        {                       LptStr = l_LPT_Str;        }        [StructLayout(LayoutKind.Sequential)]        private struct OVERLAPPED        {            int Internal;            int InternalHigh;            int Offset;            int OffSetHigh;            int hEvent;        }                 //调用DLL.        [DllImport("kernel32.dll")]        private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);        [DllImport("kernel32.dll")]        private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);        [DllImport("kernel32.dll")]        private static extern bool CloseHandle(int hObject);        private int iHandle;                        ///         /// 打开端口        ///         /// 
public bool Open() { iHandle = CreateFile(LptStr, 0x40000000, 0, 0, 3, 0, 0); // iHandle = CreateFile(LptStr, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (iHandle != -1) { return true; } else { return false; } } /// /// 打印字符串,通过调用该方法可以打印需要的字符串 /// /// ///
public bool Write(String Mystring) { //如果端口为打开,则提示,打开,则打印 if (iHandle != -1) { OVERLAPPED x = new OVERLAPPED(); int i = 0; //byte[] mybyte = System.Text.Encoding.Default.GetBytes(Mystring); byte[] mybyte = Encoding.GetEncoding("GB2312").GetBytes(Mystring); bool b = WriteFile(iHandle, mybyte, mybyte.Length, ref i, ref x); return b; } else { throw new Exception("不能连接到打印机!"); } } /// /// 打印命令,通过参数,可以打印小票打印机的一些命令,比如换行,行间距,打印位图等。 /// /// ///
public bool Write(byte[] mybyte) { //如果端口为打开,则提示,打开,则打印 if (iHandle != -1) { OVERLAPPED x = new OVERLAPPED(); int i = 0; return WriteFile(iHandle, mybyte, mybyte.Length, ref i, ref x); } else { throw new Exception("不能连接到打印机!"); } } /// /// 关闭端口 /// ///
public bool Close() { return CloseHandle(iHandle); }}

 

因为我们这里主要是打印条形码和二维码,所以以条形码和二维码为例,写了一个小的调用程序(这里把打印图片的方法贴出来):

 

///         /// 打印图片方法        ///         public void PrintOne()        {            //获取图片            Bitmap bmp = new Bitmap(pictureBox1.Image);            //设置字符行间距为n点行            //byte[] data = new byte[] { 0x1B, 0x33, 0x00 };            string send = "" + (char)(27) + (char)(51) + (char)(0);            byte[] data = new byte[send.Length];            for (int i = 0; i < send.Length; i++)            {                data[i] = (byte)send[i];            }            lc.Write(data);            data[0] = (byte)'\x00';            data[1] = (byte)'\x00';            data[2] = (byte)'\x00';    // Clear to Zero.            Color pixelColor;            //ESC * m nL nH d1…dk   选择位图模式            // ESC * m nL nH            byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x00, 0x00, 0x00 };            escBmp[2] = (byte)'\x21';            //nL, nH            escBmp[3] = (byte)(bmp.Width % 256);            escBmp[4] = (byte)(bmp.Width / 256);            //循环图片像素打印图片            //循环高            for (int i = 0; i < (bmp.Height / 24 + 1); i++)            {                //设置模式为位图模式                lc.Write(escBmp);                //循环宽                for (int j = 0; j < bmp.Width; j++)                {                    for (int k = 0; k < 24; k++)                    {                        if (((i * 24) + k) < bmp.Height)  // if within the BMP size                        {                            pixelColor = bmp.GetPixel(j, (i * 24) + k);                            if (pixelColor.R == 0)                            {                                data[k / 8] += (byte)(128 >> (k % 8));                            }                        }                    }                    //一次写入一个data,24个像素                    lc.Write(data);                    data[0] = (byte)'\x00';                    data[1] = (byte)'\x00';                    data[2] = (byte)'\x00';    // Clear to Zero.                }                //换行,打印第二行                byte[] data2 = { 0xA };                lc.Write(data2);            } // data            lc.Write("\n\n");        }

 

     

在打印过程中,出现一个比较低级的错误,因为小票打印机是并口的,而我电脑是串口的,所以一直远程在另一台电脑上测试,所以打印出来的图片中间多了一条横线,这个问题解决了多半天,因为我一直考虑到是打印图片中可能少一层循环的问题,所以顺便把打印图片的原理整理了一下(之前的循环是从网上找到的,感觉应该没问题就没有细研究)。下面分享一下我的理解:

这是打印位图的命令(每一个打印机都会给出这样的说明,可以直接下载到的):

1.  ESC* m nL nH d1…dk   选择位图模式

格式:   ASCII: ESC * m nL nH d1…dk

      十进制:  [27] [42] m nL nH d1…dk

    十六进制:  [1BH][2AH] m nL nH d1…dk

说明:

    .设定位图方式(用m)、点数(用nL,nH)以及位图内容(用dk)。

    .m=0,1,32,33;0≤nL≤255,0≤nH≤3,0≤d≤255。

     k=nL+nH×256(m=0,1);k=(nL+nH×256)×3(m=32,33)。

    .水平方向点数为(nL+nH×256)。

    .如果点数超过一行,超过其最大点数(与选择的位图方式有关,详      见下表)的部分被忽略。

    .d为位图数据字节,对应位为1则表示该点打印,对应位为0,则  表示该点不打印。(k表示数据个数)

    .m用于选择位图方式。

 

模式

纵向

横向

点数

分辨率

分辨率

数据个数(k)

0

8点单密度

8

67  DPI

100  DPI

nL+nH×256

1

8点双密度

8

67  DPI

200  DPI

nL+nH×256

32

24点单密度

24

200  DPI

100  DPI

(nL+nH×256)×3

33

24点双密度

24

200  DPI

200  DPI

(nL+nH×256)×3

 

 

 

 

 

 

 

这次用的打印机打印是24点双密度的,所以我这里就只解释下m=33的情况。

从代码中可以看出,打印图片过程主要是通过循环一点点打印的,通过

lc.Write(data);

循环写入,当然前面的lc.Write(escBmp)主要是些ESC * m三个参数很容易理解就不多解释了。而data是一个长度为3byte数组,这个data在打印中起到什么作用呢?

在打印机m=33的模式纵向每次是打印24个点,也就是说,而byte8个字节,所以需要3byte类型的树才能完成模式为24点双密码的位图打印方式,通过三个字符来平凑一个像素宽24个像素长的图片,然后循环宽度,来打印图片宽度大小24个像素高度的图片,在通过每次循环24个像素的高度,最终打印出完成的图片。

需要打印的图片:

 

第一次循环先是高位24像素

 

然后把宽度分解开,循环每一像素的宽度,然后打印每一像素宽度的图片:

举个例子,假设数组data[d1,d2,d3],d1= 00000111,d2=11111111,d3 =11111111,所以打印出的一个像素宽,24像素高的图片为:

 

最终通过循环宽度与高度,把最终的位图画出来。

这里我举的是24点密度的例子,通过,如果您有兴趣研究的话,也经常看到这样的代码:

 

 

for (int i = 0; i < ((bmp.Height + 7) / 8); i++)                {                    _serialPort.Write(escBmp, 0, escBmp.Length);                    for (int j = 0; j < bmp.Width; j++)                    {                        for (int k = 0; k < 8; k++)                        {                            if (((i * 8) + k) < bmp.Height)  // if within the BMP size                            {                                pixelColor = bmp.GetPixel(j, (i * 8) + k);                                if (pixelColor.R == 0)                                {                                    data[0] += (byte)(128 >> k);                                }                            }                        }                        _serialPort.Write(data, 0, 1);                        data[0] = (byte)'\x00'; // Clear to Zero.                    }

 

这个很明显就是8点密度的模式,所以他的data长度为1,即需要8个字节就够了。

打印出的效果还是很不错的。

如果大家有兴趣研究网络打印,请参加小崔的博客:

转载地址:http://xmmpx.baihongyu.com/

你可能感兴趣的文章
微软官方提供的用于监控MS SQL Server运行状况的工具及SQL语句
查看>>
OC面向对象—封装
查看>>
Github for Windows使用介绍
查看>>
map的综合例子
查看>>
前端基于jquery的UI框架
查看>>
上传文件multipart form-data boundary 说明
查看>>
java中的数据加密3 非对称加密
查看>>
从程序员到项目经理(6):程序员加油站 -- 懂电脑更要懂人脑【转载】
查看>>
Office转SWF的一些感想(Office2007和Office2010)
查看>>
使用 IntraWeb (3) - 页面切换
查看>>
XSS解决方案系列之四:关于编码
查看>>
WP8__从windowsphone app store 中根据app id获取应用的相关信息(下载网址及图片id等)...
查看>>
javascript浮点数转换成整数三种方法
查看>>
(int)()和int()强制类型转换
查看>>
经济学发展史
查看>>
MySQL创建定时任务
查看>>
Android判断当前线程是否是主线程的方法
查看>>
Step-by-Step Guide to Portal Development for Microsoft Dynamics CRM - 摘自网络
查看>>
win7或win2008系统中,出现【已停止工作,联机检查解决方案并关闭该程序,关闭程序】解决方法!...
查看>>
UNIX环境编程学习笔记(11)——文件I/O之文件时间以及 utime 函数
查看>>