CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zxing--library

TypeScript port of ZXing multi-format 1D/2D barcode image processing library

Overview
Eval results
Files

pdf417.mddocs/reference/

PDF417

PDF417 is a stacked linear barcode format that can encode large amounts of data including text, numbers, and binary data. The format uses Reed-Solomon error correction to recover from damage and supports structured append for splitting data across multiple symbols.

Package Information

  • Package Name: @zxing/library
  • Module: PDF417 (core/pdf417/)
  • Language: TypeScript
  • Standard: ISO/IEC 15438
  • Error Correction: Reed-Solomon over GF(929)
  • Capacity: Up to 1850 text characters, 2710 digits, or 1108 bytes per symbol

Core Imports

import {
  // Reader
  PDF417Reader,
  
  // Metadata
  PDF417ResultMetadata,
  
  // Decoder components
  PDF417DecodedBitStreamParser,
  PDF417DecoderErrorCorrection,
  
  // Supporting types
  BarcodeFormat,
  DecodeHintType,
  Result,
  ResultMetadataType,
  BinaryBitmap
} from "@zxing/library";

Basic Usage

import {
  BinaryBitmap,
  HybridBinarizer,
  PDF417Reader,
  RGBLuminanceSource,
  Result,
  ResultMetadataType,
  PDF417ResultMetadata
} from "@zxing/library";

// Decode a PDF417 barcode from image data
const luminanceSource = new RGBLuminanceSource(
  imageData,  // Uint8ClampedArray
  width,      // number
  height      // number
);
const binarizer = new HybridBinarizer(luminanceSource);
const bitmap = new BinaryBitmap(binarizer);

const reader = new PDF417Reader();
try {
  const result: Result = reader.decode(bitmap);

  console.log("Decoded text:", result.getText());
  console.log("Format:", result.getBarcodeFormat()); // BarcodeFormat.PDF_417

  // Access PDF417-specific metadata
  const metadata = result.getResultMetadata()?.get(
    ResultMetadataType.PDF417_EXTRA_METADATA
  );
  
  if (metadata instanceof PDF417ResultMetadata) {
    console.log("Segment index:", metadata.getSegmentIndex());
    console.log("Segment count:", metadata.getSegmentCount());
    console.log("File ID:", metadata.getFileId());
    console.log("Is last segment:", metadata.isLastSegment());
    
    // File transfer metadata (if present)
    const fileName = metadata.getFileName();
    if (fileName) {
      console.log("File name:", fileName);
      console.log("File size:", metadata.getFileSize(), "bytes");
      console.log("Checksum:", metadata.getChecksum());
      
      const timestamp = metadata.getTimestamp();
      if (timestamp !== -1) {
        const date = new Date(timestamp * 1000);
        console.log("Timestamp:", date.toISOString());
      }
    }
  }
} catch (error) {
  console.error("PDF417 decode failed:", error);
}

Architecture

The PDF417 module is structured around several key components:

  • Reader: PDF417Reader for decoding PDF417 barcodes with single and multiple barcode support
  • Detector: Pattern detection and localization in images
  • Decoder: Bit stream parsing, error correction, and data extraction
  • Bit Stream Parser: Handles text, byte, and numeric compaction modes
  • Error Correction: Reed-Solomon-based error correction over GF(929) specific to PDF417
  • Metadata: PDF417ResultMetadata for structured append and file transfer information

The decoding process follows these stages:

  1. Detection: Locate PDF417 patterns (start/stop patterns, row indicators)
  2. Sampling: Extract codewords from the detected pattern row by row
  3. Error Correction: Apply Reed-Solomon error correction to recover from damage
  4. Decoding: Parse the bit stream and extract data using compaction modes
  5. Metadata: Extract structured append and file transfer metadata

Capabilities

PDF417Reader

Decodes PDF417 barcodes from images, supporting both single and multiple barcode detection.

/**
 * PDF417 barcode reader
 * Can detect and decode single or multiple PDF417 codes in images
 * Implements both Reader and MultipleBarcodeReader interfaces
 */
class PDF417Reader implements Reader, MultipleBarcodeReader {
  /**
   * Locates and decodes a single PDF417 code in an image
   * @param image - BinaryBitmap: binary bitmap of the image to decode
   * @param hints - Map<DecodeHintType, any>: optional decoding hints
   *   - PURE_BARCODE: boolean for pure barcode (faster, no detection needed)
   *   - TRY_HARDER: boolean for more thorough scanning
   *   - CHARACTER_SET: string for text encoding
   * @returns Result: decoded text and metadata
   * @throws NotFoundException if no PDF417 code is found
   * @throws FormatException if PDF417 code structure is invalid
   * @throws ChecksumException if error correction fails (too many errors)
   */
  decode(image: BinaryBitmap, hints?: Map<DecodeHintType, any>): Result;

  /**
   * Locates and decodes multiple PDF417 codes in an image
   * Useful for documents containing multiple PDF417 barcodes
   * @param image - BinaryBitmap: binary bitmap of the image to decode
   * @param hints - Map<DecodeHintType, any>: optional decoding hints
   * @returns Result[]: array of results, one for each detected barcode
   * @throws NotFoundException if no PDF417 codes are found
   */
  decodeMultiple(
    image: BinaryBitmap,
    hints?: Map<DecodeHintType, any>
  ): Result[];

  /**
   * Resets the reader state (no-op for PDF417)
   */
  reset(): void;
}

Usage Examples:

