Laravel: Как добавить текст на картинку? (часть 3/3, паттерн билдер)

от автора

в ,
Время чтения: 4 мин.

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


Реализация с паттерном Builder

1. Класс TextOnImageBuilder

use Intervention\Image\ImageManager;

class TextOnImageBuilder
{
    private $manager;
    private $image;
    private $text;
    private $fontPath;
    private $textColor = '#000000';
    private $rectWidth;
    private $rectHeight;
    private $rectX = 0;
    private $rectY = 0;
    private $maxFontSize = 24;
    private $lineHeightMultiplier = 1.2;

    public function __construct($driver = 'gd')
    {
        $this->manager = new ImageManager(['driver' => $driver]);
    }

    public function setImagePath($path)
    {
        $this->image = $this->manager->make($path);
        return $this;
    }

    public function setText($text)
    {
        $this->text = $text;
        return $this;
    }

    public function setFontPath($path)
    {
        $this->fontPath = $path;
        return $this;
    }

    public function setTextColor($color)
    {
        $this->textColor = $color;
        return $this;
    }

    public function setRectangle($width, $height, $x = 0, $y = 0)
    {
        $this->rectWidth = $width;
        $this->rectHeight = $height;
        $this->rectX = $x;
        $this->rectY = $y;
        return $this;
    }

    public function setMaxFontSize($size)
    {
        $this->maxFontSize = $size;
        return $this;
    }

    public function setLineHeightMultiplier($multiplier)
    {
        $this->lineHeightMultiplier = $multiplier;
        return $this;
    }

    private function getTextWidth($text, $fontSize)
    {
        $box = imagettfbbox($fontSize, 0, $this->fontPath, $text);
        return abs($box[2] - $box[0]);
    }

    private function getTextHeight($text, $fontSize)
    {
        $box = imagettfbbox($fontSize, 0, $this->fontPath, $text);
        return abs($box[7] - $box[1]);
    }

    private function calculateOptimalFontSize()
    {
        $fontSize = $this->maxFontSize;

        while ($fontSize > 0) {
            $lines = explode("\n", wordwrap($this->text, 20, "\n"));
            $totalHeight = 0;

            foreach ($lines as $line) {
                $totalHeight += $this->getTextHeight($line, $fontSize) * $this->lineHeightMultiplier;
            }

            if ($totalHeight <= $this->rectHeight) {
                $fits = true;
                foreach ($lines as $line) {
                    if ($this->getTextWidth($line, $fontSize) > $this->rectWidth) {
                        $fits = false;
                        break;
                    }
                }

                if ($fits) {
                    return $fontSize;
                }
            }

            $fontSize--;
        }

        throw new \Exception('Текст не помещается в прямоугольник');
    }

    public function applyText()
    {
        if (!$this->image || !$this->text || !$this->fontPath) {
            throw new \Exception('Необходимо указать изображение, текст и шрифт');
        }

        $fontSize = $this->calculateOptimalFontSize();
        $lines = explode("\n", wordwrap($this->text, 20, "\n"));
        $y = $this->rectY + ($this->rectHeight - $this->getTextHeight($this->text, $fontSize) * $this->lineHeightMultiplier * count($lines)) / 2;

        foreach ($lines as $line) {
            $lineHeight = $this->getTextHeight($line, $fontSize) * $this->lineHeightMultiplier;
            $this->image->text($line, $this->rectX + $this->rectWidth / 2, $y, function ($font) use ($fontSize) {
                $font->file($this->fontPath);
                $font->size($fontSize);
                $font->color($this->textColor);
                $font->align('center');
                $font->valign('top');
            });
            $y += $lineHeight;
        }

        return $this;
    }

    public function save($path)
    {
        $this->image->save($path);
        return $this;
    }

    public function output()
    {
        header('Content-Type: image/jpeg');
        echo $this->image->encode('jpg');
    }
}

2. Использование Builder

Теперь мы можем использовать TextOnImageBuilder для добавления текста на изображение:

$builder = new TextOnImageBuilder('gd'); // Используем драйвер GD

try {
    $builder
        ->setImagePath('path/to/your/image.jpg') // Путь к изображению
        ->setText('Это текст, который нужно вписать в прямоугольник') // Текст
        ->setFontPath('path/to/your/font.ttf') // Путь к шрифту
        ->setTextColor('#ffffff') // Цвет текста
        ->setRectangle(300, 100, 50, 50) // Прямоугольник (ширина, высота, x, y)
        ->setMaxFontSize(24) // Максимальный размер шрифта
        ->setLineHeightMultiplier(1.2) // Множитель межстрочного интервала
        ->applyText() // Применить текст
        ->save('path/to/save/image.jpg'); // Сохранить изображение

    // Или вывести изображение в браузер
    // $builder->output();
} catch (\Exception $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

Преимущества использования Builder:

  1. Гибкость: Каждый метод настраивает отдельный параметр, что делает код более читаемым и удобным для использования.
  2. Расширяемость: Легко добавить новые параметры (например, угол наклона текста или тень).
  3. Повторное использование: Можно использовать один и тот же Builder для создания нескольких изображений с разными параметрами.

Пример вызова с другими параметрами:

$builder = new TextOnImageBuilder('imagick'); // Используем драйвер Imagick

$builder
    ->setImagePath('path/to/another/image.jpg')
    ->setText('Другой текст')
    ->setFontPath('path/to/another/font.ttf')
    ->setTextColor('#ff0000')
    ->setRectangle(400, 150, 100, 100)
    ->setMaxFontSize(30)
    ->setLineHeightMultiplier(1.5)
    ->applyText()
    ->save('path/to/save/another_image.jpg');

Этот подход делает код более модульным и удобным для работы с изображениями и текстом.


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сколько будет 8 + 5?