imageindex-1.1.orig/0000755000175000017500000000000010605001316012464 5ustar abiabiimageindex-1.1.orig/imageindex0000755000175000017500000033544410605001307014541 0ustar abiabi#!/usr/bin/perl -w # # Edwin Huffstutler # John Reynolds # # perl script for: web page index/thumbnails of photos. # orginally started life as a menu selector for fvwm2 backgrounds... # # USAGE: # # imageindex [options] # # is assumed to be "." if not given # # Options: (can be abbreviated if unique) # # -title title for this page (saved for susbsequent runs) # -destdir export image/html tree to dir (will be created if needed) # -[no]recurse enable/disable recursion into subdirectories # -[no]medium enable/disable medium size images/links # -[no]slide enable/disable slideshow files # -[no]detail enable/disable detail file # -[no]dirs enable/disable directory entries # -[no]montage enable/disable directory montages # -forceregen force regeneration of thumbnails # -columns number of columns in html table (saved for susbsequent runs) # -exclude Exclude from processing. Can be used multiple times # -includeall Nullifies excluded file list (saved from previous run) # -skipmont Exclude from being included in a directory montage. # -reverse Order timestamps with newest first # -x override thumbnail x size # -y override thumbnail y size # -help show this text # -version show the current version # -d 'var=val' force override of global variable # # See also the configuration section at the top of the program itself, # or in ~/.imageindexrc # # (non-html-generating, utility options) # # -lowercase Lowercase all image files in a directory # -caption Store comment in image # -rotate [cw|ccw] Rotate an image clockwise or counterclockwise # -showexcluded Show which files were excluded in a prior run # ###################################################################### # # Configuration options # # sizes / dirs $thumbnail_dir = 'thumbnail'; $default_thumbnail_x = 200; $default_thumbnail_y = 200; # If both dimensions of the original are within this much of the thumb # dimensions we will skip the thumbnail and just use the original $thumbnail_threshold = 1.0; # tesla $med_x = 800; $med_y = 600; $med_dir = 'medium'; # If both dimensions of the original are within this much of the "medium" # dimensions we will skip creating the medium-size format and just use the # original $med_threshold = 1.6; # Enable/disable features, set default for various flags $do_recurse = 0; # Recurse into subdirs? $do_medium = 1; # Generate medium-format? $do_slide = 1; # Generate slides/frame view? $do_detail = 1; # Generate details page? $do_dirs = 1; # Create directory entries? $do_captions = 1; # Use caption info stored in images? $do_montage = 1; # Create directory montages? $do_emoticons = 1; # Replace ASCII smiley's with images? $do_reverse = 0; # Sort timestamps in reverse order? $do_video_files = 1; # process video files with mplayer? $do_video_thumbnail_icons = 1; # Annotate movie icon onto thumbnails of video files # video file options # Brute force way of filtering out any "movie" files (mpg, mp4, avi, # mov, etc.) which might lurk in a directory # $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)'; # Control which corner video icons are overlayed onto thumbnails of # of video files. Values can be: SouthWest, NorthWest, NorthEast, SouthEast $video_icon_gravity = 'SouthWest'; # Control which video "icon" is overlayed on top of thumbnails of video files # 1 = yellow dot with "play" arrow # 2 = purplish icon of video camera $video_icon = 1; # end video file options $max_video_icons = 0; # do not adjust, this is just a global variable # What the various image links point to - can be 'index', 'fullsize', # 'medium', 'thumbnail', 'slide', or 'details' $index_linkto = 'slide'; $details_linkto = 'index'; $slide_linkto = 'fullsize'; # Default number of columns to use $default_columns = 3; # Orientation of slide frame - 'horizontal' or 'vertical' $frame_orient = 'vertical'; # Location of items in slide pages; 'top', 'bottom', or 'none' $slide_caption = 'top'; $slide_date = 'bottom'; # Details index uses thumbs reduced by this amount $detailshrink = 2; # Quality for generated images $thumb_quality = 50; $med_quality = 80; # Minimum and maximum number of tiles in directory montage images $montage_min = 4; $montage_max = 36; # Space between montage images $montage_whitespace = 2; # What to do with leftover montage tiles; can be # 'blank' or 'repeat' $montage_fill = 'blank'; # Stylesheet specs # Set element font, etc. properties here $stylesheet = ' body { color: black; background: white; } /* Fonts in the title */ h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif; font-size: 200%; font-weight: bold; text-align: center; } h2.daterange { font-family: Arial,Helvetica,sans-serif; font-size: 125%; text-align: center; } h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%; text-align: center; } /* Photo captions & Directory titles */ div.caption { font-family: Arial,Helvetica,sans-serif; font-size: 100%; font-weight: bold; margin: 1em; } /* Overall fonts on the index and details page */ div.index { font-family: Arial,Helvetica,sans-serif; font-size: 80%; } div.detail { font-family: Arial,Helvetica,sans-serif; font-size: 80%; } div.credits { font-family: Arial,Helvetica,sans-serif; font-size: 80%; text-align: right; margin: 10px } /* Table attributes */ table.index { background: #ffffff; border: none; border-spacing: 8px; } td.index { border: none; padding: 3px } table.frame { background: #ffffff; border: none } td.frame { border: none; padding: 0px } /* Image attributes */ img.index { border: none; } img.slide { border: none; } img.frame { border: none; } /* Link attributes */ a:link { color: blue; } a:visited { color: green; } a:hover { color: red; } a:active { color: red; } '; # Text $emptycell = "empty"; $updirtext = "up one directory"; $framelinktext = "slideshow view (frames)"; $detaillinktext = "details index"; $indexlinktext = "main index"; $default_titletext = "Image directory"; # These five variables control the TITLE attribute on anchor constructs in the # index and frame views. When TITLE attributes are given they are usually # rendered as "tooltip" bubbles that show text when a cursor hovers and stops # over the active link. We use them here to give a visual cue about the image. # These variables work much like printf(1) strings. # # %f => replaced with the filename of the image # %d => replaced with the date/time of the image (or mtime of the file) # %s => replaced with the size of the file (in Kb) # %r => replaced with the resolution (XxY) of the original image # %c => replaced with the image's caption (if stored with one) # %% => replaced with a literal '%' character # # The following are used when directories are processed and a montage of # that directory is used as the thumbnail of the dir. # # %n => replaced with number of images in a directory # %b => replaced with the "begin" date from a directory of images # %e => replaced with the "end" date from a directory of images # %t => replaced with the "title" from a directory of images # # Other characters (including spaces) are literal. "undef" these in # your ~/.imageindexrc file if you don't want them to show up. The "date/time" # related constructs are interpolated using the date/time format variables # defined below. # $framethumbtitle = "%f - %d"; $indexthumbtitle = "%f (%s)"; $slidethumbtitle = "%f (%s)"; $detailthumbtitle = "%c"; $montagetitle = "%n images %b through %e"; # Date/Time format strings. These strings are formatted much like the above # variables and the definitions of the escape sequences come from the POSIX # strftime(3) definitions. NOT ALL of strftime(3) are supported for obvious # reasons. # # %S is replaced by the second as a decimal number (00-60). # %M is replaced by the minute as a decimal number (00-59). # %I is replaced by the hour (12-hour clock) as a decimal number (01-12). # %H is replaced by the hour (24-hour clock) as a decimal number (00-23). # %p is replaced by national representation of either "ante meridiem" or # "post meridiem" as appropriate (currently only U.S. "am" or "pm") # %R is equivalent to "%H:%M" (in *timeformat variables only). # %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only). # # %Y is replaced by the year with century as a decimal number. # %y is replaced by the year without century as a decimal number (00-99). # %m is replaced by the month as a decimal number (01-12). # %d is replaced by the day of the month as a decimal number (01-31). # %F is equivalent to "%Y-%m-%d" (in *dateformat variables only). # %D is equivalent to "%m/%d/%y" (in *dateformat variables only). # %% is replaced by a literal "%". $framedateformat = "%m/%d/%Y"; $frametimeformat = "%r"; $indexdateformat = "%m/%d/%Y"; $indextimeformat = "%r"; $slidedateformat = "%m/%d/%Y"; $slidetimeformat = "%r"; $detaildateformat = "%m/%d/%Y"; $detailtimeformat = "%I:%M %p"; # Pathnames $indexfile = 'index.html'; $detailfile = 'details.html'; $framefile = 'frame.html'; $slidefile = 'slides.html'; $slide_dir = 'slides'; $stylefile = 'style.css'; $montagefile = 'montage.jpg'; $emoticonprefix = 'ii_'; $emoticonsmile = $emoticonprefix . 'smile.png'; $emoticonwink = $emoticonprefix . 'wink.png'; $emoticonfrown = $emoticonprefix . 'frown.png'; # File exclusion customization (regex) # (Anything non-image and non-dir will be skipped automatically, this just # makes it silent) @exclude = qw( ^CVS$ ^.nautilus-metafile.xml$ ^.thumbnails$ ^.nfs.*$ ^.xvpics$ ^.thumbcache$ ^ALBUM.OFA$ ^desktop.ini$ ^.*.txt$ ); # Metatags $columnsmetatag = 'Columns'; $titlemetatag = 'Title'; $begindatemetatag = 'DateBegin'; $enddatemetatag = 'DateEnd'; $excludemetatag = 'ExcludedFiles'; $skipmetatag = 'SkipMontageFiles'; $numimagesmetatag = 'NumImages'; $reversemetatag = 'Reverse'; $thumbxmetatag = 'ThumbnailX'; $thumbymetatag = 'ThumbnailY'; # Any of the above can be overridden in an rc file in the user's home dir $rcfile = "$ENV{'HOME'}/.imageindexrc"; ###################################################################### # # $Id: imageindex,v 1.175 2007/04/04 19:55:51 edwinh Exp $ # # imageindex is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # imageindex is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with imageindex; see the file COPYING. # ###################################################################### use Image::Magick; # comes with ImageMagick # from CPAN - optional eval('use Image::Info qw(image_info)'); # Shipped with perl use POSIX; use Getopt::Long; use FileHandle; use File::Basename; use File::Copy; use English; use Carp; require 'flush.pl'; # to shut up -w use vars qw($opt_recurse); use vars qw($opt_slide); use vars qw($opt_dirs); use vars qw($opt_detail); use vars qw($opt_lowercase); use vars qw($opt_help); use vars qw($opt_debug); use vars qw($opt_showexcluded); use vars qw($opt_version); use vars qw($opt_updirindexoverride); &GetOptions( 'title=s', 'columns=i', 'x=i', 'y=i', 'forceregen', 'medium!', 'slide!', 'detail!', 'dirs!', 'montage!', 'recurse!', 'destdir=s', 'lowercase', 'caption=s', 'rotate=s', 'exclude=s@', 'skipmont=s@', 'showexcluded', 'includeall', 'version', 'help', 'debug', 'reverse!', 'd=s%', 'updirindexoverride' ) or die ("Invalid flag\n"); # Find out which platform we're on so we don't give incorrect options to needed # commands # $uname = `uname -s`; chomp ($uname); # Override config variables foreach my $var (keys %opt_d) { $value = $opt_d{$var}; print "(override) $var = $value\n"; eval("\$$var=\"$value\""); } &init_png_array(); # Read RC file if (-e $rcfile) { print "Using settings in $rcfile...\n" if ! defined ($opt_version); require $rcfile; } # Rotate or caption image (then exit) if (defined ($opt_rotate)) { &rotate_image($opt_rotate,\@ARGV); exit (0); } elsif (defined ($opt_caption)) { &caption_image($opt_caption,\@ARGV); exit (0); } elsif (defined ($opt_showexcluded)) { &showexcluded($ARGV[0]); exit (0); } elsif (defined ($opt_version)) { printf ("imageindex version: %s\n", &versionstring); exit (0); } # The directory to search is the first argument if (defined($ARGV[0])) { $srcdir = $ARGV[0]; $srcdir =~ s:/$::; } else { $srcdir = "."; } # Give usage message if (defined($opt_help)) { &usage(); exit(0); } # Show backtrace if debug given if (defined($opt_debug)) { $SIG{__WARN__} = \&Carp::cluck; } # Where to generate files $destdir = $srcdir; if (defined($opt_destdir)) { $destdir = $opt_destdir; $destdir =~ s:/$::; print "Exporting to $destdir\n"; unless (-d $destdir) { printf ("Creating destination directory '$destdir'.\n"); mkdir ($destdir, 0755); } } unless (-w $destdir) { printf ("No write permission for $destdir\n"); exit (1); } if (defined($opt_medium)) { $do_medium = $opt_medium } if (defined($opt_slide)) { $do_slide = $opt_slide; } if (defined($opt_detail)) { $do_detail = $opt_detail; } if (defined($opt_dirs)) { $do_dirs = $opt_dirs; } if (defined($opt_montage)) { $do_montage = $opt_montage; } if (defined($opt_recurse)) { $do_recurse = $opt_recurse; } # no montages if we aren't doing dirs anyway if ($do_dirs == 0) { $do_montage = 0; } &initialize_current_vars(); &read_stored_meta_data(); &override_by_commandline(); if (!defined(&image_info)) { print "Image::Info not found, not extracting EXIF data\n"; } opendir(DIR, "$srcdir") || die "Can't open dir $srcdir: ($!)\n"; @files = readdir DIR; closedir(DIR); @files = grep (!/^\.?\.$/, @files); # Skip the files/dirs we use or generate. Any other patterns go in the # config section (@exclude) or in exclude file my @generated_files = ($thumbnail_dir, $med_dir, $slide_dir, $indexfile, $detailfile, $stylefile, ); foreach my $pattern (@generated_files, @exclude) { @files = grep (!/^$pattern$/, @files); } @files = &exclude_files(@files); # Change all the names of image files to lowercase. if (defined ($opt_lowercase)) { &lower_case_files(@files); exit (0); } # Keep track of which column to be in my $col_counter = 1; # Count how many files we create my $object_counter = 0; my $dir_counter = 0; my $image_counter = 0; my $thumbnail_counter = 0; my $med_counter = 0; my $slide_counter = 0; my $modified_thumb = 0; # Keep track of max thumb sizes to use for slide frame width my $max_thumb_x = 0; my $max_thumb_y = 0; # Keep track of max thumb sizes to use for montage creation my $max_mont_thumb_x = 0; my $max_mont_thumb_y = 0; my $mplayer_prog = &find_in_path ('mplayer'); # Extract info print "Extracting image info"; flush (STDOUT); foreach my $file (@files) { # If directory, grab the timestamp if (-d "$srcdir/$file") { my $ts; # Grab timestamp from meta tag if (-e "$srcdir/$file/$indexfile") { my $begin = &extract_meta_tag($begindatemetatag,"$srcdir/$file/$indexfile"); if (defined($begin)) { if (!defined($firstdate) or ($begin < $firstdate)) { $firstdate = $begin; } $ts = $begin; } my $end = &extract_meta_tag($enddatemetatag,"$srcdir/$file/$indexfile"); if (defined($end)) { if (!defined($lastdate) or ($end > $lastdate)) { $lastdate = $end; } $ts = $end if (!defined($ts)); } } # Fallback on dir mtime if (!defined($ts)) { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat("$srcdir/$file"); $ts = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); } push(@{$dir_timestamp{$ts}}, $file); } else { # Collect info from the image or video &extract_file_info($file); } } print "\n"; # Do dirs first if ($do_dirs) { foreach my $ts (sort bynumber keys %dir_timestamp) { foreach my $dir (sort @{$dir_timestamp{$ts}}) { &dir_entry($dir); } # foreach dir that has this timestamp } # foreach timestamp } # Bail if nothing here if ($object_counter == 0) { print "Nothing to do!\n"; unlink("$destdir/$indexfile") if (-e "$destdir/$indexfile"); unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile"); unlink("$destdir/$stylefile") if (-e "$destdir/$stylefile"); exit(0); } # Make thumb dirs if needed foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) { unless (-d "$destdir/$checkdir") { mkdir("$destdir/$checkdir",0777); } } # Nuke old thumbnails if original image gone &nuke_out_of_date(); # Iterate over the files based on timestamp # This is just to get back/forward links undef $prev; foreach (sort bynumber keys %timestamp) { foreach my $pathname (sort @{$timestamp{$_}}) { if (defined($prev)) { my ($name,$path,$suffix); ($name,$path,$suffix) = fileparse($prev,'\.\S+'); $back{$pathname} = "$name.html"; ($name,$path,$suffix) = fileparse($pathname,'\.\S+'); $forward{$prev} = "$name.html"; } $prev = $pathname; } # foreach image that has this timestamp } # foreach timestamp # Iterate over the files based on timestamp # This will do the real work foreach (sort bynumber keys %timestamp) { foreach my $pathname (sort @{$timestamp{$_}}) { my $filename = $info{$pathname}{'file'}; my $thumbnail = $info{$pathname}{'thumb'}; my $medium = $info{$pathname}{'medium'}; my $slide = $info{$pathname}{'slide'}; my $tmp_jpg_dir = "tmp_jpg_$$"; if (!defined($firstdate) or ($info{$pathname}{'date'} < $firstdate)) { $firstdate = $info{$pathname}{'date'}; } if (!defined($lastdate) or ($info{$pathname}{'date'} > $lastdate)) { $lastdate = $info{$pathname}{'date'}; } # # First, deal with medium format of the image since we can save time shrinking # the medium down to the thumbnail rather than fullsize->thumbnail # # If the file is a video, we must first extract the first frame of the video # stream into JPEG form and we may or may not maniplate it from there. # # Skip if we want no medium images at all # if ($do_medium == 0) { $skipmedium{$pathname} = 1; unlink("$destdir/$medium") if (-e "$destdir/$medium"); } elsif (($info{$pathname}{'x'} <= ($med_x * $med_threshold)) and ($info{$pathname}{'y'} <= ($med_y * $med_threshold))) { my $image = new Image::Magick; my $retval; # Regardless of whether the "size" of a frame of a video is # close to our medium threshold size, we need to create an # image and have it there. If it is an image then the HTML will # just link to the actual image rather than creating a 'medium' # version. But for video files we need to make one regardless. # if ($info{$pathname}{'is_video'}) { if ((! -e "$destdir/$medium") or ( -M $pathname < -M "$destdir/$medium") or defined($opt_forceregen)) { my $icon = new Image::Magick; print "Creating $destdir/$medium from first frame of $pathname\n"; my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog); $retval = $image->Read(filename=>$tmpfile); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; # always want 100% here instead of $med_quality $retval = $image->Set(quality=>100); warn "$retval" if "$retval"; # If the user wants to, overlay a small icon on top of the # thumbnail of the video file to give a visual cue that this # file is a video and not a still photo # if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) { my $iconfile; if ($video_icon_gravity ne 'SouthWest' && $video_icon_gravity ne 'NorthWest' && $video_icon_gravity ne 'SouthEast' && $video_icon_gravity ne 'NorthEast') { printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n"); $video_icon_gravity = 'SouthWest'; } &write_video_icons ($tmp_jpg_dir); if ($video_icon < 1 || $video_icon > $max_video_icons) { printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n"); $video_icon = 1; } $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png'; $retval = $icon->Read(filename=>$iconfile); warn "$retval" if "$retval"; $image->Composite(image=>$icon,gravity=>$video_icon_gravity); } $retval = $image->Write(filename=>"$destdir/$medium"); warn "$retval" if "$retval"; &delete_tmp_jpg_dir($tmp_jpg_dir); } else { # Up to date, existing medium # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of # the file here and the code below will set the thumb_x/y properties. # $retval = $image->Read("$destdir/$medium"); warn "$retval" if "$retval"; } $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'med_x'} = $image->Get('width'); $info{$pathname}{'med_y'} = $image->Get('height'); $med_counter++; } else { # Skip if we are below the threshold size $skipmedium{$pathname} = 1; unlink("$destdir/$medium") if (-e "$destdir/$medium"); } } else { my $image = new Image::Magick; my $retval; # The size of the file was not within the "threshold" so we need to create a # medium version if ($info{$pathname}{'is_video'}) { # Create a medium version out of the first frame of the video file and then # resize it according to $med_x and $med_y # if ((! -e "$destdir/$medium") or ( -M $pathname < -M "$destdir/$medium") or defined($opt_forceregen)) { my $icon = new Image::Magick; my $newgeom = $med_x . "x" . $med_y; print "Creating $destdir/$medium from first frame of $pathname\n"; my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog); $retval = $image->Read(filename=>$tmpfile); warn "$retval" if "$retval"; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; # always want 100% here instead of $med_quality $retval = $image->Set(quality=>100); warn "$retval" if "$retval"; # If the user wants to, overlay a small icon on top of the # thumbnail of the video file to give a visual cue that this # file is a video and not a still photo # if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) { my $iconfile; if ($video_icon_gravity ne 'SouthWest' && $video_icon_gravity ne 'NorthWest' && $video_icon_gravity ne 'SouthEast' && $video_icon_gravity ne 'NorthEast') { printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n"); $video_icon_gravity = 'SouthWest'; } &write_video_icons ($tmp_jpg_dir); if ($video_icon < 1 || $video_icon > $max_video_icons) { printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n"); $video_icon = 1; } $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png'; $retval = $icon->Read(filename=>$iconfile); warn "$retval" if "$retval"; $image->Composite(image=>$icon,gravity=>$video_icon_gravity); } $retval = $image->Write(filename=>"$destdir/$medium"); warn "$retval" if "$retval"; &delete_tmp_jpg_dir($tmp_jpg_dir); } else { # Up to date, existing medium # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of # the file here and the code below will set the thumb_x/y properties. # $retval = $image->Read("$destdir/$medium"); warn "$retval" if "$retval"; } $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'med_x'} = $image->Get('width'); $info{$pathname}{'med_y'} = $image->Get('height'); $med_counter++; } else { my $image = new Image::Magick; my $retval; # Create medium sized pic if it is not there, # or is out of date with respect to original image if ((! -e "$destdir/$medium") or ( -M $pathname < -M "$destdir/$medium") or defined($opt_forceregen)) { my $newgeom = $med_x . "x" . $med_y; print "Creating $destdir/$medium\n"; $retval = $image->Read(filename=>$pathname); warn "$retval" if "$retval"; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; $retval = $image->Set(quality=>$med_quality); warn "$retval" if "$retval"; if ($info{$pathname}{'is_multi_image_file'}) { $retval = $image->[0]->Write(filename=>"$destdir/$medium"); warn "$retval" if "$retval"; } else { $retval = $image->Write(filename=>"$destdir/$medium"); warn "$retval" if "$retval"; } $image_cache{$pathname} = $image; } else { # Up to date, existing medium, grab dimensions # Get the right hsize/vsize tags for the medium slides. Simply do a "Read" of # the file here and the code below will set the med_x/y properties. # $retval = $image->Read("$destdir/$medium"); warn "$retval" if "$retval"; } $info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'med_x'} = $image->Get('width'); $info{$pathname}{'med_y'} = $image->Get('height'); $med_counter++; } } # # Next, deal with the thumbnail for this image. If we have just created a medium # version of the image, then an open image "handle" will exist for it. We simply # shrink that down to thumbnail size (if appropriate) rather than reading in the # original file again just to shrink it (saves processing time). # # Skip thumb if we are below the threshold size if (($info{$pathname}{'x'} <= ($current_thumbnail_x * $thumbnail_threshold)) and ($info{$pathname}{'y'} <= ($current_thumbnail_y * $thumbnail_threshold))) { my $image = new Image::Magick; my $retval; if ($info{$pathname}{'is_video'}) { # If the file is a video but the X/Y is smaller than that of our # thumbnail size we still need to make a JPG out of the first # frame and store it in the thumbnail directory. This code is # mostly like the code below for creating video thumbnails but # we're just not resizing the first frame down to thumbnail size. # my $icon = new Image::Magick; # We need to make a thumbnail from this video file. Get the first frame print "Creating $destdir/$thumbnail from first frame of $pathname\n"; my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog); $retval = $image->Read(filename=>$tmpfile); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; $retval = $image->Set(quality=>$thumb_quality); warn "$retval" if "$retval"; # If the user wants to, overlay a small icon on top of the # thumbnail of the video file to give a visual cue that this # file is a video and not a still photo # if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) { my $iconfile; if ($video_icon_gravity ne 'SouthWest' && $video_icon_gravity ne 'NorthWest' && $video_icon_gravity ne 'SouthEast' && $video_icon_gravity ne 'NorthEast') { printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n"); $video_icon_gravity = 'SouthWest'; } &write_video_icons ($tmp_jpg_dir); if ($video_icon < 1 || $video_icon > $max_video_icons) { printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n"); $video_icon = 1; } $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png'; $retval = $icon->Read(filename=>$iconfile); warn "$retval" if "$retval"; $image->Composite(image=>$icon,gravity=>$video_icon_gravity); } $retval = $image->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; push(@montagefiles,"$destdir/$thumbnail"); $modified_thumb++; $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'thumb_x'} = $image->Get('width'); $info{$pathname}{'thumb_y'} = $image->Get('height'); $thumbnail_counter++; &delete_tmp_jpg_dir($tmp_jpg_dir); } else { # is NOT a video file $info{$pathname}{'thumb_x'} = $info{$pathname}{'x'}; $info{$pathname}{'thumb_y'} = $info{$pathname}{'y'}; $skipthumb{$pathname} = 1; if (-e "$destdir/$thumbnail") { unlink("$destdir/$thumbnail"); $modified_thumb++; } push(@montagefiles,"$destdir/$filename"); } } else { my $image = new Image::Magick; my $retval; if ($info{$pathname}{'is_video'}) { my $icon = new Image::Magick; # We need to make a thumbnail from this video file. Get the first frame and # resize it down to the appropriate size if ((! -e "$destdir/$thumbnail") or ( -M $pathname < -M "$destdir/$thumbnail") or defined($opt_forceregen)) { my $newgeom = $current_thumbnail_x . "x" . $current_thumbnail_y; print "Creating $destdir/$thumbnail from first frame of $pathname\n"; my $tmpfile = &extract_first_frame_jpg ($pathname, $tmp_jpg_dir, $mplayer_prog); $retval = $image->Read(filename=>$tmpfile); warn "$retval" if "$retval"; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; # Always want 100% here instead of $thumb_quality $retval = $image->Set(quality=>100); warn "$retval" if "$retval"; # If the user wants to, overlay a small icon on top of the thumbnail # of the video file to give a visual cue that this file is a video and # not a still photo # if (defined ($do_video_thumbnail_icons) && $do_video_thumbnail_icons) { my $iconfile; if ($video_icon_gravity ne 'SouthWest' && $video_icon_gravity ne 'NorthWest' && $video_icon_gravity ne 'SouthEast' && $video_icon_gravity ne 'NorthEast') { printf (STDERR "WARNING: \$video_icon_gravity set to unknown value. Assuming 'SouthWest'\n"); $video_icon_gravity = 'SouthWest'; } &write_video_icons ($tmp_jpg_dir); if ($video_icon < 1 || $video_icon > $max_video_icons) { printf (STDERR "WARNING: \$video_icon set to unknown value. Assuming '1'\n"); $video_icon = 1; } $iconfile = $tmp_jpg_dir . '/video_icon' . $video_icon . '.png'; $retval = $icon->Read(filename=>$iconfile); warn "$retval" if "$retval"; $image->Composite(image=>$icon,gravity=>$video_icon_gravity); } $retval = $image->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; push(@montagefiles,"$destdir/$thumbnail"); $modified_thumb++; &delete_tmp_jpg_dir($tmp_jpg_dir); } else { # Up to date, existing thumb # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of # the file here and the code below will set the thumb_x/y properties. # $retval = $image->Read("$destdir/$thumbnail"); warn "$retval" if "$retval"; push(@montagefiles,"$destdir/$thumbnail"); } $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'thumb_x'} = $image->Get('width'); $info{$pathname}{'thumb_y'} = $image->Get('height'); $thumbnail_counter++; } else { # Is NOT a video file my $image = new Image::Magick; my $retval; # Create thumbnail if it is not there, # or is out of date with respect to original image if ((! -e "$destdir/$thumbnail") or ( -M $pathname < -M "$destdir/$thumbnail") or defined($opt_forceregen)) { my $newgeom = $current_thumbnail_x . "x" . $current_thumbnail_y; print "Creating $destdir/$thumbnail\n"; if (defined ($image_cache{$pathname})) { $image = $image_cache{$pathname}; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(quality=>$thumb_quality); warn "$retval" if "$retval"; if ($info{$pathname}{'is_multi_image_file'}) { $retval = $image->[0]->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; } else { $retval = $image->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; } } else { $retval = $image->Read(filename=>$pathname); warn "$retval" if "$retval"; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; $retval = $image->Set(quality=>$thumb_quality); warn "$retval" if "$retval"; if ($info{$pathname}{'is_multi_image_file'}) { $retval = $image->[0]->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; } else { $retval = $image->Write(filename=>"$destdir/$thumbnail"); warn "$retval" if "$retval"; } } push(@montagefiles,"$destdir/$thumbnail"); $modified_thumb++; } else { # Up to date, existing thumb # Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of # the file here and the code below will set the thumb_x/y properties. # $retval = $image->Read("$destdir/$thumbnail"); warn "$retval" if "$retval"; push(@montagefiles,"$destdir/$thumbnail"); } $info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'thumb_x'} = $image->Get('width'); $info{$pathname}{'thumb_y'} = $image->Get('height'); $thumbnail_counter++; } } # Set the max thumb sizes, to be used for slide frame width if ($info{$pathname}{'thumb_x'} > $max_thumb_x) { $max_thumb_x = $info{$pathname}{'thumb_x'}; } if ($info{$pathname}{'thumb_y'} > $max_thumb_y) { $max_thumb_y = $info{$pathname}{'thumb_y'}; } # Set the max montage thumb sizes, to be used when creating montage images # $bn = basename ($thumbnail); unless (defined ($skipmont{$bn})) { if ($info{$pathname}{'thumb_x'} > $max_mont_thumb_x) { $max_mont_thumb_x = $info{$pathname}{'thumb_x'}; } if ($info{$pathname}{'thumb_y'} > $max_mont_thumb_y) { $max_mont_thumb_y = $info{$pathname}{'thumb_y'}; } } # # Finally, create html for this image # &image_entry($pathname); } # foreach image that has this timestamp } # foreach timestamp # Finish up the columns if needed if (($col_counter != 1) and ($col_counter <= $current_columns) and ($object_counter > $current_columns)) { foreach ($col_counter..$current_columns) { push(@index, " $emptycell\n"); push(@details, " $emptycell\n"); } push(@index, " \n"); push(@details, " \n"); } # Nuke generated dirs if no contents system("rm -rf $destdir/$thumbnail_dir") if ($thumbnail_counter == 0); system("rm -rf $destdir/$slide_dir") if ($slide_counter == 0); system("rm -rf $destdir/$med_dir") if ($med_counter == 0); # Create montage if we had more than just dir entries here if (($dir_counter != $object_counter)) { &create_montage(@montagefiles); } # Create stylesheet &write_css(); # Write index web page open(INDEX,">$destdir/$indexfile") or die ("Can't open $destdir/$indexfile: $!\n"); &page_header('index', $index_linkto); foreach (@index) { print INDEX; } &page_footer('index'); close(INDEX); # Write photo details file if ($do_detail == 1) { open(INDEX,">$destdir/$detailfile") or die ("Can't open $destdir/$indexfile: $!\n"); &page_header('detail', $details_linkto); foreach (@details) { print INDEX; } &page_footer('detail'); close(INDEX); } else { unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile"); } # Write slide/frame files if (($do_slide == 1) and ($slide_counter > 1)) { &write_frameset(); } else { system("rm -rf $destdir/$slide_dir") if (-d "$destdir/$slide_dir"); } # Optionally export images somewhere else if ($opt_destdir) { printf ("Copying image files from '$srcdir' to '$destdir'.\n"); foreach my $image (keys %info) { # BSD's default 'cp' cannot preserve links like GNU fileutils cp can # if ($uname =~ /BSD/) { system("cp -pv $image $destdir"); } else { system("cp -dpuv $image $destdir"); } } } if (defined ($do_emoticons) && $do_emoticons) { foreach $icon ('wink', 'smile', 'frown') { if ($emoticon{$icon}) { &write_emoticon_png ($icon); } else { unlink ($destdir . '/' . $thumbnail_dir . "/$emoticonprefix${icon}.png"); } } } ###################################################################### # # Write the various HTML parts for this image # ###################################################################### sub image_entry { my $pathname = shift(@_); my $filename = $info{$pathname}{'file'}; my $link; &index_html($pathname); if ($do_detail == 1) { &details_html($pathname); } if (($do_slide == 1) and ($image_counter > 1)) { &slide_html($pathname); } else { my $file = $info{$pathname}{slide}; unlink($file) if (-e $file); } # Increment for next time $col_counter++; $col_counter = 1 if ($col_counter > $current_columns); } ############################################################################### # # Generate HTML for index page entry # ############################################################################### sub index_html { my $pathname = shift(@_); my $filename = $info{$pathname}{'file'}; my $link; my $anchortext; # At beginning of row? if ($col_counter == 1) { push(@index, " \n"); } # Image push(@index, " \n"); push(@index, "
"); push(@index, &format_date($info{$pathname}{'date'}, 'index')); push(@index,"
\n"); if (($index_linkto eq 'details') and ($do_detail == 1)) { $link = "$detailfile#$filename"; } elsif (($index_linkto eq 'medium') and !defined($skipmedium{$pathname})) { $link = $info{$pathname}{'medium'}; } elsif (($index_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { $link = $info{$pathname}{'thumb'}; } elsif (($index_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) { $link = $info{$pathname}{'slide'}; } else { $link = $filename; } $anchortext = " "; push(@index, $anchortext); if (defined($skipthumb{$pathname})) { push(@index,"\"\n"); push(@index, "
"); # Full size link push(@index,"full size"); # Medium size link if within the threshold unless (defined($skipmedium{$pathname})) { push(@index," | medium"); } # Detail list link if ($do_detail == 1) { push(@index," | details"); } push(@index,"
\n"); # Caption if any (jpeg comment field) if (($do_captions == 1) and defined($info{$pathname}{'comment'})) { my ($tmp); push(@index, "
"); # Hack: if a comment has an ellipsis at the very end, make the HTML use a # non-breakable space before it so that the ellipsis doesn't "wrap" inside # the table field. It just looks better for those cases where the comment # is just long enough to wrap when rendered in the space given # $tmp = $info{$pathname}{'comment'}; $tmp = &htmlize_caption ($tmp); if ($tmp =~ /(\s+)\.\.\.\s*$/) { $tmp =~ s/(\s+)\.\.\.\s*$/ .../; } push(@index, $tmp); push(@index,"
\n"); } push(@index, " \n\n"); # At end of row? if ($col_counter == $current_columns) { push(@index, " \n"); } } ############################################################################### # # Generate HTML for slide/frame pages # ############################################################################### sub slide_html { my $pathname = shift(@_); my $filename = $info{$pathname}{'file'}; my $link; my $anchortext; # # First the index frame info # if ($frame_orient eq 'horizontal') { push(@frame," \n"); } else { push(@frame," \n \n"); } $anchortext = " "; push(@frame, $anchortext); if (defined($skipthumb{$pathname})) { push(@frame,"\"\n"); if ($frame_orient eq 'horizontal') { push(@frame," "); } else { push(@frame," \n "); } push(@frame,"\n"); # # Then the individual slides # my $slide = new FileHandle "> $destdir/$info{$pathname}{slide}"; if (!defined($slide)) { die("$destdir/$info{$pathname}{slide}: $!"); } select($slide); print "\n"; print "\n"; print "\n"; $verstring = &versionstring(); printf ("\n", $verstring); printf ("\n"); print "$current_titletext - $filename\n"; print "\n"; print "\n\n"; &next_prev_links($pathname); # Caption if any if (($do_captions == 1) and ($slide_caption eq 'top') and defined($info{$pathname}{'comment'})) { print "
"; my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide'); print $tmp; print "
\n"; } # Date, filename if ($slide_date eq 'top') { print "
"; print &format_date($info{$pathname}{'date'}, 'slide'); print " $filename"; print "
\n"; } if ($slide_linkto eq 'index') { $link = "../$indexfile#$filename"; } elsif (($slide_linkto eq 'details') and ($do_detail == 1)) { $link = "../$detailfile#$filename"; } elsif (($slide_linkto eq 'medium') and !defined($skipmedium{$pathname})) { $link = "../$info{$pathname}{medium}"; } elsif (($slide_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { $link = "../$info{$pathname}{thumb}"; } else { $link = "../$filename"; } print "\n

