Добавление водяного знака на изображение в PHP 8

Доброго времени суток! В примере ниже мы рассмотрим с Вами как можно добавить водяной знак на изображение
в PHP. В примере будем использовать библиотеку GD, поэтому она должна быть у Вас установлена. Также в примере
будем использовать версию PHP 8.1.

Класс Image — обертка над GdImage


<?php

declare(strict_types=1);

class Image
{
    private string $filename;
    private GdImage $image;
    private int $width;
    private int $height;
    private int $bits;
    private string $mimeType;

    /**
     * @throws Exception
     */
    public function __construct(string $filename)
    {
        // если расширение не загружено - бросаем исключение
        if (!extension_loaded('gd')) {
            throw new Exception('GD extension is not loaded');
        }

        if (file_exists($filename)) {
            $this->filename = $filename;

            $fileInfo = getImageSize($filename);

            if(!$fileInfo) {
                throw new Exception("Image file corrupted: $filename"); 
            }

            $this->width  = $fileInfo[0];
            $this->height = $fileInfo[1];
            $this->bits = $fileInfo['bits'] ?? '';
            $this->mimeType = $fileInfo['mime'] ?? '';

            // выражение match, добавленное в PHP 8, выполняет функции switch
            // создаем изображение из файла заданного формата
            $this->image = match ($this->mimeType) {
                'image/gif' => imageCreateFromGif($filename),
                'image/png' => imageCreateFromPng($filename),
                'image/jpeg' => imageCreateFromJpeg($filename),
                default => throw new Exception("Unrecognized image type: $filename"),
            };
        } else {
            throw new Exception("Could not load image $filename");
        }
    }

    public function getFile(): string
    {
        return $this->filename;
    }

    public function getImage(): GdImage
    {
        return $this->image;
    }

    public function getResource(): GdImage
    {
        return $this->image;
    }

    public function cleanResource(): void
    {
        imageDestroy($this->image);
    }

    public function getWidth(): int
    {
        return $this->width;
    }

    public function getHeight(): int
    {
        return $this->height;
    }

    public function getBits(): int
    {
        return $this->bits;
    }

    public function getMime(): string
    {
        return $this->mime;
    }

    public function __toString(): string
    {
        return "[Object Image({$this->getFile()}@{$this->getWidth()}x{$this->getHeight()})]";
    }
}

Класс ImageProcessor — принимает класс Image и ставит на нем водяной знак

<?php

declare(strict_types=1);

class ImageProcessor
{
    private Image $image;

    public function __construct(Image $image)
    {
        $this->image = $image;
    }

    /**
     * Сохраняет изображение в файл 
     * 
     * @throws Exception
     */
    public function save(string $file, int $quality = 90): bool
    {
        $isSaveSuccessfully = false;
        $info = pathinfo($file);
        $extension = strtolower($info['extension']);

        //print($extension);
        $image = $this->image->getResource();
        $is_resource = is_resource($image);

        $isSaveSuccessfully = match ($extension) {
            'jpeg', 'jpg' => imageJpeg($image, $file, $quality),
            'png' => imagePng($image, $file),
            'gif' => imageGif($image, $file),
            default => throw new Exception("Unrecognized output image format: $extension"),
        };

        imageDestroy($image);

        return $isSaveSuccessfully;
    }

    /**
     * Возвращает массив из двух точек - положение водяного знака на исходном изображении 
     * 
     * @param Image $watermark
     * @param WatermarkPosition $position
     * @return array<int, int>
     */
    private function getIntPosition(Image $watermark, WatermarkPosition $position): array
    {
        return match ($position) {
            WatermarkPosition::TopLeft => [0, 0],
            WatermarkPosition::TopCenter => [
                ($this->image->getWidth() - $watermark->getWidth()) / 2,
                0
            ],
            WatermarkPosition::TopRight => [
                $this->image->getWidth() - $watermark->getWidth(),
                0
            ],
            WatermarkPosition::MiddleLeft => [
                0,
                ($this->image->getHeight()->$watermark->getHeight()) / 2
            ],
            WatermarkPosition::MiddleCenter => [
                ($this->image->getWidth() - $watermark->getWidth()) / 2,
                ($this->image->getHeight() - $watermark->getHeight()) / 2
            ],
            WatermarkPosition::MiddleRight => [
                $this->image->getWidth() - $watermark->getWidth(),
                ($this->image->getHeight() - $watermark->getHeight()) / 2
            ],
            WatermarkPosition::BottomLeft => [
                0,
                $this->image->getHeight() - $watermark->getHeight()
            ],
            WatermarkPosition::BottomCenter => [
                ($this->image->getWidth() - $watermark->getWidth()) / 2,
                $this->image->getHeight() - $watermark->getHeight()
            ],
            WatermarkPosition::BottomRight => [
                $this->image->getWidth() - $watermark->getWidth(),
                $this->image->getHeight() - $watermark->getHeight()
            ]
        };
    }

    /**
     * Добавляет водяной знак на изображение с помощью функций библиотеки GD 
     * 
     * @param Image $watermark
     * @param WatermarkPosition $position
     * @return void
     * 
     */
    public function addWatermark(Image $watermark, WatermarkPosition $position = WatermarkPosition::BottomRight): void
    {
        [$watermarkX, $watermarkY] = $this->getIntPosition($watermark, $position);

        $image = $this->image->getResource();

        ImageAlphaBlending($image, true);
        ImageSaveAlpha($image, true);
        ImageCopy($image, $watermark->getImage(), intval($watermarkX), intval($watermarkY), 0, 0, $watermark->getWidth(), $watermark->getHeight());

        ImageDestroy($watermark->getImage());
        $this->image->setGdImage($image);
    }

}

Перечисление WatermarkPosition — описывает положения для водяного знака на исходном изображении


<?php

enum WatermarkPosition
{
    case TopLeft;
    case TopCenter;
    case TopRight;
    case MiddleLeft;
    case MiddleCenter;
    case MiddleRight;
    case BottomLeft;
    case BottomCenter;
    case BottomRight;
}

watermark.php — пример использования


<?php

spl_autoload_register(fn($class) => require_once __DIR__ . "/lib/$class.php");

$image1 = 'images/base_image.png';
$image2 = 'images/watermark.png';

try {
    $baseImage = new Image($image1);
    $watermarkImage = new Image($image2);

    $imageProcessor = new ImageProcessor($baseImage);
    $imageProcessor->addWatermark($watermarkImage, WatermarkPosition::BottomLeft);

    $current_time = time();
    $imageProcessor->save("./image_with_watermark_$current_time.jpg");

} catch (Exception $e) {
    print($e->getMessage());
}

Таким образом, с помощью PHP библиотеки GD можно ставить водяные знаки на изображении и не только!

Источник