zpl.tools
Bar codes

^BQ - QR Code Bar Code

The common two-dimensional matrix symbology. Its error correction and input mode are set by a prefix on the field data, not by the command.

^BQ prints a QR Code: a square matrix with position patterns at three corners, so a scanner finds it at any angle. It is the two-dimensional symbology for a label that a phone reads, and it tolerates damage better than ^BX Data Matrix at the same size. Most of its configuration is not in the command. The error-correction level and the input mode are a prefix on the field data.

Syntax

^BQo,m,g,e,k

Support

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

The command renders, with the caveat below.

Model 2 renders, with the error correction and input mode from the ^FD prefix. The renderer does not implement model 1, mixed mode and Kanji mode. Labelary does not implement them either, so there is no compatibility gap. The renderer parses the m model argument and ignores it. — not pinned by a test

Labelary renders this command.

Parameters

oBar code orientationoptional · one of a set

For backward compatibility only. Labelary documents N as the only accepted value.

Default
N
Invalid value
discarded, and the default applies. The renderer reports an unrecognized character, and uses N.
Test coverage
No documented value is exercised by a test (N). The invalid-value rule is not pinned by a test.
N
No rotation (default)
mModeloptional · integernot honoured by zpl.tools

QR Code model. Model 1 is the original specification, and it is deprecated. Use model 2.

Default
2
Accepted range
1 to 2
Invalid value
not documented, and we have not measured it. Labelary treats this as a discrete pair with its own messages. 0 resets to 2, 3 clamps to 2, and 1 gets a deprecation notice (measured 2026-07-12). The renderer accepts the value without validation and always encodes model 2.
Test coverage
No test at the minimum, maximum or one step outside the range.
gMagnificationoptional · integer

Module size in dots. This argument is the only size control, since the ^BY module width has no effect on QR Code.

Default
2
Accepted range
1 to 100
Invalid value
clamped to the nearest allowed value. This range is asymmetric. 0 resets to the default of 2, while a value above 100 clamps to 100 (measured against Labelary 2026-07-12). The guide's stated maximum of 10 is not the real limit.
Test coverage
Bounds pinned: minimum (1), below the minimum (0), above the maximum (999). No test at the maximum. The invalid-value rule is not pinned by a test.
eError correction leveloptional · one of a set

Default error-correction level, for field data with no level prefix of its own.

Default
Q
Invalid value
discarded, and the default applies. An unrecognized level defaults to Q.
Test coverage
Values exercised: H. Not exercised: Q, M, L. The invalid-value rule is not pinned by a test.
H
Highest reliability, about 30% of codewords recoverable
Q
High reliability, about 25% (default)
M
Medium reliability, about 15%
L
Lower reliability, about 7%
kMask patternoptional · integer

Mask for the module grid.

Default
7
Accepted range
0 to 7
Invalid value
not possible — any value is accepted. Labelary reports no warning for values outside 0-7: 0, 8 and 15 are all silent (measured 2026-07-12). The renderer does not validate this argument.
Test coverage
Bounds pinned: above the maximum (8). No test at the minimum or maximum.

Behavior

The field-data prefix

The guide's syntax for the field data is an error-correction level, then an input mode, then a comma, then the payload:

^FD<level><mode>,<data>

The level is one of H, Q, M or L, in decreasing order of recoverable damage and increasing order of density. The mode is A for automatic or M for manual. Under automatic input the encoder selects the character mode. Manual input needs an explicit character mode immediately before the data:

  • N for numeric;
  • A for alphanumeric;
  • B and a four-digit byte count for 8-bit data;
  • K for Kanji.

^FDQA,0123456789ABCD is therefore automatic, and ^FDHM,N123456789012345 is manual numeric at the highest error-correction level.

The prefix is not optional. We measured this against Labelary on 2026-07-30. The encoder consumes the first two characters of the field data as the level and the mode, whether or not they name a valid pair. ^FD12345 renders exactly the same symbol as ^FDQA,345. A payload with no prefix therefore loses its first two characters, silently and with no warning. Always write the prefix.

The e argument is only a default for data that carries no level of its own. Do not depend on that state.

Symbol size

^BY has no effect on QR Code, so the magnification argument is the only size control: it is the width of one module in dots. The encoder selects the version and the mask from the payload and the error-correction level. A longer payload therefore gives a denser grid at the same magnification and not a larger symbol.

Use model 2. Model 1 is the original specification and is deprecated, and the renderer does not encode it whatever the m argument says.

The guide documents mixed mode, the D switch that splits one message across several symbols with a parity byte. The renderer implements neither mixed mode nor Kanji mode. Labelary implements neither, so a format that works there works here.

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 gives the magnification range as 1-10. Labelary accepts up to 100 and scales linearly throughout (measured 2026-07-12); the renderer follows Labelary. — pinned by test_bq_magnification_above_max_clamps
  • Labelary raises the error-correction level to the highest one the chosen version still holds, so LA,LLPpallet renders as version 1-Q (measured 2026-08-17); the renderer follows Labelary. — not pinned by a test
  • Labelary cuts the payload into segments by Okapi Barcode's run-length heuristic rather than the shortest encoding. One measured payload costs 826 bits where 808 would do (2026-08-17). The renderer follows Labelary. — not pinned by a test
  • The ^BY bar-code height is the gap Labelary leaves between an ^FO origin and the symbol's top edge, at least one dot (measured 2026-08-17); the renderer follows Labelary. — not pinned by a test

zpl.tools against the ZPL II guide

Where our renderer behaves differently from the specification.

  • The renderer never encodes model 1, whatever m says. — not pinned by a test
  • The renderer accepts R, I and B in the orientation argument. It leaves the symbol upright and turns only the field box, which is what Labelary does; the guide accepts N only. — 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.

  • Labelary ignores the e argument and takes the level from the field-data prefix alone (measured 2026-08-17). The renderer takes e as the level for data with no prefix. — not pinned by a test
  • Labelary ignores the k mask argument and always evaluates all eight masks (measured 2026-08-17). The renderer draws the requested mask. — not pinned by a test
  • Labelary drops the first three characters of the field data whether or not they are a valid prefix, and defaults to level M (measured 2026-08-17). The renderer encodes unprefixed data whole. — not pinned by a test

Example

A URL at high reliability with automatic input mode. The QA, prefix is the part to copy — drop it and the first two characters of the URL are read as the level and mode instead of being encoded.

^XA
^FO50,50
^BQN,2,6
^FDQA,https://example.com^FS
^XZ

Open in viewer