Генерация изображений из HTML кода

Генерация карточек докладчиков в Laravel на основе HTML кода, написанного с помощью Tailwind.

Я организатор конференции Full Stack Europe. Как и на многих других конференциях, мы хотели бы предоставить нашим докладчикам и преподавателям небольшое изображение, которое они могли бы расшарить в Twitter или использовать в своих слайдах. На этой карточке должно быть фото и имя докладчика, плюс информация о конференции.

Я мог бы открыть редактор изображений и вручную изготовить пару десятков таких изображений. Но вместо этого я использовал свои знания PHP и фронтэнда для создания этих карточек.

Я сделал новое Laravel-приложение, установил Tailwind и приступил к работе. Сначала я создал шаблон для отображения все карточек спикеров:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Cards</title>

    <link href="{{ mix('css/main.css') }}" rel="stylesheet">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
          integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" 
          crossorigin="anonymous">
</head>
<body class="m-8 font-noway">
@foreach($cards as $card)
    <div id="card-{{ \Illuminate\Support\Str::slug("{$card['type']} {$card['speaker_name']}") }}" 
         class="m-8 inline-flex bg-repeat border-8 content-between" 
         style="background-image: url('/images/pattern.png')">
        <img class="h-64" src="{{ $card['speaker_photo'] }}" alt="avatar"/>

        <div class="m-4 flex flex-col justify-between ">
            <div class="flex flex-col">
                <div class="text-4xl font-bold">
                    {{ $card['title'] }}
                </div>
                <div class="text-2xl">
                    by {{ $card['speaker_name'] }} - {{ $card['speaker_title'] }}
                </div>
            </div>
            <div class="text-2xl">
                Full Stack Europe<br />
                @if ($card['type'] === 'Workshop')
                    October 23, 2019<br />
                @else
                    October 24 & 25, 2019<br />

                @endif
                fullstackeurope.com
            </div>
        </div>

    </div>
@endforeach
</body>
</html>

Каждую карточку на этой странице нужно преобразовать в изображение. Некоторое время назад я сделал пакет под названием Browsershot, который может конвертировать любую веб-страницу в изображение. Для достижения этого, под капотом, он использует Headless Chrome. Browsershot также может отрендерить в изображение определенный DOM-узел.

Код, который используется для создания карточек докладчиков:

Browsershot::url('http://cards-generator.test/')
  ->select($elementId)
  ->deviceScaleFactor(2)
  ->windowSize(2000, 2000)
  ->save(storage_path("app/cards/{$slug}.jpg"));

В приложении я разместил этот кусок кода в удобную artisan-команду:

namespace App\Console\Commands;

use App\Services\Cards;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Spatie\Browsershot\Browsershot;

class GenerateCardsCommand extends Command
{
    protected $signature = 'generate-cards';

    public function handle()
    {
        collect(Cards::getAll())->each(function(array $card) {
            $this->comment("Generating card for {$card['speaker_name']}...");

            $slug = Str::slug("{$card['type']} {$card['speaker_name']}");

            $elementId = "#card-{$slug}";

            Browsershot::url('http://cards-generator.test/')
                ->select($elementId)
                ->deviceScaleFactor(2)
                ->windowSize(2000, 2000)
                ->save(storage_path("app/cards/{$slug}.jpg"));
        });

        $this->info('All done!');
    }
}

Вот некоторые из сгенерированных карточек:

Карточка докладчика конференции

Карточка докладчика конференции

Карточка докладчика конференции

Карточка докладчика конференции

Несмотря на то, что я, в основном, идентифицирую себя как бэкэнд-разработчика, но мои базовые навыки фронтенда пригодились.

Автор: Freek Van der Herten
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.