CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-zxing--core

Core barcode encoding/decoding library supporting 17 formats including QR Code, Data Matrix, Aztec, PDF 417, and various 1D barcodes

Pending
Overview
Eval results
Files

real-world-scenarios.mddocs/examples/

Common Patterns

Real-world usage patterns and recipes for common barcode scenarios. These patterns demonstrate best practices for typical applications using ZXing Core.

Pattern Index

  1. Mobile App Barcode Scanner
  2. QR Code Generation for Websites
  3. Batch Document Processing
  4. WiFi QR Code Generator
  5. Product Label Scanner
  6. Shipping Label Generator
  7. vCard QR Code Generator
  8. Multi-Format Scanner with Fallback
  9. High-Performance Video Scanning
  10. Barcode Quality Validator

Mobile App Barcode Scanner

Scenario: Continuous scanning from camera preview frames in a mobile app.

Requirements:

  • Fast processing (real-time)
  • Multiple format support
  • Good accuracy
  • Handle varying lighting
import com.google.zxing.*;
import com.google.zxing.common.*;
import java.util.*;

public class MobileBarcodeScanner {
    private final MultiFormatReader reader;
    private final Map<DecodeHintType, Object> hints;
    private long lastScanTime = 0;
    private static final long SCAN_THROTTLE_MS = 500;

    public MobileBarcodeScanner() {
        reader = new MultiFormatReader();

        // Configure for mobile scanning
        hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
            BarcodeFormat.QR_CODE,
            BarcodeFormat.EAN_13,
            BarcodeFormat.EAN_8,
            BarcodeFormat.CODE_128
        ));
        hints.put(DecodeHintType.TRY_HARDER, Boolean.FALSE); // Speed over accuracy
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");

        reader.setHints(hints);
    }

    public Result scanPreviewFrame(byte[] yuvData, int width, int height) {
        // Throttle scanning
        long now = System.currentTimeMillis();
        if (now - lastScanTime < SCAN_THROTTLE_MS) {
            return null;
        }

        try {
            // Use YUV data directly (Android camera format)
            LuminanceSource source = new PlanarYUVLuminanceSource(
                yuvData, width, height,
                0, 0, width, height,
                false
            );
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

            Result result = reader.decode(bitmap);
            lastScanTime = now;
            return result;

        } catch (NotFoundException e) {
            // No barcode in this frame - normal
            return null;
        } catch (ChecksumException | FormatException e) {
            // Damaged or invalid barcode
            return null;
        }
    }

    public void reset() {
        reader.reset();
    }
}

Key Points:

  • Use PlanarYUVLuminanceSource for camera data (fastest)
  • Throttle scans to avoid excessive CPU usage
  • Don't use TRY_HARDER for real-time scanning
  • Limit to expected formats for speed
  • Handle exceptions gracefully (most frames won't have barcodes)

QR Code Generation for Websites

Scenario: Generate QR codes for URLs, ensuring reliable scanning on various devices.

import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

public class QRCodeGenerator {

    public static BufferedImage generateQRCode(String url, int size)
            throws WriterException {

        // Configure for high reliability
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.MARGIN, 2); // Smaller margin for web

        // Generate QR code
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix matrix = writer.encode(url, BarcodeFormat.QR_CODE,
                                        size, size, hints);

        // Convert to image with colors
        BufferedImage image = new BufferedImage(size, size,
                                               BufferedImage.TYPE_INT_RGB);

        for (int y = 0; y < size; y++) {
            for (int x = 0; x < size; x++) {
                int color = matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF;
                image.setRGB(x, y, color);
            }
        }

        return image;
    }

    public static void saveQRCode(String url, String filename, int size)
            throws Exception {
        BufferedImage image = generateQRCode(url, size);
        ImageIO.write(image, "PNG", new File(filename));
    }

    // Example: Generate QR code for product page
    public static void main(String[] args) throws Exception {
        String productUrl = "https://example.com/products/item-12345";
        saveQRCode(productUrl, "product-qr.png", 300);
        System.out.println("QR code generated successfully!");
    }
}

