I'm doing a project at the moment where I will need to write text on an image using imagettftext, however the text needs an outline and there was no immediate way for me to do this.
In my head there were a couple of possibilities. One was to first write the text in a larger font first with smaller character spacing, however I couldn't find a way to change character spacing without writing each character individually, which would have been a lot of work for me. The second way I thought was to first render the text, then search for and manually colour all pixels within a certain distance of the text, which seemed quite expensive for something that would be run quite frequently.
My final solution was to just render text in the outline colour multiple times in different directions from the text location, and finally render the final text in the middle of that. At first I tried just four outline renders in each of the diagonal directions, however this caused gaps in the outline. The current version also renders orthogonally (making it 8 renders for the outline, and one for the final text) which fills gaps well as long as the outline distance is not too large. Here is an example of my code, which anyone is free to use:
And here is what it looks like:

In my head there were a couple of possibilities. One was to first write the text in a larger font first with smaller character spacing, however I couldn't find a way to change character spacing without writing each character individually, which would have been a lot of work for me. The second way I thought was to first render the text, then search for and manually colour all pixels within a certain distance of the text, which seemed quite expensive for something that would be run quite frequently.
My final solution was to just render text in the outline colour multiple times in different directions from the text location, and finally render the final text in the middle of that. At first I tried just four outline renders in each of the diagonal directions, however this caused gaps in the outline. The current version also renders orthogonally (making it 8 renders for the outline, and one for the final text) which fills gaps well as long as the outline distance is not too large. Here is an example of my code, which anyone is free to use:
<?php
/**
* Writes text using imagettftext with an outline around characters
* @author Michael Alexander http://beefsack.com
*/
function imagettftextoutline(
$image,
$size,
$angle,
$x,
$y,
$color,
$fontfile,
$text,
$outlinewidth = 1, // 1px outline
$outlinecolor = 0 // black
) {
// First offset diagonally
imagettftext($image, $size, $angle, $x - $outlinewidth,
$y - $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x - $outlinewidth,
$y + $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth,
$y - $outlinewidth, $outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth,
$y + $outlinewidth, $outlinecolor, $fontfile, $text);
// Then offset orthogonally
imagettftext($image, $size, $angle, $x - $outlinewidth, $y,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x + $outlinewidth, $y,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x, $y - $outlinewidth,
$outlinecolor, $fontfile, $text);
imagettftext($image, $size, $angle, $x, $y + $outlinewidth,
$outlinecolor, $fontfile, $text);
// Output text
imagettftext($image, $size, $angle, $x, $y, $color, $fontfile,
$text);
}
// Set the content-type
header('Content-type: image/png');
// Create the image
$im = imagecreatetruecolor(400, 30);
// Create some colors
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$black = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, 399, 29, $grey);
// The text to draw
$text = 'Testing...';
// Replace path by your own font path
$font = 'arial.ttf';
// Output text with outline
imagettftextoutline($im, 20, 0, 10, 22, $white, $font, $text, 2);
// Using imagepng() results in clearer text compared with imagejpeg()
imagepng($im);
imagedestroy($im);And here is what it looks like: