От автора: в данном уроке мы с Вами рассмотрим один из способов нанесения водяного знака на изображение средствами языка PHP. Так как используя водяные знаки на своих изображениях, Вы защитите их от использования другими пользователями, к тому же, если изображение будет использоваться на других сайтах, или будет найдено поисковой системой — водяной знак, содержащий ссылку на Ваш ресурс может привлечь дополнительный трафик.
Постановка задачи
В данном уроке нам с Вами необходимо реализовать динамическое добавление водяного знака к выводимому на экран изображению. При этом исходное изображение не должно быть изменено. А также необходимо сделать так, что бы, к примеру, при загрузке изображений на сервер, через обычную форму, выполнялось добавление водяных знаков к загружаемым изображениям.
Для решения поставленной задачи, я подготовил два изображения: исходное, на которое мы будем добавлять водяной знак, и, собственно, сам водяной знак. В качестве исходного изображения, мы будем использовать следующее изображение:

Бесплатный курс по PHP программированию
Освойте курс и узнайте, как создать динамичный сайт на PHP и MySQL с полного нуля, используя модель MVC
В курсе 39 уроков | 15 часов видео | исходники для каждого урока
Получить курс сейчас!В качестве водяного знака, будем использовать следующее изображение:
Это изображение формата PNG с 50% степенью прозрачности.
Подготовка к написанию скрипта
Первым делом необходимо убедиться, что в Вашем интерпретаторе языка PHP подключена библиотека GD. Данная библиотека используется для работы с изображениями средствами языка PHP. Поэтому если Вы не уверены, что данная библиотека подключена, откройте конфигурационный файл языка PHP и найдите строку: extension=php_gd2.dll
Напротив данной строки не должно быть символа “;”(точка с запятой). Если он есть, то удалите его и перезапустите веб-сервер Apache. Далее, для сегодняшнего урока я подготовил тестовую страницу, которая отображает на экране исходное изображение:
1 2 3 4 5 6 7 8 9 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title></title> </head> <body> <img src="img/img1.jpg"> </body> </html> |
Но если мы хотим динамически наносить на изображение водяной знак и при этом не изменять исходное изображение, значит, изображение должно обрабатываться скриптом на языке PHP. Поэтому в теге img необходимо изменить значение атрибута src. То есть значение данного атрибута должно указывать не на изображение, а на файл, который обработает исходное изображение и нанесет на него водяной знак. И конечно же вернет новое изображение. Поэтому перепишем тег img:
1 | <img src="image.php?image=img1.jpg"> |
Как Вы видите, теперь мы обращаемся к файлу image.php, который вернет нам новое изображение, и передаем ему через GET параметры путь к исходному изображению. Перед тем как приступить к созданию файла image.php, который должен вернуть нам новое изображение, давайте создадим файл конфигурации config.php, с вот таким содержимым:
1 2 3 4 | <?php define("DIR_IMG","img/"); define("WM",DIR_IMG."watermark.png"); ?> |
Данный фал нам необходим для хранения некоторых настроек, а именно:
DIR_IMG – папка где содержатся все изображения;
WM – путь к изображения, которое используется как водяной знак.
Затем создадим еще один файл – functions.php, который будет содержать в себе необходимые функции для работы скрипта. Теперь создадим файл image.php и первым делом подключим только что созданные файлы к нему.
1 2 | include "config.php"; include "functions.php"; |
Далее проверим наличие ячейки image в суперглобальном массиве $_GET, если данная ячейка есть, то создадим переменную и сохраним в нее значение данной ячейки:
1 2 3 | if($_GET['image']) { $image_for_wm = DIR_IMG.$_GET['image']; } |
Далее вызовем функцию, которая и будет автоматический добавлять водяной знак к изображению (данную функцию мы с Вами еще напишем):
1 | get_water($image_for_wm); |
Теперь переходим непосредственно к написанию функции get_water(), которая обработает исходное изображение и нанесет на него водяной знак.
Функция наложения водяного знака
Итак, переходим в файл functions.php и начнем создавать функцию get_water():
1 2 3 | function get_water($main_img_path, $load = FALSE) { $main_img = getimagesize($main_img_path); } |
Смотрите, функция принимает всего два параметра (из которых один не обязательный):
$main_img_path – путь к исходному изображению, которое требуется обработать;
$load – данный необязательный параметр принимает имя файла, под которым должно быть сохранено новое обработанное изображение. Данный параметр относится к случаю, когда мы хотим не вывести изображение на экран, а создать новое изображение из исходного, с наложенным водяным знаком, и сохранить его в папке. Путь к которой задан в конфигурационном файле.
В данной функции первым делом необходимо получить параметры исходного изображения. Для этого мы используем функцию getimagesize(), которая вернет нам массив, содержащий различные параметры изображения (путь к которому передан ей параметром). Давайте распечатаем данный массив в браузере и посмотрим, что у нас получилось:
То есть в ячейках 0 и 1 содержится ширина и высота данного изображения. В ячейке 2 одна из констант типа изображения (в нашем случае значение 2 соответствует типу jpeg). Индекс 3 содержит строку со значениями ширины и высоты изображения width=»xxx» height=»yyy», которая может быть использована внутри img тэга. Ячейка mime – содержит в себе соответствующий MIME-тип изображения.
Далее весь код будем вести внутри функции get_water(). Теперь необходимо проверить, не вернула ли функция getimagesize() – FALSE. Если да, то выходим из скрипта:
1 2 3 | if(!$main_img) { exit(); } |
Далее в соответствии с MIME-типом изображения создаем на основе исходного изображения — новое изображение в памяти. Для этого мы используем оператор switch() и функции создания нового изображения на основе заданного в соответствии с его типом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | switch($main_img['mime']) { case 'image/jpeg': case 'image/pjpeg': $img = imagecreatefromjpeg($main_img_path); break; case 'image/png': case 'image/x-png': $img = imagecreatefrompng($main_img_path); break; case 'image/gif': $img = imagecreatefromgif($main_img_path); break; } |
Обратите внимание, что для MIME-типа ‘image/jpeg’, как и для ‘image/png’, нужно учитывать и дополнительные типы (‘image/pjpeg’ и ‘image/x-png’), которые может нам вернуть браузер Internet Explorer. Далее создаем изображение в памяти из заготовленного изображения под водяной знак:

