zpl.tools
Bar codes

^B4 - Code 49 Bar Code

Stacked alphanumeric symbology from the 1980s, superseded by PDF417 and Data Matrix.

^B4 prints a Code 49 symbol: two to eight stacked rows, each four symbol characters wide, with one-module bars between the rows. It encodes the full 128-character ASCII set. Code 49 was an early attempt to put a large amount of data into a small area, and ^B7 PDF417 and ^BX Data Matrix replaced it completely. There is no reason to choose Code 49 for new work.

Syntax

^B4o,h,f,m

Support

Not implementedparsed: full · state: full · rendered: none

Recognised as valid ZPL and not implemented, with no plan to.

Labelary does not implement ^B4 either, so there is no compatibility target to match and no plan to add one. The renderer draws nothing and reports a diagnostic. Use ^B7 PDF417 or ^BX Data Matrix instead; both hold more data and scan more reliably. — pinned by test_b4_does_not_fall_back_to_text, test_b4_draws_nothing_at_all, test_b4_reports_an_unrendered_barcode_diagnostic, test_b4_reports_once_per_field

Labelary does not implement it either, so there is no parity reference.

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
hRow height multiplieroptional · integer

The renderer multiplies it by the ^BY module width for the height of one row.

Default
1
Accepted range
1 to the label height
Invalid value
not documented, and we have not measured it. The stated range is "1 to the label height", which is not a static bound. Labelary does not implement ^B4, so no measurement target exists.
Test coverage
No test at the minimum, maximum or one step outside the range.
fInterpretation lineoptional · one of a set

Where the renderer places the human-readable text. Unlike the linear symbologies, this is one argument rather than two flags.

Default
N
Invalid value
discarded, and the default applies. The renderer reports an unrecognized character. The renderer prints no text.
Test coverage
No documented value is exercised by a test (N, A, B). The invalid-value rule is not pinned by a test.
N
No text (default)
A
Above the symbol
B
Below the symbol
mStarting modeoptional · one of a set

Which Code 49 encoding mode the data starts in.

Default
A
Invalid value
discarded, and the default applies. An unrecognized value defaults to automatic optimization.
Test coverage
No documented value is exercised by a test (0, 1, 2, 3, 4, 5, A). The invalid-value rule is not pinned by a test.
0
Regular alphanumeric mode
1
Multiple-read alphanumeric mode
2
Numeric mode
3
Group alphanumeric mode
4
Alphanumeric shift 1
5
Alphanumeric shift 2
A
Automatic optimization (default)

Behavior

Two details set Code 49 apart from the linear symbologies. The interpretation line is one argument with three values and not two flags: A and B place the line above or below the symbol, and N removes it. A scanner can also read the rows in any order, because each row carries its own position.

The starting mode is the usual source of trouble in a Code 49 format. At the default A the printer analyzes the field data, chooses the mode, makes the character shifts and compacts the data. A mode from 0 to 5 requires the field data to be valid Code 49 internal characters, which are not ASCII. Lower case and most punctuation need a two-character sequence of a shift character and an unshifted character, so a lower-case a is sent as >A.

An invalid sequence is not an error. The formatter stops there and prints a symbol with only the data before it.

The height argument is a multiplier and not a height. The printer multiplies it by the ^BY module width to get the height of one row. The guide does not recommend the value 1. Code 49 has a fixed print ratio, so the ^BY ratio argument has no effect.

Code 49 offers no alternative of its own. Use ^B7 PDF417 or ^BX Data Matrix. Both hold more data, scan more reliably, and render today.

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 row-height multiplier default as the value that ^BY sets. Labelary documents a default of 1. Neither engine implements the command, so no measurement exists. — 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

zpl.tools against Labelary

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

  • Labelary does not draw a symbol either, but prints the field data on the label as an ordinary text run with no warning (measured 2026-07-30). The renderer draws nothing and reports a diagnostic, so a label that looks plausible on Labelary comes back empty. — not pinned by a test

Example

The guide's own example, in automatic mode with the interpretation line above the symbol:

^XA
^FO50,50^BY3
^B4N,20,A,A
^FD12345ABCDE^FS
^XZ

Open in viewer

  • ^B7 PDF417 Bar CodeStacked two-dimensional symbology that holds several hundred bytes, used on driving licenses and shipping documents.
  • ^BX Data Matrix Bar CodeCompact two-dimensional symbology used for small-part marking and GS1 DataMatrix.
  • ^BB Codablock Bar CodeStacked Code 128 variant that holds more data than one linear row, superseded by PDF417.