#!/usr/bin/perl -w
use strict;

#
# Web Gallery Generator (C) 2003 Derek Fountain All Rights Reserved
#
# v1.11 - 20030426 - Cleanups to the HTML
# v1.10 - 20030426 - Add EXIF information
# v1.01 - 20030205 - Small hack to prevent edited pages being overwritten
# v1.00 - 20030205 - First usable version
#
#    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
#
# This program generates a basic but function web interface for viewing a
# collection of images using a web browser. It runs on Linux (only SuSE
# has been tested but just about any modern distribution should work), and
# requires the Image Magick tools to be installed.
#
# It takes each image and creates a thumbnail for it, then copies both
# image and thumbnail into an output directory structure. It then produces 
# a set of "gallery" HTML pages, each containing 25 thumbnail images. The 
# thumbnails are made into HTML links to a set of individual web pages, one
# per image, which show the image full size.
#
# The output HTML is simple and entirely contained within this script. You
# can modify it to suit your website as required. It's not in the slightest
# bit complicated. :o) Just watch for the @@KEYWORD@@ strings - those are
# the ones which get replaced on the fly with file names, etc. See the code.
#
# The default settings in the HTML place a credit link to the original
# source for the script. It'd be nice if this was left in place so others
# can find the script, but this isn't obligatory.
#

BEGIN {
  our $haveEXIFSupport;
  if( eval("require Image::Info;" ) ) {
    Image::Info->import("image_info");
    $haveEXIFSupport = 1;
  } else {
    $haveEXIFSupport = 0;
  }
}
our $haveEXIFSupport;


my $usage = <<USAGE;
Usage:

  gallery.pl --pagename  pagename
             --title     title
             --directory directory
             \[--imagesonly\]
             \[--exif\]
             image_list

pagename is the filename of the page being generated. It will have _n.html
added (where 'n' starts at 0) for each page of 25 images generated

title is the HTML title for the page

directory is the directory where the HTML and images are copied

If \"--imagesonly\" is given, the HTML files are not recreated. The images
and thumbnails will still be copied into place. If you want to preserve
individual gallery or image HTML files, set them to read only and they will
be skipped.

If \"--exif\" is given, any EXIF data in the image file will be extracted
and added to the HTML file.

