Unicode characters on ZPL printer

前端 未结 10 2033
既然无缘
既然无缘 2020-11-27 04:32

I have the task of re-designing a system to print shipping labels, using a networked Zebra GK420T. I have been able to send ZPL print jobs to it perfectly fine, but I cannot

相关标签:
10条回答
  • 2020-11-27 05:08

    When we use CP1251 as system encoding it causes empty symbols in labels, if we write Cyrillic in ZPL code. CP1251 users could first force convert "До свидания" to UTF-8 and get:

    До свидания

    Replace До свидания in ZPL code with these strange symbols and get "До свидания" on the label. It works with tt0003m_.fnt with ^CI28, but IMHO it is better to use hex-codes instead.

    0 讨论(0)
  • 2020-11-27 05:09

    I just discovered that you need to escape characters above ASCII by first putting ^FH before any ^FD fields that could contain a utf character and you need to prefix the utf-8 hex code with an underscore

    _D0_94 will print as Д. My final ZPL code is as follows:

    ^XA
    ^LH100,150
    ^CWT,E:TT0003M_.FNT
    ^CFT,30,30
    ^CI28
    ^FT0,0^FH^FDTesting 1 2 3^FS
    ^FT0,50^FH^FD_D0_94_D0_BE _D1_81_D0_B2_D0_B8_D0_B4_D0_B0_D0_BD_D0_B8_D1_8F^FS
    ^FT0,100^B3^FDAAA001^FS
    ^XZ
    

    I'm just going to have to make a way to generate the escape sequences, which should be much easier!

    0 讨论(0)
  • 2020-11-27 05:10

    I'm using Zebra ZM400 printer and use TT0003M_ font.

    this font does not print kazakh cyrillic.

    if you want to print cryillic + kazakh cyrillic + latin alphabet, use ARI000.FNT (arial font)

    I'm using the following method convert char to hex code

    I hope this helps

    stringConverTextToHex(stringtext)
    {
    stringnewText="";
    char[]charArray=text.ToCharArray();
    foreach(charcincharArray)
    {
    switch(c)
    {
    case'й':
    newText+="_D0_B9";
    break;
    case'Й':
    newText+="_D0_99";
    break;
    case'ц':
    newText+="_D1_86";
    break;
    case'Ц':
    newText+="_D0_A6";
    break;
    case'у':
    newText+="_D1_83";
    break;
    case'У':
    newText+="_D0_A3";
    break;
    case'к':
    newText+="_D0_BA";
    break;
    case'К':
    newText+="_D0_9A";
    break;
    case'е':
    newText+="_D0_B5";
    break;
    case'Е':
    newText+="_D0_95";
    break;
    case'н':
    newText+="_D0_BD";
    break;
    case'Н':
    newText+="_D0_9D";
    break;
    case'г':
    newText+="_D0_B3";
    break;
    case'Г':
    newText+="_D0_93";
    break;
    case'ш':
    newText+="_D1_88";
    break;
    case'Ш':
    newText+="_D0_A8";
    break;
    case'щ':
    newText+="_D1_89";
    break;
    case'Щ':
    newText+="_D0_A9";
    break;
    case'з':
    newText+="_D0_B7";
    break;
    case'З':
    newText+="_D0_97";
    break;
    case'х':
    newText+="_D1_85";
    break;
    case'Х':
    newText+="_D0_A5";
    break;
    case'ъ':
    newText+="_D1_8A";
    break;
    case'Ъ':
    newText+="_D0_AA";
    break;
    case'ф':
    newText+="_D1_84";
    break;
    case'Ф':
    newText+="_D0_A4";
    break;
    case'ы':
    newText+="_D1_8B";
    break;
    case'Ы':
    newText+="_D0_AB";
    break;
    case'в':
    newText+="_D0_B2";
    break;
    case'В':
    newText+="_D0_92";
    break;
    case'а':
    newText+="_D0_B0";
    break;
    case'А':
    newText+="_D0_90";
    break;
    case'п':
    newText+="_D0_BF";
    break;
    case'П':
    newText+="_D0_9F";
    break;
    case'р':
    newText+="_D1_80";
    break;
    case'Р':
    newText+="_D0_A0";
    break;
    case'о':
    newText+="_D0_BE";
    break;
    case'О':
    newText+="_D0_9E";
    break;
    case'л':
    newText+="_D0_BB";
    break;
    case'Л':
    newText+="_D0_9B";
    break;
    case'д':
    newText+="_D0_B4";
    break;
    case'Д':
    newText+="_D0_94";
    break;
    case'ж':
    newText+="_D0_B6";
    break;
    case'Ж':
    newText+="_D0_96";
    break;
    case'э':
    newText+="_D1_8D";
    break;
    case'Э':
    newText+="_D0_AD";
    break;
    case'я':
    newText+="_D1_8F";
    break;
    case'Я':
    newText+="_D0_AF";
    break;
    case'ч':
    newText+="_D1_87";
    break;
    case'Ч':
    newText+="_D0_A7";
    break;
    case'с':
    newText+="_D1_81";
    break;
    case'С':
    newText+="_D0_A1";
    break;
    case'м':
    newText+="_D0_BC";
    break;
    case'М':
    newText+="_D0_9C";
    break;
    case'и':
    newText+="_D0_B8";
    break;
    case'И':
    newText+="_D0_98";
    break;
    case'т':
    newText+="_D1_82";
    break;
    case'Т':
    newText+="_D0_A2";
    break;
    case'ь':
    newText+="_D1_8C";
    break;
    case'Ь':
    newText+="_D0_AC";
    break;
    case'б':
    newText+="_D0_B1";
    break;
    case'Б':
    newText+="_D0_91";
    break;
    case'ю':
    newText+="_D1_8E";
    break;
    case'Ю':
    newText+="_D0_AE";
    break;
    case'ӑ':
    newText+="_D3_91";
    break;
    case'Ӑ':
    newText+="_D3_90";
    break;
    case'ӓ':
    newText+="_D3_93";
    break;
    case'Ӓ':
    newText+="_D3_92";
    break;
    case'ә':
    newText+="_D3_99";
    break;
    case'Ә':
    newText+="_D3_98";
    break;
    case'ӛ':
    newText+="_D3_9B";
    break;
    case'Ӛ':
    newText+="_D3_9A";
    break;
    case'ӕ':
    newText+="_D3_95";
    break;
    case'Ӕ':
    newText+="_D3_94";
    break;
    case'ґ':
    newText+="_D2_91";
    break;
    case'Ґ':
    newText+="_D2_90";
    break;
    case'ѓ':
    newText+="_D1_93";
    break;
    case'Ѓ':
    newText+="_D0_83";
    break;
    case'ғ':
    newText+="_D2_93";
    break;
    case'Ғ':
    newText+="_D2_92";
    break;
    case'ӷ':
    newText+="_D3_B7";
    break;
    case'Ӷ':
    newText+="_D3_B6";
    break;
    case'ҕ':
    newText+="_D2_95";
    break;
    case'Ҕ':
    newText+="_D2_94";
    break;
    case'ђ':
    newText+="_D1_92";
    break;
    case'Ђ':
    newText+="_D0_82";
    break;
    case'ѐ':
    newText+="_D1_90";
    break;
    case'Ѐ':
    newText+="_D0_80";
    break;
    case'ӗ':
    newText+="_D3_97";
    break;
    case'Ӗ':
    newText+="_D3_96";
    break;
    case'ҽ':
    newText+="_D2_BD";
    break;
    case'Ҽ':
    newText+="_D2_BC";
    break;
    case'ҿ':
    newText+="_D2_BF";
    break;
    case'Ҿ':
    newText+="_D2_BE";
    break;
    case'є':
    newText+="_D1_94";
    break;
    case'Є':
    newText+="_D0_84";
    break;
    case'ӂ':
    newText+="_D3_82";
    break;
    case'Ӂ':
    newText+="_D3_81";
    break;
    case'җ':
    newText+="_D2_97";
    break;
    case'Җ':
    newText+="_D2_96";
    break;
    case'ӝ':
    newText+="_D3_9D";
    break;
    case'Ӝ':
    newText+="_D3_9C";
    break;
    case'ҙ':
    newText+="_D2_99";
    break;
    case'Ҙ':
    newText+="_D2_98";
    break;
    case'ӟ':
    newText+="_D3_9F";
    break;
    case'Ӟ':
    newText+="_D3_9E";
    break;
    case'ӡ':
    newText+="_D3_A1";
    break;
    case'Ӡ':
    newText+="_D3_A0";
    break;
    case'ѕ':
    newText+="_D1_95";
    break;
    case'Ѕ':
    newText+="_D0_85";
    break;
    case'ѝ':
    newText+="_D1_9D";
    break;
    case'Ѝ':
    newText+="_D0_8D";
    break;
    case'ӥ':
    newText+="_D3_A5";
    break;
    case'Ӥ':
    newText+="_D3_A4";
    break;
    case'ӣ':
    newText+="_D3_A3";
    break;
    case'Ӣ':
    newText+="_D3_A2";
    break;
    case'і':
    newText+="_D1_96";
    break;
    case'І':
    newText+="_D0_86";
    break;
    case'ї':
    newText+="_D1_97";
    break;
    case'Ї':
    newText+="_D0_87";
    break;
    case'Ӏ':
    newText+="_D3_80";
    break;
    case'ҋ':
    newText+="_D2_8B";
    break;
    case'Ҋ':
    newText+="_D2_8A";
    break;
    case'ј':
    newText+="_D1_98";
    break;
    case'Ј':
    newText+="_D0_88";
    break;
    case'қ':
    newText+="_D2_9B";
    break;
    case'Қ':
    newText+="_D2_9A";
    break;
    case'ҟ':
    newText+="_D2_9F";
    break;
    case'Ҟ':
    newText+="_D2_9E";
    break;
    case'ҡ':
    newText+="_D2_A1";
    break;
    case'Ҡ':
    newText+="_D2_A0";
    break;
    case'ӄ':
    newText+="_D3_84";
    break;
    case'Ӄ':
    newText+="_D3_83";
    break;
    case'ҝ':
    newText+="_D2_9D";
    break;
    case'Ҝ':
    newText+="_D2_9C";
    break;
    case'ӆ':
    newText+="_D3_86";
    break;
    case'Ӆ':
    newText+="_D3_85";
    break;
    case'љ':
    newText+="_D1_99";
    break;
    case'Љ':
    newText+="_D0_89";
    break;
    case'ӎ':
    newText+="_D3_8E";
    break;
    case'Ӎ':
    newText+="_D3_8D";
    break;
    case'ӊ':
    newText+="_D3_8A";
    break;
    case'Ӊ':
    newText+="_D3_89";
    break;
    case'ң':
    newText+="_D2_A3";
    break;
    case'Ң':
    newText+="_D2_A2";
    break;
    case'ӈ':
    newText+="_D3_88";
    break;
    case'Ӈ':
    newText+="_D3_87";
    break;
    case'ҥ':
    newText+="_D2_A5";
    break;
    case'Ҥ':
    newText+="_D2_A4";
    break;
    case'њ':
    newText+="_D1_9A";
    break;
    case'Њ':
    newText+="_D0_8A";
    break;
    case'ӧ':
    newText+="_D3_A7";
    break;
    case'Ӧ':
    newText+="_D3_A6";
    break;
    case'ө':
    newText+="_D3_A9";
    break;
    case'Ө':
    newText+="_D3_A8";
    break;
    case'ӫ':
    newText+="_D3_AB";
    break;
    case'Ӫ':
    newText+="_D3_AA";
    break;
    case'ҩ':
    newText+="_D2_A9";
    break;
    case'Ҩ':
    newText+="_D2_A8";
    break;
    case'ҧ':
    newText+="_D2_A7";
    break;
    case'Ҧ':
    newText+="_D2_A6";
    break;
    case'ҏ':
    newText+="_D2_8F";
    break;
    case'Ҏ':
    newText+="_D2_8E";
    break;
    case'ҫ':
    newText+="_D2_AB";
    break;
    case'Ҫ':
    newText+="_D2_AA";
    break;
    case'ҭ':
    newText+="_D2_AD";
    break;
    case'Ҭ':
    newText+="_D2_AC";
    break;
    case'ћ':
    newText+="_D1_9B";
    break;
    case'Ћ':
    newText+="_D0_8B";
    break;
    case'ќ':
    newText+="_D1_9C";
    break;
    case'Ќ':
    newText+="_D0_8C";
    break;
    case'ў':
    newText+="_D1_9E";
    break;
    case'Ў':
    newText+="_D0_8E";
    break;
    case'ӳ':
    newText+="_D3_B3";
    break;
    case'Ӳ':
    newText+="_D3_B2";
    break;
    case'ӱ':
    newText+="_D3_B1";
    break;
    case'Ӱ':
    newText+="_D3_B0";
    break;
    case'ӯ':
    newText+="_D3_AF";
    break;
    case'Ӯ':
    newText+="_D3_AE";
    break;
    case'ү':
    newText+="_D2_AF";
    break;
    case'Ү':
    newText+="_D2_AE";
    break;
    case'ұ':
    newText+="_D2_B1";
    break;
    case'Ұ':
    newText+="_D2_B0";
    break;
    case'ҳ':
    newText+="_D2_B3";
    break;
    case'Ҳ':
    newText+="_D2_B2";
    break;
    case'һ':
    newText+="_D2_BB";
    break;
    case'Һ':
    newText+="_D2_BA";
    break;
    case'ҵ':
    newText+="_D2_B5";
    break;
    case'Ҵ':
    newText+="_D2_B4";
    break;
    case'ӵ':
    newText+="_D3_B5";
    break;
    case'Ӵ':
    newText+="_D3_B4";
    break;
    case'ҷ':
    newText+="_D2_B7";
    break;
    case'Ҷ':
    newText+="_D2_B6";
    break;
    case'ӌ':
    newText+="_D3_8C";
    break;
    case'Ӌ':
    newText+="_D3_8B";
    break;
    case'ҹ':
    newText+="_D2_B9";
    break;
    case'Ҹ':
    newText+="_D2_B8";
    break;
    case'џ':
    newText+="_D1_9F";
    break;
    case'Џ':
    newText+="_D0_8F";
    break;
    case'ӹ':
    newText+="_D3_B9";
    break;
    case'Ӹ':
    newText+="_D3_B8";
    break;
    case'ҍ':
    newText+="_D2_8D";
    break;
    case'Ҍ':
    newText+="_D2_8C";
    break;
    case'ӭ':
    newText+="_D3_AD";
    break;
    case'Ӭ':
    newText+="_D3_AC";
    break;
    case'A':
    newText+="_41";
    break;
    case'a':
    newText+="_61";
    break;
    case'B':
    newText+="_42";
    break;
    case'b':
    newText+="_62";
    break;
    case'C':
    newText+="_43";
    break;
    case'c':
    newText+="_63";
    break;
    case'D':
    newText+="_44";
    break;
    case'd':
    newText+="_64";
    break;
    case'E':
    newText+="_45";
    break;
    case'e':
    newText+="_65";
    break;
    case'F':
    newText+="_46";
    break;
    case'f':
    newText+="_66";
    break;
    case'G':
    newText+="_47";
    break;
    case'g':
    newText+="_67";
    break;
    case'H':
    newText+="_48";
    break;
    case'h':
    newText+="_68";
    break;
    case'I':
    newText+="_49";
    break;
    case'i':
    newText+="_69";
    break;
    case'J':
    newText+="_4A";
    break;
    case'j':
    newText+="_6A";
    break;
    case'K':
    newText+="_4B";
    break;
    case'k':
    newText+="_6B";
    break;
    case'L':
    newText+="_4C";
    break;
    case'l':
    newText+="_6C";
    break;
    case'M':
    newText+="_4D";
    break;
    case'm':
    newText+="_6D";
    break;
    case'N':
    newText+="_4E";
    break;
    case'n':
    newText+="_6E";
    break;
    case'O':
    newText+="_4F";
    break;
    case'o':
    newText+="_6F";
    break;
    case'P':
    newText+="_50";
    break;
    case'p':
    newText+="_70";
    break;
    case'R':
    newText+="_52";
    break;
    case'r':
    newText+="_72";
    break;
    case'S':
    newText+="_53";
    break;
    case's':
    newText+="_73";
    break;
    case'T':
    newText+="_54";
    break;
    case't':
    newText+="_74";
    break;
    case'U':
    newText+="_55";
    break;
    case'u':
    newText+="_75";
    break;
    case'V':
    newText+="_56";
    break;
    case'v':
    newText+="_76";
    break;
    case'Y':
    newText+="_59";
    break;
    case'y':
    newText+="_79";
    break;
    case'Z':
    newText+="_5A";
    break;
    case'z':
    newText+="_7A";
    break;
    case'':
    newText+="";
    break;
    default:
    newText+=c;
    break;
    }
    }
    returnnewText;
    }
    

    this is the sample code


    ^SP ^XA ^PON^FS ^FPH^FO102,63,0 ^A@N,60,60,E:ARIOOO_.FNT ^FH^FD_42_75_72_61_6B _D0_A8_D3_99 ^FS ^XZ


    0 讨论(0)
  • 2020-11-27 05:18

    As others have noted, make sure to use ^CI28 (Change International Font/Encoding) and ^FH (Field Hexadecimal Indicator) and escape any non-ascii utf8 characters with an underscore and their hex value.

    However another answer included code to format the utf8 string using a gigantic switch-case block. Here is the method I use for encoding to utf8, it should be able to format any valid utf8 byte array.

    To get the byte array from a string use Encoding.UTF8.GetBytes(content).

    // From the wikipedia page on utf8 encoding - https://en.wikipedia.org/wiki/UTF-8
        private const int _Last1ByteCodePointByte1 = 0x7F;
        private const int _First2ByteCodePointByte1 = 0xC0;
        private const int _Last2ByteCodePointByte1 = 0xDF;
        private const int _Last3ByteCodePointByte1 = 0xEF;
        private const int _Last4ByteCodePointByte1 = 0xF7;
        private const int _FirstMultiByteCodePointByte2 = 0x80;
        private const int _LastMultiByteCodePointByte2 = 0xBF;
        private const char _ZplMultiByteEscapeCharacter = '_';
    
    /// <summary>
    /// Encodes a sequence of utf8 bytes for printing with the ZPL language, this means escaping multi-byte characters with an underscore ('_') followed by the hex code
    /// for each byte in the multi-byte characters.
    /// </summary>
    /// <param name="utf8Bytes">The bytes that make up the entire string, including bytes that need to be encoded and bytes that can be printed as-is.</param>
    /// <returns>A string for printing with the ZPL language. Ie all multi-byte characters escaped with an underscore ('_') followed by the hex code for each byte.</returns>
    /// <throws><see cref="ArgumentException"/> when <paramref name="utf8Bytes"/> isn't a valid utf8 encoding of a string.</throws>
    /// <remarks>
    /// Plan is to figure out how many bytes this character (code point) takes up, and if it's a 1 byte character, just use the character, but otherwise since it's a multi-byte 
    /// character then use an underscore ('_') followed by the hex encoded byte and each other byte in this code point will also be encoded. If we start the loop but have bytes 
    /// remaining in the current code point we know to hex encode this byte and continue.
    /// </remarks>
    private static string EncodeUtf8BytesForZPLIIPrinting(byte[] utf8Bytes)
    {
        var contentWithMultiByteCharsEscaped = new List<char>();
    
        var multiByteCodePoint = new List<char>();
        var remainingBytesInCurrentCodePoint = 0;
        string errorMessage = null;
    
        foreach (byte utf8Byte in utf8Bytes)
        {
            if (remainingBytesInCurrentCodePoint > 0)
            {
                if (utf8Byte < _FirstMultiByteCodePointByte2 || utf8Byte > _LastMultiByteCodePointByte2)
                {
                    errorMessage = $"The byte {utf8Byte.ToString("X2")} is not a valid as the second or later byte of a multi-byte utf8 character (codepoint).";
                    break;
                }
    
                multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
                AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
                remainingBytesInCurrentCodePoint--;
                continue; // continue since we've dealt with this byte and don't want to flow on.
            }
    
            if (multiByteCodePoint.Any())
            {
                foreach (char c in multiByteCodePoint) contentWithMultiByteCharsEscaped.Add(c);
                multiByteCodePoint.Clear();
                // flow on to loop to see what to do with the current byte.
            }
    
            if (utf8Byte <= _Last1ByteCodePointByte1)
            {
                // 1 byte - no escaping
                contentWithMultiByteCharsEscaped.Add((char)utf8Byte);
            }
            else if (utf8Byte >= _First2ByteCodePointByte1 && utf8Byte <= _Last2ByteCodePointByte1)
            {
                // 2 bytes
                multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
                AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
                remainingBytesInCurrentCodePoint = 1;
            }
            else if (utf8Byte <= _Last3ByteCodePointByte1)
            {
                // 3 bytes
                multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
                AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
                remainingBytesInCurrentCodePoint = 2;
            }
            else if (utf8Byte <= _Last4ByteCodePointByte1)
            {
                // 4 bytes
                multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
                AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
                remainingBytesInCurrentCodePoint = 3;
            }
            else
            {
                errorMessage = $"The byte {utf8Byte.ToString("X2")} is not a valid as the first byte of a utf8 character.";
                break;
            }
        }
    
        // if the last char was multiByte add it now.
        if (multiByteCodePoint.Any())
        {
            foreach (var c in multiByteCodePoint) contentWithMultiByteCharsEscaped.Add(c);
            multiByteCodePoint.Clear();
        }
    
        if (remainingBytesInCurrentCodePoint != 0 && errorMessage == null)
        {
            errorMessage = $"The last character didn't have enough bytes to finish the codepoint. It was a multi-byte character that needed {remainingBytesInCurrentCodePoint}" + 
                $" more byte{(remainingBytesInCurrentCodePoint == 1 ? null : "s")}.";
        }
    
        if (errorMessage != null)
        {
            throw new ArgumentException($"The byte array was not a valid byte array for a utf8 string: {errorMessage}", nameof(utf8Bytes));
        }
    
        return new string(contentWithMultiByteCharsEscaped.ToArray());
    
    
        void AddHexValuesToListFromByte(List<char> list, byte @byte)
        {
            // A byte is <= 255 so will always fit in a 2-digit hex number, hence the 2 in "X2". The X means hex.
            foreach (char c in @byte.ToString("X2"))
            {
                list.Add(c);
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题