Key Points:

  • Use error correction level M (good balance)
  • UTF-8 encoding for international URLs
  • Smaller margin acceptable for digital display
  • PNG format for web (lossless compression)
  • Size 300-500 pixels typical for web

Batch Document Processing

Scenario: Process scanned documents containing multiple barcodes, extract all data.

import com.google.zxing.*;
import com.google.zxing.common.*;
import com.google.zxing.multi.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

public class BatchDocumentProcessor {

    public static class DocumentData {
        public String filename;
        public List<BarcodeData> barcodes = new ArrayList<>();
        public boolean processingError = false;
        public String errorMessage;
    }

    public static class BarcodeData {
        public BarcodeFormat format;
        public String text;
        public int x, y; // Location
    }

    public static List<DocumentData> processDocuments(File[] documentFiles) {
        List<DocumentData> results = new ArrayList<>();
        MultipleBarcodeReader reader = createMultiReader();

        for (File file : documentFiles) {
            DocumentData doc = new DocumentData();
            doc.filename = file.getName();

            try {
                BufferedImage image = ImageIO.read(file);
                doc.barcodes = extractBarcodes(image, reader);

            } catch (Exception e) {
                doc.processingError = true;
                doc.errorMessage = e.getMessage();
            }

            results.add(doc);
        }

        return results;
    }

    private static MultipleBarcodeReader createMultiReader() {
        MultiFormatReader delegate = new MultiFormatReader();
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
            BarcodeFormat.CODE_128,
            BarcodeFormat.QR_CODE,
            BarcodeFormat.DATA_MATRIX,
            BarcodeFormat.EAN_13
        ));
        delegate.setHints(hints);

        return new GenericMultipleBarcodeReader(delegate);
    }

    private static List<BarcodeData> extractBarcodes(
            BufferedImage image, MultipleBarcodeReader reader)
            throws NotFoundException {

        List<BarcodeData> barcodes = new ArrayList<>();

        // Convert to binary bitmap
        int width = image.getWidth();
        int height = image.getHeight();
        int[] pixels = image.getRGB(0, 0, width, height, null, 0, width);
        LuminanceSource source = new RGBLuminanceSource(width, height, pixels);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        // Decode all barcodes
        Result[] results = reader.decodeMultiple(bitmap);

        for (Result result : results) {
            BarcodeData data = new BarcodeData();
            data.format = result.getBarcodeFormat();
            data.text = result.getText();

            // Get location
            ResultPoint[] points = result.getResultPoints();
            if (points != null && points.length > 0) {
                data.x = (int) points[0].getX();
                data.y = (int) points[0].getY();
            }

            barcodes.add(data);
        }

        return barcodes;
    }

    // Example usage
    public static void main(String[] args) {
        File[] documents = new File("scanned-docs").listFiles();
        List<DocumentData> results = processDocuments(documents);

        for (DocumentData doc : results) {
            System.out.println("\n" + doc.filename + ":");
            if (doc.processingError) {
                System.out.println("  ERROR: " + doc.errorMessage);
            } else {
                System.out.println("  Found " + doc.barcodes.size() + " barcodes");
                for (BarcodeData bc : doc.barcodes) {
                    System.out.println("  - " + bc.format + ": " + bc.text);
                }
            }
        }
    }
}

