Simple QR Code Maker lets you export your QR codes as either a PNG or an SVG. Both formats look great, but they serve different purposes: PNG is universally supported and ready to drop into any document or webpage, while SVG is infinitely scalable, perfect for print work, signage, or anything that needs to be resized without losing crispness.
Adding a logo to the center of a QR code is a popular feature, and it introduces a genuinely interesting engineering challenge. The logo can be a raster image (PNG, JPEG, BMP) or a vector SVG, and the output can be PNG or SVG. That gives four combinations, and each one requires a different approach.
The Four Combinations
1. PNG Output + PNG Logo
This is the most straightforward path. ZXing.Net generates the QR code as a System.Drawing.Bitmap using its BitmapRenderer. The logo is already a bitmap, so it can be drawn directly onto the QR code canvas using System.Drawing.Graphics.
One subtlety: ZXing renders its output in Format32bppRgb (no alpha channel). If the user has chosen a transparent background color, the alpha information gets lost before the logo is composited. The app works around this by running the raw bitmap through a color-remap pass that restores the correct alpha values before any compositing happens.
2. PNG Output + SVG Logo
The output is still a PNG, but the logo is a vector SVG file. Before any compositing can happen, the SVG needs to become a bitmap. That job falls to Magick.NET.
The SVG content is passed to MagickImage with a transparent background and the target pixel dimensions, and Magick.NET rasterizes it to a high-quality PNG in memory. That PNG is loaded into a System.Drawing.Bitmap, and from that point on it is handled exactly like any other raster logo, overlaid onto the QR code bitmap using Graphics.DrawImage.
The app also keeps the rasterized bitmap around for the live preview in the UI, since WinUI 3 cannot render SVG content directly into an Image control.

3. SVG Output + SVG Logo
This is the cleanest combination: pure vector from start to finish. ZXing.Net’s BarcodeWriterSvg and SvgRenderer produce the QR code as an SVG string. The logo SVG is then inlined directly as a nested <svg> element inside the parent SVG document.
To do this correctly, the app strips the outer <svg> wrapper from the logo file, preserves its viewBox attribute, and re-emits it as a positioned child element with explicit x, y, width, and height attributes. The result is a single self-contained SVG file with no rasterization anywhere in the pipeline; every line and curve stays perfectly sharp at any size.
4. SVG Output + PNG Logo
This is the combination that requires the most creative use of the SVG format. The output needs to be an SVG, but the logo is a raster PNG, and SVG files are text. The solution is to encode the PNG as a base64 data URI and embed it using the SVG <image> element:
<image x="200" y="200" width="120" height="120" href="data:image/png;base64,iVBORw0KGgo..." />
Before encoding, the logo bitmap is resized to the exact display dimensions using System.Drawing.Graphics with high-quality bicubic interpolation. This keeps the base64 payload as small as possible; there is no point embedding a 1024×1024 pixel logo when it will be displayed at 120×120 pixels inside the QR code.
The SVG Mask Trick
In all of the SVG output paths, there is one more problem to solve: the QR code modules underneath the logo zone need to be hidden. In the PNG path, this is easy: just paint a filled rectangle over the bitmap before drawing the logo. In SVG, it is not that simple.
SVG shapes are composited using painter’s model: each element is drawn on top of the previous ones, but a <rect fill="transparent"> painted over a vector path does absolutely nothing; transparent shapes do not erase what is beneath them. Simply layering a transparent rectangle over the QR code paths would leave the modules fully visible through the logo.
The solution is an SVG mask. A <mask> element is injected into the SVG’s <defs> section. It is white everywhere except for a black rectangle in the logo zone. The entire QR code content is then wrapped in a <g mask="url(#logo-mask)"> group. In SVG masking, white means fully visible and black means fully hidden, so the QR modules inside the logo area disappear cleanly, leaving the logo composited over a blank background.

