source: gsdl/trunk/perllib/plugins/ImageConverter.pm@ 16981

Last change on this file since 16981 was 16981, checked in by kjdon, 16 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 *
File size: 15.7 KB
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 repository browser.