Расшифровка строки Z64 (ZB64)

Я работаю над разбивкой определений этикеток ZPL, созданных программным обеспечением для создания этикеток NiceLabel. По большей части мне не нужно беспокоиться о декодировании 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, по сути, та же проблема. Итак, я провел еще несколько исследований и нашел эту статью в блоге 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;
}

Это изменение кода больше не вызывает исключения, однако оно не распаковывается должным образом и возвращает следующий результат:

"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

2 ответа

Решение

Эти данные мне нравятся, я извлек из них вот такое изображение:

ht tps:https://stackru.com/images/d08814328a4323fdab2ecce00f677977fea185fd.png

Это монохромное растровое изображение, один бит на пиксель.

Ваш код не читался до конца потока, на самом деле есть 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;
    }
}

}

Другие вопросы по тегам