Skip to footer content
COMPARE TO OTHER COMPONENTS

ZXing.Net vs IronBarcode: C# Barcode Library Comparison

ZXing.Net's BarcodeReader is not thread-safe. Every concurrent read path in your application — every parallel ForEach, every async controller action handling simultaneous requests — requires its own reader instance. That means format configuration repeated per instance, Bitmap loading and disposal per call, and reader allocation on every request. IronBarcode's static method handles the same work without per-request allocation. Thread safety is just one item on the list: no native PDF support, binding package fragmentation across Windows and Linux, and the requirement to specify every barcode format you intend to scan for — these are the characteristics that cause ZXing.Net projects to accumulate workarounds over time.

Understanding ZXing.Net

ZXing.Net is an open-source port of Google's Java ZXing ("Zebra Crossing") library, released under the Apache 2.0 licence and maintained by Michael Jahn on GitHub. It is the most downloaded open-source barcode library for .NET, with a long track record stretching back over a decade. The library covers both reading and writing across a broad set of barcode formats, and its zero-cost licence makes it accessible for projects of all sizes, including open-source products that require Apache-compatible dependencies.

ZXing.Net's architecture reflects its origins as a Java port. The BarcodeReader class is a stateful object: calling Decode writes internal state, which makes instances unsafe to share across threads. Correct concurrent usage requires either a new reader per thread or a ThreadLocal<BarcodeReader> pool — both of which place thread-safety responsibility on the caller. Image loading is also not bundled in the core package; instead, ZXing.Net provides platform-specific binding packages that each expose a different API surface.

Key architectural characteristics include:

  • Apache 2.0 Licence: Free for commercial and open-source use, with no cost and no contact-sales requirement.
  • Stateful BarcodeReader: The BarcodeReader instance is not thread-safe; a new instance must be created per thread or per parallel operation.
  • Manual Format Specification: Callers must set reader.Options.PossibleFormats before each decode; formats not listed are silently skipped with no error or warning.
  • No Native PDF Support: The library reads images only; PDF input requires a separate PDF rendering library such as PdfiumViewer to convert pages to bitmaps before decoding.
  • Platform Binding Fragmentation: The core package provides no image loading; separate binding packages (ZXing.Net.Bindings.Windows using System.Drawing, ZXing.Net.Bindings.ImageSharp using SixLabors.ImageSharp) expose different APIs for different deployment targets.
  • Active Community: Issues, pull requests, and discussion are active on GitHub, providing community-level support at no cost.
  • Broad Format Coverage: Supports QR Code, Code 128, Code 39, EAN-13, EAN-8, UPC-A, Data Matrix, PDF 417, Aztec, and other widely used symbologies.

The Stateful Reader Architecture

ZXing.Net's BarcodeReader requires a new instance for each thread or parallel operation. The following pattern shows the minimum correct setup for concurrent processing:

// ZXing.Net: must create a new reader per thread — not safe to share
Parallel.ForEach(imagePaths, path =>
{
    var reader = new BarcodeReader();               // new instance per iteration
    reader.Options.PossibleFormats = new List<BarcodeFormat>
    {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    };

    using var bitmap = new Bitmap(path);
    var result = reader.Decode(bitmap);

    if (result != null)
        results[path] = result.Text;
});
// ZXing.Net: must create a new reader per thread — not safe to share
Parallel.ForEach(imagePaths, path =>
{
    var reader = new BarcodeReader();               // new instance per iteration
    reader.Options.PossibleFormats = new List<BarcodeFormat>
    {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    };

    using var bitmap = new Bitmap(path);
    var result = reader.Decode(bitmap);

    if (result != null)
        results[path] = result.Text;
});
Imports ZXing
Imports System.Drawing
Imports System.Threading.Tasks

' ZXing.Net: must create a new reader per thread — not safe to share
Parallel.ForEach(imagePaths, Sub(path)
    Dim reader = New BarcodeReader() ' new instance per iteration
    reader.Options.PossibleFormats = New List(Of BarcodeFormat) From {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    }

    Using bitmap As New Bitmap(path)
        Dim result = reader.Decode(bitmap)

        If result IsNot Nothing Then
            results(path) = result.Text
        End If
    End Using
End Sub)
$vbLabelText   $csharpLabel

The ThreadLocal<BarcodeReader> pattern reduces per-call allocation by reusing one instance per thread, but adds disposal complexity. Either way, thread safety is the caller's responsibility in ZXing.Net, not the library's.

Understanding IronBarcode

IronBarcode is a commercial .NET barcode library developed by Iron Software. It provides barcode reading and generation through a stateless static API: BarcodeReader.Read and BarcodeWriter.CreateBarcode are the primary entry points, and neither requires instance creation or configuration before use. The library ships as a single NuGet package that runs identically on Windows, Linux, macOS, and inside Docker containers without platform-specific binding packages.

