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

qrcode.mddocs/reference/

QR Code APIs

The QR Code module provides comprehensive support for encoding and decoding QR Codes, a popular 2D barcode format. It includes classes for reading QR Codes from images, generating QR Code matrices, and managing QR Code-specific features like error correction levels, versions, and encoding modes.

Package Information

  • Package Name: @zxing/library
  • Module: QR Code (core/qrcode/)
  • Language: TypeScript
  • Format: ISO 18004:2006 QR Code standard
  • Versions: 1-40 (21×21 to 177×177 modules)
  • Error Correction: 4 levels (L ~7%, M ~15%, Q ~25%, H ~30%)

Core Imports

import {
  // Reader/Writer
  QRCodeReader,
  QRCodeWriter,
  
  // Error correction
  ErrorCorrectionLevel,
  QRCodeDecoderErrorCorrectionLevel,
  
  // Encoding modes
  Mode,
  QRCodeMode,
  
  // Version info
  Version,
  QRCodeVersion,
  
  // Encoder/Decoder
  Encoder as QRCodeEncoder,
  Decoder as QRCodeDecoder,
  
  // QR Code representation
  QRCode as QRCodeEncoderQRCode,
  ByteMatrix as QRCodeByteMatrix,
  
  // Matrix utilities
  MatrixUtil as QRCodeMatrixUtil,
  MaskUtil as QRCodeMaskUtil,
  
  // Data masks
  DataMask,
  DataMaskValues,
  
  // Bit stream parser
  QRCodeDecodedBitStreamParser,
  
  // Detector
  Detector as QRCodeDetector,
  
  // Result types
  BarcodeFormat,
  EncodeHintType,
  DecodeHintType,
  Result,
  BinaryBitmap
} from "@zxing/library";

Architecture

The QR Code module is organized into several key components:

  • Reader/Writer: Top-level classes for decoding (QRCodeReader) and encoding (QRCodeWriter) QR Codes
  • Decoder: Classes for parsing QR Code bit matrices, including error correction level, version, mode, and format information
  • Encoder: Classes for generating QR Code matrices, including encoding logic, mask pattern selection, and matrix utilities
  • Detector: Pattern detection classes for locating finder patterns, alignment patterns, and extracting QR Code regions from images
  • Version Management: Classes for handling QR Code versions (1-40) with capacity and structure information
  • Mode Selection: Encoding mode optimization for different data types (numeric, alphanumeric, byte, kanji)

Capabilities

QR Code Reader

Decodes QR Codes from binary images, with support for rotation, skew, and partial occlusion.

/**
 * QR Code decoder that locates and decodes QR Codes in images
 * Implements the Reader interface for QR Code barcode decoding
 */
class QRCodeReader implements Reader {
  /**
   * Locates and decodes a QR code in an image
   * @param image - BinaryBitmap: binary bitmap to decode
   * @param hints - Map<DecodeHintType, any>: optional decoding hints
   *   - TRY_HARDER: boolean for better accuracy
   *   - CHARACTER_SET: string for text encoding
   *   - PURE_BARCODE: boolean if image contains only QR code
   *   - NEED_RESULT_POINT_CALLBACK: ResultPointCallback for detection tracking
   * @returns Result: decoded text, raw bytes, and metadata
   * @throws NotFoundException if QR code cannot be found
   * @throws FormatException if QR code cannot be decoded (invalid structure)
   * @throws ChecksumException if error correction fails (too many errors)
   */
  decode(image: BinaryBitmap, hints?: Map<DecodeHintType, any>): Result;

  /**
   * Resets the reader state
   * Currently a no-op for QR Code reader
   */
  reset(): void;

  /**
   * Gets the decoder instance (internal use)
   * @returns Decoder: internal QR Code Decoder instance
   */
  protected getDecoder(): Decoder;

  /**
   * Extracts QR Code bits from a pure monochrome image
   * Used when PURE_BARCODE hint is set
   * @param image - BitMatrix: binary matrix containing pure QR Code
   * @returns BitMatrix: extracted QR Code bits
   * @throws NotFoundException if extraction fails
   */
  static extractPureBits(image: BitMatrix): BitMatrix;
}

Usage Examples:

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

// Basic QR Code reading
const reader = new QRCodeReader();
const luminanceSource = new RGBLuminanceSource(imageData, width, height);
const binarizer = new HybridBinarizer(luminanceSource);
const bitmap = new BinaryBitmap(binarizer);

try {
  const result: Result = reader.decode(bitmap);
  console.log("Decoded text:", result.getText());
  console.log("Format:", result.getBarcodeFormat()); // BarcodeFormat.QR_CODE
  
  // Access result points (finder patterns and alignment patterns)
  const points = result.getResultPoints();
  console.log(`Found ${points.length} result points`);
} catch (error) {
  console.error("QR decode failed:", error);
}

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

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

// Reading pure QR Code (no border/rotation, faster)
const pureHints = new Map<DecodeHintType, any>();
pureHints.set(DecodeHintType.PURE_BARCODE, true);

try {
  const result3: Result = reader.decode(bitmap, pureHints);
  console.log("Pure QR decode:", result3.getText());
} catch (error) {
  console.error("Pure QR decode failed:", error);
}

// Accessing metadata
const result: Result = reader.decode(bitmap);
const metadata = result.getResultMetadata();

if (metadata) {
  const ecLevel = metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
  console.log("Error correction level:", ecLevel); // "L", "M", "Q", or "H"
  
  const byteSegments = metadata.get(ResultMetadataType.BYTE_SEGMENTS);
  if (byteSegments) {
    console.log("Byte segments:", byteSegments);
  }
  
  const orientation = metadata.get(ResultMetadataType.ORIENTATION);
  if (orientation !== undefined) {
    console.log(`QR rotated ${orientation} degrees`);
  }
}

// With result point callback for detection tracking
const callback: ResultPointCallback = {
  foundPossibleResultPoint(point: ResultPoint): void {
    console.log(`Found finder pattern at (${point.getX()}, ${point.getY()})`);
  }
};

const trackingHints = new Map<DecodeHintType, any>();
trackingHints.set(DecodeHintType.NEED_RESULT_POINT_CALLBACK, callback);

const result4: Result = reader.decode(bitmap, trackingHints);

Error Correction Levels | Decode Hints

QR Code Writer

Encodes content as QR Code bit matrices suitable for rendering.

/**
 * QR Code encoder that generates QR Code bit matrices
 * Implements the Writer interface for QR Code encoding
 */
class QRCodeWriter implements Writer {
  /**
   * Encodes content as a QR Code bit matrix
   * @param contents - string: text content to encode
   * @param format - BarcodeFormat: must be BarcodeFormat.QR_CODE
   * @param width - number: desired width in pixels
   * @param height - number: desired height in pixels
   * @param hints - Map<EncodeHintType, any>: optional encoding hints
   *   - ERROR_CORRECTION: ErrorCorrectionLevel or string ("L"|"M"|"Q"|"H")
   *   - CHARACTER_SET: string (e.g., "UTF-8", "ISO-8859-1", "Shift_JIS")
   *   - QR_VERSION: number (1-40) to force specific version
   *   - MARGIN: number (quiet zone size in modules, default: 4)
   * @returns BitMatrix: encoded QR Code as binary matrix
   * @throws WriterException if encoding fails (data too large, invalid version)
   * @throws IllegalArgumentException if format is not QR_CODE or dimensions invalid
   */
  encode(
    contents: string,
    format: BarcodeFormat,
    width: number,
    height: number,
    hints?: Map<EncodeHintType, any>
  ): BitMatrix;

  /**
   * Default quiet zone size (border) in modules
   * Per ISO 18004, minimum is 4 modules
   */
  static QUIET_ZONE_SIZE: number; // 4

  /**
   * Renders a QR Code to a bit matrix with specified dimensions
   * Static utility for rendering QRCode instances
   * @param code - QRCode: QRCode instance to render
   * @param width - number: target width in pixels
   * @param height - number: target height in pixels
   * @param quietZone - number: quiet zone size in modules
   * @returns BitMatrix: rendered QR Code suitable for display
   * @throws WriterException if rendering fails
   */
  static renderResult(
    code: QRCode,
    width: number,
    height: number,
    quietZone: number
  ): BitMatrix;
}

Usage Examples:

