Reference for supported DataMatrix variants, field-data rules, size selection, and the provenance and limits of their regression fixtures.
For the command overview, see the ZPL command matrix. For test commands and reference-update policy, see Testing.
^BX columns (c), rows (r), and aspect ratio (a) constrain both the
ordinary byte encoder and the explicit FNC1/ECI/structured-append codeword path.
The encoder must not replace an unsatisfied constraint with a square symbol.
Legacy-quality dispatch and the renderer's empty-field suppression are unchanged.
With c=0,r=12,a=2, select the standard 26-column symbol before fitting data.
Do not widen automatically to 36 columns when the input is too long.
The renderer skips a capacity-rejected field in this row-only rectangular mode
and continues rendering the rest of the label. Direct encoder APIs return an
error. Invalid dimensions, conflicting shapes, and malformed escape sequences
still return errors. This is deliberately not a general implementation of
^CV diagnostics or of all printer field-rejection behavior.
The selection rule uses the smallest matching standard symbol for fixed rows
with automatic columns. Labelary also confirms this at r=8 and r=16.
The operator also reported consistent physical results for the r8 and r16
controls below. Those reports establish symbol presence/absence, not decoded
module equality.
With both dimensions automatic, the encoder selects a fitting symbol. DMRE
sizes are not enabled.
Physical controls used a Zebra ZD421, 300 dpi, firmware V93.21.17Z, 50 x 25 mm
labels, darkness 15 and speed 2. Native fields used ^BX, not rendered graphics.
The operator confirmed that ABC at c0,r12,a2 and c26,r12,a2 prints the same
matrix, while ABCD12345678901234567890efgh at c0,r12,a2 prints no symbol.
The same long payload prints at explicit c36,r12,a2 in the comparison strip.
These are visual/operator observations, not a claim of independently decoded
module equality for every photograph.
Separate Labelary API calls confirmed the following boundary. All commands
used ^BXN,4,200,c,12,6,,2 and differed only in c and the listed field data.
| Payload | Characters | ASCII data codewords | c0 | c26 | c36 |
|---|---|---|---|---|---|
ABCD1234567890123490efgh |
24 | 16 | 26x12 | identical 26x12 | not tested |
ABCD12345678901234901efgh |
25 | 17 | no symbol | no symbol | 36x12 |
ABCD12345678901234567890efgh |
28 | 18 | no symbol | not tested | 36x12 |
This is a data-codeword limit, not a general 24-character limit. Numeric pairs consume one ASCII codeword each. Standard 26x12 has 16 data codewords; standard 36x12 has 22. Both are classic ECC200 rectangular sizes, not DMRE extensions. The tests establish observable size selection, not the printer's internal implementation or a universal rule for all Zebra firmware.
Additional Labelary controls used ^BXN,3,200,c,r,6,_,2:
| Rows | Payload | c0 | Small fixed width | Large fixed width |
|---|---|---|---|---|
| 8 | aB!cD?eF |
no symbol | c18: no symbol | c32: 32x8 |
| 16 | aB!cD?eF#gH%iJ&kLaB!cD?eF#gH%iJ&kL |
no symbol | c36: no symbol | c48: 48x16 |
Two native-only physical test labels were sent once at SD15/PR2. The operator reported that the printer behaves like Labelary for these controls.
tests/unit_datamatrix.rscovers fixed/partial/automatic dimensions, ratio conflicts, the exact capacity boundary, special codewords, magnification, field suppression without losing subsequent fields/labels, and error retention.testdata/unit/datamatrix_dimensions.zplis a text-free fixture with a separately fetched Labelary PNG. It includes successful and suppressed fields, the exact boundary pair, explicit 36x12, FNC1, and a trailing graphic. Its golden tolerance is 0.0%; references are not generated by Labelize.- Reference endpoint:
https://api.labelary.com/v1/printers/8dpmm/labels/4.005x8.01/0/. Render comparisons are undertestdata/diffs/datamatrix_dimensions*.png. Original reference PNG SHA-256:9d4cf784f94167b4b5edd81942dfec272806d6ef40605fda39707f9ae724b8d0.
References:
The ^BX renderer evaluates field-data escapes only for quality 200. The default
escape is _, matching the newer firmware behavior described in Zebra's
^BX reference.
An explicit g parameter overrides it. Only that character is active; _1 and
~1 are not both interpreted automatically. The old firmware's separate literal
tilde rules are not emulated. Labelize's existing command splitter also treats
raw tilde as a command prefix. Use, for example, ^CT! when testing an explicit
tilde escape in raw ZPL; context-sensitive command splitting is not changed here.
Processing order is the parser's existing ^FH handling, followed by DataMatrix
escape parsing. Literal/control/decimal bytes are distinct from function
codewords. _1 emits codeword 232; _d029 emits a data byte with value 29.
Only a leading FNC1 establishes GS1 DataMatrix. FNC1 inside the data becomes a
separator when decoded; an ordinary GS byte does not establish GS1.
Supported escape tokens are a doubled escape, control shifts, dNNN bytes,
PAD (0), FNC1 (1), structured-append FNC2 (2 plus three three-digit values),
FNC3 (3), and ECI (5NNN). Malformed or unsupported escapes return errors.
The direct barcodes::datamatrix::encode API remains literal; encode_zpl
accepts byte slices with ZPL escapes. EPL disables this escape processing.
Byte-only payloads retain the existing automatic encodation. Payloads containing function codewords use ASCII encodation with numeric-pair compaction and upper shift. The existing DataMatrix library supplies capacity selection, Reed-Solomon ECC, and placement. This path prioritizes correct function-codeword positions; it does not optimize C40/Text/Base256 segments around functions and may produce larger symbols. See dimension selection for capacity rules.
Validation includes exact byte/token/codeword tests and an independent RXing
matrix decoder. Tests assert both decoded content and the GS1 symbology modifier
(]d2), plus internal FNC1, literal GS, custom escapes, ^FH-generated controls,
upper-shift bytes, padding, and EPL isolation. These checks do not use rendered
images as replacement reference data.
This ECC 200 path is separate from the ECC 000-140 implementation documented in
DATAMATRIX.md#legacy-ecc-000-140; Legacy fields never pass through these substitutions.
It does not redesign the ECC 200 string-based ZPL input/charset pipeline: literal
text follows its existing UTF-8 behavior, and non-UTF-8 high-byte ^FH sequences
remain a separate parser limitation. Decimal escapes (_d000 through _d255)
and the byte-slice encode_zpl API preserve all 256 byte values.
ZPL BX with omitted/empty quality or explicit 0/000 uses ECC000. Qualities 50, 80, 100 and 140 use their respective convolutional encoders. Explicit ECC200 and EPL keep their own paths; no Legacy input silently becomes ECC200.
The raw barcodes::datamatrix_legacy::encode API accepts literal bytes for
ECC000; encode_with_ecc additionally selects 0, 50, 80, 100 or 140. Both accept
format 1..6 and an optional complete odd square size. The pipeline implements
format encoding, CRC, nine-bit record length, convolution where applicable,
ECC header, zero fill, randomization, placement and finder border.
| ECC | Input bits/cycle | Output bits/cycle | Flush cycles | Minimum side | Maximum format-6 bytes at 49x49 |
|---|---|---|---|---|---|
| 000 | 1 | 1 | 0 | 9 | 271 |
| 050 | 3 | 4 | 3 | 11 | 200 |
| 080 | 2 | 3 | 11 | 13 | 176 |
| 100 | 1 | 2 | 15 | 13 | 131 |
| 140 | 1 | 4 | 13 | 17 | 63 |
Convolution excludes the ECC header. A partial final input group is zero-padded, then the state is flushed before adding the header and filling the data area. Each call starts with zero state. Capacity errors never truncate the payload. Legacy dimensions follow the larger requested row/column value; values above 49 are automatic, invalid small/even dimensions and rectangular aspect requests are rejected. Existing orientation, position and module scaling are reused.
The ZPL parser preserves original field bytes through tokenization, FH decoding, field resets and stored-format recall. Literal transport CR/LF/TAB are ignored as before; FH can insert them after tokenization. Legacy consumes these bytes without additional character-set transcoding, including invalid UTF-8. Formats 1..5 still enforce their byte repertoires. Backslashes and pipes stay literal after FH, matching the scoped CI13/CI27 printer observations below. Parameter g is irrelevant for Legacy. The raw encoder never interprets ZPL escapes. ECC200 has its own field-data rules.
The label renderer skips empty DataMatrix fields and continues rendering the
rest of the label, including for omitted quality and all supported Legacy
qualities. For Legacy, preserved data_bytes determine emptiness when present;
otherwise the display text is used. ECC200 uses its existing text path. This
label-level behavior is separate from the raw APIs: raw Legacy encoding rejects
empty input, while raw ECC200 encoding produces a minimal symbol.
BarcodeDatamatrixWithData, RecalledFieldData and
RecalledField carry data_bytes: Option<Vec<u8>>. Struct-literal callers must
initialize it. Parsed Legacy bytes are authoritative: callers replacing data
must update or clear data_bytes. Manual ASCII fields may use None; manual
non-ASCII Legacy fields require explicit bytes. EPL retains its ECC200 text path.
Development worked directly from ISO/IEC 16022:2000, the FCD draft for the next edition and GOST R ISO/IEC 16022-2008: Annex H (placement), I (formats), J (CRC), K (convolution), L (randomization), and Q (intermediate example values). Standard PDFs are not distributed here. Source SHA-256 identifiers:
- FCD:
c618f525c53d884614237349e0e83a85dd4378bca6ad64982d484bec6242fcaa - GOST:
ce038dcde4cd82c8626b9782abb2c379868f7d9bd709dd836cd69c5459b9888d - ISO 2000:
3726076a792673241f9dfef2253e6e1dc815573e3fd564cd176638001ba2d317
The encoder and placement generator are ported from the author's own
QR Atelier, commit
432aa76de2116adc055062f91ef622f4dc9568cb, files libs/DMlegacy.js and
libs/DMlegacyPlacementGenerator.js. Copyright 2026 David Block. This port
uses the MIT option of MIT OR Apache-2.0; see the included
license and source headers.
The pinned source already includes random byte BC at offset 211 and the ECC100 correction (no delay-12 tap). Rust uses byte slices, BitMatrix and a bounded lazy cache. Placement uses filtered bit reversal, inverse permutation, cyclic row shifts and corner swaps. This formula was empirically reconstructed and checked for all 21 supported sizes; it is not a normative algorithm or a proof beyond that finite domain. Production generates/caches grids; fixed grids are test-only.
There is no independent overall verification of the Legacy encoder. Our JavaScript encoder and our own decoder can share interpretation mistakes. Independent RXing checks concern ECC200, not Legacy.
Fixed references in testdata/legacy/ distinguish the evidence:
placement-grids.txt: all 21 grids / 18,389 cells extracted from the FCD PDF with two methods sharing that source, supplemented by visual edition checks.ecc050-annex-q-matrix.txt: the published 13x13 ECC050 / format3 / AB12-X example. Its 96 protected bits are also tested; the CRC example is 7559.ecc000-printer-matrices.txt: three recorded 200/300/400-digit matrices, sides 31/35/41. The 200-digit case came from a Zebra photo; the older 300/400 records do not independently identify their encoder.ecc000-js-port-vectors.txt: 12 same-author JS outputs, six formats and automatic/49-module sizes. These are port regressions, not independent evidence.convolution-js-stages.txt: 200 same-source stage vectors, lengths 0..49 for each quality. Input bit i is 1 when(i*i + 3*i + 7) % 11 < 5.convolution-js-matrices.txt: 48 same-source outputs, four qualities and six formats, automatic/49-module sizes; format6 includes 00/80/FF/96/01 bytes.ci13-ci27-photo-grids.txt: 12 sampled 23x23 grids / 6,348 modules from the field probes. The optionalextract-photo-grids.mjscopies measured rows from observation JSON, never calls an encoder. Geometry, thresholds and photo hashes are preserved there; these are scoped comparisons, not independent decoding.
Fixtures are frozen and read-only in tests. Stage rows use
quality|input_bits|protected_bits; convolution matrix headers use
quality|format|side|payload_hex followed by binary rows. JS vectors were produced
from the pinned source exports encodeLegacyEcc050/080/100/140 and
generateLegacyDataMatrix, not regenerated by Rust tests.
The recorded ZD421 at 300 dpi, firmware V93.21.17Z, prints Legacy symbols. ECC000/format6 tests with CI13 and CI27 preserve backslash-ampersand, doubled backslashes and doubled pipes literally. FH 0D/0A produces CR/LF. Each of the 12 sampled matrices agrees with its known-byte candidate at all 529 modules. The BX prose suggests PDF417-like substitutions, but applying those substitutions contradicted these observations. Other ECC levels/firmware are not hardware-verified by this experiment; cross-quality tests establish implementation consistency only.
For repeated format1 digit 1, 500 digits print with AUTO and requested 49x49. The supplied N500/N501 photo shows INVALID-L in both N501 fields. The contributor reports the same rejection for 511, 512, 596 and 597. Rejected fields print no DataMatrix; with CV active the printer emits INVALID-L. These observations do not establish a universal 500-character cap. Model/firmware context was retained from the experiment, not freshly queried for each photo. Reproducible ZPL and scoped results are in the printer study.
The generic encoder accepts fitting records through 511 and rejects longer records explicitly. FCD section 6.5.3 (printed p24 / PDF p32) specifies a nine-bit character count, but Table G.1 (printed p68 / PDF p76) lists 560/596 numeric characters at sides 47/49. Both pages were visually checked. Zebra also lists 596. No extended record was produced by this printer experiment, so wrapping the count, adding a tenth bit or claiming full 596 support is not justified. All other 29 Zebra maximum-field table cells are tested at and above capacity.
Device-specific field suppression and CV diagnostic rendering are separate follow-ups. Labelize returns rendering errors for unsupported input. The 501..511 generic behavior deliberately differs from the tested device's restriction.
ISO/IEC 16022:2024 removed Legacy ECC000-140, but existing printer commands still use it. This is compatibility work, not a recommendation for new labels or a claim about every modern Zebra device.