При работе над одним своим проектом понадобилось написать PHP-скрипт, который генерирует изображение. С PHP в этом нет ничего сложного.
Поскольку проект уже был сделан на Drupal, возникла мысль, а нельзя ли сделать это используя его возможности, например, для использования некоторых системных функций или подключения к базе данных. Оказалось можно. Причем, как минимум двумя способами: в отдельном файле и в собственном модуле.
Способ первый. Отдельный файл.
Создадим в корне сайта файл с названием image.php. Попробуем вывести изображение с числом материалов размещенных на нашем сайте.
Код очень простой:
/*
Подключаем Drupal и загружаем его.
После вызова функции drupal_bootstrap доступны все функции
и системные переменные Drupal.
*/
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
/*
Отправляем браузеру заголовок типа контента: изображение в формате PNG.
*/
drupal_set_header("Content-type: image/png");
/*
Создаем изображение.
*/
$image = imagecreate(88, 31);
/*
Определяем цвета фона и текста.
*/
$background_color = imagecolorallocate($image, 2, 122, 198);
$text_color = imagecolorallocate($image, 255, 255, 255);
/*
Заливаем фон изображения.
*/
imagefill($image, 0, 0, $background_color);
/*
Запрашиваем из таблицы нод количество материалов сайта.
*/
$query = "SELECT COUNT(`nid`) FROM `{node}`";
$result = db_result(db_query ($query));
/*
Выводим результат в изображение.
*/
imagestring($image, 5, 5, 7, $result , $text_color);
/*
Отдаем изображение в браузер.
*/
imagepng($image);
/*
Освобождаем ресурсы в оперативной памяти сервера.
*/
imagedestroy($image);
Способ второй. Модуль Drupal.
Статичное изображение
Пусть наш модуль называется testimagepng.
Реализуем в файле testimagepng.module хук меню, чтобы наше изображение получило путь на сайте.
$items['testimage.png'] = array(
'type' => MENU_CALLBACK,
'page callback' => 'testimagepng_image',
'access arguments' => array('access content'),
);
return $items;
}
В том же файле разместим функцию testimagepng_image которая и будет генерировать изображение. Код почти совпадает с приведенным в первом способе.
drupal_set_header("Content-type: image/png");
$image = imagecreate(88, 31);
$background_color = imagecolorallocate($image, 2, 122, 198);
$text_color = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $background_color);
$query = "SELECT COUNT(`nid`) FROM `{node}`";
$result = db_result(db_query ($query));
imagestring($image, 5, 5, 7, $result , $text_color);
imagepng($image);
imagedestroy($image);
/*
Прерываем выполнение скриптов, чтобы Drupal не посылал в конце изображения своего вывода.
*/
exit();
}
Достоинством этого метода генерации картинок состоит в том, что в HTML она вставляется при помощи кода, который ничем не отличается от кода для вставки обычного PNG-изображения в виде файла:
Изображение выглядит как статичный файл, но генерировать ее вы можете каким угодно способом внутри функции testimagepng_image().
Динамически генерируемое изображение
Небольшая неточнось, этого заголовка состоит в том, что изображение по прежнему выглядит как статичный файл, но будет генерироваться в зависимости от его названия. Попробуем запрограммировать разбор такого синтаксиса названия файла:
НОМЕР_НОДЫ-РАЗМЕР_ШРИФТА-ШИРИНА-ВЫСОТА-ЦВЕТ_ТЕКСТА-ЦВЕТ_ФОНА.РАСШИРЕНИЕ_ФАЙЛА
Например: 108-10-500-30-FFFFFF-027AC6.gif
При этом на нашем изображении формата gif будет отображаться заголовок ноды 108, шрифтом 10 пикселей, на изображении размером 500 на 30 пикселей, белыми буквами (FFFFFF) на темно-синем фоне (). Т.е. сделаем что-то вроде баннеров.
Итак, пусть модуль называется banner_generator (с полным правом, между прочим).
Реализуем в файле banner_generator.module хук меню чуть более сложный, чем выше.
$items['banners/%'] = array(
'page arguments' => array(1),
'type' => MENU_CALLBACK,
'page callback' => 'banner_generator_generate',
'access arguments' => array('access content'),
);
return $items;
}
После включения модуля в структуре нашего сайта появится адрес http://example.com/banners/ приобращении к которому все, что мы наберем после завершающего слеша попадет в функцию banner_generator_generate () в качестве первого параметра. Т.е., если мы запросим адрес http://example.com/banners/108-10-500-30-FFFFFF-027AC6.gif, то функции будет передан аргумент 108-10-500-30-FFFFFF-027AC6.gif.
Реализуем требуемую функцию.
/*
Для отображения нелатинского текста на изображениях нам потребуется файл шрифта формата True Type
Файл должен лежать в папке модуля. Взять его можно из папки шрифтов Windows.
Ф случае отсутствия файла Drupal сгенерирует страницу Page not found.
*/
$font = drupal_get_path('module', 'banner_generator') . "/arial.ttf";
if (!file_exists($font)) return drupal_not_found();
/*
Разбиваем полученый параметр на имя файла и расширение.
*/
$tmp = explode('.', $file);
$name = array_shift($tmp);
$ext = strtolower(array_pop($tmp));
/*
Разбиваем по дефисам имя файла на отдельные компоненты.
*/
$tmp = explode('-', $name);
/*
Получаем идентификатор ноды и достаем заголовок нужной страницы из базы.
*/
$nid = (int) array_shift($tmp);
$query = "SELECT `title` FROM `{node}` WHERE `nid`=%d";
$result = db_result(db_query ($query, $nid));
if ($result===FALSE) return drupal_not_found();
/*
Получаем размер шрифта. В случае, слишком большого, неправильного
или отсутствующего значения ставим по умолчанию в 15px.
*/
$size = (int) array_shift($tmp);
$size = $size < 1 ? 15 : $size;
$size = $size > 100 ? 15 : $size;
/*
Определяем размеры контейнера требуемого для текста.
*/
list($lower_left_X, $lower_left_Y, $lower_right_X, $lower_right_Y, $upper_right_X, $upper_right_Y, $upper_left_X, $upper_left_Y) = imagettfbbox ($size, 0, $font, $result);
$box_width = max($lower_left_X, $lower_right_X, $upper_right_X, $upper_left_X) - min ($lower_left_X, $lower_right_X, $upper_right_X, $upper_left_X) + 1;
$box_height = max($lower_left_Y, $lower_right_Y, $upper_right_Y, $upper_left_Y) - min($lower_left_Y, $lower_right_Y, $upper_right_Y, $upper_left_Y) + 1;
/*
Теперь получаем требуемую ширину изображения. Если она отсутствует, слишком мала или велика,
то устанавливаем ее чуть больше контейнера текста
*/
$width = (int) array_shift($tmp);
$width = $width <1 ? $box_width + $size : $width;
$width = $width > 1000 ? $box_width + $size : $width;
$height = (int) array_shift($tmp);
$height = $height <1 ? $box_height + $size : $height;
$height = $height > 1000 ? $box_height + $size : $height;
/*
Создаем изображение рассчитанных размеров
*/
$image = imagecreatetruecolor($width, $height);
if (!$image) return drupal_not_found();
/*
Получаем и задаем для изображения цвет текста и разбираем его на
красный, зеленый и голубой цвета. Все ошибки трактуются в пользу отсутствия цвета.
*/
$color = array_shift($tmp);
$red = substr($color, 0, 2);
$red = $red ? $red : '00';
$green = substr($color, 2, 2);
$green = $green ? $green : '00';
$blue = substr($color, 4, 2);
$blue = $blue ? $blue : '00';
$text_color = imagecolorallocate($image, hexdec($red), hexdec($green), hexdec($blue));
/*
Получаем и задаем для изображения цвет фона и разбираем его на
красный, зеленый и голубой цвета. Все ошибки трактуются в пользу наличия цвета.
Заливаем фон.
*/
$color = array_shift($tmp);
$red = substr($color, 0, 2);
$red = $red ? $red : 'FF';
$green = substr($color, 2, 2);
$green = $green ? $green : 'FF';
$blue = substr($color, 4, 2);
$blue = $blue ? $blue : 'FF';
$background_color = imagecolorallocate($image, hexdec($red), hexdec($green), hexdec($blue));
imagefill($image, 0, 0, $background_color);
/*
Рассчитываем координаты левого нижнего угла контейнера текста
(это особенность функции imagettftext) так, чтобы контейнер встал
по центру изображения. После этого выводим текст в изображение.
*/
$x = (int) ($width - $box_width)/2;
$y = (int) ($height + $box_height - $size/2)/2;
imagettftext ($image, $size, 0, $x, $y, $text_color, $font, $result);
/*
Теперь в зависимости от расширения файла посылаем браузеру клиента соответствующие заголовкии
и отдаем изображение. В случае неправильного расширения генерируем ошибку Page not found.
*/
switch ($ext) {
case 'png':
drupal_set_header("Content-type: image/png");
[user=imagepng]imagepng[/user]($image);
break;
case 'gif':
drupal_set_header("Content-type: image/gif");
[user=imagegif]imagegif[/user]($image);
break;
case 'jpg':
case 'jpeg':
drupal_set_header("Content-type: image/jpeg");
[user=imagejpeg]imagejpeg[/user]($image);
break;
default:
imagedestroy($image);
return drupal_not_found();
}
/*
Освобождаем ресурсы в оперативной памяти сервера и завершаем исполнение скриптов,
чтобы Drupal не добавил свой вывод.
*/
imagedestroy($image);
exit();
}
Вставлять в код сайта такое динамическое изображение можно обычным способом. Например, код баннера этой статьи будет выглядеть так:
Вернее так:
Комментарии
Пригодится, спасибо.
Отличная работа. Очень понятно написано. Спасибо. Даже не знал, что можно так просто это сделать!
Меня со вчерашнего дня терзали смутные сомнения, что должно быть просто.
Оказалось так и есть...
очень большая разница между отработчиком меню для AJAX и для вывода картинки, да. очень.
в Content-type
Мы такое уже давно делали.
Супер. Очень просто и оригинально.
Очень интересно. Скажите, а нельзя ли подобным образом выделять в постах заглавные литеры?
[deleted]
Но лучше всего использовать возможности CSS
Коротко и ясно! Спасибо.
Интересно, спасибо. Но... в плане практического применения, идей нет. А что думает народ?
Недавно нечто похожее делал для защиты изображений от не авторизованных пользователей
Можно строить какие-нибудь графики, чарты. Например, карму выстраивать.
Насчёт применения. Я думаю, что так стоит выводить только динамический контент (Капча?), который нельзя вывести текстом на фоне картинки.
Решение было сохранено на сайте DrupalCookBook.ru:
Генерация картинок на Drupal-сайте.
Авторы, предложившие решения, также указаны в сохранённой статье.
С графиками отличная идея, надо подумать...
Я старую свою програмку для транскрипции имен на китайский собираюсь так переделать.
Трабла первого варианта в том, что этот файл никуда не положить, кроме как в корень проекта.
По результатам своей работы за прошлую неделю серьезно проапгрейдил статью. Дописал еще один модуль для динамических изображений.
cool
Столько времени прошло, а пригодилось, спасибо.
Йо майо ... как раз бубунец напрягал как друпалом это сделать. Спасибо!
блин, руки кривые (((
первый вариант с php получился, но надо png
никак не могу сделать модуль!!!
вроде и папку создавал, и файл туды клал ... никак (((
блин, руки кривые (((
первый вариант с php получился, но надо png
никак не могу сделать модуль!!!
вроде и папку создавал, и файл туды клал ... никак (((
разобрался: надо создать testimagepng.info
я взял .info с workspace
и вставил в testimagepng.info
name = testimagepng
description = testimagepng.
core = 6.x
; Information added by drupal.org packaging script on 2008-07-24
version = "6.x-1.3"
core = "6.x"
project = "testimagepng"
datestamp = "1216932917"
в testimagepng.module надо в начало вставить <?рhр
после чего заработало )
Огромное спасибо!! Очень полезный пост.
Пример заработал даже под 7
Да не за что. Я уже и забыл про эту статью.