# Kanject.Core.Qr

A first-party QR Code (ISO/IEC 18004) encoder with styled SVG and PNG rendering — all 40 versions, multi-block Reed-Solomon, penalty-based masking. BCL-only and AOT-clean: no System.Drawing, no third-party packages, no native binaries in your Lambda bundle.

## Install

```bash
dotnet add package Kanject.Core.Qr

# Optional: compile-time [QrTemplate] / [QrSource] projections
dotnet add package Kanject.Core.Qr.Annotations
```

## Register

```csharp
using Kanject.Core.Qr.Extensions;

// Registers IQrEncoder, IQrSvgRenderer, IQrPngRenderer, and
// IQrScannabilityVerifier as singletons — via TryAdd, so registering
// your own implementation first overrides any of them.
builder.Services.AddQrCodes();
```

## Encode and render

`Encode` auto-selects the densest mode the payload allows (numeric → alphanumeric → byte) and the smallest version that fits it. Styling — colours, gradients, rounded or dot modules, finder-eye styling, a centre logo — goes through `QrRenderOptions`, shared by both renderers:

```csharp
public class TicketService(IQrEncoder encoder, IQrPngRenderer png)
{
    public byte[] RenderTicketQr(Ticket ticket)
    {
        var matrix = encoder.Encode($"https://acme.io/t/{ticket.Code}",
            new QrEncodeOptions { ErrorCorrection = QrErrorCorrection.H });

        return png.Render(matrix, new QrRenderOptions
        {
            Logo = new QrLogo { DataUri = acmeLogoPng },
            VerifyBeforeReturn = true,   // refuse to emit a dead ticket
        });
    }
}
```

## The output, live

This is not an illustration. The QR below is the actual SVG that `QrSvgRenderer` produced for this exact call — checked into the docs verbatim (regenerate it with `scripts/gen-core-qr-figure`):

```csharp
var matrix = new QrEncoder().Encode(
    "https://www.kanject.com/docs/core-qr/",
    new QrEncodeOptions { ErrorCorrection = QrErrorCorrection.H });

var svg = new QrSvgRenderer().Render(matrix, new QrRenderOptions
{
    ModuleShape = QrModuleShape.Rounded,
    DarkColor = "#221635",
    GradientColor = "#6d28d9",
    EyeStyle = new QrFinderEyeStyle { Color = "#6d28d9", Rounded = true },
    VerifyBeforeReturn = true,
});
```

_Figure on the web page — Version 5, 37×37 modules, error correction H — approved by the scannability verifier. Scan it: you'll land right back on this page. https://www.kanject.com/docs/core-qr/_

## Scannability, verified

Styling is where QR codes quietly die — a logo that eats too many codewords, grey-on-grey brand colours, an overlay across a finder pattern. The built-in verifier checks contrast, per-block logo error budgets, and reserved-pattern occlusion as pure arithmetic on the matrix. Set `VerifyBeforeReturn = true` (or use `QrRenderOptions.VerifiedDefault`) and the renderer throws a coded `QrUnscannableException` instead of returning a symbol that won't scan.

## Compile-time payloads

With `Kanject.Core.Qr.Annotations`, mark a `partial` POCO with `[QrTemplate]` — each `{PropertyName}` placeholder binds to a public property, URL-escaped — or put `[QrSource]` on a single string property to use its value verbatim. The generator emits the payload and encode/render helpers; misuse is a compile-time `KANQR` diagnostic, not a runtime surprise:

```csharp
[QrTemplate("https://acme.io/t/{Code}", Ecc = QrErrorCorrection.H)]
public sealed partial class TicketQr
{
    public required string Code { get; init; }
}

// Generated: ToQrPayload(), ToQrMatrix(…), ToQrSvg(…), ToQrPng(…)
var svg = new TicketQr { Code = "1042" }.ToQrSvg(encoder, svgRenderer);
```

## What ships with it

- Full ISO/IEC 18004 encoder — numeric / alphanumeric / byte modes, versions 1–40, error-correction levels L / M / Q / H
- `IQrSvgRenderer` (SVG string) and `IQrPngRenderer` (PNG bytes) with one shared `QrRenderOptions`
- Logo overlays with per-Reed-Solomon-block error budgeting, gradients, module shapes, finder-eye styling
- `IQrScannabilityVerifier` — contrast, logo budget, and reserved-pattern checks, no camera loop required
- `[QrTemplate]` / `[QrSource]` source generator — reflection-free, AOT-clean payload projections
- `KANQR###` diagnostic codes on every refusal: 001–049 compile-time, 050–099 runtime

---
_Source: https://www.kanject.com/docs/core-qr/ · Kanject Docs_
