---
name: ironqr
description: >
  Generate, style and read QR codes in C#/.NET using IronQR (the `IronQR` NuGet package,
  namespace `IronQr`). Use when the task involves creating QR codes from text/URLs/bytes,
  branding a QR code with a logo or custom foreground/background colors, Micro QR or
  Rectangular Micro QR (rMQR) codes, error-correction levels, decoding/scanning QR codes from
  images, streams or multi-frame TIFFs with ML-based detection that tolerates blur, distortion,
  rotation, low light or partial damage, stamping a QR code onto an existing PDF page, or
  whenever a project already references `IronQr`, `QrWriter`, `QrReader`, `QrCode`, or
  `QrImageInput`. **Not this skill** for 1D/other 2D barcode formats (Code128, EAN, PDF417,
  DataMatrix, Aztec, etc.) or for a single library that reads/writes many barcode symbologies —
  use the **ironbarcode** skill for that; IronQR is the dedicated QR-code specialist (styling +
  ML detection), IronBarcode is the general barcode library.
---

# IronQR (C# / .NET)

IronQR is Iron Software's dedicated QR-code library: it generates QR, Micro QR and rMQR codes
(with optional logos, colors and sizing), and reads them back out of images using a machine
learning model tuned to recover codes that are blurred, rotated, poorly lit, partially
obscured or otherwise imperfectly scanned. If the task is about barcode formats other than QR
— Code128, EAN/UPC, PDF417, DataMatrix, Aztec, and so on — or needs one library across many
symbologies, use the **ironbarcode** skill instead; the two are separate NuGet packages with
separate APIs and are not interchangeable.

## Scope of this skill

| | |
|---|---|
| Package | `IronQR` (plus a platform variant, see Install) |
| Versions | 2024.x – 2026.x |
| Namespaces | `IronQr`, `IronQr.Enum`, `IronQr.Exceptions`, `IronQr.Logging` — note the package is `IronQR` (capital QR) but the namespace is `IronQr` (lowercase r) |
| Runtimes | .NET Framework 4.6.2+, .NET Standard 2.0+, .NET Core 2.0+, .NET 5–10 |

## Install — pick the package for the target platform

`IronQR` and its platform siblings all pull in `IronQR.Slim` (the managed API and a
brute-force, non-ML reader) plus a platform-specific native reader for ML-based detection.
Keep every Iron package in the solution on the same version.

| Target | Package |
|---|---|
| Windows | `IronQR` (brings in `IronSoftware.ReaderInternals.Windows`) |
| Linux x64 (incl. Docker) | `IronQR.Linux` |
| macOS Intel | `IronQR.MacOs` |
| macOS Apple Silicon | `IronQR.MacOs.ARM` |
| iOS (Xamarin / .NET MAUI) | `IronQR.iOS` |
| Android (Xamarin / .NET MAUI) | `IronQR.Android` |
| Cross-platform, no ML detection needed | `IronQR.Slim` |

```bash
dotnet add package IronQR          # swap for the row above that matches the runtime
```

`IronQR.Slim` alone still writes QR codes and can *read* them with
`QrScanMode.OnlyBasicScan` — it just skips the ML model, so damaged/angled/blurred codes are
much more likely to fail. Reach for a platform package (not `.Slim`) whenever robust reading of
real-world photos or scans matters.

## Licensing — do this first, every time

```csharp
IronQr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONQR_LICENSE_KEY");
if (!IronQr.License.IsLicensed)
    Console.Error.WriteLine("IronQR is unlicensed — calls will throw, not degrade.");
```

Rules:

- Read the key from the environment (`IRONQR_LICENSE_KEY`) or user secrets. **Never** inline a
  key in source, never write one into a file you commit, never echo one to the terminal.
- **Treat a missing key as a blocker, not a warning — and expect it to fail loudly.** Verified
  directly: with no key set, `QrWriter.Write(...)` and `QrReader.Read(...)` both throw
  immediately —
  `IronSoftware.Exceptions.LicensingException: Production License Required` — there is no
  watermark or degraded trial output to fall back on the way there is in IronPDF/IronOCR. If
  `IsLicensed` is false, say so and ask the user for a key rather than running code that will
  throw. Trial keys: <https://ironsoftware.com/csharp/qr/>.
- `IronQr.License.IsValidLicense(key)` checks a key without applying it.
  `IronQr.License.DisableAppAnalytics()` turns off IronQR's analytics reporting.