\n"; $anchortext = ""; print "\n"; print "

\n"; # Caption if any if (($do_captions == 1) and ($slide_caption eq 'bottom') and defined($info{$pathname}{'comment'})) { print "
"; my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide'); print $tmp; print "
\n"; } # Date, filename if ($slide_date eq 'bottom') { print "
"; print &format_date($info{$pathname}{'date'}, 'slide'); print " $filename"; print "
\n"; } &next_prev_links($pathname); print "\n\n"; select(STDOUT); $slide->close(); $slide_counter++; unless(defined($first_slide)) { $first_slide = $info{$pathname}{'slide'}; } } ############################################################################### # # Generate HTML for details page # ############################################################################### sub details_html { my $pathname = shift(@_); my $filename = $info{$pathname}{'file'}; my ($link, $anchortext); # At beginning of row? if ($col_counter == 1) { push(@details, " \n"); } if ($details_linkto eq 'index') { $link = "$indexfile#$filename"; } elsif (($details_linkto eq 'medium') and !defined($skipmedium{$pathname})) { $link = "$info{$pathname}{medium}"; } elsif (($details_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { $link = "$info{$pathname}{thumb}"; } elsif (($details_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) { $link = $info{$pathname}{'slide'}; } else { $link = $filename; } push(@details," \n"); push(@details," \n"); push(@details," \n"); push(@details," \n\n"); push(@details," \n"); push(@details," \n"); push(@details,"
\n"); push(@details,"
\n"); push(@details," "); push(@details, &format_date($info{$pathname}{'date'}, 'detail')); push(@details,"
\n"); $anchortext = "
"); push(@details,"$filename
"); push(@details,"
\n"); push(@details,"
\n"); push(@details,"
"); push(@details,"Original: $info{$pathname}{geometry}"); push(@details," ($info{$pathname}{size})
"); unless (defined($skipmedium{$pathname})) { push(@details,"Medium: "); push(@details,$info{$pathname}{'med_x'} . 'x' . $info{$pathname}{'med_y'} . ""); push(@details," ($info{$pathname}{med_size})
"); } unless (defined($skipthumb{$pathname})) { push(@details,"Thumbnail: "); push(@details,$info{$pathname}{'thumb_x'} . 'x' . $info{$pathname}{'thumb_y'} . ""); push(@details," ($info{$pathname}{thumb_size})
"); } if (defined ($info{$pathname}{'is_multi_image_file'}) && $info{$pathname}{'is_multi_image_file'} == 1) { push(@details,"Multi-image File: $info{$pathname}{scenes} scenes
"); } # Video file stuff # if (defined ($info{$pathname}{'video_format'})) { push (@details, "Video Format: $info{$pathname}{'video_format'}
"); } if (defined ($info{$pathname}{'video_bitrate'})) { push (@details, "Video Bitrate: $info{$pathname}{'video_bitrate'}
"); } if (defined ($info{$pathname}{'video_fps'})) { push (@details, "Video Rate: $info{$pathname}{'video_fps'} f/s
"); } if (defined ($info{$pathname}{'audio_codec'})) { push (@details, "Audio Codec: $info{$pathname}{'audio_codec'}
"); } if (defined ($info{$pathname}{'audio_bitrate'})) { push (@details, "Audio Bitrate: $info{$pathname}{'audio_bitrate'} kbit/s
"); } if (defined ($info{$pathname}{'length'})) { push (@details, "Length (time): $info{$pathname}{'length'} sec
"); } # # EXIF data # if (defined($info{$pathname}{'flash'})) { push(@details,"Flash: $info{$pathname}{flash}
"); } if (defined($info{$pathname}{'exposure_time'})) { push(@details,"Exposure time: $info{$pathname}{exposure_time}
"); } if (defined($info{$pathname}{'focus_dist'})) { push(@details,"Focus distance: $info{$pathname}{focus_dist}
"); } if (defined($info{$pathname}{'focal_length'})) { push(@details,"Focal length: $info{$pathname}{focal_length}
"); } if (defined($info{$pathname}{'aperture'})) { push(@details,"Aperture: $info{$pathname}{aperture}
"); } push(@details,"\n"); push(@details,"
\n"); push(@details,"
\n"); push(@details," \n"); # At end of row? if ($col_counter == $current_columns) { push(@details, " \n"); } } sub delete_tmp_jpg_dir { my ($tmp_jpg_dir) = @_; while ($name = <$tmp_jpg_dir/*>) { unlink ($name); } rmdir $tmp_jpg_dir; } sub extract_first_frame_jpg { my ($filename, $tmp_jpg_dir, $mplayer_prog) = @_; my ($retval, $cmd); # Need to give 2 frames here. On some MPG files mplayer produces no output when # you request 1 frame. $cmd = "$mplayer_prog $filename -noautosub -nosound -vo jpeg:outdir=${tmp_jpg_dir}:quality=100 -frames 2 > /dev/null 2>&1"; print "About to execute: '$cmd'\n" if $opt_debug; $retval = system ($cmd); if ($retval) { printf ("warning: mplayer returned %d\n", $? >> 8); } else { return "${tmp_jpg_dir}/00000001.jpg"; } } ###################################################################### # # Extract info from a given file # ###################################################################### sub extract_file_info { my $filename = shift (@_); my $pathname = "$srcdir/$filename"; my $retval; if ($filename =~ /\.${video_regexp}$/i) { $retval = &extract_movie_info ($filename); # mplayer told us that this wasn't a movie file so at least yell # at the user so that the video regexp might be adjusted if ($retval == -1) { print "\nwarning: $pathname identified by extension as video file but mplayer doesn't recognize it\n"; flush (STDOUT); } } else { &extract_image_info ($filename); } } ###################################################################### # # Extract info from movie file # ###################################################################### sub extract_movie_info { my $filename = shift (@_); my $pathname = "$srcdir/$filename"; my ($retval, $cmd, $qt_found, $tmp); my $mplayer_prog = &find_in_path ('mplayer'); my ($format, $bitrate, $x, $y, $fps, $aspect, $acodec, $abitrate); my ($arate, $anch, $length, $is_video); print "."; flush (STDOUT); if ($mplayer_prog eq '' || ($do_video_files == 0)) { if (($do_video_files != 0) && $mplayer_prog eq '') { print "\nwarning: Trying to process video files but cannot find mplayer in \$path!\n"; flush (STDOUT); } print "\nSkipping $pathname"; flush (STDOUT); return 0; } else { $object_counter++; $image_counter++; } $cmd = "$mplayer_prog -noautosub -vo null -ao null -frames 0 -identify $pathname |"; if (! open (PIPE, $cmd)) { printf (STDERR "Could not open pipe from mplayer! - $!\n"); return 0; } $is_video = 0; $qt_found = 0; while () { if (/VIDEO:\s+(\S+)/) { $format = $1; $format =~ s/\[//g; $format =~ s/\]//g; } if (/QuickTime.*detected/) { $qt_found = 1; } if (/ID_VIDEO_FORMAT=(\S+)/) { $tmp = $1; if ($format eq 'jpeg' && $qt_found) { $tmp = 'QuickTime'; } if ($tmp =~ /0x/) { $tmp = $format; } $format = $tmp; $is_video = 1; } if (/ID_VIDEO_BITRATE=(\S+)/) { $tmp = $1; if ($tmp == 0) { $tmp = 'VBR'; } $bitrate = $tmp; } if (/ID_VIDEO_WIDTH=(\S+)/) { $x = $1; } if (/ID_VIDEO_HEIGHT=(\S+)/) { $y = $1; } if (/ID_VIDEO_FPS=(\S+)/) { $fps = $1; } if (/ID_VIDEO_ASPECT=(\S+)/) { $aspect = $1; } if (/ID_AUDIO_CODEC=(\S+)/) { $acodec = $1; } if (/ID_AUDIO_BITRATE=(\S+)/) { $tmp = $1; if ($tmp == 0) { $tmp = 'VBR'; } $abitrate = $tmp; } if (/ID_AUDIO_RATE=(\S+)/) { $arate = $1; } if (/ID_AUDIO_NCH=(\S+)/) { $anch = $1; } if (/ID_LENGTH=(\S+)/) { $length = $1; } } close (PIPE); if ($is_video) { $info{$pathname}{'is_video'} = 1; $info{$pathname}{'file'} = $filename; $info{$pathname}{'video_format'} = $format; $info{$pathname}{'video_bitrate'} = $bitrate; $info{$pathname}{'x'} = $x; $info{$pathname}{'y'} = $y; $info{$pathname}{'video_fps'} = $fps; $info{$pathname}{'video_aspect'} = $aspect; $info{$pathname}{'audio_codec'} = $acodec; $info{$pathname}{'audio_bitrate'} = $abitrate / 1000.0; $info{$pathname}{'audio_rate'} = $arate; $info{$pathname}{'audio_nch'} = $anch; $info{$pathname}{'length'} = $length; my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($pathname); $info{$pathname}{'date'} = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); $info{$pathname}{'size'} = &convert_to_kb($size); $info{$pathname}{'geometry'} = $info{$pathname}{'x'} . "x" . $info{$pathname}{'y'}; $info{$pathname}{'format'} = $info{$pathname}{'video_format'}; my ($name,$path,$suffix) = fileparse($filename,'\.\S+'); if (-e "${name}.txt") { my $text; if (! open (IN, "${name}.txt")) { warn "Cannot open ${name}.txt for reading - $!\n"; } else { $text = ; $info{$pathname}{'comment'} = $text; close (IN); } } $info{$pathname}{'thumb'} = "$thumbnail_dir/$name.jpg"; $thumb_backref{"$thumbnail_dir/$name.jpg"} = $pathname; $info{$pathname}{'medium'} = "$med_dir/$name.jpg"; $med_backref{"$med_dir/$name.jpg"} = $pathname; $info{$pathname}{'slide'} = "$slide_dir/$name.html"; $slide_backref{"$slide_dir/$name.html"} = $pathname; push(@{$timestamp{"$info{$pathname}{date}"}}, $pathname); return (0); } else { return (1); } } ###################################################################### # # Extract info from image # ###################################################################### sub extract_image_info { my $filename = shift (@_); my $pathname = "$srcdir/$filename"; my $image = new Image::Magick; my $retval; my $i; print "."; flush (STDOUT); $retval = $image->Read($pathname); if ($retval ne "") { print "\nSkipping $pathname"; flush (STDOUT); return; } else { $object_counter++; $image_counter++; } # iterate over the number of scenes in this image (if the file format # supports it). "normal" files will only have $image->[0] defined. If there # is a better way to do this, I'm all ears--even the C code embedded within # ImageMagick itself iterates like this--there doesn't seem to be an # 'attribute' that can be easily fetched. # for ($i = 0; defined $image->[$i]; $i++) { # empty } if ($i > 1) { $info{$pathname}{'is_multi_image_file'} = 1; $info{$pathname}{'scenes'} = $i; } else { $info{$pathname}{'is_multi_image_file'} = 0; $info{$pathname}{'scenes'} = 1; } $info{$pathname}{'file'} = $filename; # Use mtime as a fallback date in case we don't have exif data my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($pathname); $info{$pathname}{'date'} = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); $info{$pathname}{'x'} = $image->Get('width'); $info{$pathname}{'y'} = $image->Get('height'); $info{$pathname}{'geometry'} = $info{$pathname}{'x'} . "x" . $info{$pathname}{'y'}; $info{$pathname}{'size'} = &convert_to_kb($image->Get('filesize')); $info{$pathname}{'format'} = $image->Get('format'); $info{$pathname}{'comment'} = $image->Get('comment'); my ($name,$path,$suffix) = fileparse($filename,'\.\S+'); if ($info{$pathname}{'format'} =~ /JFIF/i) { $info{$pathname}{'thumb'} = "$thumbnail_dir/$filename"; $thumb_backref{"$thumbnail_dir/$filename"} = $pathname; $info{$pathname}{'medium'} = "$med_dir/$filename"; $med_backref{"$med_dir/$filename"} = $pathname; if (defined(&image_info)) { my $exif = image_info("$pathname"); if (my $error = $exif->{error}) { warn "Can't parse image info: $error\n"; } if (defined($opt_debug)) { print "EXIF data for $pathname:\n"; foreach (keys %$exif) { print " $_ = $exif->{$_}\n"; } print "\n"; } if (defined($exif->{DateTimeOriginal})) { # some models use / instead of : in this field... (need to check spec) $exif->{DateTimeOriginal} =~ s/\//:/g; $exif->{DateTimeOriginal} =~ /\s*([\d:]+)\s+([\d:]+)/; my $dt = $1; my $tm = $2; $tm =~ s/://; $tm =~ s/:/\./; $dt =~ s/://g; $info{$pathname}{'date'} = $dt . $tm; } if (defined($exif->{Flash})) { $info{$pathname}{'flash'} = $exif->{'Flash'}; $info{$pathname}{'flash'} =~ s/0/no/; $info{$pathname}{'flash'} =~ s/1/yes/; } if (defined($exif->{FocalLength})) { $info{$pathname}{'focal_length'} = sprintf("%4.1fmm", eval("$exif->{FocalLength}")); } if (defined($exif->{SubjectDistance})) { $info{$pathname}{'focus_dist'} = sprintf("%4.1fm", eval("$exif->{SubjectDistance}")); } if (defined($exif->{ExposureTime})) { $info{$pathname}{'exposure_time'} = $exif->{ExposureTime} . 's'; } if (defined($exif->{FNumber})) { $info{$pathname}{'aperture'} = "f/" . eval ("$exif->{FNumber}"); } } } else { $info{$pathname}{'thumb'} = "$thumbnail_dir/$name.jpg"; $thumb_backref{"$thumbnail_dir/$name.jpg"} = $pathname; $info{$pathname}{'medium'} = "$med_dir/$name.jpg"; $med_backref{"$med_dir/$name.jpg"} = $pathname; } $info{$pathname}{'slide'} = "$slide_dir/$name.html"; $slide_backref{"$slide_dir/$name.html"} = $pathname; push(@{$timestamp{"$info{$pathname}{date}"}}, $pathname); } ###################################################################### # # Write HTML for directory entries # ###################################################################### sub dir_entry { my $dir = shift(@_); my $destdirname = "$destdir/$dir"; my $srcdirname = "$srcdir/$dir"; my $anchortext; print "Processing directory $srcdirname\n"; # Recurse first if ($do_recurse == 1) { my $flags = ""; $flags .= "-medium " if ($do_medium == 1); $flags .= "-nomedium " if ($do_medium == 0); $flags .= "-slide " if ($do_slide == 1); $flags .= "-noslide " if ($do_slide == 0); $flags .= "-dirs " if ($do_dirs == 1); $flags .= "-nodirs " if ($do_dirs == 0); $flags .= "-montage " if ($do_montage == 1); $flags .= "-nomontage " if ($do_montage == 0); $flags .= "-detail " if ($do_detail == 1); $flags .= "-nodetail " if ($do_detail == 0); $flags .= "-reverse " if ($do_reverse == 1); $flags .= "-noreverse " if ($do_reverse == 0); $flags .= "-forceregen " if (defined($opt_forceregen)); $flags .= "-includeall " if (defined($opt_includeall)); $flags .= "-columns $current_columns " if (defined($opt_columns)); $flags .= "-x $opt_x " if (defined($opt_x)); $flags .= "-y $opt_y " if (defined($opt_y)); $flags .= "-destdir $destdirname " if ($destdir ne $srcdir); foreach my $var (keys %opt_d) { $flags .= " -d $var=$opt_d{$var}"; } # If we're doing recursion and $updirtext is set either by default or # within a .imageindexrc file, then we're going to employ a terrible # hack here. We know that once this recursive call to ourselves returns # we're going to create an HTML page for the directory we're in now. # However, the recursive call will not "see" that file created and thus # the link isn't made back to "../$indexfile". We will tell # the recursive call to ourselves that it needs to think there's # a ../$indexfile file there regardless of whether it's there or not # if (defined $updirtext) { $flags .= "-updirindexoverride "; } system("cd \"$srcdirname\" ;$0 $flags -recurse"); } my $dirtitle = ""; my $first; my $last; my $montage; my $montage_x; my $montage_y; # Only add entry if this dir has an index file if (-r "$destdirname/$indexfile") { # Go fetch the title and dates from the HTML my $tmp1 = &extract_meta_tag ($titlemetatag,"$destdirname/$indexfile"); my $tmp2 = &extract_meta_tag ($begindatemetatag,"$destdirname/$indexfile"); my $tmp3 = &extract_meta_tag ($enddatemetatag,"$destdirname/$indexfile"); if (defined($tmp1)) { $dirtitle = $tmp1; } if (defined($tmp2)) { $first = $tmp2; } if (defined($tmp3)) { $last = $tmp3; } # If we found generated files in this dir, flag that we found something # valid to index $object_counter++; $dir_counter++; # Set montage file if we found it if (($do_montage == 1) and ( -r "$destdirname/$thumbnail_dir/$montagefile")) { print "Found montage in $destdirname\n" if defined($opt_debug); $montage = "$destdirname/$thumbnail_dir/$montagefile"; my $image = new Image::Magick; my $retval; $retval = $image->Read(filename=>$montage); warn "$retval" if "$retval"; $montage_x = $image->Get('width'); $montage_y = $image->Get('height'); } # At beginning of row? if ($col_counter == 1) { push(@index, "\n"); push(@details, "\n"); } # Entry for this directory in main & details file push(@index, "\n"); push(@details, "\n"); push(@details, "\n"); if (defined($montage)) { push(@details, "
\n"); } else { push(@details, "
\n"); } if (defined($first)) { my ($tmp_first, $tmp_last); push(@index, "
"); push(@details, "
"); $tmp_first = &format_date ($first, 'index', 'dayonly'); $tmp_last = &format_date ($last, 'index', 'dayonly'); if ($first ne $last) { push(@index, "$tmp_first - $tmp_last"); } else { push(@index, "$tmp_first"); } $tmp_first = &format_date ($first, 'detail', 'dayonly'); $tmp_last = &format_date ($last, 'detail', 'dayonly'); if ($first ne $last) { push(@details, "$tmp_first - $tmp_last"); } else { push(@details, "$tmp_first"); } push(@index, "
\n"); push(@details, "
\n"); } if (defined($montage)) { $anchortext = ""); push(@index, "\n"); push(@index,"
"); push(@index, "$dir"); push(@index,"
\n"); $anchortext = ""); push(@details, ""); push(@details, "
\n"); } else { push(@index,"
"); push(@index, "$dir"); push(@index,"
\n"); } push(@index, "
"); push(@details, "
"); if ($dirtitle ne "") { push(@index, "$dirtitle"); push(@details, "$dirtitle"); } push(@details, "
$dir"); push(@index, "
\n"); push(@details, "
\n"); push(@details,"
\n"); push(@index, "\n"); push(@details, "\n"); # At end of row? if ($col_counter == $current_columns) { push(@index, "\n"); push(@details, "\n"); } # Increment for next item $col_counter++; $col_counter = 1 if ($col_counter > $current_columns); } # if dir had index file } ###################################################################### # # Top of HTML index/detail files # ###################################################################### sub page_header { my $this = shift(@_); my $linkto = shift(@_); my $numlink = 0; my $verstring; select(INDEX); print "\n"; print "\n"; print "\n"; $verstring = &versionstring(); printf ("\n", $verstring); printf ("\n"); if (defined ($write_meta_tag{$titlemetatag})) { print "\n"; } if (defined ($write_meta_tag{$columnsmetatag})) { print "\n"; } if (defined ($write_meta_tag{$thumbxmetatag})) { print "\n"; } if (defined ($write_meta_tag{$thumbymetatag})) { print "\n"; } if (defined ($write_meta_tag{$reversemetatag})) { print "\n"; } if (defined($firstdate)) { print "\n"; } if (defined($lastdate)) { print "\n"; } if (!defined ($opt_includeall) && defined (@opt_exclude) && scalar (@opt_exclude)) { my $tmp = join (',', @opt_exclude); my $etmp; # We need to "encode" this string in the HTML so that raw filenames # (that people should not try to access) are not exposed to the # outside world. # $etmp = &encodestring ($tmp); printf ("\n", $etmp); } printf ("\n", $image_counter); if (defined (@opt_skipmont) && scalar (@opt_skipmont)) { my $tmp = join (',', @opt_skipmont); printf ("\n", $tmp); } print "$current_titletext\n"; print "\n"; print "\n"; print "\n"; # Break out of frames print "\n"; print "