import {
  PDF417Reader,
  BinaryBitmap,
  HybridBinarizer,
  GlobalHistogramBinarizer,
  RGBLuminanceSource,
  DecodeHintType,
  BarcodeFormat,
  Result,
  ResultPoint,
  ResultMetadataType
} from "@zxing/library";

// Basic single barcode decoding
const reader = new PDF417Reader();
try {
  const result: Result = reader.decode(bitmap);
  console.log("Decoded:", result.getText());
  console.log("Format:", result.getBarcodeFormat()); // BarcodeFormat.PDF_417
} catch (error) {
  console.error("PDF417 decode failed:", error);
}

// Decode multiple PDF417 barcodes in one image
try {
  const results: Result[] = reader.decodeMultiple(bitmap);
  
  console.log(`Found ${results.length} PDF417 barcodes`);
  results.forEach((result, index) => {
    console.log(`Barcode ${index + 1}:`, result.getText());
    console.log(`  Length: ${result.getText().length} characters`);
  });
} catch (error) {
  console.error("Multiple barcode decode failed:", error);
}

// Decode with hints
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.PURE_BARCODE, true);  // Pure barcode (faster)
hints.set(DecodeHintType.TRY_HARDER, true);    // More thorough
hints.set(DecodeHintType.CHARACTER_SET, "UTF-8");

try {
  const result: Result = reader.decode(bitmap, hints);
  console.log("Decoded with hints:", result.getText());
} catch (error) {
  console.error("Decode with hints failed:", error);
}

// Access result points (corners of detected barcode)
const result: Result = reader.decode(bitmap);
const points: ResultPoint[] = result.getResultPoints();

console.log(`Found ${points.length} corner points`);
points.forEach((point, i) => {
  console.log(`Point ${i}: (${point.getX()}, ${point.getY()})`);
});

// PDF417 typically returns 4-8 points:
// Points 0-3: Outer boundary corners (clockwise from top-left)
// Points 4-7: Inner boundary points (if available)

if (points.length >= 4) {
  // Calculate barcode dimensions
  const width = ResultPoint.distance(points[0], points[1]);
  const height = ResultPoint.distance(points[0], points[3]);
  console.log(`Barcode dimensions: ${width.toFixed(1)} × ${height.toFixed(1)} pixels`);
}

// Check error correction level
const ecLevel = result.getResultMetadata()?.get(
  ResultMetadataType.ERROR_CORRECTION_LEVEL
);
if (ecLevel !== undefined) {
  console.log("Error correction level:", ecLevel); // 0-8
}

// Use GlobalHistogramBinarizer for faster processing (less accurate)
const fastSource = new RGBLuminanceSource(imageData, width, height);
const fastBinarizer = new GlobalHistogramBinarizer(fastSource);
const fastBitmap = new BinaryBitmap(fastBinarizer);

try {
  const fastResult: Result = reader.decode(fastBitmap);
  console.log("Fast decode:", fastResult.getText());
} catch (error) {
  console.error("Fast decode failed, falling back to HybridBinarizer");
  
  // Fall back to more accurate binarizer
  const accurateBitmap = new BinaryBitmap(new HybridBinarizer(fastSource));
  const accurateResult: Result = reader.decode(accurateBitmap);
}

PDF417ResultMetadata

Contains metadata specific to PDF417 barcodes, including structured append information for multi-symbol messages and optional file transfer data.

/**
 * Metadata for PDF417 barcode results
 * Contains information about structured append sequences and file transfer metadata
 * Used for multi-symbol messages and file encoding
 */
class PDF417ResultMetadata {
  /**
   * Get the segment index within a structured append sequence
   * @returns number: zero-based segment index
   */
  getSegmentIndex(): number;

  /**
   * Set the segment index
   * @param segmentIndex - number: zero-based segment index
   */
  setSegmentIndex(segmentIndex: number): void;

  /**
   * Get the file ID that is the same for all related PDF417 symbols
   * Used to group multi-symbol messages
   * @returns string: file ID string
   */
  getFileId(): string;

  /**
   * Set the file ID
   * @param fileId - string: file ID that identifies related symbols
   */
  setFileId(fileId: string): void;

  /**
   * Check if this is the last segment in a sequence
   * @returns boolean: true if this is the last segment
   */
  isLastSegment(): boolean;

  /**
   * Set whether this is the last segment
   * @param lastSegment - boolean: true if last segment
   */
  setLastSegment(lastSegment: boolean): void;

  /**
   * Get the total number of segments in the sequence
   * @returns number: segment count, or -1 if not set
   */
  getSegmentCount(): number;

  /**
   * Set the total segment count
   * @param segmentCount - number: total number of segments in sequence
   */
  setSegmentCount(segmentCount: number): void;

  /**
   * Get the sender information from file transfer metadata
   * @returns string: sender identifier or null
   */
  getSender(): string;

  /**
   * Set the sender information
   * @param sender - string: sender identifier
   */
  setSender(sender: string): void;

  /**
   * Get the addressee from file transfer metadata
   * @returns string: addressee identifier or null
   */
  getAddressee(): string;

  /**
   * Set the addressee
   * @param addressee - string: addressee identifier
   */
  setAddressee(addressee: string): void;

  /**
   * Get the filename from file transfer metadata
   * @returns string: filename
   */
  getFileName(): string;

  /**
   * Set the filename
   * @param fileName - string: name of the encoded file
   */
  setFileName(fileName: string): void;

  /**
   * Get the file size in bytes
   * @returns number: file size in bytes, or -1 if not set
   */
  getFileSize(): number;

