產生 QRCode

在某次活動中,我們使用了 QRCode 掃票機制,事先產生 QRCode 派送給報名參加的來賓,活動開始時則進行 QRCode 掃票。以下簡單記錄 QRCode 產生機制,現場掃票部分則見即時查驗票

Simple Qrcode

Simple QrcodeBacon/BaconQrCode 針對 Laravel 所提供的封裝版本,Laravel 使用者可以透過它輕鬆製作出簡易的 QRCOde。

首先透過 composer 安裝:

$ composer require simplesoftwareio/simple-qrcode

接著在需要的時候使用它即可:

use Qrcode;

// ...

Qrcode::generate('嵌入 QRCode 的字串', 'QRCode 的儲存位置');

也可以不儲存 QRCode,直接在 blade 渲染出來:

{!! QrCode::generate('嵌入 QRCode 的字串'); !!}

當然這都只是簡單的範例,還有許多可以調整的設定,例如透過 .format() 指定 QRCode 檔案格式、透過 .size()color() 指定 QRCode 的尺寸、顏色等,甚至透過 merge() 將自己的 logo 嵌入到 QRCode 裡:

QrCode::errorCorrection('H')
->format('png')
->size(300)
->color(0, 0, 0)
->merge('logo-path.png', .2, true)
->generate('嵌入 QRCode 的字串');

提示

errorCorrection() 為 QRCode 的容錯能力。容錯能力越高,QRCode 能儲存的資料量越少,但圖形越簡單,越容易被讀取。細節可參考這篇 台大電子報 的說明。

Simple Qrcode 圖例:

simple-qrcode

加入文字到 QRCode

QRCode 產生出來了,但也有更多問題冒出來:

這張 QRCode 怎麼掃就是掃不出來,怎麼辦?

我們會隨信附上票號,到時可透過票號查詢。

如果來賓只印了 QRCode,沒有印信件內容,更沒有印票號呢?

那我把票號直接印在 QRCode 上可以了吧?

Step1. 產生文字方塊

首先透過 imagettfbbox() 產生一個文字方塊,並將票號 $code 輸入進去:

// 取得字型檔 & 設定字型大小
$fontFile = "path-to-font.ttf";
$fontSize = 12;

// 產生文字方塊 & 計算寬高
$textBox = imagettfbbox($fontSize, $angle = 0, $fontFile, $code);
$textW = $textBox[4] - $textBox[6];
$textH = $textBox[1] - $textBox[7];

說明

imagettfbbox() 會回傳一個陣列,索引 0 - 7 對應的值分別是:

0左下 X 座標1左下 Y 座標 2右下 X 座標3右下 Y 座標 4右上 X 座標5右上 Y 座標 6左上 X 座標7左上 Y 座標

Step2. 計算座標

我們剛才產生了文字方塊,並計算出它的寬高,接下來要與 QRCode 的圖片尺寸比對,計算出適當的座標將文字輸入進去,以確保文字在 QRCode 下方水平置中:

$qrcode = QrCode::generate($code);

$image = imagecreatefromstring($qrcode);
$imageW = imagesx($image);
$imageH = imagesy($image);

$x = ($imageW / 2) - ($textW / 2);
$y = $imageH - $textH;

Step3. 加入文字

計算完座標之後,透過 PHP 的 imagettftext() 將文字寫入圖片,並儲存下來,就大功告成了:

imagettftext($image, $fontSize, 0, $x, $y, $color, $fontFile, $code);
imagepng($image, $file);

發信事件監聽

QRCode 產生完後,就是透過 Laravel 的發信機制將票券寄給來賓了,這部分參照 Laravel 官方文件便可。

若要確保系統有正常發信,可分別在發信時與發信後紀錄 Log,Laravel 預設提供 MessageSendingMessageSent 兩個監聽事件給我們使用,在 EventServiceProvider 註冊它們即可:

protected $listen = [
    'Illuminate\Mail\Events\MessageSending' => [
        'App\Listeners\LogSendingMessage',
    ],
    'Illuminate\Mail\Events\MessageSent' => [
        'App\Listeners\LogSentMessage',
    ],
];
Last Updated: 8/9/2019, 6:50:41 PM