#!/usr/bin/perl -w # jigl - Jason's Image Gallery # #################### # GNU General Public License # # 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 # #################### # Author: Jason Paul use Getopt::Long; # options parsing use Text::ParseWords; # for converting the config options into an array use File::Copy; # for the copy and move functions use File::Path; # for the rmtree function use Cwd; # directory functions use DirHandle; # for recursive dir handling use POSIX qw(strftime); # for proper localtime handling ###################### ### setup some variables ###################### my $version = "v2.0.1"; my $author = "Jason Paul"; my $progName = $0; my $jpgTypeStr = "*.[jJ][pP][gG] *.[jJ][pP][eE][gG]"; my $gifTypeStr = "*.[gG][iI][fF]"; my $pngTypeStr = "*.[pP][nN][gG]"; my $fileTypeStr = "$jpgTypeStr $gifTypeStr $pngTypeStr"; my $jiglRCDir = "$ENV{HOME}/.jigl"; # dir for global themes/configs my $jiglRCFile = $jiglRCDir . "/jigl.opts"; # name of users global config file my $gblThemeDir = $jiglRCDir . "/themes"; # global name of dir for themes my $galDatFile = "gallery.dat"; # name of gallery data file my $themeDir = "theme"; # dir for optional theme files my $thumbDir = "thumbs"; # thumbnail directory my $thumbPrefix = "$thumbDir/thumb_"; # thumbnail prefix my $slideDir = "slides"; # slide directory my $slidePrefix = "$slideDir/slide_"; # slide prefix my $exifProg = "jhead"; # program to extract exif info my $imgInfoProg = "identify"; # program to display image info my $scaleProg = "convert"; # program to resize images my $waterMarkProg = &getWatermarkProg; # program to watermark images my $indexPrefix = "index"; # name of index file my $indexExt = ".html"; # extension for the index file my $indexTmpl = "index_template"; # name of index template file my $slideTmpl = "slide_template"; # name of slide template file my $infoTmpl = "info_template"; # name of info template file my $gblIndexTmpl = $gblThemeDir . "/default/" . $indexTmpl; # global index template my $gblSlideTmpl = $gblThemeDir . "/default/" . $slideTmpl; # global slide template my $gblInfoTmpl = $gblThemeDir . "/default/" . $infoTmpl; # global info template my @imgMgkVer = &checkSiteInstall; # check for img tools; return ImageMagick ver ###################### ### end variable setup ###################### # Get the list of directories we're going to be running the # program on. my @dirs = &getDirs; # Initial system-wide options to the defaults # This needs to be done first. my %optsSys = &setSystemOpts; # check to see if the user has a resource file in their home dir. # If they do get these options here. my %optsRC = &getRCOpts; # check the validity of the options (i.e. bounds check) %optsRC = &checkOpts(\%optsRC, \%optsSys); # get the command line options. print "Checking the command line options.\n"; my %optsCmd = &getCmdOpts({}); # check the validity of the options (i.e. bounds check) %optsCmd = &checkOpts(\%optsCmd, \%optsSys); # save the starting dir so we know how to get back my $startDir = cwd; # check if the recursive option was used on the command line # this option will only be recognized on the command line if ($optsCmd{r}) { @rDirs = (); # recurse through each dir that was passed to us on the cmd line foreach $dir (@dirs) { push @rDirs,$dir; push @rDirs,&recursiveDirList($dir,()); } @dirs = @rDirs; } # run on each directory given on the cmdLine. foreach $dir (@dirs) { # skip any directory that is not valid if (! (-d $dir)) { for (my $i=0;$i<30;$i++) {print "-"}; print "\n\'$dir\' is not a valid directory. Skipping!\n"; next; } die "Can't cd to $dir: $!\n" unless chdir $dir; for (my $i=0;$i<30;$i++) {print "-"}; print "\nProcessing directory \'$dir\'\n\n"; # get any options from this dirs gallery.dat file my %optsGal = &getGalOpts; # check the validity of the options (i.e. bounds check) %optsGal = &checkOpts(\%optsGal, \%optsSys); # the final options hash. my %opts = mergeOpts(\%optsSys,\%optsRC,\%optsGal,\%optsCmd); print "Checking the final options for validity\n"; %opts = &checkOpts(\%opts, \%optsSys); # skip this directory if it's the output dir. We have to wait untill here # to check this because we have to wait for all the options to be processed # and merged first. if ($dir =~ /\/$opts{wd}/) { print "Skipping this directory because it's the output directory\n"; die "Can't cd to $startDir $!\n" unless chdir $startDir; next; } # was the -h or --help option called? &printUsage if $opts{h}; # was the -v or --version option called? &printVersion if $opts{v}; # generate the albumInfo array my $albumInfo = &genAlbumInfo(\%opts,$dir); # reset the titlebar/title for the gallery if the -it option is used $albumInfo->{titlebar} = $opts{it} if ($opts{it} ne ""); $albumInfo->{title} = $opts{it} if ($opts{it} ne ""); if (($#{$albumInfo->{images}} == -1) && (!($opts{cg}))) { print "No image files were found in \'$dir\'\nSkipping directory.\n"; } elsif (!($opts{cg})) { # generate thumbnails &genThumbs(\%opts,$albumInfo); # generate slides &genSlides(\%opts,$albumInfo) if $opts{gs} or $opts{gi}; # remove any existing template files from local gallery and # remove the global default theme so it can be regenerated &removeFiles(($indexTmpl,$slideTmpl,$infoTmpl,"$gblThemeDir/$optsSys{theme}")) if $opts{ut}; # remove any existing html files to prevent stale files my @fileList = glob($indexPrefix . "*" . $indexExt); push @fileList,glob("[0-9]*" . $indexExt); &removeFiles(@fileList); # read in theme and store as a hash %theme = &readTheme(\%opts); print "Using theme '$opts{theme}' for html pages\n"; # generate slide pages &genSlidePages(\%opts,\%theme,$albumInfo) if $opts{gs}; # generate info page &genInfoPages(\%opts,\%theme,$albumInfo) if $opts{gi}; # generate the index page(s) for $page (0 .. $albumInfo->{numPages}) { # figure out which thumbnails we're going to list on this page $startIndex = $page * ($opts{iw} * $opts{ir}); $endIndex = (($opts{iw} * $opts{ir}) * ($page + 1)) - 1; if (($endIndex >= $#{$albumInfo->{images}}) or $endIndex == -1) { $endIndex = $#{$albumInfo->{images}}; } &genIndexPage(\%opts,\%theme,$albumInfo,$page,$startIndex,$endIndex); } # Copy any necessary theme files to the local themeDir # first remove the current themeDir so it doesn't get stale files rmtree("$themeDir",0,0) if -d $themeDir; # flag to let us know if any files need to be copied my $filesCopied = 0; # open the theme dir for reading opendir GBLTHEMEDIR, "$gblThemeDir/$opts{theme}" or die "Cannot open $gblThemeDir/$opts{theme}: $!\n"; # get the list of files from that dir (except for . and ..) # prepend the theme dir back on to the filename as well my @themeFileList = map "$gblThemeDir/$opts{theme}/$_", grep !/^\.\.?$/, readdir GBLTHEMEDIR; # close the theme dir closedir GBLTHEMEDIR; # see if we need to copy any files foreach $file (@themeFileList) { # We don't want to copy the standard theme files or any directories # They're not needed if (!-d $file && !($file =~ /$indexTmpl/) && !($file =~ /$slideTmpl/) && !($file =~ /$infoTmpl/) && !($file =~ /$opts{theme}\.theme/)) { # make sure the themeDir is created. if (!-d $themeDir) { $dieMsg = "Cannot create the directory '$themeDir'.\n"; mkdir "$themeDir",0755 or die "$dieMsg : $!\n"; } print "Copying the necessary theme files\n" if !$filesCopied; $filesCopied = 1; # we only want to print above message once # copy the file to the themeDir $dieMsg="Cannot copy $file to $themeDir\n"; copy "$file","$themeDir" or die "$dieMsg: $!\n"; } } # copy the thumb,slide and theme dir to the web-dir. # we won't do this if the web-dir is "." or the -uo or -lo opts are set if ($opts{wd} ne "." && !$opts{uo} && !$opts{lo}) { # remove the web-dir first to prevent stale files if (-d $opts{wd}) { rmtree("$opts{wd}",0,0) } # create the output directory for the gallery if need be $dieMsg = "Cannot create the directory '$opts{wd}'.\n"; mkdir "$opts{wd}",0755 or die "$dieMsg : $!\n"; print "Copying the files to '$opts{wd}'\n"; foreach $dirToCopy ($thumbDir, $slideDir, $themeDir) { # if we need to copy that directory, do so if (-d $dirToCopy) { # make the new dir $dieMsg="Cannot create the directory '$opts{wd}/$dirToCopy'.\n"; mkdir "$opts{wd}/$dirToCopy",0755 or die "$dieMsg : $!\n"; # copy each file from that dir foreach $file (glob "$dirToCopy/*") { $dieMsg="Cannot copy $file to $opts{wd}/$dirToCopy\n"; copy $file,"$opts{wd}/$dirToCopy" or die "$dieMsg: $!\n"; } } } # move all the html files foreach $file (glob "*$indexExt") { $dieMsg = "Cannot move $file to $opts{wd}/$file\n"; move "$file","$opts{wd}/$file" or die "$dieMsg $!\n"; } } } # debug - print out the images and descriptions. if ($opts{d} >= 5) { print "--------------------------\n"; print "Album Title Bar: $albumInfo->{titlebar}\n"; print "Album Title: $albumInfo->{title}\n"; print "Album Header: $albumInfo->{header}\n"; print "Album Footer: $albumInfo->{footer}\n"; print "Album Index Files: $albumInfo->{numPages}\n"; for $i (0 .. $#{$albumInfo->{images}}) { print "--------------------------\n"; print "images[$i]{file}: <$albumInfo->{images}[$i]->{file}>\n"; print "images[$i]{desc}: <$albumInfo->{images}[$i]->{desc}>\n"; print "images[$i]{size}: <$albumInfo->{images}[$i]->{size}>\n"; print "images[$i]{width}: <$albumInfo->{images}[$i]->{width}>\n"; print "images[$i]{height}: <$albumInfo->{images}[$i]->{height}>\n"; print "images[$i]{thumb}: <$albumInfo->{images}[$i]->{thumb}>\n"; print "images[$i]{thumbx}: <$albumInfo->{images}[$i]->{thumbx}>\n"; print "images[$i]{thumby}: <$albumInfo->{images}[$i]->{thumby}>\n"; print "images[$i]{slide}: <$albumInfo->{images}[$i]->{slide}>\n"; print "images[$i]{slidex}: <$albumInfo->{images}[$i]->{slidex}>\n"; print "images[$i]{slidey}: <$albumInfo->{images}[$i]->{slidey}>\n"; print "images[$i]{slidekb}: <$albumInfo->{images}[$i]->{slidekb}>\n"; if ($#{$albumInfo->{images}[$i]->{exif}} > 0) { for $j (0 .. $#{$albumInfo->{images}[$i]->{exif}}) { print " $j: $albumInfo->{images}[$i]->{exif}->[$j]{field} : "; print " $albumInfo->{images}[$i]->{exif}->[$j]{val}\n"; } } } } # change back to our starting dir. die "Can't cd to $startDir $!\n" unless chdir $startDir; } print "\n$progName has finished processing\n"; exit 0; ########################### [ Functions below ] ############################### # checkSiteInstall - check to make sure the site is properly configured to work # with jigl. # # Checks for the environment variable HOME. # Checks to see if ImageMagick is installed. # # returns: The version of ImageMagick # # Sets the array reference to be the version of ImageMagick we have. # There is output differences in the 'identify' program we need to consider # which is why we need this. # sub checkSiteInstall { # check to see if the $HOME environment variable is set. # exit with an ERROR if it's not. if (!(defined $ENV{HOME})) { print "ERROR: The HOME environment variable has not been set!\n"; print " Please set this so jigl knows where to check for default settings.\n\n"; exit 1; } # check to see if ImageMagick is installed # exit with an error if it can't be found. my $tmp = qx/$imgInfoProg/; # run the 'identify' prog. if ($? == -1) { print "ERROR: ImageMagick could not be found!.\n"; print " Please make sure the ImageMagick tools are in your path!\n\n"; exit 1; } # pull out the version field from a valid response my @tmpArr = split /\n/,$tmp; # split on new line chop @tmpArr; # chop any remaining new line my @lineArr = split / /,$tmpArr[0]; # split up the first line of the output return split /[.]/,$lineArr[2]; # split and return the version field X.y.z } ########################################################### # getWatermarkProg - determine what program we have that can watermark. # older versions of ImageMagick use the program 'combine'. Newer versions # use 'composite'. We'll check and see which one you have installed. # sub getWatermarkProg { my $prog = ""; # check for composte first if (open CHECK, "composite 2>&1|") { if ( =~ /imagemagick/i) { $prog = "composite"; } close CHECK; } elsif (open CHECK, "combine 2>&1|") { if ( =~ /imagemagick/i) { $prog = "combine"; } close CHECK; } if ($prog eq "") { $prog = "none";} return $prog; } ########################################################### # getDirs - get the list of directories we are going to run the program on. # # If no directories are given on the command line then we assume the # current directory. # # Returns a list. # sub getDirs { # save the ARGV option because the GetOptions function will modify # it and we need it intact for later. my @tmpArgv = @ARGV; my @tmpDirs = (); # call getCmdOpts, but we don't want the output, just the resulting # ARGV list. &getCmdOpts ({}); # check to see if any dirs were on the cmdline or not. if ($#ARGV == -1) { @tmpDirs = ("."); } else { @tmpDirs = @ARGV; } # retrore the orginal args @ARGV = @tmpArgv; return @tmpDirs; } ########################################################### # recursiveDirList - return an array of all the sub directories of # the passed in argument. # # arguments: # optsSys - default system options # rd - root directory to recurse on. # dl - an empty list used to store the resultant directory list. # # returns a list of the sub directories. # The initial directory is NOT included in the result. # This function also omits the following directories: # ., .., slides, thumbs, theme and web. # # sub recursiveDirList { my ($rd,@dl) = @_; # open this directory my $d = new DirHandle $rd; if (defined $d) { # while the directory can be read while (defined($_ = $d->read)) { # we want all dirs except ., .., slides, thumbs, theme and web. if ((-d "$rd\/$_") && $_ ne "." && $_ ne ".." && $_ ne $slideDir && $_ ne $thumbDir && $_ ne $themeDir && $_ ne $optsSys{wd}) { push @dl,"$rd\/$_"; # add this dir to our list push @dl,&recursiveDirList("$rd\/$_",()); # check for subdirs } } } return @dl; } ########################################################### # setSystemOpts - setup the system-wide option defaults # # returns a hash which contains all the option and value pairs # the key values MUST be the same as the short option name!! # sub setSystemOpts { return ("cg"=>"0", # create-gallerydat "uec"=>"0", # use-exif-comment "rs"=>"a", # replace-spaces (default ask) "ut"=>"0", # update-templates "ft"=>"0", # force-thumb "fs"=>"0", # force-slide "it"=>"", # index-title "iw"=>"5", # index-width "ir"=>"0", # index-row "skb"=>"1", # slide-kbsize "sxy"=>"1", # slide-xysize "uo"=>"0", # use-original "lo"=>"0", # link-original "aro"=>"0", # auto-rotate-originals "gs"=>"1", # generate-slides "gi"=>"1", # generate-info "sy"=>"480", # slideY "ty"=>"75", # thumbY "iy"=>"240", # infoY "ws"=>"0", # watermark-slide "wf"=>"watermark.png", # watermark-file "wg"=>"southeast", # watermark-gravity "gb"=>"0", # go-back "gbs"=>"Go Back", # go-back-string "gburl"=>"../index.html", # go-back-url "theme"=>"default", # theme "wd"=>"web", # web-dir "r"=> "0", # recurse "h"=> "0", # display help "v"=> "0", # display version "d"=> "0" # debug level ); } ########################################################### # getRCOpts - get any options from the users jigl.opts file. # There may be comments in the file (line starting with '#') # Only the last line with options on it will be used. # # returns a hash sub getRCOpts { # check to see if the rc file exists. if (-e $jiglRCFile) { print "Found \'$jiglRCFile\' - Parsing options.\n"; } else { print "No \'$jiglRCFile\' resource file found.\n"; return (); } open RCFILE, "<$jiglRCFile" or die "Cannot open \'$jiglRCFile\'\n"; # store the argv array temporarily my @tmpARGV = @ARGV; # get the options from the file and run them through the command line # option checker the ensure validity. my $line = ""; while () { if ($_ =~ /^#/) { # skip - line is not an option } elsif ($_ =~ /^[ ]*$/) { # skip - line is blank } else { $line = $_; } } close RCFILE; # close the rc file chop $line; # strip off that pesky newline @ARGV = &shellwords($line); # move out options in the ARGV variable my %opts = getCmdOpts({}); # retrun a nice hash of options @ARGV = @tmpARGV; # restore the original ARGV array. # return the options hash for the RC file return %opts; } ########################################################### # cgetGalOpts - get any options that are stored in gallery.dat # Any line that starts with "GAL-OPTIONS" is considered an option argument # and will be used. # # returns a hash sub getGalOpts { # check to see if the gallery.dat file exists. if (-e $galDatFile) { print "Found \'$galDatFile\' - Parsing options.\n"; } else { print "No \'$galDatFile\' resource file found.\n"; return (); } open DATFILE, "<$galDatFile" or die "Cannot open \'$galDatFile\'\n"; # store the argv array temporarily my @tmpARGV = @ARGV; # get the options from the file and run them through the command line # option checker the ensure validity. @tmpArr = (); # temporary array while () { # build up an array of args found in the gallery.dat file if ($_ =~ /^GAL-OPTIONS/) { chop $_; # strip off that pesky newline my @tmpOpt = &shellwords($_); # make array of the options line shift @tmpOpt; # remove the tag from the array push @tmpArr,@tmpOpt; # add our new option to the array } } close DATFILE; # close the gallery.dat file @ARGV = @tmpArr; # move our options to the ARGV variable my %opts = getCmdOpts({}); # retrun a nice hash of options @ARGV = @tmpARGV; # restore the original ARGV array. # return the options hash for the gallery.dat file return %opts; } ########################################################### # getCmdOpts - get the command line options # # uses the system default hash to populate the command line hash. Any new # options that were passed from the command line will be updated here. # returns a new hash that contains all the options for the program. # # When the procedure is done the value of @ARGV will have changed. All of # the options will have been removed leaving just the remaining, non-options, # left. In our case these will be the directories we want to run the program # on. # sub getCmdOpts { # populate optsCmd with the hash passed in. This way we don't have to # predefine all the keys for the hash again. my (%opts) = %{$_[0]}; # get the options and store them in the optsCmd hash. die unless GetOptions(\%opts, 'cg|create-gallerydat', 'uec|use-exif-comment!', 'rs|replace-spaces=s', 'ut|update-templates!', 'ft|force-thumb!', 'fs|force-slide!', 'it|index-title=s', 'iw|index-width=i', 'ir|index-row=i', 'skb|slide-kbsize!', 'sxy|slide-xysize!', 'uo|use-original!', 'lo|link-original!', 'aro|auto-rotate-originals!', 'gs|generate-slides!', 'gi|generate-info!', 'sy|slideY=i', 'ty|thumbY=i', 'iy|infoY=i', 'ws|watermark-slides!', 'wf|watermark-file=s', 'wg|watermark-gravity=s', 'gb|go-back!', 'gbs|go-back-string=s', 'gburl|go-back-url=s', 'theme=s', 'wd|web-dir=s', 'r|recurse', 'h|help', 'v|version', 'd|debug=i' ); # return the optsCmd hash return %opts; } ########################################################### # checkOpts - check the validity of the options hash. # # Do bounds checking for options that require an argument. # Print error and set option to default if an option is bad. # # return the new options hash # sub checkOpts { my (%opts) = %{$_[0]}; # options to check my (%optsSys) = %{$_[1]}; # system defaults # bounds check the options # --replace-spaces if ((defined $opts{rs}) and !($opts{rs} =~ /^[ayn]$/i)) { print "Error: '$opts{rs}' is an invalid value to option --replace-spaces\n"; print " Valid values are [ayn] where 'a'=ask; 'y'=yes; 'n'=no\n"; print " Resetting option to default: '$optsSys{rs}'\n"; $opts{rs} = $optsSys{rs}; } # --index-width if ((defined $opts{iw}) and (($opts{iw} <= 0) or ($opts{iw} > 255))) { print "Error: '$opts{iw}' is an invalid value to option --index-width\n"; print " Valid values are 1 - 255\n"; print " Resetting option to default: '$optsSys{iw}'\n"; $opts{iw} = $optsSys{iw}; } # --index-row if ((defined $opts{ir}) and (($opts{ir} < 0) or ($opts{ir} > 255))) { print "Error: '$opts{ir}' is an invalid value to option --index-row\n"; print " Valid values are 0 - 255\n"; print " Resetting option to default: '$optsSys{ir}'\n"; $opts{ir} = $optsSys{ir}; } # --slideY if ((defined $opts{sy}) and $opts{sy} <= 0) { print "Error: '$opts{sy}' is an invalid value to option --slideY\n"; print " Minimum value is 1\n"; print " Resetting option to default: '$optsSys{sy}'\n"; $opts{sy} = $optsSys{sy}; } # --thumbY if ((defined $opts{ty}) and $opts{ty} <= 0) { print "Error: '$opts{ty}' is an invalid value to option --thumbY\n"; print " Minimum value is 1\n"; print " Resetting option to default: '$optsSys{ty}'\n"; $opts{ty} = $optsSys{ty}; } # --infoY if ((defined $opts{iy}) and $opts{iy} <= 0) { print "Error: '$opts{iy}' is an invalid value to option --infoY\n"; print " Minimum value is 1\n"; print " Resetting option to default: '$optsSys{iy}'\n"; $opts{ty} = $optsSys{ty}; } # --watermark-gravity if ((defined $opts{ws}) and (defined $opts{wg})) { if (($opts{ws}) && ("north south east west northeast northwest southeast southwest" !~ /$opts{wg}/i)) { print "Error: '$opts{wg}' is an invalid value to option --watermark-gravity\n"; print " Valid options are: north, south, east, west\n"; print " northeast, northwest, southeast, and southwest\n"; print " Resetting option to default: '$optsSys{wg}'\n"; $opts{wg} = $optsSys{wg}; } } # --watermark-file if ((defined $opts{ws}) and (defined $opts{wf})) { if ($opts{ws} && !(-e $opts{wf})) { print "Error: Cannot find the watermark file: '$opts{wf}'\n"; print " Turning OFF Watermarking of the slides!\n"; $opts{ws} = 0; } } # --theme if (defined $opts{theme}) { my $themeFile = $opts{theme} . ".theme"; # full theme file name my $gblThemeFile = $gblThemeDir . "/" . $opts{theme} . "/" . $themeFile; # reset to default if theme file doesn't exists. if (!(-e $gblThemeFile) && $opts{theme} ne $optsSys{theme}) { print "Error: Cannot find the theme file: '$themeFile'\n"; print " Make sure it's in the directory '" . $gblThemeDir . "/" . $opts{theme} . "'\n"; print " Using the default theme.\n"; $opts{theme} = $optsSys{theme}; } # re-assign the global template files if the theme defines its own # index template my $tmpTmpl = $gblThemeDir ."/". $opts{theme} ."/". $indexTmpl; $gblIndexTmpl = $tmpTmpl if (-e $tmpTmpl); # slide template $tmpTmpl = $gblThemeDir ."/". $opts{theme} ."/". $slideTmpl; $gblSlideTmpl = $tmpTmpl if (-e $tmpTmpl); # info template $tmpTmpl = $gblThemeDir ."/". $opts{theme} ."/". $infoTmpl; $gblInfoTmpl = $tmpTmpl if (-e $tmpTmpl); } # do any options specific processing return %opts; } ########################################################### # mergeOpts - merge the various options arrays into one single array. # # merger order is: # system options -> ~/.jigl/jigl.opts -> gallery.dat -> command line options. # # return a new hash with the final options defined # sub mergeOpts { my ($sys,$rc,$gal,$cmd) = @_; # start with the system options and merge the rc options over them my %opts = (); foreach $key (keys %$sys) { if (defined $$rc{$key}) { # use the rc option $opts{$key} = $$rc{$key}; } else { # keep the system default $opts{$key} = $$sys{$key}; } } # now merge the gallery options over the these foreach $key (keys %opts) { if (defined $$gal{$key}) { # use the gallery option $opts{$key} = $$gal{$key}; } } # finally merge the command line options over the these foreach $key (keys %opts) { if (defined $$cmd{$key}) { # use the command line option $opts{$key} = $$cmd{$key}; } } # debug - print all the options and when the got set. if ($opts{d} >= 2) { print "\nFinal Options:\nKey:\tSys\tRC\tGal\tCmd\tOpts\n"; foreach $key (keys %$sys) { print "$key:\t$$sys{$key}\t"; if (defined $$rc{$key}) { print "$$rc{$key}\t" } else { print "--\t"; } if (defined $$gal{$key}) { print "$$gal{$key}\t" } else { print "--\t"; } if (defined $$cmd{$key}) { print "$$cmd{$key}\t" } else { print "--\t"; } print "$opts{$key}\n"; } print "\n"; } return %opts; } ########################################################### # genAlbumInfo - generate the master array containing info about the album. # # options that concern this function: # -cg (--create-gallerydat) # -rs (--replace-spaces) # -aro (--auto-rotate-originals) # # -cg option not set # ------------------ # gallery.dat file exists: # It will parse it and return a list of hashes containing the image file # names and descriptions. The gallery.dat file will NOT be written to. # # gallery.dat file does not exists: # It will simply return the list of hashes with the file names filled in # and the descriptions empty. NO gallery.dat file will be created! # # -cg option set # -------------- # gallery.dat file exists OR gallery.dat file does not exist: # It will create a NEW gallery.dat file and return an empty array. # That means if the gallery.dat file exists, any options, file descriptions # or file ordering will be LOST! # # -------------------------- # returns a reference to a hash. # -------------------------- # The hash contains the title of the page, the header and footer strings # and a reference to an array of images. # The array of images is ordered in the way the files are listed in # gallery.dat. If no gallery.dat file is found, it will store the files # in the order that glob retuned them. # sub genAlbumInfo { my (%opts) = %{$_[0]}; # options hash my ($dir) = $_[1]; # the directory we're working on my $href = {}; # temp hash reference my $aInfo = &newAlbumInfo; # album info hash my @tmpArr = (); # temp array my $picCnt = 0; # count of valid pictures my $skipCnt = 0; # count of skipped pictures my $filesWithSpaces = 0; # number of files with spaces print "Retreiving file info from directory \'$dir\'\n" if $opts{d}; # if we have a gallery.dat file and we're not creating a new # gallery.dat file. if (-e $galDatFile && !$opts{cg}) { # read the gallery.dat file and update the albumInfo hash open GALFILE, "<$galDatFile" or die "Cannot open \'$galDatFile\'\n"; print "Found \'$galDatFile\' - Retreiving file list.\n"; while () { if ($_ =~ /^#/) { # skip - line is a comment } elsif ($_ =~ /^[ ]*$/) { # skip - line is blank } elsif ($_ =~ /^GAL-OPTIONS/) { # skip - line contains gallery options } elsif ($_ =~ /^INDEX/) { # we have some index option chop $_; # read in the index tag and it's value and # trim off the whitespace ($tag,$val) = split /----/,$_; &trimWS($tag); &trimWS($val); if ($tag =~ /index-titlebar/i) { $aInfo->{titlebar} = $val; } elsif ($tag =~ /index-title/i) { $aInfo->{title} = $val; } elsif ($tag =~ /index-header/i) { $aInfo->{header} = $val; } elsif ($tag =~ /index-footer/i) { $aInfo->{footer} = $val; } else { print "Warning: Invalid option found in $galDatFile. <$tag>\n"; } } else { # we have a filename and description line $href = &newImgInfo; # create a new image info hash chop $_; # get rid of that pesky newline # read in the file name and descr ($file,$desc) = split /----/,$_; # skip file if it doesn't exist if (!-e trimWS($file)) { print "Skipping nonexistent image file: $file\n"; $skipCnt++; next; } # stuff the file and desc into the hash $href->{file} = trimWS($file); $href->{desc} = trimWS($desc); # rotate the original before we get it's size,X,and Y info. &rotateOrig($href->{file}) if $opts{aro}; # get the files size and X,Y info @tmpArr = &getImgInfo($href->{file}); $href->{size} = $tmpArr[0]; $href->{width} = $tmpArr[1]; $href->{height} = $tmpArr[2]; # push the new image into the array if the image is valid if ($href->{size} ne "") { push @{$aInfo->{images}},$href; # my $spaceCnt = () = $file =~ m/ /g; $filesWithSpaces++ if ($href->{file} =~ / /); $picCnt++; } else { print "Skipping invalid image file: $file\n"; $skipCnt++; } } } close GALFILE; # close the gallery.dat file print "$picCnt of " . ($picCnt + $skipCnt) . " image files found in $galDatFile were valid.\n"; } else { # no gallery.dat file found - just using image files found in dir. # get a list of files to work on my @files = glob($fileTypeStr); if ($opts{cg}) { print "Creating a new $galDatFile. Checking for files in directory.\n"; } else { print "No $galDatFile file found. Checking for files in directory.\n"; } # stuff each file into the hash and push it on the list foreach $file (@files) { # create a new imgInfo structure and fill in it's name $href = &newImgInfo; $href->{file} = trimWS($file); # rotate the original before we get it's size,X,and Y info. &rotateOrig($href->{file}) if $opts{aro}; # get the files size and X,Y info @tmpArr = &getImgInfo($href->{file}); $href->{size} = $tmpArr[0]; $href->{width} = $tmpArr[1]; $href->{height} = $tmpArr[2]; # push the new image into the array if the image is valid if ($href->{size} ne "") { push @{$aInfo->{images}},$href; $filesWithSpaces++ if ($href->{file} =~ / /); $picCnt++; } else { print "Skipping invalid image file: $file\n"; $skipCnt++; } } print "$picCnt of " . ($picCnt + $skipCnt) ." image files found in the directory were valid.\n"; } # figure out how many index pages need to be generated (zero based) if ($opts{ir} > 0 && (($opts{iw} * $opts{ir}) <= $#{$aInfo->{images}})) { $numPages = int($#{$aInfo->{images}} / ($opts{iw} * $opts{ir})); } else { $numPages = 0; } $aInfo->{numPages} = $numPages; # replace spaces in filenames with underscores &convertFileNames($aInfo,$opts{rs}) if $filesWithSpaces; # get the exif info for each image &genExifInfo(\%opts,$aInfo); # are we creating a go back link? If so, prepend it to the header but # only if the gburl is not already in the header. if ($opts{gb} && ($aInfo->{header} !~ m//)) { my $gbStr = "$opts{gbs}
" . $aInfo->{header}; $aInfo->{header} = $gbStr; } # create a new gallery.dat file from the files hash and return if ($opts{cg}) { &createNewGalDat(\%opts, $aInfo); return (); } return $aInfo; } ########################################################### # rotateOrig - auto rotate the original images # # input: image file to rotate # sub rotateOrig { my ($file) = @_; my $msgPad = " "; # 25 spaces print "Rotating image: $file $msgPad\r"; qx/$exifProg -autorot "$file"/; } ########################################################### # convertFileNames - replace spaces with underscores in filenames # # Input: albumInfo reference and -rs option value # sub convertFileNames { my ($albumInfo,$opt) = @_; my $yn = ""; # temp yes/no variable my $dieMsg = ""; # message to send to die # while we don't have a yes or no answer from the user while (!($opt =~ /^[yn]$/i)) { print "\n----------------------------------------------------\n"; print "There were spaces detected in some of the filenames.\n"; print "Spaces are EVIL, hard to work with, can break programs and\n"; print "are a pain in the ass to deal with in HTML code.\n"; print "It's recommended that you convert the spaces to underscores\n"; print "in the filenames.\n"; print "You can set the -rs|--replace-spaces option and you'll never\n"; print "see this message again.\n"; print "\n"; print "Do you want jigl to convert the offending file names?\n"; print "(y) or n > "; $yn = ; # get the input from the user chop $yn; $yn = "y" if $yn eq ""; # if they hit enter, make it a "y" # if $yn is a y or n, set opt to it and quit the loop. $opt = $yn if $yn =~ /^[yn]$/i; } # if we had a yes response, convert the filenames if ($opt eq "y") { print "Converting spaces in filenames to underscores.\n"; # check each filename my $orig = ""; # original file name my $new = ""; # new file name for $i (0 .. $#{$albumInfo->{images}}) { $orig = $albumInfo->{images}[$i]->{file}; $albumInfo->{images}[$i]->{file} =~ s/ /_/g; $new = $albumInfo->{images}[$i]->{file}; # only move the file if it's different if (!($orig eq $new)) { $dieMsg = "Cannot move $orig to $new\n"; move $orig,$new or die "$dieMsg $!\n"; } } # change the filenames in the gallery.dat file too. if (-e $galDatFile) { print "Updating $galDatFile\n"; # move the gallery.dat file to a temp file my $tmpGalDatFile = "tmp-$galDatFile"; my $dieMsg = "Cannot move $galDatFile to $tmpGalDatFile!\n"; move $galDatFile,$tmpGalDatFile or die "$dieMsg$!\n"; # open for temp file for reading, original file for writing open RFILE, "<$tmpGalDatFile" or die "Cannot open \'$tmpGalDatFile\'\n"; open WFILE, ">$galDatFile" or die "Cannot open \'$galDatFile\'\n"; # read in each line and change if need be while () { if (($_ =~ /^#/) or ($_ =~ /^$/) or ($_ =~ /^GAL-OPTIONS/) or ($_ =~ /^INDEX/)) { # just write - line is a comment,blank or contains options print WFILE $_; } else { # we have a filename and description line chop $_; # get rid of that pesky newline # read in the file name and descr ($file,$desc) = split /----/,$_; trimWS($file); # trim whitespace trimWS($desc); # trim whitespace $file =~ s/ /_/g; # replace the spaces with underscores # write the line back to the file print WFILE "$file ---- $desc\n"; } } # close the files close RFILE; close WFILE; # delete the temp file } } } ########################################################### # getImgInfo - takes file name and returns an array with the # size, width, and height of the image (in that order). # sub getImgInfo { my ($file) = @_; my @retArr = (); my $badImgMsg = "identify:"; # count the number of spaces in the filename for offset reasons my $spaceCnt = () = $file =~ m/ /g; # get file size, and XxY info my $line = qx/$imgInfoProg -ping "$file" 2>&1/; chop $line; # remove pesky new line # check to see if we had a bad file if ($line =~ m/$badImgMsg*/i) { # bad image found. # push empty values into the array to return push @retArr,""; push @retArr,""; push @retArr,""; } else { # pick out the XxY portion of the line my $xyLine = $1 if ($line =~ m/( [0-9]+x[0-9]+)/); my @xyArr = split /x|[+]|[=>]/,$xyLine; # split up XxY+val+val my $width = &trimWS($xyArr[0]); my $height = &trimWS($xyArr[1]); # get the size of the file - in bytes $size = -s $file; # round (not trunc) the division to the nearest int # and add kb at the end $size = sprintf("%.0fkb",($size / 1024)); # push the values into the array to return push @retArr,$size; push @retArr,$width; push @retArr,$height; } return @retArr; } ########################################################### # createNewGalDat - create a new gallery.dat file in the # current directory from the list of valid filenames in the albumInfo # sub createNewGalDat { my (%opts) = %{$_[0]}; my ($albumInfo) = $_[1]; my $yn = "y"; # create galDatFile if (-e $galDatFile) { $yn = ""; # clear this option out so we can ask the user # while we don't have a yes or no answer from the user while (!($yn =~ /^[yn]$/i)) { print "\n----------------------------------------------------\n"; print "You have called $progName with the --create-gallerydat option\n"; print "$progName has detected that a $galDatFile already exists.\n"; print "If you overwrite this file all current descriptions and\n"; print "customizations will be LOST!!\n"; print "\nWould you like to overwrite the current $galDatFile?\n"; print "y or (n) > "; $yn = ; # get the input from the user chop $yn; $yn = "n" if $yn eq ""; # if they hit enter, make it a "n" } print "----------------------------------------------------\n\n"; } # if we wanted to if ($yn eq "y") { print "Creating $galDatFile\n"; open(GALFILE,"> $galDatFile") || die "Cannot open \'$galDatFile\' $!\n"; print GALFILE "# Options can be placed after GAL-OPTIONS tag.\n"; print GALFILE "# They should be on one line and entered like they would\n"; print GALFILE "# on the command line. e.g. GAL-OPTIONS -ft -fs -rs y\n"; print GALFILE "GAL-OPTIONS\n"; print GALFILE "#\n"; print GALFILE "##############################################\n"; print GALFILE "# Below are the index options with their values and\n"; print GALFILE "# the file names and descriptions\n"; print GALFILE "# All are seperated by '----' this MUST remain.\n"; print GALFILE "# The file name and description must be on ONE line. The\n"; print GALFILE "# description can contain html tags if desired.\n"; print GALFILE "# You can change the order and it will be preserved when\n"; print GALFILE "# you generate the index.html and slide pages.\n"; print GALFILE "##############################################\n"; print GALFILE "#\n"; print GALFILE "INDEX-TITLEBAR ---- My Pictures\n"; print GALFILE "INDEX-TITLE ---- My Pictures\n"; print GALFILE "INDEX-HEADER ---- $albumInfo->{header}\n"; print GALFILE "INDEX-FOOTER ----\n"; print GALFILE "#\n"; for $i (0 .. $#{$albumInfo->{images}}) { print GALFILE "$albumInfo->{images}[$i]->{file} ---- $albumInfo->{images}[$i]->{desc}\n"; } close GALFILE; } else { print "Skipping creation of $galDatFile\n"; } } ########################################################### # genThumbs - generates thumbnail files # # input: a reference to the options hash # a reference to the albumInfo # # side effect: update the albumInfo with the name of the thumbnail image # sub genThumbs { my (%opts) = %{$_[0]}; my ($albumInfo) = $_[1]; print "Generating thumbnails\n"; my $tmpFile = ""; # temp variable to hold image name my $tmpThumbFile = ""; # temp variable to hold thumbnail name my @tmpArr = (); # temp array my $cmd = "$scaleProg"; # cmd to run to scale images my $dieMsg = ""; # message to print if we die my $msgPad = " "; # 25 spaces my $genCnt = 0; # number of thumbnails generated my $skipCnt = 0; # number of thumbnails already existing # create the thumbnail directory if need be if (!(-d $thumbDir)) { print "The thumbnail directory: '$thumbDir' did not exist. Creating.\n"; $dieMsg = "Cannot create the directory '$thumbDir'.\n"; mkdir $thumbDir,0755 or die "$dieMsg : $!\n"; } # generate thumbnail for each image for $i (0 .. $#{$albumInfo->{images}}) { # get the name of the file and create the thumbnail filename # store the name of the thumbnail for this image in the albumInfo $tmpFile = $albumInfo->{images}[$i]->{file}; $tmpThumbFile = "$thumbPrefix" . "$tmpFile"; $albumInfo->{images}[$i]->{thumb} = $tmpThumbFile; # copy and scale if the thumbnail does not already exist or we are # forcing the generation of the thumbnails if ($opts{ft} or (!(-e $tmpThumbFile))) { # scale the image to the thumbnail size specs print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) . "\) Scaling $tmpThumbFile $msgPad"; $cmd = "$scaleProg -scale x$opts{ty} -sharpen 5 \"$tmpFile\" \"$tmpThumbFile\""; $dieMsg = "\nCannot scale the thumbnail image $tmpThumbFile!\n"; system($cmd) == 0 or die $dieMsg; # increment the generated count $genCnt++; } else { # increment the skipped count print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}} + 1) . "\) Skipping $tmpThumbFile $msgPad"; $skipCnt++; } # get the files size and X,Y info for the slide @tmpArr = &getImgInfo($albumInfo->{images}[$i]->{thumb}); $albumInfo->{images}[$i]->{thumbkb} = $tmpArr[0]; $albumInfo->{images}[$i]->{thumbx} = $tmpArr[1]; $albumInfo->{images}[$i]->{thumby} = $tmpArr[2]; } print "\r"; # move back to the start of the line print "$genCnt thumbnails generated. $msgPad\n" if $genCnt > 0; print "$skipCnt thumbnails skipped because they already existed.\n" if $skipCnt > 0; print "Finished generating thumbnail images.\n\n"; } ########################################################### # genSlides - generates slide files # # input: a reference to the options hash # a reference to the albumInfo # # side effect: update the albumInfo with the name of the slide image # update the albumInfo with the size in kb of the slide image # update the albumInfo with the XxY dimensions of the slide image # sub genSlides { my (%opts) = %{$_[0]}; my ($albumInfo) = $_[1]; print "Generating slides\n"; my $tmpFile = ""; # temp variable to hold image name my $tmpSlideFile = ""; # temp variable to hold slide name my $cmd = "$scaleProg"; # cmd to run to scale images my $dieMsg = ""; # message to print if we die my $msgPad = " "; # 25 spaces my $genCnt = 0; # number of thumbnails generated my $skipCnt = 0; # number of thumbnails already existing my $tmpArr = (); # temporary array # create the slide directory if need be if (!(-d $slideDir) && !($opts{uo})) { print "The slide directory: '$slideDir' did not exist. Creating.\n"; $dieMsg = "Cannot create the directory '$slideDir'.\n"; mkdir $slideDir,0755 or die "$dieMsg : $!\n"; } # generate slide for each image for $i (0 .. $#{$albumInfo->{images}}) { # get the name of the file and create the slide filename $tmpFile = $albumInfo->{images}[$i]->{file}; $tmpSlideFile = "$slidePrefix" . "$tmpFile"; # store the name of the slide for this image in the albumInfo if ($opts{uo}) { # if the use-originals option is used, we're not going to # create a slide $albumInfo->{images}[$i]->{slide} = $tmpFile; } else { # sotore the slide file if making slides $albumInfo->{images}[$i]->{slide} = $tmpSlideFile; } # scale the image if the slide does not already exist or we are # forcing the generation of the slides and we're not using the orig's if (($opts{fs} or (!(-e $tmpSlideFile))) && !($opts{uo})) { # scale the image to the slide size specs print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) . "\) Scaling $tmpSlideFile $msgPad"; # only scale the slide if it's Y height is greater than # the value of the sy option. if ($albumInfo->{images}[$i]->{height} > $opts{sy}) { $cmd = "$scaleProg -scale x$opts{sy} -sharpen 5 \"$tmpFile\" \"$tmpSlideFile\""; $dieMsg = "\nCannot scale the slide image!\n"; system($cmd) == 0 or die $dieMsg; } else { $dieMsg = "Cannot copy \"$tmpFile\" to \"$tmpSlideFile\"\n"; copy $tmpFile,$tmpSlideFile or die "$dieMsg $!\n"; } # if we're watermarking the slides, do it now. if ($opts{ws}) { # check to make sure we have a valid watermark program # and that the watermark image exists if ($waterMarkProg eq "none") { print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) ."\) CANNOT Watermark $tmpSlideFile. No Watermark program found! $msgPad"; } else { print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) ."\) Watermarking $tmpSlideFile $msgPad"; if ($waterMarkProg eq "composite") { $cmd = "$waterMarkProg -compose over -gravity $opts{wg} $opts{wf} \"$tmpSlideFile\" \"$tmpSlideFile\""; } else { # combine reversed the order the image and watermark # were listed on the command line. $cmd = "$waterMarkProg -compose over -gravity $opts{wg} \"$tmpSlideFile\" $opts{wf} \"$tmpSlideFile\""; } $dieMsg = "\nCannot watermark the slide image!\n"; system($cmd) == 0 or die $dieMsg; } } # increment the generated count $genCnt++; } else { # increment the skipped count print "\r\(" . ($i+1) . "/" . ($#{$albumInfo->{images}}+1) . "\) Skipping $tmpSlideFile $msgPad"; $skipCnt++; } # get the image info for the slide if ($opts{uo}) { # we're using the originals and already have the # size, X and Y info of the originals. use those. $albumInfo->{images}[$i]->{slidekb} = $albumInfo->{images}[$i]->{size}; $albumInfo->{images}[$i]->{slidex} = $albumInfo->{images}[$i]->{width}; $albumInfo->{images}[$i]->{slidey} = $albumInfo->{images}[$i]->{height}; } else { # get the files size and X,Y info for the slide @tmpArr = &getImgInfo($albumInfo->{images}[$i]->{slide}); $albumInfo->{images}[$i]->{slidekb} = $tmpArr[0]; $albumInfo->{images}[$i]->{slidex} = $tmpArr[1]; $albumInfo->{images}[$i]->{slidey} = $tmpArr[2]; } } print "\r"; # move back to the start of the line print "$genCnt slides generated. $msgPad\n" if $genCnt > 0; print "$skipCnt slides skipped because they already existed.\n" if $skipCnt > 0; print "Finished generating slide images.\n\n"; } ########################################################### # genExifInfo - generate the exif info for each jpeg image # # generates exif info for all jpg images. # # input: # reference to the options hash # reference to album info hash # # side effects: if the -uec option is set and there is an exif comment in # the exif header, then the slide description will be overwritten with the # exif comment. # sub genExifInfo { my (%opts) = %{$_[0]}; # options hash my ($albumInfo) = $_[1]; # album info print "Generating EXIF info\n"; # generate page for each image for $i (0 .. $#{$albumInfo->{images}}) { # get the name of the file $tmpFile = $albumInfo->{images}[$i]->{file}; # set the exif info for the image $albumInfo->{images}[$i]->{exif} = &getExifInfo($tmpFile); # set the image description to the exif comment if it exists if ($opts{uec}) { # check to see if there is a comment field. $haveComment = 0; for $j (0 .. $#{$albumInfo->{images}[$i]->{exif}}) { if ($albumInfo->{images}[$i]->{exif}->[$j]{field} eq "Comment") { if ($haveComment eq 0) { # first line of the comment. $desc = $albumInfo->{images}[$i]->{exif}->[$j]{val}; $haveComment++; } else { # there was more than one comment line $desc = $desc . "
" . $albumInfo->{images}[$i]->{exif}->[$j]{val}; } } } # if there was comment, save it. $albumInfo->{images}[$i]->{desc} = $desc if $haveComment; } } } ########################################################### # getExifInfo - run the exif program, and get the exif info for an image. # # This runs the users exif extraction program on an image creating an # array of hashes. Each array element is a hash which corresponds to each # line in the output of the exif program. Each hash has two keys, 'field' # and 'val'. # # jhead - the exif extraction program output in the form: # exif field : exif value # the 'field' value corresponds to everything before the first ':'. # the 'val' value corresponds to everything after the first ':'. # # all leading and trailing whitespace is removed from both field and val. # # input: the file we want to get exif info from. # return: the reference to the array of hashes. # sub getExifInfo { my ($file) = @_; my @exifArr = (); # exif info only exists in jpeg files. Return if not. return \@exifArr unless ($file =~ /jpe?g/i); # get the output of the exif program my $cmdOutput = qx/$exifProg "$file"/; # split it up into lines and parse each line my @cmdArr = split /\n/,$cmdOutput; foreach $line (@cmdArr) { # clear out the temporary hash my %tmpHash = (field => "", val => ""); # split on the field delimeters @lineArr = split /:/,$line; # add in the field value $tmpHash{field} = trimWS($lineArr[0]); shift @lineArr; # remove the first element # rejoin the line array to preserve any other ':'s in the line $line = join ':',@lineArr; # add the value to the hash $tmpHash{val} = trimWS($line); # stuff a reference to the hash at the end of the array push @exifArr,\%tmpHash; } return \@exifArr; } ########################################################### # newAlbumInfo - returns a hash structure containing all the # info about the album. sub newAlbumInfo { return { titlebar => "My Pictures", # title bar of album title => "My Pictures", # title of album header => "", # header string footer => "", # footer string images => [] # array of images - created latert }; } ########################################################### # newImgInfo - returns a hash structure containing all the # info about one file. sub newImgInfo { return { file => "", # string - filename desc => "", # string - description width => "", # images width (in pixels) height => "", # images height (in pixels) size => "", # file size of image in Kb. thumb => "", # filename of thumbnail image thumbx => "", # width of thumbnail image thumby => "", # height of thumbnail image slide => "", # filename of slide image slidekb => "", # size in kb of slide the image slidex => "", # width of slide image slidey => "", # height of slide image exif => {} # hash reference - created later }; } ########################################################### # trimWS - from begining and end of input # sub trimWS { return "" if (!(defined $_[0])); $_[0] =~ s/^\s+|\s+$//go ; return $_[0]; } ########################################################### # printVersion - prints the version info and exists # sub printVersion { print "\nJason's Image Gallery - jigl\n"; print "Version $version (c)2002-2003 $author\n"; print "Please run '$progName -h' for help\n\n"; exit 0; } ########################################################### # printUsage - print the usage and exit # sub printUsage { print < : Replaces spaces with underscores "_" in filenames. a - (default) Ask the user if they want to y - replace and do no ask n - do not replace and do no ask -it --index-title : Sets the title and title-bar of the index.html page to string. This is meant to be used when you are not using a gallery.dat file. -iw --index-width <1-255> : How many thumbnails per line you want on the index page. Default is 5 -ir --index-row <0-255> : The number of thumbnail rows you want on the index page before a new index page is generated. If 0, there is no limit and only one index page will be generated. Default is 0 -ut --update-templates : Remove the template files from the local directory and the global default theme directory. This will cause new default template files to be generated in the global default theme directory. Default is DISABLED. (can be negated). -ft --force-thumb : Force thumbnail regeneration (can be negated) -fs --force-slide : Force slide regeneration (can be negated) -skb --slide-kbsize : Print the file size of the slide on the index page under the thumbnails. Default is ENABLED (can be negated) -sxy --slide-xysize : Print the slide dimensions under the thumbnails on the index page. Default is ENABLED (can be negated) -uo --use-original : Use the original images for the slides - do not generate a scaled slide image. Default is DISABLED. (can be negated) -lo --link-original : Link to the original images from the slides. Default is DISABLED. (can be negated). -aro --auto-rotate-originals : Do a lossless rotation of the original images. This option is only useful if your digital camera sets the Orientation field in the EXIF header. All EXIF header information will be kept and the Orientation field will be updated to reflect the rotation. Default is DISABLED. (can be negated). -gs --generate-slides : Gererate slide pages and link them to the thumbnails. If this option is turned off, slides will not be generated and the info pages will be linked to the thumbnails instead. If neither slide or info pages are generated, the thumbnails will not link to anything. Default is ENABLED. (can be negated). -gi --generate-info : Gererate info pages and link them to the "info" link on the slide pages. If this option is turned off, info pages will not be generated and the "info" link on the slide pages will dissapear. If no slides are generated, then the info pages will be linked directly to the thumbnails. Default is ENABLED. (can be negated). -sy --slideY : Maximum size of slide image in the Y direction. If the height of the original image is greater than this value the slide will be scaled to this value. Otherwise the slide is simply a copy of the original. Default is 480 pixels -ty --thumbY : Size of the scaled thumbnail image in the Y direction Default is 75 pixels -iy --infoY : Maximum size of the info image in the Y direction. This is simply a scale of the slide image using HTML height and width tags. A new image is not generated. Default is 240 pixels -ws --watermark-slides : Enable watermarking of the slide images. A watermark file must be present for this option to work. Default is DISABLED. (can be negated). -wf --watermark-file : Name of the watermark file to use when -ws is enabled. Default is "watermark.png". -wg --watermark-gravity : Where to display the watermark on the slide. Default is southeast. -gb --go-back : Prepend a "Go Back" link to the header in the index.html file. If used in conjunction with -cg, the Go Back link will be added to the INDEX-HEADER tag in the newly created gallery.dat file. Default is DISABLED. (can be negated). -gbs --go-back-string : String to use for the go-back-url link. You could even make this an image to help round out a theme by making this string something like: "" Default is "Go Back". -gburl --go-back-url : URL to use when -gb is enabled. Any URL can be used. Default is '..', the previous directory. --theme : Name of theme to use. Themes files are defined as "themeName.theme". You would just enter the "themeName" portion of the file. Default is "default". -wd --web-dir : Directory to put all the gallery files (html, slides, thumbnails and possibly theme files) into. Unless a fully qualified path is used, this directory will be created in the current directory. This option is not used if the -lo or -uo option is set. Default is "web". -r --recurse : Recurse through all directories on the command line. This option will omit all directories named "slides", "thumbs", "theme", "web" and the value of -wd if that option is set. This option will ONLY be recognized from the command line to help prevent accidents. Note: Any options listed on the command line when using recursion will be applied to all directories. Default is DISABLED. -h --help : Display this information and exit -v --version : Display version and exit -d --debug <0-5> : Set debug level. Default is 0 Note: If an option listed says it can be negated you can prefix it with "no" and the opposite effect will happen. Useful for overriding options on the command line that are set elsewhere. e.g. -nouo | --nouse-original will NOT use the original files as slides. [directories] Default directory is "." Options can be used in any of three places: (listed ascending precedence) - A single line in the \$HOME/.jigl/jigl.opts file in the users home directory. - After the GAL-OPTIONS tag as a single line in the gallery.dat file in each of the album directories. - On the command line. Regardless of where they are located, they should all be listed in the same form as you would use them on the command line. Example: -lo -iw 6 -noskb Theme files can be located in either the directory jigl is processing or in the .jigl directory in the users home directory. endOfPrint exit 0; } ########################################################### # removeFiles - remove a list of files, if there is a directory listed it # will recrusively delete the entire directory as well. # sub removeFiles { my (@files) = @_; foreach $file (@files) { if (-d $file) { rmtree("$file",0,0); } else { # only delete them if they exist. @err = grep {not unlink} $file if -e $file; warn "$0: could not delete @err\n" if @err; } } } ########################################################### # genIndexPage - generate the index.html page # sub genIndexPage { my (%opts) = %{$_[0]}; my (%theme) = %{$_[1]}; my ($albumInfo) = $_[2]; my ($currPage) = $_[3]; my ($startIndex) = $_[4]; my ($endIndex) = $_[5]; my $row = 0; # keep track of what row we're on my $col = 0; # keep track of what image we're on my $col1 = 0; # keep track of what imgae we're on for size and dimensions my $thumbName = ""; # tmp variable to store thumbnail name my $thumbX = ""; # tmp variable to store the thumbnails X dimension my $thumbY = ""; # tmp variable to store the thumbnails Y dimension my $thumbkb = ""; # tmp variable to store the size (in kb) of the thumb my $slideX = ""; # tmp variable to store the slides X dimension my $slideY = ""; # tmp variable to store the slides Y dimension my $slideKb = ""; # tmp variable to store the size (in kb) of the slide my $tmpVar = ""; # tmp variable my $line = ""; # variable to store current template line in my $themeLine = 0; # a line was inserted into the template by a theme # check to see if there is a local index template present if (-e $indexTmpl) { print "Using local $indexTmpl\n" if $opts{d}; } else { if ($opts{theme} and (-e $gblIndexTmpl)) { print "Using $indexTmpl from theme '$opts{theme}'\n" if $opts{d}; } elsif (-e $gblIndexTmpl) { print "Using global default $indexTmpl\n" if $opts{d}; } else { print "No local or global $indexTmpl file found. Creating default\n"; &genIndexTemplate; } } # open the index.html file for writing if ($currPage == 0) { $indexFile = $indexPrefix . $indexExt; } else { $indexFile = $indexPrefix . $currPage . $indexExt; } open(INDEX,">$indexFile") or die "Cannot open $indexFile $!\n"; # try to open the local template file for reading first if (-e $indexTmpl) { print "Opening $indexTmpl from the local directory'\n" if $opts{d}; open(INDEXTMPL,"<$indexTmpl") or die "Cannot open $indexTmpl: $!\n"; } else { print "Opening $gblIndexTmpl'\n" if $opts{d}; open(INDEXTMPL,"<$gblIndexTmpl") or die "Cannot open $gblIndexTmpl: $!\n"; } print "Generating the $indexFile page.\n"; # read each line of the template file and do what's appropriate # for each tag found $line = ; while ($line) { # check each of the keys in the theme file foreach $key (sort {length($b)<=>length($a)} keys %theme) { if ($line =~ /$key/) { if (defined $theme{$key}) { if ($key eq "INDEX-HEADER" and ($albumInfo->{header} eq "")) { $line =~ s/$key/
/g; $themeLine = 1; } elsif ($key eq "INDEX-FOOTER" and ($albumInfo->{footer} eq "")) { $line =~ s/$key/
/g; $themeLine = 1; } else { # standard theme line. Just replace the tag w/value $tmpVar = $theme{$key}; $line =~ s/$key/$tmpVar/g; $themeLine = 1; } } else { print "WARNING: $key was not defined in the theme file!\n"; } } } if ($line =~ /INDEX-TITLEBAR/) { $line =~ s/INDEX-TITLEBAR/$albumInfo->{titlebar}/g; } if ($line =~ /INDEX-TITLE/) { $line =~ s/INDEX-TITLE/$albumInfo->{title}/g; } if ($line =~ /TIME-STAMP/) { $tmpVar = strftime "%A %d %B %Y %H:%M:%S",localtime(time()); $line =~ s/TIME-STAMP/$tmpVar/g; } if ($line =~ /INDEX-HEADER-INFO/) { $line =~ s/INDEX-HEADER-INFO/$albumInfo->{header}/g; } if ($line =~ /INDEX-FOOTER-INFO/) { $line =~ s/INDEX-FOOTER-INFO/$albumInfo->{footer}/g; } if ($line =~ /PICTURES/) { # prints out each row of images $row = 0; # to keep track of what row we're on $donePics = 0; # flag to indicate we're out of pics $opts{iw} = 5 if $opts{iw} <= 0; # reset index-width of invalid $tmpVar = ""; # clear out the tmp variable # generate a new row while we have pictures left while (!$donePics) { # create table to hold a row of thumbnails & size/dimensions $tmpVar = $tmpVar . $theme{"THUMB-ROW"}; # add the row for the thumbnail images if ($tmpVar =~ /IMG-ROW/) { $tmpVar =~ s/IMG-ROW/$theme{"IMG-ROW"}/g; } # print out the thumbnails up to the value of index-width $col = 0; # start at first column while ($col < $opts{iw} && !$donePics) { # figure out what image to grab. $imgIndex = ($opts{iw} * $row) + $col + $startIndex; $thumbName = $albumInfo->{images}[$imgIndex]->{thumb}; # replace any spaces in the name with html friendly code $thumbName =~ s/ /\%20/g; $thumbX = $albumInfo->{images}[$imgIndex]->{thumbx}; $thumbY = $albumInfo->{images}[$imgIndex]->{thumby}; # is this the last of the pictures? $donePics=1 if ($imgIndex == $endIndex); # add a thumbnail to the row if ($tmpVar =~ /IMG-COLUMN/) { # remove the link portion of the key if we are not # generating slides or info pages. if (!$opts{gi} and !$opts{gs} and $theme{"IMG-COLUMN"} =~ ///g; $theme{"IMG-COLUMN"} =~ s/<\/a>//g; } # add the tag to the end so the next img can be added $tmpCol = $theme{"IMG-COLUMN"} . "\nIMG-COLUMN"; $tmpVar =~ s/IMG-COLUMN/$tmpCol/g; } # add the link associated with this thumbnail if ($tmpVar =~ /THUMB-LINK/) { if ($opts{gs}) { $tmpLink = $imgIndex+1 . ".html"; } elsif ($opts{gi}) { $tmpLink = $imgIndex+1 . "_info.html"; } else { $tmpLink = $thumbName; } $tmpVar =~ s/THUMB-LINK/$tmpLink/g; } # add the image associated with this thumbnail if ($tmpVar =~ /THUMB-FILE/) { $tmpVar =~ s/THUMB-FILE/$thumbName/g; } # add the image width associated with this thumbnail if ($tmpVar =~ /THUMB-WIDTH/) { $tmpVar =~ s/THUMB-WIDTH/$thumbX/g; } # add the image height associated with this thumbnail if ($tmpVar =~ /THUMB-HEIGHT/) { $tmpVar =~ s/THUMB-HEIGHT/$thumbY/g; } $col++; # increment the col count } # last column in this row. Remove the tag from the line. $tmpVar =~ s/IMG-COLUMN//g; # print out the sizes and dimensions below the images if ($opts{skb} or $opts{sxy}) { # add the row for the thumbnail size and dimensions if ($tmpVar =~ /SIZE-DIMENSION-ROW/) { $tmpVar =~ s/SIZE-DIMENSION-ROW/$theme{"SIZE-DIMENSION-ROW"}/g; } # print out each size/dimension up to the width of # the row that was just generated $col1 = 0; while ($col1 < $col) { $imgIndex = ($opts{iw} * $row) + $col1; if ($opts{gi} or $opts{gs}) { $slideX = $albumInfo->{images}[$imgIndex]->{slidex}; $slideY = $albumInfo->{images}[$imgIndex]->{slidey}; $slideKb = $albumInfo->{images}[$imgIndex]->{slidekb}; } else { $slideX = $albumInfo->{images}[$imgIndex]->{thumbx}; $slideY = $albumInfo->{images}[$imgIndex]->{thumby}; $slideKb = $albumInfo->{images}[$imgIndex]->{thumbkb}; } # add a thumbnail to the row if ($tmpVar =~ /SIZE-DIMENSION-COLUMN/) { # add tag to the end so the next info can be added $tmpCol = $theme{"SIZE-DIMENSION-COLUMN"} . "\nSIZE-DIMENSION-COLUMN"; $tmpVar =~ s/SIZE-DIMENSION-COLUMN/$tmpCol/g; } # this slides dimensions and size $tmpDim = $slideX . "x" . "$slideY"; $tmpSize = "(" . $slideKb . ")"; # only print out the fields the user asked us to if ($tmpVar =~ /THUMB-DIMENSIONS/) { if ($opts{sxy}) { $tmpVar =~ s/THUMB-DIMENSIONS/$tmpDim/g; } else { $tmpVar =~ s/THUMB-DIMENSIONS//g; } } if ($tmpVar =~ /THUMB-SIZE/) { if ($opts{skb}) { $tmpVar =~ s/THUMB-SIZE/$tmpSize/g; } else { $tmpVar =~ s/THUMB-SIZE//g; } } $col1++; # increment the column1 count } # last column in this row. Remove the tag from the line. $tmpVar =~ s/SIZE-DIMENSION-COLUMN//g; } else { # no size or dimension being printed. remove the tag $tmpVar =~ s/SIZE-DIMENSION-ROW//g; } $row++; # increment the row count } $line =~ s/PICTURES/$tmpVar/g; } if ($line =~ /INDEX-NAVI/) { $tmpVar = ""; # clear out the tmp variable # only print this if we have more than one page if ($albumInfo->{numPages} > 0) { for $i (0 .. $albumInfo->{numPages}) { if ($i == 0) { $tmpPage = "./" . $indexPrefix . $indexExt; } else { $tmpPage = "./" . $indexPrefix . $i . $indexExt; } # don't link to the current page we're on if ($i == $currPage) { $tmpVar = $tmpVar . "<$i> "; } else { $tmpVar = $tmpVar . "[" . $i . "]<\/a> "; } } &trimWS($tmpVar); # remove space from the end of the line } $line =~ s/INDEX-NAVI/$tmpVar/g; } # get the next line to process if ($themeLine != 0) { # we had a theme line, so process this line again. $themeLine = 0; # reset so we only process this line once. } else { # print out the line print INDEX $line; # get new line $line = ; } } close INDEX; # close the index.html file close INDEXTMPL; # close the index_template file } ########################################################### # genIndexTemplate - generate the index template # sub genIndexTemplate { # put the default index template in the global default theme dir. open(INDEXTMPL,">$gblIndexTmpl") or die "Cannot open $gblIndexTmpl: $!\n"; print INDEXTMPL < INDEX-TITLEBAR
INDEX-TITLE
INDEX-HEADER
INDEX-NAVI

PICTURES

INDEX-FOOTER
Created on: TIME-STAMP Created with jigl
endoftemplate close INDEXTMPL; } ########################################################### # genSlidePages - generate the html slide pages # sub genSlidePages { my (%opts) = %{$_[0]}; my (%theme) = %{$_[1]}; my ($albumInfo) = $_[2]; my $slideFile = ""; # file name to create my $slideX = ""; # tmp variable to store the slides X dimension my $slideY = ""; # tmp variable to store the slides Y dimension my $curr0Index = ""; # 0 based index of this image my $curr1Index = ""; # 1 based index of this image my $prev0Index = ""; # 0 index of previous image my $prev1Index = ""; # 1 index of previous image my $next0Index = ""; # 0 index of next image my $next1Index = ""; # 1 index of next image my $tmpVar = ""; # temp variable my $line = ""; # variable to store current template line in my $themeLine = 0; # a line was inserted into the template by a theme # check to see if there is a local slide template present if (-e $slideTmpl) { print "Using local $slideTmpl\n" if $opts{d}; } else { if ($opts{theme} and (-e $gblSlideTmpl)) { print "Using $slideTmpl from theme '$opts{theme}'\n" if $opts{d}; } elsif (-e $gblSlideTmpl) { print "Using global default $slideTmpl\n" if $opts{d}; } else { print "No local or global $slideTmpl file found. Creating default\n"; &genSlideTemplate; } } print "Generating the slide html pages\n"; # create one slide for each image for $curr0Index (0 .. $#{$albumInfo->{images}}) { # setup all the 0 and 1 based index values $curr1Index = $curr0Index + 1; $prev0Index = $curr0Index - 1; $prev1Index = $curr0Index; $next0Index = $curr1Index; $next1Index = $curr1Index + 1; # handle some special cases if ($curr0Index == 0) { # this is the first file. # the previous index will be the last index $prev0Index = $#{$albumInfo->{images}}; $prev1Index = $#{$albumInfo->{images}} + 1; } if ($curr0Index == $#{$albumInfo->{images}}) { # this is the last file. # the next index will be the first index $next0Index = 0; $next1Index = 1; } # figure out what files we're supposed to be creating and linking to. $slideFile = "$curr1Index.html"; $infoFile = $curr1Index ."_info.html"; $prevSlideFile = "$prev1Index.html"; $nextSlideFile = "$next1Index.html"; # which index page is this image on. $pgIndex = int($curr0Index / ($opts{iw} * $opts{ir})) if $opts{ir} > 0; if (($albumInfo->{numPages} == 0) or $pgIndex == 0) { $indexFile = $indexPrefix . $indexExt; } else { $indexFile = $indexPrefix . $pgIndex . $indexExt; } # open the slide html file for writing open(SLIDE,">$slideFile") or die "Cannot open $slideFile $!\n"; # try to open the local template file for reading if (-e $slideTmpl) { open(SLIDETMPL,"<$slideTmpl") or die "Cannot open $slideTmpl: $!\n"; } else { open(SLIDETMPL,"<$gblSlideTmpl") or die "Cannot open $gblSlideTmpl: $!\n"; } # read each line of the template file and do what's appropriate # for each tag found $line = ; while ($line) { # check each of the keys in the theme file foreach $key (sort {length($b)<=>length($a)} keys %theme) { if ($line =~ /$key/) { if (defined $theme{$key}) { # process any special keys here # don't print the info link if we didn't gen info-pages if ($key eq "INFO-LINK" and !$opts{gi}) { $line =~ s/$key//g; $themeLine = 1; } elsif ($key eq "NEXT-SLIDE-LINK" and $curr1Index == ($#{$albumInfo->{images}} + 1)) { # this is the last slide $line =~ s/$key/LAST-SLIDE-NEXT-LINK/g; $themeLine = 1; } elsif ($key eq "PREV-SLIDE-LINK" and $curr1Index == 1) { # this is the first slide $line =~ s/$key/FIRST-SLIDE-PREV-LINK/g; $themeLine = 1; } else { # nothing special about this key $tmpVar = $theme{$key}; $line =~ s/$key/$tmpVar/g; $themeLine = 1; } } else { print "WARNING: $key was not defined in the theme file!\n"; } } } if ($line =~ /ORIG-IMAGE-NAME/) { $line =~ s/ORIG-IMAGE-NAME/$albumInfo->{images}[$curr0Index]->{file}/g; } if ($line =~ /NEXT-SLIDE-NAME/) { $tmpVar = $albumInfo->{images}[$next0Index]->{slide}; $tmpVar =~ s/ /\%20/g; $line =~ s/NEXT-SLIDE-NAME/$tmpVar/g; } if ($line =~ /PREV-SLIDE-HTML/) { $line =~ s/PREV-SLIDE-HTML/$prevSlideFile/g; } if ($line =~ /INDEX-HTML/) { $line =~ s/INDEX-HTML/$indexFile/g; } if ($line =~ /INFO-HTML/) { $line =~ s/INFO-HTML/$infoFile/g; } if ($line =~ /NEXT-SLIDE-HTML/) { $line =~ s/NEXT-SLIDE-HTML/$nextSlideFile/g; } if ($line =~ /SLIDE-IMAGE/) { # get some info about the slide $origImg = $albumInfo->{images}[$curr0Index]->{file}; $origImg =~ s/ /\%20/g; # make any spaces web friendly $slideImg = $albumInfo->{images}[$curr0Index]->{slide}; $slideImg =~ s/ /\%20/g; # make any spaces web friendly $slideX = $albumInfo->{images}[$curr0Index]->{slidex}; $slideY = $albumInfo->{images}[$curr0Index]->{slidey}; # check to see if we need to link the slide to the original if ($opts{lo}) { $tmpVar = "
"; } else { $tmpVar = ""; } $line =~ s/SLIDE-IMAGE/$tmpVar/g; } if ($line =~ /SLIDE-DESCRIPTION/) { $line =~ s/SLIDE-DESCRIPTION/$albumInfo->{images}[$curr0Index]->{desc}/g; } if ($line =~ /SLIDE-COUNT/) { $tmpVar = "\(" . $curr1Index . "\/" . ($#{$albumInfo->{images}} + 1) . "\)"; $line =~ s/SLIDE-COUNT/$tmpVar/g; } # get the next line to process if ($themeLine != 0) { # we had a theme line, so process this line again. $themeLine = 0; # reset so we only process this line once. } else { # print out the line print SLIDE $line; # get new line $line = ; } } close SLIDE; # close the index.html file close SLIDETMPL; # close the index_template file } } ########################################################### # genSlideTemplate - generate the template for each slide page # sub genSlideTemplate { # put the default slide template in the default theme dir. open(SLIDETMPL,">$gblSlideTmpl") or die "Cannot open $gblSlideTmpl: $!\n"; print SLIDETMPL < Image: ORIG-IMAGE-NAME SLIDE-COUNT
PREV-SLIDE-LINK INDEX-LINK   INFO-LINK NEXT-SLIDE-LINK
 
