Screenshot: создание изображений и PDF из PHP и Laravel

Screenshot, созданный Steve Bauman, позволяет преобразовывать HTML, Blade-представления и веб-страницы в изображения PNG, JPEG и PDF-файлы. По умолчанию пакет запускает и управляет Chrome непосредственно из PHP, без Node.js и Puppeteer. Результаты можно сохранять локально или размещать на любом настроенном диске файловой системы Laravel.

Создание снимков представлений и веб-страниц

Для передачи содержимого используются методы html(), view() и url(). Например, образовательная платформа может создавать изображения сертификатов об окончании курса, которыми пользователи смогут делиться. Следующий пример предполагает наличие Blade-представления certificates.show и модели $certificate.

use DirectoryTree\Screenshot\Facades\Screenshot;

Screenshot::view('certificates.show', data: ['certificate' => $certificate])
    ->size(width: 1440, height: 900)
    ->jpeg(quality: 85)
    ->store(path: "certificates/{$certificate->id}/preview.jpg", disk: 's3');

По умолчанию создаются изображения PNG. Для смены формата используются методы jpeg() и pdf(). Одного изменения расширения файла недостаточно.

JavaScript по умолчанию отключен. Если требуется сделать снимок общедоступной страницы продукта, использующей JavaScript, его можно включить с помощью метода javascript().

В следующем примере $product->public_url содержит полный HTTP- или HTTPS-адрес страницы.

use DirectoryTree\Screenshot\Facades\Screenshot;

Screenshot::url($product->public_url)
    ->javascript()
    ->size(width: 1280, height: 720)
    ->png()
    ->viewport()
    ->save(storage_path("app/product-{$product->id}-page.png"));

Screenshot ожидает события load страницы. Если данные загружаются или графики отрисовываются после этого события, снимок может быть создан до завершения этих операций. Для HTML и Blade-представлений следует использовать абсолютные URL ресурсов или тег <base href="...">, чтобы Chrome мог загружать таблицы стилей и изображения.

Метод size() задает размер области просмотра Chrome, используемой при отображении страницы. По умолчанию изображения охватывают всю страницу, но их максимальная ширина ограничена 2048 пикселями, а высота составляет не более 10 000 пикселей. Вызов viewport(), показанный в предыдущем примере, ограничивает снимок видимой областью просмотра в пределах этих значений.

Снимки отдельных элементов и создание PDF

Метод element() позволяет выбрать элемент страницы с помощью CSS-селектора. Его необходимо вызвать до выбора выходного формата. Например, если страница сертификата содержит навигацию и кнопки скачивания, можно поместить сам сертификат в элемент #certificate и экспортировать только его.

use DirectoryTree\Screenshot\Enums\PaperSize;
use DirectoryTree\Screenshot\Facades\Screenshot;

$source = Screenshot::view('certificates.show', ['certificate' => $certificate])
    ->size(1440, 900)
    ->element('#certificate');

$source->png()->store(
    path: "certificates/{$certificate->id}/certificate.png",
    disk: 's3'
);

$source->pdf()
    ->paper(PaperSize::A4)
    ->landscape()
    ->store(
        path: "certificates/{$certificate->id}/certificate.pdf",
        disk: 's3'
    );

При создании изображения область кадрирования определяется границами выбранного элемента. При генерации PDF Chrome перемещает элемент в тело документа и печатает его с использованием таблиц стилей страницы. CSS-селекторы, зависящие от удаленных родительских элементов, могут потребовать корректировки.

По умолчанию PDF создаются в формате A4 в портретной ориентации, с полями по 10 мм и включенным отображением фоновых элементов. Размеры полотна и полей можно настраивать. Если в таблице стилей определен размер страницы через @page, метод preferCssPageSize() позволяет использовать именно его.

Сохранение и повторное использование снимков

Чтобы сохранить предварительный просмотр сертификата локально, а затем загрузить то же изображение в облачное хранилище, можно сохранить результат вызова capture().

use DirectoryTree\Screenshot\Facades\Screenshot;

$image = Screenshot::view('certificates.show', ['certificate' => $certificate])
    ->size(1440, 900)
    ->element('#certificate')
    ->png()
    ->capture();

$image->save(storage_path("app/certificate-{$certificate->id}.png"));

$image->store(
    path: "certificates/{$certificate->id}/certificate.png",
    disk: 's3',
    options: ['visibility' => 'private'],
);

Вызов save() или store() непосредственно у исходного объекта запускает новое создание снимка при каждом обращении. Объект, возвращаемый методом capture(), хранит уже полученные байты изображения, поэтому оба вызова в примере используют один и тот же результат.

Родительский каталог файла, передаваемого в save(), должен существовать заранее. Если при вызове store() не указывать disk, будет использоваться диск Laravel по умолчанию. Объекты изображений и PDF также предоставляют исходное содержимое, данные в формате Base64 и URI данных.

Установка

Screenshot поддерживает Laravel 12 с PHP 8.2 или новее, а также Laravel 13 с PHP 8.3 или новее.

Установка пакета выполняется через Composer:

composer require directorytree/screenshot

Затем необходимо выполнить команду установки:

php artisan screenshot:install

Поставщик сервисов регистрируется автоматически. Если пакет еще не содержит установленной версии браузера, команда загружает Chrome for Testing. Для загрузки требуется расширение PHP ZIP. Проверить наличие более новой стабильной версии можно с помощью команды:

php artisan screenshot:install --update

Чтобы использовать уже установленный Chrome или Chromium, необходимо указать путь к исполняемому файлу через переменную окружения SCREENSHOT_CHROME_PATH. На сервере также должны быть установлены системные библиотеки, необходимые Chrome, и шрифты, используемые страницами.

Для удаленного рендеринга пакет поддерживает Gotenberg версии 8.38 и новее.

При локальном использовании Chrome именованные сокеты позволяют нескольким рабочим процессам совместно использовать браузер, если они запущены на одном хосте, от имени одного пользователя операционной системы и используют один закрытый каталог сокетов. При этом общий браузер означает и общие файлы cookie и хранилище данных.

В тестах метод Screenshot::fake() позволяет регистрировать создание снимков без запуска Chrome. При проверке загрузки файлов его можно использовать вместе с Storage::fake(). При этом поддельный объект Screenshot по-прежнему записывает файлы.

Дополнительные сведения о настройках, драйверах и примерах тестирования доступны в репозитории Screenshot на GitHub.

Похожие статьи

Рекомендательные технологии Подробнее
Разработка и инструменты • 9 месяцев назад

Типичные ошибки безопасности в Laravel-приложениях и как их правильно исправить

Распространённые ошибки безопасности в Laravel-приложениях и способы их устранения. Разбираем CSRF, SQL-инъекции, XSS, массовое заполнение, загрузку файлов и настройки окружения.

Кибербезопасность • 5 месяцев назад

Laravel-Lang пакеты скомпрометированы через supply chain-атаку

Атака на Laravel-Lang привела к распространению вредоносного кода через Composer-пакеты. Злоумышленники переписали Git-теги и встроили стилер, автоматически запускавшийся в PHP-приложениях.

Разработка и инструменты • 8 месяцев назад

Наследование моделей в Laravel с помощью Parental

Разбор пакета Parental для Laravel, который реализует single table inheritance (STI) в Eloquent, т.е наследование моделей в одной таблице. Статья объясняет, зачем это нужно, как настроить Parental, управлять типами моделей и работать с дочерними связями.