^BD - UPS MaxiCode Bar Code
Fixed-size hexagonal symbology read by UPS sorting equipment.
^BD prints a MaxiCode symbol: a fixed-size grid of hexagons around a bulls-eye.
UPS developed it, and UPS sorting equipment reads it optically instead of with a
scanner. MaxiCode has one purpose. It puts UPS routing data and address data on a
parcel label in the form that UPS requires. No other command in this category is
a substitute, and MaxiCode is not a general-purpose 2D code.
Syntax
^BDm,n,tSupport
The command renders, with the caveat below.
The symbol renders, including the modes 2 and 3 carrier header read off the head of the field data. Where two code-set switch sequences cost the same, the encoder picks a different one from Labelary. A symbol carrying mixed-case data then holds the same message in a different pattern of modules. The renderer does not implement structured append: it parses n and t and ignores them. Each symbol in a multi-part sequence renders as a single symbol. — not pinned by a test
Labelary renders this command.
Parameters
mModeoptional · integerWhich MaxiCode structure to encode. Modes 2 and 3 split the field data into a carrier header and a message; modes 4 to 6 encode it whole.
- Default
- 2
- Accepted range
- 2 to 6
- Invalid value
- not documented, and we have not measured it. Labelary applies no plain range here. Mode 1 passes through silently, and Labelary reports 0 and 7 and resets them to 2 (measured 2026-07-12). The renderer passes the value to the encoder as written.
- Test coverage
- Bounds pinned: zero (0). No test at the minimum or maximum.
nSymbol numberoptional · integernot honoured by zpl.toolsPosition of this symbol within a structured-append sequence.
- Default
- 1
- Accepted range
- 1 to 8
- Invalid value
- discarded, and the default applies. The renderer reports a value outside 1-8 and resets it to 1, in both directions (measured against Labelary 2026-07-12).
- Test coverage
- Bounds pinned: below the minimum (0), above the maximum (9). No test at the minimum or maximum. The invalid-value rule is not pinned by a test.
tTotal symbolsoptional · integernot honoured by zpl.toolsHow many symbols the structured-append sequence spans.
- Default
- 1
- Accepted range
- 1 to 8
- Invalid value
- discarded, and the default applies. The renderer reports a value outside 1-8 and resets it to 1.
- Test coverage
- Bounds pinned: below the minimum (0), above the maximum (9). No test at the minimum or maximum. The invalid-value rule is not pinned by a test.
Behavior
The symbol is a fixed size, with no orientation argument and no height argument.
^BY has no effect on it. A MaxiCode field keeps that fixed size at the position
that ^FO gives it, and the command draws no interpretation line.
The field data in modes 2 and 3
In modes 2 and 3 the field data is two messages together, and the guide is strict about the shape of the first:
- Mode 2, a US-style postal code — a three-digit class of service, a three-digit country code, a five-digit ZIP and a four-digit ZIP extension. The field data must contain four zeros when there is no extension.
- Mode 3, a non-US postal code — three-digit class of service, three-digit country code, then a six-character postal code that can contain letters.
An incorrect header gives an error and not a symbol. The mode does not follow the format of the postal code automatically, and a non-US code in mode 2 usually gives an invalid-character error or a message-too-short error. Select mode 3 for such a code.
The low-priority message after the header is the UPS transportation data block.
GS (0x1D) separates its fields and RS (0x1E) separates its format types, and
the block ends in EOT.
A keyboard cannot type those bytes. Use ^FH with _ before each hex pair, as
the guide's own example does. UPS's own Guide to Bar Coding with UPS defines
the mandatory fields. Use upper case throughout.
Modes 4 to 6 encode the field data whole, with no header split. Use mode 4 for a symbol outside the UPS postal routing.
Structured append
The symbol-number argument and the total argument split one message across up to eight symbols. The renderer parses both arguments and ignores them, so each symbol of a sequence renders alone. The default sequence is one symbol, and this limitation does not change it.
Known issues
Tested behavior
Differences from the specification
zpl.tools against the ZPL II guide
Where our renderer behaves differently from the specification.
- The renderer parses the structured-append arguments
nandt, and ignores them. — not pinned by a test ^BDhas no orientation argument, and the renderer always draws it unrotated. A MaxiCode field after^FWRstays upright. — 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.
- Where two MaxiCode code-set switch sequences carry the same characters in the same number of codewords, the renderer picks a different one from Labelary.
^BD4^FDab CDshifts into Code Set B twice where Labelary latches in and back out, so the two symbols hold the same message in a different pattern of modules. — not pinned by a test - A mode 3 field whose country code is 840 reads its postcode as the next six characters, where Labelary gathers nine digits across whatever separates them.
^BD3^FD403840HALLEIXYZencodes the postcodeHALLEIand the messageXYZhere, and on Labelary a postcode of no digits at all and a message of spaces. Measured against live Labelary on 2026-08-19; no corpus label reaches it. — not pinned by a test
Example
Mode 4, which encodes the field data whole with no carrier header:
^XA
^FO50,50
^BD4,1,1
^FDMaxiCode mode 4 data^FS
^XZ
Related commands
^FDField DataSupplies the text or barcode content of the field.^FOField OriginSets the top-left corner of the next field.^FTField TypesetPositions the next field by its text baseline rather than its top edge.^BXData Matrix Bar CodeCompact two-dimensional symbology used for small-part marking and GS1 DataMatrix.