1 заметка с тегом

эгея

Генерация Open Graph Image в Эгее

Время чтения текста – 12 минут

Протокол Open Graph нужен, чтобы управлять представлением контента сайта в социальных сетях. По сути — это набор тегов, позволяющий задать то, как будет отображаться информация о сайте на других площадках. В том числе можно указать картинку, которая будет отображаться в постах. Сегодня мы посмотрим, как для блога на Эгее доработать Open Graph разметку и заострим внимание на Open Graph Image — мы сделаем так, чтобы для каждого поста генерировалась своя картинка с наименованием материала.

Подготовка

Перед настройкой тегов протокола Open Graph сделаем скрипт, который генерирует изображения. В результате картинка представляет собой фон и два текста: время чтения и название материала, поэтому мы заранее подготовили такое фоновое изображение для ВКонтакте:

Подробнее об оформлении сниппетов для ВКонтакте можно почитать в документации

У социальных сетей разные требования по размеру изображений: ВКонтакте, например, рекомендует 510×228 пикселей. В корне сайта создадим папку og-image-files. Перейдём в неё, положим туда подготовленное фоновое изображение и создадим ещё одну папку внутри под названием og-fonts для шрифтов на изображении.

Написание скрипта для генерации изображения

Скрипт будем писать на php. Так как он генерирует картинку с пользовательским текстом, мы реализуем поддержку параметров, переданных в ссылке. Создаём в директории og-image-files файл og-image.php и открываем его. Весь код, написанный на php нужно заключить в тег <?php … >. Первым делом опишем функцию, которая откроет фоновое изображение. Пробуем открыть изображение и вернуть его. Если не удалось — создаём пустое белое и выводим сообщение об ошибке на нём.


Функция загрузки изображения

<?php
function LoadPNG($imgname)
{
   $im = @imagecreatefrompng($imgname);

   if(!$im)
   {
       $im  = imagecreatetruecolor(150, 30);
       $bgc = imagecolorallocate($im, 255, 255, 255);
       $tc  = imagecolorallocate($im, 0, 0, 0);

       imagefilledrectangle($im, 0, 0, 150, 30, $bgc);

       imagestring($im, 1, 5, 5, 'Ошибка загрузки ' . $imgname, $tc);
   }

   return $im;
}

Следом опишем другую функцию: она будет помещать текст на картинку, автоматически расставляя переносы строки. Она принимает картинку, размер текста, угол наклона, отступ слева и сверху, цвет текста, шрифт, сам текст и максимальную ширину. Внутри строка делится на массив слов, считаются все необходимые значения ширины текста, а затем в цикле for элементы массива помещаются на изображение.


Функция загрузки изображения

function imagettftextjustified(&$image, $size, $angle, $left, $top, $color, $font, $text, $max_width, $minspacing = 3, $linespacing = 1)
{
   $wordwidth = array();
   $linewidth = array();
   $linewordcount = array();
   $largest_line_height = 0;
   $lineno = 0;
   $words = explode(" ", $text);
   $wln = 0;
   $linewidth[$lineno] = 0;
   $linewordcount[$lineno] = 0;
   foreach ($words as $word)
   {
       $dimensions = imagettfbbox($size, $angle, $font, $word);
       $line_width = $dimensions[2] - $dimensions[0];
       $line_height = $dimensions[1] - $dimensions[7];
       if ($line_height > $largest_line_height) $largest_line_height = $line_height;
       if (($linewidth[$lineno] + $line_width + $minspacing) > $max_width)
       {
           $lineno++;
           $linewidth[$lineno] = 0;
           $linewordcount[$lineno] = 0;
           $wln = 0;
       }
       $linewidth[$lineno] += $line_width + $minspacing;
       $wordwidth[$lineno][$wln] = $line_width;
       $wordtext[$lineno][$wln] = $word;
       $linewordcount[$lineno]++;
       $wln++;
   }
   for ($ln = 0;$ln <= $lineno;$ln++)
   {
       $slack = $max_width - $linewidth[$ln];
       if (($linewordcount[$ln] > 1) && ($ln != $lineno)) $spacing = ($slack / ($linewordcount[$ln] - 1));
       else $spacing = $minspacing;
       $x = 0;
       for ($w = 0;$w < $linewordcount[$ln];$w++)
       {
           imagettftext($image, $size, $angle, $left + intval($x) , $top + $largest_line_height + ($largest_line_height * $ln * $linespacing) , $color, $font, $wordtext[$ln][$w]);
           $x += $wordwidth[$ln][$w] + 20; //+ $spacing + $minspacing;
       }
   }
   return true;
}

Теперь основная часть: так как при переходе на страницу со скриптом должна отображаться исключительно картинка формата png, указываем Content-Type как image/png. Затем читаем изображение и получаем первый параметр social_network — он пригодится, если вы хотите в зависимости от социальной сети вставлять разные фоновые изображения. Загружаем картинку и получаем второй параметр — text. Все пробелы будут переформатированы в «%20», поэтому методом str_replace возвращаем всё обратно. Время чтения — параметр time_for_read, а time_ending — слово, которое нужно подставить после числа минут. Далее загружаем нужные шрифты.

Так как параметры принимаются через ссылку, в теории, туда можно передать любой SQL-запрос — это называется SQL-инъекцией. Такую брешь можно устранить, если все числовые переменные ещё раз перевести в числовые типы данных, а в строках экранировать кавычки: для этого есть функция mysql_escape_string(). А ещё в строку могут передать HTML-теги — для удаления тегов можно воспользоваться функцией strip_tags().

