TypeScript port of ZXing multi-format 1D/2D barcode image processing library
Production-ready patterns and complete implementations for common barcode use cases.
Mobile-responsive barcode scanner for inventory management:
import {
BrowserMultiFormatReader,
BarcodeFormat,
DecodeHintType,
Result
} from '@zxing/library';
interface InventoryItem {
code: string;
format: BarcodeFormat;
timestamp: Date;
}
class InventoryScanner {
private reader: BrowserMultiFormatReader;
private scanning = false;
private items: InventoryItem[] = [];
private seenCodes = new Set<string>();
constructor() {
const hints = new Map();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.EAN_13,
BarcodeFormat.UPC_A,
BarcodeFormat.CODE_128,
BarcodeFormat.QR_CODE
]);
this.reader = new BrowserMultiFormatReader(hints, 300);
}
async start(videoElement: HTMLVideoElement): Promise<void> {
this.scanning = true;
await this.reader.decodeFromVideoDevice(
null,
videoElement,
(result, error) => {
if (!this.scanning || !result) return;
const code = result.getText();
// Only process new codes
if (!this.seenCodes.has(code)) {
this.seenCodes.add(code);
this.items.push({
code,
format: result.getBarcodeFormat(),
timestamp: new Date()
});
this.onItemScanned(code, result.getBarcodeFormat());
this.playBeep();
}
}
);
}
stop(): void {
this.scanning = false;
this.reader.stopContinuousDecode();
this.reader.reset();
}
getItems(): InventoryItem[] {
return [...this.items];
}
clearItems(): void {
this.items = [];
this.seenCodes.clear();
}
private onItemScanned(code: string, format: BarcodeFormat): void {
console.log(`Scanned ${format}: ${code}`);
this.updateUI();
}
private updateUI(): void {
const list = document.getElementById('item-list')!;
list.innerHTML = this.items
.map(item => `
<div class="item">
<strong>${item.code}</strong>
<span>${item.format}</span>
<span>${item.timestamp.toLocaleTimeString()}</span>
</div>
`)
.join('');
document.getElementById('count')!.textContent = `${this.items.length}`;
}
private playBeep(): void {
const audio = document.getElementById('beep') as HTMLAudioElement;
audio?.play().catch(() => {});
}
}
// HTML setup
/*
<video id="video" width="100%" autoplay></video>
<div id="count">0</div>
<div id="item-list"></div>
<audio id="beep" src="beep.mp3"></audio>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="clear">Clear</button>
*/
const scanner = new InventoryScanner();
const video = document.getElementById('video') as HTMLVideoElement;
document.getElementById('start')!.onclick = () => scanner.start(video);
document.getElementById('stop')!.onclick = () => scanner.stop();
document.getElementById('clear')!.onclick = () => {
scanner.clearItems();
document.getElementById('item-list')!.innerHTML = '';
document.getElementById('count')!.textContent = '0';
};Generate vCard QR codes for business cards:
import {
QRCodeWriter,
BarcodeFormat,
EncodeHintType,
QRCodeDecoderErrorCorrectionLevel
} from '@zxing/library';
interface ContactInfo {
name: string;
email?: string;
phone?: string;
company?: string;
title?: string;
url?: string;
}
function generateVCardQR(contact: ContactInfo, size: number = 400): HTMLCanvasElement {
// Build vCard format
let vcard = 'BEGIN:VCARD\n';
vcard += 'VERSION:3.0\n';
vcard += `FN:${contact.name}\n`;
if (contact.email) vcard += `EMAIL:${contact.email}\n`;
if (contact.phone) vcard += `TEL:${contact.phone}\n`;
if (contact.company) vcard += `ORG:${contact.company}\n`;
if (contact.title) vcard += `TITLE:${contact.title}\n`;
if (contact.url) vcard += `URL:${contact.url}\n`;
vcard += 'END:VCARD';
// Generate QR Code
const writer = new QRCodeWriter();
const hints = new Map();
hints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.M);
hints.set(EncodeHintType.CHARACTER_SET, 'UTF-8');
const matrix = writer.encode(vcard, BarcodeFormat.QR_CODE, size, size, hints);
// Render to canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
const moduleSize = Math.floor(size / matrix.getWidth());
canvas.width = matrix.getWidth() * moduleSize;
canvas.height = matrix.getHeight() * moduleSize;
// White background
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Black modules
ctx.fillStyle = '#000000';
for (let y = 0; y < matrix.getHeight(); y++) {
for (let x = 0; x < matrix.getWidth(); x++) {
if (matrix.get(x, y)) {
ctx.fillRect(x * moduleSize, y * moduleSize, moduleSize, moduleSize);
}
}
}
return canvas;
}
// Usage
const contact: ContactInfo = {
name: 'John Doe',
email: 'john@example.com',
phone: '+1-555-0100',
company: 'Example Corp',
title: 'Software Engineer',
url: 'https://example.com'
};
const qrCanvas = generateVCardQR(contact);
document.getElementById('container')!.appendChild(qrCanvas);Validate QR code tickets with signature verification:
import {
QRCodeReader,
QRCodeWriter,
BarcodeFormat,
BinaryBitmap,
HybridBinarizer,
RGBLuminanceSource
} from '@zxing/library';
import crypto from 'crypto';
interface Ticket {
id: string;
eventId: string;
userId: string;
timestamp: number;
signature: string;
}
class TicketSystem {
private secret: string;
constructor(secret: string) {
this.secret = secret;
}
generateTicket(ticketData: Omit<Ticket, 'signature'>): string {
const data = JSON.stringify(ticketData);
const signature = this.sign(data);
const ticket: Ticket = {
...ticketData,
signature
};
return JSON.stringify(ticket);
}
generateTicketQR(ticketData: Omit<Ticket, 'signature'>, size: number = 300): BitMatrix {
const ticketJson = this.generateTicket(ticketData);
const writer = new QRCodeWriter();
const hints = new Map();
hints.set(EncodeHintType.ERROR_CORRECTION, 'H');
return writer.encode(ticketJson, BarcodeFormat.QR_CODE, size, size, hints);
}
validateTicket(qrData: string): { valid: boolean; ticket?: Ticket; error?: string } {
try {
const ticket: Ticket = JSON.parse(qrData);
// Verify signature
const { signature, ...data } = ticket;
const expectedSignature = this.sign(JSON.stringify(data));
if (signature !== expectedSignature) {
return { valid: false, error: 'Invalid signature' };
}
// Check expiration (if needed)
const age = Date.now() - ticket.timestamp;
if (age > 24 * 60 * 60 * 1000) { // 24 hours
return { valid: false, error: 'Ticket expired' };
}
return { valid: true, ticket };
} catch (error) {
return { valid: false, error: 'Invalid ticket data' };
}
}
private sign(data: string): string {
return crypto
.createHmac('sha256', this.secret)
.update(data)
.digest('hex')
.substring(0, 16);
}
}
// Usage
const ticketSystem = new TicketSystem('my-secret-key');
// Generate ticket
const ticketData = {
id: 'T12345',
eventId: 'EVT001',
userId: 'USR789',
timestamp: Date.now()
};
const qrMatrix = ticketSystem.generateTicketQR(ticketData);
// Render qrMatrix to display...
// Validate scanned ticket
const scannedData = "..."; // From QR code scan
const validation = ticketSystem.validateTicket(scannedData);
if (validation.valid) {
console.log('Valid ticket:', validation.ticket);
// Grant access
} else {
console.error('Invalid ticket:', validation.error);
// Deny access
}Decode shipping labels with multiple barcodes:
import {
PDF417Reader,
MultiFormatReader,
BarcodeFormat,
DecodeHintType,
Result
} from '@zxing/library';
interface ShippingLabel {
trackingNumber?: string;
destination?: string;
weight?: string;
orderNumber?: string;
}
async function readShippingLabel(imagePath: string): Promise<ShippingLabel> {
const { data, info } = await sharp(imagePath).raw().toBuffer({ resolveWithObject: true });
const luminanceSource = new RGBLuminanceSource(
new Uint8ClampedArray(data),
info.width,
info.height
);
const bitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
// Try PDF417 first (common for shipping labels)
const pdf417Reader = new PDF417Reader();
try {
const results = pdf417Reader.decodeMultiple(bitmap);
return parseShippingData(results);
} catch (e) {}
// Fall back to 1D barcodes
const hints = new Map();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.CODE_128,
BarcodeFormat.ITF
]);
const reader = new MultiFormatReader();
const result = reader.decode(bitmap, hints);
return {
trackingNumber: result.getText()
};
}
function parseShippingData(results: Result[]): ShippingLabel {
const label: ShippingLabel = {};
results.forEach(result => {
const text = result.getText();
// Parse based on format or content
if (text.match(/^1Z\w{16}$/)) {
label.trackingNumber = text; // UPS format
} else if (text.match(/^\d{20,22}$/)) {
label.trackingNumber = text; // FedEx format
}
// Add more parsing logic as needed
});
return label;
}Generate multiple barcode formats for product labels:
import {
MultiFormatWriter,
QRCodeWriter,
DataMatrixWriter,
BarcodeFormat,
EncodeHintType,
SymbolShapeHint
} from '@zxing/library';
interface ProductLabel {
sku: string;
name: string;
price: number;
qrData: string;
}
function generateProductLabel(product: ProductLabel): {
ean13: BitMatrix | null;
qrCode: BitMatrix;
dataMatrix: BitMatrix;
} {
const writer = new MultiFormatWriter();
// EAN-13 barcode (if SKU is 12-13 digits)
let ean13: BitMatrix | null = null;
if (product.sku.match(/^\d{12,13}$/)) {
try {
ean13 = writer.encode(
product.sku,
BarcodeFormat.EAN_13,
200,
100,
new Map()
);
} catch (e) {}
}
// QR Code with product info
const qrWriter = new QRCodeWriter();
const qrHints = new Map();
qrHints.set(EncodeHintType.ERROR_CORRECTION, 'M');
const qrCode = qrWriter.encode(
product.qrData,
BarcodeFormat.QR_CODE,
200,
200,
qrHints
);
// Data Matrix with SKU
const dmWriter = new DataMatrixWriter();
const dmHints = new Map();
dmHints.set(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
const dataMatrix = dmWriter.encode(
product.sku,
BarcodeFormat.DATA_MATRIX,
100,
100,
dmHints
);
return { ean13, qrCode, dataMatrix };
}
// Usage
const product: ProductLabel = {
sku: '5901234123457',
name: 'Example Product',
price: 29.99,
qrData: 'https://example.com/product/5901234123457'
};
const barcodes = generateProductLabel(product);
// Render all barcodes to label
if (barcodes.ean13) {
renderToCanvas(barcodes.ean13, document.getElementById('ean13') as HTMLCanvasElement);
}
renderToCanvas(barcodes.qrCode, document.getElementById('qr') as HTMLCanvasElement);
renderToCanvas(barcodes.dataMatrix, document.getElementById('dm') as HTMLCanvasElement);Point-of-sale barcode scanner with product lookup:
import {
BrowserBarcodeReader,
BarcodeFormat,
DecodeHintType
} from '@zxing/library';
interface Product {
code: string;
name: string;
price: number;
}
class CheckoutScanner {
private reader: BrowserBarcodeReader;
private database: Map<string, Product>;
constructor(productDatabase: Product[]) {
const hints = new Map();
hints.set(DecodeHintType.POSSIBLE_FORMATS, [
BarcodeFormat.EAN_13,
BarcodeFormat.UPC_A,
BarcodeFormat.EAN_8,
BarcodeFormat.UPC_E
]);
this.reader = new BrowserBarcodeReader(500, hints);
this.database = new Map(productDatabase.map(p => [p.code, p]));
}
async startScanning(videoElement: HTMLVideoElement): Promise<void> {
await this.reader.decodeFromVideoDevice(
null,
videoElement,
(result, error) => {
if (!result) return;
const code = result.getText();
const product = this.database.get(code);
if (product) {
this.addToCart(product);
} else {
this.showError(`Product not found: ${code}`);
}
}
);
}
stop(): void {
this.reader.stopContinuousDecode();
this.reader.reset();
}
private addToCart(product: Product): void {
console.log('Added to cart:', product);
// Update cart UI
}
private showError(message: string): void {
console.error(message);
// Show error in UI
}
}Medical/pharmaceutical document scanning:
import {
DataMatrixReader,
BinaryBitmap,
HybridBinarizer,
RGBLuminanceSource,
DecodeHintType
} from '@zxing/library';
interface MedicalDocument {
ndc: string; // National Drug Code
lot: string; // Lot number
expiration: string; // Expiration date
serial: string; // Serial number
}
async function scanMedicalLabel(imagePath: string): Promise<MedicalDocument | null> {
const { data, info } = await sharp(imagePath).raw().toBuffer({ resolveWithObject: true });
const luminanceSource = new RGBLuminanceSource(
new Uint8ClampedArray(data),
info.width,
info.height
);
const bitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
const reader = new DataMatrixReader();
const hints = new Map();
hints.set(DecodeHintType.PURE_BARCODE, true);
try {
const result = reader.decode(bitmap, hints);
const text = result.getText();
// Parse GS1 format: (01)NDC(17)YYMMDD(10)LOT(21)SERIAL
return parseGS1DataMatrix(text);
} catch (error) {
console.error('Failed to read Data Matrix:', error);
return null;
}
}
function parseGS1DataMatrix(data: string): MedicalDocument | null {
const aiPattern = /\((\d{2})\)([^\(]+)/g;
const ais = new Map<string, string>();
let match;
while ((match = aiPattern.exec(data)) !== null) {
ais.set(match[1], match[2]);
}
return {
ndc: ais.get('01') || '',
expiration: ais.get('17') || '',
lot: ais.get('10') || '',
serial: ais.get('21') || ''
};
}Generate WiFi credential QR codes:
import {
QRCodeWriter,
BarcodeFormat,
EncodeHintType,
QRCodeDecoderErrorCorrectionLevel
} from '@zxing/library';
interface WiFiCredentials {
ssid: string;
password: string;
security: 'WPA' | 'WEP' | 'nopass';
hidden?: boolean;
}
function generateWiFiQR(wifi: WiFiCredentials, size: number = 300): BitMatrix {
// WiFi QR format: WIFI:T:WPA;S:ssid;P:password;H:true;;
let wifiString = `WIFI:T:${wifi.security};S:${escapeWiFi(wifi.ssid)};`;
if (wifi.security !== 'nopass') {
wifiString += `P:${escapeWiFi(wifi.password)};`;
}
if (wifi.hidden) {
wifiString += 'H:true;';
}
wifiString += ';';
const writer = new QRCodeWriter();
const hints = new Map();
hints.set(EncodeHintType.ERROR_CORRECTION, QRCodeDecoderErrorCorrectionLevel.M);
return writer.encode(wifiString, BarcodeFormat.QR_CODE, size, size, hints);
}
function escapeWiFi(str: string): string {
return str.replace(/([\\";,:])/g, '\\$1');
}
// Usage
const wifi: WiFiCredentials = {
ssid: 'MyNetwork',
password: 'SecurePassword123',
security: 'WPA'
};
const qrMatrix = generateWiFiQR(wifi);
// Render qrMatrix...Process multiple barcode images in parallel:
import {
MultiFormatReader,
BinaryBitmap,
HybridBinarizer,
RGBLuminanceSource,
DecodeHintType
} from '@zxing/library';
import sharp from 'sharp';
import { promises as fs } from 'fs';
import path from 'path';
interface DecodeResult {
filename: string;
success: boolean;
text?: string;
format?: BarcodeFormat;
error?: string;
}
async function processBarcodeImages(dirPath: string): Promise<DecodeResult[]> {
const files = await fs.readdir(dirPath);
const imageFiles = files.filter(f => f.match(/\.(png|jpg|jpeg)$/i));
// Process in parallel
const promises = imageFiles.map(file =>
decodeImageFile(path.join(dirPath, file))
.then(result => ({
filename: file,
success: true,
text: result.text,
format: result.format
}))
.catch(error => ({
filename: file,
success: false,
error: error.message
}))
);
return Promise.all(promises);
}
async function decodeImageFile(filePath: string): Promise<{ text: string; format: BarcodeFormat }> {
const { data, info } = await sharp(filePath)
.raw()
.toBuffer({ resolveWithObject: true });
const luminanceSource = new RGBLuminanceSource(
new Uint8ClampedArray(data),
info.width,
info.height
);
const bitmap = new BinaryBitmap(new HybridBinarizer(luminanceSource));
const reader = new MultiFormatReader();
const hints = new Map();
hints.set(DecodeHintType.TRY_HARDER, true);
const result = reader.decode(bitmap, hints);
return {
text: result.getText(),
format: result.getBarcodeFormat()
};
}
// Usage
const results = await processBarcodeImages('./images');
console.log(`Processed ${results.length} images`);
console.log(`Successful: ${results.filter(r => r.success).length}`);
console.log(`Failed: ${results.filter(r => !r.success).length}`);
results.forEach(result => {
if (result.success) {
console.log(`${result.filename}: ${result.format} = ${result.text}`);
} else {
console.log(`${result.filename}: ${result.error}`);
}
});Install with Tessl CLI
npx tessl i tessl/npm-zxing--library@0.21.14