QRコードを生成するには
文字列や URL から QR コードの PNG 画像を作る書き方を、各言語の定番ライブラリで示す。
生成した画像を別のデコーダで読み戻して確かめた結果と、誤り訂正レベル・余白・データの長さによるつまずきもまとめる。
公開:
選んだ言語は、ほかのサンプルコードを開いたときも最初に表示する。
Python
# pip install "qrcode[pil]"
import qrcode
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=10, border=4)
qr.add_data("https://example.com/hello")
qr.make(fit=True)
qr.make_image().save("qr.png")
print(qr.version) # 2qrcode.QRCode に誤り訂正レベル・1 マスの大きさ(box_size)・余白(border)を渡し、make(fit=True) でデータに合う版を自動で選ばせる。
PNG の保存には Pillow を使うので、qrcode[pil] の形で一緒に入れる。
JavaScript
// npm install qrcode
import QRCode from "qrcode";
const url = "https://example.com/hello";
await QRCode.toFile("qr.png", url, { errorCorrectionLevel: "M", margin: 4, scale: 10 });
console.log(QRCode.create(url, { errorCorrectionLevel: "M" }).version); // 2QRCode.toFile に出力先とデータを渡すと、.png の拡張子で PNG として保存される。scale は 1 モジュールのピクセル数、margin は余白のモジュール数で、画像の幅は(版の幅 + 余白 × 2)× scale になる。
TypeScript
// npm install qrcode && npm install -D @types/qrcode
import QRCode from "qrcode";
const url: string = "https://example.com/hello";
const options: QRCode.QRCodeToFileOptions = { errorCorrectionLevel: "M", margin: 4, scale: 10 };
await QRCode.toFile("qr.png", url, options);
console.log(QRCode.create(url, { errorCorrectionLevel: "M" }).version); // 2qrcode 本体には型定義が含まれず、@types/qrcode を別に入れる。
オプションに QRCode.QRCodeToFileOptions の型を付けておくと、errorCorrectionLevel に "X" のような存在しない値を渡したときに実行前に気づける。
Go
// go get github.com/skip2/go-qrcode
package main
import (
"fmt"
"github.com/skip2/go-qrcode"
)
func main() {
q, err := qrcode.New("https://example.com/hello", qrcode.Medium)
if err != nil {
panic(err)
}
if err := q.WriteFile(256, "qr.png"); err != nil {
panic(err)
}
fmt.Println(q.VersionNumber) // 2
}qrcode.New にデータと誤り訂正レベル(qrcode.Medium)を渡し、WriteFile で一辺 256 ピクセルの PNG を書く。
余白は既定で付き、選ばれた版は VersionNumber で読める。
Rust
// Cargo.toml: qrcode = "0.14", image = "0.25"
use image::Luma;
use qrcode::{EcLevel, QrCode};
fn main() {
let code = QrCode::with_error_correction_level("https://example.com/hello", EcLevel::M).unwrap();
let img = code.render::<Luma<u8>>().min_dimensions(256, 256).build();
img.save("qr.png").unwrap();
println!("{:?}", code.version()); // Normal(2)
}標準ライブラリに QR コードの生成が無いので、qrcode と image の 2 つのクレートを使う。render::<Luma<u8>>() で白黒の画像にし、min_dimensions で下限の大きさを指定すると、1 モジュールが整数ピクセルになる大きさへ切り上がる。
Java
// ZXing の core と javase(3.5.4)をクラスパスに置く
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.nio.file.Path;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
// 何も指定しないと誤り訂正レベルは L になる。他の言語に合わせて M を指定する
Map<EncodeHintType, Object> hints = Map.of(
EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M,
EncodeHintType.MARGIN, 4);
BitMatrix matrix = new QRCodeWriter()
.encode("https://example.com/hello", BarcodeFormat.QR_CODE, 256, 256, hints);
MatrixToImageWriter.writeToPath(matrix, "PNG", Path.of("qr.png"));
}
}ZXing では QRCodeWriter で BitMatrix を作り、MatrixToImageWriter で PNG に書き出す。
誤り訂正レベルの既定は L なので、ほかの言語と条件をそろえるなら EncodeHintType.ERROR_CORRECTION で明示する。
C#
// dotnet add package QRCoder
using System.IO;
using QRCoder;
using var generator = new QRCodeGenerator();
using var data = generator.CreateQrCode("https://example.com/hello", QRCodeGenerator.ECCLevel.M);
byte[] png = new PngByteQRCode(data).GetGraphic(10); // 1 モジュールを 10 ピクセルで描く
File.WriteAllBytes("qr.png", png);QRCodeGenerator.CreateQrCode で誤り訂正レベルを指定してデータを作り、PngByteQRCode に渡すと PNG のバイト列が得られる。GetGraphic(10) の 10 は 1 モジュールのピクセル数で、描画用のネイティブライブラリを足さなくても Linux のコンテナで動いた。
標準ライブラリでは作れないので定番のライブラリを使う
QR コードの生成は、ここで扱う 7 言語のどれも標準ライブラリだけでは書けないので、各言語で広く使われているライブラリを使った。
- Python は
qrcode8.2(Pillow 13.0.0、Python 3.14.10)。 - JavaScript は
qrcode1.5.4(Node.js 24.22.2)。 - TypeScript は同じ
qrcode1.5.4 に@types/qrcode1.5.6 とtsxを組み合わせた。 - Go は
github.com/skip2/go-qrcode(v0.0.0-20200617195104-da1b6568686e、Go 1.27.3)。 - Rust は
qrcode0.14.1 とimage0.25.10(Rust 1.99.1)。 - Java は ZXing の
coreとjavase3.5.4(OpenJDK 21.0.13)。 - C# は
QRCoder1.8.1(.NET SDK 8.0.426)。
画像を作れたことと、QR コードとして読めることは別なので、7 つの PNG はすべて別のデコーダ(Debian bookworm の zbar-tools に入っている zbarimg 0.23.92)で読み戻した。
結果は 7 つとも元の文字列 https://example.com/hello と一致した。
誤り訂正レベルは頑丈さと大きさの引き換えになる
QR コードには誤り訂正レベルが L・M・Q・H の 4 段階あり、上げるほど汚れや欠けに強くなる代わりに、同じデータでも模様が大きく細かくなる。
- 25 文字の URL を
qrcode(Python)で作ると、L と M は 25 モジュール四方(版 2)、Q は 29 四方(版 3)、H は 33 四方(版 4)になった。 - 中央を正方形に白く塗りつぶして
zbarimgが読める最大の一辺を調べると、L は 3 モジュール、M は 5、Q は 10、H は 13 だった。
既定値はライブラリごとに違う。
Python の qrcode は M だった。
JavaScript と Rust は 30 文字の URL で L より大きい版になり、既定が L ではないと分かった。
Java の ZXing は L と同じ 33 モジュール幅になり、既定が L だった。
ライブラリを替えたら、レベルを明示して条件をそろえる。
余白を削ると読めなくなることがある
QR コードの周りには、規格で 4 モジュール分の白い余白(クワイエットゾーン)が必要とされている。
画像を小さくしたくて余白を削ると、読み取り側が模様の外側を判別できなくなる場合がある。
- 余白 0 の PNG を黒で囲んだ画像に貼ると、
zbarimgは読めなかった。余白が 1 モジュールあれば同じ条件で読めた。 - 周囲が白・暗いグレー・白黒のランダムなブロックのときは、余白 0 でも読めた。
zbarimg は読み取りに寛容なほうなので、読めたからといってスマートフォンのカメラで読める保証にはならない。
余白は規格どおり 4 モジュール残しておくのが安全である。
URL が長いと版が上がって 1 つの点が小さくなる
データが長くなるほど必要な版が上がり、模様のモジュール数が増える。
誤り訂正レベル M の qrcode(Python)で、https://example.com/ に文字を足して長さを変えた結果は次のとおりだった。
- 25 文字は版 2(25 モジュール四方)。
- 60 文字は版 4(33 四方)。
- 100 文字は版 6(41 四方)。
- 200 文字は版 10(57 四方)。
レベルを H にすると 200 文字は版 15(77 四方)になる。
画像の幅を 256 ピクセルに固定すると、1 モジュールは版 2 で約 7.8 ピクセル、版 10 で約 3.9 ピクセルになる(余白 4 モジュールを含めて計算)。
点が小さいほど読み取りは難しくなるので、QR コードに入れる URL は短縮するか、不要なクエリを削るほうがよい。
ライブラリを入れる前に実行すると失敗する
どの例も外部ライブラリが前提なので、入れ忘れると最初の行で止まる。
実際に出た表示は次のとおりだった。
- Python は
ModuleNotFoundError: No module named 'qrcode'になる。 - Node.js は
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'qrcode'になる。 - Go は
no required module provides package github.com/skip2/go-qrcode; to add it:に続いてgo get github.com/skip2/go-qrcodeが案内される。 - TypeScript は
@types/qrcodeを外すとTS7016: Could not find a declaration file for module 'qrcode'になる。
言語ごとの入れ方は各コードの先頭のコメントに書いた。
画像の大きさの決め方が言語ごとに違う
同じ文字列・同じ誤り訂正レベル M でも、出力された PNG の一辺は言語で違った。
- Python(
box_size=10)、JavaScript と TypeScript(scale: 10)、C#(GetGraphic(10))は 330 ピクセルだった。33 モジュール(余白込み)× 10 ピクセルである。 - Rust は
min_dimensions(256, 256)を渡しても 264 ピクセルだった。33 モジュール × 8 ピクセルに切り上がった。 - Go と Java は指定した 256 ピクセルのままだった。
1 モジュールのピクセル数を指定する書き方は点の細かさを決めやすく、全体の大きさを指定する書き方は貼り付け先の枠に合わせやすい。