如何将图像转换为 zpl 代码以便使用 zebra 打印机进行打印?

How to convert image to zpl code for printing using zebra printer?

我想通过 Zebra 打印机从 android 应用程序打印图像和其他一些文本。我能够为文本数据创建 zpl 代码,但我无法为图像创建 zpl 代码。 zpl 不支持 base64 代码。图像需要转换为十六进制 ascii 并使用 ^GF 命令打印。

包含文本和图像的原始数据可在 this Pastebin link, and can be viewed in labelary viewer 上获得。

是否有图像转换过程?

我解决了问题并发布了答案,以便其他人可以从解决方案中受益。可以使用以下转换器将位图图像转换为 zpl 代码 class.

public class ZPLConverter {
    private int blackLimit = 380;
    private int total;
    private int widthBytes;
    private boolean compressHex = false;
    private static Map<Integer, String> mapCode = new HashMap<Integer, String>();

    {
        mapCode.put(1, "G");
        mapCode.put(2, "H");
        mapCode.put(3, "I");
        mapCode.put(4, "J");
        mapCode.put(5, "K");
        mapCode.put(6, "L");
        mapCode.put(7, "M");
        mapCode.put(8, "N");
        mapCode.put(9, "O");
        mapCode.put(10, "P");
        mapCode.put(11, "Q");
        mapCode.put(12, "R");
        mapCode.put(13, "S");
        mapCode.put(14, "T");
        mapCode.put(15, "U");
        mapCode.put(16, "V");
        mapCode.put(17, "W");
        mapCode.put(18, "X");
        mapCode.put(19, "Y");
        mapCode.put(20, "g");
        mapCode.put(40, "h");
        mapCode.put(60, "i");
        mapCode.put(80, "j");
        mapCode.put(100, "k");
        mapCode.put(120, "l");
        mapCode.put(140, "m");
        mapCode.put(160, "n");
        mapCode.put(180, "o");
        mapCode.put(200, "p");
        mapCode.put(220, "q");
        mapCode.put(240, "r");
        mapCode.put(260, "s");
        mapCode.put(280, "t");
        mapCode.put(300, "u");
        mapCode.put(320, "v");
        mapCode.put(340, "w");
        mapCode.put(360, "x");
        mapCode.put(380, "y");
        mapCode.put(400, "z");
    }

    public String convertFromImage(Bitmap image, Boolean addHeaderFooter) {
        String hexAscii = createBody(image);
        if (compressHex) {
            hexAscii = encodeHexAscii(hexAscii);
        }

        String zplCode = "^GFA," + total + "," + total + "," + widthBytes + ", " + hexAscii;

        if (addHeaderFooter) {
            String header = "^XA " + "^FO0,0^GFA," + total + "," + total + "," + widthBytes + ", ";
            String footer = "^FS" + "^XZ";
            zplCode = header + zplCode + footer;
        }
        return zplCode;
    }

