php GD库为图片添加文字且自动换行,水平居中

本文可以帮助你解决一下问题: 1,GD库添加文字水印无法换行 2,GD库添加文本实现居中显示 3,stil/gd-text扩展包添加文字为中文时,换行失效 GD库是[php](https://baike.baidu.com/item/php/9337)处理图形的扩展库,GD库提供了一系列用来处理图片的[API](https://baike.baidu.com/item/API),使用GD库可以处理图片,或者生成图片,也可以给图片加水印。我们常用它生成网址验证码,图片缩略图,分享海报等等。 ### 一,前置条件 1,确保你的PHP安装了GD扩展,并在php.ini启用了该扩展 2,phpinfo()查看GD扩展已开启: [![](http://static.tscgo.cn/FsrK6MhfkdzDsmB_FTPl4WG3Alqi )](http://static.tscgo.cn/FsrK6MhfkdzDsmB_FTPl4WG3Alqi ) 3,以下每段代码快最后三句均为调试代码,用以在浏览器查看当前图片。所有代码块连接在一起时,需要去除调试代码 ### 二,创建一幅图片(画布) ```` //1,添加一个画布$img,同时给这个画布添加背景图片$bg $bgPath = './images/bg.jpg'; $bgInfo = getimagesize($bgPath); $bgFun = 'imagecreatefrom'.image_type_to_extension($bgInfo[2], false); //创建一个图: 460*830 $bg = $bgFun($bgPath); $bgWidth = imagesx($bg); //背景宽度 $bgHeight = imagesy($bg); //背景高度 $img = imageCreatetruecolor($bgWidth,$bgHeight); $color = imagecolorallocate($img, 0, 0, 0); imagefill($img, 0, 0, $color); //平滑的拷贝背景图到$img imagecopyresampled($img,$bg,0,0,0,0,imagesx($bg),imagesy($bg),imagesx($bg),imagesy($bg)); //打印查看: header('Content-Type: image/png;charset=UTF-8');//必须声明 imagepng($img); exit; ```` ### 三,为图片添加水印 ```` //2,使用GD库在一张图片上面加文字: $font = realpath("./font/simhei.ttf"); //显示的文字 $text = "A class drawing multiline and aligned text on pictures"; //设置字体颜色 $black = imagecolorallocate($img, 65, 65, 65); //将ttf文字写到图片中 imagettftext($img, 16, 0, 52, 170, $black, $font, $text); //打印查看: header('Content-Type: image/png;charset=UTF-8');//必须声明 imagepng($img); exit; ```` [![](http://static.tscgo.cn/FnVyzUFrE_obZ2Lq6Co6xsnNBcc2 )](http://static.tscgo.cn/FnVyzUFrE_obZ2Lq6Co6xsnNBcc2 ) 可以看到,当文本超长时,直接被截断了,超出图片宽度的文字直接不可见。事实上,使用GD库为图片添加文字水印,它是不处理文本长度的。我们可以理解GD库只处理图片有多少个像素组成,图片长宽多少,每一个像素点A(x,y)是什么颜色。 但是我们动态生成的图片,不确定性太多,例如用户昵称,用户签名,商品名称等等这些长度都不确定,造成了我们生成的图片的文本长度不确定,如果文本超出了,那么我们需要怎样去处理超长的文本呢? 答案是,自己去处理,通过自己设置的字体大小,图片的宽度,计算出多少个字之后,就会被截断,在截断之前自己生成新的字符串写在第二行上面。 关键函数: ```` $box = imagettfbbox ( 20, 0,$font, $ccv ); 根据 $box 提供的信息适当裁剪 $ccv 使之可以放下 ```` ### 四,[stil/gd-text](https://packagist.org/packages/stil/gd-text)的引入 但是人生苦短,研究技术的小伙伴可以自己去搞一搞,但是打工人时间紧任务重,gd-text这个包已经帮我们封装好了我们想要的东西,就不建议重复造轮子了,我们直接使用。 它的使用方法类似于我们在PPT上面添加一个文本框一样:给它一个左上角坐标A(x,y),宽度,高度。一个文本框就形成了,那我们在这个框里面写东西,超出的文字就会自动换行。是不是非常方便 1,安装:使用composer安装: ```` composer require stil/gd-text ```` 2,使用: ```` <?php require __DIR__.'./vendor/autoload.php'; use GDText\Box; use GDText\Color; //1,添加一个画布$img,同时给这个画布添加背景图片$bg $bgPath = './images/bg.jpg'; $bgInfo = getimagesize($bgPath); $bgFun = 'imagecreatefrom'.image_type_to_extension($bgInfo[2], false); //创建一个图: 460*830 $bg = $bgFun($bgPath); $bgWidth = imagesx($bg); //背景宽度 $bgHeight = imagesy($bg); //背景高度 $img = imageCreatetruecolor($bgWidth,$bgHeight); $color = imagecolorallocate($img, 0, 0, 0); imagefill($img, 0, 0, $color); //平滑的拷贝背景图到$img imagecopyresampled($img,$bg,0,0,0,0,imagesx($bg),imagesy($bg),imagesx($bg),imagesy($bg)); //3,使用gd-text包来为图片添加文字: $box = new Box($img); $font = realpath("./font/simhei.ttf"); //显示的文字 $text = "A class drawing multiline and aligned text on pictures"; $box->setFontFace($font); $box->setFontColor(new Color(77, 77, 77));//字体颜色 $box->setFontSize(18);//字体大小 $box->setLineHeight(1.5);//行高 //下面为关键函数:画一个文本框: $box->setBox(52, 200, 370, 200);//文本框起始点(x,y),长宽 //第一个参数设置水平:靠左-left,居中-center,靠右-right;第二个参数设置垂直:靠左-left,居中-center,靠右-right $box->setTextAlign('left', 'top'); $box->draw($text); //再画一个,这个是水平居中对齐的文本框: $box->setBox(52, 380, 370, 200); $box->setTextAlign('center', 'top'); $box->draw($text); //打印查看: header('Content-Type: image/png;charset=UTF-8');//必须声明 imagepng($img); exit; ```` [![](http://static.tscgo.cn/FldZZHWHBx2sJfU7qseFAnGjNjQB )](http://static.tscgo.cn/FldZZHWHBx2sJfU7qseFAnGjNjQB ) 3,可以看到非常方便,但是当我们的传入的文本为中文时,奇怪的事情发生了,它并不能自动换行???(手动黑人问号脸)。网上没啥资料,自己看源代码了。 ###五,修改源代码 通过查看gd-text包的源码,我们可以发现,它是在src/Box.php里面的wrapTextWithOverflow()方法实现换行的。原来,当文本填满第一行时,它是通过空格拆分一串字符的,这很好理解,例如我们第一行八个单词排上去,还差3个字母,就要填满第一行了,而第九个单词(International)有13个字母,按照惯例我们不会把这个单词的前3个字母给拆掉的,而是整个单词都写在第二行去。所有,它内部是用explode()这个函数根据空格去拆单词,然后逐个计算在那里换行的。 但是我们的中文并不是每个字后面都有空格,explode()函数分隔符为必传,那怎么办呢? #####1,自己在每个字后面加1个空格,这样就能自动换行。但是这样字距很大,并不美观。 #####2,str_split(string,length)函数,它可以分割字符串,第二个参数为分割后每个字符的长度。 但是分完后你会发现全是是乱码,因为中文一个字占三个字节,英文字母占一个字节; 那我当要插入中文的时候,用str_split(string,3)不就好了吗?当插入英文,就用explode();完美! 并不,很多时候我们都不敢保证一段就一定是英文或者是中文,它还有可能中文夹杂着英文,那这时候,依然是乱码。 这时候,还有其他办法吗?有,我们用强大的正则表达式 #####3,使用正则表达式 这个方法可以真正完全解决问题,下面我们开始修改源代码: 事实上我们是不建议直接改扩展包里面的源码的,这样不规范,但是现在先略过 1,在src/Box.php里面添加一个方法: ```` /** * 拆分字符串为数组 * @param $str * @author shaochao 2021/3/23 21:22 * @return array|false|string[] */ protected function mb_str_split($str){ return preg_split('/(?<!^)(?!$)/u', $str ); } ```` 2,修改wrapTextWithOverflow()方法,加多一个参数,用来判断用那种方法截取字符串:英文文本用空格;中文、中英混合文本用正则 ```` /** * Splits overflowing text into array of strings. * @param string $text * @return string[] */ protected function wrapTextWithOverflow($text,$isChinese = false) { $lines = array(); // Split text explicitly into lines by \n, \r\n and \r $explicitLines = preg_split('/\n|\r\n?/', $text); foreach ($explicitLines as $line) { // Check every line if it needs to be wrapped if($isChinese){ $words = $this->mb_str_split($line); }else{ $words = explode(" ", $line); } $line = $words[0]; for ($i = 1; $i < count($words); $i++) { $box = $this->calculateBox($line." ".$words[$i]); if (($box[4]-$box[6]) >= $this->box['width']) { $lines[] = $line; $line = $words[$i]; } else { $line .= $words[$i]; } } $lines[] = $line; } return $lines; } ```` 3,修改draw()方法,加多一个参数,用来判断用那种方法截取字符串:英文文本用空格;中文、中英混合文本用正则 ```` /** * Draws the text on the picture. * @param string $text Text to draw. May contain newline characters. */ public function draw($text,$isChinese = false) { if (!isset($this->fontFace)) { throw new \InvalidArgumentException('No path to font file has been specified.'); } switch ($this->textWrapping) { case TextWrapping::NoWrap: $lines = array($text); break; case TextWrapping::WrapWithOverflow: default: $lines = $this->wrapTextWithOverflow($text,$isChinese); break; } //只改上面这几行,该方法下面的代码维持不变 ```` 4,使用draw()方法的时候,遇到中文文本,我们第二个参数传true就可以了。 ```` //4,使用gd-text包来添加一段中文文本: $text2 = '你好,世界,学无止境。你好,世界,学无止境。你好,世界,学无止境。你好,世界,学无止境。你好,世界,学无止境。'; $box->setBox(52, 550, 370, 200); $box->setTextAlign('left', 'top'); $box->draw($text2,true); ```` [![](http://static.tscgo.cn/Fgcl_Ze1gVdmo2twFA7RPpNxvSAA )](http://static.tscgo.cn/Fgcl_Ze1gVdmo2twFA7RPpNxvSAA ) 至此,使用GD库为图片添加文字且自动换行的分享到此结束,希望对你有所帮助。

评论

  1. #1

    一个程序媛 2021-12-01 12:00:08
    之前总是无法换行,现在懂了!感谢