function pic() { //获取get传入关键词,默认一路火系统 $kw = isset($_GET["k"]) ? $_GET["k"] : "一路火系统"; $kw = trim($kw); $kw = strip_tags($kw); //截取最多100字符 $dst_nr = mb_substr($kw, 0, 100, 'UTF-8'); // ============ 新增这一行:绘图用文本转GBK,专门修复GD FreeType乱码 ============ $drawText = iconv("UTF-8", "GBK//IGNORE", $dst_nr); // ========================================================================= $cacheDir = __DIR__.'/cache'; $cacheFile = $cacheDir.'/'.md5($dst_nr).'.jpg'; //md5仍然用原始utf8字符串,缓存key不变! $lockFile = $cacheDir.'/'.md5($dst_nr).'.lock'; //缓存已存在,直接读取磁盘静态文件返回 if(file_exists($cacheFile)){ read_static_file($cacheFile); } //锁文件超过120秒判定为僵尸锁,直接删除,防止进程被杀永久卡死 if(file_exists($lockFile) && (time()-filemtime($lockFile))>120){ @unlink($lockFile); } //创建锁文件,用于并发防竞态 $fpLock = fopen($lockFile, 'w'); if($fpLock === false){ http_response_code(503); die('系统繁忙'); } //加排他锁 flock($fpLock, LOCK_EX); //拿到锁之后二次校验缓存,防止等待锁期间别的进程已经生成图片 if(file_exists($cacheFile)){ flock($fpLock, LOCK_UN); fclose($fpLock); @unlink($lockFile); read_static_file($cacheFile); } //禁用range分段请求 if (!empty($_SERVER['HTTP_RANGE'])) { header("Accept-Ranges: none"); unset($_SERVER['HTTP_RANGE']); } // ========== 改动区域:固定500*250,棕色纯色背景,不再读取image文件夹背景图 ========== $imgWidth = 500; $imgHeight = 250; $dst = imagecreatetruecolor($imgWidth, $imgHeight); //棕色 RGB:128,74,26,可自行修改三色值调整深浅 $bgColor = imagecolorallocate($dst,128,74,26); imagefill($dst,0,0,$bgColor); // ===================================================================================== //字体文件路径校验 $font = __DIR__ . '/Alibaba-PuHuiTi-Bold.ttf'; if (!file_exists($font)) { http_response_code(404); die('字体文件缺失'); } $baseFontSize = 38; //基础字号 $minFontSize = 22; //最小字号下限 $lineGap = 10; //文本行间距 $textScale = 0.7; //文字区域占画布比例 //文字允许最大宽高范围 $allowWidth = intval($imgWidth * $textScale); $allowHeight = intval($imgHeight * $textScale); //文字白色,描边黑色半透明 $textColor = imagecolorallocatealpha($dst,255,255,255,0); $strokeColor = imagecolorallocatealpha($dst,0,0,0,40); // 这里全部换成 $drawText,不再直接用 $dst_nr list($singleW) = getTextBox($baseFontSize, $drawText, $font); //判断是否需要自动换行 if ($singleW <= $allowWidth) { $textLines = array($drawText); } else { $textLines = array(); $tempStr = ''; $strTotalLen = mb_strlen($dst_nr, 'UTF-8'); //逐字符做自动换行切割(⚠️ 重点:这里循环切割依然基于原始UTF8 $dst_nr,防止切错汉字) for ($i = 0; $i < $strTotalLen; $i++) { $char = mb_substr($dst_nr, $i, 1, 'UTF-8'); $tmpTest = iconv("UTF-8", "GBK//IGNORE", $tempStr.$char); list($tmpW) = getTextBox($baseFontSize, $tmpTest, $font); if ($tmpW > $allowWidth-5 && $tempStr !== '') { $textLines[] = iconv("UTF-8", "GBK//IGNORE", $tempStr); $tempStr = $char; } else { $tempStr .= $char; } } if ($tempStr !== '') $textLines[] = iconv("UTF-8", "GBK//IGNORE", $tempStr); } //字号自动降级,超出范围就缩小字号直到满足限制 $fontsize = $baseFontSize; while ($fontsize >= $minFontSize) { $blockH = getBlockTotalHeight($textLines,$fontsize,$font,$lineGap); $maxLineW = 0; foreach ($textLines as $line) { list($lw) = getTextBox($fontsize,$line,$font); if ($lw > $maxLineW) $maxLineW = $lw; } if ($maxLineW <= $allowWidth && $blockH <= $allowHeight) break; $fontsize--; } //缓存每行文字宽高信息 $lineInfo = array(); foreach ($textLines as $lineTxt) { list($lw,$lh) = getTextBox($fontsize,$lineTxt,$font); $lineInfo[] = array('text'=>$lineTxt,'w'=>$lw,'h'=>$lh); } //计算文本块起始Y坐标,整体垂直居中 $totalBlockH = getBlockTotalHeight($textLines,$fontsize,$font,$lineGap); $currentY = ($imgHeight-$totalBlockH)/2; foreach ($lineInfo as $info) { $x = ($imgWidth-$info['w'])/2; //水平居中 $drawY = $currentY + $info['h']; drawStrokeText($dst,$fontsize,0,$x,$drawY,$strokeColor,$textColor,$font,$info['text']); $currentY += $info['h'] + $lineGap; } //新建真彩色画布,复制背景图层 $newImg = imagecreatetruecolor($imgWidth,$imgHeight); imagecopy($newImg,$dst,0,0,0,0,$imgWidth,$imgHeight); imagedestroy($dst); //重点:直接写入磁盘文件,不经过PHP内存缓冲区,禁止内存图片直接输出 $writeOk = imagejpeg($newImg, $cacheFile,90); imagedestroy($newImg); //释放文件锁,删除锁文件 flock($fpLock, LOCK_UN); fclose($fpLock); @unlink($lockFile); //校验磁盘静态文件真正生成成功才输出 if(!$writeOk || !file_exists($cacheFile)){ http_response_code(500); die('图片写入磁盘失败'); } //读取已经落地完成的磁盘静态jpg输出 read_static_file($cacheFile); }