Как преобразовать изображение в код zpl для печати на принтере Zebra?

Я хочу напечатать изображение вместе с некоторыми другими текстами через принтер Zebra из приложения Android. Я могу создать zpl-код для текстовых данных, но у меня возникла проблема с созданием zpl-кода для изображения. Zpl не поддерживает код base64. Изображение необходимо преобразовать в шестнадцатеричный формат ascii и распечатать с помощью команды ^GF.

Необработанные данные с текстом и изображением доступны на этом Pastebin link, и может быть просмотрен в режиме просмотра ярлыков.

Есть ли какой-либо процесс преобразования изображения?

1 ответ

Я решил проблему и выкладываю ответ для пользы других людей. Растровое изображение может быть преобразовано в код zpl с использованием следующего класса конвертера.

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-код можно проверить в программе просмотра ярлыков.

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;
    }
}

Отсюда был указан код конвертера и добавлена ​​поддержка использования 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 пикселей, конвертируете ее в шестнадцатеричное значение и затем в текстовое представление этих шестнадцатеричных чисел. Вот код C++, сделанный с Borland:

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;

Это мой первый пост за Stackru за много лет. Если вам нравится, пожалуйста, upvote!

:)

Некоторые ссылки: ZPL PDF doc (см. Стр. 191 для преобразования графики) https://www.zebra.com/content/dam/zebra/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf (при наличии ссылки не работает, попробуйте "zpl-zbi2-pm-en.pdf" в Google)

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
            }


    }
        }

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