Good afternoon, Habrahabr.
Thanks to the warmly accepted past publication, I can publish this article here. Thanks to everyone who put the pros.
On duty, I often have to generate reports for label printers of the Zebra family.
Zebras they areThe label generation mechanism is as follows: first, a decorated line is sent to the printer from the user's machine (computer, data collection terminal), then the internal processor of the printer processes this line and prints it. But the language in which the line for the printer is formed ( called ZPL) at first glance causes the uninitiated nervous hiccups and jitters.
I ask under the cat, all who want to understand this issue.
Example:^XA ^FO 0,10 ^GB632,0,2^FS ^FO0,25 ^FB632,1,0,C,0 ^ASN,70,70 ^FDWAR INC.^FS ^FO0,100 ^GB632,0,2^FS ^FO0,120 ^FB632,1,0,C,0 ^ASN,60,60 ^FDGoose^FS ^FO0,180 ^FB632,1,0,C,0 ^ASN,60,60 ^FDWild^FS ^FO0,240 ^GB632,0,2^FS ^FO120,260 ^BY2 ^BCN,70,N,N,N ^FDSECRECTCODE^FS - ^XZ
And the print displays such a neat badge:

Let's see what is written in this code, and consider the main elements.
I will devote the first part of my speech to the analysis of the syntax of this language, in an amount sufficient to create labels of satisfactory quality. In the second part, I will give examples of code in Java and VisualBasic, in order to send the label to print independently. Based on these examples, you can build your own program for printing.
Part 1. ZPL syntax
Firstly, all measurements in the ZPL are indicated in points. Therefore, for a clearer idea you should look in the documentation on the printer, what is your density of points per unit of length.
First, let's briefly go over the main teams, then consider them in more detail in conjunction.
1. Start and end of ZPL code:
^ XA - the beginning of the code,
^ XZ - the end of the code;
2. Indents for subsequent content:
^ FO x, y where: x is indent from the left edge, y is indent from above;
')
3. Field divider:
^ FS - marks the end of the field definition. It can literally be considered as a signal at the end of the line;
4. Scalable text. It consists of two parts, the choice of font and text input:
4.1. Font selection:
^ A <font name> <text orientation>, <font height in points>, <width in points>:<font name> - the default is A. More precisely, it does not even need to be entered, other fonts can be taken from the official documentation:

<text orientation>:
N - normal orientation; R - rotated 90 degrees clockwise;
I - inverted by 180 degrees; B - rotated 270 degrees;
4.2. Text output with the parameters specified in the previous paragraph:
^ FD <text>Examples:
^XA ^FO20,20 - 20 ^ASN,70,70 - , S, 70 ^FDWAR INC.^FS – WAR INC. ^XZ

Rotate and change the font:
^XA ^FO20,20 - 20 ^ABB,30,30 - 270 , B, 30 ^FDWAR INC.^FS – WAR INC. ^XZ

Change the font to S:
^XA ^FO20,20 - 20 ^ASB,30,30 - 270 , S, 30 ^FDWAR INC.^FS – WAR INC. ^XZ

Be sure to remember that different fonts may look different in terms of size, as can be seen above. I most often use the font S.
5.Block text:
^ FB <width>, <number of lines>, <spaces between lines>, <text alignment>, <indent for the second or subsequent line><text position> - can take values: L (on the left edge), R (on the right edge), C (on the center), J (stretch the text on the width of the margin);
Usually I used this command to center the text, or if it is necessary to place the text in several lines. If the text does not fit into the line, it begins to superimpose on itself. And it turns out like this:
^XA ^FO 20,20 ^FB400,1,0,C,0 ^AVN,70,70 ^FDWAR INC WILD GOOSE^FS ^XZ

Change the code so that there are two lines, and make the distance between the lines of 10 points:
^XA ^FO 20,20 ^FB400,2,10,C,0 ^AVN,70,70 ^FDWAR INC WILD GOOSE^FS ^XZ