  /**
   * Set the file size
   * @param fileSize - number: size in bytes
   */
  setFileSize(fileSize: number): void;

  /**
   * Get the Unix epoch timestamp (seconds since 1970-01-01)
   * @returns number: timestamp in seconds, or -1 if not set
   */
  getTimestamp(): number;

  /**
   * Set the Unix epoch timestamp
   * @param timestamp - number: seconds since 1970-01-01
   */
  setTimestamp(timestamp: number): void;

  /**
   * Get the 16-bit CRC checksum using CCITT-16 algorithm
   * @returns number: checksum value (0-65535), or -1 if not set
   */
  getChecksum(): number;

  /**
   * Set the CRC checksum
   * @param checksum - number: CCITT-16 checksum value
   */
  setChecksum(checksum: number): void;

  /**
   * Get optional data in legacy format
   * @returns Int32Array: optional data array
   * @deprecated Use dedicated parsed fields instead
   */
  getOptionalData(): Int32Array;

  /**
   * Set optional data in legacy format
   * @param optionalData - Int32Array: optional data as int array
   * @deprecated Parse and use specific fields instead
   */
  setOptionalData(optionalData: Int32Array): void;
}

Usage Examples:

import {
  PDF417Reader,
  PDF417ResultMetadata,
  ResultMetadataType,
  BinaryBitmap,
  Result
} from "@zxing/library";

// Decode and access metadata
const reader = new PDF417Reader();
const result: Result = reader.decode(bitmap);

const metadata = result.getResultMetadata()?.get(
  ResultMetadataType.PDF417_EXTRA_METADATA
);

if (metadata instanceof PDF417ResultMetadata) {
  // Check for structured append (multi-symbol message)
  const segmentIndex: number = metadata.getSegmentIndex();
  const segmentCount: number = metadata.getSegmentCount();
  const isLast: boolean = metadata.isLastSegment();

  if (segmentCount > 0) {
    console.log(`Segment ${segmentIndex + 1} of ${segmentCount}`);
    console.log("Last segment:", isLast);
    console.log("File ID:", metadata.getFileId());
  }

  // Access file transfer metadata (if present)
  const fileName: string = metadata.getFileName();
  if (fileName) {
    console.log("File name:", fileName);
    
    const fileSize: number = metadata.getFileSize();
    if (fileSize !== -1) {
      console.log("File size:", fileSize, "bytes");
    }
    
    const checksum: number = metadata.getChecksum();
    if (checksum !== -1) {
      console.log("Checksum (CCITT-16):", checksum.toString(16));
    }

    const timestamp: number = metadata.getTimestamp();
    if (timestamp !== -1) {
      const date = new Date(timestamp * 1000);
      console.log("Timestamp:", date.toISOString());
      console.log("Date:", date.toLocaleString());
    }
  }

  // Access sender/receiver info (if present)
  const sender: string = metadata.getSender();
  const addressee: string = metadata.getAddressee();
  
  if (sender) {
    console.log("Sender:", sender);
  }
  if (addressee) {
    console.log("Addressee:", addressee);
  }
}

// Reconstruct multi-segment message
interface SegmentInfo {
  index: number;
  text: string;
  metadata: PDF417ResultMetadata;
}

const segments = new Map<string, SegmentInfo[]>(); // Keyed by file ID

// Decode multiple barcodes and collect segments
const results: Result[] = reader.decodeMultiple(bitmap);

for (const result of results) {
  const meta = result.getResultMetadata()?.get(
    ResultMetadataType.PDF417_EXTRA_METADATA
  );

  if (meta instanceof PDF417ResultMetadata) {
    const fileId: string = meta.getFileId();
    const index: number = meta.getSegmentIndex();

    // Initialize segment array for this file ID
    if (!segments.has(fileId)) {
      segments.set(fileId, []);
    }

    segments.get(fileId)!.push({
      index,
      text: result.getText(),
      metadata: meta
    });
  }
}

// Reconstruct complete message for each file
segments.forEach((segmentList, fileId) => {
  // Sort by index
  segmentList.sort((a, b) => a.index - b.index);

  const segmentCount = segmentList[0].metadata.getSegmentCount();
  
  // Check if all segments received
  if (segmentList.length === segmentCount) {
    // Concatenate data
    let completeMessage = "";
    for (const segment of segmentList) {
      completeMessage += segment.text;
    }

    console.log(`File ${fileId}: complete (${completeMessage.length} characters)`);
    console.log("Complete message:", completeMessage);
  } else {
    console.log(`File ${fileId}: incomplete (${segmentList.length}/${segmentCount} segments)`);
  }
});

PDF417DecodedBitStreamParser

Parses the decoded codewords from a PDF417 barcode into text, handling various compaction modes including text, byte, and numeric encoding.

/**
 * Parser for decoding PDF417 bit streams from codewords
 * Handles text, byte, and numeric compaction modes
 * Also handles character set ECIs and macro PDF417 control blocks
 */
class PDF417DecodedBitStreamParser {
  /**
   * Decode codewords into text and metadata
   * @param codewords - Int32Array: array of decoded codewords from the barcode
   * @param ecLevel - string: error correction level as string ("0"-"8")
   * @returns DecoderResult: decoded text, byte segments, and metadata
   * @throws FormatException if the bit stream format is invalid
   */
  static decode(
    codewords: Int32Array,
    ecLevel: string
  ): DecoderResult;
}

Compaction Modes:

