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: