7

Does anybody have a function that draws a ttf string (imagettftext) with specified letter spacing?

I cannot find any build-in GD function so I think that it should be done letter by letter adding some constant width.

Maybe someone have such function already :)

ps. the best font will be arial.ttf

radzi0_0
  • 1,250
  • 2
  • 14
  • 24

6 Answers6

27
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{        
    if ($spacing == 0)
    {
        imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
    }
    else
    {
        $temp_x = $x;
        for ($i = 0; $i < strlen($text); $i++)
        {
            $bbox = imagettftext($image, $size, $angle, $temp_x, $y, $color, $font, $text[$i]);
            $temp_x += $spacing + ($bbox[2] - $bbox[0]);
        }
    }
}

and the call:

imagettftextSp($image, 30, 0, 30, 30, $black, 'arial.ttf', $text, 23);

Function parameters order meets standard imagettftext parameters order, and the last parameter is optional $spacing parameter. If not set or the passed value is 0, the kerning / letter spacing is not set.

radzi0_0
  • 1,250
  • 2
  • 14
  • 24
  • 1
    replace $text[$i] with mb_substr($text, $i, 1) to overcome trouble with mulitibyte characters – Juergen Schulze Mar 06 '18 at 21:44
  • While I appreciate the efforts, I don't think this function is robust enough. It mangles the letter spacing when its iterated through like that. See for yourself. Compare a spacing of 0 with a spacing of 0.1, you'll notice the 0.1 kerning ends up being all over the place. – 1owk3y Oct 07 '19 at 01:56
  • For future reference, there's a better answer to this problem [here](https://stackoverflow.com/a/65254013/2288578). – Clonkex Aug 10 '23 at 02:23
12

I know this was answered a while back, but I needed a solution that had letter spacing and maintained the angular offsets.

I modified radzi's code to accomplish this:

function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{        
    if ($spacing == 0)
    {
        imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
    }
    else
    {
        $temp_x = $x;
        $temp_y = $y;
        for ($i = 0; $i < strlen($text); $i++)
        {
            imagettftext($image, $size, $angle, $temp_x, $temp_y, $color, $font, $text[$i]);
            $bbox = imagettfbbox($size, 0, $font, $text[$i]);
            $temp_x += cos(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
            $temp_y -= sin(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
        }
    }
}
Pidalia
  • 121
  • 1
  • 2
9

Just to complete pidalia's answer (which is the best) to avoid some trouble with special char (like "é" or "à")

static function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0) {
    if ($spacing == 0) {
        imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
    } else {
        $temp_x = $x;
        $temp_y = $y;
        //to avoid special char problems
        $char_array = preg_split('//u',$text, -1, PREG_SPLIT_NO_EMPTY);
        foreach($char_array as $char) {
            imagettftext($image, $size, $angle, $temp_x, $temp_y, $color, $font, $char);
            $bbox = imagettfbbox($size, 0, $font, $char);
            $temp_x += cos(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
            $temp_y -= sin(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
        }
    }
}
Samsquanch
  • 8,866
  • 12
  • 50
  • 89
Lliw
  • 407
  • 3
  • 6
2

GD doesn't support kerning, so you'll have to do it manually. Personally, I wrote a function that would write each letter separately. I can't find it right now, but it's something along the lines of:

function drawText(&$image, $text, $fgColor, $font, $fgColor, 
                   $fontSize = 14, $kerning = 0, $x = 0, $y = 0) {
    $letters = explode('', $text);

    foreach ($letters as $n => $letter) {
        $bbox = imagettftext($image, $fontSize, 0, $x, $y, $fgColor, $font, $letter);
        $x += $bbox[2] + $kerning;
    }
}
nikc.org
  • 16,462
  • 6
  • 50
  • 83
1

I have tried all the answers here and none seems to do a decent job. If you draw the bounding box, this is what happens:

enter image description here

Clearly they are not evenly spaced. It appears that the bounding box, returned by imagettftext() and imagettfbbox(), only tells you what is drawn. This might seem enough, but it isn't, because of font kerning. This means that even when you say a letter should be draw at (x,y), that that will not be one of the coordinates of the bounding box. A correction for kerning is needed. enter image description here

I botched together this code for horizontal text:

function getBBoxW($bBox)
{
  return $bBox[2] - $bBox[0];
}


function imagettftextSpacing($image, $size, $x, $y, $color, $font, $text, $spacing = 0)
{
    $testStr = 'test';
    $testW   = getBBoxW(imagettfbbox($size, 0, $font, $testStr));
    foreach (mb_str_split($text) as $char)
    {
        $fullBox = imagettfbbox($size, 0, $font, $char . $testStr);
        imagettftext($image, $size, 0, $x - $fullBox[0], $y, $color, $font, $char);
        $x += $spacing + getBBoxW($fullBox) - $testW;
    }
}

The results are much better. Note that the $testStr can have any value.

Here's an example of the result, the first line is normal text, the second line has a negative spacing:

enter image description here enter image description here

KIKO Software
  • 15,283
  • 3
  • 18
  • 33
-1

Try this function:

$image = imagecreatetruecolor(500,200);
$text = "Text to print";
$text_color=imagecolorallocate($image,255,255,255);
$font_size = 18;
$space = 8;
$font = "path_to_font/arial.ttf";
$x=20;
$y=20;
for ($i = 0; $i <strlen($text); $i++){
   $arr = imagettftext ($image, $font_size,0, $x, $y, $text_color, $font, $text{$i});
   $x = $arr[4]+$space;
}
imagejpeg($image);
destroyimage($image);
VIK
  • 689
  • 2
  • 9
  • 20