e.g.
gallery.pl --pagename "egypt" \\
           --title "Egypt 2002" \\
           --exif \\
           --directory /home/derek/public_html/photos/galleries/egypt \\
           /disks/turtle/pictures/digital_camera/egypt/*.jpg
USAGE


use Getopt::Long;
use File::Basename;
use File::Path;
use File::Copy;
use POSIX qw( ceil );

my $pageName;
my $title;
my $directory;
my $exif;
my $imagesOnly;

my $optionsResult = 
  GetOptions( "pagename=s",              \$pageName,
	      "title=s",                 \$title,
	      "directory=s",             \$directory,
	      "exif!",                   \$exif,
	      "imagesonly!",             \$imagesOnly );

if( !$optionsResult or
    !defined($pageName) or 
    !defined($title) or 
    !defined($directory) or
    scalar(@ARGV) == 0 ) {
  print STDERR $usage;
  exit(1);
}

if( $exif and !$haveEXIFSupport ) {
  print STDERR <<EXIF_WARNING;
Warning:

Can't do EXIF because the Images::Info module isn't available in your Perl
distribution. Try:

  perl -MCPAN -e 'install Image::Info'

EXIF information will not be generated.

EXIF_WARNING

  $exif = 0;
}

# Ensure pagename ends in HTML suffix
#
$pageName .= ".html" if( $pageName !~ /html$/ );
my $imageDirName = "_$pageName";

# Create directories for images and thumbs
#
my $imagesDir = $directory . "/$imageDirName/images";
my $thumbsDir = $directory . "/$imageDirName/thumbs";
eval { mkpath $imagesDir }; die "Unable to create $imagesDir\n" if( $@ );
eval { mkpath $thumbsDir }; die "Unable to create $thumbsDir\n" if( $@ );

# Loop over all image files given on command line copying them into the right
# place, and making a thumbnail from each
#
my @imageFiles = ();
my $index = 0;
foreach my $imageFile (@ARGV) {
  print "Copying ", basename($imageFile), " into place\n";
  if( ! -r $imageFile ) {
    print STDERR "Can't read $imageFile - skipping\n";
    next;
  }

  my $exifData = "";
  if( $exif ) {
    my $info = image_info( $imageFile );
    if( !$info->{error} ) {
      my %info = %{$info};

      if( exists($info{DateTime}) and
	  exists($info{ExposureTime}) and
	  exists($info{FNumber}) ) {
	$exifData .= "Date - $info{DateTime} <br/>".
	             "Shutter - $info{ExposureTime} <br/>".
                     "Aperture - " . eval($info{FNumber});
      }
    }
  }

  my $copyStatus = copy $imageFile, $imagesDir.'/'.basename($imageFile);
  if( !$copyStatus ) {
    print STDERR "Can't copy $imageFile to ",
                 $imagesDir.'/'.basename($imageFile), " - skipping\n";
    next;
  }

  print "Creating thumbnail for ", basename($imageFile), "\n";
  my $thumbFile = $thumbsDir.'/'.basename($imageFile);
  $copyStatus = copy $imageFile, $thumbFile;
  if( !$copyStatus ) {
    print STDERR "Can't copy $imageFile to $thumbFile - skipping\n";
    next;
  }
  system( "/usr/bin/mogrify -size 140x140 -resize 140x140 $thumbFile" );
  if( ($? >> 8) != 0 ) {
    print STDERR "Can't create thumb file $thumbFile - skipping\n";
    next;
  }

  $imageFiles[$index++] =
    { image => "$imageDirName/images/".basename($imageFile),
      thumb => "$imageDirName/thumbs/".basename($imageFile),
      exif  => $exifData,
      stats => (stat($imageFile))[7] };
}
exit(0) if( defined($imagesOnly) and $imagesOnly );

my $numGalleryPages = ceil(scalar(@imageFiles)/25);

my $internalDataPointer = tell DATA;

# Generate one gallery page for each 25 images
#
for( my $galleryPageNumber = 0; 
     $galleryPageNumber < $numGalleryPages; $galleryPageNumber++ ) {

  # Copy the gallery page skeleton into the required file. Each gallery
  # file has the gallery page number appended to it
  #
  (my $thisPageName = $pageName) =~ s/\.html$/_$galleryPageNumber\.html/;
  my $targetHTML = $directory . "/" . $thisPageName;

  # On the fly modifications of the values in the HTML skeleton
  #
  if( -e $targetHTML ) {
    if( !-w $targetHTML ) {
      print "Output file $targetHTML exists and is not writable - skipping\n";
      next;
    }
    my $foundNonEditedMark = 0;
    if( open HTMLHANDLE, $targetHTML ) {
      while( <HTMLHANDLE> ) {
	if( /\<p\>\<\/p\>/ ) {
	  $foundNonEditedMark = 1;
	  last;
	}
      }
      close HTMLHANDLE;
      if( ! $foundNonEditedMark ) {
	print "Output file $targetHTML exists and has been edited - ".
              "skipping\n";
	next;
      }
    }
  }
  open OUT_HANDLE, ">$targetHTML" or 
    die "Can't open $targetHTML - disk full?\n";

  seek DATA, $internalDataPointer, 0;
  while( <DATA> ) {

    # Lines which don't have @@BLAH@@ type structs are passed straight through
    #
    if( ! /\@\@(\w+)\@\@/ ) {
      print OUT_HANDLE $_;
      next;
    }

    my $line = $_;
    my $outputLine;
    my $htmlBit;
    if( $1 eq "TITLE" ) {

      # Title substitution - simple
      #
      ($outputLine = $line) =~ s/\@\@TITLE\@\@/$title/;

    } elsif( $1 =~ /(\w*IMG)(\d+)/ ) {

      # Image or image statistics substitution
      #

      # This gives the number of the image being processed, at 25 per page
      #
      my $imageNumber = $2 + ($galleryPageNumber * 25);
    
      # If there is a processed image corresponding to the number in the
      # skeleton I drop an IMG tag in for it. Otherwise use an empty string
      #
      if( $imageNumber > $#imageFiles ) {

	# No images left, leave this slot in the table empty
	#
	$outputLine = "<td/>";

      } else {

	# Find the image data recorded earlier
	#
	my %imageData = %{$imageFiles[$imageNumber]};

	if( $1 eq "IMG" ) {

	  # Image substitution. Prepare "previous" link for all except the
	  # first image on the gallery page; prepare "next" link for all
	  # except the last image on the gallery page
	  #
	  my $previous ;
	  my $next;
	  if( $imageNumber%25 != 0 ) {
	    my %previousImageData = %{$imageFiles[$imageNumber-1]};
	    $previous = basename($previousImageData{image}) . ".html";
	  }
	  if( $imageNumber%25 != 24 and
	      $imageNumber < scalar(@imageFiles)-1 ) {
	    my %nextImageData = %{$imageFiles[$imageNumber+1]};
	    $next = basename($nextImageData{image}) . ".html";
	  }

	  # Create an HTML page for this image, and generate the HTML bit
	  # to insert into this gallery page as a link to it
	  #
	  generateImagePage( $imageData{image},
			     $directory . "/" . $imageData{image} . ".html",
			     "../../$thisPageName",
			     $previous,
			     $next,
			     $imageData{exif} );
	  $htmlBit = "<a href=\"$imageData{image}.html\">" .
	             "<img src=\"$imageData{thumb}\" border=\"0\" alt=\"\">".
		     "</a>";
	} else {

	  # Image statistics substitution - just the image size
	  #
	  $htmlBit = int($imageData{stats} / 1024) . "K";
	}

	# Create the output line as per input, only substituting the
	# @@ marker for the generated HTML bit
	#
	($outputLine = $line) =~ s/\@\@\w*IMG\d+\@\@/$htmlBit/;    
      }

    } elsif( $1 =~ /PREVIOUS/ ) {

      # Link to previous page substitution
      #
      if( $galleryPageNumber == 0 ) {
	$htmlBit = "";
      } else {
	my $previousPageNumber = $galleryPageNumber - 1;
	(my $prevPageName = $pageName) =~ 
	                               s/\.html$/_$previousPageNumber\.html/;
	$htmlBit = "<a href=\"$prevPageName\">Previous</a>";
      }
      ($outputLine = $line) =~ s/\@\@PREVIOUS\@\@/$htmlBit/;    

    } elsif( $1 =~ /NEXT/ ) {

      # Link to next page substitution
      #
      if( $galleryPageNumber == $numGalleryPages-1 ) {
	$htmlBit = "";
      } else {
	my $nextPageNumber = $galleryPageNumber + 1;
	(my $nextPageName = $pageName) =~ 
	                               s/\.html$/_$nextPageNumber\.html/;
	$htmlBit = "<a href=\"$nextPageName\">Next</a>";
      }
      ($outputLine = $line) =~ s/\@\@NEXT\@\@/$htmlBit/;    

    } elsif( $1 =~ /NOFN/ ) {

      # Substitutute the "Page n of n" thing
      #
      $htmlBit = sprintf( "Page %d of %d", $galleryPageNumber+1,
                                           $numGalleryPages );
      ($outputLine = $line) =~ s/\@\@NOFN\@\@/$htmlBit/;  

    } else {

      # Pass it through
      #
      $outputLine = $line;
    }
    
    print OUT_HANDLE $outputLine;
  }

  close OUT_HANDLE;
}



# Subroutine to create a page containing one image
#
# Takes:
#
#  name of image file
#  name of html file to create
#  name of gallery html file to return to for the "up" link
#  name of html file to move to for "previous"
#  name of html file to move to for "next"
#
# The latter two may be undefined.
#
# Returns nothing useful.
#
sub generateImagePage {
  my( $imageFile, $htmlFile, $galleryPage,
      $previous, $next, $imageEXIFData ) = @_;

  # What to do if the HTML file already exists?
  #
  if( -e $htmlFile ) {
    if( !-w $htmlFile ) {
      print "Output file $htmlFile exists and is not writable - skipping\n";
      return;
    }
    my $foundNonEditedMark = 0;
    open HTMLHANDLE, $htmlFile or return;
    while( <HTMLHANDLE> ) {
      if( /\<p\>\<\/p\>/ ) {
	$foundNonEditedMark = 1;
	last;
      }
    }
    close HTMLHANDLE;
    if( ! $foundNonEditedMark ) {
      print "Output file $htmlFile exists and has been edited - skipping\n";
      return;
    }
  }

  my( $imageHTML, $previousHTML, $nextHTML );

  my $imageFileBasename = basename $imageFile;

  if( !defined( $previous ) ) {
    $previousHTML = "&nbsp;";
  } else {
    $previousHTML = "<a href=\"$previous\">Previous image in gallery</a>";
  }

  if( !defined( $next ) ) {
    $nextHTML = "&nbsp;";
    $imageHTML = "<a href=\"$galleryPage\"><img border=\"0\" " .
                 "src=\"$imageFileBasename\" alt=\"\"/></a>";
  } else {
    $nextHTML = "<a href=\"$next\">Next image in gallery</a>";
    $imageHTML = "<a href=\"$next\"><img border=\"0\" " .
                 "src=\"$imageFileBasename\" alt=\"\"/></a>";
  }

  open IMAGE_HANDLE, ">$htmlFile" or die "Can't open $htmlFile\n";

################################################
#
# Here document containing image page HTML
#
  print IMAGE_HANDLE <<END_IMAGE_DATA;
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
<head><title>$imageFile</title>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">
</head>
<body bgcolor="black" text="yellow" link="skyblue" vlink="red">


<table align="center">
<tr>
<td><h1 align="center">$imageFileBasename</h1></td>
</tr>

<tr>
<td><h5 align="center"><a href="$galleryPage">Back to gallery</a></h5></td>
</tr>

<tr>
<td align="left">$previousHTML</td>
<td />
<td align="right">$nextHTML</td>
</tr>

<tr>
<td colspan="3">$imageHTML</td>
</tr>

<tr align="center">
<td colspan="3"><font size=-1>$imageEXIFData</font></td>
</tr>

<tr>
<td align="left">$previousHTML</td>
<td />
<td align="right">$nextHTML</td>
</tr>
</table>

<!-- Insert your comments for this image in this paragraph.
     The empty paragraph tag sequence is looked for when the script recreates
     a file.
     If it's not there, the script assumes the HTML has been edited with
     captions, etc., and doesn't overwrite it
-->
<div align="center"><p></p></div>

</body>
</html>
END_IMAGE_DATA
#
##################################################

  close IMAGE_HANDLE;
}


# This data is for the gallery page skeleton
#
__DATA__
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>@@TITLE@@</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body bgcolor="black" text="yellow" link="skyblue" vlink="red">
<h1 align="center">@@TITLE@@</h1>

<!-- Insert your comments for this image in this paragraph.
     The empty paragraph tag sequence is looked for when the script recreates
     a file.
     If it's not there, the script assumes the HTML has been edited with
     captions, etc., and doesn't overwrite it
-->
<div align="center"><p></p></div>

<table align="center" cellspacing="4">

<tr>
<td colspan="2" align="left">@@PREVIOUS@@</td>
<td align="center">@@NOFN@@</td>
<td colspan="2" align="right">@@NEXT@@</td>
</tr>

<tr>
<td align="center">@@IMG0@@</td>
<td align="center">@@IMG1@@</td>
<td align="center">@@IMG2@@</td>
<td align="center">@@IMG3@@</td>
<td align="center">@@IMG4@@</td>
</tr>

<tr>
<td align="center"><font size=1>@@STATSIMG0@@</font></td>
<td align="center"><font size=1>@@STATSIMG1@@</font></td>
<td align="center"><font size=1>@@STATSIMG2@@</font></td>
<td align="center"><font size=1>@@STATSIMG3@@</font></td>
<td align="center"><font size=1>@@STATSIMG4@@</font></td>
</tr>

<tr>
<td align="center">@@IMG5@@</td>
<td align="center">@@IMG6@@</td>
<td align="center">@@IMG7@@</td>
<td align="center">@@IMG8@@</td>
<td align="center">@@IMG9@@</td>
</tr>

<tr>
<td align="center"><font size=1>@@STATSIMG5@@</font></td>
<td align="center"><font size=1>@@STATSIMG6@@</font></td>
<td align="center"><font size=1>@@STATSIMG7@@</font></td>
<td align="center"><font size=1>@@STATSIMG8@@</font></td>
<td align="center"><font size=1>@@STATSIMG9@@</font></td>
</tr>

<tr>
<td align="center">@@IMG10@@</td>
<td align="center">@@IMG11@@</td>
<td align="center">@@IMG12@@</td>
<td align="center">@@IMG13@@</td>
<td align="center">@@IMG14@@</td>
</tr>

<tr>
<td align="center"><font size=1>@@STATSIMG10@@</font></td>
<td align="center"><font size=1>@@STATSIMG11@@</font></td>
<td align="center"><font size=1>@@STATSIMG12@@</font></td>
<td align="center"><font size=1>@@STATSIMG13@@</font></td>
<td align="center"><font size=1>@@STATSIMG14@@</font></td>
</tr>

<tr>
<td align="center">@@IMG15@@</td>
<td align="center">@@IMG16@@</td>
<td align="center">@@IMG17@@</td>
<td align="center">@@IMG18@@</td>
<td align="center">@@IMG19@@</td>
</tr>

<tr>
<td align="center"><font size=1>@@STATSIMG15@@</font></td>
<td align="center"><font size=1>@@STATSIMG16@@</font></td>
<td align="center"><font size=1>@@STATSIMG17@@</font></td>
<td align="center"><font size=1>@@STATSIMG18@@</font></td>
<td align="center"><font size=1>@@STATSIMG19@@</font></td>
</tr>

<tr>
<td align="center">@@IMG20@@</td>
<td align="center">@@IMG21@@</td>
<td align="center">@@IMG22@@</td>
<td align="center">@@IMG23@@</td>
<td align="center">@@IMG24@@</td>
</tr>

<tr>
<td align="center"><font size=1>@@STATSIMG20@@</font></td>
<td align="center"><font size=1>@@STATSIMG21@@</font></td>
<td align="center"><font size=1>@@STATSIMG22@@</font></td>
<td align="center"><font size=1>@@STATSIMG23@@</font></td>
<td align="center"><font size=1>@@STATSIMG24@@</font></td>
</tr>

<tr>
<td colspan="2" align="left">@@PREVIOUS@@</td>
<td align="center">@@NOFN@@</td>
<td colspan="2" align="right">@@NEXT@@</td>
</tr>

</table>

<div align="center">
<br><br>
<font size="1">Gallery generated with
<a href="http://derekfountain.webhop.org/hacks/web_gallery">
The Web Image Gallery Generator</a></font>
</div>

</body>
</html>

