Images

edit

Meziantou.Framework.Imaging.Barcodes renders codes with Meziantou.Framework.Imaging: save them in any format the library writes, draw them on existing images, and paint them with gradients or patterns.

dotnet package add Meziantou.Framework.Imaging.Barcodes

The types are in the Meziantou.Framework.Imaging.Barcodes namespace. The examples also use the Meziantou.Framework.Barcodes, Meziantou.Framework.Imaging, Meziantou.Framework.Imaging.Drawing and Meziantou.Framework.Imaging.Formats namespaces.

Render a code as an image

ToImage renders a QR code or a barcode on a new Image<Rgba32> of its size, quiet zone included:

var qrCode = QRCode.Create("https://www.meziantou.net", ErrorCorrectionLevel.M);

// Default options: 10 pixels per module, a 4-module quiet zone, black on white
using var image = qrCode.ToImage();
Assert.Equal((330, 330), (image.Width, image.Height));

// Save to a file (image.Save("qrcode.webp", new WebPEncoder())) or to a stream
using var webp = new MemoryStream();
image.Save(webp, new WebPEncoder());

// Custom options, the same as the canvas drawing takes: any brush, and no background
using var customImage = qrCode.ToImage(new QRCodeDrawingOptions
{
    ModuleSize = 4,
    QuietZoneModules = 2,
    DarkBrush = new SolidBrush(new Rgba32(0x00, 0x33, 0x66)),
    LightBrush = null,
});
Assert.Equal((116, 116), (customImage.Width, customImage.Height));
Assert.Equal(Colors.Transparent, customImage.Frames[0][0, 0]);
Assert.Equal(new Rgba32(0x00, 0x33, 0x66), customImage.Frames[0][8, 8]);

// An indexed PNG stores the two colors in a palette, as ToPng does
using var png = new MemoryStream();
customImage.Save(png, new PngEncoder { PixelLayout = PngPixelLayout.Indexed });
var barcode = Barcode.CreateEan13("5901234123457");
using var image = barcode.ToImage(new BarcodeDrawingOptions { ModuleWidth = 3, ModuleHeight = 100 });
Assert.Equal((345, 100), (image.Width, image.Height));

Save the image like any other: see Saving images.

Paint with any brush

The modules are painted with brushes, so a code can have a gradient, a pattern or an image. The brushes are in the coordinates of the image:

var qrCode = QRCode.Create("https://www.meziantou.net", ErrorCorrectionLevel.Q);

// The brushes are in the coordinates of the image: the gradient goes across the whole symbol
var options = new QRCodeDrawingOptions
{
    ModuleSize = 5,
    QuietZoneModules = 2,
    DarkBrush = new LinearGradientBrush(new PointD(0, 0), new PointD(165, 165), [new GradientStop(0, new Rgba32(0x7c, 0x3a, 0xed)), new GradientStop(1, new Rgba32(0x0e, 0x74, 0x90))]),
};

using var image = qrCode.ToImage(options);

A QR code whose modules go from purple at the top left to teal at the bottom right

Keep the modules dark and the background light everywhere: a gradient that gets too light loses the contrast scanners need. See Brushes and pens for the brushes.

Draw on an existing image

DrawQRCode and DrawBarcode draw on the canvas of Image.Draw, as vector shapes: the transform, the clip and the layers of the canvas apply. MeasureQRCode and MeasureBarcode give the size of the drawing first, to place it:

using var ticket = new Image<Rgba32>(400, 200, Colors.White); // or Image.Load<Rgba32>("ticket.png")
var qrCode = QRCode.Create("TICKET-0042");
var options = new QRCodeDrawingOptions { ModuleSize = 4, QuietZoneModules = 2 };

// The size of the drawing, quiet zone included, to lay out the page before drawing
var size = QRCodeImageRenderer.MeasureQRCode(qrCode, options);
Assert.Equal((100, 100), (size.Width, size.Height)); // (21 modules + 2 x 2) x 4 pixels

