TypeScript port of ZXing multi-format 1D/2D barcode image processing library
This document covers the primary entry points for barcode decoding and encoding in @zxing/library. These high-level APIs automatically detect barcode formats and delegate to format-specific implementations.
import {
// Primary reader/writer classes
MultiFormatReader,
MultiFormatWriter,
// Base interfaces
Reader,
Writer,
// Result types
Result,
ResultPoint,
ResultPointCallback,
// Hint enums
DecodeHintType,
EncodeHintType,
// Barcode format
BarcodeFormat,
// Data structures
BitMatrix,
BinaryBitmap
} from '@zxing/library';Primary barcode decoder that auto-detects format. Supports configurable hints for optimization and format filtering.
/**
* Multi-format barcode reader with auto-format detection
* Delegates to format-specific readers based on hints
*/
class MultiFormatReader implements Reader {
/**
* Decode a barcode from a binary bitmap
* Attempts to decode using all configured format readers
* @param image - BinaryBitmap: binary representation of image containing barcode
* @param hints - Map<DecodeHintType, any>: optional hints for decoding behavior
* - POSSIBLE_FORMATS: BarcodeFormat[] to limit format attempts
* - TRY_HARDER: boolean for accuracy over speed
* - CHARACTER_SET: string for text encoding
* - PURE_BARCODE: boolean if image contains only barcode
* @returns Result: decoded barcode with text, format, and metadata
* @throws NotFoundException if no barcode is found in any format
* @throws FormatException if barcode is found but invalid
* @throws ChecksumException if checksum validation fails
*/
decode(image: BinaryBitmap, hints?: Map<DecodeHintType, any>): Result;
/**
* Decode using previously set hints
* More efficient for continuous scanning as hints are cached
* Call setHints() first to configure reader
* @param image - BinaryBitmap: binary image containing barcode
* @returns Result: decoded barcode data
* @throws NotFoundException if no barcode is found
* @throws FormatException if barcode is invalid
* @throws ChecksumException if checksum fails
*/
decodeWithState(image: BinaryBitmap): Result;
/**
* Set hints for subsequent decode operations
* Hints are cached for use with decodeWithState()
* @param hints - Map<DecodeHintType, any>|null: decode hints, or null to clear
*/
setHints(hints: Map<DecodeHintType, any> | null): void;
/**
* Reset reader state
* Clears cached hints and resets internal readers
*/
reset(): void;
}Usage Examples:
import {
MultiFormatReader,
BinaryBitmap,
HybridBinarizer,
RGBLuminanceSource,
DecodeHintType,
BarcodeFormat,
Result
} from '@zxing/library';
// Prepare image
const luminanceSource = new RGBLuminanceSource(pixels, width, height);
const binaryBitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
// Configure reader with hints
const reader = new MultiFormatReader();
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.QR_CODE,
BarcodeFormat.EAN_13,
BarcodeFormat.CODE_128
]);
hints.set(DecodeHintType.TRY_HARDER, true);
hints.set(DecodeHintType.CHARACTER_SET, 'UTF-8');
// Decode with hints
try {
const result: Result = reader.decode(binaryBitmap, hints);
console.log('Text:', result.getText());
console.log('Format:', result.getBarcodeFormat());
console.log('Raw bytes:', result.getRawBytes());
console.log('Timestamp:', result.getTimestamp());
} catch (error) {
console.error('Decode failed:', error);
}
// For continuous scanning (more efficient with setHints)
reader.setHints(hints);
// Process multiple frames
const frames: BinaryBitmap[] = [bitmap1, bitmap2, bitmap3];
for (const bitmap of frames) {
try {
const result: Result = reader.decodeWithState(bitmap);
console.log('Frame decoded:', result.getText());
break; // Stop on first success
} catch (error) {
// Continue to next frame
}
}
// Reset when done
reader.reset();Decode without hints (tries all formats):
import { MultiFormatReader, BinaryBitmap, Result } from '@zxing/library';
const reader = new MultiFormatReader();
try {
// Attempts all formats: QR, Data Matrix, Aztec, PDF417, and all 1D formats
const result: Result = reader.decode(binaryBitmap);
console.log('Decoded:', result.getText());
console.log('Format:', result.getBarcodeFormat());
} catch (error) {
console.error('No barcode found in any format:', error);
}Progressive hint strategy:
import { MultiFormatReader, DecodeHintType, BarcodeFormat, BinaryBitmap, Result } from '@zxing/library';
async function decodeWithProgression(bitmap: BinaryBitmap): Promise<Result | null> {
const reader = new MultiFormatReader();
// Strategy 1: Fast decode with common formats
try {
const hints1 = new Map<DecodeHintType, any>();
hints1.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.QR_CODE,
BarcodeFormat.EAN_13
]);
return reader.decode(bitmap, hints1);
} catch (e) {
console.debug('Fast decode failed, trying harder...');
}
// Strategy 2: Try harder with same formats
try {
const hints2 = new Map<DecodeHintType, any>();
hints2.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.QR_CODE,
BarcodeFormat.EAN_13
]);
hints2.set(DecodeHintType.TRY_HARDER, true);
return reader.decode(bitmap, hints2);
} catch (e) {
console.debug('Hard decode failed, trying all formats...');
}
// Strategy 3: Try all formats with TRY_HARDER
try {
const hints3 = new Map<DecodeHintType, any>();
hints3.set(DecodeHintType.TRY_HARDER, true);
return reader.decode(bitmap, hints3);
} catch (e) {
console.debug('All strategies failed');
}
return null;
}Primary barcode encoder. Creates barcode matrix from text content.
/**
* Multi-format barcode writer
* Delegates to format-specific writers
*/
class MultiFormatWriter implements Writer {
/**
* Encode content as a barcode in specified format
* @param contents - string: text content to encode
* @param format - BarcodeFormat: barcode format to generate (QR_CODE, DATA_MATRIX, AZTEC, PDF_417, CODE_128, CODE_39, EAN_13, EAN_8, UPC_A, ITF)
* @param width - number: width in pixels
* @param height - number: height in pixels
* @param hints - Map<EncodeHintType, any>: encoding hints (pass new Map() if no hints needed)
* - ERROR_CORRECTION: varies by format
* - CHARACTER_SET: string (e.g., "UTF-8")
* - MARGIN: number (pixels)
* - QR_VERSION: number (1-40 for QR Code)
* - AZTEC_LAYERS: number (negative=compact, 0=auto, positive=full)
* - DATA_MATRIX_SHAPE: SymbolShapeHint
* - GS1_FORMAT: boolean
* @returns BitMatrix: encoded barcode as binary matrix
* @throws WriterException if encoding fails
* @throws IllegalArgumentException if parameters are invalid
* @throws UnsupportedOperationException if format doesn't support encoding
*/
encode(
contents: string,
format: BarcodeFormat,
width: number,
height: number,
hints: Map<EncodeHintType, any>
): BitMatrix;
}Usage Examples:
import {
MultiFormatWriter,
BarcodeFormat,
EncodeHintType,
QRCodeDecoderErrorCorrectionLevel,
BitMatrix
} from '@zxing/library';
const writer = new MultiFormatWriter();
// Encode QR Code with hints
const qrHints = new Map<EncodeHintType, any>();
qrHints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.H);
qrHints.set(EncodeHintType.MARGIN, 1);
qrHints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
const qrMatrix: BitMatrix = writer.encode(
'https://example.com',
BarcodeFormat.QR_CODE,
300,
300,
qrHints
);
// Render to canvas/image
const width: number = qrMatrix.getWidth();
const height: number = qrMatrix.getHeight();
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const isBlack: boolean = qrMatrix.get(x, y);
// Draw black or white pixel at (x, y)
}
}Encode different formats:
import {
MultiFormatWriter,
BarcodeFormat,
EncodeHintType,
SymbolShapeHint,
BitMatrix
} from '@zxing/library';
const writer = new MultiFormatWriter();
// Data Matrix with square shape
const dmHints = new Map<EncodeHintType, any>();
dmHints.set(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
dmHints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
const dmMatrix: BitMatrix = writer.encode(
'Data Matrix',
BarcodeFormat.DATA_MATRIX,
200,
200,
dmHints
);
// Aztec with compact mode
const aztecHints = new Map<EncodeHintType, any>();
aztecHints.set(EncodeHintType.AZTEC_LAYERS, -3); // Compact, 3 layers
aztecHints.set(EncodeHintType.ERROR_CORRECTION, 33); // 33% ECC
const aztecMatrix: BitMatrix = writer.encode(
'Aztec Code',
BarcodeFormat.AZTEC,
0, // 0 = auto-size
0,
aztecHints
);
// Code 128 with margin
const code128Hints = new Map<EncodeHintType, any>();
code128Hints.set(EncodeHintType.MARGIN, 10);
const code128Matrix: BitMatrix = writer.encode(
'CODE128',
BarcodeFormat.CODE_128,
300,
100,
code128Hints
);
// EAN-13 (must be 12 or 13 digits, check digit auto-calculated)
const ean13Matrix: BitMatrix = writer.encode(
'123456789012', // 12 digits, check digit added automatically
BarcodeFormat.EAN_13,
200,
100,
new Map()
);Error handling:
import {
MultiFormatWriter,
BarcodeFormat,
WriterException,
IllegalArgumentException,
UnsupportedOperationException,
BitMatrix
} from '@zxing/library';
const writer = new MultiFormatWriter();
try {
const matrix: BitMatrix = writer.encode(
'Data to encode',
BarcodeFormat.QR_CODE,
200,
200,
new Map()
);
console.log('Encoding successful');
} catch (error) {
if (error instanceof WriterException) {
console.error('Encoding failed:', error.message);
// Data may be incompatible with format
} else if (error instanceof IllegalArgumentException) {
console.error('Invalid parameters:', error.message);
// Check dimensions, format, or hints
} else if (error instanceof UnsupportedOperationException) {
console.error('Format not supported for encoding:', error.message);
// MaxiCode and some others don't have writers
} else {
console.error('Unexpected error:', error);
}
}Base interface for barcode readers.
/**
* Interface for barcode decoders
* All format-specific readers implement this interface
*/
interface Reader {
/**
* Decode a barcode from binary image
* @param image - BinaryBitmap: binary representation of image to decode
* @param hints - Map<DecodeHintType, any>: optional decoding hints
* @returns Result: decoded barcode with text and metadata
* @throws NotFoundException if no barcode found
* @throws FormatException if barcode is invalid
* @throws ChecksumException if checksum validation fails
*/
decode(image: BinaryBitmap, hints?: Map<DecodeHintType, any>): Result;
/**
* Reset reader state
* Clears cached data and prepares for next decode
*/
reset(): void;
}Implementation Examples:
import { Reader, BinaryBitmap, Result, DecodeHintType } from '@zxing/library';
// Using any Reader implementation
function decodeWithReader(reader: Reader, bitmap: BinaryBitmap): Result | null {
try {
const result: Result = reader.decode(bitmap);
return result;
} catch (error) {
console.error('Decode failed:', error);
return null;
}
}
// With hints
function decodeWithReaderAndHints(
reader: Reader,
bitmap: BinaryBitmap,
hints: Map<DecodeHintType, any>
): Result | null {
try {
const result: Result = reader.decode(bitmap, hints);
return result;
} catch (error) {
console.error('Decode with hints failed:', error);
return null;
}
}
// Multiple decode attempts with different readers
import { QRCodeReader, DataMatrixReader, AztecCodeReader } from '@zxing/library';
function tryMultipleReaders(bitmap: BinaryBitmap): Result | null {
const readers: Reader[] = [
new QRCodeReader(),
new DataMatrixReader(),
new AztecCodeReader()
];
for (const reader of readers) {
try {
const result: Result = reader.decode(bitmap);
console.log(`Decoded with ${reader.constructor.name}`);
return result;
} catch (error) {
// Try next reader
}
}
console.error('All readers failed');
return null;
}Base interface for barcode encoders.
/**
* Interface for barcode encoders
* All format-specific writers implement this interface
*/
interface Writer {
/**
* Encode content as barcode matrix
* @param contents - string: text content to encode
* @param format - BarcodeFormat: barcode format to generate
* @param width - number: desired width in pixels
* @param height - number: desired height in pixels
* @param hints - Map<EncodeHintType, any>: optional encoding hints
* @returns BitMatrix: encoded barcode as binary matrix where true=black, false=white
* @throws WriterException if encoding fails
* @throws IllegalArgumentException if parameters are invalid
*/
encode(
contents: string,
format: BarcodeFormat,
width: number,
height: number,
hints?: Map<EncodeHintType, any>
): BitMatrix;
}Implementation Examples:
import { Writer, BarcodeFormat, BitMatrix, EncodeHintType } from '@zxing/library';
// Using any Writer implementation
function encodeWithWriter(
writer: Writer,
contents: string,
format: BarcodeFormat,
width: number,
height: number
): BitMatrix | null {
try {
const matrix: BitMatrix = writer.encode(contents, format, width, height, new Map());
return matrix;
} catch (error) {
console.error('Encode failed:', error);
return null;
}
}
// With hints
function encodeWithWriterAndHints(
writer: Writer,
contents: string,
format: BarcodeFormat,
width: number,
height: number,
hints: Map<EncodeHintType, any>
): BitMatrix | null {
try {
const matrix: BitMatrix = writer.encode(contents, format, width, height, hints);
return matrix;
} catch (error) {
console.error('Encode with hints failed:', error);
return null;
}
}
// Batch encoding with single writer instance
import { QRCodeWriter } from '@zxing/library';
function encodeBatch(contents: string[]): BitMatrix[] {
const writer: Writer = new QRCodeWriter();
const hints = new Map<EncodeHintType, any>();
hints.set(EncodeHintType.ERROR_CORRECTION, 'M');
hints.set(EncodeHintType.MARGIN, 2);
return contents.map(content => {
try {
return writer.encode(content, BarcodeFormat.QR_CODE, 200, 200, hints);
} catch (error) {
console.error(`Failed to encode "${content}":`, error);
return null;
}
}).filter((m): m is BitMatrix => m !== null);
}
const matrices: BitMatrix[] = encodeBatch([
'https://example.com/1',
'https://example.com/2',
'https://example.com/3'
]);
console.log(`Encoded ${matrices.length} QR codes`);Encapsulates decoded barcode information including text, raw bytes, format, and metadata.
/**
* Result of barcode decoding
* Contains decoded text, raw bytes, format information, and metadata
*/
class Result {
/**
* Create decode result
* @param text - string: decoded text content
* @param rawBytes - Uint8Array: raw byte data from barcode (may be null for some formats)
* @param numBits - number: number of valid bits in rawBytes (typically 8 * rawBytes.length)
* @param resultPoints - ResultPoint[]: key detection points (corners, finder patterns, alignment patterns)
* @param format - BarcodeFormat: detected barcode format
* @param timestamp - number: decode timestamp in milliseconds since epoch
*/
constructor(
text: string,
rawBytes: Uint8Array,
numBits: number,
resultPoints: ResultPoint[],
format: BarcodeFormat,
timestamp: number
);
/**
* Get decoded text content
* @returns string: the decoded text
*/
getText(): string;
/**
* Get raw byte data from barcode
* May be null for formats that don't preserve raw bytes
* @returns Uint8Array: raw bytes or null
*/
getRawBytes(): Uint8Array;
/**
* Get number of valid bits in raw bytes
* For most formats this is 8 * rawBytes.length
* @returns number: bit count
*/
getNumBits(): number;
/**
* Get key points detected in barcode
* Points may include: finder pattern centers, corner points, alignment patterns
* @returns ResultPoint[]: array of detection points (may be empty)
*/
getResultPoints(): ResultPoint[];
/**
* Get barcode format that was decoded
* @returns BarcodeFormat: the detected format
*/
getBarcodeFormat(): BarcodeFormat;
/**
* Get result metadata
* Metadata may include: orientation, error correction level, byte segments, etc.
* @returns Map<ResultMetadataType, Object>: metadata map (may be null or empty)
*/
getResultMetadata(): Map<ResultMetadataType, Object>;
/**
* Get decode timestamp in milliseconds since Unix epoch
* @returns number: timestamp when barcode was decoded
*/
getTimestamp(): number;
/**
* Add single metadata entry to result
* @param type - ResultMetadataType: metadata type identifier
* @param value - Object: metadata value
*/
putMetadata(type: ResultMetadataType, value: Object): void;
/**
* Add multiple metadata entries to result
* @param metadata - Map<ResultMetadataType, Object>: metadata entries to add
*/
putAllMetadata(metadata: Map<ResultMetadataType, Object>): void;
}Usage Examples:
import {
MultiFormatReader,
Result,
ResultPoint,
ResultMetadataType,
BarcodeFormat,
BinaryBitmap
} from '@zxing/library';
const reader = new MultiFormatReader();
const result: Result = reader.decode(binaryBitmap);
// Access decoded data
console.log('Text:', result.getText());
console.log('Format:', result.getBarcodeFormat()); // e.g., BarcodeFormat.QR_CODE
console.log('Timestamp:', result.getTimestamp());
console.log('Num bits:', result.getNumBits());
// Access raw bytes (may be null)
const rawBytes: Uint8Array = result.getRawBytes();
if (rawBytes) {
console.log('Raw bytes:', Array.from(rawBytes));
console.log('Byte length:', rawBytes.length);
}
// Access result points (e.g., QR code finder patterns)
const points: ResultPoint[] = result.getResultPoints();
console.log(`Found ${points.length} result points`);
for (let i = 0; i < points.length; i++) {
const point: ResultPoint = points[i];
console.log(`Point ${i} at (${point.getX()}, ${point.getY()})`);
}
// Access metadata
const metadata = result.getResultMetadata();
if (metadata) {
// Error correction level
if (metadata.has(ResultMetadataType.ERROR_CORRECTION_LEVEL)) {
const ecLevel = metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
console.log('Error correction level:', ecLevel);
}
// Orientation
if (metadata.has(ResultMetadataType.ORIENTATION)) {
const orientation: number = metadata.get(ResultMetadataType.ORIENTATION) as number;
console.log(`Barcode rotated ${orientation} degrees clockwise`);
}
// Byte segments
if (metadata.has(ResultMetadataType.BYTE_SEGMENTS)) {
const byteSegments: Uint8Array[] = metadata.get(ResultMetadataType.BYTE_SEGMENTS) as Uint8Array[];
console.log(`Has ${byteSegments.length} byte segments`);
}
// Structured append information
if (metadata.has(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
const sequence: number = metadata.get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE) as number;
const parity: number = metadata.get(ResultMetadataType.STRUCTURED_APPEND_PARITY) as number;
console.log(`Part ${sequence} (parity: ${parity})`);
}
}Format-specific result processing:
import { Result, BarcodeFormat, ResultMetadataType } from '@zxing/library';
function processResult(result: Result): void {
const format: BarcodeFormat = result.getBarcodeFormat();
switch (format) {
case BarcodeFormat.QR_CODE:
console.log('QR Code:', result.getText());
// QR codes may have error correction level metadata
const qrECLevel = result.getResultMetadata()?.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
console.log('QR EC Level:', qrECLevel);
break;
case BarcodeFormat.EAN_13:
const ean13: string = result.getText();
if (ean13.length === 13) {
console.log('Country:', ean13.substring(0, 3));
console.log('Manufacturer:', ean13.substring(3, 8));
console.log('Product:', ean13.substring(8, 13));
}
break;
case BarcodeFormat.PDF_417:
// PDF417 may have extra metadata
const pdf417Meta = result.getResultMetadata()?.get(ResultMetadataType.PDF417_EXTRA_METADATA);
if (pdf417Meta) {
console.log('PDF417 segment:', pdf417Meta.getSegmentIndex());
console.log('PDF417 file ID:', pdf417Meta.getFileId());
}
break;
default:
console.log(`${format}:`, result.getText());
}
}Represents a key point in the barcode image (finder pattern, corner, alignment pattern).
/**
* Point in barcode image representing finder pattern, corner, or alignment marker
*/
class ResultPoint {
/**
* Create result point
* @param x - number: x coordinate in image
* @param y - number: y coordinate in image
*/
constructor(x: number, y: number);
/**
* Get X coordinate
* @returns number: x position in pixels
*/
getX(): number;
/**
* Get Y coordinate
* @returns number: y position in pixels
*/
getY(): number;
/**
* Calculate Euclidean distance between two points
* @param pattern1 - ResultPoint: first point
* @param pattern2 - ResultPoint: second point
* @returns number: distance in pixels
*/
static distance(pattern1: ResultPoint, pattern2: ResultPoint): number;
/**
* Order three patterns by distance from top-left
* Modifies array in place to be [bottomLeft, topLeft, topRight]
* @param patterns - ResultPoint[]: array of exactly 3 patterns
* @throws IllegalArgumentException if array length is not 3
*/
static orderBestPatterns(patterns: ResultPoint[]): void;
/**
* Check equality with another point
* @param other - any: object to compare
* @returns boolean: true if same coordinates
*/
equals(other: any): boolean;
/**
* String representation
* @returns string: formatted as "(x,y)"
*/
toString(): string;
}Usage Examples:
import { ResultPoint, Result, MultiFormatReader } from '@zxing/library';
const reader = new MultiFormatReader();
const result: Result = reader.decode(binaryBitmap);
// Access result points
const points: ResultPoint[] = result.getResultPoints();
if (points.length >= 3) {
const point1: ResultPoint = points[0];
const point2: ResultPoint = points[1];
console.log(`Point 1: (${point1.getX()}, ${point1.getY()})`);
console.log(`Point 2: (${point2.getX()}, ${point2.getY()})`);
// Calculate distance between points
const distance: number = ResultPoint.distance(point1, point2);
console.log(`Distance: ${distance.toFixed(2)} pixels`);
}
// Order finder patterns (QR Code has 3 finder patterns)
if (points.length === 3) {
// Make a copy before ordering (orderBestPatterns modifies in place)
const orderedPoints: ResultPoint[] = [...points];
ResultPoint.orderBestPatterns(orderedPoints);
console.log('Bottom-left:', orderedPoints[0]);
console.log('Top-left:', orderedPoints[1]);
console.log('Top-right:', orderedPoints[2]);
}
// Calculate barcode dimensions
function calculateBarcodeSize(points: ResultPoint[]): { width: number; height: number } | null {
if (points.length < 3) return null;
const [p0, p1, p2] = points;
const width = ResultPoint.distance(p1, p2);
const height = ResultPoint.distance(p1, p0);
return { width, height };
}
const size = calculateBarcodeSize(points);
if (size) {
console.log(`Barcode size: ${size.width.toFixed(1)} x ${size.height.toFixed(1)} pixels`);
}
// Create custom result point
const customPoint = new ResultPoint(100, 150);
console.log(customPoint.toString()); // "(100.0, 150.0)"
// Compare points
const point1 = new ResultPoint(10, 20);
const point2 = new ResultPoint(10, 20);
const point3 = new ResultPoint(15, 25);
console.log(point1.equals(point2)); // true
console.log(point1.equals(point3)); // falseCallback interface for receiving intermediate result points during detection.
/**
* Callback for result point detection during decoding
* Allows tracking of finder patterns and other key points as they are detected
*/
interface ResultPointCallback {
/**
* Called when a possible result point is found
* May be called multiple times during single decode operation
* @param point - ResultPoint: detected point with x,y coordinates
*/
foundPossibleResultPoint(point: ResultPoint): void;
}Usage Examples:
import {
ResultPointCallback,
ResultPoint,
MultiFormatReader,
DecodeHintType,
BinaryBitmap,
Result
} from '@zxing/library';
// Implement callback to track detection progress
const callback: ResultPointCallback = {
foundPossibleResultPoint(point: ResultPoint): void {
console.log(`Found point at (${point.getX()}, ${point.getY()})`);
// Could draw marker on canvas to show detection progress
drawMarker(point.getX(), point.getY());
}
};
// Use in hints
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.NEED_RESULT_POINT_CALLBACK, callback);
const reader = new MultiFormatReader();
try {
const result: Result = reader.decode(binaryBitmap, hints);
console.log('Decoded:', result.getText());
} catch (error) {
console.error('Decode failed:', error);
}
// More advanced callback with visualization
class VisualizingCallback implements ResultPointCallback {
private detectedPoints: ResultPoint[] = [];
foundPossibleResultPoint(point: ResultPoint): void {
this.detectedPoints.push(point);
console.log(`Detection progress: ${this.detectedPoints.length} points`);
}
getDetectedPoints(): ResultPoint[] {
return this.detectedPoints;
}
clear(): void {
this.detectedPoints = [];
}
}
// Usage
const vizCallback = new VisualizingCallback();
const vizHints = new Map<DecodeHintType, any>();
vizHints.set(DecodeHintType.NEED_RESULT_POINT_CALLBACK, vizCallback);
try {
const result: Result = reader.decode(binaryBitmap, vizHints);
console.log('Decoded:', result.getText());
console.log('Total points detected:', vizCallback.getDetectedPoints().length);
} catch (error) {
console.error('Failed, but detected points:', vizCallback.getDetectedPoints());
}Common decode hints for controlling reader behavior:
/**
* Decode hint type enumeration
* Used as keys in hints Map<DecodeHintType, any>
*/
enum DecodeHintType {
OTHER, // any: unspecified application-specific hint
PURE_BARCODE, // boolean: image contains only barcode, no border/rotation
POSSIBLE_FORMATS, // BarcodeFormat[]: limit decoding to specific formats
TRY_HARDER, // boolean: spend more time for better accuracy
CHARACTER_SET, // string: character encoding (e.g., "UTF-8", "ISO-8859-1")
ALLOWED_LENGTHS, // Int32Array: valid barcode lengths (for ITF)
ASSUME_CODE_39_CHECK_DIGIT, // boolean: validate Code 39 check digit
ENABLE_CODE_39_EXTENDED_MODE, // boolean: enable full ASCII for Code 39
ASSUME_GS1, // boolean: treat as GS1 barcode (affects FNC1 handling)
RETURN_CODABAR_START_END, // boolean: include Codabar start/stop characters
NEED_RESULT_POINT_CALLBACK, // ResultPointCallback: receive detection callbacks
ALLOWED_EAN_EXTENSIONS // Int32Array: allowed UPC/EAN extension lengths ([2], [5], or [2,5])
}Comprehensive Hints Example:
import {
DecodeHintType,
BarcodeFormat,
ResultPointCallback,
ResultPoint
} from '@zxing/library';
// Build comprehensive hints map
const hints = new Map<DecodeHintType, any>();
// Limit to specific formats (improves performance)
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.QR_CODE,
BarcodeFormat.EAN_13,
BarcodeFormat.CODE_128
]);
// Enable accuracy mode (slower but more reliable)
hints.set(DecodeHintType.TRY_HARDER, true);
// Specify character encoding for text interpretation
hints.set(DecodeHintType.CHARACTER_SET, 'UTF-8');
// For pure barcode images (no background, no rotation)
// hints.set(DecodeHintType.PURE_BARCODE, true);
// Code 39 specific hints
// hints.set(DecodeHintType.ASSUME_CODE_39_CHECK_DIGIT, true);
// hints.set(DecodeHintType.ENABLE_CODE_39_EXTENDED_MODE, true);
// GS1 format (for Code 128 as GS1-128)
// hints.set(DecodeHintType.ASSUME_GS1, true);
// Codabar specific
// hints.set(DecodeHintType.RETURN_CODABAR_START_END, true);
// ITF specific - allowed lengths (e.g., ITF-14)
// hints.set(DecodeHintType.ALLOWED_LENGTHS, new Int32Array([14, 16]));
// UPC/EAN extensions
// hints.set(DecodeHintType.ALLOWED_EAN_EXTENSIONS, new Int32Array([2, 5]));
// Result point callback for tracking detection progress
const callback: ResultPointCallback = {
foundPossibleResultPoint: (point: ResultPoint) => {
console.log(`Detected point at (${point.getX()}, ${point.getY()})`);
}
};
hints.set(DecodeHintType.NEED_RESULT_POINT_CALLBACK, callback);
// Use hints with reader
const reader = new MultiFormatReader();
const result = reader.decode(binaryBitmap, hints);Common encode hints for controlling writer behavior:
/**
* Encode hint type enumeration
* Used as keys in hints Map<EncodeHintType, any>
*/
enum EncodeHintType {
ERROR_CORRECTION, // varies by format: ErrorCorrectionLevel (QR), number (Aztec %, PDF417 0-8)
CHARACTER_SET, // string: character encoding (e.g., "UTF-8", "ISO-8859-1", "Shift_JIS")
DATA_MATRIX_SHAPE, // SymbolShapeHint: FORCE_NONE|FORCE_SQUARE|FORCE_RECTANGLE
DATA_MATRIX_COMPACT, // boolean: use MinimalEncoder for better compression
MIN_SIZE, // Dimension: minimum barcode size (deprecated)
MAX_SIZE, // Dimension: maximum barcode size (deprecated)
MARGIN, // number: margin/quiet zone in pixels
PDF417_COMPACT, // boolean: use compact PDF417 mode
PDF417_COMPACTION, // Compaction: TEXT|BYTE|NUMERIC
PDF417_DIMENSIONS, // Dimensions: min/max rows and columns
AZTEC_LAYERS, // number: layer count (negative=compact, 0=auto, positive=full)
QR_VERSION, // number: QR Code version (1-40)
GS1_FORMAT, // boolean: encode to GS1 standard with FNC1
FORCE_C40 // boolean: force C40 encoding for Data Matrix
}Comprehensive Hints Example:
import {
EncodeHintType,
BarcodeFormat,
QRCodeDecoderErrorCorrectionLevel,
SymbolShapeHint,
MultiFormatWriter,
BitMatrix
} from '@zxing/library';
const writer = new MultiFormatWriter();
// QR Code hints
const qrHints = new Map<EncodeHintType, any>();
qrHints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.H); // High (30%)
qrHints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
qrHints.set(EncodeHintType.MARGIN, 1); // Minimal quiet zone
// qrHints.set(EncodeHintType.QR_VERSION, 10); // Force version 10
const qrMatrix: BitMatrix = writer.encode(
'QR Code with hints',
BarcodeFormat.QR_CODE,
300,
300,
qrHints
);
// Data Matrix hints
const dmHints = new Map<EncodeHintType, any>();
dmHints.set(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
dmHints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
// dmHints.set(EncodeHintType.DATA_MATRIX_COMPACT, true); // Use MinimalEncoder
// dmHints.set(EncodeHintType.FORCE_C40, true); // Force C40 mode
// dmHints.set(EncodeHintType.GS1_FORMAT, true); // GS1 encoding
const dmMatrix: BitMatrix = writer.encode(
'Data Matrix',
BarcodeFormat.DATA_MATRIX,
200,
200,
dmHints
);
// Aztec hints
const aztecHints = new Map<EncodeHintType, any>();
aztecHints.set(EncodeHintType.ERROR_CORRECTION, 33); // 33% error correction
aztecHints.set(EncodeHintType.AZTEC_LAYERS, 0); // Auto-select layers
aztecHints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
const aztecMatrix: BitMatrix = writer.encode(
'Aztec Code',
BarcodeFormat.AZTEC,
200,
200,
aztecHints
);
// Code 128 hints
const code128Hints = new Map<EncodeHintType, any>();
code128Hints.set(EncodeHintType.MARGIN, 10); // Horizontal margin
// code128Hints.set(EncodeHintType.GS1_FORMAT, true); // For GS1-128
const code128Matrix: BitMatrix = writer.encode(
'CODE128',
BarcodeFormat.CODE_128,
300,
100,
code128Hints
);import {
MultiFormatReader,
BinaryBitmap,
HybridBinarizer,
GlobalHistogramBinarizer,
RGBLuminanceSource,
DecodeHintType,
BarcodeFormat,
Result
} from '@zxing/library';
function decodeRobust(
imageData: Uint8ClampedArray,
width: number,
height: number
): Result | null {
const reader = new MultiFormatReader();
// Strategy 1: HybridBinarizer with common formats
try {
const source1 = new RGBLuminanceSource(imageData, width, height);
const bitmap1 = new BinaryBitmap(new HybridBinarizer(source1));
const hints1 = new Map<DecodeHintType, any>();
hints1.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.QR_CODE,
BarcodeFormat.EAN_13,
BarcodeFormat.CODE_128
]);
return reader.decode(bitmap1, hints1);
} catch (e) {
console.debug('Strategy 1 failed');
}
// Strategy 2: GlobalHistogramBinarizer
try {
const source2 = new RGBLuminanceSource(imageData, width, height);
const bitmap2 = new BinaryBitmap(new GlobalHistogramBinarizer(source2));
const hints2 = new Map<DecodeHintType, any>();
hints2.set(DecodeHintType.TRY_HARDER, true);
return reader.decode(bitmap2, hints2);
} catch (e) {
console.debug('Strategy 2 failed');
}
// Strategy 3: Inverted image (white-on-black)
try {
const source3 = new RGBLuminanceSource(imageData, width, height);
const inverted = source3.invert();
const bitmap3 = new BinaryBitmap(new HybridBinarizer(inverted));
return reader.decode(bitmap3);
} catch (e) {
console.debug('Strategy 3 failed');
}
// Strategy 4: Try all formats with maximum accuracy
try {
const source4 = new RGBLuminanceSource(imageData, width, height);
const bitmap4 = new BinaryBitmap(new HybridBinarizer(source4));
const hints4 = new Map<DecodeHintType, any>();
hints4.set(DecodeHintType.TRY_HARDER, true);
return reader.decode(bitmap4, hints4);
} catch (e) {
console.debug('All strategies failed');
}
return null;
}import {
MultiFormatReader,
BinaryBitmap,
HybridBinarizer,
RGBLuminanceSource,
Result,
DecodeHintType
} from '@zxing/library';
interface BatchResult {
success: boolean;
result?: Result;
error?: Error;
}
function batchDecode(
images: { data: Uint8ClampedArray; width: number; height: number }[]
): BatchResult[] {
const reader = new MultiFormatReader();
// Set hints once for all images
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.TRY_HARDER, true);
reader.setHints(hints);
return images.map((img, index) => {
try {
const source = new RGBLuminanceSource(img.data, img.width, img.height);
const bitmap = new BinaryBitmap(new HybridBinarizer(source));
const result: Result = reader.decodeWithState(bitmap);
console.log(`Image ${index}: ${result.getText()}`);
return { success: true, result };
} catch (error) {
console.error(`Image ${index} failed:`, error);
return { success: false, error: error as Error };
}
});
}
// Process results
const results: BatchResult[] = batchDecode(imageArray);
const successful = results.filter(r => r.success);
console.log(`Successfully decoded ${successful.length} of ${results.length} images`);import {
QRCodeReader,
DataMatrixReader,
AztecCodeReader,
Code128Reader,
EAN13Reader,
BinaryBitmap,
Result
} from '@zxing/library';
// Use format-specific reader when format is known
function decodeKnownFormat(
bitmap: BinaryBitmap,
format: BarcodeFormat
): Result | null {
try {
switch (format) {
case BarcodeFormat.QR_CODE:
return new QRCodeReader().decode(bitmap);
case BarcodeFormat.DATA_MATRIX:
return new DataMatrixReader().decode(bitmap);
case BarcodeFormat.AZTEC:
return new AztecCodeReader().decode(bitmap);
case BarcodeFormat.CODE_128:
// For 1D barcodes, need to use decode() which scans rows
return new Code128Reader().decode(bitmap);
case BarcodeFormat.EAN_13:
return new EAN13Reader().decode(bitmap);
default:
// Fall back to MultiFormatReader
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [format]);
return new MultiFormatReader().decode(bitmap, hints);
}
} catch (error) {
console.error(`Failed to decode as ${format}:`, error);
return null;
}
}import { MultiFormatReader, BinaryBitmap, DecodeHintType } from '@zxing/library';
// GOOD: Reuse reader instance
const reader = new MultiFormatReader();
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.TRY_HARDER, true);
reader.setHints(hints);
for (const bitmap of bitmaps) {
try {
const result = reader.decodeWithState(bitmap);
console.log('Decoded:', result.getText());
} catch (error) {
// Continue to next bitmap
}
}
// BAD: Creating new reader each time (wasteful)
for (const bitmap of bitmaps) {
const reader = new MultiFormatReader(); // DON'T DO THIS
try {
const result = reader.decode(bitmap);
console.log('Decoded:', result.getText());
} catch (error) {
// Continue
}
}import { MultiFormatReader, DecodeHintType, BarcodeFormat } from '@zxing/library';
// GOOD: Specific formats (fast)
const hints = new Map<DecodeHintType, any>();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [BarcodeFormat.QR_CODE]);
// BAD: No hints (tries all formats, slower)
// const hints = new Map<DecodeHintType, any>();
// GOOD: setHints + decodeWithState for continuous scanning
reader.setHints(hints);
for (const bitmap of stream) {
try {
const result = reader.decodeWithState(bitmap); // Fast
} catch (e) {}
}
// BAD: Passing hints every time
for (const bitmap of stream) {
try {
const result = reader.decode(bitmap, hints); // Slower
} catch (e) {}
}Install with Tessl CLI
npx tessl i tessl/npm-zxing--library@0.21.14