zoukankan      html  css  js  c++  java
  • C#并口热敏小票打印机打印位图包括芯片的写入

    位图

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

    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("
    
    "); }  


     

         

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

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

    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个字节就够了。

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

    印机

  • 相关阅读:
    【DFS】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem D. Divisibility Game
    【二分】【三分】【计算几何】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem L. Lines and Polygon
    【线段树】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem J. Jedi Training
    【贪心】【后缀自动机】XIII Open Championship of Y.Kupala Grodno SU Grodno, Saturday, April 29, 2017 Problem E. Enter the Word
    【转载】随机生成k个范围为1-n的随机数,其中有多少个不同的随机数?
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem H. Path or Coloring
    【枚举】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem D. Cutting Potatoes
    【找规律】【递归】XVII Open Cup named after E.V. Pankratiev Stage 4: Grand Prix of SPb, Sunday, Octorber 9, 2016 Problem F. Doubling
    【贪心】Codeforces Round #436 (Div. 2) D. Make a Permutation!
    【计算几何】【圆反演】计蒜客17314 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 G. Finding the Radius for an Inserted Circle
  • 原文地址:https://www.cnblogs.com/lihaishu/p/4500497.html
Copyright © 2011-2022 走看看