// Drawn as a vector shape: the transform and the clip of the canvas apply, any brush works
ticket.Draw(canvas => canvas.DrawQRCode(qrCode, new PointD(400 - 16 - size.Width, 16), options));
Assert.Equal(Colors.Black, ticket.Frames[0][284 + 8, 16 + 8]); // the top-left finder pattern, after the 8-pixel quiet zone

// Save to a file (ticket.Save("ticket-with-qrcode.png")) or to a stream
using var png = new MemoryStream();
ticket.Save(png, new PngEncoder());

A white ticket with a QR code in its top-right corner

With SnapToPixels (the default), the module sizes and the position are rounded to whole pixels, so that every module has the same size and sharp edges. Turn it off to draw a code at an exact fractional size, for instance under a scale transform.

DrawBarcode prints the human-readable text of the barcode (Barcode.Text, with its check digit) under the bars when BarcodeDrawingOptions.Font is set:

var barcode = Barcode.CreateEan13("590123412345");
var options = new BarcodeDrawingOptions
{
    ModuleWidth = 2,
    ModuleHeight = 60,
    QuietZoneModules = 9,
    DarkBrush = new SolidBrush(new Rgba32(0x00, 0x33, 0x66)),
    Font = BarcodeFonts.OcrB, // prints barcode.Text, "5901234123457", under the bars
    FontSize = 24,
};

var size = BarcodeImageRenderer.MeasureBarcode(barcode, options);
Assert.Equal(226, size.Width); // (95 modules + 2 x 9) x 2 pixels

using var label = new Image<Rgba32>(300, 140, Colors.White);
label.Draw(canvas => canvas.DrawBarcode(barcode, new PointD(20, 20), options));
Assert.Equal(new Rgba32(0x00, 0x33, 0x66), label.Frames[0][20 + 18, 50]); // the first guard bar

A dark blue EAN-13 barcode with its digits printed under the bars

BarcodeFonts has the two fonts of optical character recognition usually printed under barcodes: OcrB under EAN and UPC barcodes, and OcrA. They are Fonts of Meziantou.Framework.Imaging, so you can also draw any text with them:

using var image = new Image<Rgba32>(280, 72, Colors.White);
image.Draw(canvas =>
{
    var ink = new SolidBrush(Colors.Black);
    canvas.FillText(ink, "OCR-A 0123456789", BarcodeFonts.OcrA, size: 24, new PointD(8, 6));
    canvas.FillText(ink, "OCR-B 0123456789", BarcodeFonts.OcrB, size: 24, new PointD(8, 38));
});

The same text written with the OCR-A font, then with the OCR-B font

  • Both fonts are generated from the character shapes of their standards (ANSI X3.17-1981 for OCR-A, ANSI X3.49-1975 for OCR-B), are monospaced, map the printable ASCII characters, and are licensed with the package.
  • OCR-A also has the Hook, the Chair and the Fork (U+2440 to U+2442), which stand for the tilde, the grave accent and the underline. OCR-B also has the letters of its extended repertoire (Ä, Å, Æ, Ö, Ø, Ü, Ñ, IJ, å, æ, ø, ij, ß) and §, ¤, £, ¥, ¨ and ´.
  • Any other font works too: see Fonts.

Options

ToImage and the canvas drawing take the same options. The sizes are in units of the canvas: pixels, unless a transform applies.

QRCodeDrawingOptionsDefaultDescription
ModuleSize10The size of a module
QuietZoneModules4The width of the margin around the code, in modules
DarkBrushBlackThe brush of the dark modules
LightBrushWhiteThe brush of the light modules and of the margin; null keeps the background (transparent for ToImage)
SnapToPixelstrueRound the module size and the position to whole units
BarcodeDrawingOptionsDefaultDescription
ModuleWidth2The width of a module
ModuleHeight80The height of the bars
QuietZoneModules10The width of the margins on the left and on the right, in modules
DarkBrushBlackThe brush of the bars
LightBrushWhiteThe brush of the spaces, of the margins and of the background of the text; null keeps the background
SnapToPixelstrueRound the module sizes and the position to whole units
FontnullThe font of the text under the bars; null draws no text
FontSize14The size of the text
TextBrushnullThe brush of the text; null uses DarkBrush
TextSpacing2The space between the bars and the text