Добавление текста к изображению с помощью PHP и GD

Может быть множество причин, по которым необходимо добавить текст поверх существующего изображения. Для меня основной причиной было добавление заголовка статьи поверх фонового изображения "шаблона" для создания SEO-изображений.

Код

Я добавил комментарии в код для пояснения того, что он делает. В коде используется Laravel хелпер public_path(), но вместо него можно использовать нативный PHP или другой фреймворк:

// Получение изображения шаблона, к которому необходимо добавить текст
$image = public_path('social/social-template.png');

// Создание объекта GD Image
$image = imagecreatefromstring($image);

// Определение цвета и размера шрифта текста
$navy = imagecolorallocate($image, 20, 45, 111);
$font_size = 128;
$font_path = public_path('fonts/Jokker-Semibold.woff');

// Перенос текста на основе максимальной ширины (см. следующий блок кода)
$text = $this->wrapText('This is my title', $font_size, $font_path);

// Добавление текста к изображению
imagettftext(
image: $image,
size: $font_size,
angle: 0,
x: 210,
y: 550,
color: $navy,
font_filename: $font_path,
text: trim($text)
);

// Сохранение изображения
imagepng($image, public_path("social/images/this-is-my-title.png"));

// Очистка памяти
imagedestroy($image);

Перенос текста

Определить максимальную ширину и перенести текст не так просто, поскольку для каждого шрифта будет разная ширина текста. К счастью, GD предоставляет методы для расчёта этой ширины:

public function wrapText(string $text, int $font_size, string $font_path): string
{
// Переменная для хранения результата
$wrapped = '';

// Делим текст на массив слов
$words = explode(' ', $text);

foreach ($words as $word) {
// Вычисляем размер текущего результата + дополнительное слово
$teststring = "{$wrapped} {$word}";
$testbox = imagettfbbox($font_size, 0, $font_path, $teststring);

// Если ширина тестового блока превышает максимально допустимую ширину,
// добавляем разрыв строки перед словом, в противном случае добавляем пробел
if ($testbox[2] > 1900) {
$wrapped .= "\n".$word;
} else {
$wrapped .= ' '.$word;
}
}

return $wrapped;
}

Альтернативы

Если вы предпочитаете работать с HTML и CSS для генерации изображений, обратите внимание на spatie/browsershot.

Пакет может конвертировать веб-страницу в изображение или pdf. Преобразование происходит за сценой с помощью Puppeteer, управляющего headless версией Google Chrome.

use Spatie\Browsershot\Browsershot;

// будет сохранено изображение
Browsershot::url('https://example.com')->save($pathToImage);

Будет сохранён pdf, если путь, переданный в метод save, имеет расширение pdf.

// будет сохранён pdf
Browsershot::url('https://example.com')->save('example.pdf');

Вы также можете использовать произвольный html код, просто заменив метод url на html:

Browsershot::html('<h1>Hello world!!</h1>')->save('example.pdf');

Если HTML код уже сохранён в локальном файле, используйте:

Browsershot::htmlFromFilePath('/local/path/to/file.html')->save('example.pdf');

Дополнительные материалы

Предыдущая Статья

Пять новых возможностей JavaScript в 2024 году

Следующая Статья

PHP 8.4: Объявление параметров с неявным nullable объявлено устаревшим