Key Points:

  • Use GenericMultipleBarcodeReader for mixed formats
  • TRY_HARDER acceptable for batch processing (not real-time)
  • Extract location information for each barcode
  • Handle errors per document (don't fail entire batch)
  • Support multiple common formats

WiFi QR Code Generator

Scenario: Generate QR code for WiFi network credentials.

import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.util.*;

public class WiFiQRGenerator {

    public enum Security {
        WPA, WPA2, WEP, NOPASS
    }

    public static BitMatrix generateWiFiQR(String ssid, String password,
                                          Security security, boolean hidden)
            throws WriterException {

        // Build WiFi configuration string
        String wifiConfig = buildWiFiString(ssid, password, security, hidden);

        // Generate QR code with high error correction
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.MARGIN, 1);

        QRCodeWriter writer = new QRCodeWriter();
        return writer.encode(wifiConfig, BarcodeFormat.QR_CODE,
                           400, 400, hints);
    }

    private static String buildWiFiString(String ssid, String password,
                                         Security security, boolean hidden) {
        StringBuilder wifi = new StringBuilder("WIFI:");

        // Network type
        wifi.append("T:").append(security.name()).append(";");

        // SSID (escape special characters)
        wifi.append("S:").append(escapeSpecialChars(ssid)).append(";");

        // Password (if not open network)
        if (security != Security.NOPASS && password != null) {
            wifi.append("P:").append(escapeSpecialChars(password)).append(";");
        }

        // Hidden network flag
        wifi.append("H:").append(hidden ? "true" : "false").append(";");

        wifi.append(";"); // Terminator

        return wifi.toString();
    }

    private static String escapeSpecialChars(String str) {
        return str.replace("\\", "\\\\")
                  .replace(";", "\\;")
                  .replace(",", "\\,")
                  .replace(":", "\\:");
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        BitMatrix qrCode = generateWiFiQR(
            "MyHomeNetwork",
            "SecurePassword123",
            Security.WPA2,
            false
        );

        System.out.println("WiFi QR code generated!");
        System.out.println("Dimensions: " + qrCode.getWidth() + "x" +
                          qrCode.getHeight());

        // Could save to file using ImageIO as in previous examples
    }
}

Key Points:

  • Use standard WIFI: format for broad compatibility
  • Escape special characters (;, :, ,, \\)
  • High error correction for reliable scanning
  • Include hidden network flag
  • Support for common security types

Product Label Scanner

Scenario: Scan product barcodes (UPC/EAN) for inventory or checkout systems.

import com.google.zxing.*;
import com.google.zxing.common.*;
import com.google.zxing.client.result.*;
import java.util.*;

public class ProductScanner {

    private final Reader reader;
    private final Map<DecodeHintType, Object> hints;

    public ProductScanner() {
        reader = new MultiFormatReader();

        // Configure for product barcodes
        hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
            BarcodeFormat.EAN_13,
            BarcodeFormat.EAN_8,
            BarcodeFormat.UPC_A,
            BarcodeFormat.UPC_E
        ));
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
    }

    public ProductInfo scanProduct(BinaryBitmap bitmap) {
        try {
            Result result = reader.decode(bitmap, hints);

            // Parse as product barcode
            ParsedResult parsed = ResultParser.parseResult(result);

            if (parsed.getType() == ParsedResultType.PRODUCT) {
                ProductParsedResult product = (ProductParsedResult) parsed;

                return new ProductInfo(
                    product.getProductID(),
                    result.getBarcodeFormat(),
                    true,
                    null
                );
            }

            // Not a product barcode
            return new ProductInfo(
                result.getText(),
                result.getBarcodeFormat(),
                false,
                "Not a product barcode"
            );

        } catch (NotFoundException e) {
            return new ProductInfo(null, null, false, "No barcode found");
        } catch (ChecksumException e) {
            return new ProductInfo(null, null, false, "Damaged barcode");
        } catch (FormatException e) {
            return new ProductInfo(null, null, false, "Invalid barcode format");
        }
    }

    public static class ProductInfo {
        public final String productId;
        public final BarcodeFormat format;
        public final boolean success;
        public final String errorMessage;

        public ProductInfo(String productId, BarcodeFormat format,
                          boolean success, String errorMessage) {
            this.productId = productId;
            this.format = format;
            this.success = success;
            this.errorMessage = errorMessage;
        }

        public String getFormattedId() {
            if (productId == null) return null;

            // Add check digit if missing for EAN-13
            if (format == BarcodeFormat.EAN_13 && productId.length() == 12) {
                return productId + calculateEAN13CheckDigit(productId);
            }

            return productId;
        }

        private static char calculateEAN13CheckDigit(String code) {
            int sum = 0;
            for (int i = 0; i < 12; i++) {
                int digit = Character.getNumericValue(code.charAt(i));
                sum += (i % 2 == 0) ? digit : digit * 3;
            }
            int checksum = (10 - (sum % 10)) % 10;
            return (char) ('0' + checksum);
        }
    }

    // Example usage
    public static void main(String[] args) {
        ProductScanner scanner = new ProductScanner();

        // Simulate scanning (would use camera in real app)
        // BinaryBitmap bitmap = ...;
        // ProductInfo info = scanner.scanProduct(bitmap);
        //
        // if (info.success) {
        //     System.out.println("Product ID: " + info.getFormattedId());
        //     System.out.println("Format: " + info.format);
        // } else {
        //     System.out.println("Error: " + info.errorMessage);
        // }
    }
}

