必须用 imagettftext 才支持中文字体,其第4个参数需绝对路径且PHP进程有读权限;中文须UTF-8编码;需关闭alpha混合并启用alpha保存;y坐标应基于imagettfbbox动态计算;注意内存与超时限制。

gd_imagestring 用错字体路径会直接报错
PHP 的 imagestring 不支持中文字体,硬塞 TTF 路径会报 Warning: imagestring(): Invalid font identifier。真要打文字水印,必须用 imagettftext,它才认 .ttf 文件。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
-
imagettftext第 4 个参数是字体文件绝对路径,相对路径大概率失败,尤其在 CLI 或 Nginx FastCGI 下 - 字体文件需 PHP 进程有读权限,常见坑:上传到 web 目录但没开读,或放在
/var/www/fonts/却忘了加 SELinux 上下文 - 中文字符必须 UTF-8 编码,如果源字符串是 GBK,得先
mb_convert_encoding($text, 'UTF-8', 'GBK') - 示例:
imagettftext($img, 16, 0, 50, 50, $white, '/usr/share/fonts/dejavu/DejaVuSans.ttf', '© 2024');
透明度控制靠 alpha 合成,不是简单改颜色
很多人以为把水印文字颜色设成半透明(比如 imagecolorallocatealpha($img, 0, 0, 0, 127))就能“淡一点”,其实这只是让文字自身带透明通道;真正叠加时若没开启 alpha 合成,GIF/PNG 会变黑块,JPEG 则完全不透明。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 调用
imagealphablending($img, false)关闭混合模式(否则 alpha 值无效) - 再调用
imagesavealpha($img, true)保留完整 alpha 信息(尤其对 PNG 输出必需) - 文字颜色的第 4 个参数是 0–127,0 最不透明,127 最透明;别和 CSS 的 opacity 混淆
- 如果目标图是 JPEG,
imagesavealpha无效果,此时只能靠降低文字 RGB 值 + 调整 alpha 颜色来模拟灰度半透
坐标计算容易偏移,尤其中文字体 baseline 不一致
imagettftext 的 y 坐标不是文字顶部,而是基线(baseline)位置,不同字体的 ascent/descent 差异大,同一字号下“微软雅黑”和“Noto Sans CJK”垂直位置能差 10px+,导致水印忽高忽低。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
imagettfbbox先测尺寸:$bbox = imagettfbbox(16, 0, $font, $text);,返回数组里$bbox[5]是基线到底边距离,$bbox[7]是顶边到基线距离 - 想让文字垂直居中于图片右下角,别直接写
$height - 10,应算:$y = $height - ($bbox[5] - $bbox[7]) / 2 - 避免用固定偏移量硬编码坐标,尤其多语言混排时,英文、数字、中文宽度差异大,
strlen完全不可信,一律用imagettfbbox动态算
批量处理时 memory_limit 和 timeout 是隐形瓶颈
每张图加载 + 绘制 + 输出,GD 会吃掉几 MB 内存,10 张 2000×3000 的 PNG 就可能爆 Allowed memory size of ... bytes exhausted;而生成带抗锯齿的文字水印比简单 imagecopy 慢 3–5 倍,超时也常发生。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 处理前加
ini_set('memory_limit', '256M');,但别设太高,防 OOM 杀进程 - 用
set_time_limit(0)解除脚本超时,但线上环境建议配合队列,别在 Web 请求里干这事 - 大图优先用
imagecreatefromjpeg而非imagecreatefromstring(file_get_contents(...)),后者多一次内存拷贝 - 水印画完立刻
imagedestroy($img),别等脚本结束——GD 资源不自动释放
实际做下来,最耗时间的往往不是写代码,而是调那个 y 坐标和字体路径权限。字体文件放哪、Apache/Nginx/CLI 用户是否都能读、中文字串有没有悄悄被转码——这些细节卡住的人,比不会写 imagettftext 的还多。