When Transparency Enters the Picture
Simple QR Code Maker lets users choose custom foreground and background colors, including fully or partially transparent ones. This is useful for overlaying a QR code on a colored background, a website banner, or a printed design. Transparency looks great, but it adds another layer of complexity to an already interesting rendering pipeline.
The ZXing Alpha Problem (PNG)
ZXing.Net’s BitmapRenderer always outputs a Format32bppRgb bitmap: no alpha channel. If you hand it a transparent color, the renderer uses a transparent brush; in GDI+ compositing that means paint nothing. The bitmap initializes to black, and foreground and background pixels become indistinguishable.
The app works around this by always passing fully opaque versions of the user’s colors to ZXing. If either color has an alpha value below 255, a post-processing pass in ApplyAlphaToQrBitmap restores the transparency in three steps:
- Clone the
Format32bppRgbbitmap toFormat32bppArgb. This step matters becauseFormat32bppRgbstores the alpha byte as 0 in memory even though all pixels are visually opaque. Cloning to ARGB forces every alpha byte to 255, which is required for the remap in step 2 to match correctly. - Use
ImageAttributes.SetRemapTablewith aColorMap[]to remap the fully-opaque foreground and background colors to the user’s alpha-aware versions. - Redraw via
Graphics.DrawImagewith those attributes applied onto a freshFormat32bppArgbcanvas.
The result is a bitmap that looks exactly as ZXing would have rendered it had it supported transparent colors natively.
The Transparent Punchout Problem (PNG + Logo)
There is one more place where transparency bites: the logo punchout. Before drawing the logo, the app fills a rectangle in the center of the QR bitmap to clear the modules underneath. With GDI+’s default SourceOver compositing mode, filling with a transparent brush is a no-op; it adds zero to what is already there, so the QR modules show through behind the logo.
The fix is to switch to CompositingMode.SourceCopy before filling the punchout rectangle. SourceCopy replaces destination pixels outright, including their alpha channel, so filling with a transparent color actually writes A=0 to those pixels. The mode is then reset to SourceOver so the logo composites normally on top of the cleared zone.
Transparency in SVG Output
For SVG output, transparency is almost free. SVG natively supports rgba() color syntax, so the user’s colors, alpha values included, are passed directly to SvgRenderer and emitted as rgba(r,g,b,a) in the markup. The logo punchout background rectangle uses rgba() as well, so a transparent background setting produces a transparent hole behind the logo in the SVG file. The mask approach used to hide QR modules is a structural operation and works correctly regardless of background color or transparency.
A Scannability Caveat
Transparent QR codes can look striking on a printed surface or a web page, but they come with an important caveat: the actual contrast a scanner sees depends on whatever is behind the code, and that changes with context. For that reason, when either color has any transparency, the app replaces its minimum printed-size recommendation with a note that the size is surface-dependent. There is no way to calculate a reliable contrast ratio when part of the apparent color comes from whatever is showing through underneath.
Libraries That Make It Work
- ZXing.Net: Encodes the QR code data and renders output via either
BitmapRenderer(for PNG) orSvgRenderer(for SVG). Also handles all barcode and QR code decoding in the app. - Magick.NET: Rasterizes SVG logos to
Bitmapfor PNG output and for the live UI preview. It handles the SVG-to-pixel conversion with full transparency support. - System.Drawing: The workhorse for all PNG compositing: drawing the QR bitmap, overlaying logos, applying alpha remapping, rendering frame decorations (bottom labels, rounded frames, corner callouts), and fitting text.
- Custom SVG string manipulation: For SVG output, the app constructs and modifies SVG markup directly as strings, using source-generated
Regexto parse the<svg>opening tag, extractviewBoxvalues, and locate the closing</svg>tag. No XML DOM library is used, keeping things lightweight and avoiding round-trip parsing issues with SVG-specific features.
Why Both Formats?
Most users just need a PNG they can paste into a document or upload to a website. But designers and print shops consistently ask for vector formats. A QR code printed on a banner or embroidered on a shirt needs to scale up to hundreds of times its original size without any degradation. SVG gives them that flexibility while keeping the file size small (no pixel data, just mathematical paths).
Supporting both formats well means supporting all four logo combinations well, and that is where the interesting engineering lives.
Thanks for reading
Joe