PDF417 uses different compaction modes to efficiently encode different types of data:

  1. Text Compaction (mode 900): Encodes alphanumeric text using sub-modes

    • Alpha: Uppercase letters A-Z and space
    • Lower: Lowercase letters a-z
    • Mixed: Digits 0-9 and punctuation
    • Punct: Special punctuation characters
    • Switching between sub-modes with shift/latch codes
  2. Byte Compaction (modes 901, 924): Encodes raw binary data

    • Mode 901: Single-byte compaction with length indicator
    • Mode 924: Multiple-byte compaction (6 bytes in 5 codewords)
  3. Numeric Compaction (mode 902): Efficiently encodes sequences of digits

    • Groups digits and uses base-900 encoding
    • More efficient than text mode for long digit sequences
  4. ECI (Extended Channel Interpretation): Character set switching

    • Allows different character encodings within single barcode
    • ECI value specifies character set (e.g., 26 = UTF-8)
  5. Macro PDF417: Control blocks for structured append

    • BEGIN_MACRO_PDF417_CONTROL_BLOCK (928): Start macro
    • MACRO_PDF417_TERMINATOR (922): End macro

Mode Codeword Values:

CodewordModeDescription
900TEXT_COMPACTION_MODE_LATCHSwitch to text compaction
901BYTE_COMPACTION_MODE_LATCHSwitch to byte compaction (1 byte per codeword)
902NUMERIC_COMPACTION_MODE_LATCHSwitch to numeric compaction
913MODE_SHIFT_TO_BYTE_COMPACTION_MODETemporary byte shift
922MACRO_PDF417_TERMINATOREnd macro block
923BEGIN_MACRO_PDF417_OPTIONAL_FIELDStart macro optional field
924BYTE_COMPACTION_MODE_LATCH_6Switch to byte compaction (6 bytes per 5 codewords)
925ECI_USER_DEFINEDUser-defined ECI
926ECI_GENERAL_PURPOSEGeneral purpose ECI
927ECI_CHARSETCharacter set ECI
928BEGIN_MACRO_PDF417_CONTROL_BLOCKStart macro control block

Usage Examples:

import {
  PDF417DecodedBitStreamParser,
  DecoderResult
} from "@zxing/library";

// Decode codewords (typically called internally by PDF417Reader)
const codewords = new Int32Array([
  /* codeword data */
]);
const ecLevel = "5"; // Error correction level (0-8)

try {
  const decoderResult: DecoderResult = PDF417DecodedBitStreamParser.decode(
    codewords,
    ecLevel
  );

  console.log("Decoded text:", decoderResult.getText());
  console.log("Error correction level:", decoderResult.getECLevel());

  // Access byte segments if present (binary data)
  const byteSegments: Uint8Array[] | null = decoderResult.getByteSegments();
  if (byteSegments && byteSegments.length > 0) {
    console.log("Binary data segments:", byteSegments.length);
    
    byteSegments.forEach((segment, index) => {
      console.log(`Segment ${index}: ${segment.length} bytes`);
      console.log(`  First bytes: ${Array.from(segment.slice(0, 10))}`);
    });
  }

  // Check for structured append
  if (decoderResult.hasStructuredAppend()) {
    const seqNum: number = decoderResult.getStructuredAppendSequenceNumber();
    const parity: number = decoderResult.getStructuredAppendParity();
    console.log(`Structured append: sequence ${seqNum}, parity ${parity}`);
  }

  // Access additional metadata
  const other = decoderResult.getOther();
  if (other) {
    console.log("Additional metadata:", other);
  }
} catch (error) {
  console.error("Bit stream parsing failed:", error);
}

// Understanding compaction modes
function explainCompaction(): void {
  console.log("PDF417 Compaction Modes:");
  console.log();
  
  console.log("TEXT (900):");
  console.log("  Alpha: A-Z, space");
  console.log("  Lower: a-z");
  console.log("  Mixed: 0-9, punctuation");
  console.log("  Punct: special punctuation");
  console.log("  Encoding: 'HELLO' uses Alpha mode");
  console.log();
  
  console.log("BYTE (901/924):");
  console.log("  901: 1 byte per codeword");
  console.log("  924: 6 bytes per 5 codewords (more efficient)");
  console.log("  Example: Binary data, images, encrypted content");
  console.log();
  
  console.log("NUMERIC (902):");
  console.log("  Base-900 encoding of digit sequences");
  console.log("  Example: '1234567890' encoded more efficiently");
  console.log("  Groups digits: 1 codeword ≈ 2.96 digits");
  console.log();
  
  console.log("ECI (927):");
  console.log("  Charset switching: UTF-8, Shift_JIS, etc.");
  console.log("  ECI 26 = UTF-8");
  console.log("  ECI 3 = ISO-8859-1");
}

explainCompaction();

PDF417DecoderErrorCorrection

Implements Reed-Solomon error correction specific to PDF417, using modular polynomial arithmetic over GF(929) to detect and correct errors in codewords.

/**
 * PDF417 error correction implementation using Reed-Solomon algorithm
 * Uses Galois Field GF(929) to correct errors and erasures in received codewords
 * GF(929) matches the 929 possible codeword values (0-928)
 */
class PDF417DecoderErrorCorrection {
  /**
   * Constructor - initializes with PDF417's Galois Field (GF 929)
   */
  constructor();

  /**
   * Decode and correct errors in received codewords
   * Modifies received array in-place with corrected values
   * @param received - Int32Array: array of received codewords (data + EC), modified in-place
   * @param numECCodewords - number: number of error correction codewords
   * @param erasures - Int32Array: array of known erasure positions (empty if none)
   * @returns number: number of errors corrected
   * @throws ChecksumException if too many errors to correct
   */
  decode(
    received: Int32Array,
    numECCodewords: number,
    erasures: Int32Array
  ): number;
}

