[Node.js] QRコードを生成する

今回はNode.js上で簡単にQRコードが生成できるnode-qrcodeを利用し、動的にQRコードを生成してみたいと思います。 github.com

QRコード自体は非常に便利ですよね。2次元バーコードよりも情報量が増やせるため様々な用途に利用できます。 しばらく前から日本はQRコード決済が世を賑わせていますが、個人的には早く滅んでくれないかなと思っていますがそれはまた別の話しw なんでFeliCaから退化しとんねんw

インストール

node-qrcodeはコマンドラインから利用できるコマンドも用意されていますが、今回はNode内からモジュールとして利用します。そのため適当なディレクトリを作成し、package.jsonを作った上で、npm installしてやります。

$ mkdir qr && cd qr;
$ npm init
$ npm install qrcode

HelloWorld

利用方法は実にシンプルで最短で2行書くだけです。1行目でモジュールを読み込み、2行目でQRコードを生成し画像として保存しています。

const QRCode = require('qrcode');
QRCode.toFile('foo.png', 'Hello QR');

コマンドラインから実行すると、foo.pngが作成されます。

$ node test.js
$ ls -l *.png
-rw-r--r--@ 1 katsube  staff  769  6  2 19:24 foo.png

実際に生成されたQRコードは以下です。スマホなどのQRコードリーダーで読み取ってみてください。

出力方式

BASE64

HTMLのimgタグ内に直接埋め込むために、BASE64でエンコードした形で出力することができます。

const QRCode = require('qrcode');

QRCode.toDataURL('Hello QR', function (err, url) {
  console.log(`<img src="${url}">`);
});

以下のようなHTMLタグが出力されます。

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAAAklEQVR4AewaftIAAAK6SURBVO3BQW7sWAwEwSxC979yjpdcPUCQusfmZ0T8wRqjWKMUa5RijVKsUYo1SrFGKdYoxRqlWKMUa5RijVKsUYo1SrFGKdYoFw8l4ZtUTpJwh8pJEr5J5YlijVKsUYo1ysXLVN6UhJMkdCpdEt6k8qYkvKlYoxRrlGKNcvFhSbhD5Q6VO1TelIQ7VD6pWKMUa5RijXLxj0tCp/KXFWuUYo1SrFEu/rgkdCqdSpeEyYo1SrFGKdYoFx+m8kkqT6g8ofKbFGuUYo1SrFEuXpaEb0pCp9IloVPpktCpnCThNyvWKMUapVijxB8MkoRO5V9SrFGKNUqxRrl4KAmdSpeETqVLQqfSJaFTOVG5Iwmdyh1J6FROktCpvKlYoxRrlGKNEn/wQBKeUOmS0KmcJKFTOUlCp9IloVN5UxJOVJ4o1ijFGqVYo1y8TOUkCV0SOpUuCd+kcpKE36xYoxRrlGKNEn/wQBKeUOmS0KmcJOGTVN6UhE7lTcUapVijFGuU+IMXJeGbVLok3KFykoROpUtCp/J/KtYoxRqlWKNcvEzlm5LQqXRJ6FTepHKShBOVNxVrlGKNUqxRLh5KwjepdCpdEu5IQqfSqXRJOFG5IwmdyhPFGqVYoxRrlIuXqbwpCXeodEk4UTlJwonKiUqXhE7lTcUapVijFGuUiw9Lwh0qdyThiSR0KicqXRI6lROVTyrWKMUapVijXPxxKl0STpLQqXRJ6FS6JNyRhBOVNxVrlGKNUqxRLv64JJyodEm4IwmdSpeEO1S6JHQqTxRrlGKNUqxRLj5M5ZNUuiTckYQTlSdUuiR0Km8q1ijFGqVYo1y8LAnflIRO5Q6VLgldEk5UnkhCp/JEsUYp1ijFGiX+YI1RrFGKNUqxRinWKMUapVijFGuUYo1SrFGKNUqxRinWKMUapVijFGuU/wDBCBfkDBM1EQAAAABJRU5ErkJggg==">

SVG

imgタグ以外にもSVGでの出力にも対応しています。

const QRCode  = require('qrcode');
const options = {
  type: 'svg'
};