IronBarcode's reading engine performs automatic format detection across more than 50 barcode symbologies. Callers do not specify which formats to look for; the engine evaluates each image against all supported formats and returns every barcode found. For applications that need to balance scanning speed against thoroughness, a ReadingSpeed enum (Faster, Balanced, Detailed, ExtremeDetail) provides control without requiring format enumeration. The library also includes native PDF support, reading barcodes directly from PDF documents across all pages without requiring a separate PDF rendering library.

Key characteristics include:

  • Commercial Licence: Requires a paid licence key; trial mode is available for evaluation.
  • Stateless Static API: BarcodeReader.Read and BarcodeWriter.CreateBarcode are thread-safe static methods; no instance management is required.
  • Automatic Format Detection: All 50+ supported barcode formats are detected without a format list; callers tune speed rather than format scope.
  • Native PDF Support: BarcodeReader.Read accepts PDF file paths directly, processing all pages without an external PDF library.
  • Single Cross-Platform Package: One NuGet package runs on Windows, Linux, macOS, and Docker with the same API surface.
  • Fluent Generation API: BarcodeWriter.CreateBarcode returns a GeneratedBarcode object with built-in output methods including SaveAsPng, SaveAsPdf, ToPngBinaryData, and ToStream.
  • Commercial Support: Paid licences include access to Iron Software's support team with service-level commitments.

Feature Comparison

The following table highlights the fundamental differences between ZXing.Net and IronBarcode:

Feature ZXing.Net IronBarcode
Licence Apache 2.0 (free) Commercial
Thread Safety Not thread-safe — new instance per thread required Thread-safe static API
Format Detection Manual — PossibleFormats list required Automatic — 50+ formats
PDF Reading No — requires PdfiumViewer or similar Native — single method call
Platform Support Separate binding packages per platform Single package, all platforms
Barcode Generation Returns Bitmap, requires manual save Fluent API with built-in output
Pricing Free From $799 (Lite) / $1,199 (Professional)

Detailed Feature Comparison

Feature ZXing.Net IronBarcode
Reading
Thread-safe reading No — per-thread instances required Yes — static stateless API
Automatic format detection No — PossibleFormats required Yes — all formats auto-detected
Multiple barcodes per image Yes — DecodeMultiple Yes — default behaviour
PDF barcode reading No — external library required Yes — native
Damaged barcode recovery TryHarder flag ML-powered image correction
Speed / accuracy tuning Format list reduction ReadingSpeed enum
Generation
Code 128 / Code 39 Yes Yes
QR Code Yes Yes
QR Code with logo No Yes
QR Code colour customisation No Yes
Output to PNG/JPEG/PDF Manual via System.Drawing Built-in output methods
Fluent generation chain No Yes
Platform and Deployment
Windows (System.Drawing) Yes — ZXing.Net.Bindings.Windows Yes
Linux / Docker Yes — ZXing.Net.Bindings.ImageSharp (different API) Yes — same API
macOS Yes — ZXing.Net.Bindings.ImageSharp Yes
Docker extra dependencies May require libgdiplus None
Single NuGet package No — core + binding + optional ImageSharp Yes
Licensing and Support
Licence type Apache 2.0 Commercial
Cost Free From $799
Community support GitHub issues, active community Yes
Commercial SLA support No Yes
Open-source friendly Yes No

Thread Safety and Concurrent Processing

Thread safety is the most important operational difference between the two libraries, and it affects more projects than the documentation warnings might suggest.

ZXing.Net Approach

ZXing.Net's BarcodeReader is a stateful object. When Decode is called, internal state is written. If two threads share the same instance, those writes race. The results are non-deterministic: you may get a result from a different image, a null where a barcode exists, or an exception. The correct approach is to create one reader per thread — meaning every parallel or async path allocates a reader, configures it, uses it once, and discards it:

// ZXing.Net: new reader instance required per parallel operation
Parallel.ForEach(imagePaths, path =>
{
    var reader = new BarcodeReader();
    reader.Options.PossibleFormats = new List<BarcodeFormat>
    {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    };

    using var bitmap = new Bitmap(path);
    var result = reader.Decode(bitmap);

    if (result != null)
        results[path] = result.Text;
});
// ZXing.Net: new reader instance required per parallel operation
Parallel.ForEach(imagePaths, path =>
{
    var reader = new BarcodeReader();
    reader.Options.PossibleFormats = new List<BarcodeFormat>
    {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    };

    using var bitmap = new Bitmap(path);
    var result = reader.Decode(bitmap);

    if (result != null)
        results[path] = result.Text;
});
Imports ZXing
Imports System.Drawing
Imports System.Threading.Tasks

