热敏小票打印类,商超开发必备

网上也查过很多关于打印小票的资料,终究没有逃掉要用 Windows API .其实我不太喜欢用这个东东,主要是不好理解,尤其是参数。但即然.NET没有提供有效的调用LPT的方法,那就只能用了。

当前,有些热敏打印机不是 LPT 端口的,这种情况不适用下面的类,请注意!

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using Microsoft.Win32.SafeHandles;

namespace WaterClassLibrary
{

    /// <summary>
    /// LPT端口操作类,请使用 Open() 打开端口,最后结束时调用 Close()
    /// 本类调用了 Windows API
    /// </summary>
    public class Cls_PrintLPT
    {
        #region 申明使用 Windows API
        static class DESIREDACCESS
        {
            public const uint GENERIC_READ = 0x80000000;
            public const uint GENERIC_WRITE = 0x40000000;
            public const uint GENERIC_EXECUTE = 0x20000000;
            public const uint GENERIC_ALL = 0x10000000;
        }
        /// <summary>
        /// Sharing mode of the file or object
        ///</summary>
        static class SHAREMODE
        {
            public const uint FILE_SHARE_READ = 0x00000001;
            public const uint FILE_SHARE_WRITE = 0x00000002;
            public const uint FILE_SHARE_DELETE = 0x00000004;
        }
        /// <summary>
        /// Action to take on files that exist, and which action to take when files do not exist.
        /// </summary>
        static class CREATIONDISPOSITION
        {
            public const uint CREATE_NEW = 1;
            public const uint CREATE_ALWAYS = 2;
            public const uint OPEN_EXISTING = 3;
            public const uint OPEN_ALWAYS = 4;
            public const uint TRUNCATE_EXISTING = 5;
        }
        /// <summary>
        /// File attributes and flags for the file.
        /// </summary>
        static class FLAGSANDATTRIBUTES
        {
            public const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
            public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
            public const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
            public const uint FILE_FLAG_RANDOM_ACCESS = 0x10000000;
            public const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
            public const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
            public const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
            public const uint FILE_FLAG_POSIX_SEMANTICS = 0x01000000;
            public const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
            public const uint FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
            public const uint FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000;
        }

        /// <summary>
        /// 申请打开文件函数
        /// 使用本函数将对应使用 FileStream及StreamWriter 进行端口指令发送
        /// 当 FileStream及StreamWriter 都 Close 后,本函数打开的端口也会
        /// 被关闭,所以不需要单独再申明使用 CloseHandle(IntPtr hObject)
        /// </summary>
        /// <param name="lpFileName"></param>
        /// <param name="dwDesiredAccess"></param>
        /// <param name="dwShareMode"></param>
        /// <param name="lpSecurityAttributes"></param>
        /// <param name="dwCreationDisposition"></param>
        /// <param name="dwFlagsAndAttributes"></param>
        /// <param name="hTemplateFile"></param>
        /// <returns></returns>        
        [DllImport("kernel32.dll", EntryPoint = "CreateFile", CharSet = CharSet.Auto)]
        private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes
            , int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);

        #endregion

        /// <summary>
        /// 打开端口的指针
        /// </summary>
        SafeFileHandle iHandle;

        /// <summary>
        /// LPT端口号
        /// </summary>
        private string LPT_Name;

        /// <summary>
        /// 操作端口的文件流
        /// </summary>
        FileStream fs;

        /// <summary>
        /// 操作端口文件流的写入操作流
        /// </summary>
        StreamWriter sw;

        /// <summary>
        /// 常用 ESC 命令
        /// </summary>
        static class ESC_CMD
        {
            /// <summary>
            /// 立即打印并换行,本模式下不关闭文件流不起作用
            /// </summary>
            public static string PrintAndLineFeed = ((char)10).ToString();

            /// <summary>
            /// 初始化打印机,使得打印设置恢复默认
            /// </summary>
            public static string IniPrinter = ((char)(27)).ToString() + ((char)(64)).ToString();

            /// <summary>
            /// 设置行高为30
            /// </summary>
            public static string LineHeight = ((char)(27)).ToString() + ((char)(51)).ToString() + ((char)(30)).ToString();

            /// <summary>
            /// 开始加粗字体
            /// </summary>
            public static string StrWiderStart = ((char)(27)).ToString() + ((char)(33)).ToString() + ((char)(48)).ToString();//加粗

