
ThinkPHP 的二维码巡检图里imagettftext 写入的中文标题突然变成一排小方块。这种乱码通常会被人怀疑是 PHP 的 GD 库没编译好或者是文件编码不是 UTF-8但实际上 InfoCodePhoto 的 generate_photo 里最容易被忽略的是 font_file 这个键。为了让 Codex 直接参与排查我先在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建了 API Key把 Codex 的模型通道切到 TaoTokenBase URL 填 https://taotoken.net/api拿到 Key 后它就能通过 TaoToken 走通模型通道专心查 GD 文本绘制的问题。1. 乱码在 InfoCodePhoto 的 imagettftext 调用链上1.1 generate_photo 里中文标题经过的绘制路径InfoCodePhoto 生成一张巡检图的流程并不复杂imagecreatetruecolor 创建 1064 x 639 的画布imagefill 填充白色底deal_background 画顶部深蓝色区域deal_logo 把 logo 和二维码原样贴上去最后才是 imagettftext 写标题、提示文字和表格数据。问题往往出在最后一步因为 imagettftext 需要一个真实的 TTF 字体文件来渲染字形。判断标准很简单如果「杆子光伏安全定点巡检二维码」这一行变成方块而图片里的英文字母和数字还正常说明 imagettftext 本身执行成功问题出在字体文件缺少中文字形。如果整行中文完全消失那大概率是 font_file 路径错误GD 库加载字体失败后直接放弃了这行文本。这两种情况的原因和修法完全不同不能混在一起处理。1.2 配置数组里 font_file 的路径不一致是最大嫌疑InfoCodePhoto 的默认配置里写了两处字体路径。类属性 self::$data[font_file] 是ROOT_PATH . public\\ttf\\ . abd.ttf而注释示例里写的是ROOT_PATH . abd.ttf。如果 generate_photo 被调用时没有通过 $origin_data 显式传入 font_file程序最终用的是类属性里拼出来的那一条。abd.ttf 这个文件名看起来像项目自己放的定制字体它未必包含 CJK 统一表意文字的字形。很多开源字体只覆盖拉丁字符集传到 imagettftext 后中文就映射不到任何字形输出图片上自然是豆腐块。另一个隐蔽问题是目录分隔符开发环境是 Windows 时反斜杠没问题部署到 Linux 后public\ttf\abd.ttf会被当成一个非法路径file_exists 直接返回 false。2. 排查前先让 Codex 走 TaoToken 模型通道2.1 从 TaoToken 官网创建 API Key要请 Codex 加入排查先得让它能稳定调用到模型。打开 TaoToken 完成注册在控制台创建一个 API Key创建后复制并保存为 YOUR_API_KEY。这个 Key 是在后面的 Codex 配置里作为环境变量使用的不会写进项目代码。提醒一下落地页和接口地址是两码事。落地页 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 用来注册账号、创建 Key、查看模型广场和用量真正要填进 Codex config.toml 的 Base URL 是 https://taotoken.net/api末尾没有 /v1。两者混用会导致 Codex 把网页地址当作 API 入口报出 404 或连接失败。2.2 在 ~/.codex/config.toml 里写入 TaoToken 供应商Codex 的自定义模型供应商写在 ~/.codex/config.toml不要把它和 Claude Code 的 settings.json 混为一谈。Claude Code 用 ANTHROPIC_BASE_URL 环境变量Codex 认的是 config.toml 里的 model_provider 配置。打开或创建这个文件写入下面内容model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat其中 YOUR_MODEL_ID 不能靠自己猜打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场复制实际可用的模型 ID。env_key 指定的 TAOTOKEN_API_KEY 是 Codex 读取环境变量的名字所以还要在终端里导出一次export TAOTOKEN_API_KEYYOUR_API_KEY把 YOUR_API_KEY 换成你在 TaoToken 创建的那串真实 Key。设置完成后Codex 发起的模型请求会通过 TaoToken 的兼容通道走通这一步是为了保证后续排查对话不被连接错误打断。2.3 先让 Codex 跑一句与乱码无关的验证不要一上来就把整个 InfoCodePhoto 类贴给 Codex先确认模型通道本身是通的。在 Codex 会话里发起一句与业务无关的提问比如询问 PHP 的 imagettftext 函数签名或 GD 库版本差异。如果 Codex 能正常回答说明 Key、Base URL、模型 ID 这条链路没有问题再进入代码排查环节。3. 让 Codex 按这三处逻辑核对 generate_photo 中文绘制3.1 font_file 的绝对路径与文件真实存在性把 generate_photo 方法里和字体加载相关的代码摘出来让 Codex 检查 ROOT_PATH 在项目里是怎么定义的。重点看拼出来的完整路径到底是ROOT_PATH . abd.ttf还是ROOT_PATH . public/ttf/abd.ttf并对比服务器上真实文件所在目录。与其在对话里反复猜不如直接在本地命令行执行一句php -r $font __DIR__ . /public/ttf/abd.ttf; var_dump(file_exists($font), is_readable($font), filesize($font));把执行结果贴给 Codex。file_exists 为 false 时路径问题坐实返回 true 但文件只有几十 KB那这个字体大概率不是中文字体需要换成覆盖 CJK 字形的开源字体。这时让 Codex 给出一个下载量较大的中文字体文件名不要继续用 abd.ttf 硬扛。3.2 imagettftext 参数里字体、坐标与颜色的覆盖关系generate_photo 方法开头有一段很关键的覆盖逻辑foreach 遍历 self::$data只要 $origin_data 里存在同名键就用新值替换默认值。这本来是为了灵活传参但也带来了隐患。如果调用方在 $origin_data 里传入了 font_file 且值是空字符串generate_photo 里的英文提示文字会照常显示中文字符则全部静默失败。让 Codex 对着这段代码检查 imagettftext 的四个关键调用点标题绘制用 title_font_size 和固定坐标 (235, 102)温馨提示用 15 号字固定在 (680, 610)数据部分用 data_font_size 配合 $num 和 $count 计算坐标。如果标题和提示文字都乱码而数据区域只有部分乱码说明问题集中在分行截取而不是字体本身。这个判断可以让 Codex 直接输出结论再决定改字体还是改截取逻辑。3.3 cn_row_substr 按字节分行是否切断中文cn_row_substr 是 InfoCodePhoto 里专门做文本分行的函数它按字节计算每行长度默认每行显示 line_number 个字每个中文字符占 3 字节。函数内部用 mg_cn_substr 以 3 字节为单位截取逻辑上对纯中文没问题但如果标题或表格值里混入了全角空格、生僻字或 emoji这些字符可能占 3 到 4 字节按固定 3 字节一截就会把 UTF-8 字符从中间劈开生成图片上出现单个方框或问号。让 Codex 特别检查设备编码这一行代码里通过strpos($val[title], 设备编码)把 line_number 改成 8比默认的 12 更短。如果设备编码是「字母 数字 横线」的混合结构每个字符占 1 字节那么一行 8 个字符在图片上的实际宽度远小于中文 8 个字符的宽度视觉上会跟其他行对不齐。这时需要让 Codex 基于 data_col_distance 和 data_start_x 算出每列的字符宽度上限再反过来校验 line_number 是否合理。4. 用本地 PHP CLI 重跑验证图片和调用记录4.1 重跑 generate_photo 看输出图片修改完 font_file 路径或换掉字体后不要直接在浏览器里调接口用本地 PHP CLI 跑一次更干净。因为浏览器输出图片时会出现 Content-Type 和调试信息混在一起的问题CLI 环境能直接看到 warning 和 fatal error。执行php /path/to/generate_photo_test.php测试脚本里调用 generate_photo传入巡检标题、表格数组、二维码路径和输出图片路径然后检查输出图片是否存在。Codex 只能帮你生成和解释这段代码实际执行要由你在本地完成再把执行结果和报错信息贴回对话让它继续分析。重复两三轮后图片上的中文标题从方块变成正常文字说明 imagettftext 已经正确读到了中文字形。4.2 回 TaoToken 控制台看 Codex 通道的调用记录整个排查过程里Codex 每次提问都会通过 TaoToken 通道产生一次模型调用。回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的用量页面能看到刚才几轮诊断对话的请求次数和消耗情况这比在多个 Key 之间来回切换更容易核算成本。如果你把 font_file 换成服务器上真实存在的开源中文字体后标题仍然出现个别方框那就把新字体在本地生成的图片路径和 generate_photo 里对应的那几行贴给 Codex让它按字节偏移量反推是哪个字符被 mg_cn_substr 切断。修完这一版记得把 public/ttf 目录下的字体文件一起纳入版本库避免新环境部署时再次出现同样的字体缺失问题。