' ZXing.Net: new reader instance required per parallel operation
Parallel.ForEach(imagePaths, Sub(path)
    Dim reader = New BarcodeReader()
    reader.Options.PossibleFormats = New List(Of BarcodeFormat) From {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.EAN_13
    }

    Using bitmap As New Bitmap(path)
        Dim result = reader.Decode(bitmap)

        If result IsNot Nothing Then
            results(path) = result.Text
        End If
    End Using
End Sub)
$vbLabelText   $csharpLabel

The ThreadLocal<BarcodeReader> pattern reduces per-call allocation by reusing one reader per thread, but it adds disposal complexity and still requires format configuration on each instance.

IronBarcode Approach

IronBarcode's BarcodeReader.Read is a stateless static method. There is no instance to share, no instance to isolate, and no configuration state to protect. The same call runs safely from any number of concurrent threads:

// IronBarcode: static method, thread-safe, no instance management
var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Balanced,
    ExpectMultipleBarcodes = true,
    MaxParallelThreads = 4
};

Parallel.ForEach(imagePaths, path =>
{
    var result = BarcodeReader.Read(path, options).FirstOrDefault();
    if (result != null)
        results[path] = result.Value;
});
// IronBarcode: static method, thread-safe, no instance management
var options = new BarcodeReaderOptions
{
    Speed = ReadingSpeed.Balanced,
    ExpectMultipleBarcodes = true,
    MaxParallelThreads = 4
};

Parallel.ForEach(imagePaths, path =>
{
    var result = BarcodeReader.Read(path, options).FirstOrDefault();
    if (result != null)
        results[path] = result.Value;
});
Imports System.Threading.Tasks
Imports IronBarcode

' IronBarcode: static method, thread-safe, no instance management
Dim options As New BarcodeReaderOptions With {
    .Speed = ReadingSpeed.Balanced,
    .ExpectMultipleBarcodes = True,
    .MaxParallelThreads = 4
}

Parallel.ForEach(imagePaths, Sub(path)
    Dim result = BarcodeReader.Read(path, options).FirstOrDefault()
    If result IsNot Nothing Then
        results(path) = result.Value
    End If
End Sub)
$vbLabelText   $csharpLabel

IronBarcode's async and multithreaded barcode reading documentation explains the internal thread pooling model. The MaxParallelThreads property controls the degree of parallelism without requiring external coordination.

Format Detection

Format detection policy is the second significant operational difference between the two libraries.

ZXing.Net Approach

ZXing.Net requires reader.Options.PossibleFormats to be populated before each decode. If a barcode format is not listed, it will not be detected — with no error, no warning, and no partial result:

// ZXing.Net: format list required — unlisted formats produce null, not an error
var reader = new BarcodeReader();
reader.Options.PossibleFormats = new List<BarcodeFormat>
{
    BarcodeFormat.QR_CODE,
    BarcodeFormat.CODE_128,
    BarcodeFormat.CODE_39,
    BarcodeFormat.EAN_13,
    BarcodeFormat.EAN_8,
    BarcodeFormat.UPC_A,
    BarcodeFormat.DATA_MATRIX,
    BarcodeFormat.PDF_417
    // An Aztec barcode in the same image will return null silently.
};
reader.Options.TryHarder = true;

using var bitmap = new Bitmap(imagePath);
var results = reader.DecodeMultiple(bitmap);
// ZXing.Net: format list required — unlisted formats produce null, not an error
var reader = new BarcodeReader();
reader.Options.PossibleFormats = new List<BarcodeFormat>
{
    BarcodeFormat.QR_CODE,
    BarcodeFormat.CODE_128,
    BarcodeFormat.CODE_39,
    BarcodeFormat.EAN_13,
    BarcodeFormat.EAN_8,
    BarcodeFormat.UPC_A,
    BarcodeFormat.DATA_MATRIX,
    BarcodeFormat.PDF_417
    // An Aztec barcode in the same image will return null silently.
};
reader.Options.TryHarder = true;

using var bitmap = new Bitmap(imagePath);
var results = reader.DecodeMultiple(bitmap);
Imports ZXing
Imports ZXing.Common

' ZXing.Net: format list required — unlisted formats produce null, not an error
Dim reader As New BarcodeReader()
reader.Options.PossibleFormats = New List(Of BarcodeFormat) From {
    BarcodeFormat.QR_CODE,
    BarcodeFormat.CODE_128,
    BarcodeFormat.CODE_39,
    BarcodeFormat.EAN_13,
    BarcodeFormat.EAN_8,
    BarcodeFormat.UPC_A,
    BarcodeFormat.DATA_MATRIX,
    BarcodeFormat.PDF_417
    ' An Aztec barcode in the same image will return null silently.
}
reader.Options.TryHarder = True

