2995 lines
85 KiB
Perl
Executable file
2995 lines
85 KiB
Perl
Executable file
#!/usr/bin/perl -w
|
|
#
|
|
## this versions hasa some minor changes by age@systemausfall.org
|
|
#
|
|
# Edwin Huffstutler <edwinh@computer.org>
|
|
# John Reynolds <johnjen@reynoldsnet.org>
|
|
#
|
|
# perl script for: web page index/thumbnails of photos.
|
|
# orginally started life as a menu selector for fvwm2 backgrounds...
|
|
#
|
|
# USAGE:
|
|
#
|
|
# imageindex [options] <directory>
|
|
#
|
|
# <directory> is assumed to be "." if not given
|
|
#
|
|
# Options: (can be abbreviated if unique)
|
|
#
|
|
# -title <string> title for this page (saved for susbsequent runs)
|
|
# -destdir <dir> export image/html tree to dir (will be created if needed)
|
|
# -[no]recurse enable/disable recursion into subdirectories
|
|
# -[no]medium enable/disable medium size images/links
|
|
# -[no]slide enable/disable slideshow files
|
|
# -[no]detail enable/disable detail file
|
|
# -[no]dirs enable/disable directory entries
|
|
# -[no]montage enable/disable directory montages
|
|
# -forceregen force regeneration of thumbnails
|
|
# -columns <num> number of columns in html table (saved for susbsequent runs)
|
|
# -exclude <file> Exclude <file> from processing. Can be used multiple times
|
|
# -includeall Nullifies excluded file list (saved from previous run)
|
|
# -skipmont <file> Exclude <file> from being included in a directory montage.
|
|
# -reverse Order timestamps with newest first
|
|
# -x <num> override thumbnail x size
|
|
# -y <num> override thumbnail y size
|
|
# -help show this text
|
|
# -version show the current version
|
|
# -d 'var=val' force override of global variable
|
|
#
|
|
# See also the configuration section at the top of the program itself,
|
|
# or in ~/.imageindexrc
|
|
#
|
|
# (non-html-generating, utility options)
|
|
#
|
|
# -lowercase Lowercase all image files in a directory
|
|
# -caption <file> <string> Store comment in image
|
|
# -rotate <file> [cw|ccw] Rotate an image clockwise or counterclockwise
|
|
# -showexcluded Show which files were excluded in a prior run
|
|
#
|
|
######################################################################
|
|
|
|
#
|
|
# Configuration options
|
|
#
|
|
|
|
# sizes / dirs
|
|
$thumbnail_dir = 'thumbnail';
|
|
$default_thumbnail_x = 200;
|
|
$default_thumbnail_y = 200;
|
|
|
|
# If both dimensions of the original are within this much of the thumb
|
|
# dimensions we will skip the thumbnail and just use the original
|
|
$thumbnail_threshold = 1.0;
|
|
|
|
$med_x = 512;
|
|
$med_y = 384;
|
|
$med_dir = 'medium';
|
|
|
|
# If both dimensions of the original are within this much of the "medium"
|
|
# dimensions we will skip creating the medium-size format and just use the
|
|
# original
|
|
$med_threshold = 1.6;
|
|
|
|
# Enable/disable features, set default for various flags
|
|
$do_recurse = 0; # Recurse into subdirs?
|
|
$do_medium = 1; # Generate medium-format?
|
|
$do_slide = 1; # Generate slides/frame view?
|
|
$do_detail = 1; # Generate details page?
|
|
$do_dirs = 1; # Create directory entries?
|
|
$do_montage = 1; # Create directory montages?
|
|
$do_emoticons = 1; # Replace ASCII smiley's with images?
|
|
$do_reverse = 0; # Sort timestamps in reverse order?
|
|
|
|
# What the various image links point to - can be 'index', 'fullsize',
|
|
# 'medium', 'thumbnail', 'slide', or 'details'
|
|
$index_linkto = 'slide';
|
|
$details_linkto = 'index';
|
|
$slide_linkto = 'fullsize';
|
|
|
|
# Default number of columns to use
|
|
$default_columns = 3;
|
|
|
|
# Orientation of slide frame - 'horizontal' or 'vertical'
|
|
$frame_orient = 'vertical';
|
|
|
|
# Location of items in slide pages; 'top', 'bottom', or 'none'
|
|
$slide_caption = 'top';
|
|
$slide_date = 'bottom';
|
|
|
|
# Details index uses thumbs reduced by this amount
|
|
$detailshrink = 2;
|
|
|
|
# Quality for generated images
|
|
$thumb_quality = 50;
|
|
$med_quality = 80;
|
|
|
|
# Minimum and maximum number of tiles in directory montage images
|
|
$montage_min = 4;
|
|
$montage_max = 36;
|
|
|
|
# Space between montage images
|
|
$montage_whitespace = 2;
|
|
|
|
# What to do with leftover montage tiles; can be
|
|
# 'blank' or 'repeat'
|
|
$montage_fill = 'blank';
|
|
|
|
# Stylesheet specs
|
|
# Set element font, etc. properties here
|
|
$stylesheet = '
|
|
body { color: black; background: white; }
|
|
|
|
/* Fonts in the title */
|
|
h1.title { font-family: "Comic Sans MS",Helvetica,sans-serif; font-size: 200%; font-weight: bold; text-align: center; }
|
|
h2.daterange { font-family: Arial,Helvetica,sans-serif; font-size: 125%; text-align: center; }
|
|
h3 { font-family: Arial,Helvetica,sans-serif; font-size: 90%; text-align: center; }
|
|
|
|
/* Photo captions & Directory titles */
|
|
div.caption { font-family: Arial,Helvetica,sans-serif; font-size: 100%; font-weight: bold; margin: 1em; }
|
|
|
|
/* Overall fonts on the index and details page */
|
|
div.index { font-family: Arial,Helvetica,sans-serif; font-size: 80%; }
|
|
div.detail { font-family: Arial,Helvetica,sans-serif; font-size: 80%; }
|
|
div.credits { font-family: Arial,Helvetica,sans-serif; font-size: 80%; text-align: right; margin: 10px }
|
|
|
|
/* Table attributes */
|
|
table.index { background: #ffffff; border: none; border-spacing: 8px; }
|
|
td.index { border: none; padding: 3px }
|
|
table.frame { background: #ffffff; border: none }
|
|
td.frame { border: none; padding: 0px }
|
|
|
|
/* Image attributes */
|
|
img.index { border: none; }
|
|
img.slide { border: none; }
|
|
img.frame { border: none; }
|
|
|
|
/* Link attributes */
|
|
a:link { color: blue; }
|
|
a:visited { color: green; }
|
|
a:hover { color: red; }
|
|
a:active { color: red; }
|
|
|
|
';
|
|
|
|
|
|
# Text
|
|
$emptycell = "<I>empty</I>";
|
|
$updirtext = "up one directory";
|
|
$framelinktext = "slideshow view (frames)";
|
|
$detaillinktext = "details index";
|
|
$indexlinktext = "main index";
|
|
$default_titletext = "Image directory";
|
|
|
|
# These five variables control the TITLE attribute on anchor constructs in the
|
|
# index and frame views. When TITLE attributes are given they are usually
|
|
# rendered as "tooltip" bubbles that show text when a cursor hovers and stops
|
|
# over the active link. We use them here to give a visual cue about the image.
|
|
# These variables work much like printf(1) strings.
|
|
#
|
|
# %f => replaced with the filename of the image
|
|
# %d => replaced with the date/time of the image (or mtime of the file)
|
|
# %s => replaced with the size of the file (in Kb)
|
|
# %r => replaced with the resolution (XxY) of the original image
|
|
# %c => replaced with the image's caption (if stored with one)
|
|
# %% => replaced with a literal '%' character
|
|
#
|
|
# The following are used when directories are processed and a montage of
|
|
# that directory is used as the thumbnail of the dir.
|
|
#
|
|
# %n => replaced with number of images in a directory
|
|
# %b => replaced with the "begin" date from a directory of images
|
|
# %e => replaced with the "end" date from a directory of images
|
|
# %t => replaced with the "title" from a directory of images
|
|
#
|
|
# Other characters (including spaces) are literal. "undef" these in
|
|
# your ~/.imageindexrc file if you don't want them to show up. The "date/time"
|
|
# related constructs are interpolated using the date/time format variables
|
|
# defined below.
|
|
#
|
|
$framethumbtitle = "%f - %d";
|
|
$indexthumbtitle = "%f (%s)";
|
|
$slidethumbtitle = "%f (%s)";
|
|
$detailthumbtitle = "%c";
|
|
$montagetitle = "%n images %b through %e";
|
|
|
|
# Date/Time format strings. These strings are formatted much like the above
|
|
# variables and the definitions of the escape sequences come from the POSIX
|
|
# strftime(3) definitions. NOT ALL of strftime(3) are supported for obvious
|
|
# reasons.
|
|
#
|
|
# %S is replaced by the second as a decimal number (00-60).
|
|
# %M is replaced by the minute as a decimal number (00-59).
|
|
# %I is replaced by the hour (12-hour clock) as a decimal number (01-12).
|
|
# %H is replaced by the hour (24-hour clock) as a decimal number (00-23).
|
|
# %p is replaced by national representation of either "ante meridiem" or
|
|
# "post meridiem" as appropriate (currently only U.S. "am" or "pm")
|
|
# %R is equivalent to "%H:%M" (in *timeformat variables only).
|
|
# %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only).
|
|
#
|
|
# %Y is replaced by the year with century as a decimal number.
|
|
# %y is replaced by the year without century as a decimal number (00-99).
|
|
# %m is replaced by the month as a decimal number (01-12).
|
|
# %d is replaced by the day of the month as a decimal number (01-31).
|
|
# %F is equivalent to "%Y-%m-%d" (in *dateformat variables only).
|
|
# %D is equivalent to "%m/%d/%y" (in *dateformat variables only).
|
|
# %% is replaced by a literal "%".
|
|
|
|
$framedateformat = "%m/%d/%Y";
|
|
$frametimeformat = "%r";
|
|
|
|
$indexdateformat = "%m/%d/%Y";
|
|
$indextimeformat = "%r";
|
|
|
|
$slidedateformat = "%m/%d/%Y";
|
|
$slidetimeformat = "%r";
|
|
|
|
$detaildateformat = "%m/%d/%Y";
|
|
$detailtimeformat = "%I:%M %p";
|
|
|
|
# Pathnames
|
|
$indexfile = 'index.html';
|
|
$detailfile = 'details.html';
|
|
$framefile = 'frame.html';
|
|
$slidefile = 'slides.html';
|
|
$slide_dir = 'slides';
|
|
$stylefile = 'style.css';
|
|
$montagefile = 'montage.jpg';
|
|
$emoticonprefix = 'ii_';
|
|
$emoticonsmile = $emoticonprefix . 'smile.png';
|
|
$emoticonwink = $emoticonprefix . 'wink.png';
|
|
$emoticonfrown = $emoticonprefix . 'frown.png';
|
|
|
|
# File exclusion customization (regex)
|
|
# (Anything non-image and non-dir will be skipped automatically, this just
|
|
# makes it silent)
|
|
@exclude = qw(
|
|
^CVS$
|
|
^.nautilus-metafile.xml$
|
|
^.thumbnails$
|
|
^.xvpics$
|
|
^.thumbcache$
|
|
^ALBUM.OFA$
|
|
^desktop.ini$
|
|
);
|
|
|
|
# Metatags
|
|
$columnsmetatag = 'Columns';
|
|
$titlemetatag = 'Title';
|
|
$begindatemetatag = 'DateBegin';
|
|
$enddatemetatag = 'DateEnd';
|
|
$excludemetatag = 'ExcludedFiles';
|
|
$skipmetatag = 'SkipMontageFiles';
|
|
$numimagesmetatag = 'NumImages';
|
|
$reversemetatag = 'Reverse';
|
|
$thumbxmetatag = 'ThumbnailX';
|
|
$thumbymetatag = 'ThumbnailY';
|
|
|
|
# Any of the above can be overridden in an rc file in the user's home dir
|
|
$rcfile = "$ENV{'HOME'}/.imageindexrc";
|
|
|
|
######################################################################
|
|
#
|
|
# $Id: imageindex,v 1.164 2003/12/30 23:12:00 jjreynold Exp $
|
|
#
|
|
# imageindex is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2, or (at your option)
|
|
# any later version.
|
|
#
|
|
# imageindex is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with imageindex; see the file COPYING.
|
|
#
|
|
######################################################################
|
|
|
|
use Image::Magick; # comes with ImageMagick
|
|
|
|
# from CPAN - optional
|
|
eval('use Image::Info qw(image_info)');
|
|
|
|
# Shipped with perl
|
|
use POSIX;
|
|
use Getopt::Long;
|
|
use FileHandle;
|
|
use File::Basename;
|
|
use File::Copy;
|
|
use English;
|
|
use Carp;
|
|
require 'flush.pl';
|
|
|
|
# to shut up -w
|
|
use vars qw($opt_recurse);
|
|
use vars qw($opt_slide);
|
|
use vars qw($opt_dirs);
|
|
use vars qw($opt_detail);
|
|
use vars qw($opt_lowercase);
|
|
use vars qw($opt_help);
|
|
use vars qw($opt_debug);
|
|
use vars qw($opt_showexcluded);
|
|
use vars qw($opt_version);
|
|
|
|
&GetOptions(
|
|
'title=s',
|
|
'columns=i',
|
|
'x=i',
|
|
'y=i',
|
|
'forceregen',
|
|
'medium!',
|
|
'slide!',
|
|
'detail!',
|
|
'dirs!',
|
|
'montage!',
|
|
'recurse!',
|
|
'destdir=s',
|
|
'lowercase',
|
|
'caption=s',
|
|
'rotate=s',
|
|
'exclude=s@',
|
|
'skipmont=s@',
|
|
'showexcluded',
|
|
'includeall',
|
|
'version',
|
|
'help',
|
|
'debug',
|
|
'reverse!',
|
|
'd=s%'
|
|
) or die ("Invalid flag\n");
|
|
|
|
# Find out which platform we're on so we don't give incorrect options to needed
|
|
# commands
|
|
#
|
|
$uname = `uname -s`;
|
|
chomp ($uname);
|
|
|
|
# Override config variables
|
|
foreach my $var (keys %opt_d) {
|
|
$value = $opt_d{$var};
|
|
print "(override) $var = $value\n";
|
|
eval("\$$var=\"$value\"");
|
|
}
|
|
|
|
&init_png_array();
|
|
|
|
# Read RC file
|
|
if (-e $rcfile) {
|
|
print "Using settings in $rcfile...\n" if ! defined ($opt_version);
|
|
require $rcfile;
|
|
}
|
|
|
|
# Rotate or caption image (then exit)
|
|
if (defined ($opt_rotate)) {
|
|
&rotate_image($opt_rotate,\@ARGV);
|
|
exit (0);
|
|
} elsif (defined ($opt_caption)) {
|
|
&caption_image($opt_caption,\@ARGV);
|
|
exit (0);
|
|
} elsif (defined ($opt_showexcluded)) {
|
|
&showexcluded($ARGV[0]);
|
|
exit (0);
|
|
} elsif (defined ($opt_version)) {
|
|
printf ("imageindex version: %s\n", &versionstring);
|
|
exit (0);
|
|
}
|
|
|
|
# The directory to search is the first argument
|
|
if (defined($ARGV[0])) {
|
|
$srcdir = $ARGV[0];
|
|
$srcdir =~ s:/$::;
|
|
} else {
|
|
$srcdir = ".";
|
|
}
|
|
|
|
# Give usage message
|
|
if (defined($opt_help)) {
|
|
&usage();
|
|
exit(0);
|
|
}
|
|
|
|
# Show backtrace if debug given
|
|
if (defined($opt_debug)) {
|
|
$SIG{__WARN__} = \&Carp::cluck;
|
|
}
|
|
|
|
# Where to generate files
|
|
$destdir = $srcdir;
|
|
if (defined($opt_destdir)) {
|
|
$destdir = $opt_destdir;
|
|
$destdir =~ s:/$::;
|
|
print "Exporting to $destdir\n";
|
|
unless (-d $destdir) {
|
|
printf ("Creating destination directory '$destdir'.\n");
|
|
mkdir ($destdir, 0755);
|
|
}
|
|
}
|
|
|
|
unless (-w $destdir) {
|
|
printf ("No write permission for $destdir\n");
|
|
exit (1);
|
|
}
|
|
|
|
if (defined($opt_medium)) {
|
|
$do_medium = $opt_medium
|
|
}
|
|
|
|
if (defined($opt_slide)) {
|
|
$do_slide = $opt_slide;
|
|
}
|
|
|
|
if (defined($opt_detail)) {
|
|
$do_detail = $opt_detail;
|
|
}
|
|
|
|
if (defined($opt_dirs)) {
|
|
$do_dirs = $opt_dirs;
|
|
}
|
|
|
|
if (defined($opt_montage)) {
|
|
$do_montage = $opt_montage;
|
|
}
|
|
|
|
if (defined($opt_recurse)) {
|
|
$do_recurse = $opt_recurse;
|
|
}
|
|
|
|
# no montages if we aren't doing dirs anyway
|
|
if ($do_dirs == 0) {
|
|
$do_montage = 0;
|
|
}
|
|
|
|
&initialize_current_vars();
|
|
&read_stored_meta_data();
|
|
&override_by_commandline();
|
|
|
|
if (!defined(&image_info)) {
|
|
print "Image::Info not found, not extracting EXIF data\n";
|
|
}
|
|
|
|
opendir(DIR, "$srcdir") || die "Can't open dir $srcdir: ($!)\n";
|
|
@files = readdir DIR;
|
|
closedir(DIR);
|
|
@files = grep (!/^\.?\.$/, @files);
|
|
|
|
# Skip the files/dirs we use or generate. Any other patterns go in the
|
|
# config section (@exclude) or in exclude file
|
|
my @generated_files = ($thumbnail_dir, $med_dir, $slide_dir,
|
|
$indexfile, $detailfile, $stylefile,
|
|
);
|
|
|
|
foreach my $pattern (@generated_files, @exclude) {
|
|
@files = grep (!/$pattern/, @files);
|
|
}
|
|
|
|
@files = &exclude_files(@files);
|
|
|
|
# Change all the names of image files to lowercase.
|
|
if (defined ($opt_lowercase)) {
|
|
&lower_case_files(@files);
|
|
exit (0);
|
|
}
|
|
|
|
# Keep track of which column to be in
|
|
my $col_counter = 1;
|
|
|
|
# Count how many files we create
|
|
my $object_counter = 0;
|
|
my $dir_counter = 0;
|
|
my $image_counter = 0;
|
|
my $thumbnail_counter = 0;
|
|
my $med_counter = 0;
|
|
my $slide_counter = 0;
|
|
my $modified_thumb = 0;
|
|
|
|
# Keep track of max thumb sizes to use for slide frame width
|
|
my $max_thumb_x = 0;
|
|
my $max_thumb_y = 0;
|
|
|
|
# Keep track of max thumb sizes to use for montage creation
|
|
my $max_mont_thumb_x = 0;
|
|
my $max_mont_thumb_y = 0;
|
|
|
|
# Extract info
|
|
print "Extracting image info";
|
|
flush (STDOUT);
|
|
|
|
foreach my $file (@files) {
|
|
|
|
# If directory, grab the timestamp
|
|
if (-d "$srcdir/$file") {
|
|
|
|
my $ts;
|
|
|
|
# Grab timestamp from meta tag
|
|
if (-e "$srcdir/$file/$indexfile") {
|
|
|
|
my $begin = &extract_meta_tag($begindatemetatag,"$srcdir/$file/$indexfile");
|
|
if (defined($begin)) {
|
|
if (!defined($firstdate) or ($begin < $firstdate)) {
|
|
$firstdate = $begin;
|
|
}
|
|
$ts = $begin;
|
|
}
|
|
|
|
my $end = &extract_meta_tag($enddatemetatag,"$srcdir/$file/$indexfile");
|
|
if (defined($end)) {
|
|
if (!defined($lastdate) or ($end > $lastdate)) {
|
|
$lastdate = $end;
|
|
}
|
|
$ts = $end if (!defined($ts));
|
|
}
|
|
|
|
}
|
|
|
|
# Fallback on dir mtime
|
|
if (!defined($ts)) {
|
|
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks) = stat("$srcdir/$file");
|
|
$ts = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
|
|
}
|
|
|
|
push(@{$dir_timestamp{$ts}}, $file);
|
|
|
|
} else {
|
|
|
|
# Collect info from the image
|
|
&fill_image_info($file);
|
|
|
|
}
|
|
}
|
|
print "\n";
|
|
|
|
|
|
# Do dirs first
|
|
if ($do_dirs) {
|
|
foreach my $ts (sort bynumber keys %dir_timestamp) {
|
|
foreach my $dir (sort @{$dir_timestamp{$ts}}) {
|
|
&dir_entry($dir);
|
|
} # foreach dir that has this timestamp
|
|
} # foreach timestamp
|
|
}
|
|
|
|
|
|
# Bail if nothing here
|
|
if ($object_counter == 0) {
|
|
print "Nothing to do!\n";
|
|
unlink("$destdir/$indexfile") if (-e "$destdir/$indexfile");
|
|
unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile");
|
|
unlink("$destdir/$stylefile") if (-e "$destdir/$stylefile");
|
|
exit(0);
|
|
}
|
|
|
|
# Make thumb dirs if needed
|
|
foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) {
|
|
unless (-d "$destdir/$checkdir") {
|
|
mkdir("$destdir/$checkdir",0777);
|
|
}
|
|
}
|
|
|
|
# Nuke old thumbnails if original image gone
|
|
&nuke_out_of_date();
|
|
|
|
# Iterate over the files based on timestamp
|
|
# This is just to get back/forward links
|
|
undef $prev;
|
|
foreach (sort bynumber keys %timestamp) {
|
|
|
|
foreach my $pathname (sort @{$timestamp{$_}}) {
|
|
|
|
if (defined($prev)) {
|
|
my ($name,$path,$suffix);
|
|
|
|
($name,$path,$suffix) = fileparse($prev,'\.\S+');
|
|
$back{$pathname} = "$name.html";
|
|
|
|
($name,$path,$suffix) = fileparse($pathname,'\.\S+');
|
|
$forward{$prev} = "$name.html";
|
|
}
|
|
$prev = $pathname;
|
|
|
|
} # foreach image that has this timestamp
|
|
|
|
} # foreach timestamp
|
|
|
|
# Iterate over the files based on timestamp
|
|
# This will do the real work
|
|
foreach (sort bynumber keys %timestamp) {
|
|
|
|
foreach my $pathname (sort @{$timestamp{$_}}) {
|
|
|
|
my $filename = $info{$pathname}{'file'};
|
|
my $thumbnail = $info{$pathname}{'thumb'};
|
|
my $medium = $info{$pathname}{'medium'};
|
|
my $slide = $info{$pathname}{'slide'};
|
|
|
|
if (!defined($firstdate) or ($info{$pathname}{'date'} < $firstdate)) {
|
|
$firstdate = $info{$pathname}{'date'};
|
|
}
|
|
|
|
if (!defined($lastdate) or ($info{$pathname}{'date'} > $lastdate)) {
|
|
$lastdate = $info{$pathname}{'date'};
|
|
}
|
|
|
|
#
|
|
# First, deal with medium format of the image since we can save time shrinking
|
|
# the medium down to the thumbnail rather than fullsize->thumbnail
|
|
#
|
|
|
|
# Skip if we want no medium images at all
|
|
#
|
|
if ($do_medium == 0) {
|
|
$skipmedium{$pathname} = 1;
|
|
unlink("$destdir/$medium") if (-e "$destdir/$medium");
|
|
|
|
} elsif (($info{$pathname}{'x'} <= ($med_x * $med_threshold)) and
|
|
($info{$pathname}{'y'} <= ($med_y * $med_threshold))) {
|
|
|
|
# Skip if we are below the threshold size
|
|
$skipmedium{$pathname} = 1;
|
|
unlink("$destdir/$medium") if (-e "$destdir/$medium");
|
|
|
|
} else {
|
|
|
|
my $image = new Image::Magick;
|
|
my $retval;
|
|
|
|
# Create medium sized pic if it is not there,
|
|
# or is out of date with respect to original image
|
|
if ((! -e "$destdir/$medium") or
|
|
( -M $pathname < -M "$destdir/$medium") or
|
|
defined($opt_forceregen)) {
|
|
|
|
my $newgeom = $med_x . "x" . $med_y;
|
|
|
|
print "Creating $destdir/$medium\n";
|
|
|
|
$retval = $image->Read(filename=>$pathname);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Resize(geometry=>$newgeom);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(interlace=>Line);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(quality=>$med_quality);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Write(filename=>"$destdir/$medium");
|
|
warn "$retval" if "$retval";
|
|
|
|
$image_cache{$pathname} = $image;
|
|
|
|
} else {
|
|
|
|
# Up to date, existing medium, grab dimensions
|
|
# Get the right hsize/vsize tags for the medium slides. Simply do a "Read" of
|
|
# the file here and the code below will set the med_x/y properties.
|
|
#
|
|
$retval = $image->Read("$destdir/$medium");
|
|
warn "$retval" if "$retval";
|
|
}
|
|
|
|
$info{$pathname}{'med_size'} = &convert_to_kb($image->Get('filesize'));
|
|
$info{$pathname}{'med_x'} = $image->Get('width');
|
|
$info{$pathname}{'med_y'} = $image->Get('height');
|
|
|
|
$med_counter++;
|
|
}
|
|
|
|
#
|
|
# Next, deal with the thumbnail for this image. If we have just created a medium
|
|
# version of the image, then an open image "handle" will exist for it. We simply
|
|
# shrink that down to thumbnail size (if appropriate) rather than reading in the
|
|
# original file again just to shrink it (saves processing time).
|
|
#
|
|
|
|
# Skip thumb if we are below the threshold size
|
|
if (($info{$pathname}{'x'} <= ($current_thumbnail_x * $thumbnail_threshold)) and
|
|
($info{$pathname}{'y'} <= ($current_thumbnail_y * $thumbnail_threshold))) {
|
|
|
|
$info{$pathname}{'thumb_x'} = $info{$pathname}{'x'};
|
|
$info{$pathname}{'thumb_y'} = $info{$pathname}{'y'};
|
|
|
|
$skipthumb{$pathname} = 1;
|
|
if (-e "$destdir/$thumbnail") {
|
|
unlink("$destdir/$thumbnail");
|
|
$modified_thumb++;
|
|
}
|
|
|
|
push(@montagefiles,"$destdir/$filename");
|
|
|
|
} else {
|
|
|
|
my $image = new Image::Magick;
|
|
my $retval;
|
|
|
|
# Create thumbnail if it is not there,
|
|
# or is out of date with respect to original image
|
|
if ((! -e "$destdir/$thumbnail") or
|
|
( -M $pathname < -M "$destdir/$thumbnail") or
|
|
defined($opt_forceregen)) {
|
|
|
|
my $newgeom = $current_thumbnail_x . "x" . $current_thumbnail_y;
|
|
|
|
print "Creating $destdir/$thumbnail\n";
|
|
|
|
if (defined ($image_cache{$pathname})) {
|
|
$image = $image_cache{$pathname};
|
|
|
|
$retval = $image->Resize(geometry=>$newgeom);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(quality=>$thumb_quality);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Write(filename=>"$destdir/$thumbnail");
|
|
warn "$retval" if "$retval";
|
|
}
|
|
else {
|
|
$retval = $image->Read(filename=>$pathname);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Resize(geometry=>$newgeom);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(interlace=>Line);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(quality=>$thumb_quality);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Write(filename=>"$destdir/$thumbnail");
|
|
warn "$retval" if "$retval";
|
|
}
|
|
push(@montagefiles,"$destdir/$thumbnail");
|
|
|
|
$modified_thumb++;
|
|
|
|
} else {
|
|
|
|
# Up to date, existing thumb
|
|
# Get the right hsize/vsize tags for the inline thumbs. Simply do a "Read" of
|
|
# the file here and the code below will set the thumb_x/y properties.
|
|
#
|
|
$retval = $image->Read("$destdir/$thumbnail");
|
|
warn "$retval" if "$retval";
|
|
|
|
push(@montagefiles,"$destdir/$thumbnail");
|
|
|
|
}
|
|
|
|
$info{$pathname}{'thumb_size'} = &convert_to_kb($image->Get('filesize'));
|
|
$info{$pathname}{'thumb_x'} = $image->Get('width');
|
|
$info{$pathname}{'thumb_y'} = $image->Get('height');
|
|
|
|
$thumbnail_counter++;
|
|
}
|
|
|
|
# Set the max thumb sizes, to be used for slide frame width
|
|
if ($info{$pathname}{'thumb_x'} > $max_thumb_x) {
|
|
$max_thumb_x = $info{$pathname}{'thumb_x'};
|
|
}
|
|
if ($info{$pathname}{'thumb_y'} > $max_thumb_y) {
|
|
$max_thumb_y = $info{$pathname}{'thumb_y'};
|
|
}
|
|
|
|
|
|
# Set the max montage thumb sizes, to be used when creating montage images
|
|
#
|
|
$bn = basename ($thumbnail);
|
|
unless (defined ($skipmont{$bn})) {
|
|
if ($info{$pathname}{'thumb_x'} > $max_mont_thumb_x) {
|
|
$max_mont_thumb_x = $info{$pathname}{'thumb_x'};
|
|
}
|
|
if ($info{$pathname}{'thumb_y'} > $max_mont_thumb_y) {
|
|
$max_mont_thumb_y = $info{$pathname}{'thumb_y'};
|
|
}
|
|
}
|
|
|
|
#
|
|
# Finally, create html for this image
|
|
#
|
|
|
|
&image_entry($pathname);
|
|
|
|
} # foreach image that has this timestamp
|
|
|
|
} # foreach timestamp
|
|
|
|
|
|
# Finish up the columns if needed
|
|
if (($col_counter != 1) and
|
|
($col_counter <= $current_columns) and
|
|
($object_counter > $current_columns)) {
|
|
foreach ($col_counter..$current_columns) {
|
|
push(@index, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">$emptycell</TD>\n");
|
|
push(@details, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">$emptycell</TD>\n");
|
|
}
|
|
push(@index, " </TR>\n");
|
|
push(@details, " </TR>\n");
|
|
}
|
|
|
|
# Nuke generated dirs if no contents
|
|
system("rm -rf $destdir/$thumbnail_dir") if ($thumbnail_counter == 0);
|
|
system("rm -rf $destdir/$slide_dir") if ($slide_counter == 0);
|
|
system("rm -rf $destdir/$med_dir") if ($med_counter == 0);
|
|
|
|
# Create montage if we had more than just dir entries here
|
|
if (($dir_counter != $object_counter)) {
|
|
&create_montage(@montagefiles);
|
|
}
|
|
|
|
# Create stylesheet
|
|
&write_css();
|
|
|
|
# Write index web page
|
|
open(INDEX,">$destdir/$indexfile") or die ("Can't open $destdir/$indexfile: $!\n");
|
|
&page_header('index', $index_linkto);
|
|
foreach (@index) {
|
|
print INDEX;
|
|
}
|
|
&page_footer('index');
|
|
close(INDEX);
|
|
|
|
# Write photo details file
|
|
if ($do_detail == 1) {
|
|
open(INDEX,">$destdir/$detailfile") or die ("Can't open $destdir/$indexfile: $!\n");
|
|
&page_header('detail', $details_linkto);
|
|
foreach (@details) {
|
|
print INDEX;
|
|
}
|
|
&page_footer('detail');
|
|
close(INDEX);
|
|
} else {
|
|
unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile");
|
|
}
|
|
|
|
# Write slide/frame files
|
|
if (($do_slide == 1) and ($slide_counter > 1)) {
|
|
&write_frameset();
|
|
} else {
|
|
system("rm -rf $destdir/$slide_dir") if (-d "$destdir/$slide_dir");
|
|
}
|
|
|
|
# Optionally export images somewhere else
|
|
if ($opt_destdir) {
|
|
printf ("Copying image files from '$srcdir' to '$destdir'.\n");
|
|
foreach my $image (keys %info) {
|
|
# BSD's default 'cp' cannot preserve links like GNU fileutils cp can
|
|
#
|
|
if ($uname =~ /BSD/) {
|
|
system("cp -pv $image $destdir");
|
|
}
|
|
else {
|
|
system("cp -dpuv $image $destdir");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (defined ($do_emoticons) && $do_emoticons) {
|
|
foreach $icon ('wink', 'smile', 'frown') {
|
|
if ($emoticon{$icon}) {
|
|
&write_emoticon_png ($icon);
|
|
} else {
|
|
unlink ($destdir . '/' . $thumbnail_dir . "/$emoticonprefix${icon}.png");
|
|
}
|
|
}
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Write the various HTML parts for this image
|
|
#
|
|
######################################################################
|
|
sub image_entry {
|
|
|
|
my $pathname = shift(@_);
|
|
my $filename = $info{$pathname}{'file'};
|
|
my $link;
|
|
|
|
&index_html($pathname);
|
|
|
|
if ($do_detail == 1) {
|
|
&details_html($pathname);
|
|
}
|
|
|
|
if (($do_slide == 1) and ($image_counter > 1)) {
|
|
&slide_html($pathname);
|
|
} else {
|
|
my $file = $info{$pathname}{slide};
|
|
unlink($file) if (-e $file);
|
|
}
|
|
|
|
# Increment for next time
|
|
$col_counter++;
|
|
$col_counter = 1 if ($col_counter > $current_columns);
|
|
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Generate HTML for index page entry
|
|
#
|
|
###############################################################################
|
|
|
|
sub index_html {
|
|
|
|
my $pathname = shift(@_);
|
|
my $filename = $info{$pathname}{'file'};
|
|
my $link;
|
|
my $anchortext;
|
|
|
|
# At beginning of row?
|
|
if ($col_counter == 1) {
|
|
push(@index, " <TR>\n");
|
|
}
|
|
|
|
# Image
|
|
push(@index, " <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
push(@index, " <DIV CLASS=\"index\">");
|
|
push(@index, &format_date($info{$pathname}{'date'}, 'index'));
|
|
push(@index,"</DIV>\n");
|
|
|
|
if (($index_linkto eq 'details') and ($do_detail == 1)) {
|
|
$link = "$detailfile#$filename";
|
|
} elsif (($index_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
|
|
$link = $info{$pathname}{'medium'};
|
|
} elsif (($index_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
|
|
$link = $info{$pathname}{'thumb'};
|
|
} elsif (($index_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) {
|
|
$link = $info{$pathname}{'slide'};
|
|
} else {
|
|
$link = $filename;
|
|
}
|
|
|
|
$anchortext = " <A HREF=\"$link\" ";
|
|
|
|
if (defined ($indexthumbtitle) && $indexthumbtitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string ($indexthumbtitle, $pathname, 'index');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf ("TITLE=\"%s\" ", $str);
|
|
}
|
|
}
|
|
|
|
$anchortext .= "NAME=\"$filename\">";
|
|
|
|
push(@index, $anchortext);
|
|
|
|
if (defined($skipthumb{$pathname})) {
|
|
push(@index,"<IMG SRC=\"$filename\"");
|
|
} else {
|
|
push(@index,"<IMG SRC=\"$info{$pathname}{thumb}\"");
|
|
}
|
|
push(@index," WIDTH=\"$info{$pathname}{thumb_x}\" HEIGHT=\"$info{$pathname}{thumb_y}\"");
|
|
push(@index," ALT=\" $filename \"");
|
|
push(@index," CLASS=\"index\"");
|
|
push(@index,"></A>\n");
|
|
|
|
push(@index, " <DIV CLASS=\"index\">");
|
|
|
|
# Full size link
|
|
push(@index,"<A HREF=\"$filename\">full size</A>");
|
|
|
|
# Medium size link if within the threshold
|
|
unless (defined($skipmedium{$pathname})) {
|
|
push(@index," | <A HREF=\"$info{$pathname}{medium}\">medium</A>");
|
|
}
|
|
|
|
# Detail list link
|
|
if ($do_detail == 1) {
|
|
push(@index," | <A HREF=\"$detailfile#$filename\">details</A>");
|
|
}
|
|
|
|
push(@index,"</DIV>\n");
|
|
|
|
# Caption if any (jpeg comment field)
|
|
if (defined($info{$pathname}{'comment'})) {
|
|
my ($tmp);
|
|
push(@index, " <DIV CLASS=\"caption\">");
|
|
# Hack: if a comment has an ellipsis at the very end, make the HTML use a
|
|
# non-breakable space before it so that the ellipsis doesn't "wrap" inside
|
|
# the table field. It just looks better for those cases where the comment
|
|
# is just long enough to wrap when rendered in the space given
|
|
#
|
|
$tmp = $info{$pathname}{'comment'};
|
|
$tmp = &htmlize_caption ($tmp);
|
|
if ($tmp =~ /(\s+)\.\.\.\s*$/) {
|
|
$tmp =~ s/(\s+)\.\.\.\s*$/ .../;
|
|
}
|
|
push(@index, $tmp);
|
|
push(@index,"</DIV>\n");
|
|
}
|
|
|
|
push(@index, " </TD>\n\n");
|
|
|
|
# At end of row?
|
|
if ($col_counter == $current_columns) {
|
|
push(@index, " </TR>\n");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Generate HTML for slide/frame pages
|
|
#
|
|
###############################################################################
|
|
sub slide_html {
|
|
|
|
my $pathname = shift(@_);
|
|
my $filename = $info{$pathname}{'file'};
|
|
my $link;
|
|
my $anchortext;
|
|
|
|
#
|
|
# First the index frame info
|
|
#
|
|
if ($frame_orient eq 'horizontal') {
|
|
push(@frame," <TD CLASS=\"frame\" ALIGN=\"center\" VALIGN=\"middle\">\n");
|
|
} else {
|
|
push(@frame," <TR>\n <TD CLASS=\"frame\" ALIGN=\"center\" VALIGN=\"middle\">\n");
|
|
}
|
|
|
|
$anchortext = " <A HREF=\"../$info{$pathname}{slide}\" ";
|
|
if (defined ($framethumbtitle) && $framethumbtitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string ($framethumbtitle, $pathname, 'frame');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf ("TITLE=\"%s\" ", $str);
|
|
}
|
|
}
|
|
|
|
$anchortext .= "TARGET=\"view\">";
|
|
push(@frame, $anchortext);
|
|
|
|
if (defined($skipthumb{$pathname})) {
|
|
push(@frame,"<IMG SRC=\"../$filename\"");
|
|
} else {
|
|
push(@frame,"<IMG SRC=\"../$info{$pathname}{thumb}\"");
|
|
}
|
|
push(@frame," WIDTH=\"$info{$pathname}{thumb_x}\" HEIGHT=\"$info{$pathname}{thumb_y}\"");
|
|
push(@frame," ALT=\" $filename \"");
|
|
push(@frame," CLASS=\"frame\"");
|
|
push(@frame,"></A>\n");
|
|
if ($frame_orient eq 'horizontal') {
|
|
push(@frame," </TD>");
|
|
} else {
|
|
push(@frame," </TD>\n </TR>");
|
|
}
|
|
push(@frame,"\n");
|
|
|
|
#
|
|
# Then the individual slides
|
|
#
|
|
my $slide = new FileHandle "> $destdir/$info{$pathname}{slide}";
|
|
if (!defined($slide)) {
|
|
die("$destdir/$info{$pathname}{slide}: $!");
|
|
}
|
|
|
|
select($slide);
|
|
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
|
|
print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
|
|
print "<HTML>\n";
|
|
print "<HEAD>\n";
|
|
$verstring = &versionstring();
|
|
printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
|
|
printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
|
|
print "<TITLE>$current_titletext - $filename</TITLE>\n";
|
|
print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
|
|
print "</HEAD>\n<BODY>\n";
|
|
|
|
&next_prev_links($pathname);
|
|
|
|
# Caption if any
|
|
if (($slide_caption eq 'top') and defined($info{$pathname}{'comment'})) {
|
|
print "<DIV CLASS=\"caption\">";
|
|
my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide');
|
|
print $tmp;
|
|
print "</DIV>\n";
|
|
}
|
|
|
|
# Date, filename
|
|
if ($slide_date eq 'top') {
|
|
print "<DIV CLASS=\"index\">";
|
|
print &format_date($info{$pathname}{'date'}, 'slide');
|
|
print " $filename";
|
|
print "</DIV>\n";
|
|
}
|
|
|
|
if ($slide_linkto eq 'index') {
|
|
$link = "../$indexfile#$filename";
|
|
} elsif (($slide_linkto eq 'details') and ($do_detail == 1)) {
|
|
$link = "../$detailfile#$filename";
|
|
} elsif (($slide_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
|
|
$link = "../$info{$pathname}{medium}";
|
|
} elsif (($slide_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
|
|
$link = "../$info{$pathname}{thumb}";
|
|
} else {
|
|
$link = "../$filename";
|
|
}
|
|
|
|
print "\n<P>\n";
|
|
|
|
$anchortext = "<A HREF=\"$link\"";
|
|
if (defined ($slidethumbtitle) && $slidethumbtitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string ($slidethumbtitle, $pathname, 'slide');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf (" TITLE=\"%s\"", $str);
|
|
}
|
|
}
|
|
|
|
$anchortext .= ">";
|
|
print $anchortext;
|
|
|
|
if (defined($skipmedium{$pathname})) {
|
|
print "<IMG SRC=\"../$filename\"";
|
|
print " WIDTH=\"$info{$pathname}{x}\" HEIGHT=\"$info{$pathname}{y}\"";
|
|
} else {
|
|
print "<IMG SRC=\"../$info{$pathname}{medium}\"";
|
|
print " WIDTH=\"$info{$pathname}{med_x}\" HEIGHT=\"$info{$pathname}{med_y}\"";
|
|
}
|
|
print " ALT=\" $filename \"";
|
|
print " CLASS=\"slide\">";
|
|
print "</A>\n";
|
|
|
|
print "</P>\n";
|
|
|
|
# Caption if any
|
|
if (($slide_caption eq 'bottom') and defined($info{$pathname}{'comment'})) {
|
|
print "<DIV CLASS=\"caption\">";
|
|
my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide');
|
|
print $tmp;
|
|
print "</DIV>\n";
|
|
}
|
|
|
|
# Date, filename
|
|
if ($slide_date eq 'bottom') {
|
|
print "<DIV CLASS=\"index\">";
|
|
print &format_date($info{$pathname}{'date'}, 'slide');
|
|
print " $filename";
|
|
print "</DIV>\n";
|
|
}
|
|
|
|
&next_prev_links($pathname);
|
|
print "</BODY>\n</HTML>\n";
|
|
|
|
select(STDOUT);
|
|
$slide->close();
|
|
$slide_counter++;
|
|
|
|
unless(defined($first_slide)) {
|
|
$first_slide = $info{$pathname}{'slide'};
|
|
}
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Generate HTML for details page
|
|
#
|
|
###############################################################################
|
|
sub details_html {
|
|
|
|
my $pathname = shift(@_);
|
|
my $filename = $info{$pathname}{'file'};
|
|
my ($link, $anchortext);
|
|
|
|
# At beginning of row?
|
|
if ($col_counter == 1) {
|
|
push(@details, " <TR>\n");
|
|
}
|
|
|
|
|
|
if ($details_linkto eq 'index') {
|
|
$link = "$indexfile#$filename";
|
|
} elsif (($details_linkto eq 'medium') and !defined($skipmedium{$pathname})) {
|
|
$link = "$info{$pathname}{medium}";
|
|
} elsif (($details_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) {
|
|
$link = "$info{$pathname}{thumb}";
|
|
} elsif (($details_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) {
|
|
$link = $info{$pathname}{'slide'};
|
|
} else {
|
|
$link = $filename;
|
|
}
|
|
|
|
push(@details," <TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
push(@details," <TABLE BORDER=0 WIDTH=\"100%\">\n");
|
|
push(@details," <TR>\n");
|
|
push(@details," <TD VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
push(@details," <DIV CLASS=\"detail\">\n");
|
|
push(@details," <A NAME=\"$filename\">");
|
|
push(@details, &format_date($info{$pathname}{'date'}, 'detail'));
|
|
push(@details,"</A><BR>\n");
|
|
|
|
$anchortext = "<A HREF=\"$link\"";
|
|
if (defined ($detailthumbtitle) && $detailthumbtitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string ($detailthumbtitle, $pathname, 'detail');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf (" TITLE=\"%s\"", $str);
|
|
}
|
|
}
|
|
|
|
$anchortext .= ">";
|
|
push(@details, $anchortext);
|
|
|
|
if (defined($skipthumb{$pathname})) {
|
|
push(@details,"<IMG SRC=\"$filename\"");
|
|
} else {
|
|
push(@details,"<IMG SRC=\"$info{$pathname}{thumb}\"");
|
|
}
|
|
my $x = $info{$pathname}{'thumb_x'} / $detailshrink ;
|
|
my $y = $info{$pathname}{'thumb_y'} / $detailshrink ;
|
|
push(@details, sprintf(" WIDTH=\"%d\" HEIGHT=\"%d\"", $x, $y));
|
|
push(@details," ALT=\" $filename \"");
|
|
push(@details," CLASS=\"index\"");
|
|
push(@details,"></A><BR>");
|
|
push(@details,"$filename<BR>");
|
|
push(@details,"</DIV>\n");
|
|
push(@details," </TD>\n\n");
|
|
push(@details," <TD VALIGN=\"middle\" ALIGN=\"left\">\n");
|
|
push(@details," <DIV CLASS=\"detail\">");
|
|
push(@details,"Original: <A HREF=\"$filename\">$info{$pathname}{geometry}</A>");
|
|
push(@details," ($info{$pathname}{size})<BR>");
|
|
unless (defined($skipmedium{$pathname})) {
|
|
push(@details,"Medium: <A HREF=\"$info{$pathname}{medium}\">");
|
|
push(@details,$info{$pathname}{'med_x'} . 'x' . $info{$pathname}{'med_y'} . "</A>");
|
|
push(@details," ($info{$pathname}{med_size})<BR>");
|
|
}
|
|
unless (defined($skipthumb{$pathname})) {
|
|
push(@details,"Thumbnail: <A HREF=\"$info{$pathname}{thumb}\">");
|
|
push(@details,$info{$pathname}{'thumb_x'} . 'x' . $info{$pathname}{'thumb_y'} . "</A>");
|
|
#push(@details," ($info{$pathname}{thumb_size})<BR>");
|
|
push(@details,"<BR>");
|
|
}
|
|
|
|
#
|
|
# EXIF data
|
|
#
|
|
if (defined($info{$pathname}{'flash'})) {
|
|
push(@details,"Flash: $info{$pathname}{flash}<BR>");
|
|
}
|
|
if (defined($info{$pathname}{'exposure_time'})) {
|
|
push(@details,"Exposure time: $info{$pathname}{exposure_time}<BR>");
|
|
}
|
|
if (defined($info{$pathname}{'focus_dist'})) {
|
|
push(@details,"Focus distance: $info{$pathname}{focus_dist}<BR>");
|
|
}
|
|
if (defined($info{$pathname}{'focal_length'})) {
|
|
push(@details,"Focal length: $info{$pathname}{focal_length}<BR>");
|
|
}
|
|
if (defined($info{$pathname}{'aperture'})) {
|
|
push(@details,"Aperture: $info{$pathname}{aperture}<BR>");
|
|
}
|
|
|
|
push(@details,"\n");
|
|
push(@details," </DIV>\n");
|
|
push(@details," </TD>\n");
|
|
push(@details," </TR>\n");
|
|
push(@details," </TABLE>\n");
|
|
push(@details," </TD>\n");
|
|
|
|
# At end of row?
|
|
if ($col_counter == $current_columns) {
|
|
push(@details, " </TR>\n");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Extract info from image
|
|
#
|
|
######################################################################
|
|
sub fill_image_info {
|
|
|
|
my $filename = shift (@_);
|
|
my $pathname = "$srcdir/$filename";
|
|
my $image = new Image::Magick;
|
|
my $retval;
|
|
|
|
print ".";
|
|
flush (STDOUT);
|
|
$retval = $image->Read($pathname);
|
|
|
|
if ($retval ne "") {
|
|
print "\nSkipping $pathname";
|
|
flush (STDOUT);
|
|
return;
|
|
} else {
|
|
$object_counter++;
|
|
$image_counter++;
|
|
}
|
|
|
|
$info{$pathname}{'file'} = $filename;
|
|
|
|
# Use mtime as a fallback date in case we don't have exif data
|
|
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
|
|
$atime,$mtime,$ctime,$blksize,$blocks) = stat($pathname);
|
|
|
|
$info{$pathname}{'date'} = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
|
|
$info{$pathname}{'x'} = $image->Get('width');
|
|
$info{$pathname}{'y'} = $image->Get('height');
|
|
$info{$pathname}{'geometry'} = $info{$pathname}{'x'} . "x" . $info{$pathname}{'y'};
|
|
|
|
$info{$pathname}{'size'} = &convert_to_kb($image->Get('filesize'));
|
|
|
|
$info{$pathname}{'format'} = $image->Get('format');
|
|
|
|
$info{$pathname}{'comment'} = $image->Get('comment');
|
|
|
|
my ($name,$path,$suffix) = fileparse($filename,'\.\S+');
|
|
|
|
|
|
if ($info{$pathname}{'format'} =~ /JFIF/i) {
|
|
|
|
$info{$pathname}{'thumb'} = "$thumbnail_dir/$filename";
|
|
$thumb_backref{"$thumbnail_dir/$filename"} = $pathname;
|
|
|
|
$info{$pathname}{'medium'} = "$med_dir/$filename";
|
|
$med_backref{"$med_dir/$filename"} = $pathname;
|
|
|
|
if (defined(&image_info)) {
|
|
|
|
my $exif = image_info("$pathname");
|
|
|
|
if (my $error = $exif->{error}) {
|
|
warn "Can't parse image info: $error\n";
|
|
}
|
|
|
|
if (defined($opt_debug)) {
|
|
print "EXIF data for $pathname:\n";
|
|
foreach (keys %$exif) {
|
|
print " $_ = $exif->{$_}\n";
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
if (defined($exif->{DateTimeOriginal})) {
|
|
$exif->{DateTimeOriginal} =~ /\s*([\d:]+)\s+([\d:]+)/;
|
|
my $dt = $1;
|
|
my $tm = $2;
|
|
$tm =~ s/://;
|
|
$tm =~ s/:/\./;
|
|
$dt =~ s/://g;
|
|
$info{$pathname}{'date'} = $dt . $tm;
|
|
}
|
|
|
|
if (defined($exif->{Flash})) {
|
|
$info{$pathname}{'flash'} = $exif->{'Flash'};
|
|
$info{$pathname}{'flash'} =~ s/0/no/;
|
|
$info{$pathname}{'flash'} =~ s/1/yes/;
|
|
}
|
|
|
|
if (defined($exif->{FocalLength})) {
|
|
$info{$pathname}{'focal_length'} = sprintf("%4.1fmm", eval("$exif->{FocalLength}"));
|
|
}
|
|
|
|
if (defined($exif->{SubjectDistance})) {
|
|
$info{$pathname}{'focus_dist'} = sprintf("%4.1fm", eval("$exif->{SubjectDistance}"));
|
|
}
|
|
|
|
if (defined($exif->{ExposureTime})) {
|
|
$info{$pathname}{'exposure_time'} = $exif->{ExposureTime} . 's';
|
|
}
|
|
|
|
if (defined($exif->{FNumber})) {
|
|
$info{$pathname}{'aperture'} = "f/" . eval ("$exif->{FNumber}");
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$info{$pathname}{'thumb'} = "$thumbnail_dir/$name.jpg";
|
|
$thumb_backref{"$thumbnail_dir/$name.jpg"} = $pathname;
|
|
|
|
$info{$pathname}{'medium'} = "$med_dir/$name.jpg";
|
|
$med_backref{"$med_dir/$name.jpg"} = $pathname;
|
|
|
|
}
|
|
|
|
$info{$pathname}{'slide'} = "$slide_dir/$name.html";
|
|
$slide_backref{"$slide_dir/$name.html"} = $pathname;
|
|
|
|
push(@{$timestamp{"$info{$pathname}{date}"}}, $pathname);
|
|
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Write HTML for directory entries
|
|
#
|
|
######################################################################
|
|
sub dir_entry {
|
|
|
|
my $dir = shift(@_);
|
|
my $destdirname = "$destdir/$dir";
|
|
my $srcdirname = "$srcdir/$dir";
|
|
my $anchortext;
|
|
|
|
print "Processing directory $srcdirname\n";
|
|
|
|
# Recurse first
|
|
if ($do_recurse == 1) {
|
|
my $flags = "";
|
|
$flags .= "-medium " if ($do_medium == 1);
|
|
$flags .= "-nomedium " if ($do_medium == 0);
|
|
$flags .= "-slide " if ($do_slide == 1);
|
|
$flags .= "-noslide " if ($do_slide == 0);
|
|
$flags .= "-dirs " if ($do_dirs == 1);
|
|
$flags .= "-nodirs " if ($do_dirs == 0);
|
|
$flags .= "-montage " if ($do_montage == 1);
|
|
$flags .= "-nomontage " if ($do_montage == 0);
|
|
$flags .= "-detail " if ($do_detail == 1);
|
|
$flags .= "-nodetail " if ($do_detail == 0);
|
|
$flags .= "-reverse " if ($do_reverse == 1);
|
|
$flags .= "-noreverse " if ($do_reverse == 0);
|
|
$flags .= "-forceregen " if (defined($opt_forceregen));
|
|
$flags .= "-includeall " if (defined($opt_includeall));
|
|
$flags .= "-columns $current_columns " if (defined($opt_columns));
|
|
$flags .= "-x $opt_x " if (defined($opt_x));
|
|
$flags .= "-y $opt_y " if (defined($opt_y));
|
|
$flags .= "-destdir $destdirname " if ($destdir ne $srcdir);
|
|
foreach my $var (keys %opt_d) {
|
|
$flags .= " -d $var=$opt_d{$var}";
|
|
}
|
|
system("cd $srcdirname ;$0 $flags -recurse");
|
|
}
|
|
|
|
my $dirtitle = "";
|
|
my $first;
|
|
my $last;
|
|
my $montage;
|
|
my $montage_x;
|
|
my $montage_y;
|
|
|
|
# Only add entry if this dir has an index file
|
|
if (-r "$destdirname/$indexfile") {
|
|
|
|
# Go fetch the title and dates from the HTML
|
|
my $tmp1 = &extract_meta_tag ($titlemetatag,"$destdirname/$indexfile");
|
|
my $tmp2 = &extract_meta_tag ($begindatemetatag,"$destdirname/$indexfile");
|
|
my $tmp3 = &extract_meta_tag ($enddatemetatag,"$destdirname/$indexfile");
|
|
if (defined($tmp1)) {
|
|
$dirtitle = $tmp1;
|
|
}
|
|
if (defined($tmp2)) {
|
|
$first = $tmp2;
|
|
}
|
|
if (defined($tmp3)) {
|
|
$last = $tmp3;
|
|
}
|
|
|
|
# If we found generated files in this dir, flag that we found something
|
|
# valid to index
|
|
$object_counter++;
|
|
$dir_counter++;
|
|
|
|
# Set montage file if we found it
|
|
if (($do_montage == 1) and ( -r "$destdirname/$thumbnail_dir/$montagefile")) {
|
|
|
|
print "Found montage in $destdirname\n" if defined($opt_debug);
|
|
$montage = "$destdirname/$thumbnail_dir/$montagefile";
|
|
|
|
my $image = new Image::Magick;
|
|
my $retval;
|
|
|
|
$retval = $image->Read(filename=>$montage);
|
|
warn "$retval" if "$retval";
|
|
|
|
$montage_x = $image->Get('width');
|
|
$montage_y = $image->Get('height');
|
|
|
|
}
|
|
|
|
|
|
# At beginning of row?
|
|
if ($col_counter == 1) {
|
|
push(@index, "<TR>\n");
|
|
push(@details, "<TR>\n");
|
|
}
|
|
|
|
# Entry for this directory in main & details file
|
|
push(@index, "<TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
push(@details, "<TD CLASS=\"index\" VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
|
|
push(@details, "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n");
|
|
|
|
if (defined($montage)) {
|
|
push(@details, "<TR><TD VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
} else {
|
|
push(@details, "<TR><TD COLSPAN=\"2\" VALIGN=\"middle\" ALIGN=\"center\">\n");
|
|
}
|
|
|
|
if (defined($first)) {
|
|
|
|
my ($tmp_first, $tmp_last);
|
|
|
|
push(@index, "<DIV CLASS=\"index\">");
|
|
push(@details, "<DIV CLASS=\"detail\">");
|
|
|
|
$tmp_first = &format_date ($first, 'index', 'dayonly');
|
|
$tmp_last = &format_date ($last, 'index', 'dayonly');
|
|
|
|
if ($first ne $last) {
|
|
push(@index, "$tmp_first - $tmp_last");
|
|
} else {
|
|
push(@index, "$tmp_first");
|
|
}
|
|
|
|
$tmp_first = &format_date ($first, 'detail', 'dayonly');
|
|
$tmp_last = &format_date ($last, 'detail', 'dayonly');
|
|
|
|
if ($first ne $last) {
|
|
push(@details, "$tmp_first - $tmp_last");
|
|
} else {
|
|
push(@details, "$tmp_first");
|
|
}
|
|
|
|
push(@index, "</DIV>\n");
|
|
push(@details, "</DIV>\n");
|
|
}
|
|
|
|
|
|
if (defined($montage)) {
|
|
|
|
$anchortext = "<A HREF=\"$dir/$indexfile\"";
|
|
if (defined ($montagetitle) && $montagetitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string_dir ($montagetitle, $dir, 'index');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf (" TITLE=\"%s\"", $str);
|
|
}
|
|
}
|
|
$anchortext .= ">";
|
|
push(@index, $anchortext);
|
|
|
|
push(@index, "<IMG CLASS=\"index\" SRC=\"$dir/$thumbnail_dir/$montagefile\"");
|
|
push(@index, " WIDTH=\"$montage_x\" HEIGHT=\"$montage_y\"");
|
|
push(@index, " ALT=\"\"");
|
|
push(@index, ">");
|
|
push(@index, "</A>\n");
|
|
|
|
push(@index,"<DIV CLASS=\"index\">");
|
|
push(@index, "<A HREF=\"$dir/$indexfile\">$dir</A>");
|
|
push(@index,"</DIV>\n");
|
|
|
|
$anchortext = "<A HREF=\"$dir/$detailfile\"";
|
|
if (defined ($montagetitle) && $montagetitle ne '') {
|
|
my ($str);
|
|
$str = &interpolate_title_string_dir ($montagetitle, $dir, 'index');
|
|
if ($str ne '') {
|
|
$anchortext .= sprintf (" TITLE=\"%s\"", $str);
|
|
}
|
|
}
|
|
$anchortext .= ">";
|
|
push(@details, $anchortext);
|
|
|
|
push(@details, "<IMG CLASS=\"index\" SRC=\"$dir/$thumbnail_dir/$montagefile\"");
|
|
my $x = $montage_x / $detailshrink ;
|
|
my $y = $montage_y / $detailshrink ;
|
|
push(@details, sprintf(" WIDTH=\"%d\" HEIGHT=\"%d\"", $x, $y));
|
|
push(@details, " ALT=\"\"");
|
|
push(@details, ">");
|
|
push(@details, "</A>");
|
|
|
|
push(@details, "</TD><TD VALIGN=\"middle\" ALIGN=\"left\">\n");
|
|
|
|
} else {
|
|
|
|
push(@index,"<DIV CLASS=\"index\">");
|
|
push(@index, "<A HREF=\"$dir/$indexfile\">$dir</A>");
|
|
push(@index,"</DIV>\n");
|
|
|
|
}
|
|
|
|
push(@index, "<DIV CLASS=\"caption\">");
|
|
push(@details, "<DIV CLASS=\"detail\">");
|
|
|
|
if ($dirtitle ne "") {
|
|
push(@index, "$dirtitle");
|
|
push(@details, "$dirtitle");
|
|
}
|
|
|
|
push(@details, "<BR><A HREF=\"$dir/$detailfile\">$dir</A>");
|
|
|
|
push(@index, "</DIV>\n");
|
|
push(@details, "</DIV>\n");
|
|
|
|
push(@details,"</TD></TR></TABLE>\n");
|
|
|
|
push(@index, "</TD>\n");
|
|
push(@details, "</TD>\n");
|
|
|
|
# At end of row?
|
|
if ($col_counter == $current_columns) {
|
|
push(@index, "</TR>\n");
|
|
push(@details, "</TR>\n");
|
|
}
|
|
|
|
|
|
# Increment for next item
|
|
$col_counter++;
|
|
$col_counter = 1 if ($col_counter > $current_columns);
|
|
|
|
} # if dir had index file
|
|
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Top of HTML index/detail files
|
|
#
|
|
######################################################################
|
|
sub page_header {
|
|
|
|
my $this = shift(@_);
|
|
my $linkto = shift(@_);
|
|
my $numlink = 0;
|
|
my $verstring;
|
|
|
|
select(INDEX);
|
|
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
|
|
print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
|
|
print "<HTML>\n";
|
|
print "<HEAD>\n";
|
|
$verstring = &versionstring();
|
|
printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
|
|
printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
|
|
if (defined ($write_meta_tag{$titlemetatag})) {
|
|
print "<META NAME=\"$titlemetatag\" CONTENT=\"$current_titletext\">\n";
|
|
}
|
|
if (defined ($write_meta_tag{$columnsmetatag})) {
|
|
print "<META NAME=\"$columnsmetatag\" CONTENT=\"$current_columns\">\n";
|
|
}
|
|
if (defined ($write_meta_tag{$thumbxmetatag})) {
|
|
print "<META NAME=\"$thumbxmetatag\" CONTENT=\"$current_thumbnail_x\">\n";
|
|
}
|
|
if (defined ($write_meta_tag{$thumbymetatag})) {
|
|
print "<META NAME=\"$thumbymetatag\" CONTENT=\"$current_thumbnail_y\">\n";
|
|
}
|
|
if (defined ($write_meta_tag{$reversemetatag})) {
|
|
print "<META NAME=\"$reversemetatag\" CONTENT=\"$current_reverse\">\n";
|
|
}
|
|
if (defined($firstdate)) {
|
|
print "<META NAME=\"$begindatemetatag\" CONTENT=\"$firstdate\">\n";
|
|
}
|
|
if (defined($lastdate)) {
|
|
print "<META NAME=\"$enddatemetatag\" CONTENT=\"$lastdate\">\n";
|
|
}
|
|
if (!defined ($opt_includeall) && defined (@opt_exclude) && scalar (@opt_exclude)) {
|
|
my $tmp = join (',', @opt_exclude);
|
|
my $etmp;
|
|
|
|
# We need to "encode" this string in the HTML so that raw filenames
|
|
# (that people should not try to access) are not exposed to the
|
|
# outside world.
|
|
#
|
|
$etmp = &encodestring ($tmp);
|
|
printf ("<META NAME=\"$excludemetatag\" CONTENT=\"%s\">\n", $etmp);
|
|
}
|
|
printf ("<META NAME=\"$numimagesmetatag\" CONTENT=\"%d\">\n", $image_counter);
|
|
|
|
if (defined (@opt_skipmont) && scalar (@opt_skipmont)) {
|
|
my $tmp = join (',', @opt_skipmont);
|
|
printf ("<META NAME=\"$skipmetatag\" CONTENT=\"%s\">\n", $tmp);
|
|
}
|
|
print "<TITLE>$current_titletext</TITLE>\n";
|
|
print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"$stylefile\">\n";
|
|
print "</HEAD>\n";
|
|
print "<BODY>\n";
|
|
|
|
# Break out of frames
|
|
print "<SCRIPT TYPE=\"text/javascript\">\n";
|
|
print "if (parent.frames.length > 0) {\n";
|
|
print " parent.location.href = self.document.location\n";
|
|
print "}\n";
|
|
print "</SCRIPT>\n";
|
|
|
|
print "<H1 CLASS=\"title\">$current_titletext</H1>\n";
|
|
|
|
print "<H3>";
|
|
|
|
# On all these links, check to see if the variable is also defined. If
|
|
# not (done in a .imageindexrc file perhaps) then skip the link
|
|
|
|
if ((-e "$destdir/../$indexfile") and
|
|
($do_dirs == 1) and defined($updirtext)) {
|
|
print "<A HREF=\"../$indexfile\">$updirtext</A>";
|
|
$numlink++;
|
|
}
|
|
|
|
if (($do_detail == 1) and ($this eq 'index') and defined($detaillinktext)) {
|
|
print " | " if ($numlink != 0);
|
|
print "<A HREF=\"$detailfile\">$detaillinktext</A>";
|
|
$numlink++;
|
|
}
|
|
|
|
if (($this eq 'detail') and defined($indexlinktext)) {
|
|
print " | " if ($numlink != 0);
|
|
print "<A HREF=\"$indexfile\">$indexlinktext</A>";
|
|
$numlink++;
|
|
}
|
|
|
|
if (($do_slide == 1) and ($slide_counter > 1) and
|
|
defined($framelinktext)) {
|
|
print " | " if ($numlink != 0);
|
|
print "<A HREF=\"$slide_dir/$framefile\">$framelinktext</A>";
|
|
$numlink++;
|
|
}
|
|
|
|
#age test:
|
|
print " | " if ($numlink != 0);
|
|
print "<A HREF=\"../\">$updirtext</A>";
|
|
#age ende
|
|
|
|
print "\n<BR>\n" if ($numlink != 0);
|
|
|
|
print "</H3>\n";
|
|
|
|
if (defined($firstdate) and defined($lastdate)) {
|
|
|
|
my $tmp1 = &format_date($firstdate, $this, 'dayonly');
|
|
my $tmp2 = &format_date($lastdate, $this, 'dayonly');
|
|
|
|
if ($tmp1 ne $tmp2) {
|
|
if ($current_reverse == 0) {
|
|
print "<H2 CLASS=\"daterange\">$tmp1 - $tmp2</H2>\n";
|
|
} else {
|
|
print "<H2 CLASS=\"daterange\">$tmp2 - $tmp1</H2>\n";
|
|
}
|
|
} else {
|
|
print "<H2 CLASS=\"daterange\">$tmp1</H2>\n";
|
|
}
|
|
}
|
|
|
|
print "<TABLE WIDTH=\"100%\" CLASS=\"index\">\n";
|
|
|
|
select(STDOUT);
|
|
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Bottom of HTML file
|
|
#
|
|
######################################################################
|
|
sub page_footer {
|
|
|
|
my $time = localtime(time);
|
|
|
|
my $progurl = 'http://www.edwinh.org/imageindex/';
|
|
|
|
select(INDEX);
|
|
|
|
print "</TABLE>\n";
|
|
|
|
print "<DIV CLASS=\"credits\">";
|
|
print "<BR>\n";
|
|
print "<BR>\n";
|
|
print "<I>..erstellt mit <A HREF=\"$progurl\">imageindex</A>..</I>";
|
|
print "</DIV>\n";
|
|
print "</BODY>\n</HTML>\n";
|
|
|
|
select(STDOUT);
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# A "quickie" routine to show which files were excluded in a prior run
|
|
#
|
|
######################################################################
|
|
|
|
sub showexcluded {
|
|
my ($file) = @_;
|
|
my ($rfile, $tmp, $utmp, @files, $str);
|
|
|
|
if (! defined ($file)) {
|
|
if (-r $indexfile) {
|
|
$rfile = $indexfile;
|
|
}
|
|
}
|
|
else {
|
|
$rfile = $file;
|
|
}
|
|
$tmp = &extract_meta_tag ($excludemetatag, $rfile);
|
|
if (defined($tmp)) {
|
|
# We need to "decode" this string as it has been encoded for storage
|
|
# in the HTML so that raw filenames (that people should not try to
|
|
# access) are not exposed to the outside world.
|
|
#
|
|
$utmp = &decodestring ($tmp);
|
|
(@files) = split (/,/, $utmp);
|
|
$str = join (',', @files);
|
|
printf ("File '$rfile' shows the following record of excluded files:\n");
|
|
printf ("%s\n", $str);
|
|
}
|
|
else {
|
|
printf ("File '$rfile' shows no record of excluded files.\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Ignore certain files via META data stored in the index.html file
|
|
#
|
|
# Exports global variable %skipmont used later during montage
|
|
# generation.
|
|
#
|
|
######################################################################
|
|
sub exclude_files {
|
|
|
|
my @files = @_;
|
|
my (@filelist, $f, %exclude, $token, @tokens);
|
|
|
|
undef %exclude;
|
|
|
|
# -skipmont flags override any META data found. Else, look for the META tag
|
|
# then process. Check to see if any of the -skipmont options were given as
|
|
# strings of filenames concatenated with ',' characters. If so, support it.
|
|
#
|
|
if (defined (@opt_skipmont)) {
|
|
foreach (@opt_skipmont) {
|
|
(@tokens) = split (/,/, $_);
|
|
foreach $token (@tokens) {
|
|
$skipmont{$token}++;
|
|
}
|
|
}
|
|
}
|
|
elsif (-r "$destdir/$indexfile") {
|
|
my $tmp = &extract_meta_tag ($skipmetatag, "$destdir/$indexfile");
|
|
if (defined($tmp)) {
|
|
(@opt_skipmont) = split (/,/, $tmp);
|
|
my $str = join (',', @opt_skipmont);
|
|
printf ("Using saved skip-montage files: %s\n", $str);
|
|
foreach (@opt_skipmont) {
|
|
$skipmont{$_}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
# -exclude flags override any META data found. Else, look for the META tag
|
|
# then process. Check to see if any of the -exclude options were given as
|
|
# strings of filenames concatenated with ',' characters. If so, support it.
|
|
#
|
|
if (defined (@opt_exclude)) {
|
|
# -includeall takes priority over -exclude on the commandline if they are
|
|
# used together (wierd, but ...)
|
|
#
|
|
unless (defined ($opt_includeall)) {
|
|
foreach (@opt_exclude) {
|
|
(@tokens) = split (/,/, $_);
|
|
foreach $token (@tokens) {
|
|
$exclude{$token}++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elsif (-r "$destdir/$indexfile") {
|
|
my $tmp = &extract_meta_tag ($excludemetatag, "$destdir/$indexfile");
|
|
my $utmp;
|
|
if (defined($tmp) && !defined ($opt_includeall)) {
|
|
# We need to "decode" this string as it has been encoded for storage
|
|
# in the HTML so that raw filenames (that people should not try to
|
|
# access) are not exposed to the outside world.
|
|
#
|
|
$utmp = &decodestring ($tmp);
|
|
(@opt_exclude) = split (/,/, $utmp);
|
|
my $str = join (',', @opt_exclude);
|
|
printf ("Using saved excluded files: %s\n", $str);
|
|
foreach (@opt_exclude) {
|
|
$exclude{$_}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach $f (@files) {
|
|
if (! $exclude{$f}) {
|
|
push (@filelist, $f);
|
|
} else {
|
|
print "Excluding '$f'\n";
|
|
if (-d $f) {
|
|
chmod (0700, $f);
|
|
}
|
|
else {
|
|
chmod (0600, $f);
|
|
}
|
|
}
|
|
}
|
|
return (@filelist);
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Nuke generated files if original image gone
|
|
#
|
|
######################################################################
|
|
sub nuke_out_of_date {
|
|
foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) {
|
|
opendir(THUMBS,"$destdir/$checkdir") || die "Can't open dir $checkdir: ($!)\n";
|
|
foreach (readdir(THUMBS)) {
|
|
next if (m/^\.?\.$/);
|
|
next if (m/$framefile/);
|
|
next if (m/$slidefile/);
|
|
next if (m/$montagefile/);
|
|
next if (m/$emoticonsmile/);
|
|
next if (m/$emoticonwink/);
|
|
next if (m/$emoticonfrown/);
|
|
if (!defined($thumb_backref{"$checkdir/$_"}) and
|
|
!defined($slide_backref{"$checkdir/$_"}) and
|
|
!defined($med_backref{"$checkdir/$_"})) {
|
|
print "Removing stale $destdir/$checkdir/$_\n";
|
|
unlink("$destdir/$checkdir/$_") || warn "Can't unlink $destdir/$checkdir/$_: ($!)\n";
|
|
$modified_thumb++;
|
|
|
|
}
|
|
}
|
|
closedir(THUMBS);
|
|
}
|
|
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Convert bytes to kb string
|
|
#
|
|
######################################################################
|
|
sub convert_to_kb {
|
|
|
|
my $bytes = shift(@_);
|
|
$bytes = sprintf("%dk", $bytes / 1024);
|
|
return($bytes);
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Sortq by integer date stamp
|
|
#
|
|
######################################################################
|
|
sub bynumber {
|
|
if ($current_reverse == 0) {
|
|
$a <=> $b;
|
|
} else {
|
|
$b <=> $a;
|
|
}
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Write frameset file for slideshows
|
|
#
|
|
######################################################################
|
|
sub write_frameset {
|
|
|
|
# This is impossible to get rid of
|
|
my $framefudge = 35;
|
|
my $verstring;
|
|
|
|
open(FRAME,">$destdir/$slide_dir/$framefile") or die ("Can't open $destdir/$slide_dir/$framefile: $!\n");
|
|
|
|
select(FRAME);
|
|
|
|
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\"\n";
|
|
print "\"http://www.w3.org/TR/html401/frameset.dtd\">\n";
|
|
print "<HTML>\n";
|
|
print "<HEAD>\n";
|
|
$verstring = &versionstring();
|
|
printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
|
|
printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
|
|
print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">";
|
|
print "<TITLE>$current_titletext</TITLE>\n";
|
|
print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
|
|
print "</HEAD>\n";
|
|
if ($frame_orient eq 'horizontal') {
|
|
printf("<FRAMESET ROWS=\"%d, *\">\n", $max_thumb_y + $framefudge);
|
|
} else {
|
|
printf("<FRAMESET COLS=\"%d, *\">\n", $max_thumb_x + $framefudge);
|
|
}
|
|
print "<FRAME NAME=\"thumb\" SRC=\"$slidefile\">\n";
|
|
print "<FRAME NAME=\"view\" SRC=\"../$first_slide\">\n";
|
|
print "<NOFRAMES>No frames in this browser...go back</NOFRAMES>\n";
|
|
print "</FRAMESET>\n";
|
|
print "</HTML>\n";
|
|
|
|
select(STDOUT);
|
|
close (FRAME);
|
|
|
|
|
|
open(FRAME,">$destdir/$slide_dir/$slidefile") or die ("Can't open $destdir/$slide_dir/$slidefile: $!\n");
|
|
select(FRAME);
|
|
|
|
|
|
print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n";
|
|
print "\"http://www.w3.org/TR/html401/strict.dtd\">\n";
|
|
print "<HTML>\n";
|
|
print "<HEAD>\n";
|
|
$verstring = &versionstring();
|
|
printf ("<META NAME=\"GENERATOR\" CONTENT=\"imageindex %s\">\n", $verstring);
|
|
printf ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">\n");
|
|
print "<LINK TYPE=\"text/css\" REL=\"stylesheet\" HREF=\"../$stylefile\">\n";
|
|
print "<TITLE>$current_titletext</TITLE>\n";
|
|
print "</HEAD>\n<BODY>\n";
|
|
print "<TABLE CLASS=\"frame\"\n";
|
|
print " <TR>\n" if ($frame_orient eq 'horizontal');
|
|
foreach (@frame) {
|
|
print;
|
|
}
|
|
print " </TR>\n" if ($frame_orient eq 'horizontal');
|
|
print "</TABLE>\n";
|
|
print "</BODY>\n</HTML>\n";
|
|
|
|
select(STDOUT);
|
|
close(FRAME);
|
|
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Do next/index/prev links on slide pages
|
|
#
|
|
######################################################################
|
|
sub next_prev_links {
|
|
|
|
my $pathname = shift(@_);
|
|
|
|
print "<DIV CLASS=\"index\">";
|
|
|
|
if (defined($back{$pathname})) {
|
|
print "<A HREF=\"$back{$pathname}\">< previous</A> | ";
|
|
} else {
|
|
print "< previous | ";
|
|
}
|
|
print "<A HREF=\"../$indexfile\" TARGET=\"_parent\">index</A>";
|
|
if (defined($forward{$pathname})) {
|
|
print " | <A HREF=\"$forward{$pathname}\">next ></A>";
|
|
} else {
|
|
print " | next >";
|
|
}
|
|
|
|
print "</DIV>\n";
|
|
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# Lower-case all the filenames. I hate the uppercase filenames that come
|
|
# from my camera's default software (and Windud software). Plus I didn't
|
|
# want this "utility" in another script, so just place it here.
|
|
#
|
|
######################################################################
|
|
sub lower_case_files {
|
|
my (@files) = @_;
|
|
my ($newfile, $lowername);
|
|
|
|
foreach $name (@files) {
|
|
($lowername = $name) =~ tr/A-Z/a-z/;
|
|
if ($name =~ /[A-Z]/) {
|
|
print "Moving '$name' to '$lowername'\n";
|
|
move("$name","$lowername");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
######################################################################
|
|
#
|
|
# extract the NAME tag from an HTML file
|
|
#
|
|
######################################################################
|
|
sub extract_meta_tag {
|
|
my ($tag, $filename) = @_;
|
|
my ($name, $content, $retval);
|
|
|
|
if (! (open (FILE, $filename))) {
|
|
print STDERR "Cannot open '$filename' for reading - $!\n";
|
|
return (0);
|
|
}
|
|
# <META NAME="Columns" CONTENT="3">
|
|
#
|
|
while (<FILE>) {
|
|
if (/<META\s+NAME=\"(.*?)\"\s+CONTENT=\"(.*)\">/) {
|
|
$name = $1;
|
|
$content = $2;
|
|
if ($name eq $tag) {
|
|
$retval = $content;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
close (FILE);
|
|
return ($retval);
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Rotate given image 90 degrees
|
|
#
|
|
###############################################################################
|
|
sub rotate_image {
|
|
|
|
my $file = shift(@_);
|
|
my $argv = shift(@_);
|
|
|
|
if ($file =~ m/^(cw|ccw)$/) {
|
|
# If file is cw or ccw,
|
|
# assume the args were given backwards
|
|
my $tmp = $file;
|
|
$file = $$argv[0];
|
|
$$argv[0] = $tmp;
|
|
}
|
|
|
|
-r "$file" || die("$file: ", $!);
|
|
-w "$file" || die("$file: ", $!);
|
|
|
|
|
|
# grab the mtime of the file so we can reset it after we update it
|
|
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
|
|
$ctime,$blksize,$blocks) = stat($file);
|
|
my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
|
|
|
|
my ($name,$path,$suffix) = fileparse($file,'\.\S+');
|
|
my $thumb;
|
|
my $medium;
|
|
|
|
my $image = new Image::Magick;
|
|
|
|
my $retval = $image->Read("$file");
|
|
warn "$retval" if "$retval";
|
|
|
|
if (!defined($$argv[0]) or
|
|
($$argv[0] !~ m/^cc?w$/i)) {
|
|
print "Need 'cw' or 'ccw' argument to rotate image clockwise/counterclockwise\n";
|
|
exit(1);
|
|
}
|
|
|
|
if ($$argv[0] =~ /^ccw$/i) {
|
|
$deg = -90;
|
|
} else {
|
|
$deg = 90;
|
|
}
|
|
|
|
print "Rotating $file $deg degrees\n";
|
|
$retval = $image->Rotate($deg);
|
|
warn "$retval" if "$retval";
|
|
|
|
$retval = $image->Write(filename=>"$file");
|
|
warn "$retval" if "$retval";
|
|
|
|
system ("touch -t $posix_mtime $file");
|
|
|
|
# Nuke the generated images if they exist
|
|
# (touching the timestamp above breaks automatic regeneration logic)
|
|
if ($image->Get('format') =~ /JFIF/i) {
|
|
$thumb = $path . "$thumbnail_dir/$name" . $suffix;
|
|
$medium = $path . "$med_dir/$name" . $suffix;
|
|
} else {
|
|
$thumb = $path . "$thumbnail_dir/$name.jpg";
|
|
$medium = $path . "$med_dir/$name.jpg";
|
|
}
|
|
unlink($thumb) if (-e "$thumb");
|
|
unlink($medium) if (-e "$medium");
|
|
|
|
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Set or display caption for a particular image
|
|
#
|
|
###############################################################################
|
|
sub caption_image {
|
|
|
|
my $file = shift(@_);
|
|
my $argv = shift(@_);
|
|
my ($esc_comment, $tmpfile);
|
|
|
|
-r "$file" || die("$file: ", $!);
|
|
|
|
# grab the mtime of the file so we can reset it after we update it
|
|
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
|
|
$ctime,$blksize,$blocks) = stat($file);
|
|
my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime));
|
|
|
|
my $image = new Image::Magick;
|
|
|
|
my $retval = $image->Read("$file");
|
|
warn "$retval" if "$retval";
|
|
|
|
my $format = $image->Get('format');
|
|
warn "$retval" if "$retval";
|
|
|
|
# Set caption if another arg is present, or just display it
|
|
if (defined($$argv[0])) {
|
|
|
|
-w "$file" || die("$file: ", $!);
|
|
|
|
# Try to find wrjpgcom so we can use it for adding captions to JPG images
|
|
my $wrjpgcom_prog = &find_in_path ('wrjpgcom');
|
|
my $quote_file = quotemeta ($file);
|
|
|
|
# If a jpeg file and we found a wrjpgcom program in our path, use
|
|
# it! It simply puts the comment in the JPEG header without reading
|
|
# (uncompressing) and writing (re-compressing) the file out so
|
|
# there is no chance for data loss.
|
|
#
|
|
if (($format =~ /JFIF/i) and defined($wrjpgcom_prog)) {
|
|
|
|
$tmpfile = "$file.$$";
|
|
my $tmpfile_quote = "$quote_file.$$";
|
|
$esc_comment = quotemeta ($$argv[0]);
|
|
# FIXME
|
|
# check to see how '?' and other punctuation is escaped and fix
|
|
# it seems things are not correct.
|
|
system ("$wrjpgcom_prog -replace -comment $esc_comment $quote_file > $tmpfile_quote");
|
|
if (($? >> 8) != 0) {
|
|
printf(STDERR "Error in creating JPEG comment with 'wrjpgcom'. Leaving existing file intact.\n");
|
|
} else {
|
|
move($tmpfile, $file);
|
|
}
|
|
|
|
} else {
|
|
# Fall back to PerlMagick's routines.
|
|
#
|
|
$retval = $image->Comment("$$argv[0]");
|
|
warn "$retval" if "$retval";
|
|
|
|
$retval = $image->Write(filename=>"$file", quality=>"95",
|
|
sampling_factor=>"1x1");
|
|
warn "$retval" if "$retval";
|
|
}
|
|
|
|
system ("touch -t $posix_mtime $quote_file");
|
|
|
|
} else {
|
|
|
|
my $text = $image->Get('comment');
|
|
|
|
if (defined($text)) {
|
|
print "$file: \"$text\"\n";
|
|
} else {
|
|
print "$file: (no caption)\n";
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Print usage info from top of file
|
|
#
|
|
###############################################################################
|
|
sub usage {
|
|
|
|
open(FILE,"$0") or die "Can't open $0: $OS_ERROR";
|
|
while(<FILE>) {
|
|
last if (m/^\#\s+USAGE:/);
|
|
}
|
|
while(<FILE>) {
|
|
last if (m/^\#\#\#\#\#\#\#/);
|
|
s/^\# ?//;
|
|
print;
|
|
}
|
|
close(FILE);
|
|
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Format timestamp for HTML pages. This routine assumes that the date
|
|
# given to it is in YYYYMMDDHHMM.SS format that we've created from the
|
|
# EXIF/mtime date using strftime().
|
|
#
|
|
######################################################################
|
|
sub format_date {
|
|
my ($date, $context, $dayonly) = @_;
|
|
my ($timeformat, $dateformat);
|
|
|
|
if ($context eq 'frame') {
|
|
$timeformat = $frametimeformat;
|
|
$dateformat = $framedateformat;
|
|
}
|
|
elsif ($context eq 'index') {
|
|
$timeformat = $indextimeformat;
|
|
$dateformat = $indexdateformat;
|
|
}
|
|
elsif ($context eq 'slide') {
|
|
$timeformat = $slidetimeformat;
|
|
$dateformat = $slidedateformat;
|
|
}
|
|
else {
|
|
$timeformat = $detailtimeformat;
|
|
$dateformat = $detaildateformat;
|
|
}
|
|
|
|
# Replace "macro" patterns in the format string first
|
|
#
|
|
$timeformat =~ s/\%R/\%H:\%M/g;
|
|
$timeformat =~ s/\%r/\%I:\%M:\%S \%p/g;
|
|
$dateformat =~ s/\%F/\%Y-\%m-\%d/g;
|
|
$dateformat =~ s/\%D/\%m\/\%d\/\%y/g;
|
|
|
|
$date =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)?(\d\d)?\.?(\d\d)?/;
|
|
my $year = $1;
|
|
my $month = $2;
|
|
my $day = $3;
|
|
my $hour = $4;
|
|
my $min = $5;
|
|
my $sec = $6;
|
|
my ($ampm, $two_digit_year, $twelve_hour);
|
|
|
|
if ($year =~ /^\d\d(\d\d)$/) {
|
|
$two_digit_year = $1;
|
|
}
|
|
else {
|
|
$two_digit_year = '??'; # shouldn't ever been seen
|
|
}
|
|
|
|
# If we're told to, only format a date with no time
|
|
#
|
|
if (defined ($dayonly)) {
|
|
$dateformat =~ s/\%Y/$year/g;
|
|
$dateformat =~ s/\%y/$two_digit_year/g;
|
|
$dateformat =~ s/\%m/$month/g;
|
|
$dateformat =~ s/\%d/$day/g;
|
|
$dateformat =~ s/\%\%/\%/g;
|
|
return ($dateformat);
|
|
}
|
|
else {
|
|
if (defined($hour)) {
|
|
$twelve_hour = $hour;
|
|
$ampm = 'AM';
|
|
if ($hour > 12) {
|
|
$twelve_hour -= 12;
|
|
$ampm = 'PM';
|
|
}
|
|
}
|
|
else {
|
|
$hour = '??';
|
|
$twelve_hour = '??';
|
|
$ampm = '??'; #again, should never be seen
|
|
}
|
|
if (! defined ($min)) {
|
|
$min = '??';
|
|
}
|
|
if (! defined ($sec)) {
|
|
$sec = '??';
|
|
}
|
|
|
|
$dateformat =~ s/\%Y/$year/g;
|
|
$dateformat =~ s/\%y/$two_digit_year/g;
|
|
$dateformat =~ s/\%m/$month/g;
|
|
$dateformat =~ s/\%d/$day/g;
|
|
$dateformat =~ s/\%\%/\%/g;
|
|
|
|
$timeformat =~ s/\%S/$sec/g;
|
|
$timeformat =~ s/\%M/$min/g;
|
|
$timeformat =~ s/\%I/$twelve_hour/g;
|
|
$timeformat =~ s/\%H/$hour/g;
|
|
$timeformat =~ s/\%p/$ampm/g;
|
|
$timeformat =~ s/\%\%/\%/g;
|
|
|
|
return("$dateformat $timeformat");
|
|
}
|
|
|
|
}
|
|
|
|
######################################################################
|
|
#
|
|
# Return version string from CVS tag
|
|
#
|
|
######################################################################
|
|
sub versionstring {
|
|
|
|
my $ver = ' $Name: v1_0_7 $ ';
|
|
$ver =~ s/Name//g;
|
|
$ver =~ s/[:\$]//g;
|
|
$ver =~ s/\s+//g;
|
|
$ver =~ s/^v//g;
|
|
$ver =~ s/_/\./g;
|
|
if ($ver eq '') {
|
|
$ver = "cvs devel - " . '$Revision: 1.164 $ ';
|
|
# Nuke the $ signs -- what if somebody is keeping pages under RCS
|
|
# or CVS control?
|
|
$ver =~ s/\$//g;
|
|
$ver =~ s/\s*$//;
|
|
}
|
|
return($ver);
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Create CSS file that is shared among the HTML pages
|
|
#
|
|
###############################################################################
|
|
sub write_css {
|
|
|
|
|
|
open(CSS,">$destdir/$stylefile") or die ("Can't open $destdir/$stylefile: $!\n");
|
|
select(CSS);
|
|
|
|
print $stylesheet;
|
|
|
|
select(STDOUT);
|
|
close(CSS);
|
|
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# "Interpolate" %? escapes found in our printf-like strings defined for the
|
|
# TITLE attributes. See the beginning of this file for their definition
|
|
#
|
|
###############################################################################
|
|
|
|
sub interpolate_title_string {
|
|
my ($formatstring, $pathname, $context) = @_;
|
|
my ($filename, $date, $size, $resolution, $caption);
|
|
my ($tmp);
|
|
|
|
$filename = $info{$pathname}{'file'};
|
|
$date = &format_date ($info{$pathname}{'date'}, $context);
|
|
$size = $info{$pathname}{'size'};
|
|
$resolution = $info{$pathname}{'geometry'};
|
|
$caption = $info{$pathname}{'comment'};
|
|
if (! defined ($caption)) {
|
|
$caption = '';
|
|
}
|
|
$tmp = $formatstring;
|
|
|
|
$tmp =~ s/\%f/$filename/g if $filename;
|
|
$tmp =~ s/\%d/$date/g if $date;
|
|
$tmp =~ s/\%s/$size/g if $size;
|
|
$tmp =~ s/\%r/$resolution/g if $resolution;
|
|
$tmp =~ s/\%c/$caption/g;
|
|
$tmp =~ s/\%\%/%/g;
|
|
|
|
# In case the format string has " marks in it, change all those to '.
|
|
# The " marks are needed to mark the argument to the TITLE attribute.
|
|
#
|
|
$tmp =~ s/\"/\'/g;
|
|
return ($tmp);
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# "Interpolate" %? escapes found in our printf-like strings defined for the
|
|
# TITLE attributes. However, the %? escapes for this function are based on what
|
|
# you could conceivably need when processing a directory.
|
|
#
|
|
# See the beginning of this file for their definition
|
|
#
|
|
###############################################################################
|
|
|
|
sub interpolate_title_string_dir {
|
|
my ($formatstring, $dir, $context) = @_;
|
|
my ($tmp, $num, $date, $metadate, $metatitle);
|
|
|
|
$tmp = $formatstring;
|
|
$num = &extract_meta_tag($numimagesmetatag, "$srcdir/$dir/$indexfile");
|
|
|
|
# If we plucked out the number of images from the metadata of a directory's
|
|
# index.html file, replace it. Else, give a warning if we didn't find it but
|
|
# somebody still used %n
|
|
#
|
|
if (defined ($num)) {
|
|
$tmp =~ s/\%n/$num/g if $num;
|
|
}
|
|
else {
|
|
if ($tmp =~ /\%n/) {
|
|
if (!defined ($remember_warning{$dir})) {
|
|
printf (STDERR "Warning: %%n escape used in format string and %s META tag not found in %s. Re-run imageindex in '$dir'.\n", $numimagesmetatag, "$srcdir/$dir/$indexfile");
|
|
$remember_warning{$dir}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$metadate = &extract_meta_tag($begindatemetatag, "$srcdir/$dir/$indexfile");
|
|
$date = &format_date ($metadate, $context, 'dayonly');
|
|
$tmp =~ s/\%b/$date/g if $date;
|
|
|
|
$metadate = &extract_meta_tag($enddatemetatag, "$srcdir/$dir/$indexfile");
|
|
$date = &format_date ($metadate, $context, 'dayonly');
|
|
$tmp =~ s/\%e/$date/g if $date;
|
|
|
|
$metatitle = &extract_meta_tag($titlemetatag, "$srcdir/$dir/$indexfile");
|
|
$tmp =~ s/\%t/$metatitle/g if $metatitle;
|
|
|
|
# In case the format string has " marks in it, change all those to '.
|
|
# The " marks are needed to mark the argument to the TITLE attribute.
|
|
#
|
|
$tmp =~ s/\"/\'/g;
|
|
return ($tmp);
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Look for external programs we depend on in the $PATH. It just finds the first
|
|
# occurence of $prog in $PATH.
|
|
#
|
|
###############################################################################
|
|
sub find_in_path {
|
|
my ($prog) = @_;
|
|
my ($retval);
|
|
|
|
undef $retval;
|
|
foreach $dir (split (/:/, $ENV{'PATH'})) {
|
|
if (-r "$dir/$prog" && -x "$dir/$prog") {
|
|
$retval = "$dir/$prog";
|
|
}
|
|
}
|
|
return ($retval);
|
|
}
|
|
|
|
|
|
###############################################################################
|
|
#
|
|
# Encode/decode routines for exclude filenames when stuffed in a meta tag
|
|
#
|
|
###############################################################################
|
|
sub encodestring {
|
|
my ($tmp) = @_;
|
|
my $etmp;
|
|
$etmp = pack ("u*", $tmp);
|
|
# Hack the string to get rid of \n chars so we can store it on 1 line
|
|
$etmp =~ s/\n/..1xn!_ltr../g;
|
|
|
|
# Get rid of ampersands
|
|
$etmp =~ s/\&/..xn!_ltr1../g;
|
|
|
|
# Get rid of double-quotes
|
|
$etmp =~ s/\"/..sb!_lho1../g;
|
|
return ($etmp);
|
|
}
|
|
|
|
sub decodestring {
|
|
my ($tmp) = @_;
|
|
my $utmp;
|
|
|
|
# Unhack the string to bring back & characters
|
|
$tmp =~ s/\.\.sb\!_lho1\.\./\"/g;
|
|
$tmp =~ s/\.\.xn\!_ltr1\.\./\&/g;
|
|
|
|
# Unhack the string to bring back & characters
|
|
$tmp =~ s/\.\.xn\!_ltr1\.\./\&/g;
|
|
|
|
# Unhack the string to bring back \n characters
|
|
$tmp =~ s/\.\.1xn\!_ltr\.\./\n/g;
|
|
$utmp = unpack ("u*", $tmp);
|
|
return ($utmp);
|
|
}
|
|
|
|
#############################################################################
|
|
#
|
|
# This routine samples linearly (as possible) across the available files in
|
|
# a directory. The first pass at sampling is a simple modulo function based
|
|
# upon the ratio of files to the number of tiles we can use in the montage.
|
|
# If that first pass sample did not produce enough files, then we go back
|
|
# iteratively through the list and as evenly-as-possible select unused
|
|
# files from those left in the pool.
|
|
#
|
|
#############################################################################
|
|
sub sample_files_for_montage {
|
|
my (@files) = @_;
|
|
my ($numdiv, $numchosen, $chunksize, $numfiles, $numleft);
|
|
my ($i, $index, $f, @ret);
|
|
|
|
$numfiles = scalar (@files);
|
|
$numdiv = sprintf ("%d", $numfiles / $montage_max);
|
|
$numdiv++;
|
|
|
|
for ($i = 0; $i < $numfiles; $i++) {
|
|
if (($i % $numdiv) == 0) {
|
|
$chosen{$files[$i]}++;
|
|
}
|
|
}
|
|
|
|
$numchosen = scalar (keys %chosen);
|
|
|
|
$numleft = $montage_max - $numchosen;
|
|
|
|
if ($numleft) {
|
|
$chunksize = sprintf ("%d", $numfiles / $numleft);
|
|
$index = 0;
|
|
for ($i = 0; $i < $numleft; $i++) {
|
|
&mark_next_file_for_montage ($index + 1, $numfiles, @files);
|
|
$index = $index + $chunksize;
|
|
}
|
|
}
|
|
|
|
foreach $f (@files) {
|
|
if ($chosen{$f}) {
|
|
push (@ret, $f);
|
|
}
|
|
}
|
|
|
|
return (@ret);
|
|
}
|
|
|
|
#############################################################################
|
|
#
|
|
# cycle through the given list of files. If the list[$index] is already marked
|
|
# (via the global hash %chosen) then move onto the next one, etc.
|
|
#
|
|
#############################################################################
|
|
sub mark_next_file_for_montage {
|
|
my ($index, $numfiles, @files) = @_;
|
|
my ($i);
|
|
|
|
for ($i = $index; $i < $numfiles; $i++) {
|
|
if (! $chosen{$files[$i]}) {
|
|
$chosen{$files[$i]}++;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Exclude certain filenames from the list of thumbnails to be used in the
|
|
# montage image.
|
|
#
|
|
###############################################################################
|
|
sub exclude_montage_files {
|
|
my (@files) = @_;
|
|
my (@tmp, $file);
|
|
|
|
foreach (@files) {
|
|
$file = basename ($_);
|
|
unless (defined ($skipmont{$file})) {
|
|
push (@tmp, $_);
|
|
}
|
|
}
|
|
return (@tmp);
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# "html-ize" a caption found in an image. Just in case there are certain
|
|
# characters used which we want to "escape."
|
|
#
|
|
###############################################################################
|
|
sub htmlize_caption {
|
|
my ($caption, $slide) = @_;
|
|
|
|
$caption =~ s/\&/\&/g;
|
|
$caption =~ s/\</\</g;
|
|
$caption =~ s/\>/\>/g;
|
|
$caption =~ s/\"/\"/g;
|
|
|
|
# Help smiley's render in a "mo-better" way when they are at the end of a
|
|
# caption and enclosed in parens
|
|
#
|
|
if ($caption =~ /(:\-?[\(\)])\s*\)\s*$/) {
|
|
my $tmp = $1;
|
|
$caption =~ s/:\-?[\(\)]\s*\)\s*$/$tmp\ \)/;
|
|
}
|
|
|
|
$caption = &emoticonify ($caption, $slide);
|
|
|
|
return ($caption);
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Translate ASCII smiley's embedded into image captions into emoticons
|
|
#
|
|
###############################################################################
|
|
sub emoticonify {
|
|
my ($caption, $slide) = @_;
|
|
my ($thumbdir, $attr);
|
|
|
|
return ($caption) if (! defined ($do_emoticons) || $do_emoticons == 0);
|
|
|
|
# This is a hack, please ignore and move on ... nothing to see here.
|
|
#
|
|
$caption =~ s/\ /NoNBrEaKaBleSpacE/g;
|
|
|
|
$thumbdir = $thumbnail_dir;
|
|
if ($slide) {
|
|
$thumbdir = '../' . $thumbdir;
|
|
}
|
|
$attr = 'STYLE="vertical-align: middle;" WIDTH="19" HEIGHT="19"';
|
|
|
|
if ($caption =~ s/:\-?\)/\<IMG SRC=\"$thumbdir\/$emoticonsmile\" $attr ALT=\" \[smiley icon\] \"\>/g) {
|
|
$emoticon{'smile'}++;
|
|
}
|
|
if ($caption =~ s/;\-?\)/\<IMG SRC=\"$thumbdir\/$emoticonwink\" $attr ALT=\" \[smiley icon\] \"\>/g) {
|
|
$emoticon{'wink'}++;
|
|
}
|
|
if ($caption =~ s/:\-?\(/\<IMG SRC=\"$thumbdir\/$emoticonfrown\" $attr ALT=\" \[frown icon\] \"\>/g) {
|
|
$emoticon{'frown'}++;
|
|
}
|
|
|
|
# Undo the hack
|
|
#
|
|
$caption =~ s/NoNBrEaKaBleSpacE/\ /g;
|
|
return ($caption);
|
|
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Write out PNG files representing the emoticons
|
|
#
|
|
###############################################################################
|
|
sub write_emoticon_png {
|
|
my ($type) = @_;
|
|
my ($img);
|
|
|
|
if (! open (IMG, '>' . $destdir . '/' . $thumbnail_dir . "/$emoticonprefix" . $icon . ".png")) {
|
|
printf (STDERR "Could not open emoticon file for '$icon' for writine - $!\n");
|
|
return;
|
|
}
|
|
# UUDecode the small PNG files that represent the emoticons and dump them to
|
|
# the appropriate files in $thumbnail_dir
|
|
#
|
|
$img = unpack ("u*", $png{$type});
|
|
print IMG $img;
|
|
close (IMG);
|
|
}
|
|
|
|
###############################################################################
|
|
#
|
|
# Create a montage of images in the current directory. This image will be
|
|
# pointed to by the parent directory's index.html file to show a sort of
|
|
# "thumbnail preview" of the contents of this directory.
|
|
#
|
|
###############################################################################
|
|
|
|
sub create_montage {
|
|
|
|
my @files = @_;
|
|
my (@modfiles);
|
|
|
|
@files = &exclude_montage_files (@files);
|
|
|
|
foreach (@files) {
|
|
push (@modfiles, quotemeta ($_));
|
|
}
|
|
|
|
# If we have defined that a lesser number of "tiles" can be used in the
|
|
# montage vs. the # of files in this directory, then we'll "sample" the
|
|
# files as evenly as possible to avoid clustering of shots that might be
|
|
# similar to each other.
|
|
#
|
|
if (scalar (@modfiles) > $montage_max) {
|
|
@modfiles = &sample_files_for_montage (@modfiles);
|
|
}
|
|
|
|
if ($do_montage == 1) {
|
|
|
|
if (($modified_thumb != 0) or (! -e "$destdir/$thumbnail_dir/$montagefile")) {
|
|
|
|
my $number = $#modfiles + 1;
|
|
my $tile_x = 1;;
|
|
my $tile_y = 1;
|
|
|
|
# FIXME these both blindly expand x before expanding y
|
|
# Should this depend on some aspect ratio?
|
|
while(($tile_x * $tile_y) < $montage_min) {
|
|
$tile_x++;
|
|
$tile_y++ if (($tile_x * $tile_y) < $montage_min);
|
|
}
|
|
while(($tile_x * $tile_y) < $number) {
|
|
$tile_x++;
|
|
$tile_y++ if (($tile_x * $tile_y) < $number);
|
|
}
|
|
|
|
my $index = 0;
|
|
while (($#modfiles + 1) < ($tile_x * $tile_y)) {
|
|
if ($montage_fill eq 'blank') {
|
|
push(@modfiles, "NULL:");
|
|
} else {
|
|
push(@modfiles, $modfiles[$index]);
|
|
$index = ($index+1) % $number;
|
|
|
|
}
|
|
}
|
|
|
|
my $tile = sprintf("%dx%d", $tile_x, $tile_y);
|
|
my $geom = sprintf("%dx%d", $max_mont_thumb_x, $max_mont_thumb_y);
|
|
my $newgeom = sprintf("%dx%d", $current_thumbnail_x, $current_thumbnail_y);
|
|
|
|
print "Picked $tile array of $geom for montage\n" if ($opt_debug);
|
|
|
|
print "Creating $destdir/$thumbnail_dir/$montagefile\n";
|
|
|
|
system("montage -quality $thumb_quality -bordercolor white -transparent white -borderwidth $montage_whitespace -geometry $geom -tile $tile @modfiles $destdir/$thumbnail_dir/$montagefile");
|
|
if (($? >> 8) != 0) {
|
|
printf(STDERR "Error in creating montage file\n");
|
|
return(-1);
|
|
}
|
|
|
|
# Resize to std. thumbnail
|
|
my $image = new Image::Magick;
|
|
my $retval;
|
|
|
|
$retval = $image->Read(filename=>"$destdir/$thumbnail_dir/$montagefile");
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Resize(geometry=>$newgeom);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Set(interlace=>Line);
|
|
warn "$retval" if "$retval";
|
|
$retval = $image->Write(filename=>"$destdir/$thumbnail_dir/$montagefile");
|
|
warn "$retval" if "$retval";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unlink("$destdir/$thumbnail_dir/$montagefile")
|
|
if (-e "$destdir/$thumbnail_dir/$montagefile");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sub read_stored_meta_data {
|
|
my ($tmp);
|
|
|
|
if (-r "$destdir/$indexfile") {
|
|
$tmp = &extract_meta_tag ($columnsmetatag, "$destdir/$indexfile");
|
|
# If we found data, check it against program defaults
|
|
if (defined ($tmp)) {
|
|
$current_columns = $tmp;
|
|
print "Using saved number of columns: $current_columns\n" if ! defined ($opt_columns);
|
|
}
|
|
|
|
$tmp = &extract_meta_tag ($titlemetatag, "$destdir/$indexfile");
|
|
# If we found data, check it against program defaults
|
|
if (defined ($tmp)) {
|
|
$current_titletext = $tmp;
|
|
print "Using saved title: $current_titletext\n" if ! defined ($opt_title);
|
|
}
|
|
|
|
$tmp = &extract_meta_tag ($thumbxmetatag, "$destdir/$indexfile");
|
|
# If we found data, check it against program defaults
|
|
if (defined ($tmp)) {
|
|
$current_thumbnail_x = $tmp;
|
|
print "Using saved thumbnail X size: $current_thumbnail_x\n" if ! defined ($opt_x);
|
|
}
|
|
|
|
$tmp = &extract_meta_tag ($thumbymetatag, "$destdir/$indexfile");
|
|
# If we found data, check it against program defaults
|
|
if (defined ($tmp)) {
|
|
$current_thumbnail_y = $tmp;
|
|
print "Using saved thumbnail Y size: $current_thumbnail_y\n" if ! defined ($opt_y);
|
|
}
|
|
|
|
$tmp = &extract_meta_tag ($reversemetatag, "$destdir/$indexfile");
|
|
# If we found data, check it against program defaults
|
|
if (defined ($tmp)) {
|
|
$current_reverse = $tmp;
|
|
print "Using saved reverse: $current_reverse\n" if ! defined ($opt_reverse);
|
|
}
|
|
|
|
&decide_which_md_to_store();
|
|
}
|
|
}
|
|
|
|
sub override_by_commandline {
|
|
if (defined($opt_columns)) {
|
|
$current_columns = $opt_columns;
|
|
}
|
|
if (defined($opt_title)) {
|
|
$current_titletext = $opt_title;
|
|
}
|
|
if (defined($opt_reverse)) {
|
|
$current_reverse = $opt_reverse;
|
|
}
|
|
if (defined($opt_x)) {
|
|
$current_thumbnail_x = $opt_x;
|
|
if ($current_thumbnail_x != $default_thumbnail_x) {
|
|
$opt_forceregen = 1;
|
|
}
|
|
}
|
|
if (defined($opt_y)) {
|
|
$current_thumbnail_y = $opt_y;
|
|
if ($current_thumbnail_y != $default_thumbnail_y) {
|
|
$opt_forceregen = 1;
|
|
}
|
|
}
|
|
&decide_which_md_to_store();
|
|
}
|
|
|
|
sub decide_which_md_to_store {
|
|
if ($current_columns != $default_columns) {
|
|
$write_meta_tag{$columnsmetatag}++;
|
|
}
|
|
else {
|
|
undef $write_meta_tag{$columnsmetatag};
|
|
}
|
|
|
|
if ($current_thumbnail_x != $default_thumbnail_x) {
|
|
$write_meta_tag{$thumbxmetatag}++;
|
|
}
|
|
else {
|
|
undef $write_meta_tag{$thumbxmetatag};
|
|
}
|
|
|
|
if ($current_thumbnail_y != $default_thumbnail_y) {
|
|
$write_meta_tag{$thumbymetatag}++;
|
|
}
|
|
else {
|
|
undef $write_meta_tag{$thumbymetatag};
|
|
}
|
|
|
|
if ($current_titletext ne $default_titletext) {
|
|
$write_meta_tag{$titlemetatag}++;
|
|
}
|
|
else {
|
|
undef $write_meta_tag{$titlemetatag};
|
|
}
|
|
|
|
if ($current_reverse ne $do_reverse) {
|
|
$write_meta_tag{$reversemetatag}++;
|
|
}
|
|
else {
|
|
undef $write_meta_tag{$reversemetatag};
|
|
}
|
|
}
|
|
|
|
sub initialize_current_vars {
|
|
$current_columns = $default_columns;
|
|
$current_titletext = $default_titletext;
|
|
$current_thumbnail_x = $default_thumbnail_x;
|
|
$current_thumbnail_y = $default_thumbnail_y;
|
|
$current_reverse = $do_reverse;
|
|
}
|
|
|
|
##############################################################################
|
|
#
|
|
# Just initialize the 'png' array with UUENCODED PNG files for emoticons. This
|
|
# was placed down here so as not to clutter up the top of the file where the
|
|
# other globals are initialized.
|
|
#
|
|
##############################################################################
|
|
sub init_png_array {
|
|
|
|
$png{'wink'} = <<'EOF';
|
|
MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7____,
|
|
MS``S,P#___\```#__P!/FRMM`````7123E,`0.;89@````%B2T=$!?AOZ<<`
|
|
M``!F241!5'C:;8_1#8`P"$3O@Q'L!CJ!#M`FQP`FL/\J%FJ-)O+U<H&7`P!(
|
|
M5N2PN"_)TJESH'I.G6'&XFNB`<UV=5,_*]0.->:R.N?=+?A#36P6)H9!(F)Z
|
|
CI1BYC1+=-K2?9J^^SQ<7J48K([9O4:P`````245.1*Y"8((`
|
|
`
|
|
EOF
|
|
|
|
$png{'smile'} = <<'EOF';
|
|
MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7__P#,
|
|
MS`!F9@#_,P````#___]]YKD%````!G123E/______P"SOZ2_`````6)+1T0%
|
|
M^&_IQP```&U)1$%4>-I-C\$-P"`,`UV)`?I@@#ZZ03M`*L&?!]E_E=H$(5`>
|
|
MEP39#MR]E%+=&T@GD*NPD\A"PWBU@4,VADQ$*J,:OHF'<14(BX]14R#0%J1+
|
|
M>!.I(&,I=(!Q3+IT2\\+N2D#A\JP)]ORKBM^`[0;1*VK3]P`````245.1*Y"
|
|
"8((`
|
|
`
|
|
EOF
|
|
|
|
$png{'frown'} = <<'EOF';
|
|
MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````'E!,5$7____,
|
|
MS`"9F0!F9@`S,P#,S#/___\S,S,```#__P`[/ZS;`````7123E,`0.;89@``
|
|
M``%B2T=$"?'9I>P```"$241!5'C:78^Q#<,P#`19:@1I`WN!`%G`@`<(8"T0
|
|
M>`.GE-R8*EV%OVU(24YA@L7A^>"31$3,G*@6!\!7=D$@\(8%!=K)Q&/#]U-4
|
|
M=AC>\;&.0I0A:YQVG$FM)\<E`X9X`F/'5A4UK304G0Z&&3->$;-N<-TJEM;0
|
|
@UQOZ@DOVMWO_7_P`Y9]*.M\PG><`````245.1*Y"8((`
|
|
`
|
|
EOF
|
|
|
|
}
|