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

Revision 16981, 15.7 KB (checked in by kjdon, 12 years ago)

forgot to commit the changes to the BaseMediaConverter? file after renaming in svn. And need to modify ImageConverter? to use the new name

  • 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      { 'name' => "cache_generated_images",
93    'desc' => "{ImageConverter.cache_generated_images}",
94    'type' => "flag",
95    'reqd' => "no",
96    }
97         ];
98
99my $options = { 'name' => "ImageConverter",
100        'desc' => "{ImageConverter.desc}",
101        'abstract' => "yes",
102        'inherits' => "yes",
103        'args' => $arguments };
104
105sub new {
106    my ($class) = shift (@_);
107    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
108    push(@$pluginlist, $class);
109
110    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
111    push(@{$hashArgOptLists->{"OptList"}},$options);
112
113    my $self = new BaseMediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
114
115
116    return bless $self, $class;
117
118}
119
120# needs to be called after BasePlugin init, so that outhandle is set up.
121sub init {
122    my $self = shift(@_);
123
124    $self->{'tmp_file_paths'} = ();
125
126    # Check that ImageMagick is installed and available on the path
127    my $image_conversion_available = 1;
128    my $no_image_conversion_reason = "";
129    # None of this works very well on Windows 95/98...
130    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) {
131    $image_conversion_available = 0;
132    $no_image_conversion_reason = "win95notsupported";
133    } else {
134    my $result = `identify 2>&1`;
135    if ($? == -1 || $? == 256) {  # Linux and Windows return different values for "program not found"
136        $image_conversion_available = 0;
137        $no_image_conversion_reason = "imagemagicknotinstalled";
138    }
139    }
140    $self->{'image_conversion_available'} = $image_conversion_available;
141    $self->{'no_image_conversion_reason'} = $no_image_conversion_reason;
142
143    if ($self->{'image_conversion_available'} == 0) {
144    my $outhandle = $self->{'outhandle'};
145    &gsprintf($outhandle, "ImageConverter: {ImageConverter.noconversionavailable} ({ImageConverter.".$self->{'no_image_conversion_reason'}."})\n");
146    }
147       
148}
149   
150
151# convert image to new type if converttotype is set
152# generate thumbnails if required
153# generate screenview if required
154# discover image metadata
155# filename_no_path must be in utf8 and url-encoded
156sub generate_images {
157    my $self = shift(@_);
158
159    my ($filename_full_path, $filename_no_path, $doc_obj, $section) = @_;
160
161    # check image magick status
162    return 0 if $self->{'image_conversion_available'} == 0;
163    # check the filenames
164    return 0 if ($filename_no_path eq "" || !-f $filename_full_path);
165
166    if ($self->{'cache_generated_images'}) {
167    $self->init_cache_for_file($filename_full_path);
168    }
169
170    my $verbosity = $self->{'verbosity'};
171    my $outhandle = $self->{'outhandle'};
172
173    # check the size of the image against minimum size if specified
174    my $minimumsize = $self->{'minimumsize'};
175    if (defined $minimumsize && (-s $filename_full_path < $minimumsize)) {
176        print $outhandle "ImageConverter: \"$filename_full_path\" too small, skipping\n"
177        if ($verbosity > 1);
178    return 0; # or is there a better return value??
179    }
180   
181    my $filehead = $filename_no_path;
182    $filehead =~ s/\.([^\.]*)$//; # filename with no extension
183    my $assocfilemeta = "[assocfilepath]";
184    if ($section ne $doc_obj->get_top_section()) {
185    $assocfilemeta = "[parent(Top):assocfilepath]";
186    }
187
188    # The images that will get generated may contain percent signs in their src filenames
189    # Encode those percent signs themselves so that urls to the imgs refer to them correctly
190    my $url_to_filehead = &unicode::filename_to_url($filehead);
191    my $url_to_filename_no_path = &unicode::filename_to_url($filename_no_path);
192   
193    # Convert the image to a new type (if required).
194    my $converttotype = $self->{'converttotype'};
195    my $type = "unknown";
196
197    if ($converttotype ne "" && $filename_full_path !~ m/$converttotype$/) {
198    #    $doc_obj->add_utf8_metadata($section, "Image", $utf8_filename_meta);
199
200    my ($result,$filename_full_path)
201        = $self->convert($filename_full_path, $converttotype, "", "CONVERTTYPE");
202
203    $type = $converttotype;
204    $filename_no_path = "$filehead.$type";
205    $url_to_filename_no_path = "$url_to_filehead.$type";
206    }
207
208    # add Image metadata
209    $doc_obj->add_utf8_metadata($section, "Image", $url_to_filename_no_path); # url to generated image
210
211    # here we overwrite the original with the potentially converted one
212    $doc_obj->set_utf8_metadata_element($section, "Source", &unicode::url_decode($filename_no_path)); # displayname of generated image
213    $doc_obj->set_utf8_metadata_element($section, "SourceFile", $url_to_filename_no_path); # displayname of generated image
214
215    # use identify to get info about the (possibly converted) image
216    my ($image_type, $image_width, $image_height, $image_size)
217    = &identify($filename_full_path, $outhandle, $verbosity);
218
219    if ($image_type ne " ") {
220    $type = $image_type;
221    }
222
223    #overwrite the ones added in BasePlugin
224    $doc_obj->set_metadata_element ($section, "FileFormat", $type);
225    $doc_obj->set_metadata_element ($section, "FileSize",   $image_size);
226
227    $doc_obj->add_metadata ($section, "ImageType",   $image_type);
228    $doc_obj->add_metadata ($section, "ImageWidth",  $image_width);
229    $doc_obj->add_metadata ($section, "ImageHeight", $image_height);
230    $doc_obj->add_metadata ($section, "ImageSize",   $image_size);
231
232    $doc_obj->add_metadata ($section, "srclink", "<a href=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Image]\">");
233    $doc_obj->add_metadata ($section, "/srclink", "</a>");
234    $doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Image]\" width=\"100\">");
235
236    # Add the image as an associated file
237    $doc_obj->associate_file($filename_full_path, $filename_no_path, "image/$type", $section);
238
239    if ($self->{'create_thumbnail'} eq "true") {
240    $self->create_thumbnail($filename_full_path, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead);
241    }
242    if ($self->{'create_screenview'} eq "true") {
243    $self->create_screenview($filename_full_path, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead);
244    }
245}
246
247sub create_thumbnail {
248    my $self = shift(@_);
249    my ($original_file, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead) = @_;
250    $url_to_filehead = $filehead unless defined $url_to_filehead;
251
252    my $thumbnailsize = $self->{'thumbnailsize'};
253    my $thumbnailtype = $self->{'thumbnailtype'};
254   
255    # Generate the thumbnail with convert
256    my ($result,$thumbnailfile)
257    = $self->convert($original_file, $thumbnailtype, "-geometry $thumbnailsize" . "x$thumbnailsize", "THUMB");
258   
259    # Add the thumbnail as an associated file ...
260    if (-e "$thumbnailfile") {
261    $doc_obj->associate_file("$thumbnailfile", $filehead."_thumb.$thumbnailtype",
262                 "image/$thumbnailtype",$section); # name of generated image
263    $doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype);
264    $doc_obj->add_utf8_metadata ($section, "Thumb", $url_to_filehead."_thumb.$thumbnailtype"); # url to generated image
265   
266    $doc_obj->add_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>");
267   
268   
269    # Extract Thumbnail metadata from convert output
270    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
271        $doc_obj->add_metadata ($section, "ThumbWidth", $1);
272        $doc_obj->add_metadata ($section, "ThumbHeight", $2);
273    }
274    } else {
275    my $outhandle = $self->{'outhandle'};
276    print $outhandle "Couldn't find thumbnail $thumbnailfile\n";
277
278    }
279}
280
281sub create_screenview {
282   
283    my $self = shift(@_);
284    my ($original_file, $filehead, $doc_obj, $section, $assocfilemeta, $url_to_filehead) = @_;
285    $url_to_filehead = $filehead unless defined $url_to_filehead;
286
287    # To do: if the actual image smaller than the screenview size,
288    # we should use the original !
289   
290    my $screenviewsize = $self->{'screenviewsize'};
291    my $screenviewtype = $self->{'screenviewtype'};
292   
293    # make the screenview image
294    my ($result,$screenviewfilename)
295    = $self->convert($original_file, $screenviewtype, "-geometry $screenviewsize" . "x$screenviewsize", "SCREEN");   
296   
297    #add the screenview as an associated file ...
298    if (-e "$screenviewfilename") {
299    $doc_obj->associate_file("$screenviewfilename", $filehead."_screen.$screenviewtype", "image/$screenviewtype",$section); # name of generated image
300    $doc_obj->add_metadata ($section, "ScreenType", $screenviewtype);
301    $doc_obj->add_utf8_metadata ($section, "Screen", $url_to_filehead."_screen.$screenviewtype"); # url to generated image
302   
303    $doc_obj->add_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/$assocfilemeta/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>");
304
305    # get screenview dimensions, size and type
306    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
307        $doc_obj->add_metadata ($section, "ScreenWidth", $1);
308        $doc_obj->add_metadata ($section, "ScreenHeight", $2);
309    } elsif ($result =~ m/([0-9]+)x([0-9]+)/) {
310        #if the image hasn't changed size, the previous regex doesn't match
311        $doc_obj->add_metadata ($section, "ScreenWidth", $1);
312        $doc_obj->add_metadata ($section, "ScreenHeight", $2);
313    }
314    } else {
315    my $outhandle = $self->{'outhandle'};
316    print $outhandle "Couldn't find screenview file $screenviewfilename\n";
317
318    }
319
320}
321
322
323
324sub convert {
325    my $self = shift(@_);
326    my $source_file_path = shift(@_);
327    my $target_file_type = shift(@_);
328    my $convert_options  = shift(@_) || "";
329    my $convert_id       = shift(@_) || "";
330
331    my $outhandle = $self->{'outhandle'};
332    my $verbosity = $self->{'verbosity'};
333
334    my $source_file_no_path = &File::Basename::basename($source_file_path);
335
336    # Determine the full name and path of the output file
337    my $target_file_path;
338    if ($self->{'cache_generated_images'}) {
339    my $cached_image_dir = $self->{'cached_dir'};
340    my $image_root = $self->{'cached_file_root'};
341    $image_root .= "_$convert_id" if ($convert_id ne "");
342    my $image_file = "$image_root.$target_file_type";
343    $target_file_path = &util::filename_cat($cached_image_dir,$image_file);
344    }
345    else {
346    $target_file_path = &util::get_tmp_filename($target_file_type);
347    push(@{$self->{'tmp_file_paths'}}, $target_file_path);
348
349    # Output filename used to be parsed from result line:
350    #   my ($ofilename) = ($result =~ m/=>(.*\.$target_file_type)/);
351    # by the function that called 'convert'
352    # but this is no longer needed, as output filename is now
353    # explicitly passed back
354
355    }
356
357    # Generate and run the convert command
358    my $convert_command = "convert -interlace plane -verbose $convert_options \"$source_file_path\" \"$target_file_path\"";
359
360    my $print_info = { 'message_prefix' => $convert_id,
361               'message' => "Converting image $source_file_no_path to: $convert_id $target_file_type" };
362 
363    my ($regenerated,$result,$had_error)
364    = $self->autorun_general_cmd($convert_command,$target_file_path,$print_info);
365
366    return ($result,$target_file_path);
367}
368
369
370# Discover the characteristics of an image file with the ImageMagick
371# "identify" command.
372
373sub identify {
374    my ($image, $outhandle, $verbosity) = @_;
375
376    # Use the ImageMagick "identify" command to get the file specs
377    my $command = "identify \"$image\" 2>&1";
378    print $outhandle "$command\n" if ($verbosity > 2);
379    my $result = '';
380    $result = `$command`;
381    print $outhandle "$result\n" if ($verbosity > 3);
382
383    # Read the type, width, and height
384    my $type =   'unknown';
385    my $width =  'unknown';
386    my $height = 'unknown';
387
388    my $image_safe = quotemeta $image;
389    if ($result =~ /^$image_safe (\w+) (\d+)x(\d+)/) {
390    $type = $1;
391    $width = $2;
392    $height = $3;
393    }
394
395    # Read the size
396    my $size = "unknown";
397    if ($result =~ m/^.* ([0-9]+)b/) {
398    $size = $1;
399    }
400    elsif ($result =~ m/^.* ([0-9]+)(\.([0-9]+))?kb?/) {
401    $size = 1024 * $1;
402    if (defined($2)) {
403        $size = $size + (1024 * $2);
404        # Truncate size (it isn't going to be very accurate anyway)
405        $size = int($size);
406    }
407    }
408    elsif ($result =~ m/^.* (([0-9]+)(\.([0-9]+))?e\+([0-9]+))(kb|b)?/) {
409    # Deals with file sizes on Linux of type "3.4e+02kb" where e+02 is 1*10^2.
410    # 3.4e+02 therefore evaluates to 3.4 x 1 x 10^2 = 340kb.
411    # Programming languages including Perl know how that 3.4e+02 is a number,
412    # so we don't need to do any calculations.
413    $size = $1*1; # turn the string into a number by multiplying it by 1
414           #if we did $size = $1; $size would be merely the string "3.4e+02"
415    $size = int($size); # truncate size
416    }
417    print $outhandle "file: $image:\t $type, $width, $height, $size\n"
418    if ($verbosity > 2);
419
420    # Return the specs
421    return ($type, $width, $height, $size);
422}
423
424sub clean_up_temporary_files {
425    my $self = shift(@_);
426
427    foreach my $tmp_file_path (@{$self->{'tmp_file_paths'}}) {
428    if (-e $tmp_file_path) {
429        &util::rm($tmp_file_path);
430    }
431    }
432   
433}
434
4351; 
Note: See TracBrowser for help on using the browser.