Key Points:

  • Limit to UPC/EAN formats (faster, fewer false positives)
  • Use TRY_HARDER for better accuracy
  • Parse result to validate product format
  • Handle check digits appropriately
  • Provide clear error messages

Shipping Label Generator

Scenario: Generate Code 128 barcodes for shipping labels.

import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.oned.Code128Writer;
import java.util.*;

public class ShippingLabelGenerator {

    public static BitMatrix generateTrackingCode(String trackingNumber)
            throws WriterException {

        // Validate tracking number
        if (trackingNumber == null || trackingNumber.isEmpty()) {
            throw new IllegalArgumentException("Tracking number cannot be empty");
        }

        // Configure for shipping labels
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.MARGIN, 10); // Adequate quiet zones
        hints.put(EncodeHintType.CODE128_COMPACT, Boolean.FALSE);

        // Generate Code 128 barcode
        Code128Writer writer = new Code128Writer();
        return writer.encode(
            trackingNumber,
            BarcodeFormat.CODE_128,
            400,  // Width
            100,  // Height
            hints
        );
    }

    public static BitMatrix generateGS1Barcode(String gs1Data)
            throws WriterException {

        // Configure for GS1-128
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.GS1_FORMAT, Boolean.TRUE);
        hints.put(EncodeHintType.MARGIN, 10);

        Code128Writer writer = new Code128Writer();
        return writer.encode(
            gs1Data,
            BarcodeFormat.CODE_128,
            500,
            100,
            hints
        );
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        // Standard tracking number
        BitMatrix tracking = generateTrackingCode("1Z999AA10123456784");
        System.out.println("Tracking barcode: " +
                          tracking.getWidth() + "x" + tracking.getHeight());

        // GS1-128 with Application Identifiers
        // (01) GTIN, (17) Expiry Date, (10) Lot Number
        String gs1 = "01095011010209171719050810LOT12345";
        BitMatrix gs1Barcode = generateGS1Barcode(gs1);
        System.out.println("GS1 barcode generated");
    }
}

Key Points:

  • Code 128 is standard for shipping (high density, full ASCII)
  • Adequate margins essential for reliable scanning
  • GS1-128 for supply chain applications
  • Wider than tall (typical 4:1 ratio)
  • Black bars on white background

vCard QR Code Generator

Scenario: Generate QR codes containing contact information (vCard format).

import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.util.*;

public class VCardQRGenerator {

    public static class Contact {
        public String firstName;
        public String lastName;
        public String organization;
        public String title;
        public String phone;
        public String email;
        public String website;
        public String address;
    }

    public static BitMatrix generateVCardQR(Contact contact)
            throws WriterException {

        String vcard = buildVCard(contact);

        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.MARGIN, 2);