import {
  QRCodeWriter,
  BarcodeFormat,
  EncodeHintType,
  ErrorCorrectionLevel,
  QRCodeDecoderErrorCorrectionLevel,
  BitMatrix
} from "@zxing/library";

const writer = new QRCodeWriter();

// Basic QR Code generation (defaults to error correction M)
const matrix1: BitMatrix = writer.encode(
  "Hello, World!",
  BarcodeFormat.QR_CODE,
  300,
  300
);
console.log(`Generated ${matrix1.getWidth()}×${matrix1.getHeight()} QR Code`);

// With error correction level
const hints = new Map<EncodeHintType, any>();
hints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.H);
hints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.set(EncodeHintType.MARGIN, 2); // Quiet zone: 2 modules

const matrix2: BitMatrix = writer.encode(
  "https://example.com",
  BarcodeFormat.QR_CODE,
  400,
  400,
  hints
);

// Encoding with specific version
const versionHints = new Map<EncodeHintType, any>();
versionHints.set(EncodeHintType.QR_VERSION, 10); // Force version 10 (57×57 modules)
versionHints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.M);

try {
  const matrix3: BitMatrix = writer.encode(
    "Data that must fit in version 10",
    BarcodeFormat.QR_CODE,
    500,
    500,
    versionHints
  );
  console.log("Version 10 encoding successful");
} catch (e) {
  console.error("Data too large for version 10:", e);
}

// Encoding international text
const intlHints = new Map<EncodeHintType, any>();
intlHints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
intlHints.set(EncodeHintType.ERROR_CORRECTION, "H"); // String also accepted

const matrix4: BitMatrix = writer.encode(
  "Hello 世界 🌍",
  BarcodeFormat.QR_CODE,
  300,
  300,
  intlHints
);

// Minimal quiet zone for space-constrained applications
const minimalHints = new Map<EncodeHintType, any>();
minimalHints.set(EncodeHintType.MARGIN, 1); // Minimum 1 module
// Note: ISO 18004 recommends 4 modules, but 1 may work in controlled environments

const matrix5: BitMatrix = writer.encode(
  "Minimal margins",
  BarcodeFormat.QR_CODE,
  250,
  250,
  minimalHints
);

Render to canvas:

import { QRCodeWriter, BarcodeFormat, BitMatrix } from "@zxing/library";

function renderToCanvas(matrix: BitMatrix, canvas: HTMLCanvasElement): void {
  const ctx = canvas.getContext("2d")!;
  const width = matrix.getWidth();
  const height = matrix.getHeight();
  
  canvas.width = width;
  canvas.height = height;

  const imageData = ctx.createImageData(width, height);
  
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const offset = (y * width + x) * 4;
      const color = matrix.get(x, y) ? 0 : 255; // Black or white
      
      imageData.data[offset] = color;     // R
      imageData.data[offset + 1] = color; // G
      imageData.data[offset + 2] = color; // B
      imageData.data[offset + 3] = 255;   // A
    }
  }
  
  ctx.putImageData(imageData, 0, 0);
}

// Usage
const writer = new QRCodeWriter();
const matrix: BitMatrix = writer.encode("QR Code", BarcodeFormat.QR_CODE, 300, 300);
const canvas = document.getElementById("qr-canvas") as HTMLCanvasElement;
renderToCanvas(matrix, canvas);

Render to image data URL:

import { QRCodeWriter, BarcodeFormat, BitMatrix } from "@zxing/library";

function renderToDataURL(
  matrix: BitMatrix,
  scale: number = 1
): string {
  const width = matrix.getWidth() * scale;
  const height = matrix.getHeight() * scale;
  
  const canvas = document.createElement('canvas');
  canvas.width = width;
  canvas.height = height;
  
  const ctx = canvas.getContext('2d')!;
  ctx.fillStyle = 'white';
  ctx.fillRect(0, 0, width, height);
  ctx.fillStyle = 'black';
  
  for (let y = 0; y < matrix.getHeight(); y++) {
    for (let x = 0; x < matrix.getWidth(); x++) {
      if (matrix.get(x, y)) {
        ctx.fillRect(x * scale, y * scale, scale, scale);
      }
    }
  }
  
  return canvas.toDataURL('image/png');
}

// Usage
const writer = new QRCodeWriter();
const matrix: BitMatrix = writer.encode("Data", BarcodeFormat.QR_CODE, 200, 200);
const dataURL: string = renderToDataURL(matrix, 4); // 4x scale
document.getElementById('qr-img')!.setAttribute('src', dataURL);

Error Correction Levels | Encode Hints | QR Code Versions

Error Correction Level

Exported as: QRCodeDecoderErrorCorrectionLevel

QR Code error correction levels determine how much damage can be tolerated while still successfully decoding.

/**
 * QR Code error correction levels (ISO 18004:2006, Section 6.5.1)
 * Higher levels provide more error resilience but reduce data capacity
 */
class ErrorCorrectionLevel {
  /**
   * L = ~7% correction capacity
   * Can recover from up to 7% data loss or damage
   * Maximum data capacity, minimum error correction
   */
  static L: ErrorCorrectionLevel;

  /**
   * M = ~15% correction capacity
   * Can recover from up to 15% data loss or damage
   * Balanced capacity and error correction (recommended default)
   */
  static M: ErrorCorrectionLevel;

  /**
   * Q = ~25% correction capacity
   * Can recover from up to 25% data loss or damage
   */
  static Q: ErrorCorrectionLevel;

  /**
   * H = ~30% correction capacity
   * Can recover from up to 30% data loss or damage
   * Minimum data capacity, maximum error correction
   */
  static H: ErrorCorrectionLevel;

  /**
   * Gets the error correction level numeric value
   * @returns ErrorCorrectionLevelValues: enum value (0=L, 1=M, 2=Q, 3=H)
   */
  getValue(): ErrorCorrectionLevelValues;

  /**
   * Gets the two-bit encoding for format information
   * @returns number: bits representing this error correction level
   *   - 0x01 for L
   *   - 0x00 for M
   *   - 0x03 for Q
   *   - 0x02 for H
   */
  getBits(): number;

  /**
   * Parses error correction level from string
   * @param s - string: level string ("L", "M", "Q", or "H")
   * @returns ErrorCorrectionLevel: corresponding level instance
   * @throws IllegalArgumentException if string is invalid
   */
  static fromString(s: string): ErrorCorrectionLevel;

  /**
   * Parses error correction level from format information bits
   * @param bits - number: two bits encoding the level (0x00-0x03)
   * @returns ErrorCorrectionLevel: corresponding level instance
   * @throws IllegalArgumentException if bits are invalid
   */
  static forBits(bits: number): ErrorCorrectionLevel;

  /**
   * String representation of the level
   * @returns string: "L", "M", "Q", or "H"
   */
  toString(): string;

  /**
   * Checks equality with another error correction level
   * @param o - any: object to compare
   * @returns boolean: true if equal
   */
  equals(o: any): boolean;
}

/**
 * Error correction level enum values
 */
enum ErrorCorrectionLevelValues {
  L = 0, // ~7% correction capacity
  M = 1, // ~15% correction capacity
  Q = 2, // ~25% correction capacity
  H = 3, // ~30% correction capacity
}

Usage Examples:

import {
  ErrorCorrectionLevel,
  QRCodeDecoderErrorCorrectionLevel,
  QRCodeWriter,
  BarcodeFormat,
  EncodeHintType,
  BitMatrix
} from "@zxing/library";

// Using predefined levels
const levelL = ErrorCorrectionLevel.L; // Lowest correction, maximum data
const levelM = ErrorCorrectionLevel.M; // Medium correction (recommended default)
const levelQ = ErrorCorrectionLevel.Q; // Quartile correction
const levelH = ErrorCorrectionLevel.H; // Highest correction, minimum data

// Parsing from string
const level1: ErrorCorrectionLevel = ErrorCorrectionLevel.fromString("H");
console.log(level1.toString()); // "H"

const level2: ErrorCorrectionLevel = ErrorCorrectionLevel.fromString("M");
console.log(level2.getValue()); // 1 (ErrorCorrectionLevelValues.M)

// Getting bits for format information
const bits: number = ErrorCorrectionLevel.Q.getBits();
console.log("Q level bits:", bits.toString(2)); // "11" (0x03)

// Parsing from bits
const level3: ErrorCorrectionLevel = ErrorCorrectionLevel.forBits(0x00);
console.log(level3.toString()); // "M"