$current_titletext

\n"; print "

"; # On all these links, check to see if the variable is also defined. If # not (done in a .imageindexrc file perhaps) then skip the link # If the script-use-only flag updirindexoverride was given it means we've # come in from a recursive call and $updirtext was set--therefore there will # be $destdir/../$indexfile eventually ... link to it. # if ((defined ($opt_updirindexoverride) and ($do_dirs == 1)) or ((-e "$destdir/../$indexfile") and ($do_dirs == 1) and defined($updirtext))) { print "$updirtext"; $numlink++; } if (($do_slide == 1) and ($slide_counter > 1) and defined($framelinktext)) { print "  |  " if ($numlink != 0); print "$framelinktext"; $numlink++; } if (($do_detail == 1) and ($this eq 'index') and defined($detaillinktext)) { print "  |  " if ($numlink != 0); print "$detaillinktext"; $numlink++; } if (($this eq 'detail') and defined($indexlinktext)) { print "  |  " if ($numlink != 0); print "$indexlinktext"; $numlink++; } print "\n
\n" if ($numlink != 0); print "

\n"; if (defined($firstdate) and defined($lastdate)) { my $tmp1 = &format_date($firstdate, $this, 'dayonly'); my $tmp2 = &format_date($lastdate, $this, 'dayonly'); if ($tmp1 ne $tmp2) { if ($current_reverse == 0) { print "