        QRCodeWriter writer = new QRCodeWriter();
        return writer.encode(vcard, BarcodeFormat.QR_CODE, 400, 400, hints);
    }

    private static String buildVCard(Contact c) {
        StringBuilder vcard = new StringBuilder();

        vcard.append("BEGIN:VCARD\n");
        vcard.append("VERSION:3.0\n");

        // Name
        if (c.firstName != null || c.lastName != null) {
            vcard.append("N:")
                .append(c.lastName != null ? c.lastName : "").append(";")
                .append(c.firstName != null ? c.firstName : "").append(";;;\n");

            vcard.append("FN:")
                .append(c.firstName != null ? c.firstName + " " : "")
                .append(c.lastName != null ? c.lastName : "").append("\n");
        }

        // Organization and title
        if (c.organization != null) {
            vcard.append("ORG:").append(c.organization).append("\n");
        }
        if (c.title != null) {
            vcard.append("TITLE:").append(c.title).append("\n");
        }

        // Phone
        if (c.phone != null) {
            vcard.append("TEL;TYPE=CELL:").append(c.phone).append("\n");
        }

        // Email
        if (c.email != null) {
            vcard.append("EMAIL;TYPE=INTERNET:").append(c.email).append("\n");
        }

        // Website
        if (c.website != null) {
            vcard.append("URL:").append(c.website).append("\n");
        }

        // Address
        if (c.address != null) {
            vcard.append("ADR:;;").append(c.address).append(";;;;\n");
        }

        vcard.append("END:VCARD");

        return vcard.toString();
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        Contact contact = new Contact();
        contact.firstName = "John";
        contact.lastName = "Doe";
        contact.organization = "Tech Corp";
        contact.title = "Software Engineer";
        contact.phone = "+1-555-1234";
        contact.email = "john.doe@example.com";
        contact.website = "https://johndoe.com";

        BitMatrix qr = generateVCardQR(contact);
        System.out.println("vCard QR code generated!");
    }
}

Key Points:

  • Use vCard 3.0 format for broad compatibility
  • Include only essential fields (keeps QR code smaller)
  • UTF-8 encoding for international names
  • Error correction M balances size and reliability
  • Test with multiple QR readers/phones

Multi-Format Scanner with Fallback

Scenario: Try fast scanning first, fall back to thorough scan if needed.

import com.google.zxing.*;
import com.google.zxing.common.*;
import java.util.*;

public class AdaptiveScanner {

    private final MultiFormatReader reader;

    public AdaptiveScanner() {
        reader = new MultiFormatReader();
    }

    public Result scan(BinaryBitmap bitmap) {
        // Strategy 1: Fast scan with common formats
        Result result = tryFastScan(bitmap);
        if (result != null) {
            return result;
        }

        // Strategy 2: Try all formats
        result = tryAllFormats(bitmap);
        if (result != null) {
            return result;
        }

        // Strategy 3: Try harder with all formats
        result = tryHarderScan(bitmap);
        if (result != null) {
            return result;
        }

        // Strategy 4: Try inverted
        return tryInvertedScan(bitmap);
    }

    private Result tryFastScan(BinaryBitmap bitmap) {
        try {
            Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
            hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
                BarcodeFormat.QR_CODE,
                BarcodeFormat.EAN_13,
                BarcodeFormat.CODE_128
            ));

            return reader.decode(bitmap, hints);
        } catch (NotFoundException | ChecksumException | FormatException e) {
            return null;
        } finally {
            reader.reset();
        }
    }

    private Result tryAllFormats(BinaryBitmap bitmap) {
        try {
            return reader.decode(bitmap);
        } catch (NotFoundException | ChecksumException | FormatException e) {
            return null;
        } finally {
            reader.reset();
        }
    }

    private Result tryHarderScan(BinaryBitmap bitmap) {
        try {
            Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);

            return reader.decode(bitmap, hints);
        } catch (NotFoundException | ChecksumException | FormatException e) {
            return null;
        } finally {
            reader.reset();
        }
    }

    private Result tryInvertedScan(BinaryBitmap bitmap) {
        try {
            Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            hints.put(DecodeHintType.ALSO_INVERTED, Boolean.TRUE);

            return reader.decode(bitmap, hints);
        } catch (NotFoundException | ChecksumException | FormatException e) {
            return null;
        } finally {
            reader.reset();
        }
    }

    // Example usage
    public static void main(String[] args) {
        AdaptiveScanner scanner = new AdaptiveScanner();

        // BinaryBitmap bitmap = ...;
        // Result result = scanner.scan(bitmap);
        //
        // if (result != null) {
        //     System.out.println("Success: " + result.getText());
        // } else {
        //     System.out.println("Failed to decode barcode");
        // }
    }
}

