文章目录

  • 前言
  • 一、条码打印功能模块
  • 二、图片打印功能
  • 1.图片转斑马协议字段
  • 2.Canvas 生成图片
  • 三、斑马打印模拟器
  • 四、斑马打印机偏移调节
  • 总结



前言

最近客户要求新增斑马打印机打印出货牌,按照斑马的文档规则来画可真麻烦,所以打算直接界面画好展示直接图片打印好了。


一、条码打印功能模块

于是找到了一个标准的打印模块代码

// Structure and API declarions:
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public class DOCINFOA
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDocName;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pOutputFile;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDataType;
        }

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

        [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndDocPrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool EndPagePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

        // SendBytesToPrinter()
        // When the function is given a printer name and an unmanaged array
        // of bytes, the function sends those bytes to the print queue.
        // Returns true on success, false on failure.
        public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
        {
            Int32 dwError = 0, dwWritten = 0;
            IntPtr hPrinter = new IntPtr(0);
            DOCINFOA di = new DOCINFOA();
            bool bSuccess = false; // Assume failure unless you specifically succeed.

            di.pDocName = "My C#.NET RAW Document";
            di.pDataType = "RAW";

            // Open the printer.
            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
            {
                // Start a document.
                if (StartDocPrinter(hPrinter, 1, di))
                {
                    // Start a page.
                    if (StartPagePrinter(hPrinter))
                    {
                        // Write your bytes.
                        bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
                ClosePrinter(hPrinter);
            }
            // If you did not succeed, GetLastError may give more information
            // about why not.
            if (bSuccess == false)
            {
                dwError = Marshal.GetLastWin32Error();
            }
            return bSuccess;
        }

        public static bool SendFileToPrinter(string szPrinterName, string szFileName)
        {
            // Open the file.
            FileStream fs = new FileStream(szFileName, FileMode.Open);
            // Create a BinaryReader on the file.
            BinaryReader br = new BinaryReader(fs);
            // Dim an array of bytes big enough to hold the file's contents.
            Byte[] bytes = new Byte[fs.Length];
            bool bSuccess = false;
            // Your unmanaged pointer.
            IntPtr pUnmanagedBytes = new IntPtr(0);
            int nLength;

            nLength = Convert.ToInt32(fs.Length);
            // Read the contents of the file into the array.
            bytes = br.ReadBytes(nLength);
            // Allocate some unmanaged memory for those bytes.
            pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
            // Copy the managed byte array into the unmanaged array.
            Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
            // Send the unmanaged bytes to the printer.
            bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
            // Free the unmanaged memory that you allocated earlier.
            Marshal.FreeCoTaskMem(pUnmanagedBytes);
            return bSuccess;
        }

        public static bool SendStringToPrinter(string szPrinterName, string szString)
        {
            IntPtr pBytes;
            Int32 dwCount;
            // How many characters are in the string?
            dwCount = szString.Length;
            // Assume that the printer is expecting ANSI text, and then convert
            // the string to ANSI text.
            pBytes = Marshal.StringToCoTaskMemAnsi(szString);
            // Send the converted ANSI string to the printer.
            SendBytesToPrinter(szPrinterName, pBytes, dwCount);
            Marshal.FreeCoTaskMem(pBytes);
            return true;
        }
        /// <summary>
        /// 打印条码
        /// </summary>
        /// <param name="str"></param>
        /// <param name="colorDepth"></param>
        /// <param name="marginX"></param>
        /// <param name="marginY"></param>
        /// <param name="marginX1"></param>
        /// <param name="marginY1"></param>
        /// <param name="fontSizeHeight"></param>
        /// <param name="fontSizeWidth"></param>
        /// <param name="barCodeScale1"></param>
        /// <param name="barCodeScale2"></param>
        /// <param name="barCodeHeight"></param>
        /// <returns></returns>
       public static string GetZd420String(string str,
       int colorDepth = 30,
       int marginX = 380,
       int marginY = 40,
       int marginX1 = 0,
       int marginY1 = 0,
       int fontSizeHeight = 30,
       int fontSizeWidth = 24,
       int barCodeScale1 = 1,
       int barCodeScale2 = 5,
       int barCodeHeight = 100)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine($"^XA");
            builder.AppendLine($"^MD{colorDepth}");
            builder.AppendLine($"^LH{marginX},{marginY}");
            builder.AppendLine($"^FO{marginX1},{marginY1}^GB40");
            builder.AppendLine($"^ACN,{fontSizeHeight},{fontSizeWidth}");
            builder.AppendLine($"^A0N,40,47");
            builder.AppendLine($"^BY{barCodeScale1}.{barCodeScale2},2,{barCodeHeight}");
            builder.AppendLine($"^BCN,,Y,N");
            builder.AppendLine($"^FD{str}^FS");
            builder.AppendLine($"^XZ");
            return builder.ToString();
        }

这么一来,直接遍历打印机,选一个打印就好了,对于简易的条码打印已经完成功能了

List<string> strlist = new List<string>();
foreach (string sPrint in PrinterSettings.InstalledPrinters)
{
    strlist .Add(sPrint);
}
SendStringToPrinter("打印机名称", GetZd420String(str))

二、图片打印功能

1.图片转斑马协议字段

public string SendBitmap(Bitmap bitmap)
        {
            int totalbytes = 64800;
            int rowbytes = 90;
            string hex = BitmapToHex(bitmap, out totalbytes, out rowbytes);
            string mubanstring =
                "^XA" +
                "~DGR:ZLOGO.GRF," + totalbytes.ToString() + "," + rowbytes.ToString() + "," + hex +
                "^XA^FO0,0^XGR:ZLOGO.GRF,1,1^FS" +
                "^XZ";
            return mubanstring;
        }
        public string SendBitmapPath(string bitmapPath, double doubleNum = 1)
        {
            Bitmap bitmap = new Bitmap(bitmapPath);
            int newWidth = (int)(bitmap.Width * doubleNum);
            int newHeight = (int)(bitmap.Height * doubleNum);
            bitmap = (Bitmap)bitmap.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
            int totalbytes = 64800;
            int rowbytes = 90;
            string hex = BitmapToHex(bitmap, out totalbytes, out rowbytes);
            string mubanstring =
                "^XA" +
                "~DGR:ZLOGO.GRF," + totalbytes.ToString() + "," + rowbytes.ToString() + "," + hex +
                $"^XA^FO0,0^XGR:ZLOGO.GRF,1,1^FS" +
                "^XZ";
            return mubanstring;
        }
        /// <summary>
        /// 根据图片生成图片的ASCII 十六进制
        /// </summary>
        /// <param name="sourceBmp">原始图片</param>
        /// <param name="totalBytes">总共字节数</param>
        /// <param name="rowBytes">每行的字节数</param>
        /// <returns>ASCII 十六进制</returns>
        public string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes)
        {
            // 转成单色图
            Bitmap grayBmp = ConvertToGrayscale(sourceBmp as Bitmap);
            // 锁定位图数据    
            Rectangle rect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
            System.Drawing.Imaging.BitmapData bmpData = grayBmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, grayBmp.PixelFormat);
            // 获取位图数据第一行的起始地址     
            IntPtr ptr = bmpData.Scan0;
            // 定义数组以存放位图的字节流数据      
            // 处理像素宽对应的字节数,如不为8的倍数,则对最后一个字节补0    
            int width = (int)Math.Ceiling(grayBmp.Width / 8.0);
            // 获取位图实际的字节宽,这个值因为要考虑4的倍数关系,可能大于width  
            int stride = Math.Abs(bmpData.Stride);
            // 计算位图数据实际所占的字节数,并定义数组      
            int bitmapDataLength = stride * grayBmp.Height;
            byte[] ImgData = new byte[bitmapDataLength];
            // 从位图文件复制图像数据到数组,从实际图像数据的第一行开始;因ptr指针而无需再考虑行倒序存储的处理          
            System.Runtime.InteropServices.Marshal.Copy(ptr, ImgData, 0, bitmapDataLength);
            // 计算异或操作数,以处理包含图像数据但又有补0操作的那个字节         
            byte mask = 0xFF;
            // 计算这个字节补0的个数       
            //int offset = 8 * width - grayBmp.Width;
            int offset = 8 - (grayBmp.Width % 8);
            //offset %= 8;
            offset = offset % 8;
            // 按补0个数对0xFF做相应位数的左移位操作           
            mask <<= (byte)offset;
            // 图像反色处理        
            for (int j = 0; j < grayBmp.Height; j++)
            {
                for (int i = 0; i < stride; i++)
                {
                    if (i < width - 1) //无补0的图像数据
                    {
                        ImgData[j * stride + i] ^= 0xFF;
                    }
                    else if (i == width - 1) //有像素的最后一个字节,可能有补0   
                    {
                        ImgData[j * stride + i] ^= mask;
                    }
                    else  //为满足行字节宽为4的倍数而最后补的字节        
                    {
                        //ImgData[j * stride + i] = 0x00;
                        ImgData[j * stride + i] ^= 0x00;
                    }
                }
            }
            // 将位图数据转换为16进制的ASCII字符          
            string zplString = BitConverter.ToString(ImgData).Replace("-", string.Empty);

            zplString = CompressLZ77(zplString);
            totalBytes = bitmapDataLength;
            rowBytes = stride;
            return zplString;
        }

        #region 获取单色位图数据
        /// <summary>
        /// 获取单色位图数据
        /// </summary>
        /// <param name="pimage"></param>
        /// <returns></returns>
        private Bitmap ConvertToGrayscale(Bitmap pimage)
        {
            Bitmap source = null;

            // If original bitmap is not already in 32 BPP, ARGB format, then convert
            if (pimage.PixelFormat != PixelFormat.Format32bppArgb)
            {
                source = new Bitmap(pimage.Width, pimage.Height, PixelFormat.Format32bppArgb);
                source.SetResolution(pimage.HorizontalResolution, pimage.VerticalResolution);
                using (Graphics g = Graphics.FromImage(source))
                {
                    g.DrawImageUnscaled(pimage, 0, 0);
                }
            }
            else
            {
                source = pimage;
            }

            // Lock source bitmap in memory
            BitmapData sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

            // Copy image data to binary array
            int imageSize = sourceData.Stride * sourceData.Height;
            byte[] sourceBuffer = new byte[imageSize];
            Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);

            // Unlock source bitmap
            source.UnlockBits(sourceData);

            // Create destination bitmap
            Bitmap destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);

            // Lock destination bitmap in memory
            BitmapData destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);

            // Create destination buffer
            imageSize = destinationData.Stride * destinationData.Height;
            byte[] destinationBuffer = new byte[imageSize];

            int sourceIndex = 0;
            int destinationIndex = 0;
            int pixelTotal = 0;
            byte destinationValue = 0;
            int pixelValue = 128;
            int height = source.Height;
            int width = source.Width;
            int threshold = 500;

            // Iterate lines
            for (int y = 0; y < height; y++)
            {
                sourceIndex = y * sourceData.Stride;
                destinationIndex = y * destinationData.Stride;
                destinationValue = 0;
                pixelValue = 128;

                // Iterate pixels
                for (int x = 0; x < width; x++)
                {
                    // Compute pixel brightness (i.e. total of Red, Green, and Blue values)
                    pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] + sourceBuffer[sourceIndex + 3];
                    if (pixelTotal > threshold)
                    {
                        destinationValue += (byte)pixelValue;
                    }
                    if (pixelValue == 1)
                    {
                        destinationBuffer[destinationIndex] = destinationValue;
                        destinationIndex++;
                        destinationValue = 0;
                        pixelValue = 128;
                    }
                    else
                    {
                        pixelValue >>= 1;
                    }
                    sourceIndex += 4;
                }
                if (pixelValue != 128)
                {
                    destinationBuffer[destinationIndex] = destinationValue;
                }
            }

            // Copy binary image data to destination bitmap
            Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);

            // Unlock destination bitmap
            destination.UnlockBits(destinationData);

            // Dispose of source if not originally supplied bitmap
            if (source != pimage)
            {
                source.Dispose();
            }

            // Return
            return destination;
        }
        /// <summary>
        /// 获取单色位图数据(1bpp),不含文件头、信息头、调色板三类数据。
        /// </summary>
        /// <returns></returns>
        private byte[] getBitmapData()
        {
            MemoryStream srcStream = new MemoryStream();
            MemoryStream dstStream = new MemoryStream();
            Bitmap srcBmp = null;
            Bitmap dstBmp = null;
            byte[] srcBuffer = null;
            byte[] dstBuffer = null;
            byte[] result = null;
            try
            {
                srcStream = new MemoryStream(GraphBuffer);
                srcBmp = Bitmap.FromStream(srcStream) as Bitmap;
                srcBuffer = srcStream.ToArray();
                GraphWidth = srcBmp.Width;
                GraphHeight = srcBmp.Height;
                //dstBmp = srcBmp.Clone(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), PixelFormat.Format1bppIndexed);
                dstBmp = ConvertToGrayscale(srcBmp);
                dstBmp.Save(dstStream, ImageFormat.Bmp);
                dstBuffer = dstStream.ToArray();

                result = dstBuffer;

                int bfOffBits = BitConverter.ToInt32(dstBuffer, 10);
                result = new byte[GraphHeight * RowRealBytesCount];

                读取时需要反向读取每行字节实现上下翻转的效果,打印机打印顺序需要这样读取。
                for (int i = 0; i < GraphHeight; i++)
                {
                    int sindex = bfOffBits + (GraphHeight - 1 - i) * RowSize;
                    int dindex = i * RowRealBytesCount;
                    Array.Copy(dstBuffer, sindex, result, dindex, RowRealBytesCount);
                }

                for (int i = 0; i < result.Length; i++)
                {
                    result[i] ^= 0xFF;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
            finally
            {
                if (srcStream != null)
                {
                    srcStream.Dispose();
                    srcStream = null;
                }
                if (dstStream != null)
                {
                    dstStream.Dispose();
                    dstStream = null;
                }
                if (srcBmp != null)
                {
                    srcBmp.Dispose();
                    srcBmp = null;
                }
                if (dstBmp != null)
                {
                    dstBmp.Dispose();
                    dstBmp = null;
                }
            }
            return result;
        }
        #endregion
        #region 定义属性
        /// <summary>
        /// 图像的二进制数据
        /// </summary>
        public static byte[] GraphBuffer { get; set; }
        /// <summary>
        /// 图像的宽度
        /// </summary>
        private static int GraphWidth { get; set; }
        /// <summary>
        /// 图像的高度
        /// </summary>
        private static int GraphHeight { get; set; }
        private static int RowSize
        {
            get
            {
                return (((GraphWidth) + 31) >> 5) << 2;
            }
        }
        /// <summary>
        /// 每行的字节数
        /// </summary>
        private static int RowRealBytesCount
        {
            get
            {
                if ((GraphWidth % 8) > 0)
                {
                    return GraphWidth / 8 + 1;
                }
                else
                {
                    return GraphWidth / 8;
                }
            }
        }
        #endregion
        #region 定义私有字段
        /// <summary>
        /// 线程锁,防止多线程调用。
        /// </summary>
        private static object SyncRoot = new object();
        /// <summary>
        /// ZPL压缩字典
        /// </summary>
        private List<KeyValuePair<char, int>> compressDictionary;
        public List<KeyValuePair<char, int>> CompressDictionary
        {
            get
            {
                if (compressDictionary is null)
                {
                    InitCompressCode();
                }
                return compressDictionary;

            }
        }
        #endregion
        #region LZ77图像字节流压缩方法
        private string CompressLZ77(string text)
        {
            //将转成16进制的文本进行压缩
            string result = string.Empty;
            char[] arrChar = text.ToCharArray();
            int count = 1;
            for (int i = 1; i < text.Length; i++)
            {
                if (arrChar[i - 1] == arrChar[i])
                {
                    count++;
                }
                else
                {
                    result += convertNumber(count) + arrChar[i - 1];
                    count = 1;
                }
                if (i == text.Length - 1)
                {
                    result += convertNumber(count) + arrChar[i];
                }
            }
            return result;
        }

        private string DecompressLZ77(string text)
        {
            string result = string.Empty;
            char[] arrChar = text.ToCharArray();
            int count = 0;
            for (int i = 0; i < arrChar.Length; i++)
            {
                if (isHexChar(arrChar[i]))
                {
                    //十六进制值
                    result += new string(arrChar[i], count == 0 ? 1 : count);
                    count = 0;
                }
                else
                {
                    //压缩码
                    int value = GetCompressValue(arrChar[i]);
                    count += value;
                }
            }
            return result;
        }

        private int GetCompressValue(char c)
        {
            int result = 0;
            for (int i = 0; i < CompressDictionary.Count; i++)
            {
                if (c == CompressDictionary[i].Key)
                {
                    result = CompressDictionary[i].Value;
                }
            }
            return result;
        }

        private bool isHexChar(char c)
        {
            return c > 47 && c < 58 || c > 64 && c < 71 || c > 96 && c < 103;
        }

        private string convertNumber(int count)
        {
            //将连续的数字转换成LZ77压缩代码,如000可用I0表示。
            string result = string.Empty;
            if (count > 1)
            {
                while (count > 0)
                {
                    for (int i = CompressDictionary.Count - 1; i >= 0; i--)
                    {
                        if (count >= CompressDictionary[i].Value)
                        {
                            result += CompressDictionary[i].Key;
                            count -= CompressDictionary[i].Value;
                            break;
                        }
                    }
                }
            }
            return result;
        }

        private void InitCompressCode()
        {
            compressDictionary = new List<KeyValuePair<char, int>>();
            //G H I J K L M N O P Q R S T U V W X Y        对应1,2,3,4……18,19。
            //g h i j k l m n o p q r s t u v w x y z      对应20,40,60,80……340,360,380,400。            
            for (int i = 0; i < 19; i++)
            {
                compressDictionary.Add(new KeyValuePair<char, int>(Convert.ToChar(71 + i), i + 1));
            }
            for (int i = 0; i < 20; i++)
            {
                compressDictionary.Add(new KeyValuePair<char, int>(Convert.ToChar(103 + i), (i + 1) * 20));
            }
        }
        #endregion

这下图片的打印代码也搞定了

//可以直接传路径
SendStringToPrinter("打印机名称", SendBitmapPath("图片路径","放大倍数"));
//不需要保留图片的要求可以直接Bitmap打印
SendStringToPrinter("打印机名称", SendBitmap(Bitmap)));

2.Canvas 生成图片

客户样板就不展示了,写一个模板Canvas
XAML代码:

<Window x:Class="SaveCanvasAsJpg.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Save Canvas as JPG" Height="300" Width="400">
    <Grid>
        <Canvas x:Name="canvas" Background="White" Width="200" Height="150" Margin="50">
            <Ellipse Fill="Red" Width="100" Height="100"/>
            <TextBlock Text="Hello, World!" Margin="30, 60, 0, 0" FontWeight="Bold"/>
        </Canvas>
        <Button Content="Save as JPG" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0, 0, 0, 20" Click="Button_Click"/>
    </Grid>
</Window>

C#代码:

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace SaveCanvasAsJpg
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 获取Canvas控件的尺寸
            double width = canvas.ActualWidth;
            double height = canvas.ActualHeight;

            // 创建RenderTargetBitmap对象,并以Canvas为参数进行实例化
            RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)width, (int)height, 96d, 96d, System.Windows.Media.PixelFormats.Pbgra32);
            renderBitmap.Render(canvas);

            // 创建JpegBitmapEncoder对象,并以RenderTargetBitmap为参数进行实例化
            JpegBitmapEncoder encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(renderBitmap));

            // 将jpg图片保存到D盘的示例路径中
            string path = @"D:\canvas.jpg";
            using (FileStream fs = File.Open(path, FileMode.Create))
            {
                encoder.Save(fs);
            }

            MessageBox.Show("Image saved to " + path);
        }
    }
}

三、斑马打印模拟器

远程开发头疼的就是写完不知道打印效果,后来找了个模拟器网站测试,强力推荐,这下十拿九稳了

http://labelary.com/

python 调用斑马打印机打印图片 斑马打印测试页图片_c#


四、斑马打印机偏移调节

最后打印效果还需要居中一点点,直接电脑上微调了

python 调用斑马打印机打印图片 斑马打印测试页图片_c#_02

总结

功能是实现了,就是个人觉得代码越少越好,看上去代码还是太多了,有更好的实现方法还请各位大佬指点指点。
斑马打印机的规则为没有太深的去研究,大家可以去看看一下链接大佬写的,会了斑马打印机协议也是一种不错的选择,就是感觉可视化比较麻烦,而且客户要求要保留条码图片。