A built-in toSVG for the QR Code generator
Library: QR Code generator (JavaScript)
Add a small SVG output helper — toSVG — directly to the qrcodegen namespace, so a QrCode can be rendered in one line in the browser without copying boilerplate out of the demo files. It merges horizontally-adjacent dark modules into a single rectangle, which produces a meaningfully smaller SVG path.
One file, ready to drop in: qradapter.js
Motivation
The library encodes beautifully, but the SVG rendering lives only in the worked-example / demo files, so every project re-copies an SVG helper.
Promoting it into the library namespace makes the common case a one-liner, and it's a natural place to apply one small optimization.
Run-length merging. The current demo helper emits one path command per dark module (M{x},{y}h1v1h-1z). Merging each horizontal run of dark modules into a single rectangle keeps the output pixel-identical while roughly halving the path:
| Payload |
QR size |
Dark modules |
Merged runs |
Reduction |
https://nayuki.io |
25×25 |
312 |
165 |
~48% fewer path ops |
| 300-char string |
69×69 |
2436 |
1237 |
~50% fewer path ops |
That is roughly half the path-data bytes, for identical output.
/*
* QR Code generator - SVG output adapter (JavaScript)
*
* A small companion to Nayuki's QR Code generator library that adds a
* convenience SVG renderer directly to the "qrcodegen" namespace object. Load
* this file after qrcodegen.js; it extends the same global "qrcodegen", so you
* can simply call qrcodegen.toSVG(...) in the browser.
*
* Horizontally-adjacent dark modules are merged into a single rectangle, which
* shrinks the SVG path data. The output is identical to drawing every module,
* only smaller.
*
* Example:
* var qr = qrcodegen.QrCode.encodeText("Hello, world!", qrcodegen.QrCode.Ecc.MEDIUM);
* document.querySelector("div").innerHTML = qrcodegen.toSVG(qr, 4);
*
* Offered as a feature-request contribution, under the same MIT License as the
* library it extends. https://www.nayuki.io/page/qr-code-generator-library
*/
"use strict";
var qrcodegen;
(function (qrcodegen) {
// Returns a string of SVG code for an image depicting the given QR Code, with the
// given number of border modules on all sides. The result always uses Unix newlines
// ("\n"), regardless of platform. Horizontally-adjacent dark modules are merged into
// one rectangle, so the path stays compact even at large versions.
function toSVG(qr, border, lightColor, darkColor) {
if (lightColor === undefined) lightColor = "#FFFFFF";
if (darkColor === undefined) darkColor = "#000000";
if (border < 0)
throw new RangeError("Border must be non-negative");
var dim = qr.size + border * 2;
var parts = [];
for (var y = 0; y < qr.size; y++) {
for (var x = 0; x < qr.size;) {
if (!qr.getModule(x, y)) {
x++;
continue;
}
var run = 1; // Length of the dark run starting at (x, y)
while (x + run < qr.size && qr.getModule(x + run, y))
run++;
parts.push("M" + (x + border) + "," + (y + border) + "h" + run + "v1h-" + run + "z");
x += run;
}
}
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 " + dim + " " + dim + "\" stroke=\"none\">\n" +
"\t<rect width=\"100%\" height=\"100%\" fill=\"" + lightColor + "\"/>\n" +
"\t<path d=\"" + parts.join(" ") + "\" fill=\"" + darkColor + "\"/>\n" +
"</svg>\n";
}
qrcodegen.toSVG = toSVG;
})(qrcodegen || (qrcodegen = {}));
API
The function merges into the existing qrcodegen namespace:
// Returns SVG code for the QR Code, with `border` light modules on every side.
qrcodegen.toSVG(qr, border, lightColor = "#FFFFFF", darkColor = "#000000") -> string
Usage
<script src="qrcodegen.js"></script>
<script src="qradapter.js"></script> <!-- extends the same global qrcodegen -->
<script>
var qr = qrcodegen.QrCode.encodeText("Hello, world!", qrcodegen.QrCode.Ecc.MEDIUM);
document.querySelector("div").innerHTML = qrcodegen.toSVG(qr, 4);
</script>
Correctness
The helper was checked by reconstructing the module grid from its output and comparing it, cell for cell, against QrCode.getModule — the SVG path parsed back to dark cells — across QR sizes 21×21 to 69×69 and numeric, alphanumeric and byte/UTF-8 payloads. Every case matched exactly.
Notes
- Style follows the library: same namespace, per-function comment, and the
<?xml … ?> + DOCTYPE SVG preamble the existing helper uses.
- Offered under the same MIT License as the library. The header credits the library but leaves authorship of this addition open — set the copyright line however you prefer for contributions.
A built-in
toSVGfor the QR Code generatorLibrary: QR Code generator (JavaScript)
Add a small SVG output helper —
toSVG— directly to theqrcodegennamespace, so aQrCodecan be rendered in one line in the browser without copying boilerplate out of the demo files. It merges horizontally-adjacent dark modules into a single rectangle, which produces a meaningfully smaller SVG path.One file, ready to drop in: qradapter.js
Motivation
The library encodes beautifully, but the SVG rendering lives only in the worked-example / demo files, so every project re-copies an SVG helper.
Promoting it into the library namespace makes the common case a one-liner, and it's a natural place to apply one small optimization.
Run-length merging. The current demo helper emits one path command per dark module (
M{x},{y}h1v1h-1z). Merging each horizontal run of dark modules into a single rectangle keeps the output pixel-identical while roughly halving the path:https://nayuki.ioThat is roughly half the path-data bytes, for identical output.
Code - qradapter.js
API
The function merges into the existing
qrcodegennamespace:Usage
Correctness
The helper was checked by reconstructing the module grid from its output and comparing it, cell for cell, against
QrCode.getModule— the SVG path parsed back to dark cells — across QR sizes 21×21 to 69×69 and numeric, alphanumeric and byte/UTF-8 payloads. Every case matched exactly.Notes
<?xml … ?>+ DOCTYPE SVG preamble the existing helper uses.