zoukankan      html  css  js  c++  java
  • 转:C#并口热敏小票打印机打印位图

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

     

    这是DEMO的窗体:

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

    1. class LptControl  
    2.     {  
    3.         private string LptStr = "lpt1";  
    4.         public LptControl(string l_LPT_Str)  
    5.         {  
    6.              
    7.             LptStr = l_LPT_Str;  
    8.         }  
    9.         [StructLayout(LayoutKind.Sequential)]  
    10.         private struct OVERLAPPED  
    11.         {  
    12.             int Internal;  
    13.             int InternalHigh;  
    14.             int Offset;  
    15.             int OffSetHigh;  
    16.             int hEvent;  
    17.         }  
    18.       
    19.        
    20.         //调用DLL.  
    21.         [DllImport("kernel32.dll")]  
    22.         private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);  
    23.         [DllImport("kernel32.dll")]  
    24.         private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);  
    25.         [DllImport("kernel32.dll")]  
    26.         private static extern bool CloseHandle(int hObject);  
    27.         private int iHandle;  
    28.           
    29.           
    30.         /// <summary>  
    31.         /// 打开端口  
    32.         /// </summary>  
    33.         /// <returns></returns>  
    34.         public bool Open()  
    35.         {  
    36.             iHandle = CreateFile(LptStr, 0x40000000, 0, 0, 3, 0, 0);  
    37.             // iHandle = CreateFile(LptStr, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);  
    38.   
    39.             if (iHandle != -1)  
    40.             {  
    41.                 return true;  
    42.             }  
    43.             else  
    44.             {  
    45.                 return false;  
    46.             }  
    47.         }  
    48.   
    49.         /// <summary>  
    50.         /// 打印字符串,通过调用该方法可以打印需要的字符串  
    51.         /// </summary>  
    52.         /// <param name="Mystring"></param>  
    53.         /// <returns></returns>  
    54.         public bool Write(String Mystring)  
    55.         {  
    56.              //如果端口为打开,则提示,打开,则打印  
    57.             if (iHandle != -1)  
    58.             {  
    59.                 OVERLAPPED x = new OVERLAPPED();  
    60.                 int i = 0;  
    61.                 //byte[] mybyte = System.Text.Encoding.Default.GetBytes(Mystring);  
    62.                 byte[] mybyte = Encoding.GetEncoding("GB2312").GetBytes(Mystring);  
    63.                 bool b = WriteFile(iHandle, mybyte, mybyte.Length, ref i, ref x);  
    64.                 return b;  
    65.             }  
    66.             else  
    67.             {  
    68.                 throw new Exception("不能连接到打印机!");  
    69.             }  
    70.         }  
    71.         /// <summary>  
    72.         /// 打印命令,通过参数,可以打印小票打印机的一些命令,比如换行,行间距,打印位图等。  
    73.         /// </summary>  
    74.         /// <param name="mybyte"></param>  
    75.         /// <returns></returns>  
    76.         public bool Write(byte[] mybyte)  
    77.         {  
    78.             //如果端口为打开,则提示,打开,则打印  
    79.             if (iHandle != -1)  
    80.             {  
    81.                 OVERLAPPED x = new OVERLAPPED();  
    82.                 int i = 0;  
    83.                 return WriteFile(iHandle, mybyte, mybyte.Length, ref i, ref x);  
    84.             }  
    85.             else  
    86.             {  
    87.                 throw new Exception("不能连接到打印机!");  
    88.             }  
    89.         }  
    90.   
    91.         /// <summary>  
    92.         /// 关闭端口  
    93.         /// </summary>  
    94.         /// <returns></returns>  
    95.         public bool Close()  
    96.         {  
    97.             return CloseHandle(iHandle);  
    98.         }  
    99.   
    100. }  


     

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

    1. /// <summary>  
    2.        /// 打印图片方法  
    3.        /// </summary>  
    4.        public void PrintOne()  
    5.        {  
    6.            //获取图片  
    7.            Bitmap bmp = new Bitmap(pictureBox1.Image);  
    8.   
    9.            //设置字符行间距为n点行  
    10.            //byte[] data = new byte[] { 0x1B, 0x33, 0x00 };  
    11.            string send = "" + (char)(27) + (char)(51) + (char)(0);  
    12.            byte[] data = new byte[send.Length];  
    13.            for (int i = 0; i send.Length; i++)  
    14.            {  
    15.                data[i] = (byte)send[i];  
    16.            }  
    17.            lc.Write(data);  
    18.   
    19.            data[0] = (byte)'x00';  
    20.            data[1] = (byte)'x00';  
    21.            data[2] = (byte)'x00';    // Clear to Zero.  
    22.   
    23.            Color pixelColor;  
    24.   
    25.   
    26.            //ESC * m nL nH d1…dk   选择位图模式  
    27.            // ESC * m nL nH  
    28.            byte[] escBmp = new byte[] { 0x1B, 0x2A, 0x00, 0x00, 0x00 };  
    29.   
    30.            escBmp[2] = (byte)'x21';  
    31.   
    32.            //nL, nH  
    33.            escBmp[3] = (byte)(bmp.Width % 256);  
    34.            escBmp[4] = (byte)(bmp.Width / 256);  
    35.   
    36.            //循环图片像素打印图片  
    37.            //循环高  
    38.            for (int i = 0; i < (bmp.Height / 24 + 1); i++)  
    39.            {  
    40.                //设置模式为位图模式  
    41.                lc.Write(escBmp);  
    42.                //循环宽  
    43.                for (int j = 0; j bmp.Width; j++)  
    44.                {  
    45.                    for (int k = 0; k 24; k++)  
    46.                    {  
    47.                        if (((i * 24) + k) bmp.Height)  // if within the BMP size  
    48.                        {  
    49.                            pixelColor = bmp.GetPixel(j, (i * 24) + k);  
    50.                            if (pixelColor.R == 0)  
    51.                            {  
    52.                                data[k / 8] += (byte)(128 >> (k % 8));  
    53.   
    54.                            }  
    55.                        }  
    56.                    }  
    57.                    //一次写入一个data,24个像素  
    58.                    lc.Write(data);  
    59.   
    60.                    data[0] = (byte)'x00';  
    61.                    data[1] = (byte)'x00';  
    62.                    data[2] = (byte)'x00';    // Clear to Zero.  
    63.                }  
    64.   
    65.                //换行,打印第二行  
    66.                byte[] data2 = { 0xA };  
    67.                lc.Write(data2);  
    68.            } // data  
    69.            lc.Write(" ");  
    70.        }  


     

         

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

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

    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是一个长度为3的byte数组,这个data在打印中起到什么作用呢?

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

    需要打印的图片:

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

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

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

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

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

    1. for (int i = 0; i < ((bmp.Height + 7) / 8); i++)  
    2.             {  
    3.                 _serialPort.Write(escBmp, 0, escBmp.Length);  
    4.   
    5.                 for (int j = 0; j bmp.Width; j++)  
    6.                 {  
    7.                     for (int k = 0; k 8; k++)  
    8.                     {  
    9.                         if (((i * 8) + k) bmp.Height)  // if within the BMP size  
    10.                         {  
    11.                             pixelColor = bmp.GetPixel(j, (i * 8) + k);  
    12.                             if (pixelColor.R == 0)  
    13.                             {  
    14.                                 data[0] += (byte)(128 >> k);  
    15.                             }  
    16.                         }  
    17.                     }  
    18.   
    19.                     _serialPort.Write(data, 0, 1);  
    20.                     data[0] = (byte)'x00'; // Clear to Zero.  
    21.                 }  


     

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

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

    如果大家有兴趣研究网络打印,请参加小崔的博客:http://blog.csdn.net/xiaoxian8023/article/details/8440625#comments

  • 相关阅读:
    Python基础之入门
    Python基础之语句语法
    Python基础之变量
    Nginx实现反向代理和负载均衡
    Nginx配置文件简述
    Nginx简介及环境搭建
    C#利用RabbitMQ实现消息订阅与发布
    C#利用RabbitMQ实现点对点消息传输
    RabbitMQ交换机类型简述
    Android
  • 原文地址:https://www.cnblogs.com/dancheblog/p/4535032.html
Copyright © 2011-2022 走看看