Using bitmap As New Bitmap(imagePath)
    Dim results = reader.DecodeMultiple(bitmap)
End Using
$vbLabelText   $csharpLabel

If every format is listed to avoid misses, performance degrades. If only expected formats are listed, silent misses become a risk when images arrive from external sources. There is no mechanism for ZXing.Net to report that it found a barcode in an unlisted format.

IronBarcode Approach

IronBarcode detects all 50+ supported formats automatically. No format list is required or accepted:

// IronBarcode: no format list — all 50+ formats detected automatically
var results = BarcodeReader.Read(imagePath);
foreach (var barcode in results)
{
    Console.WriteLine($"{barcode.Value} ({barcode.Format})");
}
// IronBarcode: no format list — all 50+ formats detected automatically
var results = BarcodeReader.Read(imagePath);
foreach (var barcode in results)
{
    Console.WriteLine($"{barcode.Value} ({barcode.Format})");
}
Imports IronBarcode

' IronBarcode: no format list — all 50+ formats detected automatically
Dim results = BarcodeReader.Read(imagePath)
For Each barcode In results
    Console.WriteLine($"{barcode.Value} ({barcode.Format})")
Next
$vbLabelText   $csharpLabel

For applications that tune reading speed against detection thoroughness, IronBarcode exposes a ReadingSpeed enum — Faster, Balanced, Detailed, ExtremeDetail — without requiring format enumeration. Speed tuning affects how aggressively the engine processes each image, not which formats it considers.

PDF Document Support

PDF reading is a hard boundary in ZXing.Net: the library has no PDF support of any kind.

ZXing.Net Approach

When a barcode arrives embedded in a PDF, ZXing.Net requires an external PDF rendering library. The most common workaround uses PdfiumViewer, which adds approximately 20 MB of native binaries and a page rendering loop that callers must implement and maintain:

// ZXing.Net: PDF requires PdfiumViewer + a render loop per page
using var pdfDocument = PdfDocument.Load(pdfPath);

var reader = new BarcodeReader();
reader.Options.PossibleFormats = new List<BarcodeFormat>
{
    BarcodeFormat.QR_CODE,
    BarcodeFormat.CODE_128,
    BarcodeFormat.PDF_417
};

for (int i = 0; i < pdfDocument.PageCount; i++)
{
    string tempPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png");
    try
    {
        using var pageImage = pdfDocument.Render(i, 200, 200, PdfRenderFlags.CorrectFromDpi);
        pageImage.Save(tempPath, ImageFormat.Png);

        using var bitmap = new Bitmap(tempPath);
        var decoded = reader.DecodeMultiple(bitmap);
        if (decoded != null)
            results.AddRange(decoded.Select(d => d.Text));
    }
    finally
    {
        File.Delete(tempPath);
    }
}
// ZXing.Net: PDF requires PdfiumViewer + a render loop per page
using var pdfDocument = PdfDocument.Load(pdfPath);

var reader = new BarcodeReader();
reader.Options.PossibleFormats = new List<BarcodeFormat>
{
    BarcodeFormat.QR_CODE,
    BarcodeFormat.CODE_128,
    BarcodeFormat.PDF_417
};

for (int i = 0; i < pdfDocument.PageCount; i++)
{
    string tempPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png");
    try
    {
        using var pageImage = pdfDocument.Render(i, 200, 200, PdfRenderFlags.CorrectFromDpi);
        pageImage.Save(tempPath, ImageFormat.Png);

        using var bitmap = new Bitmap(tempPath);
        var decoded = reader.DecodeMultiple(bitmap);
        if (decoded != null)
            results.AddRange(decoded.Select(d => d.Text));
    }
    finally
    {
        File.Delete(tempPath);
    }
}
Imports PdfiumViewer
Imports ZXing
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.IO

Using pdfDocument = PdfDocument.Load(pdfPath)

    Dim reader = New BarcodeReader()
    reader.Options.PossibleFormats = New List(Of BarcodeFormat) From {
        BarcodeFormat.QR_CODE,
        BarcodeFormat.CODE_128,
        BarcodeFormat.PDF_417
    }

    For i As Integer = 0 To pdfDocument.PageCount - 1
        Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png")
        Try
            Using pageImage = pdfDocument.Render(i, 200, 200, PdfRenderFlags.CorrectFromDpi)
                pageImage.Save(tempPath, ImageFormat.Png)

                Using bitmap = New Bitmap(tempPath)
                    Dim decoded = reader.DecodeMultiple(bitmap)
                    If decoded IsNot Nothing Then
                        results.AddRange(decoded.Select(Function(d) d.Text))
                    End If
                End Using
            End Using
        Finally
            File.Delete(tempPath)
        End Try
    Next