QRCode.toString('Hello QR', options, function (err, str) {
  console.log(str);
});

出力結果は以下。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29 29" shape-rendering="crispEdges"><path fill="#ffffff" d="M0 0h29v29H0z"/><path stroke="#000000" d="M4 4.5h7m2 0h3m2 0h7M4 5.5h1m5 0h1m1 0h1m1 0h2m2 0h1m5 0h1M4 6.5h1m1 0h3m1 0h1m3 0h1m3 0h1m1 0h3m1 0h1M4 7.5h1m1 0h3m1 0h1m3 0h1m1 0h1m1 0h1m1 0h3m1 0h1M4 8.5h1m1 0h3m1 0h1m1 0h1m2 0h2m1 0h1m1 0h3m1 0h1M4 9.5h1m5 0h1m4 0h1m2 0h1m5 0h1M4 10.5h7m1 0h1m1 0h1m1 0h1m1 0h7M13 11.5h1M4 12.5h1m1 0h1m1 0h1m1 0h1m5 0h1m3 0h1m2 0h1M4 13.5h4m1 0h1m2 0h1m2 0h1m1 0h1m5 0h2M4 14.5h1m2 0h2m1 0h3m2 0h1m3 0h6M4 15.5h4m1 0h1m2 0h6m5 0h1M5 16.5h7m1 0h3m2 0h1m1 0h1M12 17.5h1m1 0h1m3 0h1m3 0h2M4 18.5h7m2 0h2m1 0h1m2 0h2m1 0h3M4 19.5h1m5 0h1m3 0h1m3 0h2m3 0h1M4 20.5h1m1 0h3m1 0h1m1 0h3m1 0h1m1 0h1m1 0h1M4 21.5h1m1 0h3m1 0h1m2 0h3m1 0h1m1 0h2m1 0h2M4 22.5h1m1 0h3m1 0h1m1 0h2m1 0h1m1 0h2m1 0h1m1 0h1m1 0h1M4 23.5h1m5 0h1m2 0h1m1 0h3m2 0h1m2 0h1M4 24.5h7m1 0h1m2 0h1m1 0h3m3 0h2"/></svg>

実際に表示させてみるとこんな感じです。サイズはCSSで調整してます。

文字列

おそらくコンソール画面などで表示するためのものだと思いますが、文字列でQRコードを取得できます。

const QRCode  = require('qrcode');
const options = {
  type: 'terminal'
};

QRCode.toString('Hello QR', options, function (err, str) {
  console.log(str);
});

以下のような結果が得られます。

typeの値をutf8にすると(指定を行わない場合のデフォルト値)、以下のような結果になりますが、こちらは私の環境だとQRコードリーダーが認識してくれなかったので、表示する際のフォントやら何やらをがんばらないとダメそうですね。

canvas

node-canvasに出力することも可能です。HTML5から搭載されたcanvas要素がnode上でも利用できるという物ですが、別の画像を上に載せたり、QRコード自体に装飾をするなど加工が簡単にできるようになりますね。 github.com

node-canvasは別途インストールする必要があります。

$ npm install canvas

ここでは最終的に画像データをBASE64に変換して出力しています。

const { createCanvas } = require('canvas');
const canvas = createCanvas(200, 200);

const QRCode  = require('qrcode');
const options = {};

QRCode.toCanvas(canvas, 'Hello QR', options, function(error){
  // 文字を入れる
  const ctx = canvas.getContext('2d');
  ctx.font = '20px Impact';
  ctx.fillStyle = 'blue';
  ctx.fillText('Hello!', 10, 20);

  console.log('<img src="' + canvas.toDataURL() + '" />');
});

