The USB interface of C# Zebra printer can print various types of codes

Posted by Davy on Tue, 18 Jan 2022 12:52:38 +0100

This case has been applied to the project and has been tested to ensure the printing stability.

The printer model used this time is zdesigner zd888-203dpi. ZPL needs to install Zebra printer related drivers. This printer uses USB port for communication. During printing, the upper computer first draws the printed content image, converts it to binary write bitstream, and then calls the printer's special instructions to send it to the printer for printing.

Library to be used: print management library function winspool DRV, this is a package of printer bottom driver and the call library of windows bottom API user32 DLL, call the library required for barcode, here choose Google's open source zxing library

1. Structure and API declaration

There are ready-made cases on the Internet. We don't need to explain more here. Just find out.
StartDocPrinter notifies the spool printer that a document will be printed on the spool
StartPagePrinter notifies spooler that a page will be printed on a given printer
WritePrinter notifies spool printer that specified data should be written to a given printer
EndDocPrinter terminates a print job for a given printer
EndPagePrinter indicates the end of one page and the beginning of the next
OpenPrinter retrieves a handle that identifies a particular printer or print server and opens it
ClosePrinter closes the given printer object

[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);

2. Image processing

Convert the image into ZPL command of Zebra printer. Here is also a reference to the existing code class on the Internet.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;


namespace ZebraPrintApplication
{
    /// <summary>
    ///Zebra tool class to convert images into Zebra printer commands
    /// </summary>
    public class ZebraUnity
    {
        #region defines private fields
        /// <summary>
        ///Thread lock to prevent multi-threaded calls.
        /// </summary>
        private static object SyncRoot = new object();
        /// <summary>
        ///ZPL compression dictionary
        /// </summary>
        private static List<KeyValuePair<char, int>> compressDictionary = new List<KeyValuePair<char, int>>();
        #endregion

        #region construction method

        static ZebraUnity()
        {
            InitCompressCode();
        }

        #endregion

        #region definition properties
        /// <summary>
        ///Binary data of image
        /// </summary>
        public static byte[] GraphBuffer { get; set; }
        /// <summary>
        ///Width of image
        /// </summary>
        private static int GraphWidth { get; set; }
        /// <summary>
        ///Height of image
        /// </summary>
        private static int GraphHeight { get; set; }
        private static int RowSize
        {
            get
            {
                return (((GraphWidth) + 31) >> 5) << 2;
            }
        }
        /// <summary>
        ///Bytes per line
        /// </summary>
        private static int RowRealBytesCount
        {
            get
            {
                if ((GraphWidth % 8) > 0)
                {
                    return GraphWidth / 8 + 1;
                }
                else
                {
                    return GraphWidth / 8;
                }
            }
        }
        #endregion

        #region bitmap to zebra instruction string
        /// <summary>
        ///Bitmap to zebra instruction string
        /// </summary>
        ///< param name = "bitmap" > bitmap data < / param >
        ///< param name = "totalbytes" > total bytes < / param >
        ///< param name = "rowbytes" > bytes per row < / param >
        ///< returns > zebra ZPL 2 command < / returns >
        public static string BmpToZpl(byte[] bitmap, out int totalBytes, out int rowBytes)
        {
            try
            {
                GraphBuffer = bitmap;
                byte[] bmpData = getBitmapData();
                string textHex = BitConverter.ToString(bmpData).Replace("-", string.Empty);
                string textBitmap = CompressLZ77(textHex);
                totalBytes = GraphHeight * RowRealBytesCount;
                rowBytes = RowRealBytesCount;
                return textBitmap;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        ///Bitmap to ZPL instruction
        /// </summary>
        ///< param name = "bitmap" > bitmap < / param >
        ///< param name = "totalbytes" > return the total number of bytes of the parameter < / param >
        ///< param name = "rowbytes" > returns the number of bytes per line of the parameter < / param >
        ///< returns > ZPL command < / returns >
        public static string BmpToZpl(Image bitmap, out int totalBytes, out int rowBytes)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Bmp);
                return BmpToZpl(stream.ToArray(), out totalBytes, out rowBytes);
            }
        }