End Using
$vbLabelText   $csharpLabel

This pattern has real failure modes: DPI misconfiguration causes silent misses, temp file permissions fail in locked-down environments, the PdfiumViewer native libraries require separate deployment management, and the setup does not work on Linux without additional configuration.

IronBarcode Approach

IronBarcode reads barcodes from PDFs natively — all pages, all formats, no external dependencies. The same BarcodeReader.Read call accepts PDF paths directly:

// IronBarcode: one call, all pages, native PDF support
var results = BarcodeReader.Read("invoice.pdf");
foreach (var barcode in results)
    Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Value}");
// IronBarcode: one call, all pages, native PDF support
var results = BarcodeReader.Read("invoice.pdf");
foreach (var barcode in results)
    Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Value}");
Imports IronBarcode

' IronBarcode: one call, all pages, native PDF support
Dim results = BarcodeReader.Read("invoice.pdf")
For Each barcode In results
    Console.WriteLine($"Page {barcode.PageNumber}: {barcode.Value}")
Next
$vbLabelText   $csharpLabel

No page enumeration, no DPI configuration, no temp files, and no separate library to deploy alongside the application.

Platform Bindings and Deployment

ZXing.Net's image handling is distributed across platform-specific binding packages, each with a different API surface.

ZXing.Net Approach

The core ZXing.Net package provides decoding logic but no image loading. Callers must choose a binding based on their deployment target:

Environment Package Notes
Windows desktop / WPF ZXing.Net.Bindings.Windows Uses System.Drawing — Windows only
Linux / Docker / cross-platform ZXing.Net.Bindings.ImageSharp Different API surface; adds SixLabors.ImageSharp dependency
macOS ZXing.Net.Bindings.ImageSharp Same path as Linux

The practical consequence is that Windows code using System.Drawing.Bitmap does not compile or run on Linux. Containerisation requires switching binding packages and rewriting image-loading code:

// Windows path — does not compile or run on Linux
using ZXing.Windows.Compatibility;
using System.Drawing;

var reader = new BarcodeReader();
using var bitmap = new Bitmap(imagePath);
var result = reader.Decode(bitmap);
// Windows path — does not compile or run on Linux
using ZXing.Windows.Compatibility;
using System.Drawing;

var reader = new BarcodeReader();
using var bitmap = new Bitmap(imagePath);
var result = reader.Decode(bitmap);
Imports ZXing.Windows.Compatibility
Imports System.Drawing

Dim reader As New BarcodeReader()
Using bitmap As New Bitmap(imagePath)
    Dim result = reader.Decode(bitmap)
End Using
$vbLabelText   $csharpLabel
// Linux / cross-platform path — different package, different API
using ZXing.ImageSharp;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

var reader = new BarcodeReader<Rgba32>();
using var image = Image.Load<Rgba32>(imagePath);
var result = reader.Decode(image);
// Linux / cross-platform path — different package, different API
using ZXing.ImageSharp;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

var reader = new BarcodeReader<Rgba32>();
using var image = Image.Load<Rgba32>(imagePath);
var result = reader.Decode(image);
Imports ZXing.ImageSharp
Imports SixLabors.ImageSharp
Imports SixLabors.ImageSharp.PixelFormats

Dim reader As New BarcodeReader(Of Rgba32)()
Using image As Image(Of Rgba32) = Image.Load(Of Rgba32)(imagePath)
    Dim result = reader.Decode(image)
End Using
$vbLabelText   $csharpLabel

Using the Windows binding on a Linux host also requires libgdiplus in the Docker image, adding approximately 50 MB to the container.

IronBarcode Approach

IronBarcode ships one package for all platforms. The same BarcodeReader.Read(path) call compiles and runs identically on Windows, Linux, macOS, and inside Docker containers. No binding packages, no platform conditionals, and no additional system dependencies are required. For teams containerising .NET applications, the IronBarcode Docker and Linux setup guide covers the single-package deployment pattern in detail.

Generation API

Both libraries generate barcodes, but the APIs reflect different design approaches.

ZXing.Net Approach

ZXing.Net's BarcodeWriter class creates a writer object, accepts encoding options, calls Write, and returns a Bitmap that callers must save manually using System.Drawing.Imaging:

// ZXing.Net: create writer, set options, call Write, save Bitmap manually
using ZXing;
using ZXing.Common;
using ZXing.Windows.Compatibility;
using System.Drawing.Imaging;

var writer = new BarcodeWriter
{
    Format = BarcodeFormat.CODE_128,
    Options = new EncodingOptions
    {
        Width = 300,
        Height = 100,
        Margin = 10
    }
};

