zpl.tools
Bar codes

^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,t

Support

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

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 · integer

Which 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.tools

Position 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.tools

How 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 n and t, and ignores them. — not pinned by a test
  • ^BD has no orientation argument, and the renderer always draws it unrotated. A MaxiCode field after ^FWR stays 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 CD shifts 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^FD403840HALLEIXYZ encodes the postcode HALLEI and the message XYZ here, 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

Open in viewer