    private String createBody(Bitmap bitmapImage) {
        StringBuilder sb = new StringBuilder();
        int height = bitmapImage.getHeight();
        int width = bitmapImage.getWidth();
        int rgb, red, green, blue, index = 0;
        char auxBinaryChar[] = {'0', '0', '0', '0', '0', '0', '0', '0'};
        widthBytes = width / 8;
        if (width % 8 > 0) {
            widthBytes = (((int) (width / 8)) + 1);
        } else {
            widthBytes = width / 8;
        }
        this.total = widthBytes * height;
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                rgb = bitmapImage.getPixel(w, h);
                red = (rgb >> 16) & 0x000000FF;
                green = (rgb >> 8) & 0x000000FF;
                blue = (rgb) & 0x000000FF;
                char auxChar = '1';
                int totalColor = red + green + blue;
                if (totalColor > blackLimit) {
                    auxChar = '0';
                }
                auxBinaryChar[index] = auxChar;
                index++;
                if (index == 8 || w == (width - 1)) {
                    sb.append(fourByteBinary(new String(auxBinaryChar)));
                    auxBinaryChar = new char[]{'0', '0', '0', '0', '0', '0', '0', '0'};
                    index = 0;
                }
            }
            sb.append("\n");
        }
        return sb.toString();
    }

    private String fourByteBinary(String binaryStr) {
        int decimal = Integer.parseInt(binaryStr, 2);
        if (decimal > 15) {
            return Integer.toString(decimal, 16).toUpperCase();
        } else {
            return "0" + Integer.toString(decimal, 16).toUpperCase();
        }
    }

    private String encodeHexAscii(String code) {
        int maxlinea = widthBytes * 2;
        StringBuilder sbCode = new StringBuilder();
        StringBuilder sbLinea = new StringBuilder();
        String previousLine = null;
        int counter = 1;
        char aux = code.charAt(0);
        boolean firstChar = false;
        for (int i = 1; i < code.length(); i++) {
            if (firstChar) {
                aux = code.charAt(i);
                firstChar = false;
                continue;
            }
            if (code.charAt(i) == '\n') {
                if (counter >= maxlinea && aux == '0') {
                    sbLinea.append(",");
                } else if (counter >= maxlinea && aux == 'F') {
                    sbLinea.append("!");
                } else if (counter > 20) {
                    int multi20 = (counter / 20) * 20;
                    int resto20 = (counter % 20);
                    sbLinea.append(mapCode.get(multi20));
                    if (resto20 != 0) {
                        sbLinea.append(mapCode.get(resto20)).append(aux);
                    } else {
                        sbLinea.append(aux);
                    }
                } else {
                    sbLinea.append(mapCode.get(counter)).append(aux);
                }
                counter = 1;
                firstChar = true;
                if (sbLinea.toString().equals(previousLine)) {
                    sbCode.append(":");
                } else {
                    sbCode.append(sbLinea.toString());
                }
                previousLine = sbLinea.toString();
                sbLinea.setLength(0);
                continue;
            }
            if (aux == code.charAt(i)) {
                counter++;
            } else {
                if (counter > 20) {
                    int multi20 = (counter / 20) * 20;
                    int resto20 = (counter % 20);
                    sbLinea.append(mapCode.get(multi20));
                    if (resto20 != 0) {
                        sbLinea.append(mapCode.get(resto20)).append(aux);
                    } else {
                        sbLinea.append(aux);
                    }
                } else {
                    sbLinea.append(mapCode.get(counter)).append(aux);
                }
                counter = 1;
                aux = code.charAt(i);
            }
        }
        return sbCode.toString();
    }

    public void setCompressHex(boolean compressHex) {
        this.compressHex = compressHex;
    }

    public void setBlacknessLimitPercentage(int percentage) {
        blackLimit = (percentage * 768 / 100);
    }

   }

用法示例: 您需要将图像转换为位图,转换为单色图像并进行十六进制 acii 转换。生成的 zpl 代码可以在 labelary viewer.

查看
public class Utils {

    public static String getZplCode(Bitmap bitmap, Boolean addHeaderFooter) {
        ZPLConverter zp = new ZPLConverter();
        zp.setCompressHex(true);
        zp.setBlacknessLimitPercentage(50);
        Bitmap grayBitmap = toGrayScale(bitmap);
        return zp.convertFromImage(grayBitmap, addHeaderFooter);
    }

    public static Bitmap toGrayScale(Bitmap bmpOriginal) {
        int width, height;
        height = bmpOriginal.getHeight();
        width = bmpOriginal.getWidth();

        Bitmap grayScale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(grayScale);
        Paint paint = new Paint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(bmpOriginal, 0, 0, paint);
        return grayScale;
    }
}

转换器代码已从 here 中引用并添加了对 android 使用的支持。

我知道这是板上钉钉的事情,但我仍然对当前的答案感到困惑。我想把我的经验分享给可能需要的人。

首先,^GFA代表像素的十六进制表示,但必须将其转换为可读文本(ASCII)。这是一个例子: 像素白色 = 1,黑色 = 1

1011 0100 转化为0xB4

在^GFA的数据部分,需要有B4作为数据。

如果我们选择

像素线 1:1011 0100 1001 1100 = 0xBA 0x9C 像素行 2:0011 0110 0001 1111 = 0x36 0x1F

生成的 ZPL 代码将是:

^XA(需要启动 ZPL 文件)

^F10,0(水平偏移 10 像素,垂直偏移 0 像素)

^GFA,4,4,2,BA9C361F(4为总字节数,2为每行字节数)

^F0^XZ(文件结尾)

现在,有趣的一点。如何获得代码:

您需要一张灰度位图。您需要以像素方式访问位图。换句话说,一个包含整数的数组,其值从 0 到 255 不等。

使用该数组,您可以获取每组 8 个像素,将其转换为十六进制值,然后转换为这些十六进制的文本表示形式。这是用 Borland 制作的 c++ 代码:

Graphics::TBitmap *imageFax = new Graphics::TBitmap();

unsigned char r;
unsigned char b;
ofstream outFile;
char listeHex[16];
int lineByteWidth;
int j;
int bytesCount = 0;
int widthHeight;
AnsiString testOut;