            /// <summary>
            /// 取消加宽加粗打印
            /// </summary>
            public static string StrWiderClear = ((char)(27)).ToString() + ((char)(33)).ToString() + ((char)(0)).ToString();//取消加宽加粗打印

            /// <summary>
            /// 居中命令
            /// </summary>
            public static string StrMiddleStart = ((char)27).ToString() + ((char)97).ToString() + ((char)1).ToString();

            /// <summary>
            /// 取消居中
            /// </summary>
            public static string StrMiddelClear = ((char)27).ToString() + ((char)97).ToString() + ((char)0).ToString();
        }

        /// <summary>
        /// LPT端口号,例:LPT1
        /// </summary>
        /// <param name="lpt_name"></param>
        public Cls_PrintLPT(string lpt_name)
        {
            LPT_Name = lpt_name;
        }

        /// <summary>
        /// 实例化空类,以便于使用其它的字符串方法
        /// </summary>
        public Cls_PrintLPT()
        {

        }

        /// <summary>
        /// 打印命令,通过参数,可以打印小票打印机的一些命令,比如换行,行间距,打印位图等。
        /// </summary>
        /// <param name="byData">发送到端口的字节流</param>
        /// <returns></returns>
        public void Print(byte[] byData)
        {
            //如果端口为打开,则打印
            if (iHandle.IsClosed == false)
            {
                fs.Write(byData, 0, byData.Length);
            }
        }

        /// <summary>
        /// 打印字符串
        /// </summary>
        /// <param name="strData">发送到端口的字符串</param>
        public void Print(string strData)
        {
            //如果端口为打开,则打印
            if (iHandle.IsClosed == false)
            {
                sw.Write(strData);
            }
        }

        /// <summary>
        /// 打印一行字符串
        /// </summary>
        /// <param name="strData">发送到端口的字符串</param>
        public void PrintLine(string strData)
        {
            //如果端口为打开,则打印
            if (iHandle.IsClosed == false)
            {
                sw.WriteLine(ESC_CMD.LineHeight + strData);
            }
        }

        /// <summary>
        /// 加粗打印一行
        /// </summary>
        /// <param name="strData">打印字符</param>        
        public void PrintWiderLine(string strData)
        {
            PrintLine(ESC_CMD.StrWiderStart + strData + ESC_CMD.StrWiderClear);
        }

        /// <summary>
        /// 居中打印
        /// </summary>
        /// <param name="strData">打印字符</param>
        /// <param name="withWider">是否带加粗效果</param>
        /// <returns></returns>
        public void PrintInMiddle(string strData, bool withWider)
        {            
            if (withWider == true)
            {
                PrintWiderLine(ESC_CMD.StrMiddleStart + strData);
            }
            else
            {
                PrintLine(ESC_CMD.StrMiddleStart + strData);
            }
            Print(ESC_CMD.StrMiddelClear);//这里必须单独写,因为如果加粗字符未打印就设置加粗取消,则不会有加粗效果            
        }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            //以下两句是 IntPtr 转为 SafeFileHandle,否则不能使用 SafeFileHandle 定义 CreateFile
            IntPtr handle = CreateFile(LPT_Name, 0x40000000, 0, 0, 3, 0, 0);
            iHandle = new SafeFileHandle(handle, true); //进行指针到安全文件指针的转换,这是必须的。
            if (iHandle.IsClosed == false)
            {
                fs = new FileStream(iHandle, FileAccess.ReadWrite);
                sw = new StreamWriter(fs, Encoding.GetEncoding("GB2312"));
                sw.Write(ESC_CMD.IniPrinter);//初始化打印机,使得打印设置恢复默认  
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 关闭端口,走一段纸并立即提交缓存数据
        /// </summary>        
        /// <returns></returns>
        public void Close()
        {
            //if (paperFeed == true)
            //{
            //    PaperFeed(200);//这里如果不走一段纸,那么最后打印的字符在撕纸后出不来            
            //}
            sw.Close();
            fs.Close();
        }

        /// <summary>
        /// 关闭端口,走一段纸并立即提交缓存数据
        /// </summary>
        /// <param name="LastFeedLen">走纸长度</param>
        public void Close(int LastFeedLen)
        {
            PaperFeed(LastFeedLen);
            sw.Close();
            fs.Close();
        }

        /// <summary>
        /// 打开钱箱
        /// </summary>
        /// <returns></returns>
        public void OpenMoneyBox()
        {
            try
            {
                if (iHandle.IsClosed == false)
                {
                    sw.Write(((char)27).ToString() + "p" + ((char)0).ToString() + ((char)60).ToString() + ((char)255).ToString());
                }
            }
            catch { }
        }

        /// <summary>
        /// 走纸,通常2-5的数字,能刚好走出来
        /// 其传入的数字表示要走几行
        /// </summary>
        /// <param name="n">走纸行数</param>
        /// <returns></returns>
        public void PaperFeed(int n)
        {
            Print(((char)27).ToString() + ((char)100).ToString() + ((char)n).ToString());
            //(char)27 + (char)100 + (char)n n为走纸行数           
        }

        /// <summary>
        /// 打印票据,注意因POS机小票纸宽度问题只会打印前3列
        /// 同时还要指定打印长度
        /// </summary>
        /// <param name="dtPrint">全部字段数据表</param>
        /// <returns>true:打印成功 false:打印失败</returns>        
        public void PrintDataTable(System.Data.DataTable dtPrint)
        {
            try
            {
                if (iHandle.IsClosed == false)
                {
                    string strForPrint;
                    for (int i = 0; i < dtPrint.Rows.Count; i++)
                    {
                        strForPrint = "";
                        strForPrint += PadRightEx(dtPrint.Rows[i][0].ToString(), 14, ' ');
                        strForPrint += PadRightEx(dtPrint.Rows[i][1].ToString(), 8, ' ');
                        strForPrint += PadLeftEx(dtPrint.Rows[i][2].ToString(), 8, ' ');
                        PrintLine(strForPrint);
                    }
                }
            }
            catch
            {

            }
        }

        /// <summary>
        /// 打印图片方法
        /// </summary>
        public void PrintImage(Image ig)
        {
            //获取图片
            Bitmap bmp = new Bitmap(ig);

            //设置字符行间距为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];
            }
            this.Print(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++)
            {
                //设置模式为位图模式
                this.Print(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个像素
                    this.Print(data);
                    data[0] = (byte)'\x00';
                    data[1] = (byte)'\x00';
                    data[2] = (byte)'\x00';    // Clear to Zero.
                }

                //换行,打印第二行
                byte[] data2 = { 0xA };
                this.Print(data2);
            } // data
            this.Print("\n\n");
        }