Error Correction Details:

PDF417 uses Reed-Solomon error correction with these properties:

  • Field: Galois Field GF(929) - matches the 929 possible codeword values (0-928)
  • Capability: Can correct up to t errors where 2t ≤ numECCodewords
  • Erasures: Known error positions (e.g., damaged modules) can be corrected more efficiently
    • Formula: 2e + t ≤ numECCodewords where e=erasures, t=errors
  • Algorithm: Euclidean algorithm for finding error locator polynomial

Error Correction Levels:

PDF417 supports 9 error correction levels (0-8):

LevelEC CodewordsCan Correct ErrorsCan Detect Errors
0212
1424
2848
316816
4321632
5643264
612864128
7256128256
8512256512

Usage Examples:

import {
  PDF417DecoderErrorCorrection,
  ChecksumException
} from "@zxing/library";

// Create error correction instance
const errorCorrection = new PDF417DecoderErrorCorrection();

// Simulate received codewords with potential errors
const received = new Int32Array([
  /* received codewords: data + error correction */
]);
const numECCodewords = 64; // Error correction level 5
const erasures = new Int32Array([]); // No known erasures

try {
  // Attempt error correction
  const errorsCorreected: number = errorCorrection.decode(
    received,
    numECCodewords,
    erasures
  );

  console.log(`Corrected ${errorsCorreected} errors`);
  console.log("Corrected codewords:", Array.from(received));
  
  // Extract data portion (exclude EC codewords)
  const dataCount = received.length - numECCodewords;
  const dataCodewords = received.slice(0, dataCount);
  console.log("Data codewords:", Array.from(dataCodewords));
} catch (e) {
  if (e instanceof ChecksumException) {
    console.error("Too many errors to correct");
    console.error("Maximum correctable errors:", Math.floor(numECCodewords / 2));
  } else {
    throw e;
  }
}

// Example with known erasures (damaged barcode sections)
const receivedWithDamage = new Int32Array([
  /* codewords with some known damaged positions */
]);
const knownErasures = new Int32Array([3, 7, 12, 18]); // Known damaged positions
const numEC = 64; // Level 5

try {
  const errorsFixed: number = errorCorrection.decode(
    receivedWithDamage,
    numEC,
    knownErasures
  );
  
  console.log(`Fixed ${errorsFixed} errors`);
  console.log(`Known erasures: ${knownErasures.length}`);
  console.log(`Unknown errors: ${errorsFixed - knownErasures.length}`);
  
  // With erasures, can correct more total errors:
  // 2 * erasures + errors ≤ numECCodewords
  // With 4 erasures and 64 EC: can correct up to (64 - 2*4) = 56 additional errors
} catch (e) {
  console.error("Correction failed even with known erasures:", e);
}

// Error correction capacity examples
const levels = [
  { level: 0, ec: 2 },
  { level: 1, ec: 4 },
  { level: 2, ec: 8 },
  { level: 3, ec: 16 },
  { level: 4, ec: 32 },
  { level: 5, ec: 64 },
  { level: 6, ec: 128 },
  { level: 7, ec: 256 },
  { level: 8, ec: 512 }
];

console.log("PDF417 Error Correction Capacity:");
levels.forEach(({ level, ec }) => {
  const maxErrors = Math.floor(ec / 2);
  console.log(`Level ${level}: ${ec} EC codewords, can correct ${maxErrors} errors`);
});

// With erasures formula: 2e + t ≤ numEC
// Examples with 64 EC codewords (level 5):
console.log("\nWith 64 EC codewords:");
console.log("  0 erasures: can correct 32 errors");
console.log("  10 erasures: can correct 22 errors (2*10 + 22 = 44 ≤ 64)");
console.log("  20 erasures: can correct 12 errors (2*20 + 12 = 52 ≤ 64)");
console.log("  32 erasures: can correct 0 errors (2*32 + 0 = 64 ≤ 64)");

PDF417Constants

Common constants used throughout the PDF417 implementation:

/**
 * PDF417 format constants
 * Defines structure and limits of PDF417 barcodes
 */
interface PDF417Constants {
  /**
   * Total possible codeword values in PDF417
   * Codewords range from 0 to 928
   */
  NUMBER_OF_CODEWORDS: 929;

  /**
   * Maximum codewords in a single barcode (data + error correction)
   * One less than NUMBER_OF_CODEWORDS (928) because stop pattern uses value 928
   */
  MAX_CODEWORDS_IN_BARCODE: 928;

  /**
   * Minimum number of rows in a PDF417 barcode
   */
  MIN_ROWS_IN_BARCODE: 3;

  /**
   * Maximum number of rows in a PDF417 barcode
   */
  MAX_ROWS_IN_BARCODE: 90;

  /**
   * Number of modules (bars/spaces) in each codeword
   */
  MODULES_IN_CODEWORD: 17;

  /**
   * Number of modules in the stop pattern
   */
  MODULES_IN_STOP_PATTERN: 18;

  /**
   * Number of bars in each module
   */
  BARS_IN_MODULE: 8;

  /**
   * Mode latch values
   */
  TEXT_COMPACTION_MODE_LATCH: 900;
  BYTE_COMPACTION_MODE_LATCH: 901;
  NUMERIC_COMPACTION_MODE_LATCH: 902;
  BYTE_COMPACTION_MODE_LATCH_6: 924;
  MODE_SHIFT_TO_BYTE_COMPACTION_MODE: 913;