Бесплатный курс по PHP программированию
Освойте курс и узнайте, как создать динамичный сайт на PHP и MySQL с полного нуля, используя модель MVC
В курсе 39 уроков | 15 часов видео | исходники для каждого урока
Получить курс сейчас! 1 2 3 | if(file_exists(WM)) { $water = imagecreatefrompng(WM); } |
Здесь условимся, что изображения для водяного знака должно быть только формата PNG, поэтому используем соответственно функцию imagecreatefrompng(). Затем нам необходимо создать пустое изображение, точно такого же размера, как и исходное. И перенести на него сначала исходное изображение, а затем водяной знак. Для начала создаем переменные для хранения ширины и высоты будущего пустого изображения:
1 2 | $res_width = $main_img[0]; $res_height = $main_img[1]; |
Далее при помощи функций imagesx() и imagesy(), определяем ширину и высоту, соответственно, изображения водяного знака:
1 2 | $water_width = imagesx($water); $water_height = imagesy($water); |
Затем создаем пустое полноцветное изображение, с размерами равными размерам исходного изображения:
1 | $res_img = imagecreatetruecolor($res_width,$res_height); |
Теперь остается перенести исходное изображение на только что созданное:
1 2 | imagecopyresampled($res_img,$img,0,0,0,0, $res_width,$res_height,$main_img[0],$main_img[1]); |
Для этого используем функцию imagecopyresampled(), которая копирует и изменяет, по необходимости, размеры изображения. Параметры передаваемые данной функции:
$res_img – дискриптор изображения на которое выполняется копирование (изображение приемник, то которое мы только что создали);
$img – копируемое изображение (источник);
0 — x-координата изображения на которое выполняется копирование (точка по оси x в которую будет помещено копируемое изображение);
0 — y-координата изображения на которое выполняется копирование (точка по оси y в которую будет помещено копируемое изображение);
0 — x-координата изображения источника (точка по оси x c которой будет выполнено копирование);
0 — y-координата изображения источника (точка по оси y c которой будет выполнено копирование);
$res_width – ширина участка изображения приемника, куда будет помещено копируемое изображение;
$res_height — высота участка изображения приемника, куда будет помещено копируемое изображение;
$main_img[0] – ширина участка копируемого изображения (в нашем случае указываем полную ширину, так как нам необходимо скопировать все изображения, а не его часть);
$main_img[1] — высота участка копируемого изображения (в нашем случае указываем полную высоту, так как нам необходимо скопировать все изображения, а не его часть);
Далее, нам необходимо перенести водяной знак:
1 2 | imagecopy($res_img,$water,$res_width-$water_width,$res_height-$water_height, 0,0,$water_width,$water_height); |
Для этого используем функцию imagecopy. Параметры, передаваемые данной функции:
$res_img – дискриптор изображения, на которое выполняется копирование (изображение приемник, то которое мы только что создали);
$water – дискриптор копируемого изображения;
$res_width-$water_width – координата по оси х, на изображении приемнике, в которую будет помещено копируемое изображение;
$res_height-$water_height — – координата по оси у, на изображении приемнике, в которую будет помещено копируемое изображение;
0 — x-координата изображения источника (точка по оси x c которой будет выполнено копирование);
0 — y-координата изображения источника (точка по оси y c которой будет выполнено копирование);
$water_width — ширина участка копируемого изображения (в нашем случае указываем полную ширину, так как нам необходимо скопировать все изображения, а не его часть);
$water_height — высота участка копируемого изображения (в нашем случае указываем полную высоту, так как нам необходимо скопировать все изображения, а не его часть).
Далее необходимо в зависимости от того, что содержится в переменной $load, либо вывести изображение на экран, либо сохранить его в файл, что собственно мы и сделаем:
1 2 3 4 5 6 7 | if(!$load) { header("Content-Type:image/jpeg"); imagejpeg($res_img,NULL,100); } else { imagejpeg($res_img,DIR_IMG.$load,100); } |
То есть, если мы не передадим при вызове функции параметр $load, значит, изображение будет выведено на экран. Если же передать через параметр $load имя файла, значит, новое изображение будет сохранено под этим именем. Теперь давайте откроем в браузере файл index.php и посмотрим, как отрабатывает наш скрипт:
Как Вы видите, все отлично работает. На всякий случай приведу полный код данной функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | function get_water($main_img_path, $load = FALSE) { $main_img = getimagesize($main_img_path); if(!$main_img) { exit(); } switch($main_img['mime']) { case 'image/jpeg': case 'image/pjpeg': $img = imagecreatefromjpeg($main_img_path); break; case 'image/png': case 'image/x-png': $img = imagecreatefrompng($main_img_path); break; case 'image/gif': $img = imagecreatefromgif($main_img_path); break; } if(file_exists(WM)) { $water = imagecreatefrompng(WM); } $res_width = $main_img[0]; $res_height = $main_img[1]; $water_width = imagesx($water); $water_height = imagesy($water); $res_img = imagecreatetruecolor($res_width,$res_height); imagecopyresampled($res_img,$img,0,0,0,0,$res_width,$res_height, $main_img[0],$main_img[1]); imagecopy($res_img,$water,$res_width-$water_width,$res_height-$water_height, 0,0,$water_width,$water_height); if(!$load) { header("Content-Type:image/jpeg"); imagejpeg($res_img,NULL,100); } else { imagejpeg($res_img,DIR_IMG.$load,100); } } |
Сохранение обработанного изображения в файле
Для того чтобы не выводить на экран изображение с водяным знаком, а сохранить его в папке. К примеру, это полезно, если выполняется загрузка изображений на сервер через текстовую форму. В этом случае удобно сразу же наносить водяной знак на все изображения (если конечно Вы не планируете в будущем изменять водяной знак). В этом случае можно создать простейшую страницу с формой загрузки файлов и следующим образом вызвать нашу созданную функцию (в моем случае это файл load.php):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?php include "config.php"; include "functions.php"; if($_FILES['load']) { if(move_uploaded_file($_FILES['load']['tmp_name'],DIR_IMG.$_FILES['load']['name'])) { get_water(DIR_IMG.$_FILES['load']['name'],'wm_'.$_FILES['load']['name']); } } ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title></title> <meta name="" content=""> <meta name="vs_targetSchema" content="//schemas.microsoft.com/intellisense/ie5"> </head> <body> <form method="POST" enctype="multipart/form-data"> <input type="file" name="load"> <input type="submit"> </form> </body> </html> |
Если запустить данный файл в браузере, то при загрузке изображения на сервер, будет создаваться его копия с наложенным водяным знаком.
На этом данный урок можно завершать. Всего Вам доброго, удачного кодирования! И увидимся в следующих уроках!

