Всем привет.
На текущем месте работы, в один момент, мне пришлось экстренно разобраться с печатью этикеток на принтеры Zebra. Так как информации в сети(особенно на русском) оказалось не так много, я как добросовестный программист обратился к документации. В этой статье хотелось бы сделать выжимку, для таких же счастливчиков как я, о том, что такое язык ZPL, как с ним работать и с чем я столкнулся в процессе своих познаний.
Как же всё работает
Если вкратце, то принтер Zebra и ему подобные, эмулирующие в себе ZPL, принимают на вход специальную строку с ПК, обрабатывают её и выводят на печать. Всё просто. Но вот как эту самую строку корректно сформировать, чтобы принтер всё понял?
Пример этикетки с QR-кодом
^XA
^FO50,80^A0,30,50 ^FDSamsung Galaxy S22^FS
^FO50,130^A0,30,50 ^FDSM-S908BDRHSKZ^FS
^FO50,180^A0,30,50 ^FDExynos 2200^FS
^FO50,230^A0,30,50 ^FDXclipse 920^FS
^FX Для примера этикетки взял информацию о смартфоне.
^FO490,30 ^BY5,2.0,50 ^BQN,2,6 ^FD Смартфон Samsung Galaxy S22 Ultra^FS
^BY5,2,270
^FO50,280^BC^FD12345678^FS
^XZ
И вот что у нас получилось:
Что же мы имеем?
Исходя из представленного выше кода, управляющий символ в ZPL это "^". После него следует команда отвечающая за тот или иной аспект настройки этикетки.
Давайте же узнаем о них.
^XA и ^XZ - открывающий и закрывающий символ.
^FOx,y - отступ от левого верхнего края, принимает значение от 0 до 32000
^Af,h,w - изменение шрифта для последующего текста
f - сам шрифт, может быть в значении 0-9 либо A-Z
h - высота (от 1 до 32000)
w - ширина (от 1 до 32000)
Так же после f, перед запятой можно использовать символ вращения R - 90°, I - 180°, B - 270°
^FDdata - символ начала поля данных
^FS - символ конца определения поля
^FX - комментарий
Используя эти символы можно начать выводить простые текстовые этикетки.
Например:
^XA
^FO50,80^A0,30,50 ^FDSamsung Galaxy S22^FS
^FO50,150^AEI,5,5 ^FDSamsung Galaxy S22^FS
^FO50,230^AVR,30,50 ^FDSamsung Galaxy S22^FS
^FO180,230^AAB,50,20 ^FDSamsung Galaxy S22^FS
^XZ
Но стоит знать то, что не каждый шрифт поддаётся редактированию по размеру!
Получим:
^FB maxWidth, maxLines, lineSpacing, alignment, hangingIndent
maxWidth - ширина (от 0 до 9999)
maxLines - количество строк (от 1 до 9999)
lineSpacing - пробелы между строками (от -9999 до 9999)
alignment - выравнивание ( L (по левому краю), R (по правому краю), C (по центру),
J (растянуть текст по ширине поля) Дефолтное значение - L
hangingIndent - отступ от строки (от 0 до 9999)
Теперь добавим это в наш код и посмотрим, что будет:
^XA
^FB200,5,5,C
^FO50,80^A0,30,50 ^FDSamsung Galaxy S22^FS
^FB400,5,5,J
^FO50,220^A0,30,50 ^FDSamsung Galaxy S22^FS
^XZ
Первый текст получил "окно" шириной 200 точек и максимальным количеством строк = 5 с выравниванием по центру. В 200 точек влезало только одно слово, из-за этого текст разбился на 3 строки
А второй тест получил 400 точек и выравнивание по ширине поля. Два слова влезли в первую строку, но получился большой пробел, в который, однако, не влезло третье слово
Как видно, меняя эти параметры можно получить тест абсолютно любого формата
Штрих-коды
Штрих-кодов в документации описано огромное количество, но я для себя выделил 4 основных:
^BC - orientation, height, line, lineAbove, checkDigit, mode - Code 128 Bar code
orientation - вращение (R - 90°, I - 180°, B - 270°)
height - высота (от 1 до 32000)
line - печатать ли расшифровку (Y,N)
lineAbove - расшифровка сверху (Y,N)
^XA
^BY5,2,200
^FO50,50^BCN,,Y^FD12345678^FS
^XZ
Сразу опишу настройки, применимые ко всем штрих-кодам:
^BY width, widthRatio, height
width - ширина (от 1 до 100)
widthRatio - соотношение толщин линий (от 2.0 до 3.0 шаг 0.1)
height - высота (любое положительное число)
^FD - кодируемая информация
^BE - EAN-13 Bar Code
Настройки такие же, что и у ^BC
^XA
^FO50,50^BY3
^BEN,80,Y
^FD000012345678^FS
^XZ
-
^BQ orientation, model, magnification, errorCorrection- QR Code Bar code
orientation - вращение QR (R - 90°, I - 180°, B - 270°)
model - обычный или улучшенyый QR (1 или 2) Важно! Современными принтерами Zebra, model(1) не воспринимается, рекомендовано использовать (2)
magnification - увеличение QR (от 1 до 10)
errorCorrection - H = ultra-high reliability level, Q = high reliability level, M = standard level L = high density level (Рекомендую использовать H)Ещё один важный момент
По какой-то причине, некоторые принтеры не хотят воспринимать последний параметр после ^BQ, но воспринимают его после ^FD:
^FX вместо:
^XA
^FO50,50
^BQN,2,3,H
^FDSamsung Galaxy S22^FS
^XZ
^FX используй:
^XA
^FO50,50
^BQN,2,3
^FDH,Samsung Galaxy S22^FS
^XZ
^BX - orientation, height, quality, columns, rows, format, escape - Data Matrix
orientation - думаю, тут уже понятно))
height - высота (от 1 до размера листа)
quality - качество DM (0, 50, 80, 100, 140, 200) рекомендованный - 200
columns, rows - количество кодируемых колонок и строк (от 1 до 144)
format - формат DM (от 0 до 6) не используется при качестве 200
escape - эскейп символ, может использоваться любой символ, дефолтное значение тильда (~). При массовой печати стоит смотреть, не будет ли ваш эскейп символ передаваться в кодируемой информации. Я обычно использую бэкслеш (\)
С обычной кодировкой Data Matrix нет ничего сложного, просто передаётся информация и настраиваются параметры DM.
Например:
^XA
^FO50,50
^BXN,5,200,
^FDZPL (programming language)
ZPL (short for Z-level Programming Language) is an array programming
language designed to replace C and C++ programming languages in engineering
and scientific applications. Because its design goal was to obtain
cross-platform high performance, ZPL programs run fast on both sequential
and parallel computers. Highly-parallel ZPL programs are simple
and easy to write because it exclusively uses implicit parallelism.^FS
^XZ
Но как вам может быть известно, в России есть такая организация как "Честный знак" и они используют Data Matrix для маркировки товара(сигареты, алкоголь, обувь, лекарства)
В кодировку для Честного знака передаётся несколько полей, используя специальные разделители и передаваться они должны не просто строкой. DM то конечно составится, но только через проверку Честного знака он не пройдёт
В честном знаке используется открывающий символ FNC1 и разделители GS
Чтобы поставить FNC1 в начало строки необходимо использовать ваш ескейп символ и "1"
Т.е. ^BXN,5,200,,,,\,1 ^FD\1 и далее зашифрованный код маркировки
Символы GS как правило передаются внутри и прописывать из отдельно не приходится
Пример реальной этикетки Data Matrix:
P.S. Не обращайте внимание на непонятные символы переданные вместо текста, принтеры клиента лишь эмулируют ZPL, из-за чего не удалось установить на них драйвера для русского языка, в данном примере русские буквы пришлось зашифровать в URL.
^XA ^CI28
^CFP,5,10 ^FO20,2 ^FH^FD_D0_9D_D0_B0_D0_B8_D0_BC_D0_B5_D0_BD_D0_BE_D0_B2_D0_B0_D0_BD_D0_B8_D0_B5 _D0_BF_D1_80_D0_BE_D0_B4_D1_83_D0_BA_D1_86_D0_B8_D0_B8: _D0_A0_D0_B5_D0_B7_D0_B8_D0_BD_D0_BE_D0_B2_D1_8B_D0_B5 _D0_A7_D0_81_D0_A0_D0_9D_D0_AB_D0_99^FS
^CFP,5,10 ^FO20,20 ^FH^FD_D0_9C_D0_B0_D1_80_D0_BA_D0_B0_2F_D0_90_D1_80_D1_82_D0_B8_D0_BA_D1_83_D0_BB: Lemon Jelly/GRAD^FS
^CFP,5,10 ^FO20,38 ^FH^FD_D0_A1_D0_BE_D1_81_D1_82_D0_B0_D0_B2 _D0_92_D0_B5_D1_80_D1_85_2F_D0_9F_D0_BE_D0_B4_D0_BA_D0_BB_D0_B0_D0_B4_D0_BA_D0_B0_2F_D0_9D_D0_B8_D0_B7: _D0_BF_D0_BE_D0_BB_D0_B8_D0_BC_D0_B5_D1_80_2F_D1_82_D0_B5_D0_BA_D1_81_D1_82_D0_B8_D0_BB_D1_8C_2F_D1_80_D0_B5_D0_B7_D0_B8_D0_BD_D0_B0^FS
^CFP,5,10 ^FO20,56 ^FH^FD_D0_94_D0_B0_D1_82_D0_B0 _D0_B8_D0_B7_D0_B3: 202204 _D0_93_D0_B0_D1_80_D0_B0_D0_BD_D1_82_D0_B8_D0_B9_D0_BD_D1_8B_D0_B9 _D1_81_D1_80_D0_BE_D0_BA:30^FS
^CFP,5,10 ^FO20,74 ^FH^FD_D0_A1_D1_82_D1_80_D0_B0_D0_BD_D0_B0 _D0_B8_D0_B7_D0_B3_D0_BE_D1_82_D0_BE_D0_B2_D0_B8_D1_82_D0_B5_D0_BB_D1_8C: _D0_9F_D0_9E_D0_A0_D0_A2_D0_A3_D0_93_D0_90_D0_9B_D0_98_D0_AF^FS
^CFP,5,10 ^FO20,92 ^FH^FD_D0_9F_D1_80_D0_BE_D0_B8_D0_B7_D0_B2_D0_BE_D0_B4_D0_B8_D1_82_D0_B5_D0_BB_D1_8C: Design e More, S.A.^FS
^CFP,5,10 ^FO20,110 ^FH^FD_D0_90_D0_B4_D1_80_D0_B5_D1_81 _D0_BF_D1_80_D0_BE_D0_B8_D0_B7_D0_B2_D0_BE_D0_B4_D0_B8_D1_82_D0_B5_D0_BB_D1_8F: Rua das Casas Queimadas n 567.4415-439 Grijo V/N Gaia, Portugal^FS
^CFP,5,10 ^FO20,128 ^FH^FD_D0_9F_D1_80_D0_BE_D0_B4_D0_B0_D0_B2_D0_B5_D1_86 _D1_8E_D1_80_D0_B0_D0_B4_D1_80_D0_B5_D1_81 _D0_9E_D0_9E_D0_9E _D0_90_D0_A0_D0_95_D0_9D_D0_90 115 093 _D0_9C_D0_BE_D1_81_D0_BA_D0_B2_D0_B0 _D0_9F_D0_B0_D1_80_D1_82_D0_B8_D0_B9_D0_BD_D1_8B_D0_B9 _D0_BF_D0_B5_D1_80 _D0_B4 1^FS
^CFP,5,10 ^FO20,146 ^FH^FD_D0_BA_D0_BE_D1_80_D0_BF 11 _D1_8D_D1_82_D0_B0_D0_B6 3 _D0_BA_D0_BE_D0_BC_D0_BD №34 _D0_98_D0_BD_D1_81_D1_82_D1_80_D1_83_D0_BA_D1_86_D0_B8_D1_8F _D0_BF_D0_BE _D1_83_D1_85_D0_BE_D0_B4_D1_83 _D0_B7_D0_B0 _D0_B8_D0_B7_D0_B4_D0_B5_D0_BB_D0_B8_D0_B5_D0_BC _D0_BF_D1_80_D0_B8_D0_BB_D0_B0_D0_B3_D0_B0_D0_B5_D1_82_D1_81_D1_8F^FS
^CFP,5,10 ^FO20,164 ^FH^FD_D0_A0_D0_B5_D0_B7_D0_B8_D0_BD_D0_BE_D0_B2_D1_8B_D0_B5 _D0_A7_D0_81_D0_A0_D0_9D_D0_AB_D0_99^FS
^CFP,5,10 ^FO20,182 ^FH^FDLemon Jelly^FS
^CFP,5,10 ^FO20,200 ^FH^FDGRAD/231 11^FS
^CFP,5,10 ^FO20,218 ^FH^FD02 black^FS
^CFP,5,10 ^FO560,310 ^FH^FD04630162491475^FS
^CFP,5,10 ^FO560,330 ^FH^FD5aWoWlascYnb8^FS
^FO 45,270 ^BY4 ^BEN,60,Y,N ^FD2000016664232^FS
^FO465,265^GFA,294,294,7,,::1IF87FFE1IF8:::1EI0781E1E,:::::::::::1IF87FFE1E,:::1EI0781E1E,::::::::::::1IF8781E1IF8:::,: ^FS
^CFT,5,10 ^FO600,25 ^FH^FD_D0_A0_D0_B0_D0_B7_D0_BC_D0_B5_D1_80^FS
^CFT,5,10 ^FO635,100 ^FD037^FS
^FO560,150 ^BXN,4,200,,,,\,1 ^FD\10104630162491475215aWoWlascYnb89100BE929+ipgNo9UkRCzC7IfwA5bk1MhoLw3tpy9377HN7wpKFuOnG3KAfasNiHNDjwjR019xcThmlABPWm1VJFj3dl1w==^FS
^XZ
Отсканировав данный Data Matrix с помощью приложения Checkmark или Честный знак, вы увидите что он принадлежит Резиновым сапогам
P.P.S. для редактирования этикеток без печати я использую: Labelary Online ZPL Viewer
Там есть подсказки, для начала работы с ZPL это очень удобно.
Так же приложу ссылку на документацию: ZPL II Programming Guide (servopack.de)
Спасибо за уделённое время, буду рад вашим советам
Комментарии (13)
slepmog
10.08.2022 11:10+4^FOx,y — отступ от левого верхнего края, принимает значение от 0 до 32000
А измеряются
x
иy
в единицах, заданных последней командой^MU
. По умолчанию это точки, чей размер зависит от разрешения принтера. Для создания этикеток, одинаково выглядящих на принтерах с разным разрешением, удобно работать в миллиметрах,^MUm
.При этом стоит всегда возвращать единицы к точкам в конце этикетки:
^MUd^XZ
.
Это потому, что драйверы Zebra, когда транслируют печать из обычного приложения в ZPL, обнуляют все персистентные параметры, а единицы измерения обнулить забывают. (Писали про это в поддержку; не удалось попасть на кого-то, кто понял бы, о чём мы вообще.)
В результате драйвер выдаёт команду напечатать что-то по координатам, скажем,(1000, 500)
, думая, что это в точках. Но поскольку последний заданный режим был в миллиметрах, картинка рисуется на метр вправо от границы этикетки, и этикетка выходит пустой.При этом в режиме без поворотов, отражений и справаналевости
^FO
определяет левый верхний угол результирующей строки/картинки/штрихкода.^FT
определят левый нижний. Иногда удобно одно, иногда другое.^FDdata — символ начала текстового поля
Нет, это символ начала поля данных. Эти данные могут принадлежать чему угодно, не только текстовому полю. В частности, это может быть наполнение штрих-кода.
^FS — символ конца строки
Нет, это символ конца определения поля, начатого ранее. Концы строк сами по себе никак не маркируются.
^FB maxWidth, maxLines, lineSpacing, alignment, hangingIndent
Полезно помнить, что
^FB
— уникальная команда, которая недокументированно игнорирует единицы измерения, заданные в^MU
, и трактует все свои измерения как заданные в точках.
Полезно также помнить, что если текст не уместится вmaxLines
, то весь не уместившийся текст будет печататься поверх последней строки, снова и снова, пока не кончится.Тема
^TB
, улучшенной версии^FB
, не раскрыта!По какой-то причине, некоторые принтеры не хотят воспринимать последний параметр после ^BQ, но воспринимают его после ^FD:
Потому что документировано, что для
^BQ
строка данных^FD
должна начинаться с дескриптора, описывающего характеристики QR. Если его опустить, могут быть потеряны первые три символа данных.kudlitzz Автор
10.08.2022 12:43+1Спасибо за развёрнутый комментарий.
Исправил указанные Вами неточности.
С ^TB не сталкивался, поэтому не описал в статье, учту в дальнейшей работе.
slavius
10.08.2022 12:20+1Отдельное спасибо за
для редактирования этикеток без печати я использую: Labelary Online ZPL Viewer
tooler
10.08.2022 14:11Во внутреннем вебинтерфейсе принтера тоже есть предпросмотр из кода, но на этом сайте намного удобнее работает. Содержимое каталога, создать сценарий, изменить, предварительный просмотр наклейки.
kudlitzz Автор
10.08.2022 14:29Да, так же мне показалась удобной возможность передать изображение сразу при написании этикетки
kurilovigor
11.08.2022 12:07Тоже начинали с ZPL, потом перешли на печать через драйвер печати. Отказ ZPL помог легко перейти на дешевые и качественные китайские принтеры
ioncorpse
11.08.2022 17:37+2Где же вы раньше были?
Не читал, но одобряю. Не читал т.к. 4 месяца назад мне вот ровно вот это все нужно было для Честного Знака, генератор ZPL команд писал. Пришел к тем же ресурсам и всей информации в итоге, что и вы. Особенно тогда пригодилась бы инфа про FNC1 и GS.
sukhe
11.08.2022 20:48+2Штрихкоды — ерунда. Вот с печатью картинок на этикетках коллеги знатно помучились. Особенно, когда кроме Зебры одновременно используются ещё и другие аналогичные принтеры, которые хотя и совместимы, но есть нюансы.
Картинки — это всякие там рюмочки (хрупкое), стрелочки, указывающие направление верха, знаки переработки, значки стантдартов и прочее.
Отдельный квест — подбор этикеток и риббонов (красящих лент). Когда расход измеряется в сотнях километров, хочется подешевле; но тогда уже качество страдает. Ну и этикетки разные нужны — в гарантийный талон и обычные подойдут, а на изделия нужны устойчивые к истиранию и мойке.
sepetov
Статья размещена в хабах .net и Microsoft SQL Server, но о них в статье не упомянуто. Если возможно, неплохо было бы развить и эту тему, а то сейчас в статье только о ZPL.
Например, было бы интересно услышать о том, на каком подходе вы остановились при хранении в таблицах datamatrix-ов: бинарный image/blob или текст, если текст, то вырезаете ли FNC1 или он вам неудобств в хранении не доставляет?
Emulyator
А почему бы не генерировать qr-код и этикетку автоматически при печати, зачем хранить?
sepetov
Один раз только видел хранение в blob-е и не было возможности спросить причину. Возможно, она всё-таки существует :-)
Лично я храню в тексте и без управляющих символов - они только мешают. При печати подставляю их автоматически. Если интересно, чем мешают, то немногим:
Управляющие символы трудно набрать с клавиатуры, поэтому неудобно вручную делать запросы "SELECT * FROM table_name WHERE barcode='xxx'". Это приходится делать часто при разборе ошибок, а при внедрении - вообще ежедневно.
Управляющие символы часто "теряются" при передаче между разными приложениями. Например, приходилось импортировать штрихкоды из какой-то старой версии 1С, которая не могла передать FNC1 ни в каком виде: ни в файлах, ни через внешние источники данных, ни через post-запрос.
Иногда приходится пользоваться сканерами, работающими в разрыв клавиатуры, а они в нём не умеют их передавать.
Мой опыт кратенько выглядит как-то так.