  /**
   * ECI mode values
   */
  ECI_USER_DEFINED: 925;
  ECI_GENERAL_PURPOSE: 926;
  ECI_CHARSET: 927;

  /**
   * Macro PDF417 control codewords
   */
  BEGIN_MACRO_PDF417_CONTROL_BLOCK: 928;
  BEGIN_MACRO_PDF417_OPTIONAL_FIELD: 923;
  MACRO_PDF417_TERMINATOR: 922;
}

Usage Examples:

// Constants are used internally but can be referenced for understanding

// Codeword value range
console.log("Valid codeword range: 0-928");
console.log("Maximum codewords per barcode: 928");

// Structure limits
console.log("Rows: minimum 3, maximum 90");
console.log("Each codeword: 17 modules (bars and spaces)");
console.log("Stop pattern: 18 modules");

// Mode switching
console.log("Switch to text mode: codeword 900");
console.log("Switch to byte mode: codeword 901");
console.log("Switch to numeric mode: codeword 902");
console.log("Advanced byte mode: codeword 924 (6 bytes per 5 codewords)");

// ECI for character sets
console.log("ECI charset: codeword 927");
console.log("Example: 927 followed by 26 = UTF-8");
console.log("Example: 927 followed by 3 = ISO-8859-1");

// Macro PDF417 for structured data
console.log("Begin macro: codeword 928");
console.log("End macro: codeword 922");

Complete Example: Multi-Segment File Transfer

Production-ready implementation for handling multi-segment PDF417 file transfers:

import {
  PDF417Reader,
  PDF417ResultMetadata,
  BinaryBitmap,
  HybridBinarizer,
  RGBLuminanceSource,
  ResultMetadataType,
  Result
} from "@zxing/library";

interface SegmentData {
  index: number;
  text: string;
  metadata: PDF417ResultMetadata;
}

interface FileInfo {
  fileName: string;
  fileSize: number;
  checksum: number;
  timestamp: number;
  sender: string;
  addressee: string;
}

class PDF417FileTransfer {
  private segments = new Map<string, SegmentData[]>();

  /**
   * Process a PDF417 barcode and handle multi-segment reconstruction
   */
  processBarcode(
    imageData: Uint8ClampedArray,
    width: number,
    height: number
  ): void {
    const luminanceSource = new RGBLuminanceSource(imageData, width, height);
    const binarizer = new HybridBinarizer(luminanceSource);
    const bitmap = new BinaryBitmap(binarizer);

    const reader = new PDF417Reader();

    try {
      // Try to decode multiple barcodes
      const results: Result[] = reader.decodeMultiple(bitmap);

      for (const result of results) {
        this.processResult(result);
      }
    } catch (e) {
      // Fallback to single decode
      try {
        const result: Result = reader.decode(bitmap);
        this.processResult(result);
      } catch (err) {
        console.error("Failed to decode PDF417:", err);
      }
    }
  }

  private processResult(result: Result): void {
    const metadata = result.getResultMetadata()?.get(
      ResultMetadataType.PDF417_EXTRA_METADATA
    );

    if (!(metadata instanceof PDF417ResultMetadata)) {
      console.log("Single barcode (no structured append):", result.getText());
      return;
    }

    const fileId: string = metadata.getFileId();
    const segmentIndex: number = metadata.getSegmentIndex();
    const segmentCount: number = metadata.getSegmentCount();

    // Initialize segment array for this file ID
    if (!this.segments.has(fileId)) {
      this.segments.set(fileId, []);
    }

    const fileSegments = this.segments.get(fileId)!;
    fileSegments.push({
      index: segmentIndex,
      text: result.getText(),
      metadata: metadata,
    });

    console.log(`Received segment ${segmentIndex + 1}/${segmentCount} for file ${fileId}`);

    // Check if all segments received
    if (fileSegments.length === segmentCount) {
      this.reconstructFile(fileId, fileSegments);
    }
  }

  private reconstructFile(fileId: string, segments: SegmentData[]): void {
    // Sort segments by index
    segments.sort((a, b) => a.index - b.index);

    // Concatenate data
    let completeData = "";
    for (const segment of segments) {
      completeData += segment.text;
    }

    // Get file metadata from first segment
    const metadata: PDF417ResultMetadata = segments[0].metadata;
    const fileInfo: FileInfo = {
      fileName: metadata.getFileName(),
      fileSize: metadata.getFileSize(),
      checksum: metadata.getChecksum(),
      timestamp: metadata.getTimestamp(),
      sender: metadata.getSender(),
      addressee: metadata.getAddressee()
    };

    console.log("\n=== File Reconstruction Complete ===");
    console.log("File ID:", fileId);
    console.log("File name:", fileInfo.fileName);
    console.log("File size:", fileInfo.fileSize, "bytes");
    console.log("Complete data length:", completeData.length, "characters");

    if (fileInfo.timestamp !== -1) {
      const date = new Date(fileInfo.timestamp * 1000);
      console.log("Timestamp:", date.toISOString());
    }

    if (fileInfo.sender) {
      console.log("Sender:", fileInfo.sender);
    }
    
    if (fileInfo.addressee) {
      console.log("Addressee:", fileInfo.addressee);
    }

    // Verify checksum if present
    if (fileInfo.checksum !== -1) {
      const calculatedChecksum = this.calculateCCITT16Checksum(completeData);
      
      if (calculatedChecksum === fileInfo.checksum) {
        console.log("✓ Checksum valid");
      } else {
        console.warn("✗ Checksum mismatch!");
        console.warn(`  Expected: ${fileInfo.checksum}`);
        console.warn(`  Calculated: ${calculatedChecksum}`);
      }
    }

    console.log("\nComplete data:");
    console.log(completeData);
    console.log("===================================\n");

    // Clean up
    this.segments.delete(fileId);
  }

