Наверх

Генерируем картинку для статьи из её заголовка

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

<?php
// Указываем картинку, которая будет фоном по умолчанию
$background = 'assets/images/preview_bg.png';

// Получаем ID ресурса и текст для надписи
$id = $input;
$pagetitle = $options;
if (!$id) {
    $id = $modx->resource->id;
}
if (!$resource = $modx->getObject('modResource', $id)) {
    return;
}
if (!$pagetitle) {
    $pagetitle = $resource->menutitle ?: $resource->pagetitle;
}

// Создаём папки для превьюшек
$path = $modx->getOption('base_path') . 'assets/images/';
if (!file_exists($path)) mkdir($path);
$path .= 'previews/';
if (!file_exists($path)) mkdir($path);
$path .= $id . '/';
if (!file_exists($path)) mkdir($path);

$output = $path;

// Если к ресурсу прикреплено изображение, используем его имя
if ($resource->getTVValue('img')) {
    $tmp = explode('.', basename($resource->getTVValue('img')));
    array_pop($tmp);
    $name = implode('.', $tmp);
    $output .= $name;
    
    // и устанавливаем его в качестве фона
    $background = $resource->getTVValue('img');
} else {
    // а если нет - то название картинки будет preview.png
    $output .= 'preview';
}

// Добавляем хеш надписи в имя картинки
$output .= '-' . substr(md5($pagetitle), 0, 3);

// И расширение png
$output .= '.png';

// Проверяем, что такое изображение мы ещё не генерировали
if (!file_exists($output) || true) {

    // Переносим текст по строчкам
    $pagetitle = rtrim($pagetitle);
    $length = 27;
    $words = explode(' ', $pagetitle);
    $pagetitle = '';
    $actual = '';
    foreach ($words as $word) {
        if (mb_strlen($actual.$word) <= $length)
            $actual .= $word.' ';
        else {
            if ($actual != '')
                $pagetitle .= rtrim($actual).'|';
            $actual = $word;
            $actual .= ' ';
        }
    }
    $pagetitle .= trim($actual);
    $rows = explode("|", $pagetitle);
    
    $draw = new \ImagickDraw();
    $outputImage = new \Imagick($modx->getOption('base_path') . $background);
    
    // Подготавливаем фоновую картинку
    $width = 800;
    $height = 600;
    
    $cropWidth = $outputImage->getImageWidth();
    $cropHeight = $outputImage->getImageHeight();
    
    // Если фоновая картинка маловата, пишем сообщение в лог
    if ($cropWidth < $width || $cropHeight < $height) {
        $modx->log(1, "[getPreview] Background image for resource {$id} is small");
        return;
    }
    
    // Приводим картинку к соответствующим пропорциям
    if ($cropHeight < $height * $cropWidth / $width) {
        $newWidth = $cropHeight * $width / $height;
        $outputImage->cropimage($newWidth, $cropHeight);
    } else {
        $newHeight = $cropWidth * $height / $width;
        $outputImage->cropimage($cropWidth, $newHeight);
    }
    
    // И уменьшаем до нужных размеров
    $outputImage->scaleImage($width, $height);
    
    // Рисуем закрашенный полупрозрачный прямоугольник
    $draw->setFillColor('#00669966');
    $points = [
        ['x' => 25, 'y' => 40],
        ['x' => $width - 25, 'y' => 40], 
        ['x' => $width - 25, 'y' => 60 + 65 * count($rows)], 
        ['x' => 25, 'y' => 60 + 65 * count($rows)],
    ];
    $draw->polygon($points);
    $outputImage->drawImage($draw);
    
    // Устанавливаем параметры текста
    $draw->setStrokeWidth(0);
    
    // Нужно загрузить файл шрифта на сервер
    $draw->setFont($modx->getOption('base_path') . 'fonts/RobotoSlab-Bold.ttf');
    $draw->setFontSize(45);
    $draw->setGravity(\Imagick::GRAVITY_NORTHWEST);
    $draw->setTextAntialias(true);
    
    // Рисуем сначала тень текста
    $draw->setStrokeColor('#00000030');
    $draw->setFillColor('#00000030');
    $y = 55; $x = 50;
    foreach ($rows as $row) {
        $outputImage->annotateImage($draw, $x, $y, 0, $row);
        $y += 65;
    }
    
    // Потом сам текст поверх тени
    $draw->setStrokeColor('#fff');
    $draw->setFillColor('#fff');
    $y = 50; $x = 45;
    foreach ($rows as $row) {
        $outputImage->annotateImage($draw, $x, $y, 0, $row);
        $y += 65;
    }
    
    // И сохраняем картинку
    $outputImage->setImageFormat('png');
    $outputImage->writeimage($output);
}

// Обрезаем путь от корня сервера
$output = str_replace($modx->getOption('base_path'), '/', $output);

// и выводим адрес картинки
return $output;

На сервер должен быть установлен ImageMagick.


8 комментариев

  1. Павел Александрович Устюгов 09 июня 2017, 17:42 # +1
    драгоценный код)
    1. Павел 09 июня 2017, 18:22(Комментарий был изменён) # 0
      А в чем смысл конвертировать текст в изображение? Можно же просто поверх вывести блок с полупрозрачным фоном и нормальным «текстовым» текстом.

      PS: не на тот уровень, сори
      1. Павел Александрович Устюгов 09 июня 2017, 18:30(Комментарий был изменён) # 0
        чтоб водный знак нанести на фотографию, к примеру. В вашем случае парсеры смогут вытащить картинку с сайта без надписи.

        Ответил за Илью )
        1. Павел 09 июня 2017, 18:39 # 0
          Хм… Да, в таком ключе конечно использование оправдано, спасибо.
          В любом случае, полезный скрипт. Найдет свое применение :)
        2. Илья Уткин 09 июня 2017, 18:38 # +1
          Например, чтобы настроить отображение этой картинки, когда люди делятся ссылкой в соцсетях.
      2. Конрад Мицци 19 октября 2017, 22:29 # 0
        А как можно запилить возможность в вызове снипета указывать поле у ресурса, картинку из которого надо брать (может у меня там ТВ с Image+)
        И еще там где рисуется полупрозрачный прямоугольник — надо либо через плейсхолдер, либо внутри снипета указать путь к файлу png, который наляпывается поверх нашей пикчи?
        Спасибо!
        1. Илья Уткин 20 октября 2017, 12:11 # 0
          Ну возможность такая есть, а готового решения у меня нет. Если напишете, поделитесь, я поправлю код)
          1. Конрад Мицци 20 октября 2017, 12:39 # 0
            Только если найду где стибрить))) Сам я в php дуб-дубом)))

        Авторизация

        через сервис Loginza:


        Шаблоны MODX

        1 2 Дальше »

        Объектная
        модель
        MODX