GD库完全可胜任小程序分享图生成,无需imagick;需确保GD支持PNG/JPEG、用imagettftext渲染中文、imagecopyresampled缩放防失真、像素级文字换行、直出PNG流而非存文件,并注意字体商用授权。

用 GD 库绘制小程序分享图,别碰 imagick
PHP 生成小程序分享图本质是「在图片上叠加文字、二维码、头像等元素」,GD 库完全够用,且部署简单、无额外依赖。很多项目强行上 imagick,结果在 Docker 或共享主机里报 Class 'Imagick' not found,纯属增加运维负担。
关键点:确保 PHP 编译时启用了 --with-gd(绝大多数环境默认开启),并确认支持 png 和 jpeg:
var_dump(gd_info()['PNG Support'] && gd_info()['JPEG Support']); // 必须返回 true
- 文字渲染优先用
imagettftext(),别用imagestring()——后者不支持中文字体且字号固定 - 字体文件路径必须是绝对路径(
__DIR__ . '/fonts/msyh.ttc'),相对路径在 CLI 或不同入口下极易失效 - 中文乱码?不是编码问题,是字体不包含对应字形——换全量中文字体(如
simhei.ttf或 NotoSansCJK),别用系统自带的简版
imagecopyresampled() 合成头像和二维码时缩放失真怎么办
小程序分享图常需把用户头像裁成圆形、把二维码缩放到指定尺寸。直接用 imagecopyresized() 会模糊;用 imagecopyresampled() 是正确选择,但必须注意源图尺寸和目标区域比例一致,否则拉伸变形。
例如把 300×300 头像缩到 120×120 圆形区域:
$dst = imagecreatetruecolor(120, 120);
imagealphablending($dst, false);
imagesavealpha($dst, true);
imagecopyresampled($dst, $src, 0, 0, 0, 0, 120, 120, 300, 300);
立即学习“PHP免费学习笔记(深入)”;
- 务必调用
imagealphablending()和imagesavealpha(),否则 PNG 透明背景变黑 - 圆形裁剪不能靠 CSS 或前端遮罩——后端要真画圆:先创建透明图层,用
imagefilledellipse()画白底圆,再用imagecopy()把头像贴上去,最后用imagealphablending()混合 - 二维码建议用
endroid/qr-code生成 PNG 资源后直接imagecreatefromstring(file_get_contents($qrPath))加载,别用 base64 解码,容易因换行符出错
文字自动换行 + 行高控制,wordwrap() 不管用
wordwrap() 只按字符数切分,对中英文混排、全角标点、emoji 完全无效。实际要用「像素宽度判定」:逐字测量 imagettfbbox() 宽度,累计超限则换行。
示例逻辑片段:
$words = preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY);
$line = '';
$lines = [];
foreach ($words as $char) {
$box = imagettfbbox($font_size, 0, $font_path, $line . $char);
$width = $box[2] - $box[0];
if ($width > $max_width) {
$lines[] = $line;
$line = $char;
} else {
$line .= $char;
}
}
if ($line) $lines[] = $line;
- 别用
mb_substr()分割字符串——某些字体中 emoji 占多个码位,preg_split('//u')才真正按 Unicode 字符切 - 行高不能硬写
$y += 30,要根据imagettfbbox()的[1]和[7]计算实际高度差 - 多行文字居中?每行单独计算起始
$x:用imagettfbbox()得到该行宽度,再套公式$x = ($canvas_width - $line_width) / 2
生成后直接输出给小程序,别存文件
小程序 canvas.toTempFilePath 要求图片 URL 可直链访问,但后端生成图若存本地再返回链接,会引发并发写入冲突、磁盘爆满、缓存不一致等问题。正确做法是设置响应头直出 PNG 流:
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
exit;
- 务必加
exit,防止后续代码输出空格或 BOM 破坏二进制流 - 不要用
file_put_contents()写临时文件再readfile()——多一次 IO,还可能因权限或路径问题失败 - 如果需要 CDN 缓存,由 Nginx/Apache 做响应头
Cache-Control控制,PHP 层只管生成和输出
最易被忽略的是字体授权和版权——商用项目别随便下“免费字体”,很多所谓“免费”仅限个人使用,小程序上线被投诉字体侵权已有先例。宁可花几百买正版商用授权,也别在上线前两天才发现字体不能用。