        /// <summary>
        ///Generate ASCII hexadecimal of the picture according to the picture
        /// </summary>
        ///< param name = "sourcebmp" > original picture < / param >
        ///< param name = "totalbytes" > total bytes < / param >
        ///< param name = "rowbytes" > bytes per row < / param >
        ///< returns > ASCII hex < / returns >
        public static string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes)
        {
            // Convert to monochrome
            Bitmap grayBmp = ConvertToGrayscale(sourceBmp as Bitmap);
            // Lock bitmap data    
            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);
            // Gets the starting address of the first row of bitmap data     
            IntPtr ptr = bmpData.Scan0;
            // Defines an array to hold byte stream data of a bitmap      
            // The number of bytes corresponding to the processing pixel width. If it is not a multiple of 8, 0 will be added to the last byte    
            int width = (int)Math.Ceiling(grayBmp.Width / 8.0);
            // Get the actual byte width of the bitmap. This value may be greater than width because the multiple of 4 should be considered  
            int stride = Math.Abs(bmpData.Stride);
            // Calculate the actual number of bytes occupied by bitmap data and define the array      
            int bitmapDataLength = stride * grayBmp.Height;
            byte[] ImgData = new byte[bitmapDataLength];
            // Copy the image data from the bitmap file to the array, starting from the first line of the actual image data; Because of ptr pointer, there is no need to consider the processing of row reverse storage          
            System.Runtime.InteropServices.Marshal.Copy(ptr, ImgData, 0, bitmapDataLength);
            // Calculates the XOR operand to process the byte containing image data but with a complement 0 operation         
            byte mask = 0xFF;
            // Calculate the number of 0 complements in this byte       
            //int offset = 8 * width - grayBmp.Width;
            int offset = 8 - (grayBmp.Width % 8);
            //offset %= 8;
            offset = offset % 8;
            // Shift 0xFF to the left according to the number of complement 0           
            mask <<= (byte)offset;
            // Image anti color processing        
            for (int j = 0; j < grayBmp.Height; j++)
            {
                for (int i = 0; i < stride; i++)
                {
                    if (i < width - 1) //Image data without complement 0
                    {
                        ImgData[j * stride + i] ^= 0xFF;
                    }
                    else if (i == width - 1) //There is the last byte of a pixel, and there may be a complement of 0   
                    {
                        ImgData[j * stride + i] ^= mask;
                    }
                    else  //The last byte supplemented to meet the multiple of 4 for the line byte width        
                    {
                        //ImgData[j * stride + i] = 0x00;
                        ImgData[j * stride + i] ^= 0x00;
                    }
                }
            }
            // Converts bitmap data to hexadecimal ASCII characters          
            string zplString = BitConverter.ToString(ImgData);
            zplString = CompressLZ77(zplString.Replace("-", string.Empty));
            totalBytes = bitmapDataLength;
            rowBytes = stride;
            return zplString;
        }
        #endregion

        #region get monochrome bitmap data
        /// <summary>
        ///Get monochrome bitmap data
        /// </summary>
        /// <param name="pimage"></param>
        /// <returns></returns>
        public static 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>
        ///Get monochrome bitmap data (1bpp), excluding file header, information header and palette.
        /// </summary>
        /// <returns></returns>
        private static 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];

                When reading, each line of bytes needs to be read reversely to achieve the effect of turning up and down. The printing order of the printer needs to be read in this way.
                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 LZ77 image byte stream compression method
        public static string CompressLZ77(string text)
        {
            //Compress text converted to hexadecimal
            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 static 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]))
                {
                    //Hexadecimal value
                    result += new string(arrChar[i], count == 0 ? 1 : count);
                    count = 0;
                }
                else
                {
                    //Compression code
                    int value = GetCompressValue(arrChar[i]);
                    count += value;
                }
            }
            return result;
        }

        private static 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 static bool isHexChar(char c)
        {
            return c > 47 && c < 58 || c > 64 && c < 71 || c > 96 && c < 103;
        }

        private static string convertNumber(int count)
        {
            //Convert continuous numbers into LZ77 compressed code, such as 000, which can be represented by 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 static void InitCompressCode()
        {
            //G H I J K L M n o p q r st U V W x y corresponds to 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 corresponds to 20,40,60,80...... 340360380400.            
            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
    }
}