Бесплатный курс по PHP программированию
Освойте курс и узнайте, как создать динамичный сайт на PHP и MySQL с полного нуля, используя модель MVC
В курсе 39 уроков | 15 часов видео | исходники для каждого урока
Получить курс сейчас!
Разработка веб-приложения на PHP
Создайте веб-приложение на PHP на примере приема платежей на сайте
Смотреть
Очень интересная статья, и для меня это очень удивительно, поскольку я даже не представлял, что такое возможно. Спасибо.
Здравствуйте!
Спасибо Вам за комментарий!
Это один из многочисленных примеров использования библиотеки GD языка PHP.
Виктор. Извиняюсь за вопрос не к месту. Для моего сайта я хочу сделать форму обратной связи с НАДЕЖНОЙ капчей. У вас тут есть информация об этом? Если нет, то буду признателен если сделаете. У меня на сайте была страница с формой обратной связи, но в один прекрасный момент капча перестала выполнять роль защиты от спама.
Хороший пост!
здорово!
Виктор,огромное спасибо!
Но я почему то подумал сначала что этот функционал будет прямо на сайте))
потом разобрался это как отдельный сервис,все работает!единственное по размеру не все пропорционально вод.знак весит 7кб а изображение увеличивается в 3 раза..
а как сделать чтобы load по несколько штук за раз загружал..
вы здесь не отвечаете?
Сергей, задайте вопрос на нашем форуме, там удобнее решать вопросы подобного рода.
я не могу зарегится, у Вас капча в неодеквате могу кучу скринов прислать((
С капчей все в порядке, другие пользователи ведь регистрируются. Вы нам писали в службу поддержки. Напишите еще раз и скиньте доступ к Вашему компьютеру через TeamViewer, я подключусь, и мы попробуем зарегистрироваться вместе. Мне почему-то кажется, что вместе у нас получится)
Виктор вы динамически выводите изображение с помощью:
как мне поступить если оно у меня выводится иначе
<img src="product["img"]?>» alt=»product["title"]?>»/>
Отличный урок! Виктор, я пытаюсь приклеить данные из формы к изображению. Не подскажете как это лучше всего сделать используя библиотеку GD?
Заранее благодарен!
Здравствуйте!imagefttext()
В библиотеке GD есть специальные функции, которые позволяют добавить текст непосредственно на изображение. К примеру