Summary
BarcodePDF417 emits invalid symbols whenever the symbol's row count is not a multiple of 3. The left row indicator codewords encode a different row count than the right row indicator codewords, so spec-compliant decoders (zxing, hardware scanners) reject the barcode entirely. Affects 2.2.9 (latest) and current master.
UUID serials that happened to compact to a 5-data-column × 8-row grid never scanned, while serials landing on 9-row grids always did — so failures looked random and data-dependent.
Root cause
In lib/src/pdf417.dart, _getLeftCodeWord, the cluster-0 case:
switch (tableId) {
case 0:
x = (rows - 3) ~/ 3; // ← should be (rows - 1) ~/ 3
break;
ISO/IEC 15438 defines the left row indicator for cluster-0 rows as 30 * (rowNum ~/ 3) + (rows - 1) ~/ 3 (see also zxing's PDF417.encodeChar row indicator logic). _getRightCodeWord already uses the correct (rows - 1) ~/ 3 in its cluster-1 case.
(rows - 3) ~/ 3 == (rows - 1) ~/ 3 only when rows % 3 == 0 — which is why the bug is invisible for many payloads and then breaks others completely.
Reproduction
Any input that produces a row count not divisible by 3, e.g.:
final bc = Barcode.pdf417(moduleHeight: 4.0, preferredRatio: 154.0 / 36.0);
final svg = bc.toSvg('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'); // 4 cols × 8 rows
Render the output and try any PDF417 decoder (e.g. zxing) — it fails to decode. Decoding the raw codewords from the rendered symbol (8 rows, ECC level 2) shows:
- left row indicators (rows 0/3/6, cluster 0):
1, 31, 61 → encode 7 rows
- right row indicators (rows 1/4/7, cluster 3):
2, 32, 62 → encode 8 rows
Patching only the three left indicator codewords in the rendered image to 2, 32, 62 makes the same image decode correctly to the original string, confirming the single-line root cause.
Fix
case 0:
x = (rows - 1) ~/ 3;
break;
PR with the fix and a regression test (validates all left/right row indicators against the spec formulas across several payload lengths and security levels, covering rows % 3 ∈ {0, 1, 2}): #79
Summary
BarcodePDF417emits invalid symbols whenever the symbol's row count is not a multiple of 3. The left row indicator codewords encode a different row count than the right row indicator codewords, so spec-compliant decoders (zxing, hardware scanners) reject the barcode entirely. Affects 2.2.9 (latest) and current master.UUID serials that happened to compact to a 5-data-column × 8-row grid never scanned, while serials landing on 9-row grids always did — so failures looked random and data-dependent.
Root cause
In
lib/src/pdf417.dart,_getLeftCodeWord, the cluster-0 case:ISO/IEC 15438 defines the left row indicator for cluster-0 rows as
30 * (rowNum ~/ 3) + (rows - 1) ~/ 3(see also zxing'sPDF417.encodeCharrow indicator logic)._getRightCodeWordalready uses the correct(rows - 1) ~/ 3in its cluster-1 case.(rows - 3) ~/ 3 == (rows - 1) ~/ 3only whenrows % 3 == 0— which is why the bug is invisible for many payloads and then breaks others completely.Reproduction
Any input that produces a row count not divisible by 3, e.g.:
Render the output and try any PDF417 decoder (e.g. zxing) — it fails to decode. Decoding the raw codewords from the rendered symbol (8 rows, ECC level 2) shows:
1, 31, 61→ encode 7 rows2, 32, 62→ encode 8 rowsPatching only the three left indicator codewords in the rendered image to
2, 32, 62makes the same image decode correctly to the original string, confirming the single-line root cause.Fix
PR with the fix and a regression test (validates all left/right row indicators against the spec formulas across several payload lengths and security levels, covering rows % 3 ∈ {0, 1, 2}): #79