Зачем и почему
Эту статью меня побудил написать гневный отзыв одного хабрапользователя, заявившего, что, в переводе на русский, звучит примерно так: «хорош писать комментарии, пиши что-то полезное».
Послав его куда подальше и немного подумав, я решил, что он-таки прав, тем более что я вроде как не первый год пишу всякий код и наверняка есть, о чем рассказать — а вдруг кому пригодиться. Оглянувшись назад, вспомнилось, что как-то была задачка автоматизировать создание фотоальбома. Так почему бы не написать об этом?
Зачем и почему — 2
Я сам любитель фотографировать, и естественно, надо бы свои творения показывать человекам. Для этого интернет — самое подходящее место. Первое, что пришло на ум в свое время — это Picasa. Но тут были моменты, которые мне сильно не нравились. Это
- ограниченный объем места;
- фотографии лежат не у меня, и мало ли что может с ними случиться, а трудов жалко.
Что нравилось — это то, что пикасофская софтинка позволяла массово грузить фотки и создавать альбом, а это важно, когда фоток много.
Все бы ничего, но случилось чудо — у меня появился свой веб-сервер. Соответственно захотелось держать все фотки у себя. К счастью, пикаса умела делать и локальные фотоальбомы — достаточно настроить нужные шаблоны — и в нужное время жать нужную кнопку. Пока меня все устраивало, но стало напрягать то, что большая красивая пикаса и линукссовый сервер вместе не смотрелись. Но какое-то время этот подход работал, ну и фиг бы с ним, но…
Тут я полностью перебрался под линуксы, соответственно запускать виндовую пикасу стало напряжно. Это был весомый аргумент к поиску нового решения. Таким решением стал скриптик, который позволял генерировать фотоальбом под линуксом, запускался из консоли и не требовал графического интерфейса.
Требования
Итак, что я хотел получить:
- скрипт, запускаемый из консоли, без каких либо тыканий мышой, по типу «запустил и забыл»;
- уменьшение фоток до нужного размера для просмотра;
- уменьшение до маленького размера, дабы показать в каталоге;
- поворот картинок если надо;
- декорирование картинок и вставка копирайта;
- генерирование html-страничек для показа всего этого добра;
- отсутствие привязки к БД.
Поехали
Раз нам надо запуск из консоли, то выбор сразу пал на Perl — просто, быстро и есть опыт написания скриптов на нем.
Все операции с ка��тинками я возложил на ImageMagick. Тут столкнулся с тем, что непонятно, картинка вертикальная или горизонтальная, по какой оси ужимать. Наверно тут есть несколько способов решения, недавно на хабре проскакивал рецепт. Я решил так: командочкой «identify» получаем параметры картинки и смотрим, что больше — первый размер или второй. И в зависимости от этого жмем картинку с нужным параметром.
Детектим размерчик
Тут пришлось учесть один момент, то что имя файла может содержать пробелы и то, что в выводе от команды «identify» тоже пробелы используются как разделители. Немного танцев с бубном — и все работает.
sub getSize { my $fname = shift; my $cmd = escapeShell ("identify '$fname'"); my $info = `$cmd`; my $fname_len = length($fname); $info =~ s/^.{$fname_len}(.*)$/$1/; my $type; my $size; ($type, $size, undef) = split (' ', $info); my ($width, $height) = split ('x', $size); return ($width, $height); }
Тут пришлось учесть один момент, то что имя файла может содержать пробелы и то, что в выводе от команды «identify» тоже пробелы используются как разделители. Немного танцев с бубном — и все работает.
По размеру делаем ресайз
sub resize { my $size = shift; my $fname_in = shift; my $fname_out = shift; my ($w, $h) = getSize ($fname_in); print "W: $w, H: $h\n"; my $cmd; if ($w > $h) { $cmd = "convert '$fname_in' -resize $size -auto-orient '$fname_out'"; } else { $cmd = "convert '$fname_in' -resize x$size -auto-orient '$fname_out'"; } print "CMD: $cmd\n"; $cmd = escapeShell($cmd); `$cmd`; }
Далее, я помнил, что пикаса приделывала тени к превью картинок. Порыв инет, оказалось, что это абсолютно не проблема и решается с помошью уже упомянутой ImageMagick. Рецепт взял отсюда.
Делаем тень
sub shadow { my $fname_in = escapeShell(shift); my $fname_out = escapeShell(shift); my $cmd = "convert -page +3+3 '$fname_in' -matte " . "\\( +clone -background black -shadow 70x2+2+2 \\) " . " +swap -background '#9AB6D7' -mosaic '$fname_out'"; `$cmd`; }
Пока разбирался с тенями, оказалось, что можно приделать к фоткам еще и watermark. А почему бы и нет? Рисуем свою картинку с подписью и накладываем на фотку с помощью того же ImageMagick. Рецепт.
Вставляем картинку с копирайтом
sub signImg { my $sign = shift; my $fname_in = shift; my $fname_out = shift; my $cmd = "convert $sign -fill grey50 -colorize 40 miff:- | " . " composite -dissolve 30 -gravity south - '$fname_in' " . " '$fname_out'"; $cmd = escapeShell($cmd); `$cmd`; }
Вообще с картинками можно творить все, что угодно, там много примеров, каждый найдет себе то, что ему по душе.
Возникла проблема html-обвязки. Обычно на больших сайтах информация лежит в базе данных и в УРЛе передается ИД картинки и все хорошо и красиво. Но у меня на моем серверочке не было базы — она туда просто не помещалась, потому что сервер представлял собой допиленный роутер ASUS

