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



magic-qr-code

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

npmjs.com

npm 安裝指令

npm install magic-qr-code

使用方法

將字串編碼成QR Code

使用模組提供的encodeString函數可以將任意字串編碼成QR Code資料;encodeBuffer函數則可以將任意Buffer編碼成QR Code資料。用法如下:

import { encodeString } from "magic-qr-code";

encodeString("任意文字");

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

import { encodeString, ErrorCorrection } from "magic-qr-code";

encodeString("任意文字", ErrorCorrection.High);

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

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

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

encodeString函數將字串編碼成QR Code的資料後,會以Buffer陣列(Buffer[])的形式回傳。如下:

import { encodeString } from "magic-qr-code";

const result = encodeString("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中的值只會有010表示為白點;1表示為黑點。利用這筆資料,我們就可以產生出QR Code的圖形了!

Node.js有一個強大的畫圖模組──canvas。這個canvas模組是用C++語言來實作的,效能極佳。

canvas模組和magic-qr-code模組搭配使用的程式碼如下:

import { writeFileSync } from "node:fs";

import { encodeString } from "magic-qr-code";
import { createCanvas } from "canvas";

const result = encodeString("https://magiclen.org".toUpperCase());

function draw(data: Buffer[], size = 1024) {
    const marginSize = 1;
    const dataLength = data.length;
    const dataLengthWithMargin = dataLength + (2 * marginSize);
    const canvas = createCanvas(size, size);
    const ctx = canvas.getContext("2d");
    const pointSize = Math.floor(size / dataLengthWithMargin);

    if (pointSize === 0) {
        throw new Error("cannot draw this QR Code");
    }

    const 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]) {
                const x = (j * pointSize) + margin;
                const y = (i * pointSize) + margin;

                ctx.fillRect(x, y, pointSize, pointSize);
            }
        }
    }

    return canvas;
}

const canvas = draw(result);

// Output

const pngBuffer = canvas.toBuffer();

writeFileSync("./out.png", pngBuffer);

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

node-qrcode