Нижний колонтитул для картинки или фото с помощью ImageMagick

Сниппеты 20:09 / 05.07.2021 3

Недавно в одном из чатов обсуждалась задача на ImageMagick, которая мне показалось интересной, ввиду того, что её посчитали сложной и готовых похожих решений не нашлось.

Вкратце задача выглядела так: необходимо написать скрипт, который с помощью ImageMagick будет добавлять нижний колонтитул (футер) на фото или картинку. Причем этот колонтитул не должен накладываться на первоначальное изображение, а добавляться внизу по ширине обрабатываемого изображения. На колонтитуле (черном) должны быть логотип (статический) и две надписи (белые) с возможностью выбора произвольных шрифтов для этих надписей.

Ранее я никогда не имел опыта с ImageMagick для PHP, так как для моих задач мне вполне хватало возможностей библиотеки GD. Мне захотелось решить эту задачу, и с готовым решением я делюсь в этой публикации.

Репозиторий с кодом на GitHub: https://github.com/Icemont/Simple-Imagick-Footer

Краткий план

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

Для загрузки обрабатываемых изображений у нас будет простая статическая HTML страница, с помощью которой мы будем передавать наше изображение нашему скрипту обработчику. Скрипт обработчик будет проверять загруженное изображение на соответствие разрешенным к обработке типам изображений, обрабатывать изображение и выводить его непосредственно в браузер.

Идея

Немного ознакомившись с документацией ImageMagick для PHP на официальном сайте, у меня возникла идея, как решить эту задачу просто и быстро. Так как логотип нам нужен статический, а цвет фона тоже статичен - черный, то мы можем заранее нарисовать наш колонтитул с логотипом в графическом редакторе и сохранить его в размере, заведомо превышающем потенциальный размер выходного изображения. Для изображения колонтитула важно использовать формат использующий сжатие без потерь - я выбрал PNG. Назовем изображение "footer.png".

Вот такой колонтитул я по-быстрому нарисовал (это превью, оригинал в репозитории):

Решение

Данное решение не претендует на идеальность, а также не претендует быть истинной в последней инстанции, и всего лишь демонстрирует один из возможных вариантов решения этой задачи.

Создадим нашу статическую HTML страницу для передачи обрабатываемого фото или картинки скрипту-обработчику.

Для того, чтобы наша страница имела хоть какой-то визуально приятный вид, будем использовать CSS фреймворк Bootstrap 4.

Файл: imagemagick.html

<!doctype html>
<html lang="en">
<head>
    <title>ImageMagick Test</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
<main role="main">
    <div class="container">
        <div class="jumbotron">
            <h1 class="display-4 mb-4">ImageMagick Test!</h1>
            <form method="post" action="imagemagick.php" enctype="multipart/form-data" id="loadform">
                <div class="row">
                    <div class="col-auto col-lg-10">
                        <div class="form-group">
                            <label for="fileselect">Select an image file</label>
                            <input type="file" name="image" class="form-control" id="fileselect">
                        </div>
                        <div class="form-group">
                            <label for="text1">Label #1</label>
                            <input type="text" class="form-control" id="text1" name="text1" maxlength="35" aria-describedby="text1Help">
                            <small id="text1Help" class="form-text text-muted">Maximum 35 characters (will be trimmed)</small>
                        </div>
                        <div class="form-group">
                            <label for="text2">Label #2</label>
                            <input type="text" class="form-control" id="text2" name="text2" maxlength="65" aria-describedby="text2Help">
                            <small id="text2Help" class="form-text text-muted">Maximum 65 characters (will be trimmed)</small>
                        </div>
                        <button id="but_upload" type="submit" class="btn btn-primary">Upload &amp; Test</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</main>
</body>
</html>

 

Вот так выглядит наша страница загрузки изображения в браузере:

Также нам понадобятся шрифты для надписей. Для примера я взял бесплатные шрифты Droid Sans и Droid Serif Italic. Они также присутствуют в репозитории проекта на GitHub. Для шрифтов создадим папку "fonts" и поместим шрифты в неё.

Код самого скрипта для обработки:

<?php

/**
 * Simple Imagick Footer
 *
 * @author   Ray Icemont <ray@icemont.dev>
 * @license  https://opensource.org/licenses/Apache-2.0
 */

setlocale(LC_ALL, 'ru_RU.utf-8');
mb_internal_encoding('utf-8');

define('MAIN_DIR', dirname(__FILE__));
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];

if (isset($_FILES['image']['tmp_name']) &&
    in_array(mime_content_type($_FILES['image']['tmp_name']), $allowed_types)) {
    if (!$string = filter_input(INPUT_POST, 'text1', FILTER_SANITIZE_SPECIAL_CHARS)) {
        $string = "Header with the name of the picture";
    }

    if (!$string2 = filter_input(INPUT_POST, 'text2', FILTER_SANITIZE_SPECIAL_CHARS)) {
        $string2 = "Signed by Lorem Ipsum";
    }

    $string = mb_substr($string, 0, 35);
    $string2 = mb_substr($string2, 0, 65);

    try {
        $im = new Imagick();
        $im->readImage($_FILES['image']['tmp_name']);

        $im_res = $im->getImageGeometry();

        $footer = new Imagick();
        $footer->readImage(MAIN_DIR . '/footer.png');

        $draw = new ImagickDraw();
        $draw->setTextEncoding('UTF-8');
        $draw->setFillColor(new ImagickPixel('white'));
        $draw->setFont(MAIN_DIR . '/fonts/droid-sans.ttf');
        $draw->setFontSize(120);
        $footer->annotateImage($draw, 20, 150, 0, $string);

        $draw->setFont(MAIN_DIR . '/fonts/droid-serif-italic.ttf');
        $draw->setFontSize(62);
        $footer->annotateImage($draw, 20, 250, 0, $string2);
        $footer->resizeImage($im_res['width'], null, imagick::FILTER_LANCZOS, 0.9);
        $im->addImage($footer);

        $im->resetIterator();
        $combined = $im->appendImages(true);

        header("Content-Type: image/jpg");
        echo $combined->getImageBlob();
    } catch (ImagickException $e) {
        exit('Imagick Error: ' . $e->getMessage());
    } catch (ImagickDrawException $e) {
        exit('Imagick Draw Error: ' . $e->getMessage());
    }
} else {
    exit('Error: Access denied! Select an image file!');
}

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

Результат обработки фото выглядит вот так:

Результат полностью соответствует поставленной задаче. Используя идею, реализованную в данном коде, мы можем легко создать библиотеку для удобной обработки изображений подобным образом в нашем проекте.