A ZXing.Net barcode reader can compile cleanly on a developer's Windows machine and then throw the first time it runs in a Linux container, with nothing in the build to warn you. The cause is packaging rather than your code, because on modern .NET the base ZXing.Net package cannot read an image at all, so a separate binding does the actual decoding, and the binding most tutorials reach for wraps System.Drawing.Common, which Microsoft restricted to Windows from .NET 6.
IronBarcode reads from one package that behaves the same on Windows, Linux, and macOS, with no binding to choose, which is the difference this piece is about.
Full disclosure. We build IronBarcode at Iron Software, and this read looks at where ZXing.Net's binding and preprocessing gaps cost a team and where IronBarcode covers them from one package.
ZXing.Net and IronBarcode at a Glance
Here is where the two differ before the detail, because the packaging row at the top is the one that decides your platform.
| Aspect | ZXing.Net | IronBarcode |
|---|---|---|
| Reads from the base package alone | No, a binding package is required | Yes, from one package |
| Same code across Windows, Linux, macOS | No, the binding decides the platform | Yes |
| The common System.Drawing binding | Windows-only from .NET 6 | No binding to pick |
| Read a barcode from a PDF | Not supported | Supported, BarcodeReader.ReadPdf
|
| Imperfect-scan handling | Minimal, you supply clean images | Built in, deskew, denoise, contrast |
| Read and write | Yes | Yes, BarcodeReader.Read and BarcodeWriter.CreateBarcode
|
Table 1. ZXing.Net and IronBarcode across the packaging, platform, and reading questions, from each project's own repository and documentation.
Which Binding Decides Your Platform?
The base package pushes the real work into a ZXing.Net.Bindings.* package, and which one you pick sets which operating systems your code runs on. The sample most projects start from looks harmless.
using ZXing;
using ZXing.Windows.Compatibility; // the binding most tutorials reach for
var reader = new BarcodeReader();
using var bitmap = (Bitmap)Image.FromFile("scanned-label.png");
var result = reader.Decode(bitmap);
// Builds and runs on Windows. Throws in a Linux container.
That builds and runs locally and then throws in a Linux container, because the binding it imports is Windows-only. The choice underneath it is wider than one import.
- You pick an imaging stack before you read a code: ImageSharp, SkiaSharp, or Windows.Compatibility, each with its own namespace and API, and every tutorial assumes a different one.
-
The obvious binding is Windows-only:
ZXing.Net.Bindings.Windows.CompatibilitywrapsSystem.Drawing.Common, restricted to Windows from .NET 6, so it builds locally and fails at runtime in a Linux container. - Getting out is a rewrite, not a config change: moving to ImageSharp or SkiaSharp means rewriting the image-handling layer rather than flipping a setting.
What Does the Reader Still Leave You?
Settle the binding and the reader still hands back most of the hard part, because ZXing.Net decodes what you give it and does little to rescue a poor image. Deskewing, denoising, and contrast correction for a warehouse photo or a damaged label is code you write against a separate imaging library and tune by trial and error. Reading a barcode out of a PDF is not supported either, so you rasterise the pages with another library first and feed the images in. The upstream Java project the port tracks is in maintenance mode, so that list of things you assemble is unlikely to shorten, even as the port itself keeps shipping, 0.16.11 as of October 2025. IronBarcode corrects skew, blur, and low contrast inside the library, and reads barcodes straight out of a PDF without a separate rasteriser.
One Package, the Same on Every Platform
IronBarcode reads, corrects, and writes from a single package, so there is no binding decision and no imaging stack to assemble.
using IronBarCode;
// One package, the same behaviour on Windows, Linux, and macOS
var scanned = BarcodeReader.Read("warehouse-scan.jpg");
Console.WriteLine(scanned.First().Value);
// Straight out of a PDF, no rasteriser to write yourself
var fromPdf = BarcodeReader.ReadPdf("shipping-manifest.pdf");
// Generate from the same package
var label = BarcodeWriter.CreateBarcode("ABC-12345", BarcodeEncoding.Code128);
label.SaveAsPng("label.png");
That reads a scan, pulls a code straight out of a PDF, and writes a new barcode, all from one package that behaves the same on Windows, Linux, and macOS. You can pull IronBarcode from NuGet and run this in a couple of minutes.
Where IronBarcode Reads Everywhere
The one case for ZXing.Net is generating and reading clean images on a single, fixed platform, where the binding is chosen once. In modern .NET that is the exception rather than the rule, since most deployments now cross Linux and containers and a real scan is more often a phone photo at an angle than a clean render.
On that far more common side, the choice is between assembling an imaging layer yourself or covering it with IronBarcode from one package, and the barcode reading guide walks the read path end to end.
IronBarcode has a free trial if you want to run your own scans through it before deciding.
Which input are you dealing with, clean generated codes on a fixed platform, or a photo someone took in a warehouse? Tell us in the comments, that second one has better answers than most people expect.
ZXing.Net is an open-source project distributed under Apache-2.0, and is not affiliated with Iron Software. The details above are drawn from the project's own repository, README, and NuGet listings. If something has changed since, correct us in the comments.
Top comments (0)