Оквадрачивание всех картинок внутри директории

Date January 20th, 2012 Author Vitaly Agapov

Взгляни на его форму: квадрат, воплощение коварства!

— м/ф «SpongeBob SquarePants»

Возникла необходимость сделать все изображения в одной директории квадратными, причём таким образом, чтобы не нарушить центровку изображений. В моём случае это была директория с файлами изображений Drupal, поэтому задача разбивается на две части. Первая часть – универсальная – как автоматически отредактировать все изображения. Вторая часть – для Drupal – как обновить данные о новых изображениях в базе данных.

Пользоваться будем нашим любимым Perl и его модулем для работы с библиотекой ImageMagick.

Часть 1. Оквадрачиваем

Для достижения цели я набросал такой вот скрипт quad.pl:

01.#!/usr/bin/perl
02.use Image::Magick;
03.use POSIX;
04. 
05.my $path="/var/www/drupal/sites/default/files";
06. 
07.# Пробежимся по всем файлам и для каждого вызовем функцию quad_image()
08.my @files = <$path/*.jpg>;
09.foreach $file (@files) {
10.    quad_image($file, $file);
11.}
12.exit(0);
13. 
14.sub quad_image {
15.    my ($in_img, $out_img) = @_;
16.    if ( !(-f "$in_img") ) {
17.         die ("Image file not found");
18.    }
19.    my $q;
20.    # Создадим новый объект Image::Magick
21.    $q = Image::Magick->new;
22.    $q->Read("$in_img");
23.    # Узнаем ширину и высоту изображения
24.    my ($x_size, $y_size) = $q->Get('width', 'height');
25. 
26.    # Если один из параметро нулевой, то файл - битый
27.    if ($x_size == 0 || $y_size == 0){
28.            return ("Unable to get filesize: $in_img.");
29.        }
30. 
31.    my $width;
32.    if ( $x_size > $y_size ) {
33.        $width=$x_size;
34.        my $yoffset = floor( ($x_size - $y_size)*0.5 );
35.        # Растягиваем высоту до значения ширины
36.        $q->Extent(geometry=>$x_size."x".$y_size."+0-".$yoffset, width=>$width, height=>$width);
37.    }
38.    elsif ( $y_size > $x_size ) {
39.        $width=$y_size;
40.        my $xoffset = floor( ($y_size - $x_size)*0.5 );
41.        # Растягиваем ширину до значения высоты
42.        $q->Extent(geometry=>$x_size."x".$y_size."-".$xoffset."+0", width=>$width, height=>$width);
43.    }
44. 
45.    if ( $width ) {
46.            # Небольшой необязательный кусок. Здесь мы добавляем надпись на преобразованное изображение
47.            # Вычисляем размер букв
48.            my $pointsize = floor( $width*0.04 );
49.            # Вычисляем координаты надписи
50.            my $x = floor( $width*0.06 );
51.            my $y = $width - $x;
52.            my $text = 'my watermark';
53.            # Вставляем надпись
54.            $q->Annotate(x=>$x, y=>$y, pointsize=>$pointsize, fill=>'grey', text=>$text);
55.     
56.            # Дальше устанавливаем качество JPEG-файла
57.            $q->Set(quality=>75);
58.            # И записываем файл
59.            $q->Write("$out_img");
60.    }
61.}

С комментариями внутри скрипта должно вроде бы бть всё ясно.

Часть 2. Подгоняем Drupal

Вторую часть можно не читать тем, кому просто был интересен механизм преобразования изображений. У меня же была задача сделать квадратными все изображения товаров и каталогов Ubercart. Если просто оквадратить вышеприведённым скриптом все изображения и удалить все стили изображений из поддиректории styles, то пропорции всех выводимых изображений на странице исказятся. Это всё оттого, что ширина и высота изображений хранятся в базе данных и используются для установки атрибутов при выводе изображений на страницу.

Поэтому, когда я обнаружил такой вот эффект, пришлось срочно писать ещё один скрипт, исправляющий всё это безобразие. Скрипт просматривает все изображения в директории, сравнивает их размер  со значением поля filesize таблицы file_managed и если находит несоответствие, то исправляет это значение на новое и заодно правит значения width и height во всех полях таблиц, ссылающихся на fid этого файла.

Вот и сам скрипт repair_drupal.pl.

01.#!/usr/bin/perl
02.use Image::Magick;
03.use POSIX;
04.use DBI();
05. 
06.my $path="/var/www/drupal/sites/default/files";
07.glob $db="mydb";
08.glob $dbuser="myuser";
09.glob $dbpwd="pypwd";
10.$dbh = DBI->connect("DBI:mysql:database=$db;host=localhost",$dbuser,$dbpwd,{'RaiseError' => 1});
11. 
12.        $dbh->do("SET NAMES utf8");
13.        $dbh->do("SET CHARACTER SET utf8");
14. 
15.my @files = <$path/*.jpg>;
16.foreach $file (@files) {
17.    repair_drupal($file);
18.}
19.exit(0);
20. 
21.sub repair_drupal {
22.    my $in_img = $_[0];
23.    if ( !(-f "$in_img") ) {
24.         die ("Image file not found");
25.    }
26.    # Откусываем название файла от пути
27.    $in_img =~ /([^\/]+)$/;
28.    my $filename = $1;
29.    # Получаем размер файла в байтах
30.    my $filesize = -s $in_img;
31.    my $q;
32.    $q = Image::Magick->new;
33.    $q->Read("$in_img");
34. 
35.    my ($x_size, $y_size) = $q->Get('width', 'height');
36.    if ($x_size == 0 || $y_size == 0){
37.            return ("Unable to get filesize: $in_img.");
38.        }
39.    # Получаем размер этого файла, который указан в базе данных
40.    my $sql = "SELECT fid,filesize FROM file_managed WHERE filename = '".$filename."'";
41.    print $sql."\n";
42.    my $sth=$dbh->prepare($sql);
43.    $sth->execute();
44.    if ( $ref = $sth->fetchrow_hashref() ) {
45.        # Если размеры не совпадают, то проворачиваем все нужные действия
46.        if ( $ref->{filesize} != $filesize ) {
47.            print "Filesizes differ! (".$ref->{filesize}."->".$filesize.")\n";   
48.            $sql = "UPDATE file_managed SET filesize=".$filesize;
49.            print $sql."\n";
50.            $dbh->do($sql);
51.            # Если речь не про Ubercart, то
52.            # Вместо uc_product и uc_catalog будут другие таблицы
53.            $sql = "UPDATE field_data_uc_product_image SET uc_product_image_width = ".$x_size.",uc_product_image_height=".$y_size." WHERE uc_product_image_fid=".$ref->{fid};
54.            print $sql."\n";
55.            $dbh->do($sql);
56.            $sql = "UPDATE field_revision_uc_product_image SET uc_product_image_width = ".$x_size.",uc_product_image_height=".$y_size." WHERE uc_product_image_fid=".$ref->{fid};
57.            print $sql."\n";
58.            $dbh->do($sql);
59.                        $sql = "UPDATE field_data_uc_catalog_image SET uc_catalog_image_width = ".$x_size.",uc_catalog_image_height=".$y_size." WHERE uc_catalog_image_fid=".$ref->{fid};
60.                        print $sql."\n";
61.                        $dbh->do($sql);
62.                        $sql = "UPDATE field_revision_uc_catalog_image SET uc_catalog_image_width = ".$x_size.",uc_catalog_image_height=".$y_size." WHERE uc_catalog_image_fid=".$ref->{fid};
63.                        print $sql."\n";
64.                        $dbh->do($sql);
65.        }
66.        else {
67.            print "Filesizes OK\n";
68.        }
69.        }
70.    else {
71.        print "\n!!!!! No such file! \n";
72.    }
73.}

Итак, чтобы сделать квадратными все изображения в Drupal, надо сделать следующее:

  1. Преобразовать все картинки в sites/default/files скриптом из первой части.
  2. Удалить все миниатюры и прочие стили изображений из sites/default/files/styles.
  3. Поправить информацию о параметрах изображений в базе данных скриптом из второй части.
  4. Очистить кэш.

Ссылки

Документация по модулю Image::Magic
http://www.imagemagick.org/script/perl-magick.php

Tags: ,
Category: Drupal, Perl | No comments »

Comments

Leave a comment

 Comment Form 

Rich Text Editor, comment