May 2009
In an earlier text the idea/ implementation of a local Perl lib was discussed. In this text a better example of writing a Perl library is performed but in the context of creating Perl interfaces to a command line utility which can in turn be called as subroutines from a Perl program.
The enlightenment transform utility or etu for short takes a variety of options to scale up or down the size of an image leveraging imlib2. The syntax (as of this writing) is:
etu [options][arguments] etu [-D|--daemonize interval] etu [-d|--dir dir][-s|--src][-h|--height height] etu [-w|--width width][-q|--quality percent] etu [-f|--file filename][-o|--output filename] Single file Options: -f|--file file Single input image filename -o|--output file Output image filename Directory Options: -s|--src dir Original images directory -d|--dir dir Output directory Global Options: -h|--height size Height in pixels default: 96px -q|--quality level Quality level (1-100) default: 75% -w|--width size Width in pixels default: 128px
For the sake of argument the goal is to write a Perl lib which other Perl programs can call which performs the following:
Essentially wrap the program's full functionality less the daemonize capability. Given that the nature of etu is known and the desired functions are known the sub routines and their arguments can be laid out easily:
Note the redundant arguments of width, height and quality. In order to simplify the work those could be commoned up into an initializer that sets up the arguments then calls the appropriate function.
etu_init()
$etu = "/usr/local/bin/etu"; # We do not protect this so the programmer
# might override it
# Match the defaults of etu for intilization
my ($width, $height, $quality) = (0,0,0);
###
# etu_init: Initialize the common arguments for the functions
# Although it looks useless we can always use it for
# for more stuff later and it makes the caller look cleaner
#
sub etu_init()
{
($width, $height, $quality) = $@_;
}
1; # Main - return a true value
=head1 DESCRIPTION
This small library provides a Perl interface to the etu program. There are
five subroutines to initialize image parameters, scale single images,
update single images, scale entire directories and update entire
directories. Note that etu does not support recursion so the directories
must be flat. Also note that if etu_init() is not used the etu program
will simply use the defaults (see etu -u for details).
The usage for each is:
etu_init (image_width_in_pixels, image_height_in_pixels,
image_quality_percent);
=head1 EXAMPLES
etu_init (800, 600, 90);
=head1 PREREQUISITES
The etu utility must be installed on your OS.
=head1 COREQUISITES
none
=cut
Note that the documentation part of the lib is already started, this will save time later as well. Here is what the perldoc output looks like:
ETU(1) User Contributed Perl Documentation ETU(1)
DESCRIPTION
This small library provides a Perl interface to the etu program. There
are five subroutines to initialize image parameters, scale single
images, update single images, scale entire directories and update
entire directories. Note that etu does not support recursion so the
directories must be flat. Also note that if etu_init() is not used the
etu program will simply use the defaults (see etu −u for details).
The usage for each is:
etu_init(image_width_in_pixels, image_height_in_pixels,
image_quality_percent_value);
EXAMPLES
etu_init (800, 600, 90);
PREREQUISITES
The etu utility must be installed on your OS.
COREQUISITES
none
perl v5.8.8 2009—05—15 ETU(1)
Now time to do all of the transform functions but there is one
small problem; when invoking etu if any of the three initializer
variables (width, height, quality) were not set in
etu_init and they are passed they will all equal zero!
There is a cure, build the argument string by seeing if they
exist:
###
# argvector: A helper for each of the transform calls determine
# whether or not the 3 globals have been set (w,h,q)
###
sub argvector()
{
my @args;
if ($width) {
push (@args, " -w $width ");
}
if ($height) {
push (@args, " -h $height ");
}
if ($quality) {
push (@args, " -q $quality");
}
return (@args);
}
Note that all three are checked, that is in case someone for
some bizarre reason passed a 0 to etu_init().
Time to create the transform functions. Since etu checks on the existence of directories and files and creates new ones as needed, there really isn't much to the functions at all, first the single image scaling:
###
# etu_scale_image: Scale an image
###
sub etu_scale_image
{
my ($src, $dst) = @_;
my @etu_extargs = argvector();
system("$etu -f $src -d $dst @etu_extargs");
}
Ironically, etu will update a pre-existing image if any of the
parameters have changed or the source file has changed
automatically using the scale function. In order to force
an
update all that is needed is a thin wrapper to
etu_scale_image:
###
# etu_update_image: etu automatically updates so we just wrap scale
###
sub etu_update_image
{
etu_scale_image(@_);
}
Now of course doing the same for the directories is trivial:
###
# etu_directory_scale/update: Setup and call etu to scale. Note we update
# by default so just thin-wrap the scale call.
###
sub etu_scale_dir
{
my ($src, $dst) = @_;
my @etu_extargs = argvector();
system("$etu -s $src -d $dst @etu_extargs");
}
sub etu_update_dir { etu__scale_dir(@_); }
The observant will notice in the two worker functions there is redundancy:
$etu is called twiceargvector() is called twiceWhile the external functions will not change at all by adding a single function which:
etu with the correct options (file or
directory)Some coding can be saved:
###
# exec_etu: execute etu with all the arguments provided
###
sub exec_etu
{
my @etu_extargs = argvector;
system("$etu @_ @etu_extargs");
}
And now the functions look like so:
###
# etu_scale_image and update: Setup and call etu to scale. Note we update
# by default so just thin-wrap the scale call.
###
sub etu_scale_image
{
my ($src, $dst) = @_;
etu_exec("-f $src -o $dst");
}
sub etu_update_image { etu_scale_image(@_); }
###
# etu_directory_scale/update: Setup and call etu to scale. Note we update
# by default so just thin-wrap the scale call.
###
sub etu_scale_dir
{
my ($src, $dst) = @_;
etu_exec("-s $src -d $dst");
}
sub etu_update_dir { etu__scale_dir(@_); }
$etu = "/usr/local/bin/etu"; # We do not protect this so the programmer
# might override it
my ($width, $height, $quality) = (0,0,0); # We know we need em so set em up
###
# etu_init: Initialize the common arguments for the functions
# Although it looks useless we can always use it for
# for more stuff later and it makes the caller look cleaner
###
sub etu_init
{
($width, $height, $quality) = @_;
}
###
# argvector: Build the tail end of the argument vector based on
# whether or not the 3 globals have been set (w,h,q)
###
sub argvector
{
my @args;
if ($width) {
push (@args, " -w $width ");
}
if ($height) {
push (@args, " -h $height ");
}
if ($quality) {
push (@args, " -q $quality");
}
return (@args);
}
###
# exec_etu: execute etu with all the arguments provided
###
sub exec_etu
{
my @etu_extargs = argvector;
system("$etu @_ @etu_extargs");
}
###
# etu_scale_image and update: Setup and call etu to scale. Note we update
# by default so just thin-wrap the scale call.
sub etu_scale_image
{
my ($src, $dst) = @_;
etu_exec("-f $src -o $dst");
}
sub etu_update_image { etu_scale_image(@_); }
###
# etu_directory_scale/update: Setup and call etu to scale. Note we update
# by default so just thin-wrap the scale call.
###
sub etu_scale_dir
{
my ($src, $dst) = @_;
etu_exec("-s $src -d $dst");
}
sub etu_update_dir { etu__scale_dir(@_); }
1; # Main - return a true value
=head1 DESCRIPTION
This small library provides a Perl interface to the etu program. There are
five subroutines to initialize image parameters, scale single images,
update single images, scale entire directories and update entire
directories. Note that etu does not support recursion so the directories
must be flat. Also note that if etu_init() is not used the etu program
will simply use the defaults (see etu -u for details).
The usage for each is:
etu_init(image_width_in_pixels, image_height_in_pixels,
image_quality_percent_value);
etu_scale_image(image_source_file, image_destination_file);
etu_update_image(image_source_file, image_destination_file);
etu_scale_dir(image_source_dir, image_destination_dir);
etu_update_dir(image_source_dir, image_destination_dir);
=head1 EXAMPLES
etu_init (80, 60, 80);
etu_scale_image (myphoto.jpg, myphoto_thumb.jpg);
etu_update_image (myphoto.jpg, myphoto_thumb.jpg);
etu_scale_dir (/home/me/my_party_photos, /home/me/my_party_photos_thumbs);
=head1 PREREQUISITES
The etu utility must be installed on your OS.
=head1 COREQUISITES
none
=cut
And the final perldoc output:
ETU(1) User Contributed Perl Documentation ETU(1)
DESCRIPTION
This small library provides a Perl interface to the etu program. There
are five subroutines to initialize image parameters, scale single
images, update single images, scale entire directories and update
entire directories. Note that etu does not support recursion so the
directories must be flat. Also note that if etu_init() is not used the
etu program will simply use the defaults (see etu −u for details).
The usage for each is:
etu_init(image_width_in_pixels, image_height_in_pixels,
image_quality_percent_value);
etu_scale_image(image_source_file, image_destination_file);
etu_update_image(image_source_file, image_destination_file);
etu_scale_dir(image_source_dir, image_destination_dir);
etu_update_dir(image_source_dir, image_destination_dir);
EXAMPLES
etu_init (80, 60, 80);
etu_scale_image (myphoto.jpg, myphoto_thumb.jpg);
etu_update_image (myphoto.jpg, myphoto_thumb.jpg);
etu_scale_dir (/home/me/my_party_photos, /home/me/my_party_photos_thu
mbs);
PREREQUISITES
The etu utility must be installed on your OS.
COREQUISITES
none
perl v5.8.8 2009—05—15 ETU(1)
With a little thinking simple command utilities that might appear to be difficult to wrestle with inside of Perl programs can be thin-wrapped into meaningful system interfaces.