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

Источник: «Adding text to an image using PHP and 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 объявлено устаревшим