Передавать имя файла не хотелось из соображений секурности. Оставался выход — для каждого файла генерить свой html. Тоже не есть красиво.
На ум пришла идея, заимствованная из линуксов — одна и та же команда может иметь несколько симлинков, и в зависимости от имени симлинка она делает соответствующие дейстия. Итак, решение: пиш��м один темплейтик для показа картинки, создаем массивчик, в котором прописаны все имена файлов — это достаточно секурно, и создаем кучу симлинков, где имя симлинки совпадает с индексом массива картинок. Все — вот она база, собранная на коленке. А по имени файла лезем в массивчик и вытаскиваем имя файла картинки.
Генерируем PHP-шки для сайта
sub genPhpFiles { # Check if index exists already my $new_index = undef; if (-f "$dir_in/photo_index.inc") { `cp $dir_in/photo_index.inc $dir_out_html/photo_index.inc`; } else { $new_index = 1; open F, ">$dir_out_html/photo_index.inc"; print F '<? $desc = array ('; print F "\n"; } # Gen index and symlinks opendir(my $dh, $dir_out_img) || die "Can't read dir `$dir_in': $!\n"; my $idx = 0; my @files = readdir $dh; @files = sort @files; foreach (@files) { next if ($_ =~ m/^\./ || $_ =~ m/\.inc$/); if ($new_index) { print F "array ('name' =>'$_', 'desc' => '$_: '),\n"; } `ln -s template.php $dir_out_html/$idx.php`; $idx++; } closedir $dh; if ($new_index) { print F "\n); ?>"; close F; } }
А так внутри PHP-шки определяем имя файла картинки по имени симлинка
<?php // Extract number from file name and use it as ID $file = $_SERVER["SCRIPT_NAME"]; $id = preg_replace('/.*\/(\d+)\.php$/', '$1', $file); ?>
Ну вот вроде и все — все требования выполнены.
Хм, получилось даже больше, чем я хотел изначально…
Пользуюсь этим скриптиком вот уже на протяжении нескольких месяцев, практически ничего не меняя, и остаюсь вполне доволен — он делает все, что было нужно — одним запуском командочки генерит и картинки, и html-ки, остается только положить на ��ервер.
Вот так выглядит сгенерированный результат в браузере:
Индекс фотографий

Отдельная фотография

Как пользоваться
Если кому нужно — то все скопом лежит в архивчике.
Пользоваться элементарно:
- распаковать архивчик
- поправить стили и темплейты под ваш дизайн (photo.css, photo.inc, photo_list.inc, template.php, index.php)
- в каталог «in» сложить исходные картинки
- запустить «make_photo_album» и получить в каталоге «out» картинки нужного размера и сгенерированные html-ки.
Канэц. Точка.