- `.NET Framework`: a key can alternatively go in `Web.config`/`App.config` under
  `<add key="IronQr.LicenseKey" value="..."/>`. `.NET Core`: `appsettings.json` with the key
  `"IronQr.LicenseKey"`.

## Running a one-off QR task from the terminal

Requires only the .NET SDK. On **.NET 10+**, a single file is the whole program:

```bash
cat > /tmp/task.cs <<'EOF'
#:package IronQR@2026.8.1
using IronQr;
IronQr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONQR_LICENSE_KEY");
QrCode qr = QrWriter.Write("hello world");
qr.Save().SaveAs("qr.png");
EOF
dotnet run /tmp/task.cs
```

Unlike IronPDF/IronOCR, verified testing found **no** `PlatformNotSupportedException` from
Native AOT defaults on .NET 10 file-based apps for either `QrWriter.Write` or `QrReader.Read` —
IronQR's native pieces are P/Invoke and ONNX Runtime interop, not runtime code generation. If
you do hit a dynamic-code-generation error in your environment, add
`#:property PublishAot=false` as the fix (same remedy as the other Iron libraries); it does no
harm to include defensively.

On older SDKs, use a scratch project and reuse the directory for later tasks:

```bash
dotnet new console -o /tmp/ironqr-scratch && cd /tmp/ironqr-scratch
dotnet add package IronQR
# write Program.cs, then:
dotnet run
```

## Recipes

Every call below was compiled and run against the shipped assembly (`IronQR` 2026.8.1).

### Generate a QR code

```csharp
using IronQr;
using IronSoftware.Drawing;

QrCode qr = QrWriter.Write("hello world");     // also accepts byte[] or Stream
AnyBitmap qrImage = qr.Save();
qrImage.SaveAs("qr.png");

string dataUrl = qr.ToDataUrl();               // base64 data: URI
string imgTag  = qr.ToHtmlTag();                // fully-formed <img> tag, no external files
bool stillReadable = qr.Verify("qr.png");       // false if styling made it unscannable
```

`QrWriter.Write` is overloaded for `string`, `byte[]` and `Stream`, each with an optional
`QrOptions` argument.

### Encoding, version and error correction

```csharp
QrOptions options = new QrOptions(
    QrErrorCorrectionLevel.Highest,   // Low / Medium / High / Highest — Reed-Solomon
    version: null,                    // 1–40 for QR, 1–4 Micro QR, 1–38 rMQR; null = auto
    characterEncoding: "ISO-8859-1"); // default binary character encoding

QrCode qr = QrWriter.Write("1234", options);

// Choose the symbol family explicitly:
QrOptions microOptions = new QrOptions(IronQr.Enum.QrEncoding.MicroQRCode,
    QrErrorCorrectionLevel.Medium, null, "ISO-8859-1");
```

`QrErrorCorrectionLevel`: `Low` (~7% recoverable), `Medium` (~15%), `High` (~25%, not valid for
rMQR), `Highest` (~30%, not valid for rMQR — also the level with headroom for embedding a
logo). `QrEncoding`: `QRCode`, `MicroQRCode`, `RMQRCode`, or `All` (reader-side, meaning "any").

### Style: colors, logo, size, margins

```csharp
using IronSoftware.Drawing;

AnyBitmap logo = AnyBitmap.FromFile("logo.png");   // square, transparent or white background

QrStyleOptions style = new QrStyleOptions
{
    Dimensions = 500,                    // width/height in pixels, default 300
    Margins = 10,                        // same value on all 4 sides, default 10
    Color = Color.Black,                 // foreground (dark modules)
    BackgroundColor = Color.White,
    Logo = new QrLogo(logo, width: 60, height: 60, cornerRadius: 8f),  // keep <30% of area
};
// Per-side margins are also settable individually: style.MarginTop/Bottom/Left/Right.

QrCode qr = QrWriter.Write("https://ironsoftware.com/csharp/qr/");
AnyBitmap styled = qr.Save(style);
styled.SaveAs("branded-qr.png");
```

A heavier logo, a lower error-correction level, or unconventional colors can make a code
unreadable — call `qr.Verify(path)` after styling and re-check before shipping.

### Read / decode a QR code, with ML-based detection