And now we indent for the second line 30 dots to the left:
^XA ^FO 20,20 ^FB400,2,10,L,30 ^AVN,70,70 ^FDWAR INC WILD GOOSE^FS ^XZ

6. Drawing rectangles:
^ GB <width>, <height>, <line thickness>, (<line color>, <corner rounding>)In brackets are optional parameters.
Line color: B (black) or W (white)
The rounding of corners is indicated by a number from 0 to 8 (strong rounding)
Examples:
With strong rounding:
^XA ^FO20,20 ^GB300,100,2,B,8^FS ^XZ

No rounding:
^XA ^FO20,20 ^GB300,100,2^FS ^XZ

If you want to draw just a line, then draw a rectangle with a height of 0:
^XA ^FO20,20 ^GB300,0,2^FS ^XZ

7. The barcode consists of three commands - the first sets its dimensions, the second setting and the third - the content:
7.1. Barcode dimensions:
^ BY <width>, (<ratio of line thickness>, <barcode height>)7.2. Barcode Settings:
^ BC <orientation>, <barcode height in dots>, <print code decryption>, <code decryption above barcode>, <mode><orientation> - N - normal orientation; R - rotated 90 degrees clockwise; I - inverted by 180 degrees; B - rotated 270 degrees;
<print whether decoding code>, <decoding code above barcode> - take values Y (yes) or N (no);
<mode> - I did not understand this field, by default N;
^ BC - barcode in standard 128 (more Code_128 ); There are also several other formats, but due to the fact that I did not have to use them, they will not be considered in this article, and I recommend to look at the information on them in the official documentation supplied to Zebra printers;
7.3. Print barcode:
^ FD <coded information>Unfortunately, the barcode can not be placed in the "box" as text to center the width of the label, and therefore you have to shamanize with fields and indents.
Examples:
^XA ^FO 20,20 ^BY3 ^BCN,100,Y,N,N ^FD123456789^FS ^XZ

Let's reverse, and specify print the decoding from the top (now turned out from below):
^XA ^FO 20,20 ^BY3 ^BCI,100,Y,Y,N ^FD123456789^FS ^XZ

At this point, we will focus on the consideration of the main elements of the ZPL, and proceed to part two, in which we briefly consider the mechanism for sending information to the printer.
Part 2. Printing
The transfer mechanism is nowhere easier. For this, we need to know the IP, where the printer is located, and the port. Next, we create a stream that is sent to the specified address, and we get a label at the output.
Visual Basic example:
Dim i As Double Dim SSCC As String Dim ipAddress As String = "127.0.0.1" Dim port As Integer = 1234 Dim ZPLString As String Try ' Dim client As New System.Net.Sockets.TcpClient client.Connect(ipAddress, port) Dim writer As New System.IO.StreamWriter(client.GetStream()) ' ZPL- ZPLString= "^XA" & "^BY2" & "^FO0,200" & "^FB632,2,0,C,0" & "^ASN,60,60" & "^FDHELLO HABRAHABR!!!^FS" & "^XZ" writer.Write(ZPLString) writer.Flush() ' writer.Close() client.Close() Catch ex As Exception End Try
Java example:
import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; public class PrintToZebra { public static void main(String[] args) throws IOException { System.out.println(" "); try(Socket s = new Socket("127.0.0.1", 1234)){ OutputStream out = s.getOutputStream(); PrintWriter writer = new PrintWriter(out, true); String ZPLString= "^XA" + "^BY2" + "^FO0,200" + "^FB632,2,0,C,0" + "^ASN,60,60" + "^FDHELLO HABRAHABR!!!^FS" + "^XZ"; writer.println(ZPLString); writer.flush(); System.out.println(" "); } } }
As you can see, nothing complicated. I hope that my article will make it easier for the brave Zebra tamers.
So let me leave. Waiting for advice, criticism and support.