zpl.tools
Bar codes

^BX - Data Matrix Bar Code

Compact two-dimensional symbology used for small-part marking and GS1 DataMatrix.

^BX prints a Data Matrix symbol: square modules inside an L-shaped finder pattern, with no position markers that need a quiet zone. It therefore packs more data into a small footprint than ^BQ QR Code. It is the symbology for direct part marking and for GS1 DataMatrix in healthcare. Choose ^BQ where a phone camera reads the label, and ^B7 PDF417 where the payload runs to hundreds of bytes.

Syntax

^BXo,h,s,c,r,f,g,a

Support

Partially supportedparsed: full · state: full · rendered: partial

The command renders, with the caveat below.

ECC 200 symbols render at the requested module size. The c argument selects a square symbol size, and the g argument turns on the field-data escape sequences. The renderer parses the other sizing arguments r, f and a and ignores them. The renderer does not implement the older quality levels. Labelary supports quality 200 only. That part is not a compatibility gap. — not pinned by a test

Labelary renders this command.

Parameters

oBar code orientationoptional · one of a set

Rotation of the symbol and interpretation line.

Default
the orientation set by ^FW, which itself defaults to N
Invalid value
discarded, and the default applies. The renderer reports an unrecognized character as invalid, and uses the default.
Test coverage
No documented value is exercised by a test (N, R, I, B). The invalid-value rule is not pinned by a test.
N
No rotation (default)
R
Rotated 90° clockwise
I
Rotated 180°. The symbol reads upside down
B
Rotated 270° clockwise
hElement heightoptional · dots

Size of one square module. The elements are square. This size is also their width.

Default
the element size that makes the whole symbol match the ^BY bar code height
Accepted range
1 to the label width dots
Invalid value
not documented, and we have not measured it. The stated range is "1 to the width of the label". This range is not a static bound. Neither Labelary nor the renderer validates it.
Test coverage
No test at the minimum, maximum or one step outside the range.
sQuality leveloptional · integernot honoured by zpl.tools

Error-correction scheme. ECC 200 is the only scheme in current use, and the only one to specify.

Default
0
Accepted range
0 to 200
Invalid value
not possible — any value is accepted. The accepted values are a discrete set (0, 50, 80, 100, 140, 200) rather than a range, and Labelary does not report values outside it. The renderer always encodes ECC 200.
Test coverage
No test at the minimum, maximum or one step outside the range.
cColumnsoptional · integer

Requested symbol width in modules. The renderer reads it as a square symbol size. ^BXN,5,200,22 asks for a 22x22 symbol.

Default
the size the data needs
Accepted range
10 to 144 modules
Invalid value
discarded, and the default applies. Labelary validates columns and rows jointly against the fixed set of square sizes. It ignores the whole command when the pair matches no size in that set (measured 2026-07-12). An unrecognized size defaults to automatic sizing.
Test coverage
Bounds pinned: below the minimum (0). No test at the minimum or maximum. The invalid-value rule is not pinned by a test.
rRowsoptional · integernot honoured by zpl.tools

Requested symbol height in modules.

Default
the size the data needs
Accepted range
10 to 144 modules
Invalid value
discarded, and the default applies. The renderer parses this argument but does not use it, and sizes the symbol from c alone.
Test coverage
Bounds pinned: below the minimum (0). No test at the minimum or maximum. The invalid-value rule is not pinned by a test.
fFormat IDoptional · integernot honoured by zpl.tools

Data-type hint for the pre-ECC-200 schemes. The renderer ignores it for ECC 200, the level to use.

Default
6
Accepted range
1 to 6
Invalid value
not possible — any value is accepted. Labelary reports no warning for 0 or 7 (measured 2026-07-12).
Test coverage
Bounds pinned: above the maximum (7). No test at the minimum or maximum.
gEscape characteroptional · single character

Character that introduces a control sequence inside the field data. _1 is an FNC1, __ one literal _, _d065 the byte 65, and _A the byte that character names less 0x40. Any other sequence prints no symbol.

Default
~
Invalid value
not possible — any value is accepted. Any single character is accepted. A slot of more than one is trimmed to the first, and the trim is reported: the trailing space in _ counts, so the length is judged on the slot as written. The default ~ never reaches the field data, which the ZPL parser reads first.
Test coverage
aAspect ratiooptional · one of a setnot honoured by zpl.tools

Whether the renderer can use a rectangular symbol.