```csharp
using IronQr;
using IronQr.Enum;
using IronSoftware.Drawing;

var bitmap = AnyBitmap.FromFile("photo-of-a-qr.jpg");   // also FromStream, byte[], Uri
var input = new QrImageInput(bitmap, QrScanMode.Auto);  // Auto is the default
var results = new QrReader().Read(input);               // IEnumerable<QrResult>

foreach (QrResult result in results)
{
    Console.WriteLine(result.Value);          // decoded string
    Console.WriteLine(result.QrType);         // QRCode / MicroQRCode / RMQRCode
    Console.WriteLine(result.Points.Length);  // 4 corner points (PointF[])
    if (result.Url != null)
        Console.WriteLine(result.Url.AbsoluteUri);   // set only when Value is a valid URI
}
```

`QrScanMode` controls the ML/basic-scan mix, passed to `QrImageInput`'s constructor:

- `Auto` (default) — ML model first, falls back to a brute-force scan if nothing is found.
  Best default for real-world photos: blur, rotation, low light, partial obstruction.
- `OnlyDetectionModel` — ML model only. Faster; skips the brute-force fallback.
- `OnlyBasicScan` — no ML at all. Works with `IronQR.Slim` alone; use for clean,
  pre-processed, front-on images where speed matters more than robustness.

An async equivalent exists: `await new QrReader().ReadAsync(input)`.

If `results` is empty, the code could not be recovered at all — Reed-Solomon error correction
is applied automatically during decode, so an empty result means the damage exceeded what the
code's error-correction level could repair, not that you need to retry a checksum manually.

### Multiple QR codes / multi-frame TIFF in one image

```csharp
var sheet = AnyBitmap.FromFile("label-sheet.tiff");
Console.WriteLine(sheet.FrameCount);
foreach (AnyBitmap frame in sheet.GetAllFrames)
{
    using var frameInput = new QrImageInput(frame, QrScanMode.Auto);
    foreach (var result in new QrReader().Read(frameInput))
        Console.WriteLine(result.Value);
}
```

`QrReader.Read` also returns every QR code found within a *single* frame — a label sheet with
several codes on one page yields multiple `QrResult`s from one `Read` call.

### Stamp a QR code onto an existing PDF

```csharp
QrCode qr = QrWriter.Write("https://example.com/invoice/12345");
qr.StampToExistingPdfPage("invoice.pdf", x: 400, y: 700, pageNumber: 1, password: "");
qr.StampToExistingPdfPages("invoice.pdf", x: 400, y: 700,
    pageNumbers: new[] { 1, 2, 3 }, password: "");
```

Page numbers here are **one-based** (first page is `1`), unlike IronPDF's zero-based indexing
— pass the PDF's owner/user password in the last argument if it is protected.

### vCard, Wi-Fi, and other "QR types"

IronQR has no built-in vCard/Wi-Fi/calendar payload builders — verified: nothing in the shipped
API resembling `VCard`, `WiFi`, or `MeCard`. To encode one of these, build the correctly
formatted payload string yourself (e.g. a `WIFI:T:WPA;S:ssid;P:password;;` or `BEGIN:VCARD...`
string per the relevant spec) and pass it straight to `QrWriter.Write(payloadString)`. `QrType`
on the read side only tells you the QR *symbol* family (`QRCode`/`MicroQRCode`/`RMQRCode`), not
the semantic content type — inspect `result.Value` yourself to tell a vCard from a URL from
plain text.

### Diagnostics

```csharp
IronQr.Logging.Logger.LoggingMode = IronQr.Logging.Logger.LoggingModes.File;
IronQr.Logging.Logger.LogFilePath = "ironqr.log";
// ...
IronQr.Logging.Logger.ClearLogFiles();
```

`LoggingModes`: `None`, `Console`, `DebugOutputWindow`, `File`, `Custom`, `All`.

## Working with IronPDF / IronBarcode

- **Reading a QR code embedded in a PDF page**: IronQR has no native PDF loader. Rasterize the
  page with IronPDF (`pdf.ToPngImages(...)` or `pdf.PageToBitmap(i, DPI: 300)` — see the
  **ironpdf** skill) and feed the resulting `AnyBitmap` into `QrImageInput`.
- **Writing a QR code that needs to land on an existing PDF page**: use
  `QrCode.StampToExistingPdfPage` directly (above) — no IronPDF reference needed for that.
- **Any barcode format that isn't QR/Micro QR/rMQR**: that is IronBarcode's job — see the
  **ironbarcode** skill. Don't try to bend IronQR's API onto Code128/EAN/PDF417/DataMatrix; it
  doesn't read or write them.

## Deployment