using var bitmap = writer.Write("PRODUCT-001");
bitmap.Save("output.png", ImageFormat.Png);
// ZXing.Net: create writer, set options, call Write, save Bitmap manually
using ZXing;
using ZXing.Common;
using ZXing.Windows.Compatibility;
using System.Drawing.Imaging;

var writer = new BarcodeWriter
{
    Format = BarcodeFormat.CODE_128,
    Options = new EncodingOptions
    {
        Width = 300,
        Height = 100,
        Margin = 10
    }
};

using var bitmap = writer.Write("PRODUCT-001");
bitmap.Save("output.png", ImageFormat.Png);
Imports ZXing
Imports ZXing.Common
Imports ZXing.Windows.Compatibility
Imports System.Drawing.Imaging

Dim writer As New BarcodeWriter With {
    .Format = BarcodeFormat.CODE_128,
    .Options = New EncodingOptions With {
        .Width = 300,
        .Height = 100,
        .Margin = 10
    }
}

Using bitmap = writer.Write("PRODUCT-001")
    bitmap.Save("output.png", ImageFormat.Png)
End Using
$vbLabelText   $csharpLabel

Saving to formats other than PNG or JPEG, saving to a PDF, or producing binary output for an HTTP response all require additional System.Drawing plumbing that the caller implements.

IronBarcode Approach

IronBarcode's BarcodeWriter.CreateBarcode returns a GeneratedBarcode object that provides built-in output methods through a fluent chain:

// IronBarcode: create and save in one fluent chain
BarcodeWriter.CreateBarcode("PRODUCT-001", BarcodeEncoding.Code128)
    .ResizeTo(300, 100)
    .SaveAsPng("output.png");
// IronBarcode: create and save in one fluent chain
BarcodeWriter.CreateBarcode("PRODUCT-001", BarcodeEncoding.Code128)
    .ResizeTo(300, 100)
    .SaveAsPng("output.png");
Imports IronBarCode

' IronBarcode: create and save in one fluent chain
BarcodeWriter.CreateBarcode("PRODUCT-001", BarcodeEncoding.Code128) _
    .ResizeTo(300, 100) _
    .SaveAsPng("output.png")
$vbLabelText   $csharpLabel

The GeneratedBarcode object also exposes .ToPngBinaryData(), .ToJpegBinaryData(), .ToStream(), and .SaveAsPdf() — output targets that ZXing.Net requires callers to implement themselves using System.Drawing.

API Mapping Reference

ZXing.Net IronBarcode Notes
new BarcodeReader() Static — no instance BarcodeReader.Read(...) is a static call
reader.Options.PossibleFormats = new List<BarcodeFormat> { ... } Not needed Auto-detection covers all formats
reader.Options.TryHarder = true Speed = ReadingSpeed.Balanced Accuracy tuning via BarcodeReaderOptions
reader.Decode(bitmap) BarcodeReader.Read(imagePath) Pass path directly — no Bitmap needed
reader.DecodeMultiple(bitmap) BarcodeReader.Read(imagePath) Always returns a collection
result.Text result.Value Property renamed
result.BarcodeFormat result.Format Property renamed
BarcodeFormat.QR_CODE BarcodeEncoding.QRCode Enum naming convention change
BarcodeFormat.CODE_128 BarcodeEncoding.Code128 Enum naming convention change
BarcodeFormat.EAN_13 BarcodeEncoding.EAN13 Enum naming convention change
new BarcodeWriter { Format = BarcodeFormat.CODE_128, Options = new EncodingOptions { ... } } BarcodeWriter.CreateBarcode(data, BarcodeEncoding.Code128) Static factory method
writer.Write(data) returns Bitmap .SaveAsPng(path) / .ToPngBinaryData() Built-in output methods on GeneratedBarcode
No PDF support — needs PdfiumViewer BarcodeReader.Read("doc.pdf") Native PDF support
Not thread-safe Thread-safe static API No instance management needed

When Teams Consider Moving from ZXing.Net to IronBarcode

Several scenarios prompt development teams to evaluate IronBarcode as an alternative to ZXing.Net.

Concurrent and High-Throughput Processing

A ZXing.Net integration that works correctly in a single-threaded console application can exhibit non-deterministic behaviour when the same code is moved into an ASP.NET Core controller handling concurrent requests, or into a background job that processes batches in parallel. The per-thread instance pattern that ZXing.Net requires means that every request or parallel task allocates a full reader object with its configured decoder set, uses it once, and discards it. As request volume grows, this allocation pressure becomes measurable. Teams that reach this point — particularly those running high-throughput scanning pipelines in logistics, warehousing, or document processing — often look for a library whose threading model does not impose this overhead on every caller.

Mixed-Format Document Scanning

