L'article mentionne à plusieurs reprises que l'instruction Field Origin ^FO ne fonctionne pas avec l'affichage des images.
Ne tenez pas compte de cette remarque erronée. Il ne faut pas confondre la lettre O avec le chiffre zéro. En fonction de la font utilisée, cette distinction est parfois impossible et l'erreur de frappe si vite arrivée :-/ ! Voila pourquoi j'aime fonts avec le zéro barré ;-)
Introduction
Cette article est relativement technique mais passe en revue un certain nombre d'informations nécessaires permettant d'imprimer des images sur une étiquette Zebra.
J'y suis arrivé, vous pourrez donc le faire aussi.
Pour ma part, je dois réaliser cela à partir d'un programme en Clipper (Harbour Project) et n'ai donc pas d'autres choix que de me taper les spécifications.
Vous pouvez également vous référer à ces quelques articles d'introduction:
- Zebra + ZPL sous Clipper Windows
- Imprimante Zebra USB, ZPL et Spooler Windows - Sacré Windows!!!
- Transformer une image en BMP 2 couleurs pour une impression PCL
Pour imprimer une image, il faut utiliser la commande ~DG pour définir l'image et stocker l'image en RAM.
Ensuite, l'image est imprimée à l'aide de la commande ^XG.
L'exemple Zebra se base sur un petit damier codé assez simplement.
Cet exemple à deux défauts:
- L'image est petite. Si vous utilisez une étiquette de 1", il est fort probable que vous ne voyez jamais rien. L'image est simplement imprimée hors de l'étiquette!
- L'image est toujours imprimée depuis la coordonnée 0,0.
Lu ailleurs dans la doc technique de 500 pages, la coordonnée par défaut de l'image est 0,0.
Malgré les commandes de positionnement ^FO (field origin), l'origine d'impression sur ma Zebra est toujours 0,0!!!
Il y a même l'exemple ^XG prétendant imprimer une image a différentes positions sur l'étiquette Zebra LP2824... cet exemple ne fonctionne simplement pas. L'origine est et reste 0,0 et rien d'autre. Il faudra donc prévoir son image en fonction de cette caractéristique.
~DGR:SAMPLE.GRF,00080,010, FFFFFFFFFFFFFFFFFFFF 8000FFFF0000FFFF0001 8000FFFF0000FFFF0001 8000FFFF0000FFFF0001 FFFF0000FFFF0000FFFF FFFF0000FFFF0000FFFF FFFF0000FFFF0000FFFF FFFFFFFFFFFFFFFFFFFF ^XA ^F020,20^XGR:SAMPLE.GRF,3,3^FS ^XZ
Vous pouvez clairement voir que sur notre Zebra LP2824 la partie supérieure gauche du damier est imprimé "hors" de l'étiquette (depuis le point d'origine 0,0).
Il est donc très important de tenir compte de cette caractéristique lors de la création de notre image.
Encodage de l'image et notes de calculs
Encodage Hexadécimal
Ce point est probablement l'un des plus simple (cfr la doc).
Chaque point/pixel est représenté par un bit dans un octet/byte (il y a 8 bits dans un octet).
Et chaque octet est codé en Hexadécimal pour le définition de l'image.
FF en Hexadécimal --> 255 en décimal --> 11111111 en binaire --> donc 8 points nous d'affilés
80 en Hexadécimal --> 128 en décimal --> 10000000 en binaire --> un point noir + 7 point blancs.
Une ligne contenant FF FF FF représente donc une ligne d'une image avec 24 points noir.
Nous avons donc un ligne ZPL par ligne dans l'image ET chaque ligne doit contenir un multiple de 8 points.
Si l'image fait 203 pixels (lignes) de haut, le bloc ZPL de l'image fera 203 lignes.
Si l'image fait 120 pixels de large, il faudra 120 / 8bits = 15 octets pour coder chaque ligne.
Comme la représentation d'un octet se fait avec 2 caractères Hexadécimal (FF = 255 en décimal), chaque ligne du bloc ZPL définissant l'image contiendra 30 caractères alphanumériques.
Question de point de vue
Là aussi je pense que la doc Zebra est un peu confuse.
Je ne sais pas pour vous mais moi je ne pars pas d'une image en mm pour ensuite savoir comment la plaquer sur une étiquette.
Je pars plutôt de l'espace disponible sur mon étiquette... puis détermine la taille de l'image à y placer... et je forge l'image en PIXELS en fonction de ce que j'ai besoin!
Pas besoin de se prendre la tête avec le nombre de dpi de l'imprimante et les mm disponibles.
La définition du bloc ~DG ci-dessous reprend donc les mensuration et caclul à partir de la taille en pixels.
Définition de l'image
Le bloc ~DG est défini comme suit:
~DGd:o.x,t,w,data
- d = l'endroit où l'on stocke l'image... c'est en R (la ram)
- o.x = Nom de l'image et son extension. Nom court, sans fioriture, moins de 8 caractères. optez pour un truc come LOGO.GRF
- t = Taille de l'image en byte/octet. hauteur_en_pixels * largeur_en_pixels / 8.
N'oubliez pas de prendre une image dont la largeur est un multiple de 8.
Pour une image 120 x 203 --> 120 x 203 / 8 = 3045 octets - w = Nombre de bytes/octets par ligne. Dépend uniquement de la largeur de l'image.
Largeur_en_Pixel / 8 --> 120 / 8 = 15 octets sur chaque ligne. - data = donnée de l'image au format hexadécimal (comme décrit ci-dessus).
Cette étiquette étant destiné à une imprimante Zebra LP2824 Plus avec une étiquette Z-Select 2000D de 50.8 x 25.4mm (2" x 1").
~DGR:GGLOGO.GRF,03045,015, 800000000000000000000000000000 000000000000000000000000000000 ... ... 000000000000000000000000000000 000000000000000000000000000000 ^XA ^F00,0^XGR:GGLOGO.GRF,1,1^FS ^FX--FAIRE ETIQUETTE LARGE--^FS ^FX--Print Qty--^FS ^PQ@@QTY@@,0,0,Y ^FX--FR LABEL--^FS ^FO20,25^AR,40,30^FB405,1,0,C,0^FD@@LIBEEN@@^FS ^FX--PRODUCT CODE--^FS ^FO120,75^AT,40,30^FB300,1,0,C,0^FD@@ARTI@@^FS ^FX--BOX LOGO--^FS ^FO20,65^GB100,130,2^FS ^FX--EAN - CODE128--^FS ^FO230,130^BY1^AD,18,10 ^BCN,40,Y,N, ^FD@@EAN@@^FS ^XZ
Au final, nous obtenons ceci.
Cas pratique
Voici comment faire pour créer l'image à afficher sur une étiquette.
Pour commencer, j'ai essayer de produire le code ZPL de l'étiquette avec un cadre à l'emplacement où je voulais placer l'image.
Cela permet:
- De connaître les coordonnées utiles.
- d'avoir la dimension de l'image à produire.
Au final, j'ai produit l'étiquette suivante.
^XA ^FX--FAIRE ETIQUETTE LARGE--^FS ^FX--Print Qty--^FS ^PQ1,0,0,Y ^FX--FR LABEL--^FS ^FO20,25^AR,40,30^FB405,1,0,C,0^FD@@LIBEEN@@^FS ^FX--PRODUCT CODE--^FS ^FO120,75^AT,40,30^FB300,1,0,C,0^FD@@ARTI@@^FS ^FX--BOX LOGO--^FS ^FO20,65^GB100,130,2^FS ^FX--EAN - CODE128--^FS ^FO230,130^BY1^AD,18,10 ^BCN,40,Y,N, ^FD@@EAN@@^FS ^XZ
Les valeurs avec @@code@@ sont destinés à être remplacés au vol par un programme.
Ce qui nous intéresse, c'est la BOX pour la future position du logo.
^FO20,65^GB100,130,2^FS
Cette boite se trouve à l'origine 20,65 (gauche,top) avec une largeur de 100 et hauteur de 130 points.
Mais souvenez vous, une image est toujours affichée depuis l'origine 0,0.
Pour remplir le cadre, il faudra une image de:
- 20 + 100 = 120 point de large.
On veillera à ne pas placer de graphique sur une bande de 20 pixels sur la gauche de l'image).
La largeur est aussi un multiple de 8 (pour le codage Hexadécimal) --> 15 octets par ligne. - 65 + 103 = 195 Pixels de haut.
On veillera à ne pas placer de graphique sur la bande supérieure de 65 pixels (afin de ne dessiner que dans le cadre).
Créer l'image
J'ai utiliser la méthode décrite dans le précédent article Transformer une image en BMP 2 couleurs pour une impression PCL pour créer l'image ci-dessous.
Bon, j'ai fait un petit excès de zèle sur la hauteur de l'image, elle fait 203 pixels de haut (au lieu des 195 prévu).
La taille en octet de mon image sera donc de Hauteur_en_pixel x Largeur_en_Pixel / 8 = 3045 octets!
Le nombre d'octet par ligne pour l'image est de largeur_en_pixel / 8 = 15 octets!
Voici l'image pour ceux qui voudraient répéter l'opération (l'image doit être en 2 couleurs, 120x203 pixels)
Blogger n'aime pas les BMP 2 couleurs du coup c'est un PNG qu'il vous faudra certainement la retransformé en BMP 2 couleurs |
Cela permet d'obtenir un fichier brute qui peut être rechargé en Python (sous format binaire).
Il faut sauter les 32 premiers octets de l'entête et chaque ligne est codée sur 16 octets de large au lieu de 15 octets (il faut donc se débarrasser du dernier octet de chaque ligne qui est toujours à 00).
Voici un petit script python (iPython) sans prétention qui permet de faire les manipulations nécessaires à la création du bloc de donnée de l'image en HexaDécimal.
file = open( 'GGLOGO.im1', 'rb' ) # Read BINARY file fileContent = file.read() file.close() withoutHeader = fileContent[32:] # Get the first byte as Hex # "{:02x}".format( withoutHeader[0] ) # Image Size in Pixels (or dots). Look for Image property in Gimp # Width must be a multiple of 8 image_width = 120 image_height = 203 byte_per_line = int( image_width / 8 ) byte_to_read = int( (image_width * image_height)/8 ) # Test the size of array of data with image size len( withoutHeader ) # 3248 (inclus le 16ieme octet 00 à virer sur chaque ligne!) byte_to_read # 3045 (n'inclus pas le CR dans le calcul) # Result = List with one line per image line. # Each image line is a list of 15 byte under HexaDecimal # representation (we throw the 16th byte) # # result = [ [ '00', '00', '00', '00', '00', '00', '00' , '00' , '00' , '00' , '00' , '00', '00', '00' ,'00' ], # [ '00', '00', '00', '00', '00', '00', '00' , '00' , '00' , '00' , '00' , '00', '00', '00' ,'00' ], # ... # ] iCount = 0 result = [] sublist = [] for c in withoutHeader: iCount += 1 # Garder que les 15 premiers bytes if iCount < 16: sublist.append( "{:02x}".format( c ) ) # Tous les 16 bytes c est une nouvelle ligne if iCount == 16: # skip the 16th byte # Start a new sublist result.append( sublist ) sublist = [] iCount = 0 if len( sublist )>0: result.append( sublist ) # View the result of the resultLines transformation done here under #for l in result # print( ''.join(l) ) # Joindre les représentations Hexa Decimal en ligne (une string par ligne de l'image) ) # # resultLines = [ '000000000000000000000000000000' , # '000000000000000000000000000000' , # ... # ] resultLines = [ ''.join(l) for l in result ] outFile = open( 'GGLOGO.GRF' , 'w', encoding='utf8' ) for l in resultLines: outFile.write( l+'\n' ) outfile.close()
Je vous laisse décortiquer le script qui génère un fichier 'GGLOGO.GRF'. C'est un peu brute de décoffrage mais vous devriez pouvoir reproduire toutes ces étapes par vous même.
Le fichier GGLogo.GRF contient uniquement les données Hexadécimales de l'image à placer dans le bloc data de la commande ZPL ~DG .
Comme déjà calculé précédement, le paramètre T faut 3045 octets et le paramètre w vaut 15 octets.
Au final, nous avons une belle étiquette en ZPL dont voici le contenu complet.
~DGR:GGLOGO.GRF,03045,015, 800000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000020 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000000000000000020 000000000000000000000000000010 000000000000000017fffff8000020 0000000000000000e0000007000010 000000000000000300000000400010 000000000000000c00000000300020 00000000000000101e0fff400c0010 0000000000000060e230003f030020 000000000000004304200000e08010 000000000000010c002000001c4010 000000000000023004200000039010 00000000000004400420000000d820 000000000000088004400000003410 000000000000130004400000000b10 000000000000220004400000000490 000000000000cc0004400000000360 000000000001900004600000000130 000000000006600004400000000110 000000000018800004400000000120 00000003c0e3000000400000000190 00000007f90600000a640000005e20 0000001ffe01f017f8017ffffd0010 0000003ffe000de800000000000020 0000007fde000003002000e0000010 000000fc0300fc0f007fc1e000f020 000000fc0103ff0f503ff1ff0fff10 000001f80807ff8ffc7ff1ff9fffe0 000001f80fe7878ffffff9ffffeff0 000001f807ff1f0fe3f1f9f87f01f0 000001f003feff0f878079f07c0070 000001f003ee000f078039e07c0070 000001f0e3ce001e07003de0781830 000001f1b3ce001f0f003de0783c38 000001f363cf000e07001de0786438 000001f3e3cf01df07803de0f83c38 000001f9c7cf83df07c07de07c183c 000001fc07cfffdf03fffde0fc007c 000000fc0fc7ff9f03fffdf07e00fe 000000ffffc1ff3f00ffbff07f01fe 000000ffffc0700b002f3ff87ffffe 0000007fffc00000000004007fffff 0000003fff800000000000003fffff 0000000fff000000000000001fff9c 00000003fc000000000000000fff00 000000103c0000000000000003fc00 000000103c00000000000000000000 000000103c00000000000000000000 000000103800000000000000000000 000000103c00000000000000000000 000000187c00000000000000000000 0000001cfc00000000000000000000 0000001ffe00000000000000000000 0000000ff000000000000000000000 00000007e000000000000000000000 000000010400000000000000000000 000000001c00000000000000000000 000000301c00000000000000000000 000000383c00000000000000000000 000000381c00000000000000000000 000000383c00000000000000000000 000000383c00000000000000000000 0000003c3c00000000000000000000 0000003cfc00000000000000000000 0000001ffc00000000000000000000 0000001ffc00000000000000000000 0000001ffc00000000000000000000 0000000ffc00000000000000000000 000000073c00000000000000000000 000000003c00000000000000000000 000000003c00000000000000000000 000000603c00000000000000000000 000000203c00000000000000000000 000000307c00000000000000000000 000000707c00000000000000000000 00000039f800000000000000000000 0000003ff800000000000000000000 0000001ff000000000000000000000 0000001fe000000000000000000000 00000007c000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 ^XA ^F00,0^XGR:GGLOGO.GRF,1,1^FS ^FX--FAIRE ETIQUETTE LARGE--^FS ^FX--Print Qty--^FS ^PQ1,0,0,Y ^FX--FR LABEL--^FS ^FO20,25^AR,40,30^FB405,1,0,C,0^FD@@LIBEEN@@^FS ^FX--PRODUCT CODE--^FS ^FO120,75^AT,40,30^FB300,1,0,C,0^FD@@ARTI@@^FS ^FX--BOX LOGO--^FS ^FO20,65^GB100,130,2^FS ^FX--EAN - CODE128--^FS ^FO230,130^BY1^AD,18,10 ^BCN,40,Y,N, ^FD@@EAN@@^FS ^XZN'oubliez pas de remplacer les @@code@@ par des valeurs pertinentes... et surtout de sauver votre fichier en UTF8. En espérant que cela puisse vous être utile.
4 commentaires:
bonjour
Je trouve votre article très intéressant mais je suis confronté à un souci je n'arrive pas avec mon clavier à générer le caret (^). Vous utilisez quel éditeur pour pouvoir écrire un (^)X ?
Je suis Mageia par avance merci de votre aide.
ensuite je teste tout ça sur une 105LS
Georges
bonjour Goerge. il s agit d un caractere européen utilisé principalement en langue française. il est egalement disponible depuis la nuit des temps dans les tables ASCII... c dst dire si cela date. le "caret" est aussi utilisé pour identifier le caractere Controle Ctrl.
A vous lire, j imagine que vous utikiser un clavier qwerty... il faudra chercher sur le net pour savoir comment produire le caractère au clavier (Sinon faite un copier coller).
Sur un clavier azerty, il suffit d utiliser la touche ^ suivie d un espace :-)
Bonjour Dominique
J'avance et pour ce faire j'ai installé python que je découvre en mme tps.
J'ai un autre souci; j'ai fait un 'copier-coller' de votre script et l'interpréteur m'indique une erreur en ligne 37 :
File "trsf_im1_pour_zebra.py", line 37, in
sublist.append( "{:02x}".format( c ) )
ValueError: Unknown format code 'x' for object of type 'str'
Un peu confus et honteux de vous poser la question mais savez vous à quoi c'est dû; Docteur?
est ce grave? et auriez vous revu votre scrip dernierement..... dès fois que...
par avance merci beaucoup de votre aide et soutien
Georges
Bonjour,
ce code utilise Python 2.7 (pour info utile, cela est important car la gestion des string est différente entre 2.7 et python 3.5).
L'erreur dans
"{:02x}".format( c )
provient du fait que "c" ne contient pas un entier mais une chaine de caractère.
Vous pouvez tester cette ligne directement dans mode interactif en affectant une valeur à "c"... comme par exemple
c = 29
"{:02x}".format( c )
Si vous testez le code ci-dessous alors vous obtenez la meme erreur.
c = "35"
"{:02x}".format( c )
La question est donc... pourquoi avez vous une chaine de caractère dans "c" plutot qu'une valeur entière.
Bonne journée,
Dominique
Enregistrer un commentaire