Добавление водяного знака на все картинки сайта -8


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


Мы решили написать универсальный скрипт, который можно запустить из консоли или прямо в браузере и обновить все картинки на сайте.


И так задача:


  1. Картинки находятся в папку img в корне сайта;
  2. Добавить на картинки водяной знак по центру;
  3. Перенести все картинки в папку img2;

В процессе решения задачи, оказалось, что все картинки ещё и разного размера от 200 до 7000 пикселей по ширине, а водный знак в виде логотипа должен быть на всех.Как мы решали эту задачу:


Этап 1. Обойти все файлы


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


$path  =  $_SERVER['DOCUMENT_ROOT']; //Переменная окружения
$root  =  $path."/img"; //Рабочая папка

И функция обхода всех элементов папки:


function find_new($dir)
{
    $new_dir = null;
    $dir_files = opendir($dir);
    while(false !== ($file = readdir($dir_files)))
    {

        if($file != '.' && $file != '..')
            $new_dir[] = $dir."/".$file;
    }

    if($new_dir)
        foreach($new_dir as $check )
        {
            if(is_file($check)) {
                echo $check . "<br>";
            } elseif(is_dir($check))
                find_new($check);
        }
}
find_new($root);

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


Единственное что тут не реализована, проверка на картинки, но она нам не пригодилась.


Этап 2. Воссоздать структуру папок


Так как у нас множество папок, с вложенными папками, и так до 10 уровня, для успешной работы всех функций копирования и перемещения файлов, нужна готовая структура.


Для этого мы определяем имя конечно папки и если её нет, то создаем:


$fileName = basename($check); //Определяем имя файла
$new = str_replace("img","img2",$check); //Заменяем имя папки
$put = substr($new,0,-strlen($fileName)); //Определяем путь до папки
if (!file_exists($put)) {
    mkdir($put, 0777, true); //Если папки нет, то создаем
}

Данный код, вставляется после: echo $check; и при выполнении он генерирует новую структуру папок, на вашем сервере, при этом, запускать его можно бесконечно много раз, он не повредит структуру, а создает соседнею папку img2.


Этап 3. Добавить логотип на картинки


Для этого будем использовать четыре стандартные функции: imagecreatefrompng, imagecreatefromjpeg, imagecopy, imagejpeg и парочку дополнительных типа: imagedestroy, imagesx.


Все это библиотека GD для PHP, подключена у всех по умолчанию и так:


$stamp = imagecreatefrompng('stamp.png'); //Логотип, размер произвольный
$sx = imagesx($stamp); //Получаем ширину
$sy = imagesy($stamp); //Высоту
$im = imagecreatefromjpeg($check); //Исходная картинка
imagecopy($im, $stamp, imagesx($im) - $sx - 10,  imagesy($im) - $sy - 10, 0, 0, imagesx($stamp), imagesy($stamp)); //Копируем логотип на картинку
imagejpeg($im, $new, 100); //Создаем изображение
imagedestroy($im); //Чистим за собой

Таким образом, после 3-5 минут работы скрипта, в зависимости от количества файлов, у нас получается копия всех изображений в папке img2, но логотип находится в правом нижнем углу, и на всех картинках имеет разный размер. Можно поиграться с цифрами в imagecopy но эффекта от этого не будет. Картинки разные, значит и водный знак должен быть разным, поэтому мы переходим к 4 этапу.


4 этап. Адаптация и выравнивание логотипа


Для этого нам понадобиться преобразовать, исходный логотип stamp.png до размеров картинки, на которой будем его размещать, и вклеить точно по её середине.
Приступим:


