Skip to content

Instantly share code, notes, and snippets.

@trevarj
Last active February 26, 2023 03:25
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trevarj/1255e5cbc08fb3f79c3f255e25989a18 to your computer and use it in GitHub Desktop.
Save trevarj/1255e5cbc08fb3f79c3f255e25989a18 to your computer and use it in GitHub Desktop.
Convert Unicode to Image and Compress for ZPL - Zebra (^GF for ZPL)
/**
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
________________________________________________________________________
* The purpose of this is to take some unicode text
* and create an image out of it so that we can
* print it on an old Zebra label printer that
* doesn't have enough memory for unicode fonts.
*
* The string that is returned can be embedded directly into
* a ZPL script and sent to the printer.
*
* After many searches and scouring of manuals,
* and with the help of others, I finally got this working well.
*
* Hopefully this can help someone else and that they
* find it faster than it took me to piece it together.
*
* Obviously I'm leaving out all the imports and stuff.
*/
public String textToZ64CompressedImage(String input){
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g2d = img.createGraphics();
Font font = new Font(g2d.getFont().getName(), Font.PLAIN, 28);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = fm.stringWidth(input);
int height = fm.getHeight();
img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
g2d = img.createGraphics();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.WHITE);
g2d.setFont(font);
g2d.drawString(input, 0, fm.getAscent());
g2d.dispose();
// LZ77 compression
ByteArrayOutputStream compressedImage = new ByteArrayOutputStream();
DataBufferByte dataBufferByte = (DataBufferByte)img.getRaster().getDataBuffer();
DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(compressedImage);
try
{
deflaterOutputStream.write(dataBufferByte.getData(), 0, dataBufferByte.getData().length);
deflaterOutputStream.finish();
}
catch (IOException e)
{
LOG.debug("Couldn't compress image for printing.");
return "I/O error";
}
// without compression
// byte[] encodedBytesImage = Base64.getMimeEncoder().encode(dataBufferByte.getData());
byte[] encodedBytesImage = Base64.getMimeEncoder().encode(compressedImage.toByteArray());
String crcString = getCRCHexString(encodedBytesImage);
String bytesPerRow = String.valueOf(dataBufferByte.getData().length / height);
int binaryByteCount = (width * height) / 8;
String encodedAscii = "^GFA," + binaryByteCount + "," + binaryByteCount + "," + bytesPerRow + ",:Z64:" + new String(encodedBytesImage, Charsets.US_ASCII) + ":" + crcString;
try
{
compressedImage.close();
}
catch (IOException e)
{
LOG.error("Couldn't close image output stream.");
}
return encodedAscii;
}
/**
* Reads in a sequence of bytes and prints out its 16 bit
* Cylcic Redundancy Check (CRC-CCIIT 0xFFFF).
*
* 1 + x + x^5 + x^12 + x^16 is irreducible polynomial.
*
*/
private static String getCRCHexString(byte[] bytes)
{
int crc = 0x0000; // initial value
int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
for (byte b : bytes)
{
for (int i = 0; i < 8; i++)
{
boolean bit = ((b >> (7 - i) & 1) == 1);
boolean c15 = ((crc >> 15 & 1) == 1);
crc <<= 1;
if (c15 ^ bit)
{
crc ^= polynomial;
}
}
}
crc &= 0xffff;
return Integer.toHexString(crc);
}
@dv996coding
Copy link

Hello, Excuse me, how to print the complete picture data? I used your demo to test and found that the Bufferimage object is passed in alone, the printer cannot recognize the correct content to print

@trevarj
Copy link
Author

trevarj commented Aug 16, 2022

found that the Bufferimage object is passed in alone

I don't understand

the printer cannot recognize the correct content to print

Maybe your printer uses a different mechanism for printing images or the ZPL is incorrect

@dv996coding
Copy link

May I ask you a question? That is, I just want to use ZPL instructions to print pictures, how should I do it? I see your demo is to convert text to image for printing

@trevarj
Copy link
Author

trevarj commented Aug 16, 2022

@dv996coding I'm not sure, but my guess is that you can just create your own BufferedImage and then use the rest of the code (line 54 and after) to display the image.

@dv996coding
Copy link

I see that the width and height in your demo are for reading fonts, not pictures. When I used it for reference, I found that the picture could not be printed normally

@trevarj
Copy link
Author

trevarj commented Aug 16, 2022

Change the width and height. My options are for printing labels.

@dv996coding
Copy link

Thanks, I'm trying it out

@dv996coding
Copy link

dv996coding commented Aug 16, 2022

 public String image2Z64CompressedImage(BufferedImage img) {
        // LZ77 compression
        ByteArrayOutputStream compressedImage = new ByteArrayOutputStream();
        DataBufferByte dataBufferByte = (DataBufferByte) img.getRaster().getDataBuffer();
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(compressedImage);
        try {
            deflaterOutputStream.write(dataBufferByte.getData(), 0, dataBufferByte.getData().length);
            deflaterOutputStream.finish();
            deflaterOutputStream.close();
        } catch (IOException e) {
            LOG.debug("Couldn't compress image for printing. {}", e);
            return null;
        }
        int width = img.getWidth();
        int height = img.getHeight();
        byte[] encodedBytesImage = Base64.getMimeEncoder().encode(compressedImage.toByteArray());
        try {
            compressedImage.close();
        } catch (IOException e) {
            LOG.error("Couldn't close image output stream. {}", e);
            return null;
        }
        String crcString = getCRCHexString(encodedBytesImage);
        String bytesPerRow = String.valueOf(dataBufferByte.getData().length / height);
        int binaryByteCount = (width * height) / 8;
        String encodedAscii = "^GFA," + binaryByteCount + "," + binaryByteCount + "," + bytesPerRow + ",:Z64:" + new String(encodedBytesImage, Charsets.US_ASCII) + ":" + crcString;
        return encodedAscii;
    }

@dv996coding
Copy link

dv996coding commented Aug 16, 2022

Here is the code snippet, can you help me see it? After I do this, I can't print the picture normally

@trevarj
Copy link
Author

trevarj commented Aug 16, 2022

Sorry, I don't have access to a printer to test this. Also, have not worked with ZPL in many years. Consult the ZPL manual and your specific printer's user manual to see if it can in fact print images using the GFA tag. I think there are also tools from Zebra where you can generate test labels and images, so you may not need to do this programmatically in Java.

The purpose of this script is to print unicode symbols as an image, since some old printers don't have enough memory to store a unicode font.

@dv996coding
Copy link

OK, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment