PHP 生成二維碼掃描後中文顯示亂碼,核心原因通常是 **編碼不一致**(生成端 UTF-8、掃描端預期 GBK/其他)或 **vCard 格式欄位缺失** 導致解析器誤判。本文深度剖析根因、提供三大主流庫整合代碼、並解決 Logo 嵌入與編碼兼容難題。
| 現象 | 根因 | 驗證方法 |
|---|---|---|
| Android 掃描 vCard 姓名亂碼 | vCard 缺少 `ORG` 等關鍵欄位,解析器回退錯誤編碼 | 補全所有 vCard 標準欄位測試 |
| 所有掃描器中文亂碼 | QR Code 內容實際為 GBK/Big5,但掃描器預設 UTF-8 | 用線上解碼工具檢視原始位元組 |
| 特定字元(如表情符)消失 | QR Code 版本過小/錯誤修正等級過高,容量不足 | 升級 Version、降低 Error Correction Level |
<?php
function generateGoogleQR(string $data, int $size = 150, string $level = 'L', int $margin = 0): string {
$params = http_build_query([
'chs' => "${size}x{$size}",
'cht' => 'qr',
'chl' => $data,
'choe' => 'UTF-8', // 強制指定編碼
'chld' => "$level|$margin",
]);
return "https://chart.googleapis.com/chart?$params";
}
// 使用
echo '<img src="' . generateGoogleQR('中文測試內容') . '" />';
?>
> ⚠️ Google 於 2019 年停止服務,**生產環境勿用**。
# 安裝
composer require endroid/qrcode # 現代維護分支
# 或下載原版:https://sourceforge.net/projects/phpqrcode/
<?php
require_once 'phpqrcode/qrlib.php'; // 原版入口
// 1. 直接輸出圖片到瀏覽器
QRcode::png('中文內容測試', false, 'L', 4, 2);
// 2. 儲存檔案
QRcode::png('https://example.com?vcard=...', 'qrcode.png', 'M', 6, 2);
// 3. 關鍵:強制 UTF-8 編碼輸入
$data = mb_convert_encoding('中文內容', 'UTF-8', 'auto');
QRcode::png($data, false, 'L', 4);
?>
composer require bacon/bacon-qr-code
<?php
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
$writer = new Writer(
new ImageRenderer(
new RendererStyle(300), // 300px
new SvgImageBackEnd()
)
);
// 直接輸出 SVG(無損縮放、易嵌入 Logo)
header('Content-Type: image/svg+xml');
echo $writer->writeString('中文測試內容');
// PNG 輸出需額外安裝 imagine/imagine
?>
# 安裝系統庫
apt-get install libqrencode-dev
# PHP 擴充
pecl install qrencode
# php.ini
extension=qrencode.so
<?php
// 簡單介面
qr_encode('中文內容', 'qrcode.png', 300, 300, 3, QR_ECLEVEL_L);
// 參數:資料、檔名、寬、高、版本、錯誤修正等級
?>
<?php
require_once 'phpqrcode/qrlib.php';
function generateQRWithLogo(string $data, string $logoPath, string $outputPath, int $qrSize = 300): bool {
// 1. 產生基礎 QR Code (較高錯誤修正等級 H 容許 30% 遮擋)
QRcode::png($data, $outputPath, 'H', 10, 1); // 邊距 1
// 2. 載入 QR Code 與 Logo
$qr = imagecreatefrompng($outputPath);
$logo = imagecreatefromstring(file_get_contents($logoPath));
if (!$qr || !$logo) return false;
// 3. 計算置中座標
$qrW = imagesx($qr);
$qrH = imagesy($qr);
$logoW = imagesx($logo);
$logoH = imagesy($logo);
// Logo 最大佔 QR Code 20% 面積
$maxLogoW = $qrW * 0.2;
$maxLogoH = $qrH * 0.2;
if ($logoW > $maxLogoW || $logoH > $maxLogoH) {
$scale = min($maxLogoW / $logoW, $maxLogoH / $logoH);
$logoW = (int)($logoW * $scale);
$logoH = (int)($logoH * $scale);
$logo = imagescale($logo, $logoW, $logoH);
}
$dstX = ($qrW - $logoW) / 2;
$dstY = ($qrH - $logoH) / 2;
// 4. 白色底板(可選,增強對比)
$white = imagecolorallocate($qr, 255, 255, 255);
imagefilledrectangle($qr, $dstX-2, $dstY-2, $dstX+$logoW+2, $dstY+$logoH+2, $white);
// 5. 合成 (支援透明度)
imagealphablending($qr, true);
imagesavealpha($qr, true);
imagecopy($qr, $logo, $dstX, $dstY, 0, 0, $logoW, $logoH);
// 6. 輸出
imagepng($qr, $outputPath);
imagedestroy($qr);
imagedestroy($logo);
return true;
}
// 使用
generateQRWithLogo('https://example.com', 'logo.png', 'qr_with_logo.png');
?>
<?php
function buildVCard(array $data): string {
$required = [
'FN' => $data['name'] ?? '', // 完整姓名 (必填)
'ORG' => $data['company'] ?? ' ', // 公司/組織 (關鍵:防亂碼必填)
'TEL;TYPE=WORK,VOICE' => $data['tel'] ?? '',
'EMAIL' => $data['email'] ?? '',
'URL' => $data['website'] ?? '',
'ADR;TYPE=WORK' => $data['address'] ?? '',
'TITLE' => $data['title'] ?? '',
'NOTE' => $data['note'] ?? '',
];
$lines = ['BEGIN:VCARD', 'VERSION:3.0'];
foreach ($required as $key => $val) {
if ($val !== '') {
$lines[] = "$key:" . str_replace(['\n', '\r', ':'], ['\\n', '\\r', '\\:'], $val);
}
}
$lines[] = 'END:VCARD';
return implode("\r\n", $lines); // vCard 標準換行 CRLF
}
// 生成含 Logo 的 vCard QR Code
$vcard = buildVCard([
'name' => '張三豐',
'company' => '武當派', // 必填!
'tel' => '+886-2-1234567',
'email' => 'zhang@wudang.org',
]);
generateQRWithLogo($vcard, 'logo.png', 'vcard_qr.png');
?>
ORG: ` 也要保留。採用 BaconQrCode 或 phpqrcode 搭配完整 vCard 模板與 H 級錯誤修正,能徹底解決中文亂碼、Logo 嵌入、跨平台掃描兼容三大核心痛點。