zpl.tools
Print control

^JM - Set Dots per Millimetre

Halves the print density, which doubles the physical size of everything on the label.

^JMB halves the printhead's dot density: 24 dots/mm becomes 12, 12 becomes 6, 8 becomes 4 and 6 becomes 3. The format does not change, but every coordinate and dimension in it covers twice the distance, so the label doubles in size. It scales the whole format, not one field, and it must appear before the first ^FS of the format.

Syntax

^JMa

Support

Supportedparsed: full · state: full · rendered: full

Every documented parameter renders the same output Labelary produces.

The parser reads the density letter and the renderer applies it. ^JMB halves the printhead's dot density, so one ZPL dot covers two device pixels and every coordinate, dimension, font height and barcode module doubles. The page keeps the size the request asked for, so a label authored for full density overflows it and the overflow is clipped, which is what Labelary does. The magnification reaches the finished page geometry and not the arguments. ^MU's inch and millimeter conversions still divide by the head's real resolution. The ^PW window is still centered on the unscaled page width. The density is a printer setting and survives ^XZ until a later ^JMA takes it back. A ^JM outside a format is ignored silently, and one past the format's first ^FS is ignored with Labelary's own report. Measured against Labelary on 2026-08-20 at 6, 8 and 12 dpmm. — not pinned by a test

Labelary renders this command.

Parameters

aDensityoptional · one of a set

Selects full or half the printhead's dot density. Half density doubles every coordinate and dimension on the label.

Default
the density already in force, or A at power-up
Invalid value
discarded, and the current value is kept. A letter outside A and B is reported as Value 'X' is not a valid density adjustment (A/B); used 'A' instead and leaves the density already in force in place. Labelary's message names A, but its render does not use it: ^XA^JMB^JMX...^XZ stays at half density (measured 2026-08-20).
Test coverage
No documented value is exercised by a test (A, B). The invalid-value rule is pinned by 2 tests.
A
Normal print density (default)
B
Half print density; all content doubles in size

Behavior

Half density doubles field origins as well as field sizes. Under ^JMB, ^FO50,50 lands where ^FO100,100 lands at full density, so a layout that fits at ^JMA runs off the label at ^JMB.

The guide states that the effect outlives the format. One ^JMB applies to every label after it until ^JMA or a power cycle, which makes it a risk in a shared prologue.

The guide names one casualty. Under ^JMB the UPS MaxiCode symbol falls out of specification, because a doubled module size breaks the geometry the symbology requires.

The renderer does not parse ^JM, so a format that uses it renders at full density. Every element sits at half its correct size and half its intended position, with nothing in the image to show the error. The only workaround is to write the doubled coordinates and sizes directly.

Differences from the specification

zpl.tools against Labelary

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

  • A density 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 is too long and was trimmed to one character; the report here names the character that survived, as in Value 'BB' is too long and was trimmed to 'B' (measured 2026-08-21). The wording is deliberate and belongs to ParseWarning::TrimmedArgument, so ^MC and ^PM read the same way, each adding that the value is a boolean. — pinned by test_jm_two_letter_density_reports_the_surviving_letter

Example

Half density:

^XA
^JMB
^FO50,50^A0N,40,40^FDHalf density^FS
^XZ

Open in viewer