  /**
   * Calculate CCITT-16 CRC checksum
   */
  private calculateCCITT16Checksum(data: string): number {
    let crc = 0xFFFF;
    
    for (let i = 0; i < data.length; i++) {
      const byte = data.charCodeAt(i);
      crc ^= byte << 8;
      
      for (let j = 0; j < 8; j++) {
        if (crc & 0x8000) {
          crc = (crc << 1) ^ 0x1021;
        } else {
          crc = crc << 1;
        }
      }
    }
    
    return crc & 0xFFFF;
  }

  /**
   * Get status of all in-progress file transfers
   */
  getStatus(): Map<string, { received: number; total: number }> {
    const status = new Map<string, { received: number; total: number }>();
    
    for (const [fileId, segments] of this.segments) {
      const total = segments.length > 0 ? segments[0].metadata.getSegmentCount() : 0;
      
      status.set(fileId, {
        received: segments.length,
        total: total,
      });
    }
    
    return status;
  }
}

// Usage
const fileTransfer = new PDF417FileTransfer();

// Process multiple images containing segments
fileTransfer.processBarcode(imageData1, width1, height1);
fileTransfer.processBarcode(imageData2, width2, height2);
fileTransfer.processBarcode(imageData3, width3, height3);

// Check status
const status = fileTransfer.getStatus();
for (const [fileId, info] of status) {
  console.log(`File ${fileId}: ${info.received}/${info.total} segments received`);
  
  if (info.received === info.total) {
    console.log(`  ✓ Complete`);
  } else {
    console.log(`  ⏳ Waiting for ${info.total - info.received} more segments`);
  }
}

Advanced Usage

Error Handling

import {
  PDF417Reader,
  NotFoundException,
  FormatException,
  ChecksumException,
  BinaryBitmap
} from "@zxing/library";

const reader = new PDF417Reader();

// Comprehensive error handling
function safePDF417Decode(bitmap: BinaryBitmap): Result | null {
  try {
    const result: Result = reader.decode(bitmap);
    console.log("Success:", result.getText());
    return result;
  } catch (e) {
    if (e instanceof NotFoundException) {
      console.error("No PDF417 barcode found in image");
      // Image may not contain PDF417, or code is too damaged
    } else if (e instanceof FormatException) {
      console.error("Invalid PDF417 format");
      // Code structure is malformed
    } else if (e instanceof ChecksumException) {
      console.error("Error correction failed - too many errors");
      // Barcode has too much damage for current EC level
    } else {
      console.error("Unknown error:", e);
    }
    return null;
  }
}

// Retry with different binarizers
function decodeWithFallback(
  imageData: Uint8ClampedArray,
  width: number,
  height: number
): Result | null {
  const reader = new PDF417Reader();
  const source = new RGBLuminanceSource(imageData, width, height);

  // Try HybridBinarizer first (more accurate)
  try {
    const bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
    return reader.decode(bitmap1);
  } catch (e) {
    console.debug("HybridBinarizer failed, trying GlobalHistogramBinarizer");
  }

  // Fall back to GlobalHistogramBinarizer (faster)
  try {
    const bitmap2 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
    return reader.decode(bitmap2);
  } catch (e) {
    console.debug("GlobalHistogramBinarizer also failed");
  }

  return null;
}

Decode Hints

/**
 * Decode hints for PDF417 reading
 */
interface PDF417DecodeHints {
  /**
   * PURE_BARCODE: boolean
   * Indicate image contains only barcode (no extra content)
   * Enables faster processing without detection
   */
  [DecodeHintType.PURE_BARCODE]?: boolean;

  /**
   * TRY_HARDER: boolean
   * Try harder to find barcode (slower but more accurate)
   * Scans more rows and tries more orientations
   */
  [DecodeHintType.TRY_HARDER]?: boolean;

  /**
   * CHARACTER_SET: string
   * Character set for text decoding
   * Examples: "UTF-8", "ISO-8859-1", "Windows-1252"
   */
  [DecodeHintType.CHARACTER_SET]?: string;
}

Usage Examples:

import {
  PDF417Reader,
  DecodeHintType,
  BinaryBitmap
} from "@zxing/library";

const reader = new PDF417Reader();

// Pure barcode (fastest)
const pureHints = new Map<DecodeHintType, any>();
pureHints.set(DecodeHintType.PURE_BARCODE, true);

const pureResult: Result = reader.decode(pureBitmap, pureHints);

// Try harder (most accurate)
const hardHints = new Map<DecodeHintType, any>();
hardHints.set(DecodeHintType.TRY_HARDER, true);

const hardResult: Result = reader.decode(difficultBitmap, hardHints);

// Character set specification
const utf8Hints = new Map<DecodeHintType, any>();
utf8Hints.set(DecodeHintType.CHARACTER_SET, "UTF-8");

const utf8Result: Result = reader.decode(bitmap, utf8Hints);

// Combined hints
const combinedHints = new Map<DecodeHintType, any>();
combinedHints.set(DecodeHintType.TRY_HARDER, true);
combinedHints.set(DecodeHintType.CHARACTER_SET, "UTF-8");

