]> git.donarmstrong.com Git - deb_pkgs/libapache-gallery-perl.git/blob - lib/Apache/Gallery.pm
Change underscores to spaces for images too (Closes: #348724)
[deb_pkgs/libapache-gallery-perl.git] / lib / Apache / Gallery.pm
1 package Apache::Gallery;
2
3 # $Author: mil $ $Rev: 324 $
4 # $Date: 2011-02-22 21:56:06 +0100 (Tue, 22 Feb 2011) $
5
6 use strict;
7
8 use vars qw($VERSION);
9
10 $VERSION = "1.0.1";
11
12 BEGIN {
13
14         if (exists($ENV{MOD_PERL_API_VERSION})
15                 and ($ENV{MOD_PERL_API_VERSION}==2)) {
16                 require mod_perl2;
17                 if ($mod_perl::VERSION >= 1.99 && $mod_perl::VERSION < 2.0) {
18                         die "mod_perl 2.0.0 or later is now required";
19                 }
20                 require Apache2::ServerRec;
21                 require Apache2::RequestRec;
22                 require Apache2::Log;
23                 require APR::Table;
24                 require Apache2::RequestIO;
25                 require Apache2::SubRequest;
26                 require Apache2::Const;
27         
28                 Apache2::Const->import(-compile => 'OK','DECLINED','FORBIDDEN','NOT_FOUND','HTTP_NOT_MODIFIED');
29
30                 $::MP2 = 1;
31         } else {
32                 require mod_perl;
33
34                 require Apache;
35                 require Apache::Constants;
36                 require Apache::Request;
37         
38                 Apache::Constants->import('OK','DECLINED','FORBIDDEN','NOT_FOUND');
39                 $::MP2 = 0;
40         }
41 }
42
43 use Image::Info qw(image_info);
44 use Image::Size qw(imgsize);
45 use Image::Imlib2;
46 use Text::Template;
47 use File::stat;
48 use File::Spec;
49 use POSIX qw(floor);
50 use URI::Escape;
51 use CGI;
52 use CGI::Cookie;
53 use Encode;
54 use HTTP::Date;
55 use Digest::MD5 qw(md5_base64);
56
57 use Data::Dumper;
58
59 # Regexp for escaping URI's
60 my $escape_rule = "^A-Za-z0-9\-_.!~*'()\/";
61 my $memoized;
62
63 sub handler {
64
65         my $r = shift or Apache2::RequestUtil->request();
66
67         unless (($r->method eq 'HEAD') or ($r->method eq 'GET')) {
68                 return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
69         }
70
71         if ((not $memoized) and ($r->dir_config('GalleryMemoize'))) {
72                 require Memoize;
73                 Memoize::memoize('get_imageinfo');
74                 $memoized=1;
75         }
76
77         $r->headers_out->{"X-Powered-By"} = "apachegallery.dk $VERSION - Hest design!";
78         $r->headers_out->{"X-Gallery-Version"} = '$Rev: 324 $ $Date: 2011-02-22 21:56:06 +0100 (Tue, 22 Feb 2011) $';
79
80         my $filename = $r->filename;
81         $filename =~ s/\/$//;
82         my $topdir = $filename;
83
84         my $media_rss_enabled = $r->dir_config('GalleryEnableMediaRss');
85
86         # Just return the http headers if the client requested that
87         if ($r->header_only) {
88
89                 if (!$::MP2) {
90                         $r->send_http_header;
91                 }
92
93                 if (-f $filename or -d $filename) {
94                         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
95                 }
96                 else {
97                         return $::MP2 ? Apache2::Const::NOT_FOUND() : Apache::Constants::NOT_FOUND();
98                 }
99         }
100
101         my $cgi = new CGI;
102
103         # Handle selected images
104         if ($cgi->param('selection')) {
105                 my @selected = $cgi->param('selection');
106                 my $content = join "<br />\n",@selected;
107                 $r->content_type('text/html');
108                 $r->headers_out->{'Content-Length'} = length($content);
109
110                 if (!$::MP2) {
111                         $r->send_http_header;
112                 }
113
114                 $r->print($content);
115                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
116         }
117         
118         # Selectmode providing checkboxes beside all thumbnails
119         my $select_mode = $cgi->param('select');
120         
121         # Let Apache serve icons without us modifying the request
122         if ($r->uri =~ m/^\/icons/i) {
123                 return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
124         }
125         # Lookup the file in the cache and scale the image if the cached
126         # image does not exist
127         if ($r->uri =~ m/\.cache\//i) {
128
129                 my $filename = $r->filename().$r->path_info();
130                 $filename =~ s/\.cache//;
131
132                 $filename =~ m/\/(\d+)x(\d+)\-/;
133                 my $image_width = $1;
134                 my $image_height = $2;
135
136                 $filename =~ s/\/(\d+)x(\d+)\-//;
137
138                 my ($width, $height, $type) = imgsize($filename);
139
140                 my $imageinfo = get_imageinfo($r, $filename, $type, $width, $height);
141         
142                 my $cached = scale_picture($r, $filename, $image_width, $image_height, $imageinfo);
143
144                 my $file = cache_dir($r, 0);
145                 $file =~ s/\.cache//;
146
147                 my $subr = $r->lookup_file($file);
148                 $r->content_type($subr->content_type());
149
150                 if ($::MP2) {
151                         my $fileinfo = stat($file);
152
153                         my $nonce = md5_base64($fileinfo->ino.$fileinfo->mtime);
154                         if ($r->headers_in->{"If-None-Match"} eq $nonce) {
155                                 return Apache2::Const::HTTP_NOT_MODIFIED();
156                         }
157
158                         if ($r->headers_in->{"If-Modified-Since"} && str2time($r->headers_in->{"If-Modified-Since"}) < $fileinfo->mtime) {
159                                 return Apache2::Const::HTTP_NOT_MODIFIED();
160                         }
161
162                         $r->headers_out->{"Content-Length"} = $fileinfo->size; 
163                         $r->headers_out->{"Last-Modified-Date"} = time2str($fileinfo->mtime); 
164                         $r->headers_out->{"ETag"} = $nonce;
165                         $r->sendfile($file);
166                         return Apache2::Const::OK();
167                 }
168                 else {
169                         $r->path_info('');
170                         $r->filename($file);
171                         return Apache::Constants::DECLINED();
172                 }
173                 
174         }
175
176         my $uri = $r->uri;
177         $uri =~ s/\/$//;
178
179         unless (-f $filename or -d $filename) {
180                 show_error($r, 404, "404!", "No such file or directory: ".uri_escape($r->uri, $escape_rule));
181                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
182         }
183
184         my $doc_pattern = $r->dir_config('GalleryDocFile');
185         unless ($doc_pattern) {
186                 $doc_pattern = '\.(mpe?g|avi|mov|asf|wmv|doc|mp3|ogg|pdf|rtf|wav|dlt|html?|csv|eps)$'
187         }
188         my $img_pattern = $r->dir_config('GalleryImgFile');
189         unless ($img_pattern) {
190                 $img_pattern = '\.(jpe?g|png|tiff?|ppm)$'
191         }
192
193         # Let Apache serve files we don't know how to handle anyway
194         if (-f $filename && $filename !~ m/$img_pattern/i) {
195                 return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
196         }
197
198         if (-d $filename) {
199
200                 unless (-d cache_dir($r, 0)) {
201                         unless (create_cache($r, cache_dir($r, 0))) {
202                                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
203                         }
204                 }
205
206                 my $tpl_dir = $r->dir_config('GalleryTemplateDir');
207
208                 # Instead of reading the templates every single time
209                 # we need them, create a hash of template names and
210                 # the associated Text::Template objects.
211                 my %templates = create_templates({layout       => "$tpl_dir/layout.tpl",
212                                                   index        => "$tpl_dir/index.tpl",
213                                                   directory    => "$tpl_dir/directory.tpl",
214                                                   picture      => "$tpl_dir/picture.tpl",
215                                                   file         => "$tpl_dir/file.tpl",
216                                                   comment      => "$tpl_dir/dircomment.tpl",
217                                                   nocomment    => "$tpl_dir/nodircomment.tpl",
218                                                   rss          => "$tpl_dir/rss.tpl",
219                                                   rss_item     => "$tpl_dir/rss_item.tpl",
220                                                   navdirectory => "$tpl_dir/navdirectory.tpl",
221                                                  });
222
223
224
225
226                 my %tpl_vars;
227
228                 $tpl_vars{TITLE} = "Index of: $uri";
229
230                 if ($media_rss_enabled) {
231                         # Put the RSS feed on all directory listings
232                         $tpl_vars{META} = '<link rel="alternate" href="?rss=1" type="application/rss+xml" title="" id="gallery" />';
233                 }
234
235                 unless (opendir (DIR, $filename)) {
236                         show_error ($r, 500, $!, "Unable to access directory $filename: $!");
237                         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
238                 }
239
240                 $tpl_vars{MENU} = generate_menu($r);
241
242                 $tpl_vars{FORM_BEGIN} = $select_mode?'<form method="post">':'';
243                 $tpl_vars{FORM_END}   = $select_mode?'<input type="submit" name="Get list" value="Get list"></form>':'';
244
245                 # Read, sort, and filter files
246                 my @files = grep { !/^\./ && -f "$filename/$_" } readdir (DIR);
247
248                 @files=gallerysort($r, @files);
249
250                 my @downloadable_files;
251
252                 if (@files) {
253                         # Remove unwanted files from list
254                         my @new_files = ();
255                         foreach my $picture (@files) {
256
257                                 my $file = $topdir."/".$picture;
258
259                                 if ($file =~ /$img_pattern/i) {
260                                         push (@new_files, $picture);
261                                 }
262
263                                 if ($file =~ /$doc_pattern/i) {
264                                         push (@downloadable_files, $picture);
265                                 }
266
267                         }
268                         @files = @new_files;
269                 }
270
271                 # Read and sort directories
272                 rewinddir (DIR);
273                 my @directories = grep { !/^\./ && -d "$filename/$_" } readdir (DIR);
274                 my $dirsortby;
275                 if (defined($r->dir_config('GalleryDirSortBy'))) {
276                         $dirsortby=$r->dir_config('GalleryDirSortBy');
277                 } else {
278                         $dirsortby=$r->dir_config('GallerySortBy');
279                 }
280                 if ($dirsortby && $dirsortby =~ m/^(size|atime|mtime|ctime)$/) {
281                         @directories = map(/^\d+ (.*)/, sort map(stat("$filename/$_")->$dirsortby()." $_", @directories));
282                 } else {
283                         @directories = sort @directories;
284                 }
285
286                 closedir(DIR);
287
288
289                 # Combine directories and files to one listing
290                 my @listing;
291                 push (@listing, @directories);
292                 push (@listing, @files);
293                 push (@listing, @downloadable_files);
294                 
295                 if (@listing) {
296
297                         my $filelist;
298
299                         my $file_counter = 0;
300                         my $start_at = 1;
301                         my $max_files = $r->dir_config('GalleryMaxThumbnailsPerPage');
302
303                         if (defined($cgi->param('start'))) {
304                                 $start_at = $cgi->param('start');
305                                 if ($start_at < 1) {
306                                         $start_at = 1;
307                                 }
308                         }
309
310                         my $browse_links = "";
311                         if (defined($max_files)) {
312                         
313                                 for (my $i=1; $i<=scalar(@listing); $i++) {
314
315                                         my $from = $i;
316
317                                         my $to = $i+$max_files-1;
318                                         if ($to > scalar(@listing)) {
319                                                 $to = scalar(@listing);
320                                         }
321
322                                         if ($start_at < $from || $start_at > $to) {
323                                                 $browse_links .= "<a href=\"?start=$from\">$from - ".$to."</a> ";
324                                         }
325                                         else {
326                                                 $browse_links .= "$from - $to ";
327                                         }
328
329                                         $i+=$max_files-1;
330
331                                 }
332
333                         }
334
335                         $tpl_vars{BROWSELINKS} = $browse_links;
336
337                         DIRLOOP:
338                         foreach my $file (@listing) {
339
340                                 $file_counter++;
341
342                                 if ($file_counter < $start_at) {
343                                         next;
344                                 }
345
346                                 if (defined($max_files) && $file_counter > $max_files+$start_at-1) {
347                                         last DIRLOOP;
348                                 }
349
350                                 my $thumbfilename = $topdir."/".$file;
351
352                                 my $fileurl = $uri."/".$file;
353
354                                 if (-d $thumbfilename) {
355                                         my $dirtitle = '';
356                                         if (-e $thumbfilename . ".folder") {
357                                                 $dirtitle = get_filecontent($thumbfilename . ".folder");
358                                         }
359
360                                         $dirtitle = $dirtitle ? $dirtitle : $file;
361                                         $dirtitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
362
363                                         $tpl_vars{FILES} .=
364                                              $templates{directory}->fill_in(HASH=> {FILEURL => uri_escape($fileurl, $escape_rule),
365                                                                                     FILE    => $dirtitle,
366                                                                                    }
367                                                                            );
368
369                                 }
370                                 elsif (-f $thumbfilename && $thumbfilename =~ /$doc_pattern/i && $thumbfilename !~ /$img_pattern/i) {
371                                         my $type = lc($1);
372                                         my $stat = stat($thumbfilename);
373                                         my $size = $stat->size;
374                                         my $filetype;
375
376                                         if ($thumbfilename =~ m/\.(mpe?g|avi|mov|asf|wmv)$/i) {
377                                                 $filetype = "video-$type";
378                                         } elsif ($thumbfilename =~ m/\.(txt|html?)$/i) {
379                                                 $filetype = "text-$type";
380                                         } elsif ($thumbfilename =~ m/\.(mp3|ogg|wav)$/i) {
381                                                 $filetype = "sound-$type";
382                                         } elsif ($thumbfilename =~ m/$doc_pattern/i) {
383                                                 $filetype = "application-$type";
384                                         } else {
385                                                 $filetype = "unknown";
386                                         }
387
388                                         # Debian bug #348724 <http://bugs.debian.org/348724>
389                                         # not images
390                                         my $filetitle = $file;
391                                         $filetitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
392
393                                         $tpl_vars{FILES} .=
394                                              $templates{file}->fill_in(HASH => {%tpl_vars,
395                                                                                 FILEURL => uri_escape($fileurl, $escape_rule),
396                                                                                 ALT => "Size: $size Bytes",
397                                                                                 FILE => $filetitle,
398                                                                                 TYPE => $type,
399                                                                                 FILETYPE => $filetype,
400                                                                                }
401                                                                       );
402                                 }
403                                 elsif (-f $thumbfilename) {
404
405                                         my ($width, $height, $type) = imgsize($thumbfilename);
406                                         next if $type eq 'Data stream is not a known image file format';
407
408                                         my @filetypes = qw(JPG TIF PNG PPM GIF);
409
410                                         next unless (grep $type eq $_, @filetypes);
411                                         my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $width, $height);        
412                                         my $imageinfo = get_imageinfo($r, $thumbfilename, $type, $width, $height);
413                                         my $cached = get_scaled_picture_name($thumbfilename, $thumbnailwidth, $thumbnailheight);
414
415                                         my $rotate = readfile_getnum($r, $imageinfo, $thumbfilename.".rotate");
416
417                                         # Debian bug #348724 <http://bugs.debian.org/348724>
418                                         # HTML <img> tag, alt attribute
419                                         my $filetitle = $file;
420                                         $filetitle =~ s/_/ /g if $r->dir_config('GalleryUnderscoresToSpaces');
421
422                                         my %file_vars = (FILEURL => uri_escape($fileurl, $escape_rule),
423                                                          FILE    => $filetitle,
424                                                          DATE    => $imageinfo->{DateTimeOriginal} ? $imageinfo->{DateTimeOriginal} : '', # should this really be a stat of the file instead of ''?
425                                                          SRC     => uri_escape($uri."/.cache/$cached", $escape_rule),
426                                                          HEIGHT => (grep($rotate==$_, (1, 3)) ? $thumbnailwidth : $thumbnailheight),
427                                                          WIDTH => (grep($rotate==$_, (1, 3)) ? $thumbnailheight : $thumbnailwidth),
428                                                          SELECT  => $select_mode?'<input type="checkbox" name="selection" value="'.$file.'">&nbsp;&nbsp;':'',);
429                                         $tpl_vars{FILES} .= $templates{picture}->fill_in(HASH => {%tpl_vars,
430                                                                                                  %file_vars,
431                                                                                                 },
432                                                                                        );
433
434                                         if ($media_rss_enabled) {
435                                                 my ($content_image_width, undef, $content_image_height) = get_image_display_size($cgi, $r, $width, $height);
436                                                 my %item_vars = ( 
437                                                         THUMBNAIL => uri_escape($uri."/.cache/$cached", $escape_rule),
438                                                         LINK      => uri_escape($fileurl, $escape_rule),
439                                                         TITLE     => $file,
440                                                         CONTENT   => uri_escape($uri."/.cache/".$content_image_width."x".$content_image_height."-".$file, $escape_rule)
441                                                 );
442                                                 $tpl_vars{ITEMS} .= $templates{rss_item}->fill_in(HASH => { 
443                                                         %item_vars
444                                                 });
445                                         }
446                                 }
447                         }
448                 }
449                 else {
450                         $tpl_vars{FILES} = "No files found";
451                         $tpl_vars{BROWSELINKS} = "";
452                 }
453
454                 # Generate prev and next directory menu items
455                 $filename =~ m/(.*)\/.*?$/;
456                 my $parent_filename = $1;
457
458                 $r->document_root =~ m/(.*)\/$/;
459                 my $root_path = $1;
460                 print STDERR "$filename vs $root_path\n";
461                 if ($filename ne $root_path) {
462                         unless (opendir (PARENT_DIR, $parent_filename)) {
463                                 show_error ($r, 500, $!, "Unable to access parent directory $parent_filename: $!");
464                                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
465                         }
466         
467                         my @neighbour_directories = grep { !/^\./ && -d "$parent_filename/$_" } readdir (PARENT_DIR);
468                         my $dirsortby;
469                         if (defined($r->dir_config('GalleryDirSortBy'))) {
470                                 $dirsortby=$r->dir_config('GalleryDirSortBy');
471                         } else {
472                                 $dirsortby=$r->dir_config('GallerySortBy');
473                         }
474                         if ($dirsortby && $dirsortby =~ m/^(size|atime|mtime|ctime)$/) {
475                                 @neighbour_directories = map(/^\d+ (.*)/, sort map(stat("$parent_filename/$_")->$dirsortby()." $_", @neighbour_directories));
476                         } else {
477                                 @neighbour_directories = sort @neighbour_directories;
478                         }
479
480                         closedir(PARENT_DIR);
481
482                         my $neightbour_counter = 0;
483                         foreach my $neighbour_directory (@neighbour_directories) {
484                                 if ($parent_filename.'/'.$neighbour_directory eq $filename) {
485                                         if ($neightbour_counter > 0) {
486                                                 print STDERR "prev directory is " .$neighbour_directories[$neightbour_counter-1] ."\n";
487                                                 my $linktext = $neighbour_directories[$neightbour_counter-1];
488                                                 if (-e $parent_filename.'/'.$neighbour_directories[$neightbour_counter-1] . ".folder") {
489                                                         $linktext = get_filecontent($parent_filename.'/'.$neighbour_directories[$neightbour_counter-1] . ".folder");
490                                                 }
491                                                 my %info = (
492                                                 URL => "../".$neighbour_directories[$neightbour_counter-1],
493                                                 LINK_NAME => "<<< $linktext",
494                                                 DIR_FILES => "",
495                                                 );
496                                                 $tpl_vars{PREV_DIR_FILES} = $templates{navdirectory}->fill_in(HASH=> {%info});
497                                                 print STDERR $tpl_vars{PREV_DIR_FILES} ."\n";
498
499                                         }
500                                         if ($neightbour_counter < scalar @neighbour_directories - 1) {
501                                                 my $linktext = $neighbour_directories[$neightbour_counter+1];
502                                                 if (-e $parent_filename.'/'.$neighbour_directories[$neightbour_counter-1] . ".folder") {
503                                                         $linktext = get_filecontent($parent_filename.'/'.$neighbour_directories[$neightbour_counter+1] . ".folder");
504                                                 }
505                                                 my %info = (
506                                                 URL => "../".$neighbour_directories[$neightbour_counter+1],
507                                                 LINK_NAME => "$linktext >>>",
508                                                 DIR_FILES => "",
509                                                 );
510                                                 $tpl_vars{NEXT_DIR_FILES} = $templates{navdirectory}->fill_in(HASH=> {%info});
511                                                 print STDERR "next directory is " .$neighbour_directories[$neightbour_counter+1] ."\n";
512                                         }
513                                 }
514                                 $neightbour_counter++;
515                         }
516                 }
517
518                 if (-f $topdir . '.comment') {
519                         my $comment_ref = get_comment($topdir . '.comment');
520                         my %comment_vars;
521                         $comment_vars{COMMENT} = $comment_ref->{COMMENT} . '<br />' if $comment_ref->{COMMENT};
522                         $comment_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
523                         $tpl_vars{DIRCOMMENT} = $templates{comment}->fill_in(HASH => \%comment_vars);
524                         $tpl_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
525                 } else {
526                         $tpl_vars{DIRCOMMENT} = $templates{nocomment}->fill_in(HASH=>\%tpl_vars);
527                 }
528
529                 if ($cgi->param('rss')) {
530                         $tpl_vars{MAIN} = $templates{rss}->fill_in(HASH => \%tpl_vars);
531                         $r->content_type('application/rss+xml');
532                 } else {
533                         $tpl_vars{MAIN} = $templates{index}->fill_in(HASH => \%tpl_vars);
534                         $tpl_vars{MAIN} = $templates{layout}->fill_in(HASH => \%tpl_vars);
535                         $r->content_type('text/html');
536                 }
537
538                 $r->headers_out->{'Content-Length'} = length($tpl_vars{MAIN});
539
540                 if (!$::MP2) {
541                         $r->send_http_header;
542                 }
543
544                 $r->print($tpl_vars{MAIN});
545                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
546
547         }
548         else {
549
550                 # original size
551                 if (defined($ENV{QUERY_STRING}) && $ENV{QUERY_STRING} eq 'orig') {
552                         if ($r->dir_config('GalleryAllowOriginal') ? 1 : 0) {
553                                 $r->filename($filename);
554                                 return $::MP2 ? Apache2::Const::DECLINED() : Apache::Constants::DECLINED();
555                         } else {
556                                 return $::MP2 ? Apache2::Const::FORBIDDEN() : Apache::Constants::FORBIDDEN();
557                         }
558                 }
559         
560                 # Create cache dir if not existing
561                 my @tmp = split (/\//, $filename);
562                 my $picfilename = pop @tmp;
563                 my $path = (join "/", @tmp)."/";
564                 my $cache_path = cache_dir($r, 1);
565
566                 unless (-d $cache_path) {
567                         unless (create_cache($r, $cache_path)) {
568                                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
569                         }
570                 }
571
572                 my ($orig_width, $orig_height, $type) = imgsize($filename);
573
574                 my $imageinfo = get_imageinfo($r, $filename, $type, $orig_width, $orig_height);
575
576                 my ($image_width, $width, $height, $original_size) = get_image_display_size($cgi, $r, $orig_width, $orig_height);
577
578                 my $cached = get_scaled_picture_name($filename, $image_width, $height);
579                 
580                 my $tpl_dir = $r->dir_config('GalleryTemplateDir');
581
582                 my %templates = create_templates({layout         => "$tpl_dir/layout.tpl",
583                                                   picture        => "$tpl_dir/showpicture.tpl",
584                                                   navpicture     => "$tpl_dir/navpicture.tpl",
585                                                   info           => "$tpl_dir/info.tpl",
586                                                   scale          => "$tpl_dir/scale.tpl",
587                                                   scaleactive    => "$tpl_dir/scaleactive.tpl",
588                                                   orig           => "$tpl_dir/orig.tpl",
589                                                   refresh        => "$tpl_dir/refresh.tpl",
590                                                   interval       => "$tpl_dir/interval.tpl",
591                                                   intervalactive => "$tpl_dir/intervalactive.tpl",
592                                                   slideshowisoff => "$tpl_dir/slideshowisoff.tpl",
593                                                   slideshowoff   => "$tpl_dir/slideshowoff.tpl",
594                                                   pictureinfo    => "$tpl_dir/pictureinfo.tpl",
595                                                   nopictureinfo  => "$tpl_dir/nopictureinfo.tpl",
596                                                  });
597
598                 my %tpl_vars;
599
600                 my $resolution = (($image_width > $orig_width) && ($height > $orig_height)) ? 
601                         "$orig_width x $orig_height" : "$image_width x $height";
602
603                 $tpl_vars{TITLE} = "Viewing ".$r->uri()." at $image_width x $height";
604                 $tpl_vars{META} = " ";
605                 $tpl_vars{RESOLUTION} = $resolution;
606                 $tpl_vars{MENU} = generate_menu($r);
607                 $tpl_vars{SRC} = uri_escape(".cache/$cached", $escape_rule);
608                 $tpl_vars{URI} = $r->uri();
609         
610                 my $exif_mode = $r->dir_config('GalleryEXIFMode');
611                 unless ($exif_mode) {
612                         $exif_mode = 'namevalue';
613                 }
614
615                 unless (opendir(DATADIR, $path)) {
616                         show_error($r, 500, "Unable to access directory", "Unable to access directory $path");
617                         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
618                 }
619                 my @pictures = grep { /$img_pattern/i } readdir (DATADIR);
620                 closedir(DATADIR);
621                 @pictures = gallerysort($r, @pictures);
622
623                 $tpl_vars{TOTAL} = scalar @pictures;
624
625                 my $prevpicture;
626                 my $nextpicture;
627         
628                 for (my $i=0; $i <= $#pictures; $i++) {
629                         if ($pictures[$i] eq $picfilename) {
630
631                                 $tpl_vars{NUMBER} = $i+1;
632
633                                 $prevpicture = $pictures[$i-1];
634                                 my $displayprev = ($i>0 ? 1 : 0);
635
636                                 if ($r->dir_config("GalleryWrapNavigation")) {
637                                         $prevpicture = $pictures[$i>0 ? $i-1 : $#pictures];
638                                         $displayprev = 1;
639                                 }
640                                 if ($prevpicture and $displayprev) {
641                                         my ($orig_width, $orig_height, $type) = imgsize($path.$prevpicture);
642                                         my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);      
643                                         my $imageinfo = get_imageinfo($r, $path.$prevpicture, $type, $orig_width, $orig_height);
644                                         my $cached = get_scaled_picture_name($path.$prevpicture, $thumbnailwidth, $thumbnailheight);
645                                         my %nav_vars;
646                                         $nav_vars{URL}       = uri_escape($prevpicture, $escape_rule);
647                                         $nav_vars{FILENAME}  = $prevpicture;
648                                         $nav_vars{WIDTH}     = $width;
649                                         $nav_vars{PICTURE}   = uri_escape(".cache/$cached", $escape_rule);
650                                         $nav_vars{DIRECTION} = "&laquo; <u>p</u>rev";
651                                         $nav_vars{ACCESSKEY} = "P";
652                                         $tpl_vars{BACK} = $templates{navpicture}->fill_in(HASH => \%nav_vars);
653                                 }
654                                 else {
655                                         $tpl_vars{BACK} = "&nbsp;";
656                                 }
657
658                                 $nextpicture = $pictures[$i+1];
659                                 if ($r->dir_config("GalleryWrapNavigation")) {
660                                         $nextpicture = $pictures[$i == $#pictures ? 0 : $i+1];
661                                 }       
662
663                                 if ($nextpicture) {
664                                         my ($orig_width, $orig_height, $type) = imgsize($path.$nextpicture);
665                                         my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);      
666                                         my $imageinfo = get_imageinfo($r, $path.$nextpicture, $type, $thumbnailwidth, $thumbnailheight);
667                                         my $cached = get_scaled_picture_name($path.$nextpicture, $thumbnailwidth, $thumbnailheight);
668                                         my %nav_vars;
669                                         $nav_vars{URL}       = uri_escape($nextpicture, $escape_rule);
670                                         $nav_vars{FILENAME}  = $nextpicture;
671                                         $nav_vars{WIDTH}     = $width;
672                                         $nav_vars{PICTURE}   = uri_escape(".cache/$cached", $escape_rule);
673                                         $nav_vars{DIRECTION} = "<u>n</u>ext &raquo;";
674                                         $nav_vars{ACCESSKEY} = "N";
675
676                                         $tpl_vars{NEXT} = $templates{navpicture}->fill_in(HASH => \%nav_vars);
677                                         $tpl_vars{NEXTURL}   = uri_escape($nextpicture, $escape_rule);
678                                 }
679                                 else {
680                                         $tpl_vars{NEXT} = "&nbsp;";
681                                         $tpl_vars{NEXTURL}   = '#';
682                                 }
683                         }
684                 }
685
686                 my $foundcomment = 0;
687                 if (-f $path . '/' . $picfilename . '.comment') {
688                         my $comment_ref = get_comment($path . '/' . $picfilename . '.comment');
689                         $foundcomment = 1;
690                         $tpl_vars{COMMENT} = $comment_ref->{COMMENT} . '<br />' if $comment_ref->{COMMENT};
691                         $tpl_vars{TITLE} = $comment_ref->{TITLE} if $comment_ref->{TITLE};
692                 } elsif ($r->dir_config('GalleryCommentExifKey')) {
693                         my $comment = decode("utf8", $imageinfo->{$r->dir_config('GalleryCommentExifKey')});
694                         $tpl_vars{COMMENT} = encode("iso-8859-1", $comment);
695                 } else {
696                         $tpl_vars{COMMENT} = '';
697                 }
698
699                 my @infos = split /, /, $r->dir_config('GalleryInfo') ? $r->dir_config('GalleryInfo') : 'Picture Taken => DateTimeOriginal, Flash => Flash';
700                 my $foundinfo = 0;
701                 my $exifvalues;
702                 foreach (@infos) {
703         
704                         my ($human_key, $exif_key) = (split " => ")[0,1];
705                         my $value = $imageinfo->{$human_key};
706                         if (defined($value)) {
707
708                                 $foundinfo = 1;
709
710                                 if ($exif_mode eq 'namevalue') {
711                                         my %info_vars;
712                                         $info_vars{KEY} = $human_key;
713                                         $info_vars{VALUE} = $value;
714                                         $tpl_vars{INFO} .=  $templates{info}->fill_in(HASH => \%info_vars);
715                                 }
716
717                                 if ($exif_mode eq 'variables') {
718                                         $tpl_vars{"EXIF_".uc($exif_key)} = $value;
719                                 }
720
721                                 if ($exif_mode eq 'values') {
722                                         $exifvalues .= "| ".$value." ";
723                                 }
724
725                         } 
726
727                 }
728
729                 if ($exif_mode eq 'values') {
730                         if (defined($exifvalues)) {
731                                 $tpl_vars{EXIFVALUES} = $exifvalues;
732                         }
733                         else {
734                                 $tpl_vars{EXIFVALUES} = "";
735                         }
736                 }
737
738                 if ($foundcomment and !$foundinfo) {
739                         $tpl_vars{INFO} = "";
740                 }
741
742                 if ($exif_mode ne 'namevalue') {
743                         $tpl_vars{INFO} = "";
744                 }
745
746                 if ($exif_mode eq 'namevalue' && $foundinfo or $foundcomment) {
747
748                         $tpl_vars{PICTUREINFO} = $templates{pictureinfo}->fill_in(HASH => \%tpl_vars);
749
750                         unless (defined($exifvalues)) {
751                                 $tpl_vars{EXIFVALUES} = "";
752                         }
753
754                 }
755                 else {
756                         $tpl_vars{PICTUREINFO} = $templates{nopictureinfo}->fill_in(HASH => \%tpl_vars);
757                 }
758
759                 # Fill in sizes and determine if any are smaller than the
760                 # actual image. If they are, $scaleable=1
761                 my $scaleable = 0;
762                 my @sizes = split (/ /, $r->dir_config('GallerySizes') ? $r->dir_config('GallerySizes') : '640 800 1024 1600');
763                 foreach my $size (@sizes) {
764                         if ($size<=$original_size) {
765                                 my %sizes_vars;
766                                 $sizes_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
767                                 $sizes_vars{SIZE}     = $size;
768                                 $sizes_vars{WIDTH}    = $size;
769                                 if ($width == $size) {
770                                         $tpl_vars{SIZES} .= $templates{scaleactive}->fill_in(HASH => \%sizes_vars);
771                                 }
772                                 else {
773                                         $tpl_vars{SIZES} .= $templates{scale}->fill_in(HASH => \%sizes_vars);
774                                 }
775                                 $scaleable = 1;
776                         }
777                 }
778
779                 unless ($scaleable) {
780                         my %sizes_vars;
781                         $sizes_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
782                         $sizes_vars{SIZE}     = $original_size;
783                         $sizes_vars{WIDTH}    = $original_size;
784                         $tpl_vars{SIZES} .= $templates{scaleactive}->fill_in(HASH => \%sizes_vars);
785                 }
786
787                 $tpl_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
788
789                 if ($r->dir_config('GalleryAllowOriginal')) {
790                         $tpl_vars{SIZES} .= $templates{orig}->fill_in(HASH => \%tpl_vars);
791                 }
792
793                 my @slideshow_intervals = split (/ /, $r->dir_config('GallerySlideshowIntervals') ? $r->dir_config('GallerySlideshowIntervals') : '3 5 10 15 30');
794                 foreach my $interval (@slideshow_intervals) {
795
796                         my %slideshow_vars;
797                         $slideshow_vars{IMAGEURI} = uri_escape($r->uri(), $escape_rule);
798                         $slideshow_vars{SECONDS} = $interval;
799                         $slideshow_vars{WIDTH} = ($width > $height ? $width : $height);
800
801                         if ($cgi->param('slideshow') && $cgi->param('slideshow') == $interval and $nextpicture) {
802                                 $tpl_vars{SLIDESHOW} .= $templates{intervalactive}->fill_in(HASH => \%slideshow_vars);
803                         }
804                         else {
805
806                                 $tpl_vars{SLIDESHOW} .= $templates{interval}->fill_in(HASH => \%slideshow_vars);
807
808                         }
809                 }
810
811                 if ($cgi->param('slideshow') and $nextpicture) {
812
813                         $tpl_vars{SLIDESHOW} .= $templates{slideshowoff}->fill_in(HASH => \%tpl_vars);
814
815                         unless ((grep $cgi->param('slideshow') == $_, @slideshow_intervals)) {
816                                 show_error($r, 200, "Invalid interval", "Invalid slideshow interval choosen");
817                                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
818                         }
819
820                         $tpl_vars{URL} = uri_escape($nextpicture, $escape_rule);
821                         $tpl_vars{WIDTH} = ($width > $height ? $width : $height);
822                         $tpl_vars{INTERVAL} = $cgi->param('slideshow');
823                         $tpl_vars{META} .=  $templates{refresh}->fill_in(HASH => \%tpl_vars);
824
825                 }
826                 else {
827                         $tpl_vars{SLIDESHOW} .=  $templates{slideshowisoff}->fill_in(HASH => \%tpl_vars);
828                 }
829
830                 $tpl_vars{MAIN} = $templates{picture}->fill_in(HASH => \%tpl_vars);
831                 $tpl_vars{MAIN} = $templates{layout}->fill_in(HASH => \%tpl_vars);
832
833                 $r->content_type('text/html');
834                 $r->headers_out->{'Content-Length'} = length($tpl_vars{MAIN});
835
836                 if (!$::MP2) {
837                         $r->send_http_header;
838                 }
839
840                 $r->print($tpl_vars{MAIN});
841                 return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
842
843         }
844
845 }
846
847 sub cache_dir {
848
849         my ($r, $strip_filename) = @_;
850
851         my $cache_root;
852
853         unless ($r->dir_config('GalleryCacheDir')) {
854
855                 $cache_root = '/var/tmp/Apache-Gallery/';
856                 if ($r->server->is_virtual) {
857                         $cache_root = File::Spec->catdir($cache_root, $r->server->server_hostname);
858                 } else {
859                         $cache_root = File::Spec->catdir($cache_root, $r->location);
860                 }
861
862         } else {
863
864                 $cache_root = $r->dir_config('GalleryCacheDir');
865
866         }
867
868         # If the uri contains .cache we need to remove it
869         my $uri = $r->uri;
870         $uri =~ s/\.cache//;
871
872         my (undef, $dirs, $filename) = File::Spec->splitpath($uri);
873         # We don't need a volume as this is a relative path
874
875         if ($strip_filename) {
876                 return(File::Spec->canonpath(File::Spec->catdir($cache_root, $dirs)));
877         } else {
878                 return(File::Spec->canonpath(File::Spec->catfile($cache_root, $dirs, $filename)));
879         }
880 }
881
882 sub create_cache {
883
884         my ($r, $path) = @_;
885
886                 unless (mkdirhier ($path)) {
887                         show_error($r, 500, $!, "Unable to create cache directory in $path: $!");
888                         return 0;
889                 }
890
891         return 1;
892 }
893
894 sub mkdirhier {
895
896         my $dir = shift;
897
898         unless (-d $dir) {
899
900                 unless (mkdir($dir, 0755)) {
901                         my $parent = $dir;
902                         $parent =~ s/\/[^\/]*$//;
903
904                         mkdirhier($parent);
905
906                         mkdir($dir, 0755);
907                 }
908         }
909 }
910
911 sub get_scaled_picture_name {
912
913         my ($fullpath, $width, $height) = @_;
914
915         my (undef, undef, $type) = imgsize($fullpath);
916
917         my @dirs = split(/\//, $fullpath);
918         my $filename = pop(@dirs);
919         my $newfilename;
920
921         if (grep $type eq $_, qw(PPM TIF GIF)) {
922                 $newfilename = $width."x".$height."-".$filename;
923                 # needs to be configurable
924                 $newfilename =~ s/\.(\w+)$/-$1\.jpg/;
925         } else {
926                 $newfilename = $width."x".$height."-".$filename;
927         }
928
929         return $newfilename;
930         
931 }
932
933 sub scale_picture {
934
935         my ($r, $fullpath, $width, $height, $imageinfo) = @_;
936
937         my @dirs = split(/\//, $fullpath);
938         my $filename = pop(@dirs);
939
940         my ($orig_width, $orig_height, $type) = imgsize($fullpath);
941
942         my $cache = cache_dir($r, 1);
943
944         my $newfilename = get_scaled_picture_name($fullpath, $width, $height);
945
946         if (($width > $orig_width) && ($height > $orig_height)) {
947                 # Run it through the resize code anyway to get watermarks
948                 $width = $orig_width;
949                 $height = $orig_height;
950         }
951
952         my ($thumbnailwidth, $thumbnailheight) = get_thumbnailsize($r, $orig_width, $orig_height);
953
954         # Do we want to generate a new file in the cache?
955         my $scale = 1;
956
957         if (-f $cache."/".$newfilename) {       
958                 $scale = 0;
959
960                 # Check to see if the image has changed
961                 my $filestat = stat($fullpath);
962                 my $cachestat = stat($cache."/".$newfilename);
963                 if ($filestat->mtime >= $cachestat->mtime) {
964                         $scale = 1;
965                 }       
966
967                 # Check to see if the .rotate file has been added or changed
968                 if (-f $fullpath . ".rotate") {
969                         my $rotatestat = stat($fullpath . ".rotate");
970                         if ($rotatestat->mtime > $cachestat->mtime) {
971                                 $scale = 1;
972                         }       
973                 }               
974                 # Check to see if the copyrightimage has been added or changed
975                 if ($r->dir_config('GalleryCopyrightImage') && -f $r->dir_config('GalleryCopyrightImage')) {
976                         unless ($width == $thumbnailwidth or $width == $thumbnailheight) {
977                                 my $copyrightstat = stat($r->dir_config('GalleryCopyrightImage'));
978                                 if ($copyrightstat->mtime > $cachestat->mtime) {
979                                         $scale = 1;
980                                 }       
981                         }
982                 }       
983
984         }       
985
986         if ($scale) {
987
988                 my $newpath = $cache."/".$newfilename;
989                 my $rotate = readfile_getnum($r, $imageinfo, $fullpath . ".rotate");
990                 my $quality = $r->dir_config('GalleryQuality');
991
992                 if ($width == $thumbnailwidth or $width == $thumbnailheight) {
993
994                         resizepicture($r, $fullpath, $newpath, $width, $height, $rotate, '', '', '', '', '', '');
995
996                 } else {
997
998                         resizepicture($r, $fullpath, $newpath, $width, $height, $rotate, 
999                                 ($r->dir_config('GalleryCopyrightImage') ? $r->dir_config('GalleryCopyrightImage') : ''), 
1000                                 ($r->dir_config('GalleryTTFDir') ? $r->dir_config('GalleryTTFDir') : ''), 
1001                                 ($r->dir_config('GalleryCopyrightText') ? $r->dir_config('GalleryCopyrightText') : ''), 
1002                                 ($r->dir_config('GalleryCopyrightColor') ? $r->dir_config('GalleryCopyrightColor') : ''), 
1003                                 ($r->dir_config('GalleryTTFFile') ? $r->dir_config('GalleryTTFFile') : ''), 
1004                                 ($r->dir_config('GalleryTTFSize') ?  $r->dir_config('GalleryTTFSize') : ''),
1005                                 ($r->dir_config('GalleryCopyrightBackgroundColor') ?  $r->dir_config('GalleryCopyrightBackgroundColor') : ''),
1006                                 $quality);
1007
1008                 }
1009         }
1010
1011         return $newfilename;
1012
1013 }
1014
1015 sub get_thumbnailsize {
1016         my ($r, $orig_width, $orig_height) = @_;
1017
1018         my $gallerythumbnailsize=$r->dir_config('GalleryThumbnailSize');
1019
1020         if (defined($gallerythumbnailsize)) {
1021                 warn("Invalid setting for GalleryThumbnailSize") unless
1022                         $gallerythumbnailsize =~ /^\s*\d+\s*x\s*\d+\s*$/i;
1023         }
1024
1025         my ($thumbnailwidth, $thumbnailheight) = split(/x/i, ($gallerythumbnailsize) ?  $gallerythumbnailsize : "100x75");
1026
1027         my $width = $thumbnailwidth;
1028         my $height = $thumbnailheight;
1029
1030         # If the image is rotated, flip everything around.
1031         if (defined $r->dir_config('GalleryThumbnailSizeLS')
1032         and $r->dir_config('GalleryThumbnailSizeLS') eq '1'
1033         and $orig_width < $orig_height) {
1034                 
1035                 $width = $thumbnailheight;
1036                 $height = $thumbnailwidth;
1037         }
1038
1039         my $scale = ($orig_width ? $width/$orig_width : 1);
1040
1041         if ($orig_height) {
1042                 if ($orig_height * $scale > $thumbnailheight) {
1043                         $scale = $height/$orig_height;
1044                         $width = $orig_width * $scale;
1045                 }
1046         }
1047
1048         $height = $orig_height * $scale;
1049
1050         $height = floor($height);
1051         $width  = floor($width);
1052
1053         return ($width, $height);
1054 }
1055
1056 sub get_image_display_size {
1057         my ($cgi, $r, $orig_width, $orig_height) = @_;
1058
1059         my $width = $orig_width;
1060
1061         my $original_size=$orig_height;
1062         if ($orig_width>$orig_height) {
1063                 $original_size=$orig_width;
1064         }
1065
1066         # Check if the selected width is allowed
1067         my @sizes = split (/ /, $r->dir_config('GallerySizes') ? $r->dir_config('GallerySizes') : '640 800 1024 1600');
1068
1069         my %cookies = fetch CGI::Cookie;
1070
1071         if ($cgi->param('width')) {
1072                 unless ((grep $cgi->param('width') == $_, @sizes) or ($cgi->param('width') == $original_size)) {
1073                         show_error($r, 200, "Invalid width", "The specified width is invalid");
1074                         return $::MP2 ? Apache2::Const::OK() : Apache::Constants::OK();
1075                 }
1076
1077                 $width = $cgi->param('width');
1078                 my $cookie = new CGI::Cookie(-name => 'GallerySize', -value => $width, -expires => '+6M');
1079                 $r->headers_out->{'Set-Cookie'} = $cookie;
1080
1081         } elsif ($cookies{'GallerySize'} && (grep $cookies{'GallerySize'}->value == $_, @sizes)) {
1082
1083                 $width = $cookies{'GallerySize'}->value;
1084
1085         } else {
1086                 $width = $sizes[0];
1087         }       
1088
1089         my $scale;
1090         my $image_width;
1091         if ($orig_width<$orig_height) {
1092                 $scale = ($orig_height ? $width/$orig_height: 1);
1093                 $image_width=$width*$orig_width/$orig_height;
1094         }
1095         else {
1096                 $scale = ($orig_width ? $width/$orig_width : 1);
1097                 $image_width = $width;
1098         }
1099
1100         my $height = $orig_height * $scale;
1101
1102         $image_width = floor($image_width);
1103         $width       = floor($width);
1104         $height      = floor($height);
1105
1106         return ($image_width, $width, $height, $original_size);
1107 }
1108
1109 sub get_imageinfo {
1110         my ($r, $file, $type, $width, $height) = @_;
1111         my $imageinfo = {};
1112         if ($type eq 'Data stream is not a known image file format') {
1113                 # should never be reached, this is supposed to be handled outside of here
1114                 log_error("Something was fishy with the type of the file $file\n");
1115         } else { 
1116
1117                 # Some files, like TIFF, PNG, GIF do not have EXIF info 
1118                 # embedded but use .thm files instead.
1119                 $imageinfo = get_imageinfo_from_thm_file($file, $width, $height);
1120
1121                 # If there is no .thm file and our file is a JPEG file we try to extract the EXIf
1122                 # info using Image::Info
1123                 unless (defined($imageinfo) && (grep $type eq $_, qw(JPG))) {
1124                         # Only for files that natively keep the EXIF info in the same file
1125                         $imageinfo = image_info($file);
1126                 }
1127         }
1128
1129         unless (defined($imageinfo->{width}) and defined($imageinfo->{height})) {
1130                 $imageinfo->{width} = $width;
1131                 $imageinfo->{height} = $height;
1132         }
1133
1134         my @infos = split /, /, $r->dir_config('GalleryInfo') ? $r->dir_config('GalleryInfo') : 'Picture Taken => DateTimeOriginal, Flash => Flash';
1135         foreach (@infos) {
1136                 
1137                 my ($human_key, $exif_key) = (split " => ")[0,1];
1138                 if (defined($exif_key) && defined($imageinfo->{$exif_key})) {
1139                         my $value = "";
1140                         if (ref($imageinfo->{$exif_key}) eq 'Image::TIFF::Rational') { 
1141                                 $value = $imageinfo->{$exif_key}->as_string;
1142                         } 
1143                         elsif (ref($imageinfo->{$exif_key}) eq 'ARRAY') {
1144                                 foreach my $element (@{$imageinfo->{$exif_key}}) {
1145                                         if (ref($element) eq 'ARRAY') {
1146                                                 foreach (@{$element}) {
1147                                                         $value .= $_ . ' ';
1148                                                 }
1149                                         } 
1150                                         elsif (ref($element) eq 'HASH') {
1151                                                 $value .= "<br />{ ";
1152                                         foreach (sort keys %{$element}) {
1153                                                         $value .= "$_ = " . $element->{$_} . ' ';
1154                                                 }
1155                                         $value .= "} ";
1156                                         } 
1157                                         else {
1158                                                 $value .= $element;
1159                                         }
1160                                         $value .= ' ';
1161                                 }
1162                         } 
1163                         else {
1164                                 my $exif_value = $imageinfo->{$exif_key};
1165                                 if ($human_key eq 'Flash' && $exif_value =~ m/\d/) {
1166                                         my %flashmodes = (
1167                                                 "0"  => "No",
1168                                                 "1"  => "Yes",
1169                                                 "9"  => "Yes",
1170                                                 "16" => "No (Compulsory) Should be External Flash",
1171                                                 "17" => "Yes (External)",
1172                                                 "24" => "No",
1173                                                 "25" => "Yes (Auto)",
1174                                                 "73" => "Yes (Compulsory, Red Eye Reducing)",
1175                                                 "89" => "Yes (Auto, Red Eye Reducing)"
1176                                         );
1177                                         $exif_value = defined $flashmodes{$exif_value} ? $flashmodes{$exif_value} : 'unknown flash mode';
1178                                 }
1179                                 $value = $exif_value;
1180                         }
1181                         if ($exif_key eq 'MeteringMode') {
1182                                 my $exif_value = $imageinfo->{$exif_key};
1183                                 if ($exif_value =~ /^\d+$/) {
1184                                         my %meteringmodes = (
1185                                                 '0' => 'unknown',
1186                                                 '1' => 'Average',
1187                                                 '2' => 'CenterWeightedAverage',
1188                                                 '3' => 'Spot',
1189                                                 '4' => 'MultiSpot',
1190                                                 '5' => 'Pattern',
1191                                                 '6' => 'Partial',
1192                                                 '255' => 'Other'
1193                                         );
1194                                         $exif_value = defined $meteringmodes{$exif_value} ? $meteringmodes{$exif_value} : 'unknown metering mode';
1195                                 }
1196                                 $value = $exif_value;
1197                                 
1198                         }
1199                         if ($exif_key eq 'LightSource') {
1200                                 my $exif_value = $imageinfo->{$exif_key};
1201                                 if ($exif_value =~ /^\d+$/) {
1202                                         my %lightsources = (
1203                                                 '0' => 'unknown',
1204                                                 '1' => 'Daylight',
1205                                                 '2' => 'Fluorescent',
1206                                                 '3' => 'Tungsten (incandescent light)',
1207                                                 '4' => 'Flash',
1208                                                 '9' => 'Fine weather',
1209                                                 '10' => 'Cloudy weather',
1210                                                 '11' => 'Shade',
1211                                                 '12' => 'Daylight fluorescent',
1212                                                 '13' => 'Day white fluorescent',
1213                                                 '14' => 'Cool white fluorescent',
1214                                                 '15' => 'White fluorescent',
1215                                                 '17' => 'Standard light A',
1216                                                 '18' => 'Standard light B',
1217                                                 '19' => 'Standard light C',
1218                                                 '20' => 'D55',
1219                                                 '21' => 'D65',
1220                                                 '22' => 'D75',
1221                                                 '23' => 'D50',
1222                                                 '24' => 'ISO studio tungsten',
1223                                                 '255' => 'other light source'
1224                                         );
1225                                         $exif_value = defined $lightsources{$exif_value} ? $lightsources{$exif_value} : 'unknown light source';
1226                                 }
1227                                 $value = $exif_value;
1228                         }
1229                         if ($exif_key eq 'FocalLength') {
1230                                 if ($value =~ /^(\d+)\/(\d+)$/) {
1231                                         $value = eval { $1 / $2 };
1232                                         if ($@) {
1233                                                 $value = $@;
1234                                         } else {
1235                                                 $value = int($value + 0.5) . "mm";
1236
1237                                         }
1238                                 }
1239                         }
1240                         if ($exif_key eq 'ShutterSpeedValue') {
1241                                 if ($value =~ /^((?:\-)?\d+)\/(\d+)$/) {
1242                                         $value = eval { $1 / $2 };
1243                                         if ($@) {
1244                                                 $value = $@;
1245                                         } else {
1246                                                 eval {
1247                                                         $value = 1/(exp($value*log(2)));
1248                                                         if ($value < 1) {
1249                                                                 $value = "1/" . (int((1/$value)));
1250                                                         } else {
1251                                                                 $value = int($value*10)/10; 
1252                                                         }
1253                                                 };
1254                                                 if ($@) {
1255                                                         $value = $@;
1256                                                 } else {
1257                                                         $value = $value . " sec";
1258                                                 }
1259                                         }
1260                                 }
1261                         }
1262                         if ($exif_key eq 'ApertureValue') {
1263                                 if ($value =~ /^(\d+)\/(\d+)$/) {
1264                                         $value = eval { $1 / $2 };
1265                                         if ($@) {
1266                                                 $value = $@;
1267                                         } else {
1268                                                 # poor man's rounding
1269                                                 $value = int(exp($value*log(2)*0.5)*10)/10;
1270                                                 $value = "f" . $value;
1271                                         }
1272                                 }
1273                         }
1274                         if ($exif_key eq 'FNumber') {
1275                                 if ($value =~ /^(\d+)\/(\d+)$/) {
1276                                         $value = eval { $1 / $2 };
1277                                         if ($@) {
1278                                                 $value = $@;
1279                                         } else {
1280                                                 $value = int($value*10+0.5)/10;
1281                                                 $value = "f" . $value;
1282                                         }
1283                                 }
1284                         }
1285                         $imageinfo->{$human_key} = $value;
1286                 } 
1287         }
1288
1289         if ($r->dir_config('GalleryUseFileDate') &&
1290                 ($r->dir_config('GalleryUseFileDate') eq '1'
1291                 || !$imageinfo->{"Picture Taken"} )) {
1292
1293                 my $st = stat($file);
1294                 $imageinfo->{"DateTimeOriginal"} = $imageinfo->{"Picture Taken"} = scalar localtime($st->mtime) if $st;
1295         }
1296
1297         return $imageinfo;
1298 }
1299
1300 sub get_imageinfo_from_thm_file {
1301
1302         my ($file, $width, $height) = @_;
1303
1304         my $imageinfo = undef;
1305         # Windows based file extensions are often .THM, so check 
1306         # for both .thm and .THM
1307         my $unix_file = $file;
1308         my $windows_file = $file;
1309         $unix_file =~ s/\.(\w+)$/.thm/;
1310         $windows_file =~ s/\.(\w+)$/.THM/;
1311
1312         if (-e $unix_file && -f $unix_file && -r $unix_file) {
1313                 $imageinfo = image_info($unix_file);
1314                 $imageinfo->{width} = $width;
1315                 $imageinfo->{height} = $height;
1316         }
1317         elsif (-e $windows_file && -f $windows_file && -r $windows_file) {
1318                 $imageinfo = image_info($windows_file);
1319                 $imageinfo->{width} = $width;
1320                 $imageinfo->{height} = $height;
1321         }
1322
1323         return $imageinfo;
1324 }
1325
1326
1327 sub readfile_getnum {
1328         my ($r, $imageinfo, $filename) = @_;
1329
1330         my $rotate = 0;
1331
1332         print STDERR "orientation: ".$imageinfo->{Orientation}."\n";
1333         # Check to see if the image contains the Orientation EXIF key,
1334         # but allow user to override using rotate
1335         if (!defined($r->dir_config("GalleryAutoRotate")) 
1336                 || $r->dir_config("GalleryAutoRotate") eq "1") {
1337                 if (defined($imageinfo->{Orientation})) {
1338                         print STDERR $imageinfo->{Orientation}."\n";
1339                         if ($imageinfo->{Orientation} eq 'right_top') {
1340                                 $rotate=1;
1341                         }       
1342                         elsif ($imageinfo->{Orientation} eq 'left_bot') {
1343                                 $rotate=3;
1344                         }
1345                 }
1346         }
1347
1348         if (open(FH, "<$filename")) {
1349                 my $temp = <FH>;
1350                 chomp($temp);
1351                 close(FH);
1352                 unless ($temp =~ /^\d$/) {
1353                         $rotate = 0;
1354                 }
1355                 unless ($temp == 1 || $temp == 2 || $temp == 3) {
1356                         $rotate = 0;
1357                 }
1358                 $rotate = $temp;
1359         }
1360
1361         return $rotate;
1362 }
1363
1364 sub get_filecontent {
1365         my $file = shift;
1366         open(FH, $file) or return undef;
1367         my $content = '';
1368         {
1369                 local $/;
1370                 $content = <FH>;
1371         }
1372         close(FH);
1373         return $content;
1374 }
1375
1376 sub get_comment {
1377         my $filename = shift;
1378         my $comment_ref = {};
1379         $comment_ref->{TITLE} = undef;
1380         $comment_ref->{COMMENT} = '';
1381
1382         open(FH, $filename) or return $comment_ref;
1383         my $title = <FH>;
1384         if ($title =~ m/^TITLE: (.*)$/) {
1385                 chomp($comment_ref->{TITLE} = $1);
1386         } 
1387         else {
1388                 $comment_ref->{COMMENT} = $title;
1389         }
1390
1391         while (<FH>) {
1392                 chomp;
1393                 $comment_ref->{COMMENT} .= $_;
1394         }
1395         close(FH);
1396
1397         return $comment_ref;
1398 }
1399
1400 sub show_error {
1401
1402         my ($r, $statuscode, $errortitle, $error) = @_;
1403
1404         my $tpl = $r->dir_config('GalleryTemplateDir');
1405
1406         my %templates = create_templates({layout => "$tpl/layout.tpl",
1407                                           error  => "$tpl/error.tpl",
1408                                          });
1409
1410         my %tpl_vars;
1411         $tpl_vars{TITLE}      = "Error! $errortitle";
1412         $tpl_vars{META}       = "";
1413         $tpl_vars{ERRORTITLE} = "Error! $errortitle";
1414         $tpl_vars{ERROR}      = $error;
1415
1416         $tpl_vars{MAIN} = $templates{error}->fill_in(HASH => \%tpl_vars);
1417
1418         $tpl_vars{PAGE} = $templates{layout}->fill_in(HASH => \%tpl_vars);
1419
1420         $r->status($statuscode);
1421         $r->content_type('text/html');
1422
1423         $r->print($tpl_vars{PAGE});
1424
1425 }
1426
1427 sub generate_menu {
1428
1429         my $r = shift;
1430
1431         my $root_text = (defined($r->dir_config('GalleryRootText')) ? $r->dir_config('GalleryRootText') : "root:" );
1432         my $root_path = (defined($r->dir_config('GalleryRootPath')) ? $r->dir_config('GalleryRootPath') : "" );
1433
1434         my $subr = $r->lookup_uri($r->uri);
1435         my $filename = $subr->filename;
1436
1437         my @links = split (/\//, $r->uri);
1438         my $uri = $r->uri;
1439         $uri =~ s/^$root_path//g;
1440
1441         @links = split (/\//, $uri);
1442
1443         # Get the full path of the base directory
1444         my $dirname;
1445         {
1446                 my @direlem = split (/\//, $filename);
1447                 for my $i ( 0 .. ( scalar(@direlem) - scalar(@links) ) ) {
1448                         $dirname .= shift(@direlem) . '/';
1449                 }
1450                 chop $dirname;
1451         }
1452
1453         my $picturename;
1454         if (-f $filename) {
1455                 $picturename = pop(@links);     
1456         }
1457
1458         if ($r->uri eq $root_path) {
1459                 return qq{ <a href="$root_path">$root_text</a> };
1460         }
1461
1462         my $menu;
1463         my $menuurl = $root_path;
1464         foreach my $link (@links) {
1465
1466                 $menuurl .= $link."/";
1467                 my $linktext = $link;
1468                 unless (length($link)) {
1469                         $linktext = "$root_text ";
1470                 }
1471                 else {
1472                         
1473                         $dirname = File::Spec->catdir($dirname, $link);
1474
1475                         if (-e $dirname . ".folder") {
1476                                 $linktext = get_filecontent($dirname . ".folder");
1477                         }
1478                 }
1479
1480                 if ("$root_path$uri" eq $menuurl) {
1481                         $menu .= "$linktext  / ";
1482                 }
1483                 else {
1484                         $menu .= "<a href=\"".uri_escape($menuurl, $escape_rule)."\">$linktext</a> / ";
1485                 }
1486
1487         }
1488
1489         if (-f $filename) {
1490                 $menu .= $picturename;
1491         }
1492         else {
1493
1494                 if ($r->dir_config('GallerySelectionMode') && $r->dir_config('GallerySelectionMode') eq '1') {
1495                         $menu .= "<a href=\"".uri_escape($menuurl, $escape_rule);
1496                         $menu .= "?select=1\">[select]</a> ";
1497                 }
1498         }
1499
1500         return $menu;
1501 }
1502
1503 sub resizepicture {
1504         my ($r, $infile, $outfile, $x, $y, $rotate, $copyrightfile, $GalleryTTFDir, $GalleryCopyrightText, $text_color, $GalleryTTFFile, $GalleryTTFSize, $GalleryCopyrightBackgroundColor, $quality) = @_;
1505
1506         # Load image
1507         my $image = Image::Imlib2->load($infile) or warn("Unable to open file $infile, $!");
1508
1509         # Scale image
1510         $image=$image->create_scaled_image($x, $y) or warn("Unable to scale image $infile. Are you running out of memory?");
1511
1512         # Rotate image
1513         if ($rotate != 0) {
1514                 $image->image_orientate($rotate);
1515         }
1516
1517         # blend copyright image onto image
1518         if ($copyrightfile ne '') {
1519                 if (-f $copyrightfile and (my $logo=Image::Imlib2->load($copyrightfile))) {
1520                         my $x = $image->get_width();
1521                         my $y = $image->get_height();
1522                         my $logox = $logo->get_width();
1523                         my $logoy = $logo->get_height();
1524                         $image->blend($logo, 0, 0, 0, $logox, $logoy, $x-$logox, $y-$logoy, $logox, $logoy);
1525                 }
1526                 else {
1527                         log_error("GalleryCopyrightImage $copyrightfile was not found");
1528                 }
1529         }
1530
1531         if ($GalleryTTFDir && $GalleryCopyrightText && $GalleryTTFFile && $text_color) {
1532                 if (!-d $GalleryTTFDir) {
1533
1534                         log_error("GalleryTTFDir $GalleryTTFDir is not a dir\n");
1535
1536                 } elsif ($GalleryCopyrightText eq '') {
1537
1538                         log_error("GalleryCopyrightText is empty. No text inserted to picture\n");
1539
1540                 } elsif (!-e "$GalleryTTFDir/$GalleryTTFFile") {
1541
1542                         log_error("GalleryTTFFile $GalleryTTFFile was not found\n");
1543
1544                 } else {
1545  
1546                         $GalleryTTFFile =~ s/\.TTF$//i;
1547                         $image->add_font_path("$GalleryTTFDir");
1548
1549                         $image->load_font("$GalleryTTFFile/$GalleryTTFSize");
1550                         my($text_x, $text_y) = $image->get_text_size("$GalleryCopyrightText");
1551                         my $x = $image->get_width();
1552                         my $y = $image->get_height();
1553
1554                         my $offset = 3;
1555
1556                         if (($text_x < $x - $offset) && ($text_y < $y - $offset)) {
1557                                 if ($GalleryCopyrightBackgroundColor =~ /^\d+,\d+,\d+,\d+$/) {
1558                                         my ($br_val, $bg_val, $bb_val, $ba_val) = split (/,/, $GalleryCopyrightBackgroundColor);
1559                                         $image->set_colour($br_val, $bg_val, $bb_val, $ba_val);
1560                                         $image->fill_rectangle ($x-$text_x-$offset, $y-$text_y-$offset, $text_x, $text_y);
1561                                 }
1562                                 my ($r_val, $g_val, $b_val, $a_val) = split (/,/, $text_color);
1563                                 $image->set_colour($r_val, $g_val, $b_val, $a_val);
1564                                 $image->draw_text($x-$text_x-$offset, $y-$text_y-$offset, "$GalleryCopyrightText");
1565                         } else {
1566                                 log_error("Text is to big for the picture.\n");
1567                         }
1568                 }
1569         }
1570
1571         if ($quality && $quality =~ m/^\d+$/) {
1572                 $image->set_quality($quality);
1573         }
1574
1575         $image->save($outfile);
1576
1577 }
1578
1579 sub gallerysort {
1580         my $r=shift;
1581         my @files=@_;
1582         my $sortby = $r->dir_config('GallerySortBy');
1583         my $filename=$r->lookup_uri($r->uri)->filename;
1584         $filename=(File::Spec->splitpath($filename))[1] if (-f $filename);
1585         if ($sortby && $sortby =~ m/^(size|atime|mtime|ctime)$/) {
1586                 @files = map(/^\d+ (.*)/, sort map(stat("$filename/$_")->$sortby()." $_", @files));
1587         } else {
1588                 @files = sort @files;
1589         }
1590         return @files;
1591 }
1592
1593 # Create Text::Template objects used by Apache::Gallery. Takes a
1594 # hashref of template_name, template_filename pairs, and returns a
1595 # list of template_name, texttemplate_object pairs.
1596 sub create_templates {
1597      my $templates = shift;
1598
1599      # This routine is called whenever a template has an error. Prints
1600      # the error to STDERR and sticks the error in the output
1601      sub tt_broken {
1602           my %args = @_;
1603           # Pull out the name and filename from the arg option [see
1604           # Text::Template for details]
1605           @args{qw(name file)} = @{$args{arg}};
1606           print STDERR qq(Template $args{name} ("$args{file}") is broken: $args{error});
1607           # Don't include the file name in the output, as the user can see this.
1608           return qq(<!-- Template $args{name} is broken: $args{error} -->);
1609      }
1610
1611
1612
1613      my %texttemplate_objects;
1614
1615      for my $template_name (keys %$templates) {
1616           my $tt_obj = Text::Template->new(TYPE   => 'FILE',
1617                                            SOURCE => $$templates{$template_name},
1618                                            BROKEN => \&tt_broken,
1619                                            BROKEN_ARG => [$template_name, $$templates{$template_name}],
1620                                           )
1621                or die "Unable to create new Text::Template object for $template_name: $Text::Template::ERROR";
1622           $texttemplate_objects{$template_name} = $tt_obj;
1623      }
1624      return %texttemplate_objects;
1625 }
1626
1627 sub log_error {
1628         if ($::MP2) {
1629                 Apache2::RequestUtil->request->log_error(shift());
1630         } else {
1631                 Apache->request->log_error(shift());
1632         }
1633 }
1634
1635 1;
1636
1637 =head1 NAME
1638
1639 Apache::Gallery - mod_perl handler to create an image gallery
1640
1641 =head1 SYNOPSIS
1642
1643 See the INSTALL file in the distribution for installation instructions.
1644
1645 =head1 DESCRIPTION
1646
1647 Apache::Gallery creates an thumbnail index of each directory and allows 
1648 viewing pictures in different resolutions. Pictures are resized on the 
1649 fly and cached. The gallery can be configured and customized in many ways
1650 and a custom copyright image can be added to all the images without
1651 modifying the original.
1652
1653 =head1 CONFIGURATION
1654
1655 In your httpd.conf you set the global options for the gallery. You can
1656 also override each of the options in .htaccess files in your gallery
1657 directories.
1658
1659 The options are set in the httpd.conf/.htaccess file using the syntax:
1660 B<PerlSetVar OptionName 'value'>
1661
1662 Example: B<PerlSetVar GalleryCacheDir '/var/tmp/Apache-Gallery/'>
1663
1664 =over 4
1665
1666 =item B<GalleryAutoRotate>
1667
1668 Some cameras, like the Canon G3, can detect the orientation of a 
1669 the pictures you take and will save this information in the 
1670 'Orientation' EXIF field. Apache::Gallery will then automatically
1671 rotate your images. 
1672
1673 This behavior is default but can be disabled by setting GalleryAutoRotate
1674 to 0.
1675
1676 =item B<GalleryCacheDir>
1677
1678 Directory where Apache::Gallery should create its cache with scaled
1679 pictures. The default is /var/tmp/Apache-Gallery/ . Here, a directory
1680 for each virtualhost or location will be created automaticly. Make
1681 sure your webserver has write access to the CacheDir.
1682
1683 =item B<GalleryTemplateDir>
1684
1685 Full path to the directory where you placed the templates. This option
1686 can be used both in your global configuration and in .htaccess files,
1687 this way you can have different layouts in different parts of your 
1688 gallery.
1689
1690 No default value, this option is required.
1691
1692 =item B<GalleryInfo>
1693
1694 With this option you can define which EXIF information you would like
1695 to present from the image. The format is: '<MyName => KeyInEXIF, 
1696 MyOtherName => OtherKeyInEXIF'
1697
1698 Examples of keys: B<ShutterSpeedValue>, B<ApertureValue>, B<SubjectDistance>,
1699 and B<Camera>
1700
1701 You can view all the keys from the EXIF header using this perl-oneliner:
1702
1703 perl C<-e> 'use Data::Dumper; use Image::Info qw(image_info); print Dumper(image_info(shift));' filename.jpg
1704
1705 Default is: 'Picture Taken => DateTimeOriginal, Flash => Flash'
1706
1707 =item B<GallerySizes>
1708
1709 Defines which widths images can be scaled to. Images cannot be
1710 scaled to other widths than the ones you define with this option.
1711
1712 The default is '640 800 1024 1600'
1713
1714 =item B<GalleryThumbnailSize>
1715
1716 Defines the width and height of the thumbnail images. 
1717
1718 Defaults to '100x75'
1719
1720 =item B<GalleryThumbnailSizeLS>
1721
1722 If set to '1', B<GalleryThumbnailSize> is the long and the short side of
1723 the thumbnail image instead of the width and height.
1724
1725 Defaults to '0'.
1726
1727 =item B<GalleryCopyrightImage>
1728
1729 Image you want to blend into your images in the lower right
1730 corner. This could be a transparent png saying "copyright
1731 my name 2001".
1732
1733 Optional.
1734
1735 =item B<GalleryWrapNavigation>
1736
1737 Make the navigation in the picture view wrap around (So Next
1738 at the end displays the first picture, etc.)
1739
1740 Set to 1 or 0, default is 0
1741
1742 =item B<GalleryAllowOriginal>
1743
1744 Allow the user to download the Original picture without
1745 resizing or putting the CopyrightImage on it.
1746
1747 Set to 1 or 0, default is 0
1748
1749 =item B<GallerySlideshowIntervals>
1750
1751 With this option you can configure which intervals can be selected for
1752 a slideshow. The default is '3 5 10 15 30'
1753
1754 =item B<GallerySortBy>
1755
1756 Instead of the default filename ordering you can sort by any
1757 stat attribute. For example size, atime, mtime, ctime.
1758
1759 =item B<GalleryDirSortBy>
1760
1761 Set this variable to sort directories differently than other items,
1762 can be set to size, atime, mtime and ctime; setting any other value
1763 will revert to sorting by name.
1764
1765 =item B<GalleryMemoize>
1766
1767 Cache EXIF data using Memoize - this will make Apache::Gallery faster
1768 when many people access the same images, but it will also cache EXIF
1769 data until the current Apache child dies.
1770
1771 =item B<GalleryUseFileDate>
1772
1773 Set this option to 1 to make A::G show the files timestamp
1774 instead of the EXIF value for "Picture taken".
1775
1776 =item B<GallerySelectionMode>
1777
1778 Enable the selection mode. Select images with checkboxes and
1779 get a list of filenames. 
1780
1781 =item B<GalleryEXIFMode>
1782
1783 You can choose how Apache::Gallery should display EXIF info
1784 from your images. 
1785
1786 The default setting is 'namevalue'. This setting will make 
1787 Apache::Gallery print out the names and values of the EXIF values 
1788 you configure with GalleryInfo. The information will be parsed into 
1789 $INFO in pictureinfo.tpl.  
1790
1791 You can also set it to 'values' which will make A::G parse
1792 the configured values into the var $EXIFVALUES as 'value | value | value'
1793
1794 If you set this option to 'variables' the items you configure in GalleryInfo 
1795 will be available to your templates as $EXIF_<KEYNAME> (in all uppercase). 
1796 That means that with the default setting "Picture Taken => DateTimeOriginal, 
1797 Flash => Flash" you will have the variables $EXIF_DATETIMEORIGINAL and 
1798 $EXIF_FLASH avilable to your templates. You can place them
1799 anywhere you want.
1800
1801 =item B<GalleryRootPath>
1802
1803 Change the location of gallery root. The default is ""
1804
1805 =item B<GalleryRootText>
1806
1807 Change the name that appears as the root element in the menu. The
1808 default is "root:"
1809
1810 =item B<GalleryMaxThumbnailsPerPage>
1811
1812 This options controls how many thumbnails should be displayed in a 
1813 page. It requires $BROWSELINKS to be in the index.tpl template file.
1814
1815 =item B<GalleryImgFile>
1816
1817 Pattern matching the files you want Apache::Gallery to view in the
1818 index as thumbnails. 
1819
1820 The default is '\.(jpe?g|png|tiff?|ppm)$'
1821
1822 =item B<GalleryDocFile>
1823
1824 Pattern matching the files you want Apache::Gallery to view in the index
1825 as normal files. All other filetypes will still be served by Apache::Gallery
1826 but are not visible in the index.
1827
1828 The default is '\.(mpe?g|avi|mov|asf|wmv|doc|mp3|ogg|pdf|rtf|wav|dlt|html?|csv|eps)$'
1829
1830 =item B<GalleryTTFDir>
1831
1832 To use the GalleryCopyrightText feature you must set this option to the
1833 directory where your True Type fonts are stored. No default is set.
1834
1835 Example:
1836
1837         PerlSetVar      GalleryTTFDir '/usr/share/fonts/'
1838
1839 =item B<GalleryTTFFile>
1840
1841 To use the GalleryCopyrightText feature this option must be set to the
1842 name of the True Type font you wish to use. Example:
1843
1844         PerlSetVar      GalleryTTFFile 'verdanab.ttf'
1845
1846 =item B<GalleryTTFSize>
1847
1848 Configure the size of the CopyrightText that will be inserted as 
1849 copyright notice in the corner of your pictures.
1850
1851 Example:
1852
1853         PerlSetVar      GalleryTTFSize '10'
1854
1855 =item B<GalleryCopyrightText>
1856
1857 The text that will be inserted as copyright notice.
1858
1859 Example:
1860
1861         PerlSetVar      GalleryCopyrightText '(c) Michael Legart'
1862
1863 =item B<GalleryCopyrightColor>
1864
1865 The text color of your copyright notice.
1866
1867 Examples:
1868
1869 White:
1870         PerlSetVar      GalleryCopyrightColor '255,255,255,255'
1871
1872 Black:
1873         PerlSetVar      GalleryCopyrightColor '0,0,0,255'
1874
1875 Red:
1876         PerlSetVar      GalleryCopyrightColor '255,0,0,255'
1877
1878 Green:
1879         PerlSetVar      GalleryCopyrightColor '0,255,0,255'
1880
1881 Blue:
1882         PerlSetVar      GalleryCopyrightColor '0,0,255,255'
1883
1884 Transparent orange:
1885         PerlSetVar      GalleryCopyrightColor '255,127,0,127'
1886
1887 =item B<GalleryCopyrightBackgroundColor>
1888
1889 The background-color of a GalleryCopyrightText
1890
1891 r,g,b,a - for examples, see GalleryCopyrightColor
1892
1893 =item B<GalleryQuality>
1894
1895 The quality (1-100) of scaled images
1896
1897 This setting affects the quality of the scaled images.
1898 Set this to a low number to reduce the size of the scaled images.
1899 Remember to clear out your cache if you change this setting.
1900 Quality seems to default to 75, at least in the jpeg and png loader code in
1901 Imlib2 1.1.0.
1902
1903 Examples:
1904
1905 Quality at 50:
1906         PerlSetVar      GalleryQuality '50'
1907
1908 =item B<GalleryUnderscoresToSpaces>
1909
1910 Set this option to 1 to convert underscores to spaces in the listing
1911 of directory and file names, as well as in the alt attribute for HTML
1912 <img> tags.
1913
1914 =back
1915
1916 =item B<GalleryCommentExifKey>
1917
1918 Set this option to e.g. ImageDescription to use this field as comments
1919 for images.
1920
1921 =item B<GalleryEnableMediaRss>
1922
1923 Set this option to 1 to enable generation of a media RSS feed. This
1924 can be used e.g. together with the PicLens plugin from http://piclens.com
1925
1926 =head1 FEATURES
1927
1928 =over 4
1929
1930 =item B<Rotate images>
1931
1932 Some cameras, like the Canon G3, detects the orientation of a picture
1933 and adds this info to the EXIF header. Apache::Gallery detects this
1934 and automaticly rotates images with this info.
1935
1936 If your camera does not support this, you can rotate the images 
1937 manually, This can also be used to override the rotate information
1938 from a camera that supports that. You can also disable this behavior
1939 with the GalleryAutoRotate option.
1940
1941 To use this functionality you have to create file with the name of the 
1942 picture you want rotated appened with ".rotate". The file should include 
1943 a number where these numbers are supported:
1944
1945         "1", rotates clockwise by 90 degree
1946         "2", rotates clockwise by 180 degrees
1947         "3", rotates clockwise by 270 degrees
1948
1949 So if we want to rotate "Picture1234.jpg" 90 degrees clockwise we would
1950 create a file in the same directory called "Picture1234.jpg.rotate" with
1951 the number 1 inside of it.
1952
1953 =item B<Comments>
1954
1955 To include comments for a directory you create a <directory>.comment
1956 file where the first line can contain "TITLE: New title" which
1957 will be the title of the page, and a comment on the following 
1958 lines.
1959 To include comments for each picture you create files called 
1960 picture.jpg.comment where the first line can contain "TITLE: New
1961 title" which will be the title of the page, and a comment on the
1962 following lines.
1963
1964 Example:
1965
1966         TITLE: This is the new title of the page
1967         And this is the comment.<br />
1968         And this is line two of the comment.
1969
1970 The visible name of the folder is by default identical to the name of
1971 the folder, but can be changed by creating a file <directory>.folder
1972 with the visible name of the folder.
1973
1974 It is also possible to set GalleryCommentExifKey to the name of an EXIF
1975 field containing the comment, e.g. ImageDescription. The EXIF comment is
1976 overridden by the .comment file if it exists.
1977
1978 =back
1979
1980 =head1 DEPENDENCIES
1981
1982 =over 4
1983
1984 =item B<Perl 5>
1985
1986 =item B<Apache with mod_perl>
1987
1988 =item B<URI::Escape>
1989
1990 =item B<Image::Info>
1991
1992 =item B<Image::Size>
1993
1994 =item B<Text::Template>
1995
1996 =item B<Image::Imlib2>
1997
1998 =item B<X11 libraries>
1999 (ie, XFree86)
2000
2001 =item B<Imlib2>
2002 Remember the -dev package when using rpm, deb or other package formats!
2003
2004 =back
2005
2006 =head1 AUTHOR
2007
2008 Michael Legart <michael@legart.dk>
2009
2010 =head1 COPYRIGHT AND LICENSE
2011
2012 Copyright (C) 2001-2005 Michael Legart <michael@legart.dk>
2013
2014 Templates designed by Thomas Kjaer <tk@lnx.dk>
2015
2016 Apache::Gallery is free software and is released under the Artistic License.
2017 See B<http://www.perl.com/language/misc/Artistic.html> for details.
2018
2019 The video icons are from the GNOME project. B<http://www.gnome.org/>
2020
2021 =head1 THANKS
2022
2023 Thanks to Thomas Kjaer for templates and design of B<http://apachegallery.dk>
2024 Thanks to Thomas Eibner and other for patches. (See the Changes file)
2025
2026 =head1 SEE ALSO
2027
2028 L<perl>, L<mod_perl>, L<Image::Imlib2>, L<CGI::FastTemplate>,
2029 L<Image::Info>, and L<Image::Size>.
2030
2031 =cut