SLIDE-IMAGE
 
SLIDE-DESCRIPTION
SLIDE-COUNT
 
PREV-SLIDE-LINK INDEX-LINK   INFO-LINK NEXT-SLIDE-LINK
endoftemplate close SLIDETMPL; } ########################################################### # genInfoPages - generate the html EXIF pages # sub genInfoPages { my (%opts) = %{$_[0]}; my (%theme) = %{$_[1]}; my ($albumInfo) = $_[2]; my $infoFile = ""; # file name of info page to create my $slideFile = ""; # slide file associated with current info page my $prevSlideFile = ""; # next slide page my $prevInfoFile = ""; # next info page my $nextSlideFile = ""; # previous slide page my $nextInfoFile = ""; # previous info page my $slideX = ""; # tmp variable to store the slides X dimension my $slideY = ""; # tmp variable to store the slides Y dimension my $curr0Index = ""; # 0 based index of this image my $curr1Index = ""; # 1 based index of this image my $prev0Index = ""; # 0 index of previous image my $prev1Index = ""; # 1 index of previous image my $next0Index = ""; # 0 index of next image my $next1Index = ""; # 1 index of next image my $tmpVar = ""; # temp variable my $line = ""; # variable to store current template line in my $themeLine = 0; # a line was inserted into the template by a theme # check to see if there is a local info template present if (-e $infoTmpl) { print "Using local $infoTmpl\n" if $opts{d}; } else { if ($opts{theme} and (-e $gblInfoTmpl)) { print "Using $infoTmpl from theme '$opts{theme}'\n" if $opts{d}; } elsif (-e $gblInfoTmpl) { print "Using global default $infoTmpl\n" if $opts{d}; } else { print "No local or global $infoTmpl file found. Creating default\n"; &genInfoTemplate; } } print "Generating the info html pages\n"; # create one info page for each image for $curr0Index (0 .. $#{$albumInfo->{images}}) { # setup all the 0 and 1 based index values $curr1Index = $curr0Index + 1; $prev0Index = $curr0Index - 1; $prev1Index = $curr0Index; $next0Index = $curr1Index; $next1Index = $curr1Index + 1; # handle some special cases if ($curr0Index == 0) { # this is the first file. # the previous index will be the last index $prev0Index = $#{$albumInfo->{images}}; $prev1Index = $#{$albumInfo->{images}} + 1; } if ($curr0Index == $#{$albumInfo->{images}}) { # this is the last file. # the next index will be the first index $next0Index = 0; $next1Index = 1; } # figure out what files we're supposed to be creating and linking to. $infoFile = $curr1Index ."_info.html"; $slideFile = "$curr1Index.html"; $prevSlideFile = "$prev1Index.html"; $prevInfoFile = $prev1Index ."_info.html"; $nextSlideFile = "$next1Index.html"; $nextInfoFile = $next1Index ."_info.html"; # which index page is this image on. $pgIndex = int($curr0Index / ($opts{iw} * $opts{ir})) if $opts{ir} > 0; if (($albumInfo->{numPages} == 0) or $pgIndex == 0) { $indexFile = $indexPrefix . $indexExt; } else { $indexFile = $indexPrefix . $pgIndex . $indexExt; } # open the info html file for writing open(INFO,">$infoFile") or die "Cannot open $infoFile $!\n"; # try and open the local template file for reading if (-e $infoTmpl) { open(INFOTMPL,"<$infoTmpl") or die "Cannot open $infoTmpl: $!\n"; } else { open(INFOTMPL,"<$gblInfoTmpl") or die "Cannot open $gblInfoTmpl: $!\n"; } # read each line of the template file and do what's appropriate # for each tag found $line = ; while ($line) { # check each of the keys in the theme file foreach $key (sort {length($b)<=>length($a)} keys %theme) { if ($line =~ /$key/) { if (defined $theme{$key}) { # process any special keys here # don't print slide links if we didn't gen slide-pages if ($key eq "PREV-SLIDE-LINK" and !$opts{gs}) { $line =~ s/$key//g; $themeLine = 1; } elsif ($key eq "NEXT-SLIDE-LINK" and !$opts{gs}) { $line =~ s/$key//g; $themeLine = 1; } elsif ($key eq "THIS-SLIDE-LINK" and !$opts{gs}) { $line =~ s/$key//g; $themeLine = 1; } elsif ($key eq "NEXT-INFO-LINK" and $curr1Index == ($#{$albumInfo->{images}} + 1)) { # this is the last info page $line =~ s/$key/LAST-INFO-NEXT-LINK/g; $themeLine = 1; } elsif ($key eq "NEXT-SLIDE-LINK" and $curr1Index == ($#{$albumInfo->{images}} + 1)) { # this is the last slide $line =~ s/$key/LAST-SLIDE-NEXT-LINK/g; $themeLine = 1; } elsif ($key eq "PREV-INFO-LINK" and $curr1Index == 1) { # this is the first info page $line =~ s/$key/FIRST-INFO-PREV-LINK/g; $themeLine = 1; } elsif ($key eq "PREV-SLIDE-LINK" and $curr1Index == 1) { # this is the first slide $line =~ s/$key/FIRST-SLIDE-PREV-LINK/g; $themeLine = 1; } else { # nothing special about this key $tmpVar = $theme{$key}; $line =~ s/$key/$tmpVar/g; $themeLine = 1; } } else { print "WARNING: $key was not defined in the theme file!\n"; } } } if ($line =~ /ORIG-IMAGE-NAME/) { $line =~ s/ORIG-IMAGE-NAME/$albumInfo->{images}[$curr0Index]->{file}/g; } if ($line =~ /NEXT-SLIDE-NAME/) { $tmpVar = $albumInfo->{images}[$next0Index]->{slide}; $tmpVar =~ s/ /\%20/g; $line =~ s/NEXT-SLIDE-NAME/$tmpVar/g; } if ($line =~ /THIS-SLIDE-HTML/) { $line =~ s/THIS-SLIDE-HTML/$slideFile/g; } if ($line =~ /PREV-SLIDE-HTML/) { $line =~ s/PREV-SLIDE-HTML/$prevSlideFile/g; } if ($line =~ /PREV-INFO-HTML/) { $line =~ s/PREV-INFO-HTML/$prevInfoFile/g; } if ($line =~ /INDEX-HTML/) { $line =~ s/INDEX-HTML/$indexFile/g; } if ($line =~ /NEXT-INFO-HTML/) { $line =~ s/NEXT-INFO-HTML/$nextInfoFile/g; } if ($line =~ /NEXT-SLIDE-HTML/) { $line =~ s/NEXT-SLIDE-HTML/$nextSlideFile/g; } if ($line =~ /INFO-IMAGE/) { # get the slide dimensions $slideX = $albumInfo->{images}[$curr0Index]->{slidex}; $slideY = $albumInfo->{images}[$curr0Index]->{slidey}; # set the Y dimension of the slide image to the value of # the -iy option and scale the X value to maintain the aspect # ratio. If slides height is smaller than the value of # the -iy option, don't do anything. if ($slideY > $opts{iy}) { $scaleVal = ($slideY/$opts{iy}); # this rounds the division to the nearest integer $slideX = sprintf("%.0f",($slideX / $scaleVal)); $slideY = $opts{iy}; } # get some other info about the slide and image $origImg = $albumInfo->{images}[$curr0Index]->{file}; $origImg =~ s/ /\%20/g; # make any spaces web friendly $slideImg = $albumInfo->{images}[$curr0Index]->{slide}; $slideImg =~ s/ /\%20/g; # make any spaces web friendly # check to see if we need to link the slide to the original if ($opts{lo}) { $tmpVar = ""; } else { $tmpVar = ""; } $line =~ s/INFO-IMAGE/$tmpVar/g; } if ($line =~ /SLIDE-DESCRIPTION/) { $line =~ s/SLIDE-DESCRIPTION/$albumInfo->{images}[$curr0Index]->{desc}/g; } if ($line =~ /SLIDE-COUNT/) { $tmpVar = "\(" . $curr1Index . "\/" . ($#{$albumInfo->{images}} + 1) . "\)"; $line =~ s/SLIDE-COUNT/$tmpVar/g; } if ($line =~ /EXIF-NAME-COL/) { # returns all the exif names available for this image. # each name (i.e. date, apature, shutter speed, flash...), # will have a before it, and a
after it. # this is really meant to be put in a column # do we have exifInfo to print if ($#{$albumInfo->{images}[$curr0Index]->{exif}} > 0) { $tmpVar = ""; # clear out the var # create the exif name column for $j (0 .. $#{$albumInfo->{images}[$curr0Index]->{exif}}) { $tmpVar = $tmpVar . "
$albumInfo->{images}[$curr0Index]->{exif}->[$j]{field}
\n"; } } else { $tmpVar = "
No EXIF info available
\n"; } $line =~ s/EXIF-NAME-COL/$tmpVar/g; } if ($line =~ /EXIF-VAL-COL/) { # returns all the exif values available for this image. # each value (corresponding to the names above) # will have a
before it, and a
after it. # this is really meant to be put in a column # do we have exifInfo to print if ($#{$albumInfo->{images}[$curr0Index]->{exif}} > 0) { $tmpVar = ""; # clear out the var # create the exif value column for $j (0 .. $#{$albumInfo->{images}[$curr0Index]->{exif}}) { $tmpVar = $tmpVar . "
 : $albumInfo->{images}[$curr0Index]->{exif}->[$j]{val}
\n"; } } else { $tmpVar = " \n"; } $line =~ s/EXIF-VAL-COL/$tmpVar/g; } # get the next line to process if ($themeLine != 0) { # we had a theme line, so process this line again. $themeLine = 0; # reset so we only process this line once. } else { # print out the line print INFO $line; # get new line $line = ; } } close INFO; # close the index.html file close INFOTMPL; # close the index_template file } } ########################################################### # genInfoTemplate - generate the template for each info page # sub genInfoTemplate { # put the default into template in the default theme dir. open(INFOTMPL,">$gblInfoTmpl") or die "Cannot open $gblInfoTmpl: $!\n"; print INFOTMPL < Info for image: ORIG-IMAGE-NAME SLIDE-COUNT
PREV-SLIDE-LINK PREV-INFO-LINK   NEXT-INFO-LINK NEXT-SLIDE-LINK
INDEX-LINK   THIS-SLIDE-LINK
 
INFO-IMAGE
EXIF-NAME-COL EXIF-VAL-COL
 
 
SLIDE-DESCRIPTION
SLIDE-COUNT
 
INDEX-LINK   THIS-SLIDE-LINK
PREV-SLIDE-LINK PREV-INFO-LINK   NEXT-INFO-LINK NEXT-SLIDE-LINK
endoftemplate close INFOTMPL; } ########################################################### # readTheme - read in the theme. # # We check to make sure the theme file exists in the checkOpts() func. # We can assume that the theme file exists in the global dir. If not, we # are creating a default theme. # # returns a hash where the key is the tag and the value is the html code to # insert. # sub readTheme { my (%opts) = %{$_[0]}; my %thash = (); # empty theme hash my $themeFile = $opts{theme} . ".theme"; my $gblThemeFile = $gblThemeDir . "/" . $opts{theme} . "/" . $themeFile; # Check to see if the theme file exists. if (-e $gblThemeFile) { $themeFile = $gblThemeFile; } else { if ($opts{theme} ne "default") { print "Error: $opts{theme} does not exist. Using default theme instead!\n"; $opts{theme} = "default"; } # we couldn't find the theme specified. Create the default # theme and use that. $themeFile = $gblThemeFile; print "Default theme did not exist. Creating.\n"; $dieMsg = "Cannot create the directory '$jiglRCDir'.\n"; mkdir $jiglRCDir,0755 or die "$dieMsg : $!\n" if (!-d $jiglRCDir); $dieMsg = "Cannot create the directory '$gblThemeDir'.\n"; mkdir $gblThemeDir,0755 or die "$dieMsg : $!\n" if (!-d $gblThemeDir); $defaultDir = $gblThemeDir . "/default"; $dieMsg = "Cannot create the directory '$defaultDir'.\n"; mkdir $defaultDir,0755 or die "$dieMsg : $!\n" if (!-d $defaultDir); &genDefaultTheme($themeFile); } # read in the theme file. open(THEME,"<$themeFile") or die "Cannot open '$themeFile': $!\n"; # make a hash of all the theme tags and their values my $currTag = ""; # key into the hash my $tagVal = ""; # value of the hash while () { chop $_; if ($_ =~ /^#/) { # skip - line is a comment } elsif ($_ =~ /^[ ]*$/) { # skip - line is blank } elsif ($_ =~ m/^<([A-Z\-_]+)>/) { # start of a tag definition $currTag = $1; $thash{$currTag} = ""; } elsif ($_ =~ /^<\/$currTag>/) { # end of a tag definition chop($thash{$currTag}); # remove the last newline of this tag. $currTag = ""; # reset the tag $tagVal = ""; # reset the value } elsif ($currTag ne "") { # we're inside a tag. Add this line to the currTag hash # put newline at end to preserve the html structure when viewing source $thash{$currTag} = $thash{$currTag} . $_ . "\n"; } else { # dunno what this line is print "Error in Theme: Cannot process this line: '$_'\n"; } } close THEME; if ($opts{d} == 5) { for $key (keys %thash) { print "$key : $thash{$key}\n"; } } return %thash; } ########################################################### # genDefaultTheme - Generate the default theme for jigl. # The default.theme file will be written in the current directory. # sub genDefaultTheme { my ($theme) = @_; open(THEME,">$theme") or die "Cannot open $theme: $!\n"; print THEME < Index # Link to a slides info page Info # Link to the current slide. Used on info page This Slide # Link to next slide Next >> # The Next Link to use on the last slide page Last Slide # Link to previous slide << Prev # The Prev Link to use on the first slide page First Slide # Link to next info page Next Info >> # The Next Link to use on the last info page Last Info # Link to previous info page << Prev Info # The Prev Link to use on the first info page First Info # Header area on index page

INDEX-HEADER-INFO
# Footer area on index page

INDEX-FOOTER-INFO
# Table around each row of thumbnails # This tag must be present and must contain the two tags: # IMG-ROW and SIZE-DIMENSION-ROW #
IMG-ROW SIZE-DIMENSION-ROW

# Row of the actual thumbnails # This tag must be present and must contain the IMG-COLUMN tag # IMG-COLUMN # Row of size and dimension information under each slide # This tag must be present and must contain the SIZE-DIMENSION-COLUMN tag. # SIZE-DIMENSION-COLUMN # An individual thumbnail # This tag must be present. # # The size and dimension information for an individual thumbnail # This tag must be present. # THUMB-DIMENSIONS THUMB-SIZE endoftheme close THEME; }