出力結果は以下の通り。

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAABmJLR0QA/wD/AP+gvaeTAAAFXUlEQVR4nO2dT2gUVxzHPxtz0IDSaBZMDRJacjBFRcFCEaSHoBJBQSGxPWhLLYgHFTx4K54iRQQFD/7FCi0txNpLYy2BEPASIQUxpIgSBKM5GpDUeNrXw5uQ3dl1d9/Mm8nOL78PLMP83r8Zvvt77/3e/MsZYwyKGJqW+gAUv6igwlBBhaGCCsNJ0OfPYXAQJiaq5xschHv3oh3Q+/e2/NjYom1szNrm56PVOT9vy9+/H618pjAOXL5sDBhz9uyH8xQKNk9Li0vNi8zM2PL9/Yu2w4et7fXraHVOT9vynZ3RymcJ7XKFoYIKoznNxt6+hdu3YXzc7m/bBkeOQFtbtPqePYPr12FyEnI52L4djh+Hjg5/x5w1UvPQiQno7obTp2FoCEZG4MwZ2LQJRkfd67t7FzZvhkuXYGYGXryAgQHbxoMHpXmbm0u3kol0io8fW8+oRKWFxLk5OHAAZmetEIcOWfvDh7B/Pxw8CFNT0NpaX/tTU3D0KLS3w/AwdHVZ+/g47N0LfX3We9evt/Z166wHr13rdp6ZxGUGtTDLredXPMu9csXazp8vr/PCBZt27Zrdr2eWe/Kk3b9zp7y+ixdt2sBAqb211ZjeXpezzSaRPLS/H44d+9AfBHbvLrUtdIG7dlkvLWbHDrutFdsWMzJiu8++vvK0PXtsV/7oUam9rc16qnQiCdrZCT09ldMqdbkvX9rtzp0frjMsdDVevbLd6cqV5Wn5fOX68vnok68skco0IZez25s3Yc2aynk2bHCrs1CobH/zxm5Xry615/PLw0NjrxQBoV8uGEf/K7L9Edg+r5D/IwM9Bj4J9tuDvL8V5fk1sH1sjDFm61ZjmpqMmZ0tP0b4Osj7Y6idbwx8WdZ+LcqPN9lfXFIKW/4Mtt9VSPsBGAa66q5t3z7roVevVkr9NtiGF5N/AkbrbiOrpBSZ/QycAL4HZoGhoOmvAtvfWFHr49QpuHULzp2Dpibo7YV37+DGDYAe4HegeFbUAUwD/wKfxT6bhsbFnaN3uRjIG/grFN4UDAwaaC3KV7vLNcaYJ0+M6e4uDZVyOWPgFwMtobY7gjyT4rvcXHDQkcktzHgW/yBV8z99akOUVatgyxbYuDF6/YUCrFjxBfApMAf8gzHTVcuHqXW8rufniu/6UxfUd/2u6WGkCapXW4Shggoj8VlurS4vjGuXk3T+WiR9fq6ohwpDBRWGCiqMZXANvzpJhyVpox4qDBVUGCqoMDI/htYaA12XArOOeqgwVFBhqKDCSHwMTTqui1v/Upf3jXqoMFRQYaigwvA+hqYd5/m+RcX1lpRGQz1UGCqoMFRQYcS+jbPRkHZ90xX1UGGooMJQQYUROw5NOg50HRNdx8y4Y27cONb3GK8eKgwVVBgqqDC8P07oiu+11bj50358MIw+TqiUoIIKQwUVhvfroa5jWNxH6n2j10OVhkIFFYYKKgxxcahvGv01NmHUQ4WhggpDBRWG93uKljpu9P3eoKVe+3VFPVQYKqgwVFBhpL6W65ulHuPixskahypVUUGFoYIKw/t9uUnj+z1Evt9zlPZ9wWHUQ4WhggpDBRVGw8ehcddmfX/mw7V82nGxeqgwVFBhqKDCyPx3W+LGwXGfP036+VVX1EOFoYIKQwUVRubfOe8adyb9+cpa7YXROFSpigoqDBVUGJkfQ13HKN/XS32vNev1UKUEFVQYKqgwxH23pdHiRL0eqsRCBRWGCioM8d9tqUWtMS7pMVbjUKUqKqgwVFBhiPtuy3JHPVQYKqgwVFBhqKDCUEGFoYIKQwUVhgoqDBVUGCqoMFRQYaigwlBBhaGCCkMFFcb/JCAhpAILP7YAAAAASUVORK5CYII=" />

実際に表示するとこのようになります。文字列Helloが左上に入っています。

オプション

高さと横幅