Key Points:

  • Start with fastest approach (common formats, no TRY_HARDER)
  • Progressively try more expensive strategies
  • Reset reader between attempts
  • Try inverted as last resort
  • Good for uncertain input quality

High-Performance Video Scanning

Scenario: Optimize for real-time video frame processing with minimal CPU usage.

import com.google.zxing.*;
import com.google.zxing.common.*;
import java.util.*;
import java.util.concurrent.*;

public class VideoScanner {

    private final MultiFormatReader reader;
    private final ExecutorService executor;
    private final Map<DecodeHintType, Object> hints;
    private volatile boolean isScanning = false;

    // Rate limiting
    private static final long MIN_FRAME_INTERVAL_MS = 100;
    private long lastFrameTime = 0;

    // Deduplication
    private String lastResult = null;
    private long lastResultTime = 0;
    private static final long RESULT_DEDUP_MS = 2000;

    public VideoScanner() {
        reader = new MultiFormatReader();
        executor = Executors.newSingleThreadExecutor();

        // Optimize for speed
        hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.POSSIBLE_FORMATS, Arrays.asList(
            BarcodeFormat.QR_CODE,
            BarcodeFormat.EAN_13,
            BarcodeFormat.CODE_128
        ));
        hints.put(DecodeHintType.TRY_HARDER, Boolean.FALSE);
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");

