root/gsdl/trunk/perllib/plugins/ImageConverter.pm @ 19420

Revision 19420, 16.6 KB (checked in by kjdon, 11 years ago)

on linux, running identify was returning 1, which looked like 256 and so looked like it couldn't find imagemagick. modified the test to be 256 on windows, -1 otherwise

  • Property svn:executable set to *
Line 
1###########################################################################
2#
3# ImageConverter - helper plugin that does image conversion using ImageMagick
4#
5# A component of the Greenstone digital library software
6# from the New Zealand Digital Library Project at the
7# University of Waikato, New Zealand.
8#
9# Copyright (C) 2008 New Zealand Digital Library Project
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26package ImageConverter;
27
28use BaseMediaConverter;
29
30
31use strict;
32no strict 'refs'; # allow filehandles to be variables and viceversa
33
34use gsprintf 'gsprintf';
35
36BEGIN {
37    @ImageConverter::ISA = ('BaseMediaConverter');
38}
39
40my $arguments = [
41      { 'name' => "create_thumbnail",
42    'desc' => "{ImageConverter.create_thumbnail}",
43    'type' => "enum",
44    'list' => [{'name' => "true", 'desc' => "{common.true}"},
45           {'name' => "false", 'desc' => "{common.false}"}],
46    'deft' => "true",
47    'reqd' => "no" },
48      { 'name' => "thumbnailsize",
49    'desc' => "{ImageConverter.thumbnailsize}",
50    'type' => "int",
51    'deft' => "100",
52    'range' => "1,",
53    'reqd' => "no" },
54      { 'name' => "thumbnailtype",
55    'desc' => "{ImageConverter.thumbnailtype}",
56    'type' => "string",
57    'deft' => "gif",
58    'reqd' => "no" },
59      { 'name' => "noscaleup",
60    'desc' => "{ImageConverter.noscaleup}",
61    'type' => "flag",
62    'reqd' => "no" },
63      { 'name' => "create_screenview",
64    'desc' => "{ImageConverter.create_screenview}",
65    'type' => "enum",
66    'list' => [{'name' => "true", 'desc' => "{common.true}"},
67           {'name' => "false", 'desc' => "{common.false}"}],
68    'deft' => "true",
69    'reqd' => "no" },
70      { 'name' => "screenviewsize",
71    'desc' => "{ImageConverter.screenviewsize}",
72    'type' => "int",
73    'deft' => "500",
74    'range' => "1,",
75    'reqd' => "no" },
76      { 'name' => "screenviewtype",
77    'desc' => "{ImageConverter.screenviewtype}",
78    'type' => "string",
79    'deft' => "jpg",
80    'reqd' => "no" },
81      { 'name' => "converttotype",
82    'desc' => "{ImageConverter.converttotype}",
83    'type' => "string",
84    'deft' => "",
85    'reqd' => "no" },
86      { 'name' => "minimumsize",
87    'desc' => "{ImageConverter.minimumsize}",
88    'type' => "int",
89    'deft' => "100",
90    'range' => "1,",
91    'reqd' => "no" }
92         ];
93
94my $options = { 'name' => "ImageConverter",
95        'desc' => "{ImageConverter.desc}",
96        'abstract' => "yes",
97        'inherits' => "yes",
98        'args' => $arguments };
99
100sub new {
101    my ($class) = shift (@_);
102    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
103    push(@$pluginlist, $class);
104
105    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
106    push(@{$hashArgOptLists->{"OptList"}},$options);
107
108    my $self = new BaseMediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
109
110
111    return bless $self, $class;
112
113}
114
115# needs to be called after BasePlugin init, so that outhandle is set up.
116sub init {
117    my $self = shift(@_);
118
119    $self->{'tmp_file_paths'} = ();
120
121    # Check that ImageMagick is installed and available on the path
122    my $image_conversion_available = 1;
123    my $no_image_conversion_reason = "";
124    # None of this works very well on Windows 95/98...
125    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) {
126    $image_conversion_available = 0;
127    $no_image_conversion_reason = "win95notsupported";
128    } else {
129    my $result = `identify -version 2>&1`;
130    my $return_value = $?;
131    if ( ($ENV{'GSDLOS'} eq "windows" && $return_value == 256) || $return_value == -1) {  # Linux and Windows return different values for "program not found"
132        $image_conversion_available = 0;
133        $no_image_conversion_reason = "imagemagicknotinstalled";
134    }
135    }
136    $self->{'image_conversion_available'} = $image_conversion_available;
137    $self->{'no_image_conversion_reason'} = $no_image_conversion_reason;
138
139    if ($self->{'image_conversion_available'} == 0) {
140    my $outhandle = $self->{'outhandle'};
141    &gsprintf($outhandle, "ImageConverter: {ImageConverter.noconversionavailable} ({ImageConverter.".$self->{'no_image_conversion_reason'}."})\n");
142    }
143       
144}
145   
146
147# convert image to new type if converttotype is set
148# generate thumbnails if required
149# generate screenview if required
150# discover image metadata
151# filename_no_path must be in utf8 and url-encoded
152sub generate_images {
153    my $self = shift(@_);
154    my ($filename_full_path, $filename_no_path, $doc_obj, $section) = @_;
155
156    # check image magick status
157    return 0 if $self->{'image_conversion_available'} == 0;
158
159    # check the filenames
160    return 0 if ($filename_no_path eq "" || !-f $filename_full_path);
161
162    if ($self->{'enable_cache'}) {
163    $self->init_cache_for_file($filename_full_path);
164    }
165
166    my $verbosity = $self->{'verbosity'};
167    my $outhandle = $self->{'outhandle'};
168
169    # check the size of the image against minimum size if specified
170    my $minimumsize = $self->{'minimumsize'};
171    if (defined $minimumsize && (-s $filename_full_path < $minimumsize)) {
172        print $outhandle "ImageConverter: \"$filename_full_path\" too small, skipping\n"
173        if ($verbosity > 1);
174    return 0; # or is there a better return value??
175    }
176   
177    my $filehead = $filename_no_path;
178    $filehead =~ s/\.([^\.]*)$//; # filename with no extension
179    my $assocfilemeta = "[assocfilepath]";
180    if ($section ne $doc_obj->get_top_section()) {
181    $assocfilemeta = "[parent(Top):assocfilepath]";
182    }
183
184    # The images that will get generated may contain percent signs in their src filenames
185    # Encode those percent signs themselves so that urls to the imgs refer to them correctly
186    my $url_to_filehead = &unicode::filename_to_url($filehead);
187    my $url_to_filename_no_path = &unicode::filename_to_url($filename_no_path);
188   
189    # Convert the image to a new type (if required).
190    my $converttotype = $self->{'converttotype'};
191    my $type = "unknown";
192
193    if ($converttotype ne "" && $filename_full_path !~ m/$converttotype$/) {
194    #    $doc_obj->add_utf8_metadata($section, "Image", $utf8_filename_meta);
195
196    my ($result,$filename_full_path)
197        = $self->convert($filename_full_path, $converttotype, "", "CONVERTTYPE");
198
199    $type = $converttotype;
200    $filename_no_path = "$filehead.$type";
201    $url_to_filename_no_path = "$url_to_filehead.$type";
202    }
203
204    # add Image metadata
205    $doc_obj->add_utf8_metadata($section, "Image", $url_to_filename_no_path); # url to generated image
206
207    # here we overwrite the original with the potentially converted one
208    $doc_obj->set_utf8_metadata_element($section, "Source", &unicode::url_decode($filename_no_path)); # displayname of generated image
209    $doc_obj->set_utf8_metadata_element($section, "SourceFile", $url_to_filename_no_path); # displayname of generated image
210
211    # use identify to get info about the (possibly converted) image
212    my ($image_type, $image_width, $image_height, $image_size)
213    = &identify($filename_full_path, $outhandle, $verbosity);
214
215    if ($image_type ne " ") {
216    $type = $self->correct_mime_type($image_type);
217    }
218
219    #overwrite the ones added in BasePlugin
220    $doc_obj->set_metadata_element ($section, "FileFormat", $type);
221    $doc_obj->set_metadata_element ($section, "FileSize",   $image_size);
222
223    $doc_obj->add_metadata ($section, "ImageType",   $image_type);
224    $doc_obj->add_metadata ($section, "ImageWidth",  $image_width);
225    $doc_obj->add_metadata ($section, "ImageHeight", $image_height);
226    $doc_obj->add_metadata ($section, "ImageSize",   $image_size);
227
228    if ((defined $self->{'MaxImageWidth'})
229    && ($image_width > $self->{'MaxImageWidth'})) {
230    $self->{'MaxImageWidth'} = $image_width;
231    }
232    if ((defined $self->{'MaxImageHeight'})
233    && ($image_height > $self->{'MaxImageHeight'})) {
234    $self->{'MaxImageHeight'} = $image_height;
235    }
236
237
238    $doc_obj->add_metadata ($section, "srclink", "<a href=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Image]\">");
239    $doc_obj->add_metadata ($section, "/srclink", "</a>");
240    $doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Image]\" width=\"[ImageWidth]\" height=\"[ImageHeight]\">");
241
242    # Add the image as an associated file
243    $doc_obj->associate_file($filename_full_path, $filename_no_path, "image/$type", $section);
244
245    if ($self->{'create_thumbnail'} eq "true") {
246    $self->create_thumbnail($filename_full_path, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead);
247    }
248    if ($self->{'create_screenview'} eq "true") {
249    $self->create_screenview($filename_full_path, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead, $image_width, $image_height);
250    }
251}
252
253sub create_thumbnail {
254    my $self = shift(@_);
255    my ($original_file, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead) = @_;
256    $url_to_filehead = $filehead unless defined $url_to_filehead;
257
258    my $thumbnailsize = $self->{'thumbnailsize'};
259    my $thumbnailtype = $self->correct_mime_type($self->{'thumbnailtype'});
260
261    # Generate the thumbnail with convert
262    my ($result,$thumbnailfile)
263    = $self->convert($original_file, $thumbnailtype, "-geometry $thumbnailsize" . "x$thumbnailsize", "THUMB");
264   
265    # Add the thumbnail as an associated file ...
266    if (-e "$thumbnailfile") {
267    $doc_obj->associate_file("$thumbnailfile", $filehead."_thumb.$thumbnailtype",
268                 "image/$thumbnailtype",$section); # name of generated image
269    $doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype);
270    $doc_obj->add_utf8_metadata ($section, "Thumb", $url_to_filehead."_thumb.$thumbnailtype"); # url to generated image
271   
272    $doc_obj->add_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>");
273   
274   
275    # Extract Thumbnail metadata from convert output
276    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
277        $doc_obj->add_metadata ($section, "ThumbWidth", $1);
278        $doc_obj->add_metadata ($section, "ThumbHeight", $2);
279    }
280    } else {
281    my $outhandle = $self->{'outhandle'};
282    print $outhandle "Couldn't find thumbnail $thumbnailfile\n";
283
284    }
285}
286
287sub create_screenview {
288   
289    my $self = shift(@_);
290    my ($original_file, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead, $image_width, $image_height) = @_;
291    $url_to_filehead = $filehead unless defined $url_to_filehead;
292
293    my $screenviewsize = $self->{'screenviewsize'};
294    my $screenviewtype = $self->correct_mime_type($self->{'screenviewtype'});
295
296    # Scale the image, unless the original image is smaller than the screenview size and -noscaleup is set
297    my $scale_option = "-geometry $screenviewsize" . "x$screenviewsize";
298    if ($self->{'noscaleup'} && $image_width < $screenviewsize && $image_height < $screenviewsize)
299    {
300    $scale_option = "";
301    }
302   
303    # make the screenview image
304    my ($result,$screenviewfilename)
305    = $self->convert($original_file, $screenviewtype, $scale_option, "SCREEN");   
306   
307    #add the screenview as an associated file ...
308    if (-e "$screenviewfilename") {
309    $doc_obj->associate_file("$screenviewfilename", $filehead."_screen.$screenviewtype", "image/$screenviewtype",$section); # name of generated image
310    $doc_obj->add_metadata ($section, "ScreenType", $screenviewtype);
311    $doc_obj->add_utf8_metadata ($section, "Screen", $url_to_filehead."_screen.$screenviewtype"); # url to generated image
312   
313    $doc_obj->add_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>");
314
315    # get screenview dimensions, size and type
316    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
317        $doc_obj->add_metadata ($section, "ScreenWidth", $1);
318        $doc_obj->add_metadata ($section, "ScreenHeight", $2);
319    } elsif ($result =~ m/([0-9]+)x([0-9]+)/) {
320        #if the image hasn't changed size, the previous regex doesn't match
321        $doc_obj->add_metadata ($section, "ScreenWidth", $1);
322        $doc_obj->add_metadata ($section, "ScreenHeight", $2);
323    }
324    } else {
325    my $outhandle = $self->{'outhandle'};
326    print $outhandle "Couldn't find screenview file $screenviewfilename\n";
327
328    }
329
330}
331
332
333
334sub convert {
335    my $self = shift(@_);
336    my $source_file_path = shift(@_);
337    my $target_file_type = shift(@_);
338    my $convert_options  = shift(@_) || "";
339    my $convert_id       = shift(@_) || "";
340
341    my $outhandle = $self->{'outhandle'};
342    my $verbosity = $self->{'verbosity'};
343
344    my $source_file_no_path = &File::Basename::basename($source_file_path);
345
346    # Determine the full name and path of the output file
347    my $target_file_path;
348    if ($self->{'enable_cache'}) {
349    my $cached_image_dir = $self->{'cached_dir'};
350    my $image_root = $self->{'cached_file_root'};
351    $image_root .= "_$convert_id" if ($convert_id ne "");
352    my $image_file = "$image_root.$target_file_type";
353    $target_file_path = &util::filename_cat($cached_image_dir,$image_file);
354    }
355    else {
356    $target_file_path = &util::get_tmp_filename($target_file_type);
357    push(@{$self->{'tmp_file_paths'}}, $target_file_path);
358
359    # Output filename used to be parsed from result line:
360    #   my ($ofilename) = ($result =~ m/=>(.*\.$target_file_type)/);
361    # by the function that called 'convert'
362    # but this is no longer needed, as output filename is now
363    # explicitly passed back
364    }
365
366    # Generate and run the convert command
367    my $convert_command = "convert -interlace plane -verbose $convert_options \"$source_file_path\" \"$target_file_path\"";
368
369    my $print_info = { 'message_prefix' => $convert_id,
370               'message' => "Converting image $source_file_no_path to: $convert_id $target_file_type" };
371 
372    my ($regenerated,$result,$had_error)
373    = $self->autorun_general_cmd($convert_command,$target_file_path,$print_info);
374
375    return ($result,$target_file_path);
376}
377
378
379# Discover the characteristics of an image file with the ImageMagick
380# "identify" command.
381
382sub identify {
383    my ($image, $outhandle, $verbosity) = @_;
384
385    # Use the ImageMagick "identify" command to get the file specs
386    my $command = "identify \"$image\" 2>&1";
387    print $outhandle "$command\n" if ($verbosity > 2);
388    my $result = '';
389    $result = `$command`;
390    print $outhandle "$result\n" if ($verbosity > 3);
391
392    # Read the type, width, and height
393    my $type =   'unknown';
394    my $width =  'unknown';
395    my $height = 'unknown';
396
397    my $image_safe = quotemeta $image;
398    if ($result =~ /^$image_safe (\w+) (\d+)x(\d+)/) {
399    $type = $1;
400    $width = $2;
401    $height = $3;
402    }
403
404    # Read the size
405    my $size = "unknown";
406    if ($result =~ m/^.* ([0-9]+)b/) {
407    $size = $1;
408    }
409    elsif ($result =~ m/^.* ([0-9]+)(\.([0-9]+))?kb?/) {
410    $size = 1024 * $1;
411    if (defined($2)) {
412        $size = $size + (1024 * $2);
413        # Truncate size (it isn't going to be very accurate anyway)
414        $size = int($size);
415    }
416    }
417    elsif ($result =~ m/^.* (([0-9]+)(\.([0-9]+))?e\+([0-9]+))(kb|b)?/) {
418    # Deals with file sizes on Linux of type "3.4e+02kb" where e+02 is 1*10^2.
419    # 3.4e+02 therefore evaluates to 3.4 x 1 x 10^2 = 340kb.
420    # Programming languages including Perl know how that 3.4e+02 is a number,
421    # so we don't need to do any calculations.
422    $size = $1*1; # turn the string into a number by multiplying it by 1
423           #if we did $size = $1; $size would be merely the string "3.4e+02"
424    $size = int($size); # truncate size
425    }
426    print $outhandle "file: $image:\t $type, $width, $height, $size\n"
427    if ($verbosity > 2);
428
429    # Return the specs
430    return ($type, $width, $height, $size);
431}
432
433sub clean_up_temporary_files {
434    my $self = shift(@_);
435
436    foreach my $tmp_file_path (@{$self->{'tmp_file_paths'}}) {
437    if (-e $tmp_file_path) {
438        &util::rm($tmp_file_path);
439    }
440    }
441   
442}
443
444# image/jpg is not a valid mime-type, it ought to be image/jpeg.
445# Sometimes JPEG is passed in also, want to keep things lowercase just in case.
446sub correct_mime_type {
447    my $self = shift(@_);
448    my ($file_extension) = @_;
449   
450    $file_extension = lc($file_extension);
451    $file_extension =~ s/jpg/jpeg/s;
452
453    return $file_extension;
454}
455
4561; 
Note: See TracBrowser for help on using the browser.