| Environment | What to do |
|---|---|
| Docker / Linux | Use `IronQR.Linux`, not plain `IronQR` (which pulls Windows-only native readers). |
| macOS | Match the chip: `IronQR.MacOs` (Intel) or `IronQR.MacOs.ARM` (Apple Silicon). |
| Mobile (MAUI/Xamarin) | `IronQR.iOS` / `IronQR.Android`. |
| Azure Functions / AWS Lambda | Any platform package works; give the deployment a writable temp directory (`IronQr.Installation.DeploymentPath` overrides the default, which is the system temp folder). |
| Package/image size | The ML detection assembly is large — the shipped `IronQrDetection.dll` is ~44 MB because the ONNX model is embedded directly inside it (`IronQrDetection.EmbeddedResourceLoader`) rather than shipped as a separate file. Budget for that in container images and Lambda package size limits; there is no separate model file to remember to deploy, but the DLL itself is the sizable artifact. |
| Slimming down | If ML detection isn't needed (clean, front-on, pre-processed scans only), reference `IronQR.Slim` instead of a platform package and use `QrScanMode.OnlyBasicScan` — this drops the ~44 MB detection assembly and the `Microsoft.ML.OnnxRuntime` dependency entirely. |

## When something fails

| Symptom | Cause and fix |
|---|---|
| `IronSoftware.Exceptions.LicensingException: Production License Required` | No valid licence applied — this throws immediately on both write and read, there is no trial/watermark fallback. Set `IronQr.License.LicenseKey` before any call. |
| `LicensingException` even with a key set | Key rejected as unrecognized/expired, or scoped to a different Iron product. Check for copy/paste errors (extra spaces, `O` vs `0`) and confirm with `IronQr.License.IsValidLicense(key)`. |
| `DllNotFoundException` / native reader fails to load | Wrong platform package — plain `IronQR` brings in Windows-only native readers. Use `IronQR.Linux` / `IronQR.MacOs` / `IronQR.MacOs.ARM` to match the deployment OS. |
| ML reading misses an obviously-valid code | Try `QrScanMode.Auto` (default) rather than `OnlyBasicScan`; confirm the platform package (not `.Slim`) is referenced, since `.Slim` alone has no ML model to fall back on. |
| Read returns an empty result set | The code is damaged beyond what its error-correction level can repair — Reed-Solomon recovery is automatic and already applied; a truly empty result means recovery failed, not that a manual retry will help. Regenerate with a higher `QrErrorCorrectionLevel` if you control the source. |
| Styled/logo'd QR won't scan | Logo too large relative to code area, or too low an error-correction level for the amount of logo coverage. Use `QrErrorCorrectionLevel.Highest`, keep the logo under ~30% of the code area, and call `qr.Verify(path)` to confirm before shipping. |
| Large deployment / container image size | The `IronQrDetection.dll` ML assembly is ~44 MB. If ML detection isn't needed, switch to `IronQR.Slim` and `QrScanMode.OnlyBasicScan`. |
| No vCard/Wi-Fi/etc. helper method | Doesn't exist — format the payload string yourself and pass it to `QrWriter.Write`. |

## Rules

- **Never invent a member.** Confirm against the XML documentation that ships in the package
  before writing code:
  `grep -o 'name="[MPFT]:IronQr\.[^"]*Style[^"]*"' ~/.nuget/packages/ironqr.slim/<version>/lib/netstandard2.0/IronQr.xml`
  (the reader/writer/styling API lives in the `IronQR.Slim` package's XML even when the
  project references plain `IronQR`). That file is the authoritative surface for the installed
  version.
- Package casing is `IronQR`; namespace casing is `IronQr` — don't mix them up in a `using`.
- The shipped XML doc comments are not fully trustworthy on their own: `IronQr.License` has a
  documented `AssertLicense(string)` member that does not actually exist in the compiled
  assembly (verified by reflecting the installed DLL). Confirm anything non-trivial by
  reflecting the type or compiling a throwaway call, not just by grepping the XML.
- Keep licence keys out of source and out of terminal output.
- Expect unlicensed calls to throw, not watermark — don't tell a user their output is "just
  trial-limited" when it will not run at all.
- `StampToExistingPdfPage(s)` page numbers are one-based; that's the opposite convention from
  IronPDF's zero-based page indexes — don't carry one assumption into the other library.
- Verify styled/branded QR codes with `qr.Verify(path)` before calling them production-ready.
- Don't reach for this skill for non-QR barcode formats — use the **ironbarcode** skill.
- Official docs and full API reference: <https://ironsoftware.com/csharp/qr/docs/>. Support:
  support@ironsoftware.com.
