C# 熱敏打印機 小票打印機 打印圖片


最近一直在研究並口小票打印機打印圖片問題,這也是第一次和硬件打交道,不過還好,最終成功了。

 

這是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;  
          
          
        /// <summary>  
        /// 打開端口  
        /// </summary>  
        /// <returns></returns>  
        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;  
            }  
        }  
  
        /// <summary>  
        /// 打印字符串,通過調用該方法可以打印需要的字符串  
        /// </summary>  
        /// <param name="Mystring"></param>  
        /// <returns></returns>  
        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("不能連接到打印機!");  
            }  
        }  
        /// <summary>  
        /// 打印命令,通過參數,可以打印小票打印機的一些命令,比如換行,行間距,打印位圖等。  
        /// </summary>  
        /// <param name="mybyte"></param>  
        /// <returns></returns>  
        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("不能連接到打印機!");  
            }  
        }  
  
        /// <summary>  
        /// 關閉端口  
        /// </summary>  
        /// <returns></returns>  
        public bool Close()  
        {  
            return CloseHandle(iHandle);  
        }  
  
}  

 

因為我們這里主要是打印條形碼和二維碼,所以以條形碼和二維碼為例,寫了一個小的調用程序(這里把打印圖片的方法貼出來):

 

/// <summary>  
       /// 打印圖片方法  
       /// </summary>  
       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是一個長度為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點密度的例子,通過,如果您有興趣研究的話,也經常看到這樣的代碼:

 

 
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://blog.csdn.net/xiaoxian8023/article/details/8440625#comments


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM