圖片生成ZPL指令
ZPL在線設計預覽
https://labelary.com/viewer.html
?上述網址可以自定義ZPL指令在線編輯,方便查看ZPL生成面單排版和布局
具體ZPL指令明細及指令代表含義,可自行搜索,網上很多,此處只解釋圖片轉換成ZPL后的指令
??
斑馬工具類,把圖像轉換成斑馬打印機的命令
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Share
{
/// <summary>
/// 斑馬工具類,把圖像轉換成斑馬打印機的命令
/// </summary>
public class ZebraUnity
{
#region 定義私有字段
/// <summary>
/// 線程鎖,防止多線程調用。
/// </summary>
private static object SyncRoot = new object();
/// <summary>
/// ZPL壓縮字典
/// </summary>
private static List<KeyValuePair<char, int>> compressDictionary = new List<KeyValuePair<char, int>>();
#endregion
#region 構造方法
static ZebraUnity()
{
InitCompressCode();
}
#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>
/// <param name="bitmap">位圖數據</param>
/// <param name="totalBytes">總共的字節數</param>
/// <param name="rowBytes">每行的字節數</param>
/// <returns>斑馬ZPL 2命令</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>
/// 位圖轉ZPL指令
/// </summary>
/// <param name="bitmap">位圖</param>
/// <param name="totalBytes">返回參數總共字節數</param>
/// <param name="rowBytes">返回參數每行的字節數</param>
/// <returns>ZPL命令</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>
/// 根據圖片生成圖片的ASCII 十六進制
/// </summary>
/// <param name="sourceBmp">原始圖片</param>
/// <param name="totalBytes">總共字節數</param>
/// <param name="rowBytes">每行的字節數</param>
/// <returns>ASCII 十六進制</returns>
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 獲取單色位圖數據
/// <summary>
/// 獲取單色位圖數據
/// </summary>
/// <param name="pimage"></param>
/// <returns></returns>
private 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>
/// 獲取單色位圖數據(1bpp),不含文件頭、信息頭、調色板三類數據。
/// </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];
讀取時需要反向讀取每行字節實現上下翻轉的效果,打印機打印順序需要這樣讀取。
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圖像字節流壓縮方法
private 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
}
}
實際調用
眾所周知,ZPL指令必須以^ XA開頭,以^ XZ 結尾,我們只需要將轉換的ZPL替換下方即可
??^ XA
??^ LH0,0 ^ LL80 ^ PW2080
??//TODO:生成的ZPL此處替換
??^ XZ
本文我們以轉換ASCII 16進制為例,必須我們需要轉換如下圖片

System.Drawing.Image image = Image.FromFile(@"D:\Users\steph\Pictures\R-C.jpg");
string data = ZebraUnity.BitmapToHex(image, out int to, out int row);
string zpl = string.Format("~DGR:Temp0.GRF,{0},{1},{2}^FO30,0^XGR:Temp0.GRF,1,1^FS", to, row, data);
Console.WriteLine(zpl);
??生成的ZPL指令為zpl,然后我們上述格式,將生成的ZPL替換到指令模板中
展示效果

結束語
源碼都在文中,可隨意取用,喜歡的朋友可以關注下下方的公眾號,筆者會定時分享工作中和生活中使用過的小技巧及源碼分享.
最后,感謝您的耐心陪伴!如果覺得本篇博文對您或者身邊朋友有幫助的,麻煩點個關注!贈人玫瑰,手留余香,您的支持就是我寫作最大的動力,感謝您的關注,期待和您一起探討!再會!

作者:Stephen-kzx
出處:http://www.rzrgm.cn/axing/
公眾號:會定時分享寫工作中或者生活中遇到的小游戲和小工具源碼。有興趣的幫忙點下關注!感恩!
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號