There might be many reasons you need to add text on top of an existing image. The main reason for me was to add the title of a post, on top of a "template" background image to generate SEO images.
I've added comments in the code below to denote what it does. The code uses Laravel for the public_path()
helper, but should be fairly straightforward to use native PHP or another framework instead:
// Get the template image that we want to add text to
$image = public_path('social/social-template.png');
// Create a GD Image object
$image = imagecreatefromstring($image);
// Define the color and font size of our text
$navy = imagecolorallocate($image, 20, 45, 111);
$font_size = 128;
$font_path = public_path('fonts/Jokker-Semibold.woff');
// Wrap the text based on a maximum width (see next code block)
$text = $this->wrapText('This is my title', $font_size, $font_path);
// Add the text to the image
imagettftext(
image: $image,
size: $font_size,
angle: 0,
x: 210,
y: 550,
color: $navy,
font_filename: $font_path,
text: trim($text)
);
// Store the image
imagepng($image, public_path("social/images/this-is-my-title.png"));
// Clear memory
imagedestroy($image);
Defining a max width and wrapping the text isn't very straightforward, as each font will result in a different width of the text. Luckily, GD also provides some methods to calculate that:
public function wrapText(string $text, int $font_size, string $font_path): string
{
// A variable to store our result in
$wrapped = '';
// Split the text into an array of words
$words = explode(' ', $text);
foreach ($words as $word) {
// Calculate the size of the current result + the additional word
$teststring = "{$wrapped} {$word}";
$testbox = imagettfbbox($font_size, 0, $font_path, $teststring);
// If the test box width is larger than our max allowed width,
// add a line break before the word, otherwise add a space
if ($testbox[2] > 1900) {
$wrapped .= "\n".$word;
} else {
$wrapped .= ' '.$word;
}
}
return $wrapped;
}
If you prefer to work with HTML & CSS to generate images, take a look at browsershot