使用 imagettftext() 繪製中文驗證碼時,顯示亂碼或方塊字符。原因:GD 庫的 FreeType 引擎**不直接支持 UTF-8 字符串**,需將字符串轉為 **UCS-2BE** 或 **HTML 實體** 格式。
<?php
header('Content-Type: image/jpeg');
// 1. 準備畫布
$img = imagecreatetruecolor(250, 60);
$bg = imagecolorallocate($img, 255, 255, 255);
$fg = imagecolorallocate($img, 0, 0, 0);
imagefill($img, 0, 0, $bg);
// 2. 字體文件(確保路徑正確、可讀)
$font = __DIR__ . '/fonts/NotoSansSC-Regular.otf'; // 推薦開源無襯線字體
// 3. 核心:將 UTF-8 中文轉為 HTML 實體(xxx;)
$text = '中華人民共和國';
$text = mb_convert_encoding($text, 'HTML-ENTITIES', 'UTF-8');
// 結果:中和八开区八和国
// 4. 繪製
imagettftext($img, 24, 0, 20, 40, $fg, $font, $text);
// 5. 輸出
imagejpeg($img, null, 90);
imagedestroy($img);
imagettftext() 內部會解析 xxx; 形式的 Unicode 碼點,繪製對應字形。iconv('UTF-8', 'UCS-2BE') 需處理字節序(BOM)、字體是否支持 UCS-2 的不確定性。<?php
class Captcha {
private $width = 130;
private $height = 40;
private $length = 4;
private $fontSize = 20;
private $fontFile;
private $charset = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // 去除易混淆字符
public function __construct(string $fontFile) {
$this->fontFile = $fontFile;
}
public function generate(): string {
$code = '';
for ($i = 0; $i < $this->length; $i++) {
$code .= $this->charset[random_int(0, strlen($this->charset) - 1)];
}
$img = imagecreatetruecolor($this->width, $this->height);
$bg = imagecolorallocate($img, 240, 240, 240);
imagefill($img, 0, 0, $bg);
// 干擾線
for ($i = 0; $i < 5; $i++) {
$color = imagecolorallocate($img, random_int(150, 200), random_int(150, 200), random_int(150, 200));
imageline($img, random_int(0, $this->width), random_int(0, $this->height),
random_int(0, $this->width), random_int(0, $this->height), $color);
}
// 干擾點
for ($i = 0; $i < 50; $i++) {
$color = imagecolorallocate($img, random_int(100, 200), random_int(100, 200), random_int(100, 200));
imagesetpixel($img, random_int(0, $this->width), random_int(0, $this->height), $color);
}
// 繪製文字(逐字符隨機角度/位置)
$x = 10;
for ($i = 0; $i < $this->length; $i++) {
$char = mb_convert_encoding($code[$i], 'HTML-ENTITIES', 'UTF-8');
$angle = random_int(-15, 15);
$y = random_int($this->fontSize + 5, $this->height - 5);
$color = imagecolorallocate($img, random_int(0, 100), random_int(0, 100), random_int(0, 100));
imagettftext($img, $this->fontSize, $angle, $x, $y, $color, $this->fontFile, $char);
$x += $this->fontSize + random_int(2, 6);
}
// 存入 Session
session_start();
$_SESSION['captcha_code'] = strtolower($code); // 不區分大小寫驗證
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
return $code;
}
public static function verify(string $input): bool {
session_start();
return strtolower($input) === ($_SESSION['captcha_code'] ?? '');
}
}
// 使用示例
// require 'Captcha.php';
// $captcha = new Captcha(__DIR__ . '/fonts/NotoSansSC-Regular.otf');
// $captcha->generate();
<?php
require 'Captcha.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (Captcha::verify($_POST['captcha'] ?? '')) {
echo '驗證通過';
// 進行業務邏輯
} else {
echo '驗證碼錯誤';
}
}
| 現象 | 排查方向 |
|---|---|
| 顯示方框/亂碼 | 字體路徑錯誤、字體不支持該字符、未用 HTML-ENTITIES 轉換 |
| 字符重疊/位置異常 | 逐字符繪製時 x 坐標累加邏輯錯誤 |
| Session 驗證總失敗 | 生成與驗證未同域、session_start() 位置、大小寫不一致 |
| 字體文件 403/404 | 權限、路徑、open_basedir 限制 |
mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8') 是 GD 繪製中文驗證碼的「銀彈」。配合開源字體(Noto Sans SC、Source Han Sans)、逐字符隨機化繪製、Session 安全驗證,即可構建生產級中文驗證碼系統。