昨天在用GD库的imagettftext在图片上写字的时候,发现写字的坐标并不是很精确的按照指定的参数的位置插入字体的,一直觉得很诡异。由于GD库中很多画图操作都是以坐标为基础的,因而就花了点时间了解了一下GD的坐标系统。
1.建立画布
$im = imagecreatetruecolor(200,200);
$im = imagecreatetruecolor(200,200);
当然也可以直接从png,gif,jpg等图片直接建立画布(或者称为资源、图片区域)。
画布建好之后,就可以在该画布的范围内(也就是说,如果你的画图操作超出了画图的范围,是无法体现在图像中的)进行画图操作了。
建好画布之后,我们做的测试步骤如下:
2.格式栅栏化画布。
通过imageline每隔20px花一条直线,作为识别的参考物。
for($start_x = 0;$start_x <= $canvas_w;$start_x += 20){
if($start_x == $font_x){
imageline($im,$start_x,0,$start_x,$width,$mixed);
}else{
imageline($im,$start_x,0,$start_x,$width,$red);
}
}
for($start_y = 0;$start_y <= $canvas_h;$start_y += 20){
if($start_y == $font_y){
imageline($im,0,$start_y,$height,$start_y,$mixed);
}else{
imageline($im,0,$start_y,$height,$start_y,$red);
}
}
格式栅栏化后的画布类似于下图:
3.在指定的位置添加文字
这里用到的函数是imagettftext
该 函数的接口: array imagettftext ( resource $image, float $size , float $angle , int $x , int $y , int $color , string $fontfile, string $text )
其中$x 和 $y 是文字的baseline的起点(也就是文字左下角的点的坐标,换言之,字体只是以baseline为参考,无法保证所有字符都在baseline之上)
imagettftext($im,$fontsize,0,$font_x,$font_y,$green ,$fontfile ,$text);
其中$font_x=40,$font_y=40
写出的字如下所示:
可以看出,字母g的下端已经超过了baseline的范围
4. 确定字符的盒装矩形范围
GD库提供了imagettfbbox函数用于返回字符串所占的盒装区域(矩形区域)的4个坐标(当然imagettftext本身也会返回字符串所占区域的坐标,只是在字符串画在图片上之后返回)
$box = imagettfbbox($fontsize,0,$fontfile,$text);
我们打印出来box的值为:
Array
(
[0] => 0
[1] => 7
[2] => 70
[3] => 7
[4] => 70
[5] => -23
[6] => 0
[7] => -23
)
注意观察其中有负值的坐标。
同样观察坐标值我们发现:没有任何一个点的坐标是字体盒子的坐标原点。
我们接着将盒子的矩形区域画出来:
$box_width = max(abs($box[2] - $box[0]),abs($box[4] - $box[6]));
$box_height = max(abs($box[7] - $box[1]),abs($box[5] - $box[3]));
$abs_y1 = abs($box[1]);
$abs_y2 = abs($box_height - $abs_y1);
$left_bottom_x = $font_x +$box[0];
$left_bottom_y = $font_y +$box[1];
$right_bottom_x = $left_bottom_x + $box_width;
$right_bottom_y = $left_bottom_y;
$right_top_x = $right_bottom_x;
$right_top_y = $right_bottom_y-$box_height;
$left_top_x = $left_bottom_x;
$left_top_y = $right_top_y;
imageline($im,$left_bottom_x,$left_bottom_y,$right_bottom_x,$right_bottom_y,$blue);
imageline($im,$right_bottom_x,$right_bottom_y,$right_top_x,$right_top_y,$blue)
imageline($im,$right_top_x,$right_top_y,$left_top_x,$left_top_y,$blue);
imageline($im,$left_top_x,$left_top_y,$left_bottom_x,$left_bottom_y,$blue);
画出的盒子的范围如下图中蓝色框所示(浅青色的横线为字体的baseline线):
观察字体所占矩形区域的位置和字体的baseline的位置关系,可以猜测,在字体所在的box中,字体的坐标大致是这样的:
同时我们也猜测:将字体画到图片上的时候,实际上是以字体所在区域的baseline的最左侧坐标与写字体指定的坐标位置重合而得到的,也就是类似于平移操作。下图示意:
写的寥寥草草,一片混沌,权当笔记。
需要注意的是:
1.imagettftext 中写入文字时,使用的是UTF-8的编码,要特别注意。
2.imagettftext写文字不支持自动换行,需要你手动去控制。
完整的测试代码如下:
$width = 200;
$height = 200;
$canvas_w = $height + 1;
$canvas_h = $height + 1;
$im = imagecreatetruecolor($canvas_w,$canvas_h);
$white = imagecolorallocate($im, 255, 255, 255);
$red = imagecolorallocate($im, 255, 0, 0);
$green = imagecolorallocate($im, 0, 255, 0);
$blue = imagecolorallocate($im, 0, 0, 255);
$mixed = imagecolorallocate($im, 0, 255, 255);
$line_color = $blue;
imagefill($im, 0, 0, $white);
$fontsize = 20;
$fontfile = "msyh.ttf";
$text = "string";
$font_x = 40;
$font_y = 40;
for($start_x = 0;$start_x <= $canvas_w;$start_x += 20){
if($start_x == $font_x){
imageline($im,$start_x,0,$start_x,$width,$mixed);
}else{
imageline($im,$start_x,0,$start_x,$width,$red);
}
}
for($start_y = 0;$start_y <= $canvas_h;$start_y += 20){
if($start_y == $font_y){
imageline($im,0,$start_y,$height,$start_y,$mixed);
}else{
imageline($im,0,$start_y,$height,$start_y,$red);
}
}
imagettftext($im ,$fontsize,0,$font_x,$font_y,$green ,$fontfile ,$text);
$box = imagettfbbox($fontsize,0,$fontfile,$text);
$left_bottom = array($box[0],$box[1]);
$right_bottom = array($box[2],$box[3]);
$right_top = array($box[4],$box[5]);
$left_top = array($box[6],$box[7]);
$box_width = max(abs($box[2] - $box[0]),abs($box[4] - $box[6]));
$box_height = max(abs($box[7] - $box[1]),abs($box[5] - $box[3]));
$abs_y1 = abs($box[1]);
$abs_y2 = abs($box_height - $abs_y1);
$left_bottom_x = $font_x +$box[0];
$left_bottom_y = $font_y +$box[1];
$right_bottom_x = $left_bottom_x + $box_width;
$right_bottom_y = $left_bottom_y;
$right_top_x = $right_bottom_x;
$right_top_y = $right_bottom_y-$box_height;
$left_top_x = $left_bottom_x;
$left_top_y = $right_top_y;
imageline($im,$left_bottom_x,$left_bottom_y,$right_bottom_x,$right_bottom_y,$blue);
imageline($im,$right_bottom_x,$right_bottom_y,$right_top_x,$right_top_y,$blue);
imageline($im,$right_top_x,$right_top_y,$left_top_x,$left_top_y,$blue);
imageline($im,$left_top_x,$left_top_y,$left_bottom_x,$left_bottom_y,$blue);
imagejpeg($im,'res.jpg',100);
imagedestroy($im);