        reader.setHints(hints);
    }

    public void scanFrameAsync(byte[] yuvData, int width, int height,
                              ScanCallback callback) {
        // Rate limit frame processing
        long now = System.currentTimeMillis();
        if (now - lastFrameTime < MIN_FRAME_INTERVAL_MS) {
            return;
        }
        lastFrameTime = now;

        // Skip if already processing
        if (isScanning) {
            return;
        }

        isScanning = true;

        executor.submit(() -> {
            try {
                Result result = scanFrame(yuvData, width, height);

                if (result != null) {
                    // Deduplicate results
                    if (!isDuplicate(result.getText())) {
                        callback.onBarcodeDetected(result);
                        lastResult = result.getText();
                        lastResultTime = System.currentTimeMillis();
                    }
                }
            } finally {
                isScanning = false;
            }
        });
    }

    private Result scanFrame(byte[] yuvData, int width, int height) {
        try {
            // Process center region only (faster)
            int cropSize = Math.min(width, height) * 2 / 3;
            int left = (width - cropSize) / 2;
            int top = (height - cropSize) / 2;

            LuminanceSource source = new PlanarYUVLuminanceSource(
                yuvData, width, height,
                left, top, cropSize, cropSize,
                false
            );

            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            return reader.decodeWithState(bitmap);

        } catch (NotFoundException e) {
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    private boolean isDuplicate(String text) {
        if (lastResult == null) {
            return false;
        }

        long now = System.currentTimeMillis();
        return text.equals(lastResult) &&
               (now - lastResultTime) < RESULT_DEDUP_MS;
    }

    public void shutdown() {
        executor.shutdown();
    }

    public interface ScanCallback {
        void onBarcodeDetected(Result result);
    }

    // Example usage
    public static void main(String[] args) {
        VideoScanner scanner = new VideoScanner();

        ScanCallback callback = result -> {
            System.out.println("Detected: " + result.getText());
            System.out.println("Format: " + result.getBarcodeFormat());
        };

        // In camera preview callback:
        // scanner.scanFrameAsync(yuvData, width, height, callback);

        // When done:
        // scanner.shutdown();
    }
}

Key Points:

  • Process frames asynchronously to avoid blocking camera
  • Rate limit (don't process every frame)
  • Crop to center region (faster, barcodes usually centered)
  • Deduplicate results (same barcode scanned multiple times)
  • Use decodeWithState() to maintain reader state
  • Single-threaded executor prevents concurrent processing

Barcode Quality Validator

Scenario: Validate generated barcodes can be reliably decoded.

import com.google.zxing.*;
import com.google.zxing.common.*;
import java.awt.image.BufferedImage;

public class BarcodeValidator {

    public static class ValidationResult {
        public boolean isReadable;
        public String decodedText;
        public String expectedText;
        public boolean textMatches;
        public BarcodeFormat detectedFormat;
        public BarcodeFormat expectedFormat;
        public boolean formatMatches;
        public String errorMessage;
        public int qualityScore; // 0-100

        public boolean isValid() {
            return isReadable && textMatches && formatMatches;
        }
    }

    public static ValidationResult validate(BitMatrix matrix,
                                           String expectedText,
                                           BarcodeFormat expectedFormat) {

        ValidationResult result = new ValidationResult();
        result.expectedText = expectedText;
        result.expectedFormat = expectedFormat;

        try {
            // Convert BitMatrix to BinaryBitmap
            BufferedImage image = toBufferedImage(matrix);
            int[] pixels = image.getRGB(0, 0, image.getWidth(),
                                       image.getHeight(), null, 0,
                                       image.getWidth());
            LuminanceSource source = new RGBLuminanceSource(
                image.getWidth(), image.getHeight(), pixels);
            BinaryBitmap bitmap = new BinaryBitmap(
                new HybridBinarizer(source));

            // Try to decode
            Reader reader = new MultiFormatReader();
            Result decoded = reader.decode(bitmap);

            result.isReadable = true;
            result.decodedText = decoded.getText();
            result.detectedFormat = decoded.getBarcodeFormat();

            // Validate text
            result.textMatches = expectedText.equals(decoded.getText());

            // Validate format
            result.formatMatches = expectedFormat == decoded.getBarcodeFormat();

            // Calculate quality score
            result.qualityScore = calculateQualityScore(
                matrix, result.textMatches, result.formatMatches);

        } catch (NotFoundException e) {
            result.isReadable = false;
            result.errorMessage = "Barcode not detectable";
            result.qualityScore = 0;

        } catch (ChecksumException e) {
            result.isReadable = false;
            result.errorMessage = "Checksum validation failed";
            result.qualityScore = 20;

        } catch (FormatException e) {
            result.isReadable = false;
            result.errorMessage = "Format validation failed";
            result.qualityScore = 10;
        }

        return result;
    }

    private static int calculateQualityScore(BitMatrix matrix,
                                            boolean textMatches,
                                            boolean formatMatches) {
        int score = 50; // Base score for being readable

        if (textMatches) score += 25;
        if (formatMatches) score += 25;

        // Check size (larger is generally better)
        int size = matrix.getWidth() * matrix.getHeight();
        if (size >= 300 * 300) score = Math.min(100, score + 10);
        else if (size < 150 * 150) score -= 10;

        return Math.max(0, Math.min(100, score));
    }

    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height,
                                               BufferedImage.TYPE_INT_RGB);

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int color = matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF;
                image.setRGB(x, y, color);
            }
        }

        return image;
    }

    // Example usage
    public static void main(String[] args) throws Exception {
        // Generate a barcode
        Writer writer = new MultiFormatWriter();
        BitMatrix matrix = writer.encode(
            "TEST12345",
            BarcodeFormat.CODE_128,
            300,
            100
        );

        // Validate it
        ValidationResult validation = validate(
            matrix,
            "TEST12345",
            BarcodeFormat.CODE_128
        );

        System.out.println("Is valid: " + validation.isValid());
        System.out.println("Quality score: " + validation.qualityScore);

        if (!validation.isValid()) {
            System.out.println("Error: " + validation.errorMessage);
        }
    }
}

Key Points:

  • Always validate generated barcodes before production use
  • Check text matches expected value
  • Verify format is correct
  • Test readability with actual decoder
  • Calculate quality score for automated testing
  • Useful for CI/CD pipelines

See Also

  • Quick Start - Getting started guide
  • Core Reading and Writing - Basic APIs
  • Configuration Hints - All configuration options
  • Result Parsing - Parse structured data
  • Exception Handling - Error handling

Install with Tessl CLI

npx tessl i tessl/maven-com-google-zxing--core@3.5.1

docs

examples

real-world-scenarios.md

index.md

tile.json