Zebra 打印机 - 不打印 PNG 流 *提供了我自己的答案*
Zebra Printer - Not Printing PNG Stream *Provided my own answer*
我想我已经非常接近打印了。然而它仍然不是。没有抛出异常,它似乎确实击中了斑马打印机,但没有。这是一个远景,因为我认为大多数人都处于与我相同的位置并且对此知之甚少。任何人都可以提供任何帮助,无论多么小,我都会失去生活的意志
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var stream = new MemoryStream())
{
if (responseStream == null)
{
return;
}
responseStream.CopyTo(stream);
stream.Position = 0;
using (var zipout = ZipFile.Read(stream))
{
using (var ms = new MemoryStream())
{
foreach (var e in zipout.Where(e => e.FileName.Contains(".png")))
{
e.Extract(ms);
}
if (ms.Length <= 0)
{
return;
}
var binaryData = ms.ToArray();
byte[] compressedFileData;
// Compress the data using the LZ77 algorithm.
using (var outStream = new MemoryStream())
{
using (var compress = new DeflateStream(outStream, CompressionMode.Compress, true))
{
compress.Write(binaryData, 0, binaryData.Length);
compress.Flush();
compress.Close();
}
compressedFileData = outStream.ToArray();
}
// Encode the compressed data using the MIME Base64 algorithm.
var base64 = Convert.ToBase64String(compressedFileData);
// Calculate a CRC across the encoded data.
var crc = Calc(Convert.FromBase64String(base64));
// Add a unique header to differentiate the new format from the existing ASCII hexadecimal encoding.
var finalData = string.Format(":Z64:{0}:{1}", base64, crc);
var zplToSend = "~DYR:LOGO,P,P," + finalData.Length + ",," + finalData;
const string PrintImage = "^XA^FO0,0^IMR:LOGO.PNG^FS^XZ";
try
{
var client = new System.Net.Sockets.TcpClient();
client.Connect(IpAddress, Port);
var writer = new StreamWriter(client.GetStream(), Encoding.UTF8);
writer.Write(zplToSend);
writer.Flush();
writer.Write(PrintImage);
writer.Close();
client.Close();
}
catch (Exception ex)
{
// Catch Exception
}
}
}
}
}
}
private static ushort Calc(byte[] data)
{
ushort wCrc = 0;
for (var i = 0; i < data.Length; i++)
{
wCrc ^= (ushort)(data[i] << 8);
for (var j = 0; j < 8; j++)
{
if ((wCrc & 0x8000) != 0)
{
wCrc = (ushort)((wCrc << 1) ^ 0x1021);
}
else
{
wCrc <<= 1;
}
}
}
return wCrc;
}
以下代码对我有用。问题是命令,这些非常非常重要!下面是我用过的命令概览,更多可以参考here
PrintImage
^XA
开始格式说明 ^XA 命令用于 ZPL II 代码的开头。它是左括号,表示新标签格式的开始。此命令替换为单个 ASCII 控制字符 STX(control-B,十六进制 02)。格式 ^XA 注释 有效的 ZPL II 格式要求标签格式应以 ^XA 命令开始并以 ^XZ 命令结束。
^FO
字段原点描述 ^FO 命令设置字段原点,相对于标签原点 (^LH) 位置。 ^FO 通过定义与旋转无关的沿 x 轴和 y 轴的点来设置字段区域的左上角。格式 ^FOx,y,z
- x = x 轴位置(以点为单位)接受的值:0 到 32000 默认值
值:0
- y = y 轴位置(以点为单位)接受的值:0 到 32000
默认值:0
- z = justification z参数只是
固件版本 V60.14.x、V50.14.x 或更高版本支持。公认
值:0 = 左对齐 1 = 右对齐 2 = 自动
理由(取决于脚本)默认值:最后接受 ^FW
值或 ^FW 默认值
^IM
图像移动 描述 ^IM 命令将图像从存储区域直接移动到位图中。该命令与 ^XG 命令(调用图形)相同,只是没有尺寸参数。格式 ^IMd:o.x
- d = 存储对象的位置接受值:R:、E:、B: 和 A:默认值:搜索优先级
- o = 对象名称接受值:1 到 8 个字母数字字符默认值:如果未指定名称,则使用 UNKNOWN
- x = 扩展名固定值:.GRF、.PNG
^FS
字段分隔符说明 ^FS 命令表示字段定义的结尾。或者,^FS 命令也可以作为单个 ASCII 控制代码 SI(Control-O,十六进制 0F)发出。格式 ^FS
^XZ
结束格式说明 ^XZ 命令是结束(结束)括号。它指示标签格式的结束。收到此命令时,将打印标签。此命令也可以作为单个 ASCII 控制字符 ETX(Control-C,十六进制 03)发出。 Format ^XZ Comments 标签格式必须以 ^XA 命令开始并以 ^XZ 命令结束才能成为有效的 ZPL II 格式。
zplToSend
^MN
媒体跟踪 说明 此命令指定正在使用的媒体类型和以点为单位的黑标偏移量。此项目符号列表显示与此命令关联的媒体类型:
- 连续介质 – 这种介质没有物理特征(例如网状物、缺口、穿孔、黑标)来分隔标签。标签长度由 ^LL 命令确定。
- Continuous Media,可变长度 – 与 Continuous Media 相同,但如果打印标签的部分超出定义的标签长度,标签尺寸将自动扩展以包含它们。此标签长度扩展仅适用于当前标签。请注意,^MNV 仍然需要使用 ^LL 命令来定义所需的初始标签长度。
- 非连续介质 – 这种介质具有某种类型的物理特征(例如网状、凹口、穿孔、黑标)来分隔标签。
格式 ^MNa,b
- a = media being used 接受值:N = continuous media Y = non-continuous media web sensing d, e W = non-continuous media web sensing d, e M = non-continuous media mark sensing A = auto - 在校准期间检测介质类型 d, f V = 连续介质,可变长度 g 默认值:必须输入值或忽略命令
- b = 黑色标记偏移量(以点为单位) 这设置了介质标记相对于文档分隔点的预期位置。如果设置为 0,则应在分离点找到介质标记。 (即穿孔、切割点等)所有值都以点列出。除非 a 参数设置为 M,否则忽略此参数。如果缺少此参数,则使用默认值。可接受的值: -80 到 283 对于热敏打印机 -240 到 566 对于 600 dpi 打印机 -75 到 283 对于 KR403 打印机 -120 到 283 对于所有其他打印机默认值:0
~DY
下载对象 描述 ~DY 命令以任何支持的格式下载到打印机图形对象或字体。可以使用此命令代替 ~DG 以获得更多保存和加载选项。 ~DY 是在固件高于 X.13 的打印机上下载 TrueType 字体的首选命令。它比 ~DU 快。 ~DY 命令还支持下载无线证书文件。格式 ~DYd:f,b,x,t,w,data
备注
使用证书文件时,您的打印机支持:
- 使用隐私增强邮件 (PEM) 格式的证书文件。
- 使用客户端证书和私钥作为两个文件,分别下载。
- 为 EAP-FAST 使用可导出的 PAC 文件。
- Zebra 建议使用 Linear sty
- d = 文件位置 在固件版本 V60.15.x、V50.15.x 或更高版本中,.NRD 和 .PAC 文件位于 E:。接受值:R:、E:、B: 和 A:默认值:R:
- f = 文件名接受值:1 到 8 个字母数字字符默认值:如果未指定名称,则使用 UNKNOWN
- b = 数据字段中下载的格式 .TTE 和 .TTF 仅在固件版本 V60.14.x、V50.14.x 或更高版本中受支持。可接受的值: A = 未压缩(ZB64、ASCII) B = 未压缩(.TTE、.TTF、二进制) C = AR 压缩(仅由 Zebra 的 BAR-ONE® v5 使用) P = 便携式网络图形 (.PNG) - ZB64编码默认值:必须指定一个值
clearDownLabel
^ID
说明 ^ID 命令从存储区中删除对象、图形、字体和存储格式。可以有选择地或成组地删除对象。此命令可在打印格式中用于在保存新对象之前删除对象,或在独立格式中用于删除对象。
图像名称和扩展名支持使用星号 (*) 作为通配符。这使您可以轻松删除选定的对象组。格式 ^IDd:o.x
- d = 存储对象的位置接受值:R:、E:、B: 和 A:默认值:R:
- o = 对象名称接受值:任何 1 到 8 个字符的名称默认值:如果未指定名称,则使用 UNKNOWN
x = extension Accepted Values: 任何符合 Zebra 约定的扩展名
默认值:.GRF
const string PrintImage = "^XA^FO0,0,0^IME:LOGO.PNG^FS^XZ";
var zplImageData = string.Empty;
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var stream = new MemoryStream())
{
if (responseStream == null)
{
return;
}
responseStream.CopyTo(stream);
stream.Position = 0;
using (var zipout = ZipFile.Read(stream))
{
using (var ms = new MemoryStream())
{
foreach (var e in zipout.Where(e => e.FileName.Contains(".png")))
{
e.Extract(ms);
}
if (ms.Length <= 0)
{
return;
}
var binaryData = ms.ToArray();
foreach (var b in binaryData)
{
var hexRep = string.Format("{0:X}", b);
if (hexRep.Length == 1)
{
hexRep = "0" + hexRep;
}
zplImageData += hexRep;
}
var zplToSend = "^XA" + "^FO0,0,0" + "^MNN" + "~DYE:LOGO,P,P," + binaryData.Length + ",," + zplImageData + "^XZ";
var label = GenerateStreamFromString(zplToSend);
var client = new System.Net.Sockets.TcpClient();
client.Connect(IpAddress, Port);
label.CopyTo(client.GetStream());
label.Flush();
client.Close();
var cmd = GenerateStreamFromString(PrintImage);
var client2 = new System.Net.Sockets.TcpClient();
client2.Connect(IpAddress, Port);
cmd.CopyTo(client2.GetStream());
cmd.Flush();
client2.Close();var clearDownLabel = GenerateStreamFromString("^XA^IDR:LOGO.PNG^FS^XZ");
var client3 = new System.Net.Sockets.TcpClient();
client3.Connect(IpAddress, Port);
clearDownLabel.CopyTo(client3.GetStream());
clearDownLabel.Flush();
client3.Close();
}
}
}
}
}
}
一旦你知道怎么做就很容易了。
base64 中的 Zebra ZPL 徽标示例
Python3
import crcmod
import base64
crc16 = crcmod.predefined.mkCrcFun('xmodem')
s = hex(crc16(ZPL_LOGO.encode()))[2:]
print (f"crc16: {s}")
至少我可以这么说
我想我已经非常接近打印了。然而它仍然不是。没有抛出异常,它似乎确实击中了斑马打印机,但没有。这是一个远景,因为我认为大多数人都处于与我相同的位置并且对此知之甚少。任何人都可以提供任何帮助,无论多么小,我都会失去生活的意志
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var stream = new MemoryStream())
{
if (responseStream == null)
{
return;
}
responseStream.CopyTo(stream);
stream.Position = 0;
using (var zipout = ZipFile.Read(stream))
{
using (var ms = new MemoryStream())
{
foreach (var e in zipout.Where(e => e.FileName.Contains(".png")))
{
e.Extract(ms);
}
if (ms.Length <= 0)
{
return;
}
var binaryData = ms.ToArray();
byte[] compressedFileData;
// Compress the data using the LZ77 algorithm.
using (var outStream = new MemoryStream())
{
using (var compress = new DeflateStream(outStream, CompressionMode.Compress, true))
{
compress.Write(binaryData, 0, binaryData.Length);
compress.Flush();
compress.Close();
}
compressedFileData = outStream.ToArray();
}
// Encode the compressed data using the MIME Base64 algorithm.
var base64 = Convert.ToBase64String(compressedFileData);
// Calculate a CRC across the encoded data.
var crc = Calc(Convert.FromBase64String(base64));
// Add a unique header to differentiate the new format from the existing ASCII hexadecimal encoding.
var finalData = string.Format(":Z64:{0}:{1}", base64, crc);
var zplToSend = "~DYR:LOGO,P,P," + finalData.Length + ",," + finalData;
const string PrintImage = "^XA^FO0,0^IMR:LOGO.PNG^FS^XZ";
try
{
var client = new System.Net.Sockets.TcpClient();
client.Connect(IpAddress, Port);
var writer = new StreamWriter(client.GetStream(), Encoding.UTF8);
writer.Write(zplToSend);
writer.Flush();
writer.Write(PrintImage);
writer.Close();
client.Close();
}
catch (Exception ex)
{
// Catch Exception
}
}
}
}
}
}
private static ushort Calc(byte[] data)
{
ushort wCrc = 0;
for (var i = 0; i < data.Length; i++)
{
wCrc ^= (ushort)(data[i] << 8);
for (var j = 0; j < 8; j++)
{
if ((wCrc & 0x8000) != 0)
{
wCrc = (ushort)((wCrc << 1) ^ 0x1021);
}
else
{
wCrc <<= 1;
}
}
}
return wCrc;
}
以下代码对我有用。问题是命令,这些非常非常重要!下面是我用过的命令概览,更多可以参考here
PrintImage
^XA 开始格式说明 ^XA 命令用于 ZPL II 代码的开头。它是左括号,表示新标签格式的开始。此命令替换为单个 ASCII 控制字符 STX(control-B,十六进制 02)。格式 ^XA 注释 有效的 ZPL II 格式要求标签格式应以 ^XA 命令开始并以 ^XZ 命令结束。
^FO 字段原点描述 ^FO 命令设置字段原点,相对于标签原点 (^LH) 位置。 ^FO 通过定义与旋转无关的沿 x 轴和 y 轴的点来设置字段区域的左上角。格式 ^FOx,y,z
- x = x 轴位置(以点为单位)接受的值:0 到 32000 默认值 值:0
- y = y 轴位置(以点为单位)接受的值:0 到 32000 默认值:0
- z = justification z参数只是 固件版本 V60.14.x、V50.14.x 或更高版本支持。公认 值:0 = 左对齐 1 = 右对齐 2 = 自动 理由(取决于脚本)默认值:最后接受 ^FW 值或 ^FW 默认值
^IM 图像移动 描述 ^IM 命令将图像从存储区域直接移动到位图中。该命令与 ^XG 命令(调用图形)相同,只是没有尺寸参数。格式 ^IMd:o.x
- d = 存储对象的位置接受值:R:、E:、B: 和 A:默认值:搜索优先级
- o = 对象名称接受值:1 到 8 个字母数字字符默认值:如果未指定名称,则使用 UNKNOWN
- x = 扩展名固定值:.GRF、.PNG
^FS 字段分隔符说明 ^FS 命令表示字段定义的结尾。或者,^FS 命令也可以作为单个 ASCII 控制代码 SI(Control-O,十六进制 0F)发出。格式 ^FS
^XZ 结束格式说明 ^XZ 命令是结束(结束)括号。它指示标签格式的结束。收到此命令时,将打印标签。此命令也可以作为单个 ASCII 控制字符 ETX(Control-C,十六进制 03)发出。 Format ^XZ Comments 标签格式必须以 ^XA 命令开始并以 ^XZ 命令结束才能成为有效的 ZPL II 格式。
zplToSend
^MN 媒体跟踪 说明 此命令指定正在使用的媒体类型和以点为单位的黑标偏移量。此项目符号列表显示与此命令关联的媒体类型:
- 连续介质 – 这种介质没有物理特征(例如网状物、缺口、穿孔、黑标)来分隔标签。标签长度由 ^LL 命令确定。
- Continuous Media,可变长度 – 与 Continuous Media 相同,但如果打印标签的部分超出定义的标签长度,标签尺寸将自动扩展以包含它们。此标签长度扩展仅适用于当前标签。请注意,^MNV 仍然需要使用 ^LL 命令来定义所需的初始标签长度。
- 非连续介质 – 这种介质具有某种类型的物理特征(例如网状、凹口、穿孔、黑标)来分隔标签。
格式 ^MNa,b
- a = media being used 接受值:N = continuous media Y = non-continuous media web sensing d, e W = non-continuous media web sensing d, e M = non-continuous media mark sensing A = auto - 在校准期间检测介质类型 d, f V = 连续介质,可变长度 g 默认值:必须输入值或忽略命令
- b = 黑色标记偏移量(以点为单位) 这设置了介质标记相对于文档分隔点的预期位置。如果设置为 0,则应在分离点找到介质标记。 (即穿孔、切割点等)所有值都以点列出。除非 a 参数设置为 M,否则忽略此参数。如果缺少此参数,则使用默认值。可接受的值: -80 到 283 对于热敏打印机 -240 到 566 对于 600 dpi 打印机 -75 到 283 对于 KR403 打印机 -120 到 283 对于所有其他打印机默认值:0
~DY 下载对象 描述 ~DY 命令以任何支持的格式下载到打印机图形对象或字体。可以使用此命令代替 ~DG 以获得更多保存和加载选项。 ~DY 是在固件高于 X.13 的打印机上下载 TrueType 字体的首选命令。它比 ~DU 快。 ~DY 命令还支持下载无线证书文件。格式 ~DYd:f,b,x,t,w,data
备注 使用证书文件时,您的打印机支持: - 使用隐私增强邮件 (PEM) 格式的证书文件。 - 使用客户端证书和私钥作为两个文件,分别下载。 - 为 EAP-FAST 使用可导出的 PAC 文件。 - Zebra 建议使用 Linear sty
- d = 文件位置 在固件版本 V60.15.x、V50.15.x 或更高版本中,.NRD 和 .PAC 文件位于 E:。接受值:R:、E:、B: 和 A:默认值:R:
- f = 文件名接受值:1 到 8 个字母数字字符默认值:如果未指定名称,则使用 UNKNOWN
- b = 数据字段中下载的格式 .TTE 和 .TTF 仅在固件版本 V60.14.x、V50.14.x 或更高版本中受支持。可接受的值: A = 未压缩(ZB64、ASCII) B = 未压缩(.TTE、.TTF、二进制) C = AR 压缩(仅由 Zebra 的 BAR-ONE® v5 使用) P = 便携式网络图形 (.PNG) - ZB64编码默认值:必须指定一个值
clearDownLabel
^ID 说明 ^ID 命令从存储区中删除对象、图形、字体和存储格式。可以有选择地或成组地删除对象。此命令可在打印格式中用于在保存新对象之前删除对象,或在独立格式中用于删除对象。
图像名称和扩展名支持使用星号 (*) 作为通配符。这使您可以轻松删除选定的对象组。格式 ^IDd:o.x
- d = 存储对象的位置接受值:R:、E:、B: 和 A:默认值:R:
- o = 对象名称接受值:任何 1 到 8 个字符的名称默认值:如果未指定名称,则使用 UNKNOWN
x = extension Accepted Values: 任何符合 Zebra 约定的扩展名 默认值:.GRF
const string PrintImage = "^XA^FO0,0,0^IME:LOGO.PNG^FS^XZ"; var zplImageData = string.Empty; using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { using (var stream = new MemoryStream()) { if (responseStream == null) { return; } responseStream.CopyTo(stream); stream.Position = 0; using (var zipout = ZipFile.Read(stream)) { using (var ms = new MemoryStream()) { foreach (var e in zipout.Where(e => e.FileName.Contains(".png"))) { e.Extract(ms); } if (ms.Length <= 0) { return; } var binaryData = ms.ToArray(); foreach (var b in binaryData) { var hexRep = string.Format("{0:X}", b); if (hexRep.Length == 1) { hexRep = "0" + hexRep; } zplImageData += hexRep; } var zplToSend = "^XA" + "^FO0,0,0" + "^MNN" + "~DYE:LOGO,P,P," + binaryData.Length + ",," + zplImageData + "^XZ"; var label = GenerateStreamFromString(zplToSend); var client = new System.Net.Sockets.TcpClient(); client.Connect(IpAddress, Port); label.CopyTo(client.GetStream()); label.Flush(); client.Close(); var cmd = GenerateStreamFromString(PrintImage); var client2 = new System.Net.Sockets.TcpClient(); client2.Connect(IpAddress, Port); cmd.CopyTo(client2.GetStream()); cmd.Flush(); client2.Close();var clearDownLabel = GenerateStreamFromString("^XA^IDR:LOGO.PNG^FS^XZ"); var client3 = new System.Net.Sockets.TcpClient(); client3.Connect(IpAddress, Port); clearDownLabel.CopyTo(client3.GetStream()); clearDownLabel.Flush(); client3.Close(); } } } } } }
一旦你知道怎么做就很容易了。 base64 中的 Zebra ZPL 徽标示例
Python3
import crcmod
import base64
crc16 = crcmod.predefined.mkCrcFun('xmodem')
s = hex(crc16(ZPL_LOGO.encode()))[2:]
print (f"crc16: {s}")
至少我可以这么说