        /// <summary>
        /// 左对齐中英文混排字符串,右边不够的用 char c补齐
        /// </summary>
        /// <param name="str">指定处理字符串</param>
        /// <param name="totalByteCount">按半角计算的字符总长度</param>
        /// <param name="c">补齐字符</param>
        /// <returns></returns>
        public string PadRightEx(string str, int totalByteCount, char c)
        {
            Encoding coding = Encoding.GetEncoding("gb2312");
            int wordLen = 0;//已有的含中英文字符的长度
            int byteLen = 0;//只计算半角字符的长度
            int padLen = 0; //实际应计算减去Pad的长度
            foreach (char ch in str.ToCharArray())
            {
                wordLen++;
                if (coding.GetByteCount(ch.ToString()) == 2)
                {//该字符是中文                    
                    byteLen += 2;
                    padLen += 1;
                }
                else
                {
                    byteLen += 1;
                    //padLen += 1;
                }
                if (totalByteCount <= wordLen || totalByteCount <= byteLen)
                {
                    str = str.Substring(0, wordLen);
                    break;
                }
            }
            string w = str.PadRight(totalByteCount-padLen, c);
            return w;
        }

        /// <summary>
        /// 右对齐中英文混排字符串,左边不够的用 char c补齐
        /// </summary>
        /// <param name="str">指定处理字符串</param>
        /// <param name="totalByteCount">字符总长度</param>
        /// <param name="c">补齐字符</param>
        /// <returns></returns>
        public string PadLeftEx(string str, int totalByteCount, char c)
        {
            Encoding coding = Encoding.GetEncoding("gb2312");
            int wordLen = 0;//已有的含中英文字符的长度
            int byteLen = 0;//只计算半角字符的长度
            int padLen = 0; //实际应计算减去Pad的长度
            foreach (char ch in str.ToCharArray())
            {
                wordLen++;
                if (coding.GetByteCount(ch.ToString()) == 2)
                {//该字符是中文                    
                    byteLen += 2;
                    padLen += 1;
                }
                else
                {
                    byteLen += 1;
                    //padLen += 1;
                }
                if (totalByteCount <= wordLen || totalByteCount <= byteLen)
                {
                    str = str.Substring(0, wordLen);
                    break;
                }
            }
            string w = str.PadLeft(totalByteCount-padLen, c);
            return w;
        }

    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值