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
BarcodeReaderinstance is not thread-safe; a new instance must be created per thread or per parallel operation. - Manual Format Specification: Callers must set
reader.Options.PossibleFormatsbefore 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.WindowsusingSystem.Drawing,ZXing.Net.Bindings.ImageSharpusing 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)
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.ReadandBarcodeWriter.CreateBarcodeare 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.Readaccepts 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.CreateBarcodereturns aGeneratedBarcodeobject with built-in output methods includingSaveAsPng,SaveAsPdf,ToPngBinaryData, andToStream. - 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)
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)
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
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
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
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
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
// 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
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
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")
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/awaitpipelines without blocking. - ML-Powered Image Correction: Automatic image enhancement for damaged, blurred, or low-contrast barcodes that
TryHardercannot 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().