Applications that accept documents from third parties frequently encounter barcode formats that were not anticipated when the format list was configured. A shipping integration built for Code 128 receives a shipment from a partner using QR codes. A medical records system configured for PDF 417 encounters Data Matrix symbologies from a new equipment vendor. In ZXing.Net, each of these cases produces a silent null rather than a decode error, meaning the miss may not surface until downstream processing fails. Teams that have been burned by silent format misses — particularly in contexts where a missed barcode has business consequences — begin to weigh the cost of format list maintenance against a library that detects formats automatically.

PDF Barcode Extraction

Many business documents arrive as PDFs: invoices, shipping manifests, patient records, compliance certificates. ZXing.Net cannot read these directly. Teams that need to extract barcodes from PDFs end up building and maintaining a rendering pipeline on top of ZXing.Net — selecting a PDF library, managing its native dependencies, implementing a page render loop, handling DPI configuration, and writing cleanup logic for temporary files. That infrastructure code is not barcode logic; it exists solely to bridge the gap between what ZXing.Net accepts (bitmaps) and what business documents actually are (PDFs). Teams that find themselves maintaining this bridge — especially when the PDF library itself has its own licensing, deployment, and platform considerations — often prefer a single library that handles the full input surface.

Reducing Binding Complexity

Projects that start on Windows and later deploy to Linux or Docker encounter ZXing.Net's binding fragmentation directly: the Windows binding package uses a different API from the cross-platform ImageSharp binding, and switching between them requires changing both the NuGet references and the image-loading code. Teams that containerise their .NET applications as a matter of practice, or that run the same codebase on developer machines (Windows or macOS) and production servers (Linux), find that maintaining two code paths for image loading adds friction to every future change in the scanning layer.

Common Migration Considerations

Teams transitioning from ZXing.Net to IronBarcode should plan for these specific technical changes.

BarcodeReader Instantiation Removal

Every new BarcodeReader() call in the codebase is removed during migration. The instantiation pattern — reader creation, format configuration, image loading, decode call — collapses into a single static method call. Files that import ZXing, ZXing.Common, ZXing.Windows.Compatibility, ZXing.ImageSharp, and SixLabors.ImageSharp namespaces have those imports removed and replaced with a single using IronBarCode;.

PossibleFormats Configuration Removal

The reader.Options.PossibleFormats assignment block is deleted at each call site. IronBarcode performs automatic format detection and does not accept a format restriction list. The TryHarder flag is replaced by the ReadingSpeed property on BarcodeReaderOptions, which controls detection effort without narrowing the format scope.

Binding Package Cleanup

Migration removes ZXing.Net, ZXing.Net.Bindings.Windows, ZXing.Net.Bindings.ImageSharp, and any PdfiumViewer packages that were added to support PDF reading. If libgdiplus was added to a Dockerfile to support System.Drawing through the Windows binding on Linux, that line is also removed. IronBarcode requires no platform-specific dependencies in the Docker image.

Additional IronBarcode Capabilities

Beyond the capabilities covered in the comparison sections above, IronBarcode provides:

  • QR Code with Logo Embedding: Generate QR codes with a custom logo image overlaid in the centre, with automatic error correction to ensure scannability.
  • QR Code Colour Customisation: Set foreground and background colours on generated QR codes for brand-aligned output.
  • Barcode Stamping onto PDFs: Write barcodes directly onto existing PDF pages at specified coordinates without a separate PDF library.
  • GS1 Barcode Support: Read and generate GS1-formatted barcodes, including GS1-128 and GS1 DataBar, used in retail and healthcare supply chains.
  • Multithreaded Async Reading: Built-in async methods for integrating barcode reading into async/await pipelines without blocking.
  • ML-Powered Image Correction: Automatic image enhancement for damaged, blurred, or low-contrast barcodes that TryHarder cannot recover.

.NET Compatibility and Future Readiness

IronBarcode targets .NET Standard 2.0 and above, providing compatibility with .NET Framework 4.6.2+, .NET Core 3.1, .NET 5, .NET 6, .NET 7, .NET 8, and .NET 9. The library receives regular updates aligned with Microsoft's .NET release cycle, ensuring that compatibility with .NET 10 — expected in late 2026 — will be available at or near release. ZXing.Net also maintains broad .NET compatibility through its NuGet package targets, and both libraries are suitable for modern .NET projects from this perspective.

Conclusion

ZXing.Net and IronBarcode represent different philosophies in barcode library design. ZXing.Net is a stateful, instance-based library that delegates threading responsibility, format specification, and PDF bridging entirely to the caller. IronBarcode is a stateless, static-API library that internalises threading, detects formats automatically, and handles PDF input natively. The practical consequence of this difference is that ZXing.Net code in concurrent or document-processing contexts tends to accumulate infrastructure — per-thread instance patterns, format lists, PDF rendering pipelines, and platform-specific binding branches — while IronBarcode code for the same scenarios requires none of that surrounding structure.

