根本原因是系统缺少支持ascii字符集的truetype字体或路径权限不足;需用绝对路径指向可读的-bold.ttf文件(如/var/www/fonts/dejavusans-bold.ttf),并验证file_exists、is_readable及imageftbbox返回值。

Linux 下 PHP 验证码(如 imagefttext 生成的图片)显示乱码、方块或字母缺失,根本原因不是 PHP 写错了,而是系统没提供能渲染 ASCII 字母(尤其带抗锯齿/粗体/斜体变体)的 TrueType 字体路径,或者字体文件本身不支持 Latin-1 字符集 —— 即使你只打算画英文字母,imagefttext 仍会查字体的字符映射表(cmap),缺了基本 ASCII 范围(U+0020–U+007E)就直接 fallback 到空白或方块。
为什么 imagefttext 找不到可用字体
PHP 的 GD 扩展调用 fontconfig 或直接读取字体文件,但有硬性限制:
-
imagefttext必须传入一个 绝对路径 到 .ttf/.otf 文件,不能只写字体名(如 "DejaVuSans") - 若路径指向的字体文件权限为 000、损坏、或不含 ASCII 字符映射(常见于某些精简中文字体包),GD 会静默失败,返回
false且不报错 - 很多用户把字体丢进
~/Downloads或/tmp,然后在 PHP 里写/tmp/SimHei.ttf—— 这个路径对 web server 用户(如www-data或apache)默认不可读 - CentOS/RHEL 7 自带的
fontconfig版本较老,对 .ttf 中新版 cmap 表支持差,即使fc-list能看到,GD 也可能无法加载
推荐字体路径与权限设置
必须让 web server 进程能读取字体文件。不推荐用 /usr/share/fonts/ 根目录(权限复杂),也不要用 ~/.local/share/fonts/(属于用户 home,web server 通常无权访问):
- 创建专用字体目录:
sudo mkdir -p /var/www/fonts - 复制字体进去:
sudo cp /usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf /var/www/fonts/ - 设权限:
sudo chmod 644 /var/www/fonts/DejaVuSans-Bold.ttf(关键:确保 group/o 可读) - 改属主(可选但稳妥):
sudo chown :www-data /var/www/fonts/(Ubuntu/Debian)或sudo chown :apache /var/www/fonts/(RHEL/CentOS)
imagefttext 调用时的常见错误写法
以下代码看似合理,实则高危:
imagefttext($im, 14, 0, 50, 30, $color, '/tmp/DejaVuSans.ttf', 'ABC');
问题在于:
-
/tmp目录通常挂载了noexec,nosuid,nodev,且www-data用户可能被 chroot 或受限于 systemd private tmp - 未检查
file_exists()和is_readable(),出错时只有空白图,无日志提示 - 用了普通权重字体(如
DejaVuSans.ttf)而非-Bold.ttf,在小字号下易糊成一片
正确做法:
$font = '/var/www/fonts/DejaVuSans-Bold.ttf';
if (!is_readable($font)) {
error_log("Font not readable: $font");
die('Font error');
}
imagefttext($im, 14, 0, 50, 30, $color, $font, 'ABC');
验证字体是否真能被 GD 加载
别依赖 fc-list —— 它是给桌面应用服务的;GD 走的是另一套路径解析逻辑。最直接的验证方式是写个最小 PHP 脚本:
<?php $font = '/var/www/fonts/DejaVuSans-Bold.ttf';
var_dump([
'file_exists' => file_exists($font),
'is_readable' => is_readable($font),
'filesize' => filesize($font),
'gd_support' => function_exists('imagefttext'),
]);
// 强制触发加载(不生成图,只测能否解析)
$bbox = imageftbbox(12, 0, $font, 'A');
var_dump('bbox', $bbox);
?>
如果 $bbox === false,说明 GD 拒绝该字体文件 —— 大概率是字体损坏、缺少 ASCII cmap,或用了 .ttc(字体集合)而 GD 不支持(仅部分新版支持)。此时换用 DejaVuSans-Bold.ttf 或 LiberationSans-Bold.ttf 等纯 .ttf 文件。
最易被忽略的一点:GD 默认不启用 FreeType 的高级 hinting,某些字体在小字号下笔画粘连或断裂,看起来像“字母缺失”,实际是渲染质量差。加 imagettftext 的替代方案(如使用 imagick)或预生成缓存图更可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