const level4: ErrorCorrectionLevel = ErrorCorrectionLevel.forBits(0x02);
console.log(level4.toString()); // "H"

// Comparing levels
if (levelH.equals(ErrorCorrectionLevel.H)) {
  console.log("Highest error correction selected");
}

// Using in encoding
const hints = new Map<EncodeHintType, any>();
hints.set(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// Or using the exported alias:
// hints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.H);

const writer = new QRCodeWriter();
const matrix: BitMatrix = writer.encode(
  "Important data",
  BarcodeFormat.QR_CODE,
  300,
  300,
  hints
);

// Trade-offs between levels
// L: ~7% correction  - Maximum data capacity (e.g., 2953 bytes for version 40)
//    Use for: Clean environments, high-quality printing
// M: ~15% correction - Balanced (recommended for most uses) (~2331 bytes for version 40)
//    Use for: General purpose applications
// Q: ~25% correction - More robust (~1663 bytes for version 40)
//    Use for: Moderate damage expected, outdoor applications
// H: ~30% correction - Maximum robustness (~1273 bytes for version 40)
//    Use for: High damage expected, small print sizes, critical data

// Example: selecting level based on use case
function selectErrorCorrectionLevel(useCase: string): ErrorCorrectionLevel {
  switch (useCase) {
    case 'print-high-quality':
      return ErrorCorrectionLevel.L;
    case 'screen-display':
      return ErrorCorrectionLevel.M;
    case 'outdoor-signage':
      return ErrorCorrectionLevel.Q;
    case 'small-print':
    case 'critical-data':
      return ErrorCorrectionLevel.H;
    default:
      return ErrorCorrectionLevel.M;
  }
}

const level: ErrorCorrectionLevel = selectErrorCorrectionLevel('outdoor-signage');
console.log("Selected level:", level.toString());

Error correction capacity details:

import { ErrorCorrectionLevel, Version } from "@zxing/library";

// Calculate data capacity for different EC levels
function getDataCapacity(
  version: Version,
  ecLevel: ErrorCorrectionLevel
): number {
  const totalCodewords: number = version.getTotalCodewords();
  const ecBlocks = version.getECBlocksForLevel(ecLevel);
  const ecCodewords: number = ecBlocks.getTotalECCodewords();
  return totalCodewords - ecCodewords;
}

const version10 = Version.getVersionForNumber(10);

console.log("Version 10 capacities:");
console.log("L:", getDataCapacity(version10, ErrorCorrectionLevel.L), "codewords");
console.log("M:", getDataCapacity(version10, ErrorCorrectionLevel.M), "codewords");
console.log("Q:", getDataCapacity(version10, ErrorCorrectionLevel.Q), "codewords");
console.log("H:", getDataCapacity(version10, ErrorCorrectionLevel.H), "codewords");

// Approximate byte capacities for different versions and levels
// Version 1 (21×21):  L=17  M=14  Q=11  H=7   bytes (numeric: L=41  M=34  Q=27  H=17)
// Version 10 (57×57): L=346 M=271 Q=195 H=156 bytes (numeric: L=1064 M=860 Q=624 H=482)
// Version 20 (97×97): L=858 M=666 Q=486 H=382 bytes (numeric: L=2670 M=2088 Q=1552 H=1226)
// Version 40 (177×177): L=2953 M=2331 Q=1663 H=1273 bytes

QR Code Versions

QR Code Version

Exported as: QRCodeVersion

QR Code version information determines the symbol size and data capacity. Versions range from 1 (21×21 modules) to 40 (177×177 modules).

/**
 * QR Code version information (ISO 18004:2006 Annex D)
 * Versions 1-40, where version N has dimensions (17 + 4*N) × (17 + 4*N) modules
 * Higher versions support more data but require larger physical size
 */
class Version {
  /**
   * Gets the version number
   * @returns number: version number (1-40)
   */
  getVersionNumber(): number;

  /**
   * Gets the coordinates of alignment pattern centers
   * Version 1 has no alignment patterns, version 2+ have 1 or more
   * @returns Int32Array: array of alignment pattern center coordinates in modules
   */
  getAlignmentPatternCenters(): Int32Array;

  /**
   * Gets the total number of codewords (data + error correction) in this version
   * @returns number: total codeword count
   */
  getTotalCodewords(): number;

  /**
   * Gets the dimension (width/height) in modules for this version
   * Formula: 17 + 4 * versionNumber
   * @returns number: dimension in modules (e.g., 21 for version 1, 177 for version 40)
   */
  getDimensionForVersion(): number;

  /**
   * Gets error correction blocks for specified error correction level
   * @param ecLevel - ErrorCorrectionLevel: error correction level (L, M, Q, or H)
   * @returns ECBlocks: error correction block information
   */
  getECBlocksForLevel(ecLevel: ErrorCorrectionLevel): ECBlocks;

  /**
   * Deduces version from QR Code dimensions in modules
   * @param dimension - number: dimension in modules (must be 21 + 4n where n >= 0)
   * @returns Version: version for the given dimension
   * @throws FormatException if dimension is not valid (must be 21, 25, 29, 33, ...)
   */
  static getProvisionalVersionForDimension(dimension: number): Version;

  /**
   * Gets version by version number
   * @param versionNumber - number: version number (1-40)
   * @returns Version: version instance
   * @throws IllegalArgumentException if version number is out of range
   */
  static getVersionForNumber(versionNumber: number): Version;

  /**
   * Decodes version information from version bits (for versions 7+)
   * Version info is 18 bits encoded with BCH error correction
   * @param versionBits - number: version information bits
   * @returns Version|null: decoded version or null if cannot be decoded
   */
  static decodeVersionInformation(versionBits: number): Version;

  /**
   * Builds function pattern bit matrix for this version
   * Marks positions of finder patterns, timing patterns, alignment patterns
   * @returns BitMatrix: matrix with function patterns marked as true
   */
  buildFunctionPattern(): BitMatrix;

  /**
   * String representation of version
   * @returns string: version number as string
   */
  toString(): string;
}

Usage Examples:

import { Version, ErrorCorrectionLevel, ECBlocks } from "@zxing/library";

// Getting version by number
const version1: Version = Version.getVersionForNumber(1);
const version10: Version = Version.getVersionForNumber(10);
const version40: Version = Version.getVersionForNumber(40);

console.log("Version 1 dimension:", version1.getDimensionForVersion()); // 21 modules
console.log("Version 10 dimension:", version10.getDimensionForVersion()); // 57 modules
console.log("Version 40 dimension:", version40.getDimensionForVersion()); // 177 modules

// Getting version properties
console.log("Version:", version10.getVersionNumber()); // 10
console.log("Total codewords:", version10.getTotalCodewords()); // Total data + EC
console.log("Dimension:", version10.getDimensionForVersion(), "×", version10.getDimensionForVersion());

// Alignment pattern centers
const centers: Int32Array = version10.getAlignmentPatternCenters();
console.log("Alignment centers:", Array.from(centers)); // [6, 28, 50]

// Version 1 has no alignment patterns
const centers1: Int32Array = version1.getAlignmentPatternCenters();
console.log("Version 1 alignment centers:", Array.from(centers1)); // [] empty

// Version 40 has many alignment patterns
const centers40: Int32Array = version40.getAlignmentPatternCenters();
console.log("Version 40 alignment centers:", Array.from(centers40));

// Error correction blocks
const ecBlocks: ECBlocks = version10.getECBlocksForLevel(ErrorCorrectionLevel.H);
console.log("EC codewords per block:", ecBlocks.getECCodewordsPerBlock());
console.log("Number of EC blocks:", ecBlocks.getNumBlocks());
console.log("Total EC codewords:", ecBlocks.getTotalECCodewords());

// Deducing version from dimensions
const dimension = 57; // 57×57 modules
const deducedVersion: Version = Version.getProvisionalVersionForDimension(dimension);
console.log("Version from dimension 57:", deducedVersion.getVersionNumber()); // 10

// Validate dimension
try {
  const invalid = Version.getProvisionalVersionForDimension(50); // Invalid (not 21+4n)
} catch (e) {
  console.error("Invalid dimension:", e);
}

// Version capacity calculations
function getDataCapacity(version: Version, ecLevel: ErrorCorrectionLevel): number {
  const totalCodewords: number = version.getTotalCodewords();
  const ecBlocks: ECBlocks = version.getECBlocksForLevel(ecLevel);
  const ecCodewords: number = ecBlocks.getTotalECCodewords();
  return totalCodewords - ecCodewords;
}

console.log("Version 10-L capacity:", getDataCapacity(version10, ErrorCorrectionLevel.L), "codewords");
console.log("Version 10-M capacity:", getDataCapacity(version10, ErrorCorrectionLevel.M), "codewords");
console.log("Version 10-Q capacity:", getDataCapacity(version10, ErrorCorrectionLevel.Q), "codewords");
console.log("Version 10-H capacity:", getDataCapacity(version10, ErrorCorrectionLevel.H), "codewords");

// Build function pattern (for visualization or debugging)
const functionPattern: BitMatrix = version10.buildFunctionPattern();
console.log("Function pattern size:", functionPattern.getWidth(), "×", functionPattern.getHeight());
// Shows where finder patterns, timing patterns, and alignment patterns are located

// Version capacity reference table
const capacities = [
  { version: 1,  dimension: 21,  L: 25,   M: 20,   Q: 16,   H: 10   }, // bytes
  { version: 5,  dimension: 37,  L: 108,  M: 86,   Q: 62,   H: 48   },
  { version: 10, dimension: 57,  L: 346,  M: 271,  Q: 195,  H: 156  },
  { version: 20, dimension: 97,  L: 858,  M: 666,  Q: 486,  H: 382  },
  { version: 30, dimension: 137, L: 1542, M: 1193, Q: 865,  H: 647  },
  { version: 40, dimension: 177, L: 2953, M: 2331, Q: 1663, H: 1273 }
];

// Select appropriate version for data size
function selectVersion(dataBytes: number, ecLevel: ErrorCorrectionLevel): Version | null {
  for (let v = 1; v <= 40; v++) {
    const version = Version.getVersionForNumber(v);
    const capacity = getDataCapacity(version, ecLevel);
    
    if (capacity >= dataBytes) {
      return version;
    }
  }
  return null; // Data too large
}

const requiredVersion = selectVersion(100, ErrorCorrectionLevel.M);
if (requiredVersion) {
  console.log("Need at least version:", requiredVersion.getVersionNumber());
}

Error Correction Levels

QR Code Mode

Exported as: QRCodeMode

Encoding modes determine how data is encoded in the QR Code. Different modes are optimal for different types of data.

/**
 * QR Code encoding modes (ISO 18004:2006, Section 6.4.1, Tables 2 and 3)
 * Determines how data is encoded - different modes optimize for different character sets
 */
class Mode {
  /**
   * Terminator mode (end of data)
   * Bits: 0000
   */
  static TERMINATOR: Mode;

  /**
   * Numeric mode for digits 0-9
   * Encoding: 3 digits per 10 bits = 3.33 bits per digit
   * Character count bits: 10 (versions 1-9), 12 (10-26), 14 (27-40)
   */
  static NUMERIC: Mode;

  /**
   * Alphanumeric mode for 0-9, A-Z (uppercase), space, and symbols: $ % * + - . / :
   * Encoding: 2 characters per 11 bits = 5.5 bits per character
   * Character count bits: 9 (versions 1-9), 11 (10-26), 13 (27-40)
   */
  static ALPHANUMERIC: Mode;

  /**
   * Structured append mode (links multiple QR codes)
   * Not supported for encoding in this implementation
   */
  static STRUCTURED_APPEND: Mode;

  /**
   * Byte mode for 8-bit binary data
   * Encoding: 8 bits per byte
   * Character count bits: 8 (versions 1-9), 16 (10-26), 16 (27-40)
   */
  static BYTE: Mode;

  /**
   * ECI (Extended Channel Interpretation) mode
   * Allows switching character sets within QR Code
   * Character counts don't apply
   */
  static ECI: Mode;

  /**
   * Kanji mode for Shift JIS double-byte characters
   * Encoding: 13 bits per character
   * Character count bits: 8 (versions 1-9), 10 (10-26), 12 (27-40)
   */
  static KANJI: Mode;

  /**
   * FNC1 in first position (GS1 mode for Application Identifiers)
   */
  static FNC1_FIRST_POSITION: Mode;

  /**
   * FNC1 in second position (industry-specific applications)
   */
  static FNC1_SECOND_POSITION: Mode;

  /**
   * Hanzi mode for GB 2312 Chinese characters
   */
  static HANZI: Mode;

  /**
   * Parses mode from four-bit mode indicator
   * @param bits - number: four bits encoding the mode (0x0-0xF)
   * @returns Mode: corresponding mode instance
   * @throws IllegalArgumentException if bits don't correspond to a known mode
   */
  static forBits(bits: number): Mode;

  /**
   * Gets character count bits for this mode and version
   * Number of bits used to encode the character/byte count
   * @param version - Version: QR Code version (affects bit count)
   * @returns number: bits needed to encode character count
   */
  getCharacterCountBits(version: Version): number;

  /**
   * Gets the mode numeric value
   * @returns ModeValues: enum value
   */
  getValue(): ModeValues;

  /**
   * Gets the four-bit encoding for this mode
   * Used in QR Code mode indicator
   * @returns number: bits representing this mode (0x0-0xF)
   */
  getBits(): number;

  /**
   * Checks equality with another mode
   * @param o - any: object to compare
   * @returns boolean: true if equal
   */
  equals(o: any): boolean;

  /**
   * String representation of the mode
   * @returns string: mode name (e.g., "NUMERIC", "BYTE", "KANJI")
   */
  toString(): string;
}

/**
 * Mode enum values
 */
enum ModeValues {
  TERMINATOR,           // End of data
  NUMERIC,              // Digits 0-9
  ALPHANUMERIC,         // 0-9, A-Z, space, $ % * + - . / :
  STRUCTURED_APPEND,    // Multi-QR linking
  BYTE,                 // Binary/text data
  ECI,                  // Character set switching
  KANJI,                // Shift JIS Kanji
  FNC1_FIRST_POSITION,  // GS1 format
  FNC1_SECOND_POSITION, // Industry format
  HANZI,                // GB 2312 Chinese
}

Usage Examples:

import { Mode, Version, ModeValues } from "@zxing/library";

// Mode constants
const numeric = Mode.NUMERIC;       // For digits 0-9 only
const alphanumeric = Mode.ALPHANUMERIC; // For 0-9, A-Z, and limited symbols
const byte = Mode.BYTE;             // For binary data and full Unicode
const kanji = Mode.KANJI;           // For Shift JIS Kanji characters

console.log("Numeric mode:", numeric.toString()); // "NUMERIC"
console.log("Byte mode:", byte.toString());       // "BYTE"

// Getting character count bits (varies by version)
const version1 = Version.getVersionForNumber(1);
const version10 = Version.getVersionForNumber(10);
const version40 = Version.getVersionForNumber(40);

console.log("NUMERIC character count bits:");
console.log("  Version 1:", Mode.NUMERIC.getCharacterCountBits(version1));   // 10
console.log("  Version 10:", Mode.NUMERIC.getCharacterCountBits(version10)); // 12
console.log("  Version 40:", Mode.NUMERIC.getCharacterCountBits(version40)); // 14

console.log("ALPHANUMERIC character count bits:");
console.log("  Version 1:", Mode.ALPHANUMERIC.getCharacterCountBits(version1));   // 9
console.log("  Version 10:", Mode.ALPHANUMERIC.getCharacterCountBits(version10)); // 11
console.log("  Version 40:", Mode.ALPHANUMERIC.getCharacterCountBits(version40)); // 13

console.log("BYTE character count bits:");
console.log("  Version 1:", Mode.BYTE.getCharacterCountBits(version1));   // 8
console.log("  Version 10:", Mode.BYTE.getCharacterCountBits(version10)); // 16
console.log("  Version 40:", Mode.BYTE.getCharacterCountBits(version40)); // 16

console.log("KANJI character count bits:");
console.log("  Version 1:", Mode.KANJI.getCharacterCountBits(version1));   // 8
console.log("  Version 10:", Mode.KANJI.getCharacterCountBits(version10)); // 10
console.log("  Version 40:", Mode.KANJI.getCharacterCountBits(version40)); // 12

// Mode bit patterns
console.log("NUMERIC bits:", Mode.NUMERIC.getBits().toString(2).padStart(4, '0'));       // "0001"
console.log("ALPHANUMERIC bits:", Mode.ALPHANUMERIC.getBits().toString(2).padStart(4, '0')); // "0010"
console.log("BYTE bits:", Mode.BYTE.getBits().toString(2).padStart(4, '0'));            // "0100"
console.log("KANJI bits:", Mode.KANJI.getBits().toString(2).padStart(4, '0'));          // "1000"

// Parsing from bits
const mode1: Mode = Mode.forBits(0x01); // NUMERIC
const mode2: Mode = Mode.forBits(0x02); // ALPHANUMERIC
const mode4: Mode = Mode.forBits(0x04); // BYTE
const mode8: Mode = Mode.forBits(0x08); // KANJI

console.log(mode1.toString()); // "NUMERIC"
console.log(mode2.toString()); // "ALPHANUMERIC"
console.log(mode4.toString()); // "BYTE"
console.log(mode8.toString()); // "KANJI"

// Mode selection guidelines and encoding efficiency:

// NUMERIC: Only digits 0-9 (most efficient for numbers)
//   Example: "123456789012" (12 digits)
//   Encoding: 3 digits per 10 bits = 40 bits total
//   Best for: phone numbers, quantities, IDs

// ALPHANUMERIC: 0-9, A-Z (uppercase only), space, $ % * + - . / :
//   Example: "HELLO WORLD" (11 characters)
//   Encoding: 2 chars per 11 bits = 61 bits total
//   Best for: product codes, URLs (limited), uppercase text

// BYTE: Any 8-bit data (most flexible)
//   Example: "Hello, 世界!" (UTF-8 encoded: 16 bytes)
//   Encoding: 8 bits per byte = 128 bits total
//   Best for: mixed case text, Unicode, binary data

// KANJI: Shift JIS Kanji characters (efficient for Japanese)
//   Example: "東京" (2 Kanji characters)
//   Encoding: 13 bits per character = 26 bits total
//   Best for: Japanese text in Shift_JIS encoding

// Comparing modes
if (numeric.equals(Mode.NUMERIC)) {
  console.log("Mode is NUMERIC");
}

// Mode efficiency comparison
function calculateBits(data: string, mode: Mode, version: Version): number {
  const countBits: number = mode.getCharacterCountBits(version);
  const modeBits = 4;
  
  switch (mode) {
    case Mode.NUMERIC:
      const numericBits = Math.ceil(data.length / 3) * 10;
      return modeBits + countBits + numericBits;
    
    case Mode.ALPHANUMERIC:
      const alphaBits = Math.ceil(data.length / 2) * 11;
      return modeBits + countBits + alphaBits;
    
    case Mode.BYTE:
      const byteBits = data.length * 8;
      return modeBits + countBits + byteBits;
    
    case Mode.KANJI:
      const kanjiBits = data.length * 13;
      return modeBits + countBits + kanjiBits;
    
    default:
      return 0;
  }
}

const testData = "123456789012";
console.log(`"${testData}" bits needed:`);
console.log("  NUMERIC:", calculateBits(testData, Mode.NUMERIC, version1));
console.log("  BYTE:", calculateBits(testData, Mode.BYTE, version1));

// NUMERIC: 4 (mode) + 10 (count) + 40 (data) = 54 bits
// BYTE:    4 (mode) + 8 (count) + 96 (data) = 108 bits
// NUMERIC is much more efficient for digit-only data

Mode Selection Helper:

import { Mode, Encoder } from "@zxing/library";

// The encoder automatically chooses the best mode
function determineBestMode(content: string): Mode {
  // This is what the encoder does internally
  return Encoder.chooseMode(content);
}

console.log("123456:", determineBestMode("123456").toString());           // "NUMERIC"
console.log("HELLO:", determineBestMode("HELLO").toString());             // "ALPHANUMERIC"
console.log("Hello:", determineBestMode("Hello").toString());             // "BYTE" (lowercase)
console.log("Hello, World!:", determineBestMode("Hello, World!").toString()); // "BYTE" (comma not in alphanumeric)

// Character set validation
function isNumericOnly(text: string): boolean {
  return /^\d+$/.test(text);
}

function isAlphanumericOnly(text: string): boolean {
  // Valid: 0-9, A-Z, space, $ % * + - . / :
  return /^[0-9A-Z $%*+\-./:]+$/.test(text);
}

console.log("123456 is numeric:", isNumericOnly("123456"));        // true
console.log("HELLO is alphanumeric:", isAlphanumericOnly("HELLO")); // true
console.log("Hello is alphanumeric:", isAlphanumericOnly("Hello")); // false (lowercase)
console.log("A-B is alphanumeric:", isAlphanumericOnly("A-B"));    // true
console.log("A_B is alphanumeric:", isAlphanumericOnly("A_B"));    // false (underscore not allowed)

QR Code Versions

QR Code Encoder

Exported as: QRCodeEncoder

High-level encoding logic that orchestrates QR Code generation from text content.

/**
 * QR Code encoding logic and utilities
 * Provides high-level encoding, mode selection, and matrix generation
 */
class Encoder {
  /**
   * Default character encoding for byte mode
   */
  static DEFAULT_BYTE_MODE_ENCODING: string; // "UTF-8"

  /**
   * Encodes content as a QR Code with specified error correction
   * @param content - string: text content to encode
   * @param ecLevel - ErrorCorrectionLevel: error correction level (L, M, Q, H)
   * @param hints - Map<EncodeHintType, any>: optional hints (CHARACTER_SET, QR_VERSION, MARGIN)
   * @returns QRCode: encoded QR Code instance with matrix
   * @throws WriterException if encoding fails (data too large, invalid version)
   */
  static encode(
    content: string,
    ecLevel: ErrorCorrectionLevel,
    hints?: Map<EncodeHintType, any>
  ): QRCode;

  /**
   * Chooses the best encoding mode for content
   * Automatically selects NUMERIC, ALPHANUMERIC, BYTE, or KANJI
   * @param content - string: content to encode
   * @param encoding - string: optional character encoding hint (default: undefined)
   * @returns Mode: best mode for the content
   */
  static chooseMode(content: string, encoding?: string): Mode;

  /**
   * Gets alphanumeric code for a character
   * Valid characters: 0-9, A-Z, space, $ % * + - . / :
   * @param code - number: character code point
   * @returns number: alphanumeric code (0-44) or -1 if not alphanumeric
   */
  static getAlphanumericCode(code: number): number;
}

Encoding Process:

The Encoder orchestrates the complete QR Code generation:

  1. Choose optimal encoding mode (NUMERIC/ALPHANUMERIC/BYTE/KANJI)
  2. Select QR Code version based on data size and error correction level
  3. Encode data bits according to selected mode
  4. Add error correction codewords using Reed-Solomon
  5. Generate 8 candidate matrices with different mask patterns
  6. Select mask pattern with lowest penalty score
  7. Return final QR Code with matrix, version, mode, and mask pattern

Usage Examples:

import {
  Encoder,
  ErrorCorrectionLevel,
  QRCodeEncoderQRCode,
  Mode,
  EncodeHintType,
  Version
} from "@zxing/library";

// Basic encoding
const qrCode: QRCodeEncoderQRCode = Encoder.encode(
  "Hello, World!",
  ErrorCorrectionLevel.M
);

console.log("QR Code version:", qrCode.getVersion().getVersionNumber());
console.log("Mode:", qrCode.getMode().toString());
console.log("Error correction:", qrCode.getECLevel().toString());
console.log("Mask pattern:", qrCode.getMaskPattern());

const matrix = qrCode.getMatrix();
console.log("Matrix size:", matrix.getWidth(), "×", matrix.getHeight());

// Encoding with hints
const hints = new Map<EncodeHintType, any>();
hints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.set(EncodeHintType.QR_VERSION, 5); // Force version 5

const qrCode2: QRCodeEncoderQRCode = Encoder.encode(
  "Data for version 5",
  ErrorCorrectionLevel.H,
  hints
);

// Choosing encoding mode
const mode1: Mode = Encoder.chooseMode("123456");              // NUMERIC
const mode2: Mode = Encoder.chooseMode("HELLO");               // ALPHANUMERIC
const mode3: Mode = Encoder.chooseMode("Hello, World!");       // BYTE (lowercase + comma)
const mode4: Mode = Encoder.chooseMode("こんにちは", "Shift_JIS"); // KANJI

console.log("123456 →", mode1.toString());
console.log("HELLO →", mode2.toString());
console.log("Hello, World! →", mode3.toString());

// Checking alphanumeric validity
function isValidAlphanumeric(text: string): boolean {
  for (let i = 0; i < text.length; i++) {
    if (Encoder.getAlphanumericCode(text.charCodeAt(i)) === -1) {
      return false;
    }
  }
  return true;
}

console.log("HELLO123 is alphanumeric:", isValidAlphanumeric("HELLO123")); // true
console.log("Hello123 is alphanumeric:", isValidAlphanumeric("Hello123")); // false (lowercase)
console.log("HELLO-WORLD is alphanumeric:", isValidAlphanumeric("HELLO-WORLD")); // true
console.log("HELLO_WORLD is alphanumeric:", isValidAlphanumeric("HELLO_WORLD")); // false (underscore)

// Valid alphanumeric characters
const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
console.log("Valid alphanumeric:", validChars);

// Get alphanumeric codes
console.log("'0' code:", Encoder.getAlphanumericCode('0'.charCodeAt(0))); // 0
console.log("'9' code:", Encoder.getAlphanumericCode('9'.charCodeAt(0))); // 9
console.log("'A' code:", Encoder.getAlphanumericCode('A'.charCodeAt(0))); // 10
console.log("'Z' code:", Encoder.getAlphanumericCode('Z'.charCodeAt(0))); // 35
console.log("' ' code:", Encoder.getAlphanumericCode(' '.charCodeAt(0))); // 36
console.log("'$' code:", Encoder.getAlphanumericCode('$'.charCodeAt(0))); // 37
console.log("'_' code:", Encoder.getAlphanumericCode('_'.charCodeAt(0))); // -1 (invalid)

Encoding Efficiency:

import { Mode, Version } from "@zxing/library";

// Bits required for different content types
interface EncodingStats {
  mode: string;
  modeBits: number;
  countBits: number;
  dataBits: number;
  totalBits: number;
}

function analyzeEncoding(content: string, mode: Mode, version: Version): EncodingStats {
  const modeBits = 4;
  const countBits = mode.getCharacterCountBits(version);
  let dataBits = 0;

  if (mode === Mode.NUMERIC) {
    dataBits = Math.ceil(content.length / 3) * 10;
  } else if (mode === Mode.ALPHANUMERIC) {
    dataBits = Math.ceil(content.length / 2) * 11;
  } else if (mode === Mode.BYTE) {
    dataBits = content.length * 8;
  } else if (mode === Mode.KANJI) {
    dataBits = content.length * 13;
  }

  return {
    mode: mode.toString(),
    modeBits,
    countBits,
    dataBits,
    totalBits: modeBits + countBits + dataBits
  };
}

const version1 = Version.getVersionForNumber(1);

// Compare encoding efficiency
const numericData = "123456789012"; // 12 digits
console.log("Numeric data:", numericData);
console.log(analyzeEncoding(numericData, Mode.NUMERIC, version1));
// { mode: "NUMERIC", modeBits: 4, countBits: 10, dataBits: 40, totalBits: 54 }

console.log(analyzeEncoding(numericData, Mode.BYTE, version1));
// { mode: "BYTE", modeBits: 4, countBits: 8, dataBits: 96, totalBits: 108 }
// NUMERIC saves 54 bits (50% more efficient)

const alphaData = "HELLO WORLD"; // 11 characters
console.log("Alphanumeric data:", alphaData);
console.log(analyzeEncoding(alphaData, Mode.ALPHANUMERIC, version1));
// { mode: "ALPHANUMERIC", modeBits: 4, countBits: 9, dataBits: 61, totalBits: 74 }

console.log(analyzeEncoding(alphaData, Mode.BYTE, version1));
// { mode: "BYTE", modeBits: 4, countBits: 8, dataBits: 88, totalBits: 100 }
// ALPHANUMERIC saves 26 bits (26% more efficient)

QR Code Mode | Error Correction Levels

QR Code Representation

Exported as: QRCodeEncoderQRCode

Represents a QR Code with all its properties and matrix.

/**
 * Represents a QR Code with its configuration and matrix
 * Container for encoded QR Code data, metadata, and rendering matrix
 */
class QRCode {
  /**
   * Number of mask patterns defined in QR Code standard
   */
  static NUM_MASK_PATTERNS: number; // 8

  /**
   * Create empty QR Code
   */
  constructor();

  /**
   * Gets the encoding mode
   * @returns Mode: encoding mode used (NUMERIC, ALPHANUMERIC, BYTE, etc.)
   */
  getMode(): Mode;

  /**
   * Sets the encoding mode
   * @param value - Mode: mode to set
   */
  setMode(value: Mode): void;

  /**
   * Gets the error correction level
   * @returns ErrorCorrectionLevel: EC level (L, M, Q, or H)
   */
  getECLevel(): ErrorCorrectionLevel;

  /**
   * Sets the error correction level
   * @param value - ErrorCorrectionLevel: error correction level to set
   */
  setECLevel(value: ErrorCorrectionLevel): void;

  /**
   * Gets the QR Code version
   * @returns Version: version (1-40)
   */
  getVersion(): Version;

  /**
   * Sets the QR Code version
   * @param version - Version: version to set (1-40)
   */
  setVersion(version: Version): void;

  /**
   * Gets the mask pattern (0-7)
   * @returns number: mask pattern index (0-7, or -1 if not set)
   */
  getMaskPattern(): number;

  /**
   * Sets the mask pattern
   * @param value - number: mask pattern index (0-7)
   * @throws WriterException if mask pattern is invalid
   */
  setMaskPattern(value: number): void;

  /**
   * Gets the byte matrix containing the QR Code
   * @returns ByteMatrix: matrix with QR Code modules
   */
  getMatrix(): ByteMatrix;

  /**
   * Sets the byte matrix
   * @param value - ByteMatrix: matrix to set
   */
  setMatrix(value: ByteMatrix): void;

  /**
   * Validates mask pattern value
   * @param maskPattern - number: mask pattern to validate
   * @returns boolean: true if mask pattern is valid (0-7), false otherwise
   */
  static isValidMaskPattern(maskPattern: number): boolean;

  /**
   * String representation of QR Code with all properties
   * @returns string: formatted string showing mode, EC level, version, mask pattern, and matrix
   */
  toString(): string;
}

Usage Examples:

import {
  QRCode,
  QRCodeEncoderQRCode,
  Encoder,
  ErrorCorrectionLevel,
  Mode,
  Version,
  ByteMatrix
} from "@zxing/library";

// Creating QR Code through encoder (typical usage)
const qrCode: QRCodeEncoderQRCode = Encoder.encode(
  "Hello, World!",
  ErrorCorrectionLevel.M
);

// Accessing properties
console.log("Mode:", qrCode.getMode().toString());                 // "BYTE"
console.log("Error Correction:", qrCode.getECLevel().toString());  // "M"
console.log("Version:", qrCode.getVersion().getVersionNumber());   // e.g., 1
console.log("Mask Pattern:", qrCode.getMaskPattern());             // e.g., 2 (0-7)

// Getting matrix for rendering
const matrix: ByteMatrix = qrCode.getMatrix();
console.log("Matrix dimensions:", matrix.getWidth(), "×", matrix.getHeight());

// Reading module values from matrix
for (let y = 0; y < matrix.getHeight(); y++) {
  for (let x = 0; x < matrix.getWidth(); x++) {
    const value: number = matrix.get(x, y);
    // value: -1 = not set, 0 = white/light module, 1 = black/dark module
  }
}

// Manually creating QR Code (advanced usage)
const manualQR = new QRCode();
manualQR.setMode(Mode.ALPHANUMERIC);
manualQR.setECLevel(ErrorCorrectionLevel.H);
manualQR.setVersion(Version.getVersionForNumber(5));
manualQR.setMaskPattern(3);

const manualMatrix = new ByteMatrix(37, 37); // Version 5 is 37×37
// ... populate matrix ...
manualQR.setMatrix(manualMatrix);

// Validating mask pattern
console.log("Is 5 valid mask?", QRCode.isValidMaskPattern(5));  // true
console.log("Is 8 valid mask?", QRCode.isValidMaskPattern(8));  // false
console.log("Is -1 valid mask?", QRCode.isValidMaskPattern(-1)); // false

// Valid mask patterns: 0, 1, 2, 3, 4, 5, 6, 7
for (let i = 0; i < 8; i++) {
  console.log(`Mask ${i}:`, QRCode.isValidMaskPattern(i)); // All true
}

// Debug output
console.log(qrCode.toString());
// Output format:
// <<
//  mode: BYTE
//  ecLevel: M
//  version: 1
//  maskPattern: 2
//  matrix:
//  [matrix visualization]
// >>

// Analyze QR Code
function analyzeQRCode(qr: QRCode): {
  version: number;
  dimension: number;
  mode: string;
  ecLevel: string;
  maskPattern: number;
  totalModules: number;
} {
  const version = qr.getVersion();
  const dimension = version.getDimensionForVersion();

  return {
    version: version.getVersionNumber(),
    dimension,
    mode: qr.getMode().toString(),
    ecLevel: qr.getECLevel().toString(),
    maskPattern: qr.getMaskPattern(),
    totalModules: dimension * dimension
  };
}

const analysis = analyzeQRCode(qrCode);
console.log("QR Code analysis:", analysis);

Mask Patterns:

QR Code uses 8 different mask patterns to break up patterns and improve readability. The encoder tests all 8 and selects the one with the lowest penalty score.

// Mask pattern formulas (applied to data region only):
// Pattern 0: (x + y) mod 2 == 0        (checkerboard)
// Pattern 1: y mod 2 == 0              (horizontal stripes)
// Pattern 2: x mod 3 == 0              (vertical stripes, 1:2 ratio)
// Pattern 3: (x + y) mod 3 == 0        (diagonal stripes, 1:2 ratio)
// Pattern 4: (floor(y/2) + floor(x/3)) mod 2 == 0  (larger blocks)
// Pattern 5: ((x*y) mod 2) + ((x*y) mod 3) == 0    (pattern based on x*y)
// Pattern 6: (((x*y) mod 2) + ((x*y) mod 3)) mod 2 == 0
// Pattern 7: (((x+y) mod 2) + ((x*y) mod 3)) mod 2 == 0

// The encoder calculates penalty for each mask pattern:
// Penalty 1: Consecutive runs of same-colored modules (horizontal and vertical)
// Penalty 2: 2×2 blocks of same color
// Penalty 3: Patterns resembling finder patterns (false positives)
// Penalty 4: Ratio of dark to light modules (ideal is 50:50)

QR Code Mode | Error Correction Levels | Byte Matrix

Additional Classes

Byte Matrix

Exported as: QRCodeByteMatrix

2D array of bytes representing the QR Code modules.

/**
 * 2D matrix of bytes for QR Code representation
 * Values: -1 (not set/reserved), 0 (white/light module), 1 (black/dark module)
 */
class ByteMatrix {
  /**
   * Constructs a byte matrix
   * @param width - number: width in modules
   * @param height - number: height in modules
   */
  constructor(width: number, height: number);

  /**
   * Gets the matrix height
   * @returns number: height in modules
   */
  getHeight(): number;

  /**
   * Gets the matrix width
   * @returns number: width in modules
   */
  getWidth(): number;

  /**
   * Gets the byte value at position
   * @param x - number: x coordinate (column)
   * @param y - number: y coordinate (row)
   * @returns number: byte value (-1, 0, or 1)
   */
  get(x: number, y: number): number;

  /**
   * Gets the internal array representation
   * Access pattern: array[y][x]
   * @returns Array<Uint8Array>: array of rows
   */
  getArray(): Array<Uint8Array>;

  /**
   * Sets a numeric value at position
   * @param x - number: x coordinate
   * @param y - number: y coordinate
   * @param value - number: byte value to set (-1, 0, or 1)
   */
  setNumber(x: number, y: number, value: number): void;

  /**
   * Sets a boolean value at position
   * @param x - number: x coordinate
   * @param y - number: y coordinate
   * @param value - boolean: true sets 1 (black), false sets 0 (white)
   */
  setBoolean(x: number, y: number, value: boolean): void;

  /**
   * Clears matrix with specified value
   * @param value - number: value to fill matrix with (typically -1 or 0)
   */
  clear(value: number): void;

  /**
   * Checks equality with another matrix
   * @param o - any: object to compare
   * @returns boolean: true if dimensions and all values are equal
   */
  equals(o: any): boolean;

  /**
   * String representation for debugging
   * @returns string: formatted string showing matrix (0s, 1s, and spaces for -1)
   */
  toString(): string;
}

Module Value Meanings:

  • -1: Not set / reserved (for function patterns, format info, etc.)
  • 0: White/light module
  • 1: Black/dark module

Usage Examples:

import { ByteMatrix, Encoder, ErrorCorrectionLevel, QRCodeEncoderQRCode } from "@zxing/library";

// Getting matrix from encoded QR Code
const qrCode: QRCodeEncoderQRCode = Encoder.encode("Hello", ErrorCorrectionLevel.M);
const matrix: ByteMatrix = qrCode.getMatrix();

console.log("Width:", matrix.getWidth());
console.log("Height:", matrix.getHeight());

// Reading module values
const topLeftModule: number = matrix.get(0, 0);
console.log("Top-left module:", topLeftModule); // Typically 1 (black, part of finder pattern)

// Creating custom matrix
const customMatrix = new ByteMatrix(25, 25);

// Clearing matrix
customMatrix.clear(-1); // Set all to "not set"
customMatrix.clear(0);  // Set all to white

// Setting individual modules
customMatrix.setNumber(10, 10, 1);        // Set black module
customMatrix.setBoolean(11, 10, true);    // Set black module (boolean)
customMatrix.setBoolean(12, 10, false);   // Set white module

// Drawing a 7×7 finder pattern at top-left (0,0)
for (let x = 0; x < 7; x++) {
  for (let y = 0; y < 7; y++) {
    // Finder pattern: border and 3×3 center are black
    if (
      x === 0 || x === 6 ||  // Left and right borders
      y === 0 || y === 6 ||  // Top and bottom borders
      (x >= 2 && x <= 4 && y >= 2 && y <= 4) // 3×3 center
    ) {
      customMatrix.setNumber(x, y, 1);
    } else {
      customMatrix.setNumber(x, y, 0);
    }
  }
}

// Accessing internal array (for performance-critical operations)
const array: Array<Uint8Array> = matrix.getArray();
for (let y = 0; y < matrix.getHeight(); y++) {
  const row: Uint8Array = array[y];
  for (let x = 0; x < matrix.getWidth(); x++) {
    const value: number = row[x];
    // Process module
  }
}

// Rendering to canvas with module scaling
function renderMatrixToCanvas(
  matrix: ByteMatrix,
  canvas: HTMLCanvasElement,
  moduleSize: number
): void {
  const ctx = canvas.getContext("2d")!;
  canvas.width = matrix.getWidth() * moduleSize;
  canvas.height = matrix.getHeight() * moduleSize;

  // White background
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Draw black modules
  ctx.fillStyle = "black";
  for (let y = 0; y < matrix.getHeight(); y++) {
    for (let x = 0; x < matrix.getWidth(); x++) {
      const value: number = matrix.get(x, y);
      if (value === 1) {
        ctx.fillRect(x * moduleSize, y * moduleSize, moduleSize, moduleSize);
      }
    }
  }
}

// Usage
const canvas = document.getElementById('qr-canvas') as HTMLCanvasElement;
renderMatrixToCanvas(matrix, canvas, 10); // 10 pixels per module

// Comparing matrices
const qrCode2: QRCodeEncoderQRCode = Encoder.encode("Hello", ErrorCorrectionLevel.M);
const matrix2: ByteMatrix = qrCode2.getMatrix();
console.log("Matrices equal:", matrix.equals(matrix2)); // true (same input and EC level)

// Debug output (shows matrix structure)
console.log(matrix.toString());
// Shows matrix as text:
//  1 1 1 1 1 1 1 0 ...
//  1 0 0 0 0 0 1 0 ...
//  1 0 1 1 1 0 1 0 ...
//  ...

Advanced Topics

Custom Encoding Pipeline

import {
  Encoder,
  ErrorCorrectionLevel,
  QRCodeEncoderQRCode,
  Mode,
  Version,
  BitArray,
  EncodeHintType
} from "@zxing/library";

// Complete custom encoding process
function customEncode(content: string): QRCodeEncoderQRCode {
  // Step 1: Choose mode
  const mode: Mode = Encoder.chooseMode(content);
  console.log("Step 1 - Mode:", mode.toString());

  // Step 2: Determine version and EC level
  const ecLevel = ErrorCorrectionLevel.M;
  
  // Step 3: Encode (encoder handles version selection automatically)
  const hints = new Map<EncodeHintType, any>();
  hints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
  
  const qrCode: QRCodeEncoderQRCode = Encoder.encode(content, ecLevel, hints);
  
  console.log("Step 2 - Version:", qrCode.getVersion().getVersionNumber());
  console.log("Step 3 - Mask:", qrCode.getMaskPattern());
  
  return qrCode;
}

const encoded = customEncode("Custom encoding test");

Error Handling

import {
  QRCodeReader,
  QRCodeWriter,
  BarcodeFormat,
  BinaryBitmap,
  NotFoundException,
  FormatException,
  ChecksumException,
  WriterException,
  IllegalArgumentException,
  ErrorCorrectionLevel,
  BitMatrix
} from "@zxing/library";

// Reading errors
function safeRead(bitmap: BinaryBitmap): string | null {
  const reader = new QRCodeReader();

  try {
    const result = reader.decode(bitmap);
    return result.getText();
  } catch (error) {
    if (error instanceof NotFoundException) {
      console.error("No QR code found in image");
      // Image may not contain QR code, or QR code is too small/damaged
    } else if (error instanceof FormatException) {
      console.error("QR code format is invalid");
      // QR code structure is malformed
    } else if (error instanceof ChecksumException) {
      console.error("QR code error correction failed");
      // Too many errors for current error correction level
    } else {
      console.error("Unknown read error:", error);
    }
    return null;
  }
}

// Writing errors
function safeWrite(contents: string): BitMatrix | null {
  const writer = new QRCodeWriter();

  try {
    const matrix: BitMatrix = writer.encode(
      contents,
      BarcodeFormat.QR_CODE,
      300,
      300
    );
    return matrix;
  } catch (error) {
    if (error instanceof WriterException) {
      console.error("QR code encoding failed:", error.message);
      // Data may be too large for QR code, or invalid version specified
    } else if (error instanceof IllegalArgumentException) {
      console.error("Invalid parameters:", error.message);
      // Check format, dimensions, or hint values
    } else {
      console.error("Unknown write error:", error);
    }
    return null;
  }
}

// Version-specific encoding
import { EncodeHintType } from "@zxing/library";

function encodeWithVersionFallback(
  content: string,
  preferredVersion: number
): BitMatrix | null {
  const writer = new QRCodeWriter();
  const hints = new Map<EncodeHintType, any>();
  hints.set(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);

  // Try preferred version first
  hints.set(EncodeHintType.QR_VERSION, preferredVersion);
  try {
    return writer.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
  } catch (e) {
    console.warn(`Version ${preferredVersion} failed, trying auto-select`);
  }

  // Fall back to automatic version selection
  hints.delete(EncodeHintType.QR_VERSION);
  try {
    return writer.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
  } catch (e) {
    console.error("Encoding failed even with auto-select:", e);
    return null;
  }
}

Decode Hints

Hints that can be passed to QR Code readers:

/**
 * Decode hints for QR Code reading
 */
interface QRCodeDecodeHints {
  /**
   * PURE_BARCODE: boolean
   * Set to true if image contains only QR code (no border, no rotation)
   * Enables faster extraction without detection
   */
  [DecodeHintType.PURE_BARCODE]?: boolean;

  /**
   * TRY_HARDER: boolean
   * Spend more time to find QR code
   * Tries more detection strategies and rotation angles
   */
  [DecodeHintType.TRY_HARDER]?: boolean;

  /**
   * CHARACTER_SET: string
   * Character encoding for text interpretation
   * Examples: "UTF-8", "ISO-8859-1", "Shift_JIS", "GB2312"
   */
  [DecodeHintType.CHARACTER_SET]?: string;

  /**
   * NEED_RESULT_POINT_CALLBACK: ResultPointCallback
   * Callback for finder pattern and alignment pattern detection
   */
  [DecodeHintType.NEED_RESULT_POINT_CALLBACK]?: ResultPointCallback;
}

Hint Usage Examples:

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

const reader = new QRCodeReader();

// 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);

// Shift JIS for Japanese text
const sjisHints = new Map<DecodeHintType, any>();
sjisHints.set(DecodeHintType.CHARACTER_SET, "Shift_JIS");
const sjisResult: Result = reader.decode(japaneseBitmap, sjisHints);

Encode Hints

Hints that can be passed to QR Code writers:

/**
 * Encode hints for QR Code writing
 */
interface QRCodeEncodeHints {
  /**
   * ERROR_CORRECTION: ErrorCorrectionLevel or string
   * Error correction level: L (~7%), M (~15%), Q (~25%), H (~30%)
   * Can be ErrorCorrectionLevel instance or string "L"|"M"|"Q"|"H"
   */
  [EncodeHintType.ERROR_CORRECTION]?: ErrorCorrectionLevel | string;

  /**
   * CHARACTER_SET: string
   * Character encoding for text data
   * Examples: "UTF-8", "ISO-8859-1", "Shift_JIS"
   */
  [EncodeHintType.CHARACTER_SET]?: string;

  /**
   * QR_VERSION: number
   * Force specific QR Code version (1-40)
   * If data doesn't fit, encoding will fail
   * Omit to let encoder auto-select optimal version
   */
  [EncodeHintType.QR_VERSION]?: number;

  /**
   * MARGIN: number
   * Quiet zone size in modules (default: 4)
   * ISO 18004 specifies minimum of 4 modules
   * Smaller values may work but violate standard
   */
  [EncodeHintType.MARGIN]?: number;
}

Hint Usage Examples:

import {
  QRCodeWriter,
  BarcodeFormat,
  EncodeHintType,
  ErrorCorrectionLevel,
  BitMatrix
} from "@zxing/library";

const writer = new QRCodeWriter();

// Maximum error correction
const maxECHints = new Map<EncodeHintType, any>();
maxECHints.set(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
const maxECMatrix: BitMatrix = writer.encode(
  "Critical data",
  BarcodeFormat.QR_CODE,
  300,
  300,
  maxECHints
);

// UTF-8 for international text
const utf8Hints = new Map<EncodeHintType, any>();
utf8Hints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
utf8Hints.set(EncodeHintType.ERROR_CORRECTION, "M");
const utf8Matrix: BitMatrix = writer.encode(
  "Hello 世界 🌍",
  BarcodeFormat.QR_CODE,
  300,
  300,
  utf8Hints
);

// Force specific version
const versionHints = new Map<EncodeHintType, any>();
versionHints.set(EncodeHintType.QR_VERSION, 5);
versionHints.set(EncodeHintType.ERROR_CORRECTION, "Q");
const versionMatrix: BitMatrix = writer.encode(
  "Version 5 data",
  BarcodeFormat.QR_CODE,
  300,
  300,
  versionHints
);

// Minimal margin (space-constrained)
const minMarginHints = new Map<EncodeHintType, any>();
minMarginHints.set(EncodeHintType.MARGIN, 1); // Minimum 1 module
const minMarginMatrix: BitMatrix = writer.encode(
  "Compact QR",
  BarcodeFormat.QR_CODE,
  200,
  200,
  minMarginHints
);

// Combined hints
const combinedHints = new Map<EncodeHintType, any>();
combinedHints.set(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
combinedHints.set(EncodeHintType.CHARACTER_SET, "UTF-8");
combinedHints.set(EncodeHintType.MARGIN, 2);
// Omit QR_VERSION to let encoder auto-select

const optimizedMatrix: BitMatrix = writer.encode(
  "Optimized QR Code",
  BarcodeFormat.QR_CODE,
  400,
  400,
  combinedHints
);

Related Documentation

  • Core API - Core reader/writer interfaces and Result classes
  • Common Utilities - BitArray, BitMatrix classes
  • Error Correction - Reed-Solomon implementation
  • Image Processing - BinaryBitmap, LuminanceSource
  • Types and Enums - BarcodeFormat, hint type enums

References

  • ISO 18004:2006: QR Code bar code symbology specification
  • Encoding: Numeric, Alphanumeric, Byte (8-bit), Kanji (Shift_JIS)
  • Error Correction: Reed-Solomon over GF(256)
  • Versions: 1-40 (21×21 to 177×177 modules)
  • Mask Patterns: 8 patterns (0-7) for optimal scanning

Install with Tessl CLI

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

docs

index.md

tile.json