ZXing.Net is a genuinely strong choice for projects with zero-cost requirements. Its Apache 2.0 licence makes it compatible with open-source projects and with commercial products that prefer free dependencies. For controlled environments — known barcode formats, clean images, single-threaded or lightly concurrent processing — it performs reliably. Its active GitHub community provides timely responses to issues, and the breadth of its format coverage is comparable to commercial alternatives. For applications where those conditions hold, ZXing.Net's cost advantage is real and its technical limitations are manageable.

IronBarcode addresses the scenarios where ZXing.Net's design constraints become operational costs: concurrent web APIs where per-request instance allocation is measurable, document pipelines where PDF input is the norm rather than the exception, mixed-format scanning where silent misses carry business risk, and cross-platform deployments where two binding packages mean two code paths. For those contexts, the library's commercial licence buys a threading model that does not impose instance management, format detection that does not require maintenance, and PDF support that does not require a second library.

The decision depends on the project's requirements. A single-format prototype, an open-source scanner, or a low-volume tool where cost is the binding constraint — these are cases where ZXing.Net is the appropriate choice. A production web API processing documents from external sources across multiple threads — that is where IronBarcode's design assumptions align with the operational reality. For a broader look at how the libraries compare across detection rates and real-world scanning scenarios, the ZXing.Net vs IronBarcode barcode scanner comparison provides additional analysis.

Frequently Asked Questions

What is ZXing.Net?

ZXing.Net is a .NET barcode library for generating and reading barcodes in C# applications. It is one of several alternatives developers evaluate when selecting a barcode solution for .NET projects.

What are the main differences between ZXing.Net and IronBarcode?

IronBarcode uses a static, stateless API requiring no instance management, while ZXing.Net typically requires instance creation and configuration before use. IronBarcode also provides native PDF support, automatic format detection, and single-key licensing across all environments.

Is IronBarcode easier to license than ZXing.Net?

IronBarcode uses a single license key covering both development and production deployments. This simplifies CI/CD pipelines and Docker configurations compared to licensing systems that separate SDK keys from runtime keys.

Does IronBarcode support all barcode formats that ZXing.Net supports?

IronBarcode supports over 30 barcode symbologies including QR Code, Code 128, Code 39, DataMatrix, PDF417, Aztec, EAN-13, UPC-A, GS1, and many more. Format auto-detection means no explicit format enumeration is required.

Does IronBarcode support native PDF barcode reading?

Yes. IronBarcode reads barcodes directly from PDF files using BarcodeReader.Read("document.pdf") without requiring a separate PDF rendering library. Per-page results include page number, barcode format, value, and confidence score.

How does IronBarcode handle batch processing compared to ZXing.Net?

IronBarcode's static methods are stateless and naturally thread-safe, enabling direct use of Parallel.ForEach without per-thread instance management. There is no throughput ceiling at any pricing tier.

What .NET versions does IronBarcode support?

IronBarcode supports .NET Framework 4.6.2+, .NET Core 3.1, and .NET 5, 6, 7, 8, and 9 in a single NuGet package. Platform targets include Windows x64/x86, Linux x64, and macOS x64/ARM.

How do I install IronBarcode in a .NET project?

Install IronBarcode via NuGet: run 'Install-Package IronBarCode' in the Package Manager Console, or 'dotnet add package IronBarCode' in the CLI. No additional SDK installers or runtime files are required.

Can I evaluate IronBarcode before purchasing, unlike ZXing.Net?

Yes. IronBarcode's trial mode returns complete decoded barcode values — only generated output images receive a watermark. You can benchmark read accuracy on your own documents before committing to a purchase.

What is the pricing difference between ZXing.Net and IronBarcode?

IronBarcode starts at $749 for a perpetual single-developer license covering development and production. Pricing details and volume options are available on the IronBarcode licensing page. There is no separate runtime license requirement.

Is it straightforward to migrate from ZXing.Net to IronBarcode?

Migration from ZXing.Net to IronBarcode primarily involves replacing instance-based API calls with IronBarcode's static methods, removing licensing boilerplate, and updating result property names. Most migrations involve reducing code rather than adding it.

Does IronBarcode generate QR codes with logos?

Yes. QRCodeWriter.CreateQrCode().AddBrandLogo("logo.png") embeds a brand image in a QR code natively with configurable error correction. Colored QR codes are also supported via ChangeBarCodeColor().

Jordi Bardia
Software Engineer
Jordi is most proficient in Python, C# and C++, when he isn’t leveraging his skills at Iron Software; he’s game programming. Sharing responsibilities for product testing, product development and research, Jordi adds immense value to continual product improvement. The varied experience keeps him challenged and engaged, and he ...
Read More

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me