$stamp = imagecreatefrompng('stamp.png'); //Логотип, размер БОЛЬШОЙ желательно 2000*1500
$sx = imagesx($stamp); //Получаем ширину
$sy = imagesy($stamp); //Высоту
$im = imagecreatefromjpeg($check); //Загружаем картинку
///Подгоняем знак по размерам
$w = imagesx($im) - 20; //Новый размер логотипа ширина
$koe=$sx/$w; //Определяем коэффициент сжатия
$h=ceil($sy/$koe); //Высота нового логотипа
//echo $sx."-".$sy." ".$w."-".$h." ".$koe."<BR>"; //Это для теста
$sim = imagecreatetruecolor($w, $h); //Создаем новый фон для логотипа
$transparent = imagecolorallocatealpha($sim, 0, 0, 0, 127); // для прозрачности фона
imagefill($sim, 0, 0, $transparent); // для прозрачности фона
imagesavealpha($sim, true); // для прозрачности фона
imagecopyresampled($sim,$stamp,0,0,0,0,$w,$h,$sx,$sy); //Создали новый логотип в $sim
$cn = ceil((imagesy($im) - $h)/2); //Установим по центру водяной знак
imagecopy($im, $sim, imagesx($im) - $w - 10,  $cn, 0, 0, imagesx($sim), imagesy($sim)); //Вставляем штамп на картинку

Если на этапе 3, в imagecopy в качестве размеров вставляемой картинки использовали imagesx($stamp), то здесь уже используем размеры нового логотипа imagesx($sim).


Логотип содержит отступы 10 пикселей слева и справа, и задается соответственно цифрами 20 и 10 в коде.


Этап 5. Добавляем функцию преобразования в наш цикл:


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


path  =  $_SERVER['DOCUMENT_ROOT'];
$root  =  $path."/img";

$stamp = imagecreatefrompng('stamp.png');
$sx = imagesx($stamp);
$sy = imagesy($stamp);

function find_new($dir)
{
    global  $stamp;
    global  $sx;
    global  $sy;

    $new_dir = null;
    $dir_files = opendir($dir);
    while(false !== ($file = readdir($dir_files)))
    {

        if($file != '.' && $file != '..')
            $new_dir[] = $dir."/".$file;
    }

    if($new_dir)
        foreach($new_dir as $check )
        {
            if(is_file($check)) {
                $w='';$h='';$koe='';$sim='';

                //echo $check . "<br>";
                $im = imagecreatefromjpeg($check);

                ///Подгоняем знак по размерам
                $w = imagesx($im) - 20; //Новый размер штампа ширина
                $koe=$sx/$w;
                $h=ceil($sy/$koe); //высота его
                //echo $sx."-".$sy." ".$w."-".$h." ".$koe."<BR>";
                $sim = imagecreatetruecolor($w, $h);
                $transparent = imagecolorallocatealpha($sim, 0, 0, 0, 127); // для прозрачности фона
                imagefill($sim, 0, 0, $transparent); // для прозрачности фона
                imagesavealpha($sim, true); // для прозрачности фона
                imagecopyresampled($sim,$stamp,0,0,0,0,$w,$h,$sx,$sy);

                $cn = ceil((imagesy($im) - $h)/2); //Установим по центру водяной знак

                //Вставляем штамп на картинку
                imagecopy($im, $sim, imagesx($im) - $w - 10,  $cn, 0, 0, imagesx($sim), imagesy($sim));
                $fileName = basename($check);
                $put = substr($check,0,-strlen($fileName));
                $put = str_replace("img","cache",$put);
                if (!file_exists($put)) {
                    mkdir($put, 0777, true);
                }
                $new = str_replace("img","cache",$check);

                imagejpeg($im, $new, 100); 
                imagedestroy($im);

            } elseif(is_dir($check))
                find_new($check);
        }
}
find_new($root);

Его достаточно положить в корневую папку сайта, задать исходную и конечную папку с картинками и запустить, если картинок очень много. Добавить вначале:


ignore_user_abort();
set_time_limit(0);

И запускать из консоли, чтобы видеть этапы работы.


Эксперименты со вставкой логотипа, и подбором степени его прозрачности лучше проводить в конечной папке, для этого в строке $root = $path.”/допишите/свой/путь/до/папки” или положите скрипт в конечную папку и запускайте оттуда.


Мы использовали стандартный логотип компании с прозрачностью 60%.


Заключение


Когда папка с новыми файлами будет готова, Вам остается только переименовать её с img2 в img. В итоге у вас на сайте будет папка с исходными файлами, которые можно за архивировать или удалить и папка с помеченными логотипом фотографиями.


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


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




К сожалению, не доступен сервер mySQL