Функция mysql_escape_string() ещё работает в php5, но была удалена в php7. Подробнее об альтернативах можно почитать в документации

header('Content-Type: image/png');

$social_network = $_GET['social_network'];
$social_network = mysql_escape_string(strip_tags($social_network));

$img = LoadPNG('og-image-vk.png');
$text = $_GET['text'];
$text = mysql_escape_string(strip_tags($text));
str_replace('%20', ' ', $text);
$time_for_read = $_GET['time_for_read'];
$time_for_read = intval($time_for_read);
$time_ending = $_GET['time_ending'];
$time_ending = mysql_escape_string(strip_tags($time_ending));
$tahoma_bold_font_path = '/og-fonts/Tahoma-Bold.ttf';
$tahoma_regular_font_path = '/og-fonts/Tahoma-Regular.ttf';
$prosto_font_path = '/og-fonts/ProstoOne-Regular.ttf';

Задаём нужные цвета для текста и подставляем текст на изображение. Функцией imagepng выводим изображение, а затем его удаляем для освобождения памяти.

$white_color = imagecolorallocate($img, 255, 255, 255);
$mentol_color = imagecolorallocate($img, 135, 244, 191);
imagettftextjustified($img, 45, 0, 55, 200, $white_color, $prosto_font_path, $text, 1000, 10, 1.2);
imagettftextjustified($img, 15, 0, 110, 63, $mentol_color, $prosto_font_path, "Время чтения – ".$time_for_read." ".$time_ending, 1000, 10, 1);
imagepng($img);
imagedestroy($img);
?>

После размещения скрипта на сайте можно сразу протестировать его работу: так как наш лежит на сайте leftjoin.ru в директории og-image-files, можно пройти по ссылке leftjoin.ru/og-image-files/og-image.php и увидеть следующее изображение:

Оно пустое, так как мы не передали параметров. Они передаются непосредственно в ссылку. Например, передача текста и наименования социальной сети выглядит так:

https://leftjoin.ru/og-image-files/og-image.php?text=Hello%20world&social_network=vk

Настраиваем теги протокола Open Graph

Откроем файл с тегом <header> сайта. В случае Эгеи это system/theme/templates/head.tmpl.php. Кое-какие теги предварительно размечены: например, в цикле все изображения отмечаются как og-image, что позволяет при публикации материала в социальных сетях выбирать из карусели картинку для сниппета.

А мы структурируем все теги и собираем вместе под комментарием «OPEN GRAPH PROTOCOL». В Эгее все нужные данные по каждой статье уже лежат в переменных, например, заголовок материала лежит в $content[’title’], а краткое описание страницы можно получить функцией array_keys(content). Главное для нас — добавить такую строку:

<meta property="og:image" content='https://leftjoin.ru/og-image-files/og-image.php?text=<?=urlencode($content['title'])?>&time_for_read=<?=urlencode($time_for_read)?>&time_ending=<?=urlencode($time_ending)?>' />

Она задаёт тег og-image и принимает в content изображение. Туда мы подставляем ссылку на скрипт со всеми параметрами-переменными, обработав их функцией urlencode — она и заменяет все пробелы на «%20». Если ей не воспользоваться, то текст на изображениях в социальных сетях вроде ВКонтакте, Facebook, Twitter и даже в мессенджере Telegram будет отображаться нормально, а вот в Slack вместо пробелов останутся символы «%20», даже с учётом их обработки в скрипте ранее. Должен получиться похожий блок:

<!-- OPEN GRAPH PROTOCOL-->
<meta name="og:description" content="<?array_keys(content)?>" />
<meta property="og:image" content='https://leftjoin.ru/og-image-files/og-image.php?text=<?=urlencode($content['title'])?>&time_for_read=<?=urlencode($time_for_read)?>&time_ending=<?=urlencode($time_ending)?>' />
<?php foreach ($content['og-images'] as $image): ?>
<meta property="og:image" content="<?=$image?>"/>
<?php endforeach ?>
<meta property="og:image:type" content="image/png" />
<meta name="vk:image" content='https://leftjoin.ru/og-image-files/og-image.php?text=<?=urlencode($content['title'])?>&time_for_read=<?=urlencode($time_for_read)?>&time_ending=<?=urlencode($time_ending)?>&social_network=vk' >
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:image" content='https://leftjoin.ru/og-image-files/og-image.php?text=<?=urlencode($content['title'])?>&time_for_read=<?=urlencode($time_for_read)?>&time_ending=<?=urlencode($time_ending)?>' />
<meta name="viewport" content="<?= $content['meta-viewport'] ?>">
<title><?= $content['title'] ?></title>
<meta name="og:title" content="<?= $content['title'] ?>" />
<meta property='og:type' content="article" />
<meta name="og:url" content="<?= $content['current-href'] ?>" />

В результате мы написали скрипт, который генерирует изображение с текстом-параметром прямо на сайте, а затем вызвали этот скрипт в тегах протокола Open Graph. В качестве доработки можно добавить изображения разных размеров и генерировать картинку в зависимости от социальной сети. Теги тоже могут отличаться — изображение для ВКонтакте вставляется в тег <vk:image>, а для Twitter — <twitter:image>. В примере выше указано правильное использование этих тегов.

 1 комментарий    125   2021   open graph   php   эгея