$tmp1 - $tmp2

\n"; } else { print "

$tmp2 - $tmp1

\n"; } } else { print "

$tmp1

\n"; } } print "\n"; select(STDOUT); } ###################################################################### # # Bottom of HTML file # ###################################################################### sub page_footer { my $time = localtime(time); my $progurl = 'http://www.edwinh.org/imageindex/'; select(INDEX); print "
\n"; print "
"; print "page created on $time
\n"; print "by imageindex "; print &versionstring(); # print "
\n"; # print "Edwin Huffstutler <edwinh at computer dot org>"; # print "
\n"; # print "John Reynolds <johnjen at reynoldsnet dot org>"; print "
\n"; print "\n\n"; select(STDOUT); } ###################################################################### # # A "quickie" routine to show which files were excluded in a prior run # ###################################################################### sub showexcluded { my ($file) = @_; my ($rfile, $tmp, $utmp, @files, $str); if (! defined ($file)) { if (-r $indexfile) { $rfile = $indexfile; } } else { $rfile = $file; } $tmp = &extract_meta_tag ($excludemetatag, $rfile); if (defined($tmp)) { # We need to "decode" this string as it has been encoded for storage # in the HTML so that raw filenames (that people should not try to # access) are not exposed to the outside world. # $utmp = &decodestring ($tmp); (@files) = split (/,/, $utmp); $str = join (',', @files); printf ("File '$rfile' shows the following record of excluded files:\n"); printf ("%s\n", $str); } else { printf ("File '$rfile' shows no record of excluded files.\n"); } return; } ###################################################################### # # Ignore certain files via META data stored in the index.html file # # Exports global variable %skipmont used later during montage # generation. # ###################################################################### sub exclude_files { my @files = @_; my (@filelist, $f, %exclude, $token, @tokens); undef %exclude; # -skipmont flags override any META data found. Else, look for the META tag # then process. Check to see if any of the -skipmont options were given as # strings of filenames concatenated with ',' characters. If so, support it. # if (defined (@opt_skipmont)) { foreach (@opt_skipmont) { (@tokens) = split (/,/, $_); foreach $token (@tokens) { $skipmont{$token}++; } } } elsif (-r "$destdir/$indexfile") { my $tmp = &extract_meta_tag ($skipmetatag, "$destdir/$indexfile"); if (defined($tmp)) { (@opt_skipmont) = split (/,/, $tmp); my $str = join (',', @opt_skipmont); printf ("Using saved skip-montage files: %s\n", $str); foreach (@opt_skipmont) { $skipmont{$_}++; } } } # -exclude flags override any META data found. Else, look for the META tag # then process. Check to see if any of the -exclude options were given as # strings of filenames concatenated with ',' characters. If so, support it. # if (defined (@opt_exclude)) { # -includeall takes priority over -exclude on the commandline if they are # used together (wierd, but ...) # unless (defined ($opt_includeall)) { foreach (@opt_exclude) { (@tokens) = split (/,/, $_); foreach $token (@tokens) { $exclude{$token}++; } } } } elsif (-r "$destdir/$indexfile") { my $tmp = &extract_meta_tag ($excludemetatag, "$destdir/$indexfile"); my $utmp; if (defined($tmp) && !defined ($opt_includeall)) { # We need to "decode" this string as it has been encoded for storage # in the HTML so that raw filenames (that people should not try to # access) are not exposed to the outside world. # $utmp = &decodestring ($tmp); (@opt_exclude) = split (/,/, $utmp); my $str = join (',', @opt_exclude); printf ("Using saved excluded files: %s\n", $str); foreach (@opt_exclude) { $exclude{$_}++; } } } foreach $f (@files) { if (! $exclude{$f}) { push (@filelist, $f); } else { print "Excluding '$f'\n"; if (-d $f) { chmod (0700, $f); } else { chmod (0600, $f); } } } return (@filelist); } ###################################################################### # # Nuke generated files if original image gone # ###################################################################### sub nuke_out_of_date { foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) { opendir(THUMBS,"$destdir/$checkdir") || die "Can't open dir $checkdir: ($!)\n"; foreach (readdir(THUMBS)) { next if (m/^\.?\.$/); next if (m/$framefile/); next if (m/$slidefile/); next if (m/$montagefile/); next if (m/$emoticonsmile/); next if (m/$emoticonwink/); next if (m/$emoticonfrown/); if (!defined($thumb_backref{"$checkdir/$_"}) and !defined($slide_backref{"$checkdir/$_"}) and !defined($med_backref{"$checkdir/$_"})) { print "Removing stale $destdir/$checkdir/$_\n"; unlink("$destdir/$checkdir/$_") || warn "Can't unlink $destdir/$checkdir/$_: ($!)\n"; $modified_thumb++; } } closedir(THUMBS); } } ###################################################################### # # Convert bytes to kb string # ###################################################################### sub convert_to_kb { my $bytes = shift(@_); if ($bytes > (1024 * 1024)) { $bytes = sprintf("%.1fM", $bytes / (1024.0 * 1024.0)); } else { $bytes = sprintf("%dk", $bytes / 1024); } return($bytes); } ###################################################################### # # Sortq by integer date stamp # ###################################################################### sub bynumber { if ($current_reverse == 0) { $a <=> $b; } else { $b <=> $a; } } ###################################################################### # # Write frameset file for slideshows # ###################################################################### sub write_frameset { # This is impossible to get rid of my $framefudge = 35; my $verstring; open(FRAME,">$destdir/$slide_dir/$framefile") or die ("Can't open $destdir/$slide_dir/$framefile: $!\n"); select(FRAME); print "\n"; print "\n"; print "\n"; $verstring = &versionstring(); printf ("\n", $verstring); printf ("\n"); print ""; print "$current_titletext\n"; print "\n"; print "\n"; if ($frame_orient eq 'horizontal') { printf("\n", $max_thumb_y + $framefudge); } else { printf("\n", $max_thumb_x + $framefudge); } print "\n"; print "\n"; print "No frames in this browser...go back\n"; print "\n"; print "\n"; select(STDOUT); close (FRAME); open(FRAME,">$destdir/$slide_dir/$slidefile") or die ("Can't open $destdir/$slide_dir/$slidefile: $!\n"); select(FRAME); print "\n"; print "\n"; print "\n"; $verstring = &versionstring(); printf ("\n", $verstring); printf ("\n"); print "\n"; print "$current_titletext\n"; print "\n\n"; print "\n" if ($frame_orient eq 'horizontal'); foreach (@frame) { print; } print " \n" if ($frame_orient eq 'horizontal'); print "
\n"; print "\n\n"; select(STDOUT); close(FRAME); } ###################################################################### # # Do next/index/prev links on slide pages # ###################################################################### sub next_prev_links { my $pathname = shift(@_); print "
"; if (defined($back{$pathname})) { print "< previous | "; } else { print "< previous | "; } print "index"; if (defined($forward{$pathname})) { print " | next >"; } else { print " | next >"; } print "
\n"; } ###################################################################### # # Lower-case all the filenames. I hate the uppercase filenames that come # from my camera's default software (and Windud software). Plus I didn't # want this "utility" in another script, so just place it here. # ###################################################################### sub lower_case_files { my (@files) = @_; my ($newfile, $lowername); foreach $name (@files) { ($lowername = $name) =~ tr/A-Z/a-z/; if ($name =~ /[A-Z]/) { print "Moving '$name' to '$lowername'\n"; move("$name","$lowername"); } } } ###################################################################### # # extract the NAME tag from an HTML file # ###################################################################### sub extract_meta_tag { my ($tag, $filename) = @_; my ($name, $content, $retval); if (! (open (FILE, $filename))) { print STDERR "Cannot open '$filename' for reading - $!\n"; return (0); } # # while () { if (//) { $name = $1; $content = $2; if ($name eq $tag) { $retval = $content; last; } } } close (FILE); return ($retval); } ############################################################################### # # Rotate given image 90 degrees # ############################################################################### sub rotate_image { my $file = shift(@_); my $argv = shift(@_); if ($file =~ m/^(cw|ccw)$/) { # If file is cw or ccw, # assume the args were given backwards my $tmp = $file; $file = $$argv[0]; $$argv[0] = $tmp; } -r "$file" || die("$file: ", $!); -w "$file" || die("$file: ", $!); # grab the mtime of the file so we can reset it after we update it my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, $ctime,$blksize,$blocks) = stat($file); my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); my ($name,$path,$suffix) = fileparse($file,'\.\S+'); my $thumb; my $medium; my $image = new Image::Magick; my $retval = $image->Read("$file"); warn "$retval" if "$retval"; if (!defined($$argv[0]) or ($$argv[0] !~ m/^cc?w$/i)) { print "Need 'cw' or 'ccw' argument to rotate image clockwise/counterclockwise\n"; exit(1); } if ($$argv[0] =~ /^ccw$/i) { $deg = -90; } else { $deg = 90; } print "Rotating $file $deg degrees\n"; $retval = $image->Rotate($deg); warn "$retval" if "$retval"; $retval = $image->Write(filename=>"$file"); warn "$retval" if "$retval"; system ("touch -t $posix_mtime $file"); # Nuke the generated images if they exist # (touching the timestamp above breaks automatic regeneration logic) if ($image->Get('format') =~ /JFIF/i) { $thumb = $path . "$thumbnail_dir/$name" . $suffix; $medium = $path . "$med_dir/$name" . $suffix; } else { $thumb = $path . "$thumbnail_dir/$name.jpg"; $medium = $path . "$med_dir/$name.jpg"; } unlink($thumb) if (-e "$thumb"); unlink($medium) if (-e "$medium"); } ############################################################################### # # Set or display caption for a particular image # ############################################################################### sub caption_image { my $file = shift(@_); my $argv = shift(@_); my ($esc_comment, $tmpfile); -r "$file" || die("$file: ", $!); # grab the mtime of the file so we can reset it after we update it my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, $ctime,$blksize,$blocks) = stat($file); my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); my $image = new Image::Magick; my $retval = $image->Read("$file"); warn "$retval" if "$retval"; my $format = $image->Get('format'); warn "$retval" if "$retval"; # Set caption if another arg is present, or just display it if (defined($$argv[0])) { -w "$file" || die("$file: ", $!); # Try to find wrjpgcom so we can use it for adding captions to JPG images my $wrjpgcom_prog = &find_in_path ('wrjpgcom'); my $quote_file = quotemeta ($file); # If a jpeg file and we found a wrjpgcom program in our path, use # it! It simply puts the comment in the JPEG header without reading # (uncompressing) and writing (re-compressing) the file out so # there is no chance for data loss. # if (($format =~ /JFIF/i) and defined($wrjpgcom_prog)) { $tmpfile = "$file.$$"; my $tmpfile_quote = "$quote_file.$$"; $esc_comment = quotemeta ($$argv[0]); # FIXME # check to see how '?' and other punctuation is escaped and fix # it seems things are not correct. system ("$wrjpgcom_prog -replace -comment $esc_comment $quote_file > $tmpfile_quote"); if (($? >> 8) != 0) { printf(STDERR "Error in creating JPEG comment with 'wrjpgcom'. Leaving existing file intact.\n"); } else { move($tmpfile, $file); } } else { # Fall back to PerlMagick's routines. # $retval = $image->Comment("$$argv[0]"); warn "$retval" if "$retval"; $retval = $image->Write(filename=>"$file", quality=>"95", sampling_factor=>"1x1"); warn "$retval" if "$retval"; } system ("touch -t $posix_mtime $quote_file"); } else { my $text = $image->Get('comment'); if (defined($text)) { print "$file: \"$text\"\n"; } else { print "$file: (no caption)\n"; } } } ############################################################################### # # Print usage info from top of file # ############################################################################### sub usage { open(FILE,"$0") or die "Can't open $0: $OS_ERROR"; while() { last if (m/^\#\s+USAGE:/); } while() { last if (m/^\#\#\#\#\#\#\#/); s/^\# ?//; print; } close(FILE); } ###################################################################### # # Format timestamp for HTML pages. This routine assumes that the date # given to it is in YYYYMMDDHHMM.SS format that we've created from the # EXIF/mtime date using strftime(). # ###################################################################### sub format_date { my ($date, $context, $dayonly) = @_; my ($timeformat, $dateformat); if ($context eq 'frame') { $timeformat = $frametimeformat; $dateformat = $framedateformat; } elsif ($context eq 'index') { $timeformat = $indextimeformat; $dateformat = $indexdateformat; } elsif ($context eq 'slide') { $timeformat = $slidetimeformat; $dateformat = $slidedateformat; } else { $timeformat = $detailtimeformat; $dateformat = $detaildateformat; } # Replace "macro" patterns in the format string first # $timeformat =~ s/\%R/\%H:\%M/g; $timeformat =~ s/\%r/\%I:\%M:\%S \%p/g; $dateformat =~ s/\%F/\%Y-\%m-\%d/g; $dateformat =~ s/\%D/\%m\/\%d\/\%y/g; $date =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)?(\d\d)?\.?(\d\d)?/; my $year = $1; my $month = $2; my $day = $3; my $hour = $4; my $min = $5; my $sec = $6; my ($ampm, $two_digit_year, $twelve_hour); if ($year =~ /^\d\d(\d\d)$/) { $two_digit_year = $1; } else { $two_digit_year = '??'; # shouldn't ever been seen } # If we're told to, only format a date with no time # if (defined ($dayonly)) { $dateformat =~ s/\%Y/$year/g; $dateformat =~ s/\%y/$two_digit_year/g; $dateformat =~ s/\%m/$month/g; $dateformat =~ s/\%d/$day/g; $dateformat =~ s/\%\%/\%/g; return ($dateformat); } else { if (defined($hour)) { $twelve_hour = $hour; $ampm = 'AM'; if ($hour >= 12) { $ampm = 'PM'; } if ($hour > 12) { $twelve_hour -= 12; } } else { $hour = '??'; $twelve_hour = '??'; $ampm = '??'; #again, should never be seen } if (! defined ($min)) { $min = '??'; } if (! defined ($sec)) { $sec = '??'; } $dateformat =~ s/\%Y/$year/g; $dateformat =~ s/\%y/$two_digit_year/g; $dateformat =~ s/\%m/$month/g; $dateformat =~ s/\%d/$day/g; $dateformat =~ s/\%\%/\%/g; $timeformat =~ s/\%S/$sec/g; $timeformat =~ s/\%M/$min/g; $timeformat =~ s/\%I/$twelve_hour/g; $timeformat =~ s/\%H/$hour/g; $timeformat =~ s/\%p/$ampm/g; $timeformat =~ s/\%\%/\%/g; return("$dateformat $timeformat"); } } ###################################################################### # # Return version string from CVS tag # ###################################################################### sub versionstring { my $ver = ' $Name: v1_1 $ '; $ver =~ s/Name//g; $ver =~ s/[:\$]//g; $ver =~ s/\s+//g; $ver =~ s/^v//g; $ver =~ s/_/\./g; if ($ver eq '') { $ver = "cvs devel - " . '$Revision: 1.175 $ '; # Nuke the $ signs -- what if somebody is keeping pages under RCS # or CVS control? $ver =~ s/\$//g; $ver =~ s/\s*$//; } return($ver); } ############################################################################### # # Create CSS file that is shared among the HTML pages # ############################################################################### sub write_css { open(CSS,">$destdir/$stylefile") or die ("Can't open $destdir/$stylefile: $!\n"); select(CSS); print $stylesheet; select(STDOUT); close(CSS); } ############################################################################### # # "Interpolate" %? escapes found in our printf-like strings defined for the # TITLE attributes. See the beginning of this file for their definition # ############################################################################### sub interpolate_title_string { my ($formatstring, $pathname, $context) = @_; my ($filename, $date, $size, $resolution, $caption); my ($tmp); $filename = $info{$pathname}{'file'}; $date = &format_date ($info{$pathname}{'date'}, $context); $size = $info{$pathname}{'size'}; $resolution = $info{$pathname}{'geometry'}; $caption = $info{$pathname}{'comment'}; if (! defined ($caption)) { $caption = ''; } $tmp = $formatstring; $tmp =~ s/\%f/$filename/g if $filename; $tmp =~ s/\%d/$date/g if $date; $tmp =~ s/\%s/$size/g if $size; $tmp =~ s/\%r/$resolution/g if $resolution; $tmp =~ s/\%c/$caption/g; $tmp =~ s/\%\%/%/g; # In case the format string has " marks in it, change all those to '. # The " marks are needed to mark the argument to the TITLE attribute. # $tmp =~ s/\"/\'/g; return ($tmp); } ############################################################################### # # "Interpolate" %? escapes found in our printf-like strings defined for the # TITLE attributes. However, the %? escapes for this function are based on what # you could conceivably need when processing a directory. # # See the beginning of this file for their definition # ############################################################################### sub interpolate_title_string_dir { my ($formatstring, $dir, $context) = @_; my ($tmp, $num, $date, $metadate, $metatitle); $tmp = $formatstring; $num = &extract_meta_tag($numimagesmetatag, "$srcdir/$dir/$indexfile"); # If we plucked out the number of images from the metadata of a directory's # index.html file, replace it. Else, give a warning if we didn't find it but # somebody still used %n # if (defined ($num)) { $tmp =~ s/\%n/$num/g if $num; } else { if ($tmp =~ /\%n/) { if (!defined ($remember_warning{$dir})) { printf (STDERR "Warning: %%n escape used in format string and %s META tag not found in %s. Re-run imageindex in '$dir'.\n", $numimagesmetatag, "$srcdir/$dir/$indexfile"); $remember_warning{$dir}++; } } } $metadate = &extract_meta_tag($begindatemetatag, "$srcdir/$dir/$indexfile"); $date = &format_date ($metadate, $context, 'dayonly'); $tmp =~ s/\%b/$date/g if $date; $metadate = &extract_meta_tag($enddatemetatag, "$srcdir/$dir/$indexfile"); $date = &format_date ($metadate, $context, 'dayonly'); $tmp =~ s/\%e/$date/g if $date; $metatitle = &extract_meta_tag($titlemetatag, "$srcdir/$dir/$indexfile"); $tmp =~ s/\%t/$metatitle/g if $metatitle; # In case the format string has " marks in it, change all those to '. # The " marks are needed to mark the argument to the TITLE attribute. # $tmp =~ s/\"/\'/g; return ($tmp); } ############################################################################### # # Look for external programs we depend on in the $PATH. It just finds the first # occurence of $prog in $PATH. # ############################################################################### sub find_in_path { my ($prog) = @_; my ($retval); undef $retval; foreach $dir (split (/:/, $ENV{'PATH'})) { if (-r "$dir/$prog" && -x "$dir/$prog") { $retval = "$dir/$prog"; } } return ($retval); } ############################################################################### # # Encode/decode routines for exclude filenames when stuffed in a meta tag # ############################################################################### sub encodestring { my ($tmp) = @_; my $etmp; $etmp = pack ("u*", $tmp); # Hack the string to get rid of \n chars so we can store it on 1 line $etmp =~ s/\n/..1xn!_ltr../g; # Get rid of ampersands $etmp =~ s/\&/..xn!_ltr1../g; # Get rid of double-quotes $etmp =~ s/\"/..sb!_lho1../g; return ($etmp); } sub decodestring { my ($tmp) = @_; my $utmp; # Unhack the string to bring back & characters $tmp =~ s/\.\.sb\!_lho1\.\./\"/g; $tmp =~ s/\.\.xn\!_ltr1\.\./\&/g; # Unhack the string to bring back & characters $tmp =~ s/\.\.xn\!_ltr1\.\./\&/g; # Unhack the string to bring back \n characters $tmp =~ s/\.\.1xn\!_ltr\.\./\n/g; $utmp = unpack ("u*", $tmp); return ($utmp); } ############################################################################# # # This routine samples linearly (as possible) across the available files in # a directory. The first pass at sampling is a simple modulo function based # upon the ratio of files to the number of tiles we can use in the montage. # If that first pass sample did not produce enough files, then we go back # iteratively through the list and as evenly-as-possible select unused # files from those left in the pool. # ############################################################################# sub sample_files_for_montage { my (@files) = @_; my ($numdiv, $numchosen, $chunksize, $numfiles, $numleft); my ($i, $index, $f, @ret); $numfiles = scalar (@files); $numdiv = sprintf ("%d", $numfiles / $montage_max); $numdiv++; for ($i = 0; $i < $numfiles; $i++) { if (($i % $numdiv) == 0) { $chosen{$files[$i]}++; } } $numchosen = scalar (keys %chosen); $numleft = $montage_max - $numchosen; if ($numleft) { $chunksize = sprintf ("%d", $numfiles / $numleft); $index = 0; for ($i = 0; $i < $numleft; $i++) { &mark_next_file_for_montage ($index + 1, $numfiles, @files); $index = $index + $chunksize; } } foreach $f (@files) { if ($chosen{$f}) { push (@ret, $f); } } return (@ret); } ############################################################################# # # cycle through the given list of files. If the list[$index] is already marked # (via the global hash %chosen) then move onto the next one, etc. # ############################################################################# sub mark_next_file_for_montage { my ($index, $numfiles, @files) = @_; my ($i); for ($i = $index; $i < $numfiles; $i++) { if (! $chosen{$files[$i]}) { $chosen{$files[$i]}++; last; } } } ############################################################################### # # Exclude certain filenames from the list of thumbnails to be used in the # montage image. # ############################################################################### sub exclude_montage_files { my (@files) = @_; my (@tmp, $file); foreach (@files) { $file = basename ($_); unless (defined ($skipmont{$file})) { push (@tmp, $_); } } return (@tmp); } ############################################################################### # # "html-ize" a caption found in an image. Just in case there are certain # characters used which we want to "escape." # ############################################################################### sub htmlize_caption { my ($caption, $slide) = @_; $caption =~ s/\&/\&/g; $caption =~ s/\/\>/g; $caption =~ s/\"/\"/g; # Help smiley's render in a "mo-better" way when they are at the end of a # caption and enclosed in parens # if ($caption =~ /(:\-?[\(\)])\s*\)\s*$/) { my $tmp = $1; $caption =~ s/:\-?[\(\)]\s*\)\s*$/$tmp\ \)/; } $caption = &emoticonify ($caption, $slide); return ($caption); } ############################################################################### # # Translate ASCII smiley's embedded into image captions into emoticons # ############################################################################### sub emoticonify { my ($caption, $slide) = @_; my ($thumbdir, $attr); return ($caption) if (! defined ($do_emoticons) || $do_emoticons == 0); # This is a hack, please ignore and move on ... nothing to see here. # $caption =~ s/\ /NoNBrEaKaBleSpacE/g; $thumbdir = $thumbnail_dir; if ($slide) { $thumbdir = '../' . $thumbdir; } $attr = 'STYLE="vertical-align: middle;" WIDTH="19" HEIGHT="19"'; if ($caption =~ s/:\-?\)/\\"/g) { $emoticon{'smile'}++; } if ($caption =~ s/;\-?\)/\\"/g) { $emoticon{'wink'}++; } if ($caption =~ s/:\-?\(/\\"/g) { $emoticon{'frown'}++; } # Undo the hack # $caption =~ s/NoNBrEaKaBleSpacE/\ /g; return ($caption); } ############################################################################### # # Write out PNG files representing the emoticons # ############################################################################### sub write_emoticon_png { my ($type) = @_; my ($img); if (! open (IMG, '>' . $destdir . '/' . $thumbnail_dir . "/$emoticonprefix" . $icon . ".png")) { printf (STDERR "Could not open emoticon file for '$icon' for writing - $!\n"); return; } # UUDecode the small PNG files that represent the emoticons and dump them to # the appropriate files in $thumbnail_dir # $img = unpack ("u*", $png{$type}); print IMG $img; close (IMG); } ############################################################################### # # Write out PNG files representing the emoticons # ############################################################################### sub write_video_icons { my ($tmp_jpg_dir) = @_; my ($img); foreach $key (keys %png) { next if $key !~ /video/; if (! open (IMG, '>' . $tmp_jpg_dir . '/' . $key . ".png")) { printf (STDERR "Could not open video icon file for '$key' for writing - $!\n"); return; } # UUDecode the small PNG files that represent the emoticons and dump them to # the appropriate files in $thumbnail_dir # $img = unpack ("u*", $png{$key}); print IMG $img; $max_video_icons++; close (IMG); } } ############################################################################### # # Create a montage of images in the current directory. This image will be # pointed to by the parent directory's index.html file to show a sort of # "thumbnail preview" of the contents of this directory. # ############################################################################### sub create_montage { my @files = @_; my (@modfiles); @files = &exclude_montage_files (@files); foreach (@files) { push (@modfiles, quotemeta ($_)); } # If we have defined that a lesser number of "tiles" can be used in the # montage vs. the # of files in this directory, then we'll "sample" the # files as evenly as possible to avoid clustering of shots that might be # similar to each other. # if (scalar (@modfiles) > $montage_max) { @modfiles = &sample_files_for_montage (@modfiles); } if ($do_montage == 1) { if (($modified_thumb != 0) or (! -e "$destdir/$thumbnail_dir/$montagefile")) { my $number = $#modfiles + 1; my $tile_x = 1;; my $tile_y = 1; # FIXME these both blindly expand x before expanding y # Should this depend on some aspect ratio? while(($tile_x * $tile_y) < $montage_min) { $tile_x++; $tile_y++ if (($tile_x * $tile_y) < $montage_min); } while(($tile_x * $tile_y) < $number) { $tile_x++; $tile_y++ if (($tile_x * $tile_y) < $number); } my $index = 0; while (($#modfiles + 1) < ($tile_x * $tile_y)) { if ($montage_fill eq 'blank') { push(@modfiles, "NULL:"); } else { push(@modfiles, $modfiles[$index]); $index = ($index+1) % $number; } } my $tile = sprintf("%dx%d", $tile_x, $tile_y); my $geom = sprintf("%dx%d", $max_mont_thumb_x, $max_mont_thumb_y); my $newgeom = sprintf("%dx%d", $current_thumbnail_x, $current_thumbnail_y); print "Picked $tile array of $geom for montage\n" if ($opt_debug); print "Creating $destdir/$thumbnail_dir/$montagefile\n"; system("montage -quality $thumb_quality -bordercolor white -transparent white -border $montage_whitespace -geometry $geom -tile $tile @modfiles \"$destdir/$thumbnail_dir/$montagefile\""); if (($? >> 8) != 0) { printf(STDERR "Error in creating montage file\n"); return(-1); } # Resize to std. thumbnail my $image = new Image::Magick; my $retval; $retval = $image->Read(filename=>"$destdir/$thumbnail_dir/$montagefile"); warn "$retval" if "$retval"; $retval = $image->Resize(geometry=>$newgeom); warn "$retval" if "$retval"; $retval = $image->Set(interlace=>Line); warn "$retval" if "$retval"; $retval = $image->Write(filename=>"$destdir/$thumbnail_dir/$montagefile"); warn "$retval" if "$retval"; } } else { unlink("$destdir/$thumbnail_dir/$montagefile") if (-e "$destdir/$thumbnail_dir/$montagefile"); } } sub read_stored_meta_data { my ($tmp); if (-r "$destdir/$indexfile") { $tmp = &extract_meta_tag ($columnsmetatag, "$destdir/$indexfile"); # If we found data, check it against program defaults if (defined ($tmp)) { $current_columns = $tmp; print "Using saved number of columns: $current_columns\n" if ! defined ($opt_columns); } $tmp = &extract_meta_tag ($titlemetatag, "$destdir/$indexfile"); # If we found data, check it against program defaults if (defined ($tmp)) { $current_titletext = $tmp; print "Using saved title: $current_titletext\n" if ! defined ($opt_title); } $tmp = &extract_meta_tag ($thumbxmetatag, "$destdir/$indexfile"); # If we found data, check it against program defaults if (defined ($tmp)) { $current_thumbnail_x = $tmp; print "Using saved thumbnail X size: $current_thumbnail_x\n" if ! defined ($opt_x); } $tmp = &extract_meta_tag ($thumbymetatag, "$destdir/$indexfile"); # If we found data, check it against program defaults if (defined ($tmp)) { $current_thumbnail_y = $tmp; print "Using saved thumbnail Y size: $current_thumbnail_y\n" if ! defined ($opt_y); } $tmp = &extract_meta_tag ($reversemetatag, "$destdir/$indexfile"); # If we found data, check it against program defaults if (defined ($tmp)) { $current_reverse = $tmp; print "Using saved reverse: $current_reverse\n" if ! defined ($opt_reverse); } &decide_which_md_to_store(); } } sub override_by_commandline { if (defined($opt_columns)) { $current_columns = $opt_columns; } if (defined($opt_title)) { $current_titletext = $opt_title; } if (defined($opt_reverse)) { $current_reverse = $opt_reverse; } if (defined($opt_x)) { $current_thumbnail_x = $opt_x; if ($current_thumbnail_x != $default_thumbnail_x) { $opt_forceregen = 1; } } if (defined($opt_y)) { $current_thumbnail_y = $opt_y; if ($current_thumbnail_y != $default_thumbnail_y) { $opt_forceregen = 1; } } &decide_which_md_to_store(); } sub decide_which_md_to_store { if ($current_columns != $default_columns) { $write_meta_tag{$columnsmetatag}++; } else { undef $write_meta_tag{$columnsmetatag}; } if ($current_thumbnail_x != $default_thumbnail_x) { $write_meta_tag{$thumbxmetatag}++; } else { undef $write_meta_tag{$thumbxmetatag}; } if ($current_thumbnail_y != $default_thumbnail_y) { $write_meta_tag{$thumbymetatag}++; } else { undef $write_meta_tag{$thumbymetatag}; } if ($current_titletext ne $default_titletext) { $write_meta_tag{$titlemetatag}++; } else { undef $write_meta_tag{$titlemetatag}; } if ($current_reverse ne $do_reverse) { $write_meta_tag{$reversemetatag}++; } else { undef $write_meta_tag{$reversemetatag}; } } sub initialize_current_vars { $current_columns = $default_columns; $current_titletext = $default_titletext; $current_thumbnail_x = $default_thumbnail_x; $current_thumbnail_y = $default_thumbnail_y; $current_reverse = $do_reverse; } ############################################################################## # # Just initialize the 'png' array with UUENCODED PNG files for emoticons. This # was placed down here so as not to clutter up the top of the file where the # other globals are initialized. # ############################################################################## sub init_png_array { $png{'wink'} = <<'EOF'; MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7____, MS``S,P#___\```#__P!/FRMM`````7123E,`0.;89@````%B2T=$!?AOZ<<` M``!F241!5'C:;8_1#8`P"$3O@Q'L!CJ!#M`FQP`FL/\J%FJ-)O+U:R.N?=+?A#36P6)H9!(F)Z CI1BYC1+=-K2?9J^^SQ<7J48K([9O4:P`````245.1*Y"8((` ` EOF $png{'smile'} = <<'EOF'; MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7__P#, MS`!F9@#_,P````#___]]YKD%````!G123E/______P"SOZ2_`````6)+1T0% M^&_IQP```&U)1$%4>-I-C\$-P"`,`UV)`?I@@#ZZ03M`*L&?!]E_E=H$(5`> MEP39#MR]E%+=&T@GD*NPD\A"PWBU@4,VADQ$*J,:OHF'<14(BX]14R#0%J1+ M>!.I(&,I=(!Q3+IT2\\+N2D#A\JP)]ORKBM^`[0;1*VK3]P`````245.1*Y" "8((` ` EOF $png{'frown'} = <<'EOF'; MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````'E!,5$7____, MS`"9F0!F9@`S,P#,S#/___\S,S,```#__P`[/ZS;`````7123E,`0.;89@`` M``%B2T=$"?'9I>P```"$241!5'C:78^Q#<,P#`19:@1I`WN!`%G`@`<(8"T0 M>`.GE-R8*EV%OVU(24YA@L7A^>"31$3,G*@6!\!7=D$@\(8%!=K)Q&/#]U-4 M=AC>\;&.0I0A:YQVG$FM)\$;-N<-TJEM;0 @UQOZ@DOVMWO_7_P`Y9]*.M\PG><`````245.1*Y"8((` ` EOF $png{'video_icon1'} = <<'EOF'; MB5!.1PT*&@H````-24A$4@```!D````="`8```!?>,=U````!F)+1T0`_P#_ M`/^@O:>3````"7!(67,```!(````2`!&R6L^```&$4E$051(Q[V6WXM=5Q7' M/VN?<^Z],YUDIDF3W#-ITJ9B6S'X$D-2"A6*6,6*A3XI2'WJ@P\JH@511'T3 M6Z$//O<_L(J"H'UH06B)4%O0MEIIDR;IF>F0=I*Y]_S8>Z^U?+CYJ0WVJ0L. M'-B'\]G[N]9>WP4?0\C-%IJF"4%R>6!Z*-[LF\V-MPOW$4Y1@&M=U_J1($W3 M%"+*='J[7KQP*A2RLRK$PV[YB.`'7*0$B;@)C.>#'3B5;>UM\Y'6];K_7TC3 M-!(D2A6V9!3>.UF$X>LAV,D0..R:UR"7IHH@!!',3;.-SB7;]TSO=SWI++5U M/?6;0IJF*8)DGY2;HU$X^SVQV7=%9%\(!1`0"4A1D',F6,1T`(]8;HG#I!VJ MX[_4\N!3$+JZKO/UD'#=NU72KDR*K1^/*O^)%*-]A0@A5(1J%RX37$%01")N M$=,(>4;I&\M5?.7[(;WS+3R'IFGD?R!-TT@(.8RJ^0]$_`E7QH(A18$4RTA1 M(H5@&.**NN$`GB&4B!A%>N.6T>R/3X3AGU_!;\Q_:)IF%9`BQ).C:O9X**U2 M5Y``Q0KJCND:O:G'X5XYIX;("+2!;&J ME$N/"Q?WNW:X)0("8@@]ECLDS_$\PZT#[2%U6.S)W4#NY^34HJE%VC>/%A>? M^^'&V==6KD(6\U_U?`DL$4+`0TG.$4T=%CLTS7";(]8M`*G%8H?8 M'-(`$@%'IQ=QE/'\6V(8`1 M4#7$$J06U1:1R(6M;7[WAU=X_?6SI*''+:':X3:`#5B."QES#WF.Q#.W7JLN MB:LBLUU8)LB8`(L=>L1=<_OL63S_S-PK[ M)`\>7V,RRGA.>!S(<8Z;XJ($'>RJ7.:63/.`*N0.T@X^[$!L\=PM@-+A/D=0 M.U,[(PPYN+05ST!VRSO6:7*%TBHD).V"ZD"=WN.=% M8C52A8CF#I',WK62SWYJA2^>V,WN,I/:B.>>@A[U#O6$FR`N[56(AKJ-(S]3 MV$L4WN(ZH+''B8A%+/=HR*A&QB/G@6-K?//A*^VJ7-/U0Q%9_JVF8F9#0LB$TA#/6$ZX&:Z9(,:MNRKN/KS,X6E%56;` M,$NX)=PSN(,K4N[]P/=\^5\;[[XC`.7FYOD0Y(.79;3VG+7;7W*-8[,!3P-F M'=B`6V2ERGSAQ"IW[@OHU+,RCIRXM^+0;8[X@%G$M87<@J7%*2BP<$N. MY<%GIT=.SNKI?@<(=;UN3;,A:I,WLE:G0RA!,FH=GGI(`];/\7[.2!)BE^WYJH6X(%:X#:(M=Z:ZTF'>H MS?&T#:E=_)OKO$.J"TR._&S]WD>WFHWWY`9(7==>KQ]V'=WU0C]YX!=6'MK& MP..<'&>8]:BVN,WQ',$$/%QV;P,?`(VL?.8IF3[V/,`5J?[;?JGK@V;5H5_G MI?L?\]&=KW+9%8M@BSODF87K^>($KB`E,KG]8EB][^<<^O;3TWL>T:;9N,%^ M;SIWO??VG^\NNK]\Q[O37]-+S:T^>PW/LVNPXA8H]T26CIP*Z]_X5;%\Q^_S MZ+!.U^^X^;1R?31-(W5=^^;Y?Y1B_8/>;SX4WO_-'IV_5;@R1ML9XX/;['_H M%,N??KX^OJAT^/'%O\!>',9BLN`3D\`````245.1*Y"8((` ` EOF $png{'video_icon2'} = <<'EOF'; MB5!.1PT*&@H````-24A$4@```"0````D"`,```#6WFBJ```!LU!,5$4````< M&!(F#0\L$@PP(AA((QY6+BAD.C9S0C8L&DLN'5TL(4,[)%@F*FP])VX[,VE( M,VE(.71G1D)T5DUX6UAQ9EE:27:$@701,I`Y)H4W,X0_,I4<.*81:LL0:L\4:PH1[,U M2+<]6;Y&08M318U#2Y)=49%B68QS:I1%1JM41ZM;6J9'1;)72+5#4[A75+Q= M:+)J:*A@:K)./\=A8-1L>=1W=]!>1NIC3>IF5N=K5/%B M9>A^;.]]?N!L:?MN?/5]B+1H@EXF.%X MK^Y_ONQ\M_%_P>^&D(F%HI2&LJ*'P:^!E,>$F-F4DMB&MLR(H]R"E>N"JN:# MMN>%J_.&MO*TM.2&RL.&Q]2(U]J,XMZ6XMR&Q>R(U.R6WN.%R/:'V/>Z$;PV80&LC!G6SP)^\"UFK7[]T/ ME\LGS_-:O2.#A,)&1QDIG]KSNCV;GG669=U.IX_/9:/U:Y`L9Z/. ML'/CZ5]U,DJ[,=;C9K+9"Y?4A1W9%V;CB@9[:##H=HSZMW'6[4MD)RZI"9-@ MV4>[+V%O,.CW!_TN[IT]VU]?#8D36Q8DR9(0WDB7.SV(^H0;=/L]:);V-\(^ M,0]*$T@^W/1&]A7#T"'A.JB+=60:Y4P:A'EQ+SJ&4H\%[T:ZV#!,J'<[`X@@ MTO66H2G%@[B7$:-_6%!2C+#IDYIA:%#'&`]>(]Q%J&,:RDEZW2?D`(%D272S MWREG!H00X=\<NZ5E"K46_B\.CTS-1P.;PYGII9T9+RJO-A>%<5C*I?XW+-?,LVV8UAHW[AQ MJIXVTF$N;T%>;Z*BJ>WEH6-Y[I(A%&[!BO(\XN,!)?[@]MX[M4(> M7B$LZ#6$6OED+R`!2CADW0+)+)X;7M?T.83FV>.(B^.IT?>`!B].$5X:%IJW M9L9ACS751K!5.5@00D?4Z!'P?Z:8$!=F_SEWW+(.9V$SLP6$H/D*^),,H$9Q ML+9RH))\M+':_O`RKF6$$=2>/0PP)/"1$%U;>0)5%:MDMT-22P91FW@SM>)* MB.>.K=S=]3-UDWQH8>0-20_&*B)+M?)P-($O1+^A%OVW^`TOS\RY[@).V4N]T M%0#D:##@\MML?I?=%0PP63[YIJU%)A =$L^#&,B\W?\/+ROZRK9):IT`````245.1*Y"8((` ` EOF } imageindex-1.1.orig/imageindex.10000644000175000017500000007153610605000554014677 0ustar abiabi.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "IMAGEINDEX 1" .TH IMAGEINDEX 1 "2007-03-30" "perl v5.8.8" "User Contributed Perl Documentation" .SH "NAME" imageindex \- a digital photo gallery tool .SH "SYNOPSIS" .IX Header "SYNOPSIS" Imageindex is a digital picture gallery program. It provides automatic generation of thumbnails and other size views of the images and video files, and W3C compliant \s-1HTML\s0 to allow viewing of the thumbnails and images or videos. It also creates montages of all images in a given directory to be used in directory entries within the \s-1HTML\s0. .SH "DESCRIPTION" .IX Header "DESCRIPTION" Imageindex has evolved from a simple thumbnail-generation program into a full-blown gallery application. With it you can create static thumbnails and \&\*(L"medium\*(R" views (good for dial-up web viewers) along with static \s-1HTML\s0 which presents the images in chronological order (based upon the date in the Exif header or date stamp of the file itself). Support has now been added for animated GIFs and video files (we support whatever file formats your installation of mplayer supports). .PP Imageindex creates static rather than dynamic content for many reasons. First and foremost it is much easier to archive pictures onto CD-ROMs on a periodic basis when the content is just \*(L"there\*(R" rather than \*(L"trapped\*(R" in a database of some sort. You create your own \*(L"database\*(R" with your own directories and let imageindex handle the presentation of the images to the world. Imageindex will use \*(L"montages\*(R" (or thumbnails of all your thumbnails crammed together) of subdirectories if they occur alongside images (see the the sample pages on the imageindex website \- http://www.edwinh.org/imageindex/sample.html). .PP There are four basic \*(L"views\*(R" that imageindex creates (enabled by default). The \&\*(L"index\*(R" view shows thumbnails of all images in a directory in a basic table format (defaults to 3 columns). Within each cell of the table basic information such as the date/time of the picture are given as well as any comment (or caption) present in the image. Links to the various sizes of image and other views for the image are also presented by default. .PP The second view presented is the \*(L"slide show\*(R" view. In this view the \*(L"medium\*(R" sized image is presented along with information such as date/time, any caption embedded in the image, etc. Currently for video files, the 'medium' slides only point to the actual video file processed. Future versions will allow for direct playback inside the browser (much like YouTube). There are \*(L"previous\*(R" and \&\*(L"next\*(R" links on each page which let the viewer quickly cycle through each \&\*(L"medium\*(R" image without having to constantly invoke the browser's \*(L"back\*(R" button. .PP Captions for video files can be created by creating a file with the same basename as the original video file but having \*(L".txt\*(R" as the extension. The contents of this file will be used just like captions embedded in formats like \&\s-1JPG\s0 when processing the resultant \s-1HTML\s0. .PP In conjunction with the \*(L"slide show\*(R" view there is a \*(L"frame view\*(R". When enabled a link to the frame view appears at the top of the index view's page. When the frame view is visited, the browser's pane splits into two portions. On the left all the thumbnails are lined up close together. On the right hand portion the same \*(L"slide show\*(R" pages are loaded. As the user clicks on a thumbnail on the left, it's \*(L"slide\*(R" view (including the \*(L"medium\*(R" image) is displayed in the right had side of the frame. This creates a very convenient mechanism for browsing through many images. .PP Finally a \*(L"details\*(R" view exists. This details view is much like the index view as the thumbnails (reduced in size further) are presented in a table format, but much more information is presented in each cell. This is very useful for images that come from digital cameras where Exif headers have filled with lots of neat information. .PP The \s-1HTML\s0 output of imageindex can be customized by creating a \&\*(L".imageindexrc\*(R" in your \f(CW$HOME\fR directory and placing certain variables (see \&\s-1VARIABLES\s0 section) in that file and editing to your taste. One of the variables controls the output of a cascading style sheet which ultimately directs your browser how to render the \s-1HTML\s0. All color, font, indenting, etc. changes you wish to make can be done in this style sheet variable. .SH "VARIABLES" .IX Header "VARIABLES" When you create your \*(L".imageindexrc\*(R" file in your \f(CW$HOME\fR directory, you can put any or all of the following variables in there and tweak as needed. This is Perl code itself and is subsequently \*(L"included\*(R" into imageindex as it runs. .PP You \fBmust\fR end the file by putting a \*(L"1;\*(R" at the end of it. It's a Perl thing! .PP The values you see in these examples are the program defaults. If you like the default value of a particular variable you don't need to include it in your \&\*(L".imageindexrc\*(R" file. .PP Name of the directory that holds thumbnail images .PP .Vb 1 \& $thumbnail_dir = 'thumbnail'; .Ve .PP Size of the thumbnail images in the \*(L"x\*(R" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a thumbnail image. So, if the \*(L"x\*(R" dimension is smaller than the \*(L"y\*(R" dimension, a thumbnail might have an \*(L"x\*(R" size smaller than \&\f(CW$default_thumbnail_x\fR. .PP .Vb 1 \& $default_thumbnail_x = 200; .Ve .PP Size of the thumbnail images in the \*(L"y\*(R" direction (pixels). Again, note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a thumbnail image. So, if the \*(L"y\*(R" dimension is smaller than the \*(L"x\*(R" dimension, a thumbnail might have an \*(L"y\*(R" size smaller than \&\f(CW$default_thumbnail_y\fR. .PP .Vb 1 \& $default_thumbnail_y = 200; .Ve .PP If both dimensions of the original are within this much of the thumbnail dimensions we will skip the thumbnail and just use the original as the \&\*(L"thumbnail.\*(R" .PP .Vb 1 \& $thumbnail_threshold = 1.0; .Ve .PP Size of the \*(L"medium\*(R" images in the \*(L"x\*(R" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a \*(L"medium\*(R" image. So, if the \*(L"x\*(R" dimension is smaller than the \*(L"y\*(R" dimension, a \*(L"medium\*(R" image might have an \*(L"x\*(R" size smaller than \&\f(CW$med_x\fR. .PP .Vb 1 \& $med_x = 800; .Ve .PP Size of the \*(L"medium\*(R" images in the \*(L"y\*(R" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a \*(L"medium\*(R" image. So, if the \*(L"y\*(R" dimension is smaller than the \*(L"x\*(R" dimension, a \*(L"medium\*(R" image might have an \*(L"y\*(R" size smaller than \&\f(CW$med_y\fR. .PP .Vb 1 \& $med_y = 600; .Ve .PP Name of the directory that holds \*(L"medium\*(R" images .PP .Vb 1 \& $med_dir = 'medium'; .Ve .PP If both dimensions of the original are within this much of the \*(L"medium\*(R" dimensions we will skip creating the medium-size format and just use the original. This saves needless creating a \*(L"medium\*(R" image if it's close in size to the original already. .PP .Vb 1 \& $med_threshold = 1.6; .Ve .PP Automatically recurse into subdirectories? Set to 1 to enable. .PP .Vb 1 \& $do_recurse = 0; .Ve .PP Generate \*(L"medium\*(R" images at all? Set to 1 to enable. .PP .Vb 1 \& $do_medium = 1; .Ve .PP Generate the \*(L"slide show\*(R" and frame view? Set to 1 to enable. .PP .Vb 1 \& $do_slide = 1; .Ve .PP Generate the \*(L"details\*(R" view? Set to 1 to enable. .PP .Vb 1 \& $do_captions = 1; .Ve .PP Use/display caption info stored in images? Set to 1 to enable. .PP .Vb 1 \& $do_detail = 1; .Ve .PP Process subdirectories as entries in the normal \*(L"index\*(R" and \*(L"details\*(R" views? Set to 1 to enable. If an entire directory hierarchy has been processed with \&\f(CW$do_montage\fR set to 1 (see below), the montage file for a given directory will be used as the \*(L"thumbnail\*(R" for a subdirectory. .PP .Vb 1 \& $do_dirs = 1; .Ve .PP Create a montage of all the images? When enabled all of the images that are processed are turned into an NxM montage of very small thumbnails in a tiled pattern. The resulting image is shrunk to the \f(CW$default_thumbnail_x\fR x \&\f(CW$default_thumbnail_y\fR dimensions and stored in the \f(CW$thumbnail_dir\fR directory. The size of the tiles grows as the number of images in a directory increase, but can be bounded by variables outlined below. Set to 1 to enable. .PP .Vb 1 \& $do_montage = 1; .Ve .PP Map \s-1ASCII\s0 \*(L"smiley\*(R" patterns embedded within an image's comment into real \&\*(L"emoticon\*(R" images? When enabled the \s-1ASCII\s0 smiley faces such as :) and :\-), the winks ;) and ;\-), and the frowns :( and :\-( are mapped to small \s-1PNG\s0 images that display the emotion conveyed. Set to 1 to enable. .PP .Vb 1 \& $do_emoticons = 1; .Ve .PP Sort timestamps in reverse order. .PP .Vb 1 \& $do_reverse = 1; .Ve .PP Process video files. This relies on a fairly recent version of mplayer being installed on your system. We've tested with up to 0.99.8. The kind of video files that are supported are up to the codecs that are compiled and used with mplayer installation on your system. .PP .Vb 1 \& $do_video_files = 1; .Ve .PP Overlay a small icon into one of the corners of the thumbnail and medium views when processing the first frame of a video file. This gives a \*(L"visual cue\*(R" that the file being represented in your browser is a video file and not a still image. .PP .Vb 1 \& $do_video_thumbnail_icons = 1; .Ve .PP Use the following as a regular expression to identify video files by their extension. For certain technical reasons it was more feasible to rely upon this quick and effective method. If files from your camera (or whatever) end in a different extension, just put that extension here too. .PP .Vb 1 \& $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)'; .Ve .PP If you enable the \*(L"visual cue\*(R" icons for video files mentioned above, the following variable determines which corner it is placed. Acceptable values are: SouthWest, NorthWest, NorthEast, SouthEast (case sensitive!). .PP .Vb 1 \& $video_icon_gravity = 'SouthWest'; .Ve .PP If you enable the \*(L"visual cue\*(R" icons for video files mentioned above, there are two to pick from (currently). Set to 1 (default) for a yellow dot with a 'play' arrow. Set to 2 for a purplish icon of a video camera. More of these will be created in further releases. .PP .Vb 1 \& $video_icon = 1; .Ve .PP The following three variables control what hyperlinks in the \s-1HTML\s0 output \&\*(L"point\*(R" to. They can be set to the following: .PP .Vb 7 \& index : points to the name reference for an image in the index view \& fullsize : points to the actual image itself \& medium : points to the "medium" version of an image \& thumbnail : points to the thumbnail version of an image \& slide : points to the "slide show" HTML page written for an image \& details : points to the name reference for an image in the details \& view .Ve .PP The folling variable controls what the hyperlink for the thumbnail image in the index view points to: .PP .Vb 1 \& $index_linkto = 'slide'; .Ve .PP The folling variable controls what the hyperlink for the thumbnail image in the details view points to: .PP .Vb 1 \& $details_linkto = 'index'; .Ve .PP The folling variable controls what the hyperlink for the \*(L"medium\*(R" image in the slide view points to: .PP .Vb 1 \& $slide_linkto = 'fullsize'; .Ve .PP Default number of columns to use in the index and detail views .PP .Vb 1 \& $default_columns = 3; .Ve .PP Set the orientation of slide frame \- 'horizontal' or 'vertical'. When 'vertical' the browser pane will split vertically with all the thumbnails towards the left. When 'horizontal' the browser pane splits horizontally with the thumbnails arranged in the upper portion .PP .Vb 1 \& $frame_orient = 'vertical'; .Ve .PP The following two variables can be set to any of the following three values: .PP .Vb 4 \& top : put the item in question at the top of the page when rendered \& bottom : put the item in question at the bottom of the page when \& rendered \& none : omit the item from the HTML output .Ve .PP Controls if an image caption (or comment) embedded in the image will be retrieved and written into the \s-1HTML\s0 output. By default it is written above the \&\*(L"medium\*(R" image presented in the \*(L"slide\*(R" view. .PP .Vb 1 \& $slide_caption = 'top'; .Ve .PP Controls if the date/time of an image is written into the \s-1HTML\s0 output. By default it is written below the \*(L"medium\*(R" image presented in the \*(L"slide\*(R" view. .PP .Vb 1 \& $slide_date = 'bottom'; .Ve .PP In the \*(L"detail\*(R" view, the thumbnail images are shrunk to a size smaller than the normal thumbnails (to conserve valueable space). This sets the number of times they are shrunk. By default it is shrunk by a factor of 2. .PP .Vb 1 \& $detailshrink = 2; .Ve .PP The thumbnail and \*(L"medium\*(R" images are written out as \s-1JPEG\s0 files (even if the original images were not \s-1JPEG\s0's). The following two variables control the \&\*(L"quality\*(R" for generated images. The value can range from 0 to 100 where 100 means \*(L"don't lose any quality in favor of file size.\*(R" .PP Adjust the quality of the thumbnails being written out .PP .Vb 1 \& $thumb_quality = 50; .Ve .PP Adjust the quality of the \*(L"medium\*(R" images written out .PP .Vb 1 \& $med_quality = 80; .Ve .PP Adjust the minimum number of tiles that will be found in a montage image. If the number of images in a directory is lower than this value, images will either be repeated or blanks will be inserted (see \f(CW$montage_fill\fR). .PP .Vb 1 \& $montage_min = 4; .Ve .PP Adjust the maximum number of tiles that will be found in a montage. If the number of images in a directory is higher than this number, the montage will be made by \*(L"evenly picking\*(R" \f(CW$montage_max\fR images in the directory and only using those. .PP .Vb 1 \& $montage_max = 36; .Ve .PP Adjust the space between montage images (pixels). .PP .Vb 1 \& $montage_whitespace = 2; .Ve .PP When there is not a \*(L"even\*(R" number of images in a directory and a complete NxM tile montage cannot be formed, images can be used again or empty space can be used. Set to 'repeat' to re-use images and 'blank' to use empty space. .PP .Vb 1 \& $montage_fill = 'blank'; .Ve .PP The following variable controls all aspects of how the \s-1HTML\s0 output is rendered in standards compliant browsers. The contents of this variable will be written out into a cascading style sheet and the properties found within it will govern how the \s-1HTML\s0 is rendered. All color, font, size, alignment, etc. property changes can take place. This might require some knowledge of cascading style sheets. A good primer can be found here: .PP .Vb 1 \& http://www.w3schools.com/css/css_reference.asp .Ve .PP .Vb 2 \& $stylesheet = ' \& body { color: black; background: white; } .Ve .PP .Vb 7 \& /* Fonts in the title */ \& h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif; \& font-size: 200%; font-weight: bold; text-align: center; } \& h2.daterange { font-family: Arial,Helvetica,sans-serif; \& font-size: 125%; text-align: center; } \& h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%; \& text-align: center; } .Ve .PP .Vb 3 \& /* Photo captions & Directory titles */ \& div.caption { font-family: Arial,Helvetica,sans-serif; \& font-size: 100%; font-weight: bold; margin: 1em; } .Ve .PP .Vb 7 \& /* Overall fonts on the index and details page */ \& div.index { font-family: Arial,Helvetica,sans-serif; \& font-size: 80%; } \& div.detail { font-family: Arial,Helvetica,sans-serif; \& font-size: 80%; } \& div.credits { font-family: Arial,Helvetica,sans-serif; \& font-size: 80%; text-align: right; margin: 10px } .Ve .PP .Vb 6 \& /* Table attributes */ \& table.index { background: #ffffff; border: none; \& border-spacing: 8px; } \& td.index { border: none; padding: 3px } \& table.frame { background: #ffffff; border: none } \& td.frame { border: none; padding: 0px } .Ve .PP .Vb 4 \& /* Image attributes */ \& img.index { border: none; } \& img.slide { border: none; } \& img.frame { border: none; } .Ve .PP .Vb 5 \& /* Link attributes */ \& a:link { color: blue; } \& a:visited { color: green; } \& a:hover { color: red; } \& a:active { color: red; } .Ve .PP .Vb 1 \& '; .Ve .PP Adjust what is presented in \*(L"empty\*(R" table cells when there are not an \*(L"even\*(R" number of images in a directory. .PP .Vb 1 \& $emptycell = "empty"; .Ve .PP Control the text of a hyperlink to a parent directory. If you do not desire that this link be present in the index and \*(L"details\*(R" views \*(L"undef\*(R" the variable (undef \f(CW$updirtext\fR;) .PP .Vb 1 \& $updirtext = "up one directory"; .Ve .PP Control the text of a hyperlink to the frame view. If you do not desire that this link be present in the index and \*(L"details\*(R" views \*(L"undef\*(R" the variable (undef \f(CW$framelinktext\fR;) .PP .Vb 1 \& $framelinktext = "slideshow view (frames)"; .Ve .PP Control the text of a hyperlink to the detail view. If you do not desire that this link be present in the index view \*(L"undef\*(R" the variable (undef \f(CW$detaillinktext\fR;) .PP .Vb 1 \& $detaillinktext = "details index"; .Ve .PP Control the text of a hyperlink to the index view. If you do not desire that this link be present in the detail view \*(L"undef\*(R" the variable (undef \f(CW$indexlinktext\fR;) .PP .Vb 1 \& $indexlinktext = "main index"; .Ve .PP Control the default \s-1TITLE\s0 string written out in the \s-1HTML\s0 for a given directory. This is most usually given on a per-directory basis via the command line and \*(L"remembered\*(R" within \s-1META\s0 data inside the index \s-1HTML\s0 file itself. .PP .Vb 1 \& $default_titletext = "Image directory"; .Ve .PP The following five variables control the \s-1TITLE\s0 attribute on anchor constructs in the index and frame views. When \s-1TITLE\s0 attributes are given they are usually rendered as \*(L"tooltip\*(R" bubbles that show text when a cursor hovers and stops over the active link. We use them here to give a visual cue about the image. These variables work much like \fIprintf\fR\|(1) strings. The values that can be interpolated for a given image are: .PP .Vb 6 \& %f => replaced with the filename of the image \& %d => replaced with the date/time of the image (or mtime of the file) \& %s => replaced with the size of the file (in Kb) \& %r => replaced with the resolution (XxY) of the original image \& %c => replaced with the image's caption (if stored with one) \& %% => replaced with a literal '%' character .Ve .PP The following codes are interpolated when directories are processed and a montage of that directory is used as the thumbnail of the subdirectory. .PP .Vb 4 \& %n => replaced with number of images in a directory \& %b => replaced with the "begin" date from a directory of images \& %e => replaced with the "end" date from a directory of images \& %t => replaced with the "title" from a directory of images .Ve .PP Other characters (including spaces) are literal. \*(L"undef\*(R" these in your \&\*(L".imageindexrc\*(R" file if you don't want the \s-1TITLE\s0 attributes to be written into the \s-1HTML\s0. The \*(L"date/time\*(R" related constructs are interpolated using the date/time format variables defined below. .PP Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the frame view. The default is \*(L" \- \*(R" for an image .PP .Vb 1 \& $framethumbtitle = "%f - %d"; .Ve .PP Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the index view. The default is \*(L" ()\*(R" for an image .PP .Vb 1 \& $indexthumbtitle = "%f (%s)"; .Ve .PP Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the slide view. The default is \*(L" ()\*(R" for an image .PP .Vb 1 \& $slidethumbtitle = "%f (%s)"; .Ve .PP Control the \s-1TITLE\s0 attributes for hyperlinks to thumbnail images within the detail view. The default is caption (or comment) of an image if one was embedded within it. .PP .Vb 1 \& $detailthumbtitle = "%c"; .Ve .PP Control the \s-1TITLE\s0 attributes for hyperlinks to montage images within the index view when a subdirectory is being presented. The default is to show how many images the subdirectory had and the date range that is spanned. .PP .Vb 1 \& $montagetitle = "%n images %b through %e"; .Ve .PP The following eight variables control how dates and times are formatted when written into the \s-1HTML\s0. Again we're using \fIprintf\fR\|(1)\-like variables where codes are interpolated according to a user's taste. .PP The definitions of the escape sequences come from the \s-1POSIX\s0 \fIstrftime\fR\|(3) definitions. \s-1NOT\s0 \s-1ALL\s0 of \fIstrftime\fR\|(3) are supported for obvious reasons. .PP .Vb 8 \& %S is replaced by the second as a decimal number (00-60). \& %M is replaced by the minute as a decimal number (00-59). \& %I is replaced by the hour (12-hour clock) as a decimal number (01-12). \& %H is replaced by the hour (24-hour clock) as a decimal number (00-23). \& %p is replaced by national representation of either "ante meridiem" or \& "post meridiem" as appropriate (currently only U.S. "am" or "pm") \& %R is equivalent to "%H:%M" (in *timeformat variables only). \& %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only). .Ve .PP .Vb 7 \& %Y is replaced by the year with century as a decimal number. \& %y is replaced by the year without century as a decimal number (00-99). \& %m is replaced by the month as a decimal number (01-12). \& %d is replaced by the day of the month as a decimal number (01-31). \& %F is equivalent to "%Y-%m-%d" (in *dateformat variables only). \& %D is equivalent to "%m/%d/%y" (in *dateformat variables only). \& %% is replaced by a literal "%". .Ve .PP Control the way the date is formed in the frame view .PP .Vb 1 \& $framedateformat = "%m/%d/%Y"; .Ve .PP Control the way the time is formed in the frame view .PP .Vb 1 \& $frametimeformat = "%r"; .Ve .PP Control the way the date is formed in the index view .PP .Vb 1 \& $indexdateformat = "%m/%d/%Y"; .Ve .PP Control the way the time is formed in the index view .PP .Vb 1 \& $indextimeformat = "%r"; .Ve .PP Control the way the date is formed in the slide view .PP .Vb 1 \& $slidedateformat = "%m/%d/%Y"; .Ve .PP Control the way the time is formed in the slide view .PP .Vb 1 \& $slidetimeformat = "%r"; .Ve .PP Control the way the date is formed in the detail view .PP .Vb 1 \& $detaildateformat = "%m/%d/%Y"; .Ve .PP Control the way the date is formed in the detail view .PP .Vb 1 \& $detailtimeformat = "%I:%M %p"; .Ve .PP Control what the index view's \s-1HTML\s0 filename will be .PP .Vb 1 \& $indexfile = 'index.html'; .Ve .PP Control what the detail view's \s-1HTML\s0 filename will be .PP .Vb 1 \& $detailfile = 'details.html'; .Ve .PP Control what the frame view's \s-1HTML\s0 filename will be .PP .Vb 1 \& $framefile = 'frame.html'; .Ve .PP Control what the slide view's \s-1HTML\s0 filename will be .PP .Vb 1 \& $slidefile = 'slides.html'; .Ve .PP Control the name of the directory where all the \*(L"slide view\*(R" \s-1HTML\s0 files will be deposited (one per image) .PP .Vb 1 \& $slide_dir = 'slides'; .Ve .PP Control the name of the cascading style sheet written out in each directory .PP .Vb 1 \& $stylefile = 'style.css'; .Ve .PP Control the name of the montage image if enabled .PP .Vb 1 \& $montagefile = 'montage.jpg'; .Ve .PP Control the prefix of the emoticon \s-1PNG\s0 image filenames .PP .Vb 1 \& $emoticonprefix = 'ii_'; .Ve .SH "EXAMPLES" .IX Header "EXAMPLES" As an example, suppose you just want to change some date/time format strings. A complete \*(L".imageindexrc\*(R" file in this case would be: .PP .Vb 2 \& $framedateformat = "%F"; \& $frametimeformat = "%R"; .Ve .PP .Vb 2 \& $indexdateformat = "%F"; \& $indextimeformat = "%R"; .Ve .PP .Vb 2 \& $slidedateformat = "%F"; \& $slidetimeformat = "%R"; .Ve .PP .Vb 1 \& $detaildateformat = "%m/%y"; .Ve .PP .Vb 1 \& 1; # don't for get this as the last line in the file! .Ve .SH "ACKNOWLEDGMENTS" .IX Header "ACKNOWLEDGMENTS" We would like to thank Larry Wall, creator of Perl for his \*(L"swiss army chainsaw\*(R" of a scripting language (as well as all those who have hacked on Perl throughout the years). We would also like to thank all who have contributed to ImageMagick and its companion module PerlMagick. Without PerlMagick this software would be exceedingly less robust. Additionally we would like to thank the creators of mplayer (and all contributors). Without mplayer the support introduced for video files would never have come about. .SH "AUTHORS" .IX Header "AUTHORS" .Vb 2 \& Edwin Huffstutler \& John Reynolds .Ve imageindex-1.1.orig/Makefile0000644000175000017500000000143607641727604014155 0ustar abiabi###################################################################### # $Id: Makefile.dist,v 1.3 2003/03/31 03:01:24 edwinh Exp $ # $Name: v1_1 $ ###################################################################### # Where the script binary should go PREFIX ?= /usr/local # Where perl lives PERLPATH = /usr/bin/perl ###################################################################### all: sed install: all install -m 0755 imageindex.sed $(PREFIX)/bin/imageindex install -m 0755 autocaption.sed $(PREFIX)/bin/autocaption install -m 0644 imageindex.1 $(PREFIX)/share/man/man1/imageindex.1 sed: sed -e 's%/usr/bin/perl%$(PERLPATH)%g' imageindex > imageindex.sed sed -e 's%/usr/bin/perl%$(PERLPATH)%g' autocaption > autocaption.sed clean: rm -f imageindex.sed rm -f autocaption.sed imageindex-1.1.orig/README0000644000175000017500000000564610605000554013362 0ustar abiabi _ _ _ (_)_ __ ___ __ _ __ _ ___(_)_ __ __| | _____ __ | | '_ ` _ \ / _` |/ _` |/ _ \ | '_ \ / _` |/ _ \ \/ / | | | | | | | (_| | (_| | __/ | | | | (_| | __/> < |_|_| |_| |_|\__,_|\__, |\___|_|_| |_|\__,_|\___/_/\_\ |___/ ...a digital photo gallery tool README for version 1.1 FEATURES o Index, detail, slide, and frame views o Simple, uncluttered output o Static HTML output for ease of copying/archiving o Uses captions from comments embedded in the image files (utility provided). Captions will never be lost as long as you have the image file itself. o Keeps generated images up to date, removes stale files, only generates needed thumbs, etc. o Digital photo details extracted from EXIF data o Can optionally recurse directory trees and make montage images of directory contents o Easily configurable, can use an rc file. o CSS is used for fonts/styles. o Can handle many image file formats o NEW! Now supports video files (mplayer required for support) o Pages pass W3C specs. REQUIREMENTS o perl 5.003_05 or newer o ImageMagick and the Image::Magick perl module o Optional; needed if you want to see EXIF data from the images - Image::Info from CPAN - which needs IO::String as well o mplayer (at least 0.9.88 or newer) is required if you want support for processing video files QUICK USAGE SUMMARY Please see the comments at the top of the file or run "imageindex -help" to see all of the command line options Examples: imageindex Index the images in the current directory imageindex -noslide -x 200 -y 200 /path/to/images Index the images in specified dir, without slideshow files, and override the configured thumbnail size imageindex -recurse -destdir /tmp/gallery Index images, include subdirs, and export the results to different directory imageindex -caption "Some really cool picture" Embed caption in a file and exit CONFIGURATION Edit any of the variables in configuration section at the top of the script. If you'd like, you can store any of these you'd like to override in a ~/.imageindexrc file. (Use a '1;' as the last line of the file so it can be require-d by the script.) MISCELLANEOUS The "autocaption" script provided is a quick hack to iterate over a directory full of photos, show them to you, and interactively prompt to apply embedded captions. (Captions use the JPEG comment field). This utility will eventually be replaced with something more GUI-based. ABOUT/CREDITS Yes, there are other similar programs available. The ones we saw produced cluttered or (IMHO) ugly output for the most part, relied on some sort of server processing, and didn't handle image comments in a concise manner. We did this just for our own purposes, but hopefully someone else can get some use out of it. Edwin Huffstutler John Reynolds Local Variables: mode: flyspell end: imageindex-1.1.orig/imageindex.spec0000644000175000017500000000141610605000554015457 0ustar abiabiSummary: Photo gallery creation tool Name: imageindex %define version 1.1 Version: %{version} Release: 1 Packager: Edwin Huffstutler Source: %{name}-%{version}.tar.gz Url: http://www.edwinh.org/imageindex/ License: GPL Group: Applications/Graphics BuildRoot: /var/tmp/%{name}-root BuildArch: noarch Requires: perl-Image-Info Requires: perl-IO-String Requires: ImageMagick-perl %description Photo gallery creation tool %prep %setup -q %build make all PREFIX=$RPM_BUILD_ROOT/usr %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/usr/share/man/man1 make install PREFIX=$RPM_BUILD_ROOT/usr %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %{_bindir}/* %doc %{_mandir}/man1/* %doc CHANGES README COPYING TODO imageindex-1.1.orig/TODO0000644000175000017500000000220207727242502013167 0ustar abiabi$Id: TODO,v 1.43 2003/09/09 03:13:06 edwinh Exp $ $Name: v1_1 $ Bugs - Fix embedded emoticons with unicode (pack/unpack is a problem) Additions/Enhancements - Flesh out .pod file. Do pod2man for packaging, etc - Medium-to-Large architecture change. Pre-compute what things need to be done (which slide show .html files need to be created, which thumbs, which mediums, etc.) and then execute these jobs through a for() loop with fork()/exec() keeping N jobs "active" at a time where N is configurable. This will help speed-up on dual CPU machines. - Add more samples in sample directory - Fill out lsm, send to ibiblio.org as part of release? Upload to rh contrib? Ideas (not sure) - Use something like HTML::Stream rather than manually printf'ing tags. - Add a way to dump default config variables into dotfile - Make it use a ~/.imageindex.css file for the stylesheet customization (so you don't have to muck with the perl variable) ??? If this is done also add -writecss flag or something that creates a "default" copy of this for you to edit later? - Drop-shadow option for thumbnails? Bugs/Cleanup imageindex-1.1.orig/CHANGES0000644000175000017500000001136010605001307013460 0ustar abiabi$Id: CHANGES,v 1.30 2007/04/04 19:55:51 edwinh Exp $ $Name: v1_1 $ Version 1.1: - some cameras date stamp using / instead of : for the date fields. fixed. - support for multi-image format files such as animated GIF. The first image in the multi-image deck is used for thumbnails, etc. - Major update providing support for video files. Support comes via having mplayer installed on your system. The first frame of the video file is used to create thumbnails/mediums. Small icons can be overlaid on top of the thumb/medium images to give a visual cue that the object being represented in the browser is actually a video file and not a still image. Default for this behavior is on and can be turned off by setting $do_video_thumbnail_icons to 0. A modern version of mplayer is required for all video processing. - Bug fixed where HTML links back to '../$indexhtml' files were not being made in recursive mode. - Take author page links off of default output (can use imageindex link to find us) Version 1.0.9: - fix space in filenames/dirs problem Version 1.0.8: - $do_captions rc variable to turn on/off - fix obsolete flag to montage Version 1.0.7: - autocaption uses Term::Readline::Gnu - Add "-reverse" to flip the timestamp ordering of the images Saved as metadata if not the default value - Skip .thumbcache - "-rotate" works even if you swap the file/direction spec - misc tiny fixes from john Version 1.0.6: - Support for morphing ASCII "smilies" (i.e. :), ;), and :() embedded in captions found within images over to small "emoticon" PNG images. Enabled by default. Turn off setting $do_emoticons to 0. - Date and Time formatting has been radically improved. New global variables governing this feature are "printf()-like" strings. Codes defined by POSIX strftime(3) are interpolated to allow the date/time fields to completely user-defined. - Support for adding in TITLE attributes onto anchors surrounding thumbnail, montage, and slide images. These TITLE attributes are also defined by "printf()-like" strings and are used to present more information to a user after hovering the mouse over the image (requires that the browser properly renders TITLE attributes as tooltips). - Image captions have been sanitized when embedded in TITLE attributes or within other HTML w.r.t. special characters which might cause parse errors for some rendering engines. - New METADATA attribute 'NumImages' stored in index.html files. This provides a convenient mechanism for knowing the number of images represented in the file without having to stoop to counting, etc. Version 1.0.5: - processing loops for medium/thumbnail views optimized for speed - HTML output is now "prettier" to the eye as it is indented - tweak details view with some  's instead of ' ' to render more efficiently - break out of frames if index link is hit with small javascript snippet. lets us put index link back in slides and still validate strict. - tweak autocaption to allow newline to end comment Version 1.0.4: - added option -includeall which nullifies the "excluded file" list that is saved in metadata (for full archiving purposes vs. external viewing) - tweaked the HTML for comments that end with an ellipsis (...) so they look better in tight spaces when rendered Version 1.0.3: - be able exclude some images from montages - take index link out of slides until we find a better way of handling both frame and non-frame views - minor page compliance tweaks Version 1.0.2: - montages of large (> tile grid) numbers of images now "samples" which pics to use rather than taking the first N images only - fix saving meta / defaults / commandline semantics (only save if different from defaults) - fix :hover so it applies to a only, otherwise cuased mozilla 1.1 wierdness - added quality option for thumbs & medium Version 1.0.1: - fix doctype; add version metatag/flag Version 1.0: - Initial public release Version 0.9.5: - fix up packaging a bit - doctype fixes - montage is working good enough for now Version 0.9.4: - slide caption location/date is changeable - add -d 'var=value' override option - tables, IMG borders use css - start on montage changes - dirs are timestamp sorted - meta date tags stored at yyyymmdd.ss Prior to that: Hacked on and used for a year or more for digital photos. Its fvwm2 background menu roots are much older Local Variables: mode: flyspell end: imageindex-1.1.orig/autocaption0000755000175000017500000000455410520704237014760 0ustar abiabi#!/usr/bin/perl -w # # This is a REAL QUICK HACK to add captions to JPG files. We'll replace it # with something more Rico Suave(tm) as time goes one. # # Use --force to force the program to process all JPG files regardless of # whether an existing comment exists or not. # # Use --oneline to get one line captions, ending when you hit enter. # (won't wait for . at last line to end it) # # $Id: autocaption,v 1.7 2006/10/28 16:43:11 jjreynold Exp $ # $Name: v1_1 $ # use Getopt::Long; use Term::ReadLine; use vars qw($opt_force); $size = '500x400'; $geom = "${size}+5+5"; my $term = new Term::ReadLine 'imageindex autocaption'; my $OUT = $term->OUT || \*STDOUT; &GetOptions('force','oneline') or die ("Invalid flag\n"); if (scalar (@ARGV)) { @files = @ARGV; } else { while ($name = <*.jpg *.JPG *.jpeg *.JPEG>) { push (@files, $name); } } foreach my $name (@files) { printf $OUT ("Processing $name ...\n"); $qname = quotemeta ($name); open (PIPE, "imageindex -caption $qname |") || die; $existing_caption = ''; while () { if (/: "(.*)"\s*$/) { $existing_caption = $1; } } close (PIPE); if ($existing_caption) { printf $OUT ("Existing caption: \"$existing_caption\"\n"); if (! defined ($opt_force)) { printf $OUT ("Skipping this file. Use -force to re-do captions.\n\n"); next; } } else { printf $OUT ("No existing caption found.\n"); } if (!defined($opt_oneline)) { printf $OUT ("Bringing up resized image. Enter a caption with \".\" last line:\n"); printf $OUT ("(entering \".\" will re-use any previous caption)\n"); } else { printf $OUT ("Bringing up resized image. Enter a caption:\n"); printf $OUT ("(blank will re-use any previous caption)\n"); } if ($pid = fork) { my $caption = ""; my $prompt = "$name: "; while (defined ($_ = $term->readline($prompt)) ) { warn $@ if $@; if (defined($opt_oneline)) { $caption .= $_; last; } elsif ($_ =~ /^\.$/) { last; } else { $caption .= " $_"; } } chomp($caption); $term->addhistory($caption); $caption = quotemeta ($caption); if (($caption ne '') and ($caption ne '.')) { printf $OUT ("Executing: imageindex -caption $qname $caption\n"); system ("imageindex -caption $qname $caption"); } kill 15, $pid; } else { exec 'display', '-geometry', $geom, '-resize', $size, $name; } } imageindex-1.1.orig/COPYING0000644000175000017500000004311007531261353013533 0ustar abiabi GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. imageindex-1.1.orig/imageindex.pod0000644000175000017500000005436210603361720015322 0ustar abiabi=head1 NAME imageindex - a digital photo gallery tool =head1 SYNOPSIS Imageindex is a digital picture gallery program. It provides automatic generation of thumbnails and other size views of the images and video files, and W3C compliant HTML to allow viewing of the thumbnails and images or videos. It also creates montages of all images in a given directory to be used in directory entries within the HTML. =head1 DESCRIPTION Imageindex has evolved from a simple thumbnail-generation program into a full-blown gallery application. With it you can create static thumbnails and "medium" views (good for dial-up web viewers) along with static HTML which presents the images in chronological order (based upon the date in the Exif header or date stamp of the file itself). Support has now been added for animated GIFs and video files (we support whatever file formats your installation of mplayer supports). Imageindex creates static rather than dynamic content for many reasons. First and foremost it is much easier to archive pictures onto CD-ROMs on a periodic basis when the content is just "there" rather than "trapped" in a database of some sort. You create your own "database" with your own directories and let imageindex handle the presentation of the images to the world. Imageindex will use "montages" (or thumbnails of all your thumbnails crammed together) of subdirectories if they occur alongside images (see the the sample pages on the imageindex website - http://www.edwinh.org/imageindex/sample.html). There are four basic "views" that imageindex creates (enabled by default). The "index" view shows thumbnails of all images in a directory in a basic table format (defaults to 3 columns). Within each cell of the table basic information such as the date/time of the picture are given as well as any comment (or caption) present in the image. Links to the various sizes of image and other views for the image are also presented by default. The second view presented is the "slide show" view. In this view the "medium" sized image is presented along with information such as date/time, any caption embedded in the image, etc. Currently for video files, the 'medium' slides only point to the actual video file processed. Future versions will allow for direct playback inside the browser (much like YouTube). There are "previous" and "next" links on each page which let the viewer quickly cycle through each "medium" image without having to constantly invoke the browser's "back" button. Captions for video files can be created by creating a file with the same basename as the original video file but having ".txt" as the extension. The contents of this file will be used just like captions embedded in formats like JPG when processing the resultant HTML. In conjunction with the "slide show" view there is a "frame view". When enabled a link to the frame view appears at the top of the index view's page. When the frame view is visited, the browser's pane splits into two portions. On the left all the thumbnails are lined up close together. On the right hand portion the same "slide show" pages are loaded. As the user clicks on a thumbnail on the left, it's "slide" view (including the "medium" image) is displayed in the right had side of the frame. This creates a very convenient mechanism for browsing through many images. Finally a "details" view exists. This details view is much like the index view as the thumbnails (reduced in size further) are presented in a table format, but much more information is presented in each cell. This is very useful for images that come from digital cameras where Exif headers have filled with lots of neat information. The HTML output of imageindex can be customized by creating a ".imageindexrc" in your $HOME directory and placing certain variables (see VARIABLES section) in that file and editing to your taste. One of the variables controls the output of a cascading style sheet which ultimately directs your browser how to render the HTML. All color, font, indenting, etc. changes you wish to make can be done in this style sheet variable. =head1 VARIABLES When you create your ".imageindexrc" file in your $HOME directory, you can put any or all of the following variables in there and tweak as needed. This is Perl code itself and is subsequently "included" into imageindex as it runs. You B end the file by putting a "1;" at the end of it. It's a Perl thing! The values you see in these examples are the program defaults. If you like the default value of a particular variable you don't need to include it in your ".imageindexrc" file. Name of the directory that holds thumbnail images $thumbnail_dir = 'thumbnail'; Size of the thumbnail images in the "x" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a thumbnail image. So, if the "x" dimension is smaller than the "y" dimension, a thumbnail might have an "x" size smaller than $default_thumbnail_x. $default_thumbnail_x = 200; Size of the thumbnail images in the "y" direction (pixels). Again, note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a thumbnail image. So, if the "y" dimension is smaller than the "x" dimension, a thumbnail might have an "y" size smaller than $default_thumbnail_y. $default_thumbnail_y = 200; If both dimensions of the original are within this much of the thumbnail dimensions we will skip the thumbnail and just use the original as the "thumbnail." $thumbnail_threshold = 1.0; Size of the "medium" images in the "x" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a "medium" image. So, if the "x" dimension is smaller than the "y" dimension, a "medium" image might have an "x" size smaller than $med_x. $med_x = 800; Size of the "medium" images in the "y" direction (pixels). Note that imageindex preserves the aspect ratio of an image when it is reduced from its original size to form a "medium" image. So, if the "y" dimension is smaller than the "x" dimension, a "medium" image might have an "y" size smaller than $med_y. $med_y = 600; Name of the directory that holds "medium" images $med_dir = 'medium'; If both dimensions of the original are within this much of the "medium" dimensions we will skip creating the medium-size format and just use the original. This saves needless creating a "medium" image if it's close in size to the original already. $med_threshold = 1.6; Automatically recurse into subdirectories? Set to 1 to enable. $do_recurse = 0; Generate "medium" images at all? Set to 1 to enable. $do_medium = 1; Generate the "slide show" and frame view? Set to 1 to enable. $do_slide = 1; Generate the "details" view? Set to 1 to enable. $do_captions = 1; Use/display caption info stored in images? Set to 1 to enable. $do_detail = 1; Process subdirectories as entries in the normal "index" and "details" views? Set to 1 to enable. If an entire directory hierarchy has been processed with $do_montage set to 1 (see below), the montage file for a given directory will be used as the "thumbnail" for a subdirectory. $do_dirs = 1; Create a montage of all the images? When enabled all of the images that are processed are turned into an NxM montage of very small thumbnails in a tiled pattern. The resulting image is shrunk to the $default_thumbnail_x x $default_thumbnail_y dimensions and stored in the $thumbnail_dir directory. The size of the tiles grows as the number of images in a directory increase, but can be bounded by variables outlined below. Set to 1 to enable. $do_montage = 1; Map ASCII "smiley" patterns embedded within an image's comment into real "emoticon" images? When enabled the ASCII smiley faces such as :) and :-), the winks ;) and ;-), and the frowns :( and :-( are mapped to small PNG images that display the emotion conveyed. Set to 1 to enable. $do_emoticons = 1; Sort timestamps in reverse order. $do_reverse = 1; Process video files. This relies on a fairly recent version of mplayer being installed on your system. We've tested with up to 0.99.8. The kind of video files that are supported are up to the codecs that are compiled and used with mplayer installation on your system. $do_video_files = 1; Overlay a small icon into one of the corners of the thumbnail and medium views when processing the first frame of a video file. This gives a "visual cue" that the file being represented in your browser is a video file and not a still image. $do_video_thumbnail_icons = 1; Use the following as a regular expression to identify video files by their extension. For certain technical reasons it was more feasible to rely upon this quick and effective method. If files from your camera (or whatever) end in a different extension, just put that extension here too. $video_regexp = '(avi|mov|mpg|mpeg|mjpeg|m1v|m2v|wmv|fli|nuv|vob|ogm|vcd|svcd|mp4|qt)'; If you enable the "visual cue" icons for video files mentioned above, the following variable determines which corner it is placed. Acceptable values are: SouthWest, NorthWest, NorthEast, SouthEast (case sensitive!). $video_icon_gravity = 'SouthWest'; If you enable the "visual cue" icons for video files mentioned above, there are two to pick from (currently). Set to 1 (default) for a yellow dot with a 'play' arrow. Set to 2 for a purplish icon of a video camera. More of these will be created in further releases. $video_icon = 1; The following three variables control what hyperlinks in the HTML output "point" to. They can be set to the following: index : points to the name reference for an image in the index view fullsize : points to the actual image itself medium : points to the "medium" version of an image thumbnail : points to the thumbnail version of an image slide : points to the "slide show" HTML page written for an image details : points to the name reference for an image in the details view The folling variable controls what the hyperlink for the thumbnail image in the index view points to: $index_linkto = 'slide'; The folling variable controls what the hyperlink for the thumbnail image in the details view points to: $details_linkto = 'index'; The folling variable controls what the hyperlink for the "medium" image in the slide view points to: $slide_linkto = 'fullsize'; Default number of columns to use in the index and detail views $default_columns = 3; Set the orientation of slide frame - 'horizontal' or 'vertical'. When 'vertical' the browser pane will split vertically with all the thumbnails towards the left. When 'horizontal' the browser pane splits horizontally with the thumbnails arranged in the upper portion $frame_orient = 'vertical'; The following two variables can be set to any of the following three values: top : put the item in question at the top of the page when rendered bottom : put the item in question at the bottom of the page when rendered none : omit the item from the HTML output Controls if an image caption (or comment) embedded in the image will be retrieved and written into the HTML output. By default it is written above the "medium" image presented in the "slide" view. $slide_caption = 'top'; Controls if the date/time of an image is written into the HTML output. By default it is written below the "medium" image presented in the "slide" view. $slide_date = 'bottom'; In the "detail" view, the thumbnail images are shrunk to a size smaller than the normal thumbnails (to conserve valueable space). This sets the number of times they are shrunk. By default it is shrunk by a factor of 2. $detailshrink = 2; The thumbnail and "medium" images are written out as JPEG files (even if the original images were not JPEG's). The following two variables control the "quality" for generated images. The value can range from 0 to 100 where 100 means "don't lose any quality in favor of file size." Adjust the quality of the thumbnails being written out $thumb_quality = 50; Adjust the quality of the "medium" images written out $med_quality = 80; Adjust the minimum number of tiles that will be found in a montage image. If the number of images in a directory is lower than this value, images will either be repeated or blanks will be inserted (see $montage_fill). $montage_min = 4; Adjust the maximum number of tiles that will be found in a montage. If the number of images in a directory is higher than this number, the montage will be made by "evenly picking" $montage_max images in the directory and only using those. $montage_max = 36; Adjust the space between montage images (pixels). $montage_whitespace = 2; When there is not a "even" number of images in a directory and a complete NxM tile montage cannot be formed, images can be used again or empty space can be used. Set to 'repeat' to re-use images and 'blank' to use empty space. $montage_fill = 'blank'; The following variable controls all aspects of how the HTML output is rendered in standards compliant browsers. The contents of this variable will be written out into a cascading style sheet and the properties found within it will govern how the HTML is rendered. All color, font, size, alignment, etc. property changes can take place. This might require some knowledge of cascading style sheets. A good primer can be found here: http://www.w3schools.com/css/css_reference.asp $stylesheet = ' body { color: black; background: white; } /* Fonts in the title */ h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif; font-size: 200%; font-weight: bold; text-align: center; } h2.daterange { font-family: Arial,Helvetica,sans-serif; font-size: 125%; text-align: center; } h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%; text-align: center; } /* Photo captions & Directory titles */ div.caption { font-family: Arial,Helvetica,sans-serif; font-size: 100%; font-weight: bold; margin: 1em; } /* Overall fonts on the index and details page */ div.index { font-family: Arial,Helvetica,sans-serif; font-size: 80%; } div.detail { font-family: Arial,Helvetica,sans-serif; font-size: 80%; } div.credits { font-family: Arial,Helvetica,sans-serif; font-size: 80%; text-align: right; margin: 10px } /* Table attributes */ table.index { background: #ffffff; border: none; border-spacing: 8px; } td.index { border: none; padding: 3px } table.frame { background: #ffffff; border: none } td.frame { border: none; padding: 0px } /* Image attributes */ img.index { border: none; } img.slide { border: none; } img.frame { border: none; } /* Link attributes */ a:link { color: blue; } a:visited { color: green; } a:hover { color: red; } a:active { color: red; } '; Adjust what is presented in "empty" table cells when there are not an "even" number of images in a directory. $emptycell = "empty"; Control the text of a hyperlink to a parent directory. If you do not desire that this link be present in the index and "details" views "undef" the variable (undef $updirtext;) $updirtext = "up one directory"; Control the text of a hyperlink to the frame view. If you do not desire that this link be present in the index and "details" views "undef" the variable (undef $framelinktext;) $framelinktext = "slideshow view (frames)"; Control the text of a hyperlink to the detail view. If you do not desire that this link be present in the index view "undef" the variable (undef $detaillinktext;) $detaillinktext = "details index"; Control the text of a hyperlink to the index view. If you do not desire that this link be present in the detail view "undef" the variable (undef $indexlinktext;) $indexlinktext = "main index"; Control the default TITLE string written out in the HTML for a given directory. This is most usually given on a per-directory basis via the command line and "remembered" within META data inside the index HTML file itself. $default_titletext = "Image directory"; The following five variables control the TITLE attribute on anchor constructs in the index and frame views. When TITLE attributes are given they are usually rendered as "tooltip" bubbles that show text when a cursor hovers and stops over the active link. We use them here to give a visual cue about the image. These variables work much like printf(1) strings. The values that can be interpolated for a given image are: %f => replaced with the filename of the image %d => replaced with the date/time of the image (or mtime of the file) %s => replaced with the size of the file (in Kb) %r => replaced with the resolution (XxY) of the original image %c => replaced with the image's caption (if stored with one) %% => replaced with a literal '%' character The following codes are interpolated when directories are processed and a montage of that directory is used as the thumbnail of the subdirectory. %n => replaced with number of images in a directory %b => replaced with the "begin" date from a directory of images %e => replaced with the "end" date from a directory of images %t => replaced with the "title" from a directory of images Other characters (including spaces) are literal. "undef" these in your ".imageindexrc" file if you don't want the TITLE attributes to be written into the HTML. The "date/time" related constructs are interpolated using the date/time format variables defined below. Control the TITLE attributes for hyperlinks to thumbnail images within the frame view. The default is " - " for an image $framethumbtitle = "%f - %d"; Control the TITLE attributes for hyperlinks to thumbnail images within the index view. The default is " ()" for an image $indexthumbtitle = "%f (%s)"; Control the TITLE attributes for hyperlinks to thumbnail images within the slide view. The default is " ()" for an image $slidethumbtitle = "%f (%s)"; Control the TITLE attributes for hyperlinks to thumbnail images within the detail view. The default is caption (or comment) of an image if one was embedded within it. $detailthumbtitle = "%c"; Control the TITLE attributes for hyperlinks to montage images within the index view when a subdirectory is being presented. The default is to show how many images the subdirectory had and the date range that is spanned. $montagetitle = "%n images %b through %e"; The following eight variables control how dates and times are formatted when written into the HTML. Again we're using printf(1)-like variables where codes are interpolated according to a user's taste. The definitions of the escape sequences come from the POSIX strftime(3) definitions. NOT ALL of strftime(3) are supported for obvious reasons. %S is replaced by the second as a decimal number (00-60). %M is replaced by the minute as a decimal number (00-59). %I is replaced by the hour (12-hour clock) as a decimal number (01-12). %H is replaced by the hour (24-hour clock) as a decimal number (00-23). %p is replaced by national representation of either "ante meridiem" or "post meridiem" as appropriate (currently only U.S. "am" or "pm") %R is equivalent to "%H:%M" (in *timeformat variables only). %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only). %Y is replaced by the year with century as a decimal number. %y is replaced by the year without century as a decimal number (00-99). %m is replaced by the month as a decimal number (01-12). %d is replaced by the day of the month as a decimal number (01-31). %F is equivalent to "%Y-%m-%d" (in *dateformat variables only). %D is equivalent to "%m/%d/%y" (in *dateformat variables only). %% is replaced by a literal "%". Control the way the date is formed in the frame view $framedateformat = "%m/%d/%Y"; Control the way the time is formed in the frame view $frametimeformat = "%r"; Control the way the date is formed in the index view $indexdateformat = "%m/%d/%Y"; Control the way the time is formed in the index view $indextimeformat = "%r"; Control the way the date is formed in the slide view $slidedateformat = "%m/%d/%Y"; Control the way the time is formed in the slide view $slidetimeformat = "%r"; Control the way the date is formed in the detail view $detaildateformat = "%m/%d/%Y"; Control the way the date is formed in the detail view $detailtimeformat = "%I:%M %p"; Control what the index view's HTML filename will be $indexfile = 'index.html'; Control what the detail view's HTML filename will be $detailfile = 'details.html'; Control what the frame view's HTML filename will be $framefile = 'frame.html'; Control what the slide view's HTML filename will be $slidefile = 'slides.html'; Control the name of the directory where all the "slide view" HTML files will be deposited (one per image) $slide_dir = 'slides'; Control the name of the cascading style sheet written out in each directory $stylefile = 'style.css'; Control the name of the montage image if enabled $montagefile = 'montage.jpg'; Control the prefix of the emoticon PNG image filenames $emoticonprefix = 'ii_'; =head1 EXAMPLES As an example, suppose you just want to change some date/time format strings. A complete ".imageindexrc" file in this case would be: $framedateformat = "%F"; $frametimeformat = "%R"; $indexdateformat = "%F"; $indextimeformat = "%R"; $slidedateformat = "%F"; $slidetimeformat = "%R"; $detaildateformat = "%m/%y"; 1; # don't for get this as the last line in the file! =head1 ACKNOWLEDGMENTS We would like to thank Larry Wall, creator of Perl for his "swiss army chainsaw" of a scripting language (as well as all those who have hacked on Perl throughout the years). We would also like to thank all who have contributed to ImageMagick and its companion module PerlMagick. Without PerlMagick this software would be exceedingly less robust. Additionally we would like to thank the creators of mplayer (and all contributors). Without mplayer the support introduced for video files would never have come about. =head1 AUTHORS Edwin Huffstutler John Reynolds