解码 Z64 (ZB64) 字符串
Decoding Z64 (ZB64) string
我正在努力分解由 NiceLabel 标签制作软件生成的 ZPL 标签定义。在大多数情况下,我不必担心解码 Z64,因为它只是编码图形,我不需要更改基础数据。
但是,出于某种原因,我有一行文本被标签用作图形,可能是由于字体或其他原因。
无论如何,Z64 或 ZB64 字符串是通过使用 LZ77 压缩原始数据并将其编码为 Base64,然后在末尾附加 CRC 来创建的。
测试字符串完整示例:
:Z64:eJztkDFOxDAQRb81hRsULmBtruECyRwpZYpFGLmg5AhwFKMUuYal9CtL26QwHsbe3RMguv3lz9P85wD3/CWaiZ+56OjqWA44cwKIAyfeXXL1sQ7YWqd54czltTge+VOdOQsXFp8TrLUw9KEW3+6pLU4Zk3mC0ataonSEzU8JMywGCiFcue+c8YLGvYcLF5a+68WFhbvtRs5jdmVkWolj96vgXe/it7eucT+0+gxV5N5RrdTveQpevhnxO+BEfRe0xIzc/EbUzkn3lhLSIH6DdFeu+c39Hb7c7vksfrJryB8vu6A4cxE/NjpK1/6LkJZ3+nL1gaLt3D33/Ed+AehfkrY=:6C38
测试字符串目标示例:
eJztkDFOxDAQRb81hRsULmBtruECyRwpZYpFGLmg5AhwFKMUuYal9CtL26QwHsbe3RMguv3lz9P85wD3/CWaiZ+56OjqWA44cwKIAyfeXXL1sQ7YWqd54czltTge+VOdOQsXFp8TrLUw9KEW3+6pLU4Zk3mC0ataonSEzU8JMywGCiFcue+c8YLGvYcLF5a+68WFhbvtRs5jdmVkWolj96vgXe/it7eucT+0+gxV5N5RrdTveQpevhnxO+BEfRe0xIzc/EbUzkn3lhLSIH6DdFeu+c39Hb7c7vksfrJryB8vu6A4cxE/NjpK1/6LkJZ3+nL1gaLt3D33/Ed+AehfkrY=
我要解码/解压的代码:
static string DecompressZb64(string compressedString)
{
var b64 = SmartWarehouse.Shared.Utils.Parser.ConvertFromBase64(compressedString);
var encoding = new ASCIIEncoding();
var inBytes = Encoding.ASCII.GetBytes(b64);
var outBytes = new byte[inBytes.Length];
try
{
using (var memoryStream = new MemoryStream())
using (var decompressionStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
decompressionStream.Read(outBytes, 0, inBytes.Length);
}
return encoding.GetString(outBytes);
}
catch (Exception e)
{
// TODO: DOcument exception
Console.WriteLine(e.Message);
}
return string.Empty;
}
当前异常:
Block length does not match with its complement.
堆栈跟踪:
at System.IO.Compression.Inflater.DecodeUncompressedBlock(Boolean& end_of_block)
at System.IO.Compression.Inflater.Decode()
at System.IO.Compression.Inflater.Inflate(Byte[] bytes, Int32 offset, Int32 length)
at System.IO.Compression.DeflateStream.Read(Byte[] array, Int32 offset, Int32 count)
at SmartWarehouse.Tools.Program.DecompressZb64(String compressedString) in C:\Users\[user_dir]\Source\Repos\Handheld.[user].[fork]\SmartWarehouse.Tools\Program.cs:line 511
更新:
我正在调查这个,我发现了这个 SO post it is essentially the same issue. So i did some more research and i found this article on a blog from 2007。其中讨论了一种解决方法,即跳过输入数组中的前 2 个字节,因为这些字节实际上并未包含在 RFC 规范中。
代码更改:
static string DecompressZb64(string compressedString)
{
var b64 = Convert.FromBase64String(compressedString);
var encoding = new ASCIIEncoding();
var outBytes = new byte[b64.Length - 2];
try
{
using (var memoryStream = new MemoryStream(b64))
{
memoryStream.ReadByte();
memoryStream.ReadByte();
using (var decompressionStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
decompressionStream.Read(outBytes, 0, b64.Length - 2);
}
}
return encoding.GetString(outBytes);
}
catch (Exception e)
{
// TODO: DOcument exception
Console.WriteLine(e.Message);
}
return string.Empty;
}
此代码更改实际上不再产生异常,但是它没有正确解压缩并且 returns 这个结果:
"[=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=]"
这个数据对我来说很好,我从中得到了这张图片:
这是一个单色位图,每个像素一位。
你的代码直到流结束才读取,实际有1280字节的图像数据,然后可以解码为上图。
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ZB64Decode
{
public class Program
{
static void Main(string[] args)
{
//this is C#
var sb = new StringBuilder();
sb.AppendLine("^XA");
sb.AppendLine("^UT1,1");
sb.AppendLine("^Uh0,300,64,4000,2,0,F");
sb.AppendLine("^Uh1,300,64,4000,2,0,F");
sb.AppendLine("^UC1,0,1,0,0");
sb.AppendLine("^LH0,0");
sb.AppendLine("^LT0");
sb.AppendLine("^XZ");
sb.AppendLine("^XA");
sb.AppendLine("^PW3543");
sb.AppendLine("^LL512");
sb.AppendLine("^FO532,66^GFA,885,9272,76,:Z64:eJztWkFywyAMDMOBI0/gKTyNPC1P6RN6zKFTNxiCY0AgJE0nh+zJTc0GrdYES1wu/wWzHfiVIuLR2YZpB4FJpZH38UcouP6oRHddogqPEd/9f0WyLzyTGqpsVuLUj5tvoxv8tv3gqSa3WCSZxrjIoMgUzpAGoxnW2wZMdIFHG9tOEhQtekVyze7VSzYMQ/3DVIMTRt/sFtcUA0epZ2o28GCUfi3CCOjbDWHVhMaElRyOB1nss3+C7no7rAqf4DsTM+vr+A7VUYw4rTixeqAmqdUf2bKjESrz96LGwlQWNxRvPVHNw1N+2p9wJ30UWfkIfbKT5YQY1X/5gxVinMqtXCui53vjWVmMCEcmHS/EGOT1ecnKYoQudlXra3ONIphZ3Oh14H7rCzrKdDxXrijYLV3w5Socmr50HcixCUhfNBeQvkxIQPoiVOA92AkqPdISacwsImnMSklyiVgiu8FyF6+EnUbEXjk8SS4Rq+YUviuXyCOUH6IP14er5npX37/rmiO5rkqu90K/Q5Jcux0Ud0OekCwvuZ+Q3OdI7r9EDJZJJPerkvtoiUQWY0m+d0i+Dwk4/5BJ8P2R++p+enk3XIf5I3uSdYDX93gKTnUEywvypLdmBVlVgwInk1U1iFUcqibCKVo1AjHqhc1Qanm1W2qsq5FouFZqS677diyw2r7LcL0Vnjax7rTgHsEQQC+CUsDvl+8vpMYCOGTQogEwaALBLZo+hk2gpRbYpM1llnLpx4V6fMsQ0TQM6KaAmQqisC1ijWiyahwZ7jZUVxfZIcb0m1F96x12dnzALRx9GHf8x6cFWmzwkQAPH2EA4AE26PMh0lmTagLkMye+HDG5P4+HUCbVsHGZIqqzMLPb/wCr2VTW:718B");
sb.AppendLine("^PQ1,0,1,Y^XZ");
var zpl = sb.ToString();
//extract the Z64-String
var z64Data = "";
var bytesPerRow = 0;
foreach (var item in zpl.Split('^'))
{
if (item.StartsWith("GFA"))
{
var sp= item.Split(':');
z64Data = item.Substring(sp[0].Length);
bytesPerRow = Convert.ToInt32(sp[0].Split(',')[3]);
break;
}
}
//convert String to Bitmap
Bitmap decodedBitmap = null;
if (z64Data.StartsWith(":Z64"))
{
var imageData = DecompressZb64(z64Data.Substring(5));
int width = bytesPerRow * 8;
int height = imageData.Length / bytesPerRow;
decodedBitmap = ArrayToBitmap(imageData, width, height, PixelFormat.Format1bppIndexed);
}
Debug.WriteLine(decodedBitmap.Width + ":" + decodedBitmap.Height);
}
public static Bitmap ArrayToBitmap(byte[] bytes, int width, int height, PixelFormat pixelFormat)
{
var image = new Bitmap(width, height, pixelFormat);
var imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite, pixelFormat);
try
{
Marshal.Copy(bytes, 0, imageData.Scan0, bytes.Length);
}
finally
{
image.UnlockBits(imageData);
}
return image;
}
public static byte[] DecompressZb64(string compressedString)
{
var b64 = Convert.FromBase64String(compressedString.Split(':')[0]).Skip(2).ToArray();
return Decompress(b64);
}
public static byte[] Decompress(byte[] data)
{
byte[] decompressedArray = null;
try
{
using (MemoryStream decompressedStream = new MemoryStream())
{
using (MemoryStream compressStream = new MemoryStream(data))
{
using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionMode.Decompress))
{
deflateStream.CopyTo(decompressedStream);
}
}
decompressedArray = decompressedStream.ToArray();
}
}
catch (Exception ex)
{
// do something !
}
return decompressedArray;
}
}
}
我正在努力分解由 NiceLabel 标签制作软件生成的 ZPL 标签定义。在大多数情况下,我不必担心解码 Z64,因为它只是编码图形,我不需要更改基础数据。
但是,出于某种原因,我有一行文本被标签用作图形,可能是由于字体或其他原因。
无论如何,Z64 或 ZB64 字符串是通过使用 LZ77 压缩原始数据并将其编码为 Base64,然后在末尾附加 CRC 来创建的。
测试字符串完整示例:
:Z64:eJztkDFOxDAQRb81hRsULmBtruECyRwpZYpFGLmg5AhwFKMUuYal9CtL26QwHsbe3RMguv3lz9P85wD3/CWaiZ+56OjqWA44cwKIAyfeXXL1sQ7YWqd54czltTge+VOdOQsXFp8TrLUw9KEW3+6pLU4Zk3mC0ataonSEzU8JMywGCiFcue+c8YLGvYcLF5a+68WFhbvtRs5jdmVkWolj96vgXe/it7eucT+0+gxV5N5RrdTveQpevhnxO+BEfRe0xIzc/EbUzkn3lhLSIH6DdFeu+c39Hb7c7vksfrJryB8vu6A4cxE/NjpK1/6LkJZ3+nL1gaLt3D33/Ed+AehfkrY=:6C38
测试字符串目标示例:
eJztkDFOxDAQRb81hRsULmBtruECyRwpZYpFGLmg5AhwFKMUuYal9CtL26QwHsbe3RMguv3lz9P85wD3/CWaiZ+56OjqWA44cwKIAyfeXXL1sQ7YWqd54czltTge+VOdOQsXFp8TrLUw9KEW3+6pLU4Zk3mC0ataonSEzU8JMywGCiFcue+c8YLGvYcLF5a+68WFhbvtRs5jdmVkWolj96vgXe/it7eucT+0+gxV5N5RrdTveQpevhnxO+BEfRe0xIzc/EbUzkn3lhLSIH6DdFeu+c39Hb7c7vksfrJryB8vu6A4cxE/NjpK1/6LkJZ3+nL1gaLt3D33/Ed+AehfkrY=
我要解码/解压的代码:
static string DecompressZb64(string compressedString)
{
var b64 = SmartWarehouse.Shared.Utils.Parser.ConvertFromBase64(compressedString);
var encoding = new ASCIIEncoding();
var inBytes = Encoding.ASCII.GetBytes(b64);
var outBytes = new byte[inBytes.Length];
try
{
using (var memoryStream = new MemoryStream())
using (var decompressionStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
decompressionStream.Read(outBytes, 0, inBytes.Length);
}
return encoding.GetString(outBytes);
}
catch (Exception e)
{
// TODO: DOcument exception
Console.WriteLine(e.Message);
}
return string.Empty;
}
当前异常:
Block length does not match with its complement.
堆栈跟踪:
at System.IO.Compression.Inflater.DecodeUncompressedBlock(Boolean& end_of_block)
at System.IO.Compression.Inflater.Decode()
at System.IO.Compression.Inflater.Inflate(Byte[] bytes, Int32 offset, Int32 length)
at System.IO.Compression.DeflateStream.Read(Byte[] array, Int32 offset, Int32 count)
at SmartWarehouse.Tools.Program.DecompressZb64(String compressedString) in C:\Users\[user_dir]\Source\Repos\Handheld.[user].[fork]\SmartWarehouse.Tools\Program.cs:line 511
更新:
我正在调查这个,我发现了这个 SO post it is essentially the same issue. So i did some more research and i found this article on a blog from 2007。其中讨论了一种解决方法,即跳过输入数组中的前 2 个字节,因为这些字节实际上并未包含在 RFC 规范中。
代码更改:
static string DecompressZb64(string compressedString)
{
var b64 = Convert.FromBase64String(compressedString);
var encoding = new ASCIIEncoding();
var outBytes = new byte[b64.Length - 2];
try
{
using (var memoryStream = new MemoryStream(b64))
{
memoryStream.ReadByte();
memoryStream.ReadByte();
using (var decompressionStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
{
decompressionStream.Read(outBytes, 0, b64.Length - 2);
}
}
return encoding.GetString(outBytes);
}
catch (Exception e)
{
// TODO: DOcument exception
Console.WriteLine(e.Message);
}
return string.Empty;
}
此代码更改实际上不再产生异常,但是它没有正确解压缩并且 returns 这个结果:
"[=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=][=16=]"
这个数据对我来说很好,我从中得到了这张图片:
这是一个单色位图,每个像素一位。
你的代码直到流结束才读取,实际有1280字节的图像数据,然后可以解码为上图。
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ZB64Decode
{
public class Program
{
static void Main(string[] args)
{
//this is C#
var sb = new StringBuilder();
sb.AppendLine("^XA");
sb.AppendLine("^UT1,1");
sb.AppendLine("^Uh0,300,64,4000,2,0,F");
sb.AppendLine("^Uh1,300,64,4000,2,0,F");
sb.AppendLine("^UC1,0,1,0,0");
sb.AppendLine("^LH0,0");
sb.AppendLine("^LT0");
sb.AppendLine("^XZ");
sb.AppendLine("^XA");
sb.AppendLine("^PW3543");
sb.AppendLine("^LL512");
sb.AppendLine("^FO532,66^GFA,885,9272,76,:Z64:eJztWkFywyAMDMOBI0/gKTyNPC1P6RN6zKFTNxiCY0AgJE0nh+zJTc0GrdYES1wu/wWzHfiVIuLR2YZpB4FJpZH38UcouP6oRHddogqPEd/9f0WyLzyTGqpsVuLUj5tvoxv8tv3gqSa3WCSZxrjIoMgUzpAGoxnW2wZMdIFHG9tOEhQtekVyze7VSzYMQ/3DVIMTRt/sFtcUA0epZ2o28GCUfi3CCOjbDWHVhMaElRyOB1nss3+C7no7rAqf4DsTM+vr+A7VUYw4rTixeqAmqdUf2bKjESrz96LGwlQWNxRvPVHNw1N+2p9wJ30UWfkIfbKT5YQY1X/5gxVinMqtXCui53vjWVmMCEcmHS/EGOT1ecnKYoQudlXra3ONIphZ3Oh14H7rCzrKdDxXrijYLV3w5Socmr50HcixCUhfNBeQvkxIQPoiVOA92AkqPdISacwsImnMSklyiVgiu8FyF6+EnUbEXjk8SS4Rq+YUviuXyCOUH6IP14er5npX37/rmiO5rkqu90K/Q5Jcux0Ud0OekCwvuZ+Q3OdI7r9EDJZJJPerkvtoiUQWY0m+d0i+Dwk4/5BJ8P2R++p+enk3XIf5I3uSdYDX93gKTnUEywvypLdmBVlVgwInk1U1iFUcqibCKVo1AjHqhc1Qanm1W2qsq5FouFZqS677diyw2r7LcL0Vnjax7rTgHsEQQC+CUsDvl+8vpMYCOGTQogEwaALBLZo+hk2gpRbYpM1llnLpx4V6fMsQ0TQM6KaAmQqisC1ijWiyahwZ7jZUVxfZIcb0m1F96x12dnzALRx9GHf8x6cFWmzwkQAPH2EA4AE26PMh0lmTagLkMye+HDG5P4+HUCbVsHGZIqqzMLPb/wCr2VTW:718B");
sb.AppendLine("^PQ1,0,1,Y^XZ");
var zpl = sb.ToString();
//extract the Z64-String
var z64Data = "";
var bytesPerRow = 0;
foreach (var item in zpl.Split('^'))
{
if (item.StartsWith("GFA"))
{
var sp= item.Split(':');
z64Data = item.Substring(sp[0].Length);
bytesPerRow = Convert.ToInt32(sp[0].Split(',')[3]);
break;
}
}
//convert String to Bitmap
Bitmap decodedBitmap = null;
if (z64Data.StartsWith(":Z64"))
{
var imageData = DecompressZb64(z64Data.Substring(5));
int width = bytesPerRow * 8;
int height = imageData.Length / bytesPerRow;
decodedBitmap = ArrayToBitmap(imageData, width, height, PixelFormat.Format1bppIndexed);
}
Debug.WriteLine(decodedBitmap.Width + ":" + decodedBitmap.Height);
}
public static Bitmap ArrayToBitmap(byte[] bytes, int width, int height, PixelFormat pixelFormat)
{
var image = new Bitmap(width, height, pixelFormat);
var imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite, pixelFormat);
try
{
Marshal.Copy(bytes, 0, imageData.Scan0, bytes.Length);
}
finally
{
image.UnlockBits(imageData);
}
return image;
}
public static byte[] DecompressZb64(string compressedString)
{
var b64 = Convert.FromBase64String(compressedString.Split(':')[0]).Skip(2).ToArray();
return Decompress(b64);
}
public static byte[] Decompress(byte[] data)
{
byte[] decompressedArray = null;
try
{
using (MemoryStream decompressedStream = new MemoryStream())
{
using (MemoryStream compressStream = new MemoryStream(data))
{
using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionMode.Decompress))
{
deflateStream.CopyTo(decompressedStream);
}
}
decompressedArray = decompressedStream.ToArray();
}
}
catch (Exception ex)
{
// do something !
}
return decompressedArray;
}
}
}