第3引数に様々なオプションをハッシュで指定することができます。 widthに横幅のピクセル数を指定すると、そのサイズのQRコード(画像)が生成されます。

const QRCode  = require('qrcode');
const options = {width:500};

QRCode.toFile('foo.png', 'Hello QR', options);

基本的にQRコードは正方形ですので、高さを指定する必要はありません。

colorにRGBを指定することで、QRコードや背景色を指定できます。 以下のコードではQRコードを白、背景が黒になります。

const QRCode  = require('qrcode');
const options = {
  color: {
     dark: '#FFF',  // QRコード
    light: '#000'   // 背景
  }
};

QRCode.toFile('foo.png', 'Hello QR', options);
  • 厳密にはRGBではなくRGBAとなっており、最後にアルファチャンネルの指定が可能です。
  • デフォルトでは以下の指定がされています。
    • color.dark#000000ff (QRコード)
    • color.light#ffffffff (背景)

以下のような出力結果になります。

背景を透過色

背景色color.lightで、アルファチャンネルの指定を行います。

const QRCode  = require('qrcode');
const options = {
  color: {
     dark: '#000',   // QRコード
    light: '#FFF0'   // 背景(透過させる)
  }
};

QRCode.toFile('foo.png', 'Hello QR', options);

以下のような出力結果になります。背景色をCSSで薄いピンクにしてあります。

余白

QRコード画像のマージンを指定できます。デフォルトは4です。

const QRCode  = require('qrcode');
const options = {
  margin: 10
};

QRCode.toFile('foo.png', 'Hello QR', options);

最大文字数

「エラー訂正レベル」と「文字列の種類」によって、QRコードに埋め込める最大文字列長が変わってきます。

モード L M Q H
Numeric 7,089 5,596 3,993 3,057
Alphanumeric 4,296 3,391 2,420 1,852
Byte 2,953 2,331 1,663 1,273
Kanji 1,817 1,435 1,024 784

エラー訂正レベル

エラー訂正レベルのデフォルトはMですが、オプションによって指定することが可能です。

const QRCode  = require('qrcode');
const options = {
  errorCorrectionLevel: 'M'   // エラー訂正レベル(L, M, Q, H)
};

QRCode.toFile('foo.png', 'Hello QR', options);

要はエラー訂正レベルが強いほど、たくさんの文字を埋め込めるというお話です。その代わりQRコードのサイズも大きくなります。

文字列の種類

通常、文字列の種類は自動的に判別されますが、手動で設定することができます。

モード 文字種 圧縮
Numeric 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 3文字を10bitに圧縮
Alphanumeric 0–9, A–Z (大文字のみ), 半角スペース, $, %, *, +, -, ., /, : 2文字を11bitに圧縮
Kanji シフトJIS (JIS X 0208規格に基づく) 2文字の漢字等を13bitに圧縮
Byte ISO/IEC 8859-1に基づく文字 各文字は8bit

以下のコードでいうと変数segmentに代入しているハッシュのように、文字列毎に種類を設定します。

const QRCode  = require('qrcode');
const segment = [
  { data:'ABCDEFG', mode:'alphanumeric' },
  { data:'0123456', mode:'numeric' }
];
const options = {};

QRCode.toFile('foo.png', segment, options);

次のQRコードが生成されます。2つの要素が結合されているのが確認できます。

……まぁ基本的にはライブラリにお任せしちゃうのが良いですね。なにか特別な事情がある場合に利用する感じでしょうか。

UTF8を利用するには

結論から言うとByteモードにすれば、大抵のQRコードリーダーで読み取ることが可能です。特に設定する必要はありません。 (ただし1文字あたり最大4byteかかる点に注意)

詳しくはドキュメントを参照ください。

Multibyte characters Support for multibyte characters isn't present in the initial QR Code standard, but is possible to encode UTF-8 characters in Byte mode. QR Codes provide a way to specify a different type of character set through ECI (Extended Channel Interpretation), but it's not fully implemented in this lib yet. Most QR Code readers, however, are able to recognize multibyte characters even without ECI. Note that a single Kanji/Kana or Emoji can take up to 4 bytes.

続き

blog.katsubemakito.net

参考ページ