因行動裝置的流行而跟著帶動起來的QR Code,是眾多條碼類型的其中一種,它讓我們的生活更便利,傳遞資訊的速度更快,省下許多使用鍵盤打字的時間。如果要產生出QR Code,已經有許多相關的現成工具能夠使用,甚至還能直接在網頁上產生。可是如果要我們自己用來做的話,究竟要如何才能產生QR Code呢?



magic-qr-code

「magic-qr-code」是一個使用Node.js 8之後才支援的N-API所開發的模組,引用了這個函式庫「QR Code generator library」(C語言)來編碼出QR Code的資料。

GitHub:

https://github.com/magiclen/node-qr-code

npm:

https://www.npmjs.com/package/magic-qr-code

安裝

直接使用npm指令進行安裝:

npm install --save magic-qr-code

用法

初始化
const QRCode = require('magic-qr-code');
將字串編碼成QR Code

使用模組提供的「encode」函數可以將任意字串編碼成QR Code資料,用法如下:

QRCode.encode('任意文字');

如果想要控制QR Code的容錯率,可以再加上第二個參數,用法如下:

QRCode.encode('任意文字', QRCode.ECC_H);

以上的「QRCode.ECC_H」表示30%的容錯率。另外還有25%的「QRCode.ECC_Q」、15%的「QRCode.ECC_M」和7%的「QRCode.ECC_L」。相同的任意文字,使用愈高的容錯率編碼出來的QR Code的「版本」(version)愈高,換句話說,QR Code的資料量會愈大,呈現出來的點數會愈多。當然,增加容錯率,並不一定會提升QR Code的版本,資料量還是得要超過該QR Code版本的上限,版本才會再往上提升。

這個模組會自動以該QR Code版本能夠容許的最大容錯率來選擇容錯率。舉例來說,如果相同的任意文字使用「QRCode.ECC_Q」和「QRCode.ECC_H」所編碼出來的QR Code都是同一個版本,就算我們傳給「encode」函數的第二個參數是「QRCode.ECC_Q」,這個模組會自動使用「QRCode.ECC_H」來進行編碼。

QR Code有區分不同的字元集,如果要被編碼的字串都是數字,就會使用數值(Numeric)模式來進行編碼;如果要被編碼的字串都是數字、大寫英文字母和部份有涵蓋的標點符號,就會使用數值(Alphanumeric)模式來進行編碼。QR Code編碼的模式還會影響到每個版本所能容許的字串長度,好像很複雜對吧?但是別緊張,使用這個模組來編碼QR Code,會自動選擇合適的模式來進行編碼,保證最後編碼出來的QR Code是版本最小、容錯率最高的!

「encode」函數將字串編碼成QR Code的資料後,會以Buffer陣列的形式回傳。如下:

let result = QRCode.encode('https://magiclen.org'.toUpperCase());
/*
 [
  <Buffer 01 01 01 01 01 01 01 00 01 01 00 00 01 00 01 01 01 01 01 01 01>,
  <Buffer 01 00 00 00 00 00 01 00 01 01 01 00 00 00 01 00 00 00 00 00 01>,
  <Buffer 01 00 01 01 01 00 01 00 01 01 01 00 00 00 01 00 01 01 01 00 01>,
  <Buffer 01 00 01 01 01 00 01 00 00 01 01 01 00 00 01 00 01 01 01 00 01>,
  <Buffer 01 00 01 01 01 00 01 00 01 00 00 01 01 00 01 00 01 01 01 00 01>,
  <Buffer 01 00 00 00 00 00 01 00 00 01 00 01 01 00 01 00 00 00 00 00 01>,
  <Buffer 01 01 01 01 01 01 01 00 01 00 01 00 01 00 01 01 01 01 01 01 01>,
  <Buffer 00 00 00 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 00 00>,
  <Buffer 01 00 00 01 01 01 01 01 01 00 00 01 00 01 00 00 01 00 01 01 01>,
  <Buffer 01 00 01 00 01 01 00 01 01 01 01 01 01 01 00 01 01 00 00 00 00>,
  <Buffer 00 00 01 00 01 00 01 00 01 01 01 01 00 00 01 00 00 01 00 00 00>,
  <Buffer 00 01 01 01 01 00 00 01 01 01 01 00 01 01 00 00 01 00 01 01 00>,
  <Buffer 00 01 00 01 00 01 01 00 01 01 01 01 00 01 01 01 00 01 00 01 01>,
  <Buffer 00 00 00 00 00 00 00 00 01 01 00 00 00 01 00 00 01 01 01 00 00>,
  <Buffer 01 01 01 01 01 01 01 00 01 00 01 00 00 01 00 00 00 01 01 01 00>,
  <Buffer 01 00 00 00 00 00 01 00 01 00 01 01 00 01 00 01 00 01 01 00 00>,
  <Buffer 01 00 01 01 01 00 01 00 01 01 01 00 01 00 01 01 00 01 01 00 00>,
  <Buffer 01 00 01 01 01 00 01 00 01 00 00 00 00 01 00 01 01 00 01 00 00>,
  <Buffer 01 00 01 01 01 00 01 00 00 01 00 00 00 00 01 00 00 01 00 01 01>,
  <Buffer 01 00 00 00 00 00 01 00 00 00 00 00 00 00 01 01 00 00 01 01 00>,
  <Buffer 01 01 01 01 01 01 01 00 01 01 00 01 01 01 00 00 00 01 00 01 00>
 ]
*/
將QR Code資料畫成圖

「encode」函數會回傳Buffer陣列,每個Buffer均表示一排QR Code圖形的橫向資料,Buffer中的值只會有0或1。0表示為白點;1表示為黑點。利用這筆資料,我們就可以產生出QR Code的圖形了!

Node.js有一個強大的畫圖模組──Canvas,可以直接使用npm指令進行安裝:

npm install canvas@next --save

這個「Canvas」模組也是用C語言來實作的,效能極佳。使用npm安裝時在「canvas」後面接上「@next」可以確保安裝到最新的版本。

將「Canvas」模組和「magic-qr-code」模組搭配使用的程式碼如下:

const QRCode = require('magic-qr-code');
const Canvas = require('canvas');

let result = QRCode.encode('https://magiclen.org'.toUpperCase());

function draw(data, size = 1024) {
    let marginSize = 1;
    let dataLength = data.length;
    let dataLengthWithMargin = dataLength + 2 * marginSize;
    let canvas = Canvas.createCanvas(size, size);
    let ctx = canvas.getContext('2d');
    let pointSize = Math.floor(size / dataLengthWithMargin);
    if (pointSize === 0) {
        throw new Error('cannot draw this QR Code');
    }
    let margin = Math.floor((size - (pointSize * dataLength)) / 2);
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, size, size);
    ctx.fillStyle = 'black';
    for (let i = 0; i < dataLength; ++i) {
        for (let j = 0; j < dataLength; ++j) {
            if (data[i][j]) {
                let x = j * pointSize + margin;
                let y = i * pointSize + margin;
                ctx.fillRect(x, y, pointSize, pointSize);
            }
        }
    }
    return canvas;
}

let canvas = draw(result);

// Output

const fs = require('fs');
let pngBuffer = canvas.toBuffer();
fs.writeFileSync('./out.png', pngBuffer);

以上程式會成功輸出底下張PNG格式的QR Code:

node-qrcode