用php的GD库生成海报是很常用的,比如给设计好的海报背景动态更换二维码、文本内容,生成商品分享导购图等。
效果:
代码如下:
<?php namespace wbs\sdk; /** * @email tech@alipay168.cn * @author 小韦 * @link https://www.alipay168.cn * @Date: 2020/2/20 13:50 */ class Poster { //图片质量,0~100,default=75 private $quality = 100; //字体路径 //private static $font_path = 'C:/Windows/Fonts/simfang.ttf'; private $font_path = ''; //背景图片 private $backgroud_img = ''; //二维码/水印图片 private $code_img = ''; public function __construct($config = []) { if (!empty($config['font_path'])) { $this->font_path = $config['font_path']; } if (!empty($config['backgroud_img'])) { $this->backgroud_img = $config['backgroud_img']; } if (!empty($config['code_img'])) { $this->code_img = $config['code_img']; } } /** * 开始制作 * 一个原则:在某张背景图上面添加图片、文字等 * @param $data * @return mixed */ public function start($data) { if (!empty($data['quality'])) { $this->quality = $data['quality']; } if (!empty($data['qrcode'])) { $this->code_img = $data['qrcode']; } if (!empty($data['bg_img'])) { $this->backgroud_img = $data['bg_img']; } //输入文件的路径和名称 $output_file = $data['output_file']; //商品图片 $goods_img = $data['goods_img']; if (empty($this->backgroud_img)) { $bgH = 700; $bgW = 375; $image = imagecreatetruecolor($bgW, $bgH); $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); imagejpeg($image, $output_file); imagedestroy($image); $this->backgroud_img = $output_file; // 添加二维码 $this->addPic($this->backgroud_img, $this->code_img, 100, 100, 275, 400, $output_file); //添加文字描述 $this->addWord('长按识别', 280, 520, 16, 'grey1', $output_file); } // 添加产品 $this->addPic($this->backgroud_img, $goods_img, 375, 400, 0, 0, $output_file); // 添加产品描述,对描述进行分行 $theTitle = $this->cn_row_substr($data['title'], 2, 11); //左右距离 $this->addWord($theTitle[1], 10, 425, 16, 'black', $output_file); $this->addWord($theTitle[2], 10, 450, 16, 'black', $output_file); // 文字1 $this->addWord('原价' . $data['price_member'], 20, 480, 16, 'black', $output_file); // 文字2 $this->addWord('————', 65, 480, 16, 'red', $output_file, 3); $this->addWord('————', 65, 481, 16, 'red', $output_file, 3); $this->addWord('————', 65, 482, 16, 'red', $output_file, 3); // 文字3 $this->addWord('现价' . $data['price_market'], 20, 510, 18, 'red', $output_file); //商品小图 todo 动态化来自$data 数组 if (!empty($data['imgs'])) { $w = $h = 100;//小图宽高 $dst_y = 538;//距离海报顶部的距离 $margin_left = 20;//左边距 foreach ($data['imgs'] as $k => $thumb) { //计算x轴坐标 if ($k == 0) { $dst_x = $margin_left; } else { $dst_x = ($k + 1) * $margin_left + $w * $k; } //只取3张 if ($k >= 3) { break; } //添加小图 $this->addPic($output_file, $this->code_img, $w, $h, $dst_x, $dst_y, $output_file); } } // 文字3 $this->addWord('一个有血性的程序猿520单身快乐', 10, 670, 16, 'black', $output_file); return $output_file; } /** * 添加图片 * @param $path_base string 原图 * @param $path_logo string 添加图 * @param $imgWidth int 添加图宽 * @param $imgHeight int 添加图高 * @param $dst_x int 在原图宽x处添加 * @param $dst_y int 在原图高y处添加 * @param $output_file string 生成图 * return resource 返回图片image资源 */ private function addPic($path_base, $path_logo, $imgWidth, $imgHeight, $dst_x, $dst_y, $output_file) { if (empty($path_logo)) { exit('图片不存在:' . $path_logo); } elseif (empty($path_base)) { exit('背景图不存在:' . $path_base); } $image_base = $this->ImgInfo($path_base); $image_logo = $this->ImgInfo($path_logo); imagecopyresampled($image_base, $image_logo, $dst_x, $dst_y, 0, 0, $imgWidth, $imgHeight, imagesx($image_logo), imagesy($image_logo)); // 生成一个合并后的新图 imagejpeg($image_base, $output_file, $this->quality); // 载入新图像资源 $new_pic = imagecreatefromjpeg($output_file); // 生成写入文字的的新图 imagejpeg($new_pic, $output_file, $this->quality); imagedestroy($new_pic); } /** * 从图片文件创建Image资源 * @param $img string 图片文件,支持url * @return bool|resource 成功返回图片image资源,失败返回false */ private function ImgInfo($img) { if (preg_match('/http(s)?:\/\//', $img)) { $fileSuffix = $this->getNetworkImgType($img); } else { $fileSuffix = pathinfo($img, PATHINFO_EXTENSION); } if (!$fileSuffix) { return false; } switch ($fileSuffix) { case 'jpeg': case 'jpg': $theImage = @imagecreatefromjpeg($img); break; case 'png': $theImage = imagecreatefromstring(file_get_contents($img)); //imagecreatefrompng($img); break; case 'gif': $theImage = @imagecreatefromgif($img); break; default: $theImage = @imagecreatefromstring(file_get_contents($img)); break; } return $theImage; } /** * 获取网络图片类型 * @param $url string 网络图片url,支持不带后缀名url * @return bool */ private function getNetworkImgType($url) { $ch = curl_init(); //初始化curl curl_setopt($ch, CURLOPT_URL, $url); //设置需要获取的URL curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);//设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, 3); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //支持https curl_exec($ch);//执行curl会话 $http_code = curl_getinfo($ch);//获取curl连接资源句柄信息 curl_close($ch);//关闭资源连接 if ($http_code['http_code'] == 200) { $theImgType = explode('/', $http_code['content_type']); if ($theImgType[0] == 'image') { return $theImgType[1]; } else { return false; } } else { return false; } } /** * 分行连续截取字符串 * @param $str string 需要截取的字符串,UTF-8 * @param int $row 截取的行数 * @param int $number 每行截取的字数,中文长度 * @param bool $suffix 最后行是否添加‘...’后缀 * @return array 返回数组共$row个元素,下标1到$row */ private function cn_row_substr($str, $row = 1, $number = 10, $suffix = true) { $result = array(); for ($r = 1; $r <= $row; $r++) { $result[$r] = ''; } $str = trim($str); if (!$str) return $result; $theStrlen = strlen($str); //每行实际字节长度 $oneRowNum = $number * 3; for ($r = 1; $r <= $row; $r++) { if ($r == $row and $theStrlen > $r * $oneRowNum and $suffix) { $result[$r] = $this->mg_cn_substr($str, $oneRowNum - 6, ($r - 1) * $oneRowNum) . '...'; } else { $result[$r] = $this->mg_cn_substr($str, $oneRowNum, ($r - 1) * $oneRowNum); } if ($theStrlen < $r * $oneRowNum) break; } return $result; } /** * 按字节截取utf-8字符串 * 识别汉字全角符号,全角中文3个字节,半角英文1个字节 * @param $str string 需要切取的字符串 * @param $len int 截取长度[字节] * @param int $start 截取开始位置,默认0 * @return string */ private function mg_cn_substr($str, $len, $start = 0) { $q_str = ''; $q_strlen = ($start + $len) > strlen($str) ? strlen($str) : ($start + $len); //如果start不为起始位置,若起始位置为乱码就按照UTF-8编码获取新start if ($start and json_encode(substr($str, $start, 1)) === false) { for ($a = 0; $a < 3; $a++) { $new_start = $start + $a; $m_str = substr($str, $new_start, 3); if (json_encode($m_str) !== false) { $start = $new_start; break; } } } //切取内容 for ($i = $start; $i < $q_strlen; $i++) { //ord()函数取得substr()的第一个字符的ASCII码,如果大于0xa0的话则是中文字符 if (ord(substr($str, $i, 1)) > 0xa0) { $q_str .= substr($str, $i, 3); $i += 2; } else { $q_str .= substr($str, $i, 1); } } return $q_str; } /** * 添加文字 * @param $str string 要添加的文字 * @param $posX int 在宽x处添加 * @param $poxY int 在高y处添加 * @param $font int 字体大小 * @param $color string 字体颜色 * @param $output_file string 生成图 * @param $tilt int 文字倾斜度 * return resource 返回图片image资源 */ private function addWord($str, $posX, $poxY, $font, $color, $output_file, $tilt = 0) { $ori_img = $output_file; //原图 $new_img = $output_file; //生成水印后的图片 $s_original = $this->ImgInfo($ori_img); $ImgColor = [ //为一幅图像分配颜色 'black' => imagecolorallocate($s_original, 0, 0, 0), 'red' => imagecolorallocate($s_original, 255, 0, 0), 'grey1' => imagecolorallocate($s_original, 31, 31, 18), ]; imagettftext($s_original, $font, $tilt, $posX, $poxY, $ImgColor[$color], $this->font_path, $str); imagejpeg($s_original, $new_img, $this->quality); //生成新的图片(jpg格式) imagedestroy($s_original); } } //实例操作: require ("Poster.php"); $data = [ 'title' => '一个不算长得不算太丑有信仰的代码写手...', 'price_market' => '¥0.99', 'price_member' => '¥99.99', 'goods_img' => 'https://store.mp.video.tencent-cloud.com/161/20304/snscosdownload/SH/reserved/6066d1500000815100000000b61f9d09000000a000004f50', 'output_file' => __DIR__.'/bg1222.jpg', 'quality' => 100, 'qrcode' => __DIR__.'/qr.jpg', //'bg_img' => __DIR__.'/bg1.png', 'imgs'=>[ __DIR__.'/qr.jpg', __DIR__.'/qr.jpg', __DIR__.'/qr.jpg', ] ]; $set = [ 'font_path' => __DIR__.'/simkai.ttf', ]; $poster = new \wbs\sdk\Poster($set); $newImg = $poster->start($data); echo "<img style='width: 100%' src='bg1222.jpg' />";
注意点:
①字体路径 ②字体模糊的问题处理③目录权限