Default
a square symbol
Invalid value
discarded, and the default applies. The renderer parses this argument, does not use it, and always draws a square symbol.
Test coverage
No documented value is exercised by a test (1, 2). The invalid-value rule is not pinned by a test.
1
Square (default)
2
Rectangular — not honoured by zpl.tools

Behavior

Quality level

Specify quality 200 and nothing else. ECC 200 is the current scheme, it uses Reed-Solomon error correction, and it is the only level to encode. The older levels 0 to 140 use convolution encoding, and the guide restricts them to closed systems where one party controls both the printing and the reading. Quality 200 is also the only level the renderer implements, and the only one Labelary implements.

Symbol size

The elements are square, so the height argument is also their width. At zero, or absent, the symbol takes its size from the ^BY bar code height. The renderer divides that height by the rows and columns the data needs, rounds the result, and floors it at one dot. A set value gives exactly that module size.

The column argument forces a symbol size, which suits a run of labels that must all look the same whatever the payload length. The guide's rule is that quality 200 takes even values only. It adds that a size too small for the data prints no symbol at all, and not a truncated one. Otherwise the encoder determines the rows and columns automatically.

Field data

Under quality 200 the field data holds 3072 bytes at most, and the encoder truncates more. The escape character — ~ by default, and settable with the g argument — introduces control sequences inside the field data:

  • ~X shifts to a control character, so ~A is 0x01;
  • ~1 inserts FNC1, and no other digit is valid;
  • ~dNNN inserts the byte those three digits name;
  • ~ itself is written twice.

Any other sequence, and a trailing escape character, prints no symbol at all. FNC1 makes a symbol a GS1 DataMatrix. With ^FH also in use, hexadecimal processing runs first, and the escape sequences apply to its output.

No argument requests a rectangular Data Matrix. The guide says so outright, and the aspect-ratio argument that Labelary documents does not change it.

Tested behavior

Differences from the specification

Labelary against the ZPL II guide

Where Labelary — our compatibility target — behaves differently from the printed specification.

  • The guide documents six quality levels. Labelary implements ECC 200 only. — not pinned by a test
  • The guide gives ^BX seven arguments, and states that ZPL cannot request a rectangular Data Matrix. Labelary documents an eighth aspect-ratio argument. The renderer accepts it and, like Labelary, always draws a square symbol. — not pinned by a test

zpl.tools against the ZPL II guide

Where our renderer behaves differently from the specification.

  • The guide takes the default orientation from ^FW. The renderer applies ^FW to text fields only. A barcode after ^FWR prints unrotated unless its own o argument rotates it. — not pinned by a test
  • The renderer treats c as a square symbol size and ignores r, so it draws no rectangular symbol. — not pinned by a test

zpl.tools against Labelary

Where our output differs from Labelary for the same input. These are the differences that break a migration.

  • An FNC1 escape at the start of the field data reaches the symbol as the FNC1 codeword, module for module with Labelary. One anywhere else reaches it as the byte 0x1D, which readers report as the same separator but which is a different codeword. — not pinned by a test
  • An escape slot of more than one character is trimmed in both engines, and reported on the same argument at the same bytes, in a different sentence. Labelary says the value was trimmed to one character; the report here names the character that survived, as in Value '~"' is too long and was trimmed to '~'. The sentence is the argument reader's, so ^A, ^B3, ^BO, ^CF and ^LR word their own trim reports the same way, and twenty corpus entries pair on this wording alone. The divergence is deliberate and the wording is not going to change: the surviving character is the part an author needs, and one character leaves it to be worked out. Both engines draw the same label. — pinned by test_bx_escape_character_longer_than_one_is_trimmed

Example

An ECC 200 symbol with a 6-dot module, sized automatically from the payload:

^XA
^FO50,50^BY3
^BXN,6,200
^FD12345678901234567890^FS
^XZ

Open in viewer

  • ^BQ QR Code Bar CodeThe common two-dimensional matrix symbology. A prefix on the field data sets its error correction and input mode. The command does not set them.
  • ^B7 PDF417 Bar CodeStacked two-dimensional symbology that holds several hundred bytes, used on driving licenses and shipping documents.
  • ^BF MicroPDF417 Bar CodeCompact PDF417 variant for small labels, selected from a table of 34 fixed row-and-column layouts.
  • ^FD Field DataSupplies the text or barcode content of the field.