const combinedResult: Result = reader.decode(bitmap, combinedHints);

Performance Optimization

import {
  PDF417Reader,
  BinaryBitmap,
  HybridBinarizer,
  GlobalHistogramBinarizer,
  RGBLuminanceSource,
  Result
} from "@zxing/library";

// Use GlobalHistogramBinarizer for faster processing
// (HybridBinarizer is more accurate but slower)
const luminanceSource = new RGBLuminanceSource(imageData, width, height);
const fastBinarizer = new GlobalHistogramBinarizer(luminanceSource);
const fastBitmap = new BinaryBitmap(fastBinarizer);

const reader = new PDF417Reader();

// For real-time scanning, reuse reader instance
const videoFrames: BinaryBitmap[] = getVideoFrames();

for (const frame of videoFrames) {
  try {
    const result: Result = reader.decode(frame);
    console.log("Decoded:", result.getText());
    break; // Stop on first successful decode
  } catch (e) {
    // Continue to next frame
  }
}

// Reset reader state if needed (though PDF417Reader.reset() is a no-op)
reader.reset();

// Batch processing optimization
function batchDecodePDF417(
  images: { data: Uint8ClampedArray; width: number; height: number }[]
): Result[] {
  const reader = new PDF417Reader();
  const results: Result[] = [];

  for (const img of images) {
    try {
      const source = new RGBLuminanceSource(img.data, img.width, img.height);
      const bitmap = new BinaryBitmap(new HybridBinarizer(source));
      const result: Result = reader.decode(bitmap);
      
      results.push(result);
    } catch (error) {
      console.error("Image decode failed:", error);
      // Continue with remaining images
    }
  }

  return results;
}

// Adaptive binarization
function adaptiveDecode(
  imageData: Uint8ClampedArray,
  width: number,
  height: number
): Result | null {
  const reader = new PDF417Reader();
  const source = new RGBLuminanceSource(imageData, width, height);

  // Strategy 1: HybridBinarizer (most accurate)
  try {
    const bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
    return reader.decode(bitmap1);
  } catch (e1) {
    console.debug("HybridBinarizer failed");
  }

  // Strategy 2: GlobalHistogramBinarizer (faster)
  try {
    const bitmap2 = new BinaryBitmap(new GlobalHistogramBinarizer(source));
    return reader.decode(bitmap2);
  } catch (e2) {
    console.debug("GlobalHistogramBinarizer failed");
  }

  // Strategy 3: Inverted image
  try {
    const inverted = source.invert();
    const bitmap3 = new BinaryBitmap(new HybridBinarizer(inverted));
    return reader.decode(bitmap3);
  } catch (e3) {
    console.debug("Inverted image failed");
  }

  return null;
}

Structured Append Example

import { PDF417Reader, PDF417ResultMetadata, ResultMetadataType, Result } from "@zxing/library";

class StructuredAppendHandler {
  private fileSegments = new Map<string, Map<number, string>>();

  processResult(result: Result): void {
    const meta = result.getResultMetadata()?.get(
      ResultMetadataType.PDF417_EXTRA_METADATA
    );

    if (!(meta instanceof PDF417ResultMetadata)) {
      console.log("Single segment:", result.getText());
      return;
    }

    const fileId: string = meta.getFileId();
    const index: number = meta.getSegmentIndex();
    const count: number = meta.getSegmentCount();

    if (!this.fileSegments.has(fileId)) {
      this.fileSegments.set(fileId, new Map());
    }

    const segments = this.fileSegments.get(fileId)!;
    segments.set(index, result.getText());

    console.log(`File ${fileId}: segment ${index + 1}/${count} received`);

    if (segments.size === count) {
      this.reconstructMessage(fileId, count, segments);
    }
  }

  private reconstructMessage(
    fileId: string,
    count: number,
    segments: Map<number, string>
  ): void {
    let message = "";
    
    for (let i = 0; i < count; i++) {
      const segment = segments.get(i);
      if (segment) {
        message += segment;
      } else {
        console.error(`Missing segment ${i} for file ${fileId}`);
        return;
      }
    }

    console.log(`\nFile ${fileId} complete:`);
    console.log(message);
    console.log();

    // Clean up
    this.fileSegments.delete(fileId);
  }

  getIncompleteFiles(): string[] {
    return Array.from(this.fileSegments.keys());
  }
}

// Usage
const handler = new StructuredAppendHandler();

// Process multiple barcode images
const results: Result[] = [
  /* decoded PDF417 results */
];

results.forEach(result => {
  handler.processResult(result);
});

// Check for incomplete files
const incomplete = handler.getIncompleteFiles();
if (incomplete.length > 0) {
  console.log("Incomplete files:", incomplete);
}

Related APIs

  • Core API - Core Reader interfaces, Result, BinaryBitmap
  • Image Processing - LuminanceSource, Binarizer classes
  • Common Utilities - BitMatrix, BitArray
  • Error Correction - Reed-Solomon fundamentals
  • Types and Enums - BarcodeFormat, hint types

References

  • ISO/IEC 15438: PDF417 bar code symbology specification
  • Error Correction: Reed-Solomon over GF(929)
  • Rows: 3-90 rows of stacked codewords
  • Codewords: 929 possible values (0-928)
  • Compaction: Text, Byte, and Numeric modes
  • Structured Append: Multi-symbol message support for large files
  • Macro PDF417: Control blocks for file transfer metadata

Install with Tessl CLI

npx tessl i tessl/npm-zxing--library@0.21.14

docs

index.md

tile.json