Looking for C# example of ZPL: Binary B64 and compressed Z64 encoding

186 Views Asked by At

ZPL: Binary B64 and compressed Z64 encoding

While a comment here pointed me in the right direction I was unable to really figure out what I needed to do.

The ZPL command is ~DYd:f,b,x,t,w,data and I'm looking for an example that will take a bitmap file or in-memory bitmap and compress it and encode it to Z64 with the correct ZPL output.

While the link gave overall steps it's not clear to me for the t value (total bytes in file) and the w value (total number of bytes per row) for a GRF image should be. Are they the original values before compression/encoding?

Here is some code I know works that takes an in-memory bitmap and makes it a b64 GRF string. I feel like this maybe could be simplified by removing the for loop and just using a Base64 encode command but maybe I'm wrong about that. What I want really from this is a Z64 so that the data is smaller to send to the printer.

imgData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
width = (bmp.Width + 7) / 8;
pixels = new byte[width];
sb = new StringBuilder(width * bmp.Height * 2);
pointer = imgData.Scan0;

for (yPosition = 0; yPosition < bmp.Height; yPosition++)
{
    Marshal.Copy(pointer, pixels, 0, width);

    for (xPosition = 0; xPosition < width; xPosition++)
        _ = sb.AppendFormat(CultureInfo.InvariantCulture, "{0:X2}", (byte)~pixels[xPosition]);
    pointer = (IntPtr)(pointer.ToInt64() + imgData.Stride);
}

EDIT: Here is what I have as far as code for B64 and Z64 for the data that would go with the ZPL command ~DY and ~DG. This is untested as of yet but am working towards testing.

[SupportedOSPlatform("windows")]
private static string BitmapGRFCreate(Bitmap bitmap, out int totalBytes, out int rowWidth, out ushort? crcValue, bool useCompression = false)
{
    BitmapData bitmapData = null;
    string base64String = string.Empty;

    try
    {
        if (useCompression)
        {
            bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
            rowWidth = (bitmap.Width + 7) / 8;
            totalBytes = Math.Abs(bitmapData.Stride) * bitmap.Height;
            byte[] rgbValues = new byte[totalBytes];

            using MemoryStream compressorMemoryStream = new MemoryStream();
            using DeflateStream compressor = new DeflateStream(compressorMemoryStream, CompressionMode.Compress, leaveOpen: true);
            compressor.Write(rgbValues, 0, rgbValues.Length);
            base64String = Convert.ToBase64String(compressorMemoryStream.ToArray());
            byte[] bytes = Encoding.ASCII.GetBytes(base64String);
            Crc16Ccitt crc = new(Crc16Ccitt.InitialCrcValue.Zeros);
            crcValue = crc.ComputeChecksum(bytes);
        }
        else
        {
            bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
            rowWidth = (bitmap.Width + 7) / 8;
            totalBytes = Math.Abs(bitmapData.Stride) * bitmap.Height;
            byte[] rgbValues = new byte[totalBytes];
            base64String = Convert.ToBase64String(rgbValues);
            crcValue = null;
        }

        return base64String;
    }
    finally
    {
        if (bitmap != null)
        {
            if (bitmapData != null)
            {
                bitmap.UnlockBits(bitmapData);
            }

            bitmap.Dispose();
        }
    }
}
1

There are 1 best solutions below

0
JonasH On

This is the first I have heard about ZPL, so take this with some grain of salt.

Are they the original values before compression/encoding?

Presumably that would be before compression, since it makes little sense to talk about "bytes per row" in a compressed format.

While the link gave overall steps it's not clear to me for the t value (total bytes in file) and the w value (total number of bytes per row) for a GRF image should be

Presumably the total bytes in file should be stride * height and the total number of bytes per row should be the stride. The typical minimum amount of metadata for images are:

  • Width
  • Height
  • Stride (number of bytes per row)
  • PixelFormat (implicitly 1 bit per pixel in this case)

This should be enough to make sense of the raw pixel data. So I would expect most systems to have parameters for these. All should be available from the imgData object.

I feel like this maybe could be simplified by removing the for loop and just using a Base64 encode command but maybe I'm wrong about that

Yes. You should not need any manual loops at all. If you want compression I'm guessing you should be able to use the built in ZlibStream class. The code will likely be slightly convoluted since you will probably need to represent the data in multiple different ways. Pointer as the source, byte arrays as a intermediary, stream for compression, string after base64 encoding, to byte array again before CRC-calculation, etc. Use a MemoryStream as the backing stream for your compression stream and convert this to a byte array, after making sure the compression is flushed etc.