Génération automatique des versions multiples pour SPGM
Par Julien Wajsberg le samedi 7 mai 2005, 18:23 - Informatique - Lien permanent
J'en avais déjà parlé, j'avais fait un patch pour SPGM, afin d'autoriser plusieurs versions d'une même image. L'utilisation principale que j'en fais, c'est pour proposer plusieurs tailles d'image aux visiteurs.
Or, je viens de m'apercevoir que je n'avais pas publié mon p'tit script Perl pour générer les différentes tailles automatiquement :)
Ça nécessite exiftran, Image::Magick (paquet Debian : perlmagick) et Image::EXIF (paquet Debian : libimage-exif-perl).
Il suffit de lancer ce script depuis le répertoire gal de SPGM.
S'il y a besoin de modifier les paramètres de configuration pour un répertoire donné et ses sous-répertoires, il est possible de le faire dans un fichier appelé .conf dans le répertoire en question (voir tout en bas). Ça ne fonctionne que pour des scalaires, pas pour des tableaux; donc c'est pas possible de modifier les différentes tailles, par exemple... sauf si quelqu'un me file un patch :-p.
create_thb.pl
#! /usr/bin/perl
#
# by Julien Wajsberg <flash@minet.net>
#
# January 2005
# GPLv2
use strict;
use warnings;
use Image::Magick;
use Image::EXIF;
use File::Find ();
#### conf ####
my $default_conf = {
thumbnail_prefix => "_thb_",
internal_pattern => qr/^_/,
thumbnail_geometry_landscape => "160x120",
thumbnail_geometry_portrait => "160x120",
resizes_landscape => qw/800x600 1024x768/ ,
resizes_portrait => qw/600x800 768x1024/ ,
quality => 80,
};
my $conf_filename = ".conf";
my $conf = $default_conf;
##############
my @transprogram = qw/exiftran -ai/;
$| = 1;
my $image = new Image::Magick;
# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name = *File::Find::name;
*dir = *File::Find::dir;
*prune = *File::Find::prune;
sub wanted;
File::Find::find({
wanted => \&wanted,
preprocess => \&preprocess,
postprocess => \&postprocess,
}, '.');
exit;
##############################
#
# "wanted" function for "find"
#
sub wanted {
doaction($_) unless -d;
}
###############################
#
# "preprocess" function for "find"
#
sub preprocess {
print "*** Entering $File::Find::dir\n";
# read config file for this directory
if (-r $conf_filename) {
open CONF, $conf_filename;
while (<CONF>) {
my ($key, $value) = /^\s*(\w+)\s*=\s*(\w+)$/;
next unless $key and $value;
$conf->{$key} = $value;
}
close CONF;
}
my @entries;
while ($_ = shift) {
next if /$conf->{internal_pattern}/;
push @entries, $_ if /^.*\.jpg\z/is;
push @entries, $_ if -d;
}
return @entries;
}
###############################
#
# "postprocess" function for "find"
#
sub postprocess {
# reset conf
$conf = $default_conf;
}
###################################
#
# this function is called by wanted
#
# 1st arg : shortname of the file
#
sub doaction($) {
my $shortname = shift;
my $x;
my $thumbnail = $conf->{thumbnail_prefix} . $shortname;
my $resizes;
my $thumbnail_geometry;
print $shortname, " :\n";
# rotating if necessary
if (need_rotation($shortname)) {
print " rotating";
$x = dorotate($shortname);
if ($x) {
warn " : $x" if $x;
print " -> something strange happened, let's skip this file !\n";
return;
}
print ".\n";
} else {
print " already correctly oriented.\n";
}
# pinging the file, to get the dimensions
my ($width, $height) = $image->Ping($shortname);
# orientation : landscape or portrait
if ($width < $height) {
$resizes = $conf->{resizes_portrait};
$thumbnail_geometry = $conf->{thumbnail_geometry_portrait};
} else {
$resizes = $conf->{resizes_landscape};
$thumbnail_geometry = $conf->{thumbnail_geometry_landscape};
}
if (all_resizes_done($shortname, $resizes)) {
print " nothing left to do.\n";
return;
}
# reading the file
print " reading";
$x = $image->Read($shortname);
if (defined $x and "$x") {
warn " : $x";
print " -> something strange happened, let's skip this file !\n";
return;
}
print ".\n";
# thumbnail generation
my $clone = $image->Clone();
if (! ref $clone) {
warn " : $clone";
print " -> something strange happened, let's skip this file !\n";
return;
}
print ' thumbnail : resizing to ', $thumbnail_geometry;
$x = doresize($clone, $thumbnail_geometry, $thumbnail);
if (defined $x and "$x") {
warn " : $x";
print " -> something strange happened, let's skip this file !\n";
return;
}
# various geometries generations
foreach my $geometry (@$resizes) {
my $clone = $image->Clone();
my $outputfile = '_' . $geometry . '_' . $shortname;
print " resizing to $geometry";
$x = doresize($clone, $geometry, $outputfile);
if (defined $x and "$x") {
warn " : $x";
print " -> something strange happened, let's skip this file !\n";
return;
}
}
@$image = ();
}
#############################################
#
# resizes an image
#
# 1st arg : imagemagick object
# 2nd arg : destination geometry
# 3rd arg : name of the file to write
# returns an error string if an error happens
#
sub doresize($$$) {
my ($image, $geometry, $outputfile) = @_;
do { print " -> $outputfile already exists.\n"; return; } if (-f $outputfile);
my ($destwidth, $destheight) = split /x/, $geometry;
do { print " -> original image too small.\n"; return; }
unless ($image->Get('width') > $destwidth or $image->Get('height') > $destheight);
my $x = $image->Resize(geometry => $geometry);
return "$x" if (defined $x and "$x");
print ", writing file $outputfile";
$x = $image->Write(filename => $outputfile, quality => $conf->{quality});
return "$x" if (defined $x and "$x");
print ".\n";
return;
}
#################################################
#
# losslessly rotates an image using @transprogram
#
# 1st arg : image filename
#
sub dorotate($) {
my $filename = shift;
system "@transprogram $filename 1> /dev/null 2>&1";
if ($? == -1) {
return "failed to execute: $!";
} elsif ($? & 127) {
return sprintf ("child died with signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without');
} else {
my $value = $? >> 8;
return "child exited with value $value" if ($value);
}
return;
}
#################################################
#
# looks for already existing resized files
#
# 1st arg : image filename
# 2nd arg : resizes array
#
sub all_resizes_done ($$) {
my ($name, $resizes) = @_;
# looking for the thumbnail
my $thumbnail = $conf->{thumbnail_prefix} . $name;
return 0 if ( ! -f $thumbnail);
# looking for other versions
foreach my $geometry (@$resizes) {
my $version = '_' . $geometry . '_' . $name;
return 0 if ( ! -f $version);
}
return 1;
}
#################################################
#
# returns true if the filename need to be rotated
#
# 1st arg : image filename
#
sub need_rotation ($) {
my $filename = shift;
my $exif = new Image::EXIF($filename);
my $info = $exif->get_image_info();
return 0 if
or ($info->{'Image Orientation'} eq 'Top, Left-Hand'));
return 1;
}
Exemple de fichier .conf utilisé pour La Sélection
thumbnail_geometry_portrait = 384x512 thumbnail_geometry_landscape = 640x480 quality = 90