Skip to content

PDF417: left row indicators encode wrong row count when rows % 3 != 0, producing unscannable barcodes #78

Description

@danto0

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions