Skip to content

Latest commit

 

History

History
280 lines (227 loc) · 15.9 KB

DATAMATRIX.md

File metadata and controls

280 lines (227 loc) · 15.9 KB

DataMatrix behavior and evidence

Documentation index

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.

ECC 200 dimensions

^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.

Fixed rows with automatic columns

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.

Independent evidence (2026-10-06)

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.

Regression coverage and provenance

  • tests/unit_datamatrix.rs covers 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.zpl is 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 under testdata/diffs/datamatrix_dimensions*.png. Original reference PNG SHA-256: 9d4cf784f94167b4b5edd81942dfec272806d6ef40605fda39707f9ae724b8d0.

References:

ECC 200 field data

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.

Legacy ECC 000-140

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.

Scope and byte handling

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.

Historical norms and source attribution

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.

Evidence and limitations

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 optional extract-photo-grids.mjs copies 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.

Printer observations

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.