listeHex[0] = '0';
listeHex[1] = '1';
listeHex[2] = '2';
listeHex[3] = '3';
listeHex[4] = '4';
listeHex[5] = '5';
listeHex[6] = '6';
listeHex[7] = '7';
listeHex[8] = '8';
listeHex[9] = '9';
listeHex[10] = 'A';
listeHex[11] = 'B';
listeHex[12] = 'C';
listeHex[13] = 'D';
listeHex[14] = 'E';
listeHex[15] = 'F';

imageFax->Monochrome = true;
imageFax->PixelFormat = pf8bit;

imageFax->LoadFromFile("c:/testEtiquette/test.bmp"); //1200x300pixels bitmap test image

testOut = "c:/testEtiquette/outputfile.txt";

outFile.open(testOut.c_str());

imageFax->PixelFormat = pf8bit;


lineByteWidth = imageFax->Width/8;//Number of byte per line
widthHeight = lineByteWidth*imageFax->Height;//number of total byte to be written into the output file


testOut = "^XA^FO10,0^GFA,";
outFile << testOut.c_str() << widthHeight << ',' << widthHeight << ',' << lineByteWidth << ',' ;
for(int i = 0; i < imageFax->Height; i++)
{
     unsigned char * pixel = (unsigned char *)imageFax->ScanLine[i];
     bytesCount = 0;
     b=0x00;
     for(j = 0; j < imageFax->Width; j++)
     {
        //Here is the "switch" : what is not white (255) bit = 0, is black bit = 1.
        //You can set your switch at whatever value you think is best. 0, 255 or anything between.
        //I think 255 (white) is a good for my application
        if(pixel[j] != 255)
        {
            b = b<<1;
            //It is not white (hence black), we force value 1 into current position
            b = b|0x01;
        }
        else
        {
            //Since it white, we move 1 bit to the left, pushing 0 into current position
            b = b<<1;
            b = b&0xFE;//Forcing a 0 in the current position
        }

        //If we've got a full byte (8-bits), we write it into the file
        //This will lead into cutting off part of images that width is not a multiple of 8
        if(j%8 == 7)
        {
            bytesCount++;

            r = b;
            r = r&0xF0; //Cleaning last digits
            r=r>>4; //Moving the bits to the left 0xF0 => 0x0F
            outFile << listeHex[r%16]; //Reaching into the conversion array listeHex, ASCII representation of hex value
            r = listeHex[r%16]; //For debug only

            r = b;
            r = r&0x0F;//Cleaning first digits
            outFile << listeHex[r%16];//Reaching into the conversion array listeHex, ASCII representation of hex value
            r = listeHex[r%16]; //For debug only

            b = 0x00; //Reseting for next Byte
        }
     }
}
testOut = "^F0^XZ";
outFile << testOut.c_str();
outFile.close();
delete imageFax;

一些 links : ZPL PDF 文档(图形转换见第 191 页)https://www.zebra.com/content/dam/zebra/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf (如果 link 不起作用,请在 google 上尝试 "zpl-zbi2-pm-en.pdf")

https://www.rapidtables.com/convert/number/binary-to-hex.html

这是 IP 打印机的完整工作代码(型号 GK420t ZPL,您可以访问任何 IP 打印机)。只替换三个东西1)加你IP地址2)添加你的端口号3)添加你的PNG文件路径

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using System.Net.Http;
using System.ServiceModel.Channels;
using System.Web;
using System.Web.Http;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Printing;
using System.Net.NetworkInformation;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Printing;





namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {     
            string ipAddress = "Your IP address";
            int port = Your port number;

            string zplImageData = string.Empty;
            string filePath = @"your png file path";
            byte[] binaryData = System.IO.File.ReadAllBytes(filePath);
            foreach (Byte b in binaryData)
            {
                string hexRep = String.Format("{0:X}", b);
                if (hexRep.Length == 1)
                    hexRep = "0" + hexRep;
                zplImageData += hexRep;
            }
            string zplToSend = "^XA" + "^FO50" + "50^GFA,120000,120000,100" + binaryData.Length + ",," + zplImageData + "^XZ";
            string printImage = "^XA^FO115,50^IME:LOGO.PNG^FS^XZ";

            try
            {
                // Open connection
                System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient();
                client.Connect(ipAddress, port);

                // Write ZPL String to connection
                System.IO.StreamWriter writer = new System.IO.StreamWriter(client.GetStream(), Encoding.UTF8);
                writer.Write(zplToSend);
                writer.Flush();
                writer.Write(printImage);
                writer.Flush();
                // Close Connection
                writer.Close();
                client.Close();
            }
            catch (Exception ex)
            {
                // Catch Exception
            }


    }
        }

    }