3. Data transmission and type conversion

 public bool SendStringToPrinter(string szPrinterName, string szString)
        {
            try
            {
                IntPtr pBytes;
                Int32 dwCount;
                // Get string length  
                dwCount = szString.Length;
                // Copies the string to an unmanaged memory block allocated by an unmanaged COM task and converts it to ANSI text
                pBytes = Marshal.StringToCoTaskMemAnsi(szString);
                // Sends the converted ANSI string to the printer
                bool res = SendBytesToPrinter(szPrinterName, pBytes, dwCount);
                // Freeing previously allocated unmanaged memory
                Marshal.FreeCoTaskMem(pBytes);
                return res;
            }
            catch
            {
                //WriteLog(ex.Message);
                return false;
            }
        }

        public 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; // Return flag, default failure
            di.pDocName = "My Zebra Print File";
            di.pDataType = "RAW";

            // Turn on the printer
            if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
            {
                // Start document
                if (StartDocPrinter(hPrinter, 1, di))
                {
                    // Start page
                    if (StartPagePrinter(hPrinter))
                    {
                        // Write bitstream
                        bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                        EndPagePrinter(hPrinter);
                    }
                    EndDocPrinter(hPrinter);
                }
                ClosePrinter(hPrinter);
            }
            // If not, write the reason for the error
            if (bSuccess == false)
            {
                dwError = Marshal.GetLastWin32Error();
            }
            return bSuccess;
        }

        public string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes)
        {
            //Bitmap map = new Bitmap(sourceBmp);  Equivalent to sourceBmp as Bitmap
            // Convert to monochrome
            Bitmap grayBmp = ZebraUnity.ConvertToGrayscale(sourceBmp as Bitmap);
            // Lock bitmap data    
            Rectangle rect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
            BitmapData bmpData = grayBmp.LockBits(rect, ImageLockMode.ReadWrite, grayBmp.PixelFormat);
            // Gets the starting address of the first row of bitmap data     
            IntPtr ptr = bmpData.Scan0;
            // Defines an array to hold byte stream data of a bitmap      
            // The number of bytes corresponding to the processing pixel width. If it is not a multiple of 8, 0 will be added to the last byte    
            int width = (int)Math.Ceiling(grayBmp.Width / 8.0);
            // Get the actual byte width of the bitmap. This value may be greater than width because the multiple of 4 should be considered  
            int stride = Math.Abs(bmpData.Stride);
            // Calculate the actual number of bytes occupied by bitmap data and define the array      
            int bitmapDataLength = stride * grayBmp.Height;
            byte[] ImgData = new byte[bitmapDataLength];
            // Copy the image data from the bitmap file to the array, starting from the first line of the actual image data; Because of ptr pointer, there is no need to consider the processing of row reverse storage          
            Marshal.Copy(ptr, ImgData, 0, bitmapDataLength);
            // Calculates the XOR operand to process the byte containing image data but with a complement 0 operation         
            byte mask = 0xFF;
            // Calculate the number of 0 complements in this byte       
            //int offset = 8 * width - grayBmp.Width;
            int offset = 8 - (grayBmp.Width % 8);
            //offset %= 8;
            offset = offset % 8;
            // Shift 0xFF to the left according to the number of complement 0           
            mask <<= (byte)offset;
            // Image anti color processing        
            for (int j = 0; j < grayBmp.Height; j++)
            {
                for (int i = 0; i < stride; i++)
                {
                    if (i < width - 1) //Image data without complement 0
                    {
                        ImgData[j * stride + i] ^= 0xFF;
                    }
                    else if (i == width - 1) //There is the last byte of a pixel, and there may be a complement of 0   
                    {
                        ImgData[j * stride + i] ^= mask;
                    }
                    else  //The last byte supplemented to meet the multiple of 4 for the line byte width        
                    {
                        ImgData[j * stride + i] ^= 0x00;
                    }
                }
            }
            // Converts bitmap data to hexadecimal ASCII characters          
            string zplString = BitConverter.ToString(ImgData);
            zplString = ZebraUnity.CompressLZ77(zplString.Replace("-", string.Empty));
            totalBytes = bitmapDataLength;
            rowBytes = stride;
            return zplString;
        }

4. Create UI interface

Here, you can control the size, position and type of QR code.

5. Implementation of drawing image and printing method

        private void button1_Click(object sender, EventArgs e)
        {
            int total = 0;
            int row = 0;
            string hex = BitmapToHex(bitmap, out total, out row);
            string modecmd = "~DGR:ZLOGO.GRF," + total.ToString() + "," + row.ToString() + "," + hex;//Generate picture template instruction
            var printName = "ZDesigner ZD888-203dpi ZPL";
            if (textBox2.Text != "")
            {
                printName = textBox2.Text;
            }

            bool a = SendStringToPrinter(printName, modecmd);
            string cmd = "^XA^FO0,0^XGR:ZLOGO.GRF,1,1^FS^XZ";//Calling the template instruction ^ FO is to set the position of the upper left corner of the barcode. 0,0 means no margin at all^ FS means line feed
            bool b = SendStringToPrinter(printName, cmd);//Send call template instruction

            if (a & b)
            {
                MessageBox.Show("QR code printed successfully","Tips");
            }
            else
            {
                MessageBox.Show("QR code printing failed!", "warning");
            }
            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //Create Graphics
            Graphics graphics = Graphics.FromImage(bitmap);
            //Presentation quality
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            //Background color
            graphics.Clear(Color.White);
            int codeSize = 300;
            string printContent = "";
            int printX = 140;
            int printY = 40;
            int CodeWidth = 100;
            int CodeHeigth = 100;
            try
            {
                codeSize = Convert.ToInt32(textBox1.Text);
                printX = Convert.ToInt32(textBox3X.Text);
                printY = Convert.ToInt32(textBox4Y.Text);
                CodeWidth = Convert.ToInt32(textBox3width.Text);
                CodeHeigth = Convert.ToInt32(textBox4heigth.Text);
            }
            catch
            {
            }
            //Construct QR code writer
            MultiFormatWriter mutiWriter = new MultiFormatWriter();
            ByteMatrix bm = null;
            if (comboBox1.Text=="Code39")
            {
                bm = mutiWriter.encode(InputtextBox.Text, BarcodeFormat.CODE_39, CodeWidth, CodeHeigth);
            }
            else if (comboBox1.Text == "QR_Code")
            {
                bm = mutiWriter.encode(InputtextBox.Text, BarcodeFormat.QR_CODE, CodeWidth, CodeHeigth);
            }
            else
            {
                try
                {
                    bm = mutiWriter.encode(InputtextBox.Text, BarcodeFormat.EAN_13, CodeWidth, CodeHeigth);
                }
                catch
                {
                    MessageBox.Show("Please enter 13 as a number","warning");
                    return;
                } 
            }
            Bitmap img = bm.ToBitmap();
            string printtime = DateTime.Now.ToString("yyyy/MM/dd HH:mm");
            graphics.DrawString("Date and time:" + printtime, new Font("Microsoft YaHei ", 12, FontStyle.Bold), new SolidBrush(Color.Black), 0, 0 + codeSize);
            graphics.DrawString(printContent, new Font("Microsoft YaHei ", 12, FontStyle.Bold), new SolidBrush(Color.Black), 170, 188 + codeSize);
            graphics.DrawImage(img, printX, printY + codeSize, CodeWidth, CodeHeigth);
            //display graphics
            this.pictureBox1.Image = bitmap;
        }

        public bool SendStringToPrinter(string szPrinterName, string szString)
        {
            try
            {
                IntPtr pBytes;
                Int32 dwCount;
                // Get string length  
                dwCount = szString.Length;
                // Copies the string to an unmanaged memory block allocated by an unmanaged COM task and converts it to ANSI text
                pBytes = Marshal.StringToCoTaskMemAnsi(szString);
                // Sends the converted ANSI string to the printer
                bool res = SendBytesToPrinter(szPrinterName, pBytes, dwCount);
                // Freeing previously allocated unmanaged memory
                Marshal.FreeCoTaskMem(pBytes);
                return res;
            }
            catch
            {
                return false;
            }
        }

        public 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; // Return flag, default failure
             di.pDocName = "My Zebra Print File";
             di.pDataType = "RAW";
 
             // Turn on the printer
             if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
             {
                 // Start document
                 if (StartDocPrinter(hPrinter, 1, di))
                 {
                     // Start page
                     if (StartPagePrinter(hPrinter))
                     {
                         // Write bitstream
                         bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                         EndPagePrinter(hPrinter);
                     }
                     EndDocPrinter(hPrinter);
                 }
                 ClosePrinter(hPrinter);
             }
             // If not, write the reason for the error
             if (bSuccess == false)
             {
                 dwError = Marshal.GetLastWin32Error();
             }
             return bSuccess;
         }

        public  string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes)
        {
            //Bitmap map = new Bitmap(sourceBmp);  Equivalent to sourceBmp as Bitmap
            // Convert to monochrome
            Bitmap grayBmp = ZebraUnity.ConvertToGrayscale(sourceBmp as Bitmap);
            // Lock bitmap data    
            Rectangle rect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
            BitmapData bmpData = grayBmp.LockBits(rect, ImageLockMode.ReadWrite, grayBmp.PixelFormat);
            // Gets the starting address of the first row of bitmap data     
            IntPtr ptr = bmpData.Scan0;
            // Defines an array to hold byte stream data of a bitmap      
            // The number of bytes corresponding to the processing pixel width. If it is not a multiple of 8, 0 will be added to the last byte    
            int width = (int)Math.Ceiling(grayBmp.Width / 8.0);
            // Get the actual byte width of the bitmap. This value may be greater than width because the multiple of 4 should be considered  
            int stride = Math.Abs(bmpData.Stride);
            // Calculate the actual number of bytes occupied by bitmap data and define the array      
            int bitmapDataLength = stride * grayBmp.Height;
            byte[] ImgData = new byte[bitmapDataLength];
            // Copy the image data from the bitmap file to the array, starting from the first line of the actual image data; Because of ptr pointer, there is no need to consider the processing of row reverse storage          
            Marshal.Copy(ptr, ImgData, 0, bitmapDataLength);
            // Calculates the XOR operand to process the byte containing image data but with a complement 0 operation         
            byte mask = 0xFF;
            // Calculate the number of 0 complements in this byte       
            //int offset = 8 * width - grayBmp.Width;
            int offset = 8 - (grayBmp.Width % 8);
            //offset %= 8;
            offset = offset % 8;
            // Shift 0xFF to the left according to the number of complement 0           
            mask <<= (byte)offset;
            // Image anti color processing        
            for (int j = 0; j < grayBmp.Height; j++)
            {
                for (int i = 0; i < stride; i++)
                {
                    if (i < width - 1) //Image data without complement 0
                    {
                        ImgData[j * stride + i] ^= 0xFF;
                    }
                    else if (i == width - 1) //There is the last byte of a pixel, and there may be a complement of 0   
                    {
                        ImgData[j * stride + i] ^= mask;
                    }
                    else  //The last byte supplemented to meet the multiple of 4 for the line byte width        
                    {
                        ImgData[j * stride + i] ^= 0x00;
                    }
                }
            }
            // Converts bitmap data to hexadecimal ASCII characters          
            string zplString = BitConverter.ToString(ImgData);
            zplString = ZebraUnity.CompressLZ77(zplString.Replace("-", string.Empty));
            totalBytes = bitmapDataLength;
            rowBytes = stride;
            return zplString;
        }

6. Image display and printing effect

Code39 barcode display

QRCode QR code display

EAN_13 bar code display

Barcode printing effect: