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:
1// Get the template image that we want to add text to
2$image = public_path('social/social-template.png');
3
4// Create a GD Image object
5$image = imagecreatefromstring($image);
6
7// Define the color and font size of our text
8$navy = imagecolorallocate($image, 20, 45, 111);
9$font_size = 128;
10$font_path = public_path('fonts/Jokker-Semibold.woff');
11
12// Wrap the text based on a maximum width (see next code block)
13$text = $this->wrapText('This is my title', $font_size, $font_path);
14
15// Add the text to the image
16imagettftext(
17 image: $image,
18 size: $font_size,
19 angle: 0,
20 x: 210,
21 y: 550,
22 color: $navy,
23 font_filename: $font_path,
24 text: trim($text)
25);
26
27// Store the image
28imagepng($image, public_path("social/images/this-is-my-title.png"));
29
30// Clear memory
31imagedestroy($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:
1public function wrapText(string $text, int $font_size, string $font_path): string
2{
3 // A variable to store our result in
4 $wrapped = '';
5
6 // Split the text into an array of words
7 $words = explode(' ', $text);
8
9 foreach ($words as $word) {
10 // Calculate the size of the current result + the additional word
11 $teststring = "{$wrapped} {$word}";
12 $testbox = imagettfbbox($font_size, 0, $font_path, $teststring);
13
14 // If the test box width is larger than our max allowed width,
15 // add a line break before the word, otherwise add a space
16 if ($testbox[2] > 1900) {
17 $wrapped .= "\n".$word;
18 } else {
19 $wrapped .= ' '.$word;
20 }
21 }
22
23 return $wrapped;
24}
If you prefer to work with HTML & CSS to generate images, take a look at browsershot