C 斑马打印机USB接口实现打印各种类型的码

C 斑马打印机USB接口实现打印各种类型的码本案例已应用到项目中 做过相关性测试 可以保证打印稳定性 本次使用打印机型号是 ZDesignerZD8 203dpiZPL 需要安装斑马打印机相关驱动

本案例已应用到项目中,做过相测试,可以保证打印稳定性。

本次使用打印机型号是ZDesigner ZD888-203dpi ZPL 需要安装斑马打印机相关驱动。本款打印机使用的USB口通讯,打印时通过上位机先绘制打印内容图像,转二进制写比特流的方式,再调用打印机专有指令发送给打印机进行打印。

需要用到的库:打印管理库函数winspool.Drv,这是个打印机底层驱动的包,windows底层API的调用库User32.dll,调用条码所需的库,这里选谷歌开源的zxing库.

1、结构和API声明

[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、图像处理

把图像转换成斑马打印机的ZPL命令,这里也是参考网上已有代码类。

using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; namespace ZebraPrintApplication { 
    ///  /// 斑马工具类,把图像转换成斑马打印机的命令 ///  public class ZebraUnity { 
    #region 定义私有字段 ///  /// 线程锁,防止多线程调用。 ///  private static object SyncRoot = new object(); ///  /// ZPL压缩字典 ///  private static List<KeyValuePair<char, int>> compressDictionary = new List<KeyValuePair<char, int>>(); #endregion #region 构造方法 static ZebraUnity() { 
    InitCompressCode(); } #endregion #region 定义属性 ///  /// 图像的二进制数据 ///  public static byte[] GraphBuffer { 
    get; set; } ///  /// 图像的宽度 ///  private static int GraphWidth { 
    get; set; } ///  /// 图像的高度 ///  private static int GraphHeight { 
    get; set; } private static int RowSize { 
    get { 
    return (((GraphWidth) + 31) >> 5) << 2; } } ///  /// 每行的字节数 ///  private static int RowRealBytesCount { 
    get { 
    if ((GraphWidth % 8) > 0) { 
    return GraphWidth / 8 + 1; } else { 
    return GraphWidth / 8; } } } #endregion #region 位图转斑马指令字符串 ///  /// 位图转斑马指令字符串 ///  /// 位图数据 /// 总共的字节数 /// 每行的字节数 /// 
   
     斑马ZPL 2命令 
    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; } } ///  /// 位图转ZPL指令 ///  /// 位图 /// 返回参数总共字节数 /// 返回参数每行的字节数 /// 
   
     ZPL命令 
    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); } } ///  /// 根据图片生成图片的ASCII 十六进制 ///  /// 原始图片 /// 总共字节数 /// 每行的字节数 /// 
   
     ASCII 十六进制 
    public static 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); zplString = CompressLZ77(zplString.Replace("-", string.Empty)); totalBytes = bitmapDataLength; rowBytes = stride; return zplString; } #endregion #region 获取单色位图数据 ///  /// 获取单色位图数据 ///  ///  /// 
    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; } ///  /// 获取单色位图数据(1bpp),不含文件头、信息头、调色板三类数据。 ///  /// 
    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]; 读取时需要反向读取每行字节实现上下翻转的效果,打印机打印顺序需要这样读取。 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图像字节流压缩方法 public static 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 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])) { 
    //十六进制值 result += new string(arrChar[i], count == 0 ? 1 : count); count = 0; } else { 
    //压缩码 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) { 
    //将连续的数字转换成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 static void InitCompressCode() { 
    //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 } } 

3、数据发送及类型转换

 public bool SendStringToPrinter(string szPrinterName, string szString) { 
    try { 
    IntPtr pBytes; Int32 dwCount; // 获取字符串长度  dwCount = szString.Length; // 将字符串复制到非托管 COM 任务分配的内存非托管内存块,并转换为 ANSI 文本 pBytes = Marshal.StringToCoTaskMemAnsi(szString); // 将已转换的 ANSI 字符串发送到打印机 bool res = SendBytesToPrinter(szPrinterName, pBytes, dwCount); // 释放先前分配的非托管内存 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; // 返回标志,默认失败 di.pDocName = "My Zebra Print File"; di.pDataType = "RAW"; // 打开打印机 if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) { 
    // 开始文档 if (StartDocPrinter(hPrinter, 1, di)) { 
    // 开始页 if (StartPagePrinter(hPrinter)) { 
    // 写比特流 bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten); EndPagePrinter(hPrinter); } EndDocPrinter(hPrinter); } ClosePrinter(hPrinter); } // 如果不成功,写错误原因 if (bSuccess == false) { 
    dwError = Marshal.GetLastWin32Error(); } return bSuccess; } public string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes) { 
    //Bitmap map = new Bitmap(sourceBmp); 等同于sourceBmp as Bitmap // 转成单色图 Bitmap grayBmp = ZebraUnity.ConvertToGrayscale(sourceBmp as Bitmap); // 锁定位图数据  Rectangle rect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height); BitmapData bmpData = grayBmp.LockBits(rect, 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指针而无需再考虑行倒序存储的处理  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; } } } // 将位图数据转换为16进制的ASCII字符  string zplString = BitConverter.ToString(ImgData); zplString = ZebraUnity.CompressLZ77(zplString.Replace("-", string.Empty)); totalBytes = bitmapDataLength; rowBytes = stride; return zplString; } 

4、建立UI界面

5、实现绘制图像及打印方法

 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;//将图片生成模板指令 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";//调用该模板指令 ^FO是设置条码左上角的位置的,0,0代表完全不留边距.^FS表示换行 bool b = SendStringToPrinter(printName, cmd);//发送调用模板指令 if (a & b) { 
    MessageBox.Show("二维码打印成功","提示"); } else { 
    MessageBox.Show("二维码打印失败!", "警告"); } } private void button2_Click(object sender, EventArgs e) { 
    //创建Graphics Graphics graphics = Graphics.FromImage(bitmap); //呈现质量 graphics.SmoothingMode = SmoothingMode.AntiAlias; //背景色 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 { 
    } //构造二维码写码器 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("请输入13为数字","警告"); return; } } Bitmap img = bm.ToBitmap(); string printtime = DateTime.Now.ToString("yyyy/MM/dd HH:mm"); graphics.DrawString("日期时间:" + printtime, new Font("微软雅黑", 12, FontStyle.Bold), new SolidBrush(Color.Black), 0, 0 + codeSize); graphics.DrawString(printContent, new Font("微软雅黑", 12, FontStyle.Bold), new SolidBrush(Color.Black), 170, 188 + codeSize); graphics.DrawImage(img, printX, printY + codeSize, CodeWidth, CodeHeigth); //显示图形 this.pictureBox1.Image = bitmap; } public bool SendStringToPrinter(string szPrinterName, string szString) { 
    try { 
    IntPtr pBytes; Int32 dwCount; // 获取字符串长度  dwCount = szString.Length; // 将字符串复制到非托管 COM 任务分配的内存非托管内存块,并转换为 ANSI 文本 pBytes = Marshal.StringToCoTaskMemAnsi(szString); // 将已转换的 ANSI 字符串发送到打印机 bool res = SendBytesToPrinter(szPrinterName, pBytes, dwCount); // 释放先前分配的非托管内存 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; // 返回标志,默认失败 di.pDocName = "My Zebra Print File"; di.pDataType = "RAW"; // 打开打印机 if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero)) { 
    // 开始文档 if (StartDocPrinter(hPrinter, 1, di)) { 
    // 开始页 if (StartPagePrinter(hPrinter)) { 
    // 写比特流 bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten); EndPagePrinter(hPrinter); } EndDocPrinter(hPrinter); } ClosePrinter(hPrinter); } // 如果不成功,写错误原因 if (bSuccess == false) { 
    dwError = Marshal.GetLastWin32Error(); } return bSuccess; } public string BitmapToHex(Image sourceBmp, out int totalBytes, out int rowBytes) { 
    //Bitmap map = new Bitmap(sourceBmp); 等同于sourceBmp as Bitmap // 转成单色图 Bitmap grayBmp = ZebraUnity.ConvertToGrayscale(sourceBmp as Bitmap); // 锁定位图数据  Rectangle rect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height); BitmapData bmpData = grayBmp.LockBits(rect, 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指针而无需再考虑行倒序存储的处理  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; } } } // 将位图数据转换为16进制的ASCII字符  string zplString = BitConverter.ToString(ImgData); zplString = ZebraUnity.CompressLZ77(zplString.Replace("-", string.Empty)); totalBytes = bitmapDataLength; rowBytes = stride; return zplString; } 

6、图像显示及打印效果

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/218463.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午7:11
下一篇 2026年3月18日 上午7:12


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号