字符横向间距由imagettftext()的$x参数逐字动态计算决定,需用imagettfbbox()测宽并校正角度影响,$space仅用于画布宽度计算,不控制实际绘制间距。

imagettftext 的 $x 参数决定字符横向间距
PHP 生成验证码时,字符间距不是靠 CSS 或全局配置控制的,而是由每次调用 imagettftext() 时传入的 $x(横坐标)参数逐字累加决定的。你写死一个步长,就等于设定了“平均间距”。
常见错误是直接用固定像素偏移(比如每次 +20),结果在字体粗细不均、字符宽度差异大(如 “1” vs “W”)时出现视觉拥挤或断裂。
- 推荐做法:用
imagettfbbox()先测每个字符实际宽度,再动态计算下一个$x - 示例中常漏掉字体角度影响——如果用了
$angle非 0,imagettfbbox()返回的宽度值需做 cos/sin 校正,否则偏移量会偏斜 - 若用内置位图字体(
imagestring()),间距只能靠手动插入空格或调整$x,且无宽度测量函数可用
验证码类里 $space 属性的实际作用范围
很多开源验证码类(如早期 ThinkPHP 自带类)暴露了 $space 成员变量,但它**只参与画布总宽计算和字符起始位置偏移,并不改变单个字符的绘制逻辑**。
也就是说,即使你设 $space = 30,只要 imagettftext() 的 $x 每次只加 15,字符照样重叠;反之设 $space = 5,但代码里硬写 $x += 25,照样撑开。
立即学习“PHP免费学习笔记(深入)”;
-
$space通常只用于初始化$this->width:比如$this->width = $this->size * $num + $this->space * ($num + 1) - 它不干涉绘图循环内部的坐标更新逻辑——那部分得看类里
for循环里怎么算$x - 某些类把
$space当作“最小间隔”,再叠加随机抖动(如 ±3px),这种设计才真正影响视觉密度
干扰线/噪点会掩盖真实间距,调试时先关掉
当你调大间距后发现字符反而“粘连”或边缘模糊,大概率不是间距问题,而是干扰元素在视觉上覆盖了留白区域。
特别是正弦干扰线或浅灰噪点,容易让相邻字符的空白区被填满,造成“间距变小”的错觉。
- 调试阶段务必注释掉
imageline()和imagesetpixel()相关代码 - 用纯色背景(如
imagecolorallocate($im, 255, 255, 255))+ 黑字输出,确认间距是否真的生效 - 导出图片到本地用取色器检查像素——别信浏览器缩放后的观感,要数实际空白列数
字体本身有内边距,TTF 文件差异比代码影响更大
同一段 PHP 代码,换一个 TTF 文件,字符左右留白可能差 4–6 像素。这是因为字体文件自带 glyph bounding box 和 side bearings(侧边距)信息,GD 库读取时会原样继承。
你设 imagettftext($im, 24, 0, 10, 30, $color, $font, '1'),那个 “1” 实际占位宽度,取决于字体作者怎么定义它的左/右空白。
- 对比测试:用
arial.ttf和simsun.ttc同样参数渲染,截取单字符区域看像素宽度 - 安全选择:优先用无衬线、等宽倾向的字体(如 DejaVuSans、LiberationSans),避免 script 类字体
- 终极方案:改用
imagestring()+ 字体编号 5,虽然粗糙但宽度绝对可控——只是不能抗锯齿
调整验证码字符间距这件事,表面是改一个数字,背后牵扯的是字体度量、GD 渲染路径、干扰策略三者的耦合。最容易被忽略的,是以为设了 $space 就万事大吉,却没去翻类里真正的绘图循环怎么算 $x。



















