From 2193f22849f9732cdd4573613eb35421a4ac7f01 Mon Sep 17 00:00:00 2001 From: age Date: Wed, 7 Jun 2006 09:15:22 +0000 Subject: [PATCH] aus "admin-tools" verschoben --- WKNcharts/shell/getWKNcharts.sh | 81 + fireballsuche.sh | 12 + imageindex | 2995 +++++++++++++++++++++++++++++++ 3 files changed, 3088 insertions(+) create mode 100755 WKNcharts/shell/getWKNcharts.sh create mode 100755 fireballsuche.sh create mode 100755 imageindex diff --git a/WKNcharts/shell/getWKNcharts.sh b/WKNcharts/shell/getWKNcharts.sh new file mode 100755 index 0000000..9eb9c35 --- /dev/null +++ b/WKNcharts/shell/getWKNcharts.sh @@ -0,0 +1,81 @@ +#!/bin/sh +## Dieses Script nutzt die Dienste von finanztr*ff.d* +## Dieser generiert Grafiken fuer Aktien-/Fondkurse. +## +## geeks starten $0 +## lamer glotzen auf www.mon*ysp*cial.d* + +#### Grundeinstellungen +## Schreib jeweils die WKN/ISIN und den Namen in eine Zeile einer Datei. +## Beide duerfen keine Leerzeichen enthalten. Bsp: +## 12345 Eine_Sinnlos_Aktie + +## hier kommt der Dateiname rein: +WKNFILE="wkns.txt" + +## alt: hier die Wertpapierkennnummern eintragen (WKN oder ISIN) +#WKN="881823 980705 870737 847652 847414 DE000A0AB0K1 DE0005152623 LU0048578792 DE0009802306 DE0009848002" + +## DURATION bestimmt das angezeigte Intervall +## 0 ist Tageskurs; x0 sind Tage; x00 sind Monate; x0000 Jahre +DURATION="30 300 10000 30000" +PREDURATION="&zeit=" +## Groesse der Grafik +WIDTH="&b=400" +HEIGHT="&h=240" + +##### Spezialeinstellungen +## mit TYP wird die Art des Graphen ausgewaehlt +TYP="&typ=0" +## AVERAGE zeichnet die Mittelwerte der letzten x Tage +AVERAGE1="&d=38" +AVERAGE2="&d=200" + +##### ab hier brauchst du in der Regel nichts mehr aendern +BASEURL="http://gfx.finanztreff.de/charts/cc_gatrixx.gfx?" +IMAGETYP="&out=png" +## BOERSE _muss_ angegeben werden; k.A. ob grosse Unterschiede zwischen den Boersen existieren +BOERSE="&boerse=1" +## hab ich nicht weiter getestet, bleibt deswegen erstmal default +LAND="$land=276" +DATE=`date '+0%Y-%m-%d'` + +##### genug Variablen jetzt geht's los +WKN=( `cat "${WKNFILE}"` ) +COUNT=${#WKN[@]} +INDEX=0 +echo "Ich hole folgende Paare:" +echo -e "WKN\tNAME" +while [ $INDEX -lt $COUNT ]; do + echo -e "${WKN[$INDEX]}\t${WKN[$INDEX+1]}" + INDEX=$(( $INDEX+2 )) +done +read -p "Korrekt? (j/n)" WILLE +if [ X"$WILLE" != X"j" ]; then + echo $WILLE + echo "dann eben nicht" + exit 1 +else + mkdir $DATE || exit 2 + cd $DATE + COUNT=${#WKN[@]} + INDEX=0 + while [ $INDEX -lt $COUNT ]; do + THIS_WKN=${WKN[$INDEX]} + THIS_NAME=${WKN[$INDEX+1]} + SEMIURL="herkunft=123&string=${THIS_WKN}${WIDTH}${HEIGHT}${IMAGETYP}${TYP}\ +${AVERAGE1}${AVERAGE2}${BOERSE}${LAND}" + URL=${BASEURL}${SEMIURL} + ## referer wird nur so gesetzt, wegen finanztreff + REFERER="http://www.moneyspecial.de/123/kurse_einzelkurs_charts.htm?u=0&p=0&k=0&seite=kurse${SEMIURL}" + echo -e "Ich hole jetzt die Grafiken fuer: ${THIS_WKN} ${THIS_NAME}\n" + for THIS_DURATION in $DURATION; do + URL2=${URL}${PREDURATION}${THIS_DURATION} + echo -e "--> $URL2 \n" + wget ${URL2} --referer=${REFERER} -O ${DATE}_${THIS_WKN}_${THIS_NAME}_${THIS_DURATION}.png -q + done + INDEX=$(( $INDEX+2 )) + done +fi + + diff --git a/fireballsuche.sh b/fireballsuche.sh new file mode 100755 index 0000000..202c0ba --- /dev/null +++ b/fireballsuche.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# die sed zeile wandelt html breaks in shell breaks um ;) +# damit das greppen klappt +while true; do + wget -q -O - http://www.fireball.de/livesuche/index.html \ + | grep "Klicken Sie auf eine Anfrage" \ + | sed s#\#\\n#g \ + | grep "http://suche.fireball.de/cgi-bin/pursuit?query=" \ + >> fireballsuchbegriffe.txt + sleep 10 +done + diff --git a/imageindex b/imageindex new file mode 100755 index 0000000..3ac0db5 --- /dev/null +++ b/imageindex @@ -0,0 +1,2995 @@ +#!/usr/bin/perl -w +# +## this versions hasa some minor changes by age@systemausfall.org +# +# Edwin Huffstutler +# John Reynolds +# +# perl script for: web page index/thumbnails of photos. +# orginally started life as a menu selector for fvwm2 backgrounds... +# +# USAGE: +# +# imageindex [options] +# +# is assumed to be "." if not given +# +# Options: (can be abbreviated if unique) +# +# -title title for this page (saved for susbsequent runs) +# -destdir export image/html tree to dir (will be created if needed) +# -[no]recurse enable/disable recursion into subdirectories +# -[no]medium enable/disable medium size images/links +# -[no]slide enable/disable slideshow files +# -[no]detail enable/disable detail file +# -[no]dirs enable/disable directory entries +# -[no]montage enable/disable directory montages +# -forceregen force regeneration of thumbnails +# -columns number of columns in html table (saved for susbsequent runs) +# -exclude Exclude from processing. Can be used multiple times +# -includeall Nullifies excluded file list (saved from previous run) +# -skipmont Exclude from being included in a directory montage. +# -reverse Order timestamps with newest first +# -x override thumbnail x size +# -y override thumbnail y size +# -help show this text +# -version show the current version +# -d 'var=val' force override of global variable +# +# See also the configuration section at the top of the program itself, +# or in ~/.imageindexrc +# +# (non-html-generating, utility options) +# +# -lowercase Lowercase all image files in a directory +# -caption Store comment in image +# -rotate [cw|ccw] Rotate an image clockwise or counterclockwise +# -showexcluded Show which files were excluded in a prior run +# +###################################################################### + +# +# Configuration options +# + +# sizes / dirs +$thumbnail_dir = 'thumbnail'; +$default_thumbnail_x = 200; +$default_thumbnail_y = 200; + +# If both dimensions of the original are within this much of the thumb +# dimensions we will skip the thumbnail and just use the original +$thumbnail_threshold = 1.0; + +$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 = "empty"; +$updirtext = "up one directory"; +$framelinktext = "slideshow view (frames)"; +$detaillinktext = "details index"; +$indexlinktext = "main index"; +$default_titletext = "Image directory"; + +# These five variables control the TITLE attribute on anchor constructs in the +# index and frame views. When TITLE attributes are given they are usually +# rendered as "tooltip" bubbles that show text when a cursor hovers and stops +# over the active link. We use them here to give a visual cue about the image. +# These variables work much like printf(1) strings. +# +# %f => replaced with the filename of the image +# %d => replaced with the date/time of the image (or mtime of the file) +# %s => replaced with the size of the file (in Kb) +# %r => replaced with the resolution (XxY) of the original image +# %c => replaced with the image's caption (if stored with one) +# %% => replaced with a literal '%' character +# +# The following are used when directories are processed and a montage of +# that directory is used as the thumbnail of the dir. +# +# %n => replaced with number of images in a directory +# %b => replaced with the "begin" date from a directory of images +# %e => replaced with the "end" date from a directory of images +# %t => replaced with the "title" from a directory of images +# +# Other characters (including spaces) are literal. "undef" these in +# your ~/.imageindexrc file if you don't want them to show up. The "date/time" +# related constructs are interpolated using the date/time format variables +# defined below. +# +$framethumbtitle = "%f - %d"; +$indexthumbtitle = "%f (%s)"; +$slidethumbtitle = "%f (%s)"; +$detailthumbtitle = "%c"; +$montagetitle = "%n images %b through %e"; + +# Date/Time format strings. These strings are formatted much like the above +# variables and the definitions of the escape sequences come from the POSIX +# strftime(3) definitions. NOT ALL of strftime(3) are supported for obvious +# reasons. +# +# %S is replaced by the second as a decimal number (00-60). +# %M is replaced by the minute as a decimal number (00-59). +# %I is replaced by the hour (12-hour clock) as a decimal number (01-12). +# %H is replaced by the hour (24-hour clock) as a decimal number (00-23). +# %p is replaced by national representation of either "ante meridiem" or +# "post meridiem" as appropriate (currently only U.S. "am" or "pm") +# %R is equivalent to "%H:%M" (in *timeformat variables only). +# %r is equivalent to "%I:%M:%S %p" (in *timeformat variables only). +# +# %Y is replaced by the year with century as a decimal number. +# %y is replaced by the year without century as a decimal number (00-99). +# %m is replaced by the month as a decimal number (01-12). +# %d is replaced by the day of the month as a decimal number (01-31). +# %F is equivalent to "%Y-%m-%d" (in *dateformat variables only). +# %D is equivalent to "%m/%d/%y" (in *dateformat variables only). +# %% is replaced by a literal "%". + +$framedateformat = "%m/%d/%Y"; +$frametimeformat = "%r"; + +$indexdateformat = "%m/%d/%Y"; +$indextimeformat = "%r"; + +$slidedateformat = "%m/%d/%Y"; +$slidetimeformat = "%r"; + +$detaildateformat = "%m/%d/%Y"; +$detailtimeformat = "%I:%M %p"; + +# Pathnames +$indexfile = 'index.html'; +$detailfile = 'details.html'; +$framefile = 'frame.html'; +$slidefile = 'slides.html'; +$slide_dir = 'slides'; +$stylefile = 'style.css'; +$montagefile = 'montage.jpg'; +$emoticonprefix = 'ii_'; +$emoticonsmile = $emoticonprefix . 'smile.png'; +$emoticonwink = $emoticonprefix . 'wink.png'; +$emoticonfrown = $emoticonprefix . 'frown.png'; + +# File exclusion customization (regex) +# (Anything non-image and non-dir will be skipped automatically, this just +# makes it silent) +@exclude = qw( + ^CVS$ + ^.nautilus-metafile.xml$ + ^.thumbnails$ + ^.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, " $emptycell\n"); + push(@details, " $emptycell\n"); + } + push(@index, " \n"); + push(@details, " \n"); +} + +# Nuke generated dirs if no contents +system("rm -rf $destdir/$thumbnail_dir") if ($thumbnail_counter == 0); +system("rm -rf $destdir/$slide_dir") if ($slide_counter == 0); +system("rm -rf $destdir/$med_dir") if ($med_counter == 0); + +# Create montage if we had more than just dir entries here +if (($dir_counter != $object_counter)) { + &create_montage(@montagefiles); +} + +# Create stylesheet +&write_css(); + +# Write index web page +open(INDEX,">$destdir/$indexfile") or die ("Can't open $destdir/$indexfile: $!\n"); +&page_header('index', $index_linkto); +foreach (@index) { + print INDEX; +} +&page_footer('index'); +close(INDEX); + +# Write photo details file +if ($do_detail == 1) { + open(INDEX,">$destdir/$detailfile") or die ("Can't open $destdir/$indexfile: $!\n"); + &page_header('detail', $details_linkto); + foreach (@details) { + print INDEX; + } + &page_footer('detail'); + close(INDEX); +} else { + unlink("$destdir/$detailfile") if (-e "$destdir/$detailfile"); +} + +# Write slide/frame files +if (($do_slide == 1) and ($slide_counter > 1)) { + &write_frameset(); +} else { + system("rm -rf $destdir/$slide_dir") if (-d "$destdir/$slide_dir"); +} + +# Optionally export images somewhere else +if ($opt_destdir) { + printf ("Copying image files from '$srcdir' to '$destdir'.\n"); + foreach my $image (keys %info) { + # BSD's default 'cp' cannot preserve links like GNU fileutils cp can + # + if ($uname =~ /BSD/) { + system("cp -pv $image $destdir"); + } + else { + system("cp -dpuv $image $destdir"); + } + } +} + +if (defined ($do_emoticons) && $do_emoticons) { + foreach $icon ('wink', 'smile', 'frown') { + if ($emoticon{$icon}) { + &write_emoticon_png ($icon); + } else { + unlink ($destdir . '/' . $thumbnail_dir . "/$emoticonprefix${icon}.png"); + } + } +} + +###################################################################### +# +# Write the various HTML parts for this image +# +###################################################################### +sub image_entry { + + my $pathname = shift(@_); + my $filename = $info{$pathname}{'file'}; + my $link; + + &index_html($pathname); + + if ($do_detail == 1) { + &details_html($pathname); + } + + if (($do_slide == 1) and ($image_counter > 1)) { + &slide_html($pathname); + } else { + my $file = $info{$pathname}{slide}; + unlink($file) if (-e $file); + } + + # Increment for next time + $col_counter++; + $col_counter = 1 if ($col_counter > $current_columns); + +} + + +############################################################################### +# +# Generate HTML for index page entry +# +############################################################################### + +sub index_html { + + my $pathname = shift(@_); + my $filename = $info{$pathname}{'file'}; + my $link; + my $anchortext; + + # At beginning of row? + if ($col_counter == 1) { + push(@index, " \n"); + } + + # Image + push(@index, " \n"); + push(@index, "
"); + push(@index, &format_date($info{$pathname}{'date'}, 'index')); + push(@index,"
\n"); + + if (($index_linkto eq 'details') and ($do_detail == 1)) { + $link = "$detailfile#$filename"; + } elsif (($index_linkto eq 'medium') and !defined($skipmedium{$pathname})) { + $link = $info{$pathname}{'medium'}; + } elsif (($index_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { + $link = $info{$pathname}{'thumb'}; + } elsif (($index_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) { + $link = $info{$pathname}{'slide'}; + } else { + $link = $filename; + } + + $anchortext = " "; + + push(@index, $anchortext); + + if (defined($skipthumb{$pathname})) { + push(@index,"\"\n"); + + push(@index, "
"); + + # Full size link + push(@index,"full size"); + + # Medium size link if within the threshold + unless (defined($skipmedium{$pathname})) { + push(@index," | medium"); + } + + # Detail list link + if ($do_detail == 1) { + push(@index," | details"); + } + + push(@index,"
\n"); + + # Caption if any (jpeg comment field) + if (defined($info{$pathname}{'comment'})) { + my ($tmp); + push(@index, "
"); + # Hack: if a comment has an ellipsis at the very end, make the HTML use a + # non-breakable space before it so that the ellipsis doesn't "wrap" inside + # the table field. It just looks better for those cases where the comment + # is just long enough to wrap when rendered in the space given + # + $tmp = $info{$pathname}{'comment'}; + $tmp = &htmlize_caption ($tmp); + if ($tmp =~ /(\s+)\.\.\.\s*$/) { + $tmp =~ s/(\s+)\.\.\.\s*$/ .../; + } + push(@index, $tmp); + push(@index,"
\n"); + } + + push(@index, " \n\n"); + + # At end of row? + if ($col_counter == $current_columns) { + push(@index, " \n"); + } + +} + + +############################################################################### +# +# Generate HTML for slide/frame pages +# +############################################################################### +sub slide_html { + + my $pathname = shift(@_); + my $filename = $info{$pathname}{'file'}; + my $link; + my $anchortext; + + # + # First the index frame info + # + if ($frame_orient eq 'horizontal') { + push(@frame," \n"); + } else { + push(@frame," \n \n"); + } + + $anchortext = " "; + push(@frame, $anchortext); + + if (defined($skipthumb{$pathname})) { + push(@frame,"\"\n"); + if ($frame_orient eq 'horizontal') { + push(@frame," "); + } else { + push(@frame," \n "); + } + push(@frame,"\n"); + + # + # Then the individual slides + # + my $slide = new FileHandle "> $destdir/$info{$pathname}{slide}"; + if (!defined($slide)) { + die("$destdir/$info{$pathname}{slide}: $!"); + } + + select($slide); + print "\n"; + print "\n"; + print "\n"; + $verstring = &versionstring(); + printf ("\n", $verstring); + printf ("\n"); + print "$current_titletext - $filename\n"; + print "\n"; + print "\n\n"; + + &next_prev_links($pathname); + + # Caption if any + if (($slide_caption eq 'top') and defined($info{$pathname}{'comment'})) { + print "
"; + my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide'); + print $tmp; + print "
\n"; + } + + # Date, filename + if ($slide_date eq 'top') { + print "
"; + print &format_date($info{$pathname}{'date'}, 'slide'); + print " $filename"; + print "
\n"; + } + + if ($slide_linkto eq 'index') { + $link = "../$indexfile#$filename"; + } elsif (($slide_linkto eq 'details') and ($do_detail == 1)) { + $link = "../$detailfile#$filename"; + } elsif (($slide_linkto eq 'medium') and !defined($skipmedium{$pathname})) { + $link = "../$info{$pathname}{medium}"; + } elsif (($slide_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { + $link = "../$info{$pathname}{thumb}"; + } else { + $link = "../$filename"; + } + + print "\n

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

\n"; + + # Caption if any + if (($slide_caption eq 'bottom') and defined($info{$pathname}{'comment'})) { + print "
"; + my $tmp = &htmlize_caption ($info{$pathname}{'comment'}, 'slide'); + print $tmp; + print "
\n"; + } + + # Date, filename + if ($slide_date eq 'bottom') { + print "
"; + print &format_date($info{$pathname}{'date'}, 'slide'); + print " $filename"; + print "
\n"; + } + + &next_prev_links($pathname); + print "\n\n"; + + select(STDOUT); + $slide->close(); + $slide_counter++; + + unless(defined($first_slide)) { + $first_slide = $info{$pathname}{'slide'}; + } +} + + +############################################################################### +# +# Generate HTML for details page +# +############################################################################### +sub details_html { + + my $pathname = shift(@_); + my $filename = $info{$pathname}{'file'}; + my ($link, $anchortext); + + # At beginning of row? + if ($col_counter == 1) { + push(@details, " \n"); + } + + + if ($details_linkto eq 'index') { + $link = "$indexfile#$filename"; + } elsif (($details_linkto eq 'medium') and !defined($skipmedium{$pathname})) { + $link = "$info{$pathname}{medium}"; + } elsif (($details_linkto eq 'thumbnail') and !defined($skipthumb{$pathname})) { + $link = "$info{$pathname}{thumb}"; + } elsif (($details_linkto eq 'slide') and ($do_slide == 1) and ($image_counter > 1)) { + $link = $info{$pathname}{'slide'}; + } else { + $link = $filename; + } + + push(@details," \n"); + push(@details," \n"); + push(@details," \n"); + push(@details," \n\n"); + push(@details," \n"); + push(@details," \n"); + push(@details,"
\n"); + push(@details,"
\n"); + push(@details," "); + push(@details, &format_date($info{$pathname}{'date'}, 'detail')); + push(@details,"
\n"); + + $anchortext = "
"); + push(@details,"$filename
"); + push(@details,"
\n"); + push(@details,"
\n"); + push(@details,"
"); + push(@details,"Original: $info{$pathname}{geometry}"); + push(@details," ($info{$pathname}{size})
"); + unless (defined($skipmedium{$pathname})) { + push(@details,"Medium: "); + push(@details,$info{$pathname}{'med_x'} . 'x' . $info{$pathname}{'med_y'} . ""); + push(@details," ($info{$pathname}{med_size})
"); + } + unless (defined($skipthumb{$pathname})) { + push(@details,"Thumbnail: "); + push(@details,$info{$pathname}{'thumb_x'} . 'x' . $info{$pathname}{'thumb_y'} . ""); + #push(@details," ($info{$pathname}{thumb_size})
"); + push(@details,"
"); + } + + # + # EXIF data + # + if (defined($info{$pathname}{'flash'})) { + push(@details,"Flash: $info{$pathname}{flash}
"); + } + if (defined($info{$pathname}{'exposure_time'})) { + push(@details,"Exposure time: $info{$pathname}{exposure_time}
"); + } + if (defined($info{$pathname}{'focus_dist'})) { + push(@details,"Focus distance: $info{$pathname}{focus_dist}
"); + } + if (defined($info{$pathname}{'focal_length'})) { + push(@details,"Focal length: $info{$pathname}{focal_length}
"); + } + if (defined($info{$pathname}{'aperture'})) { + push(@details,"Aperture: $info{$pathname}{aperture}
"); + } + + push(@details,"\n"); + push(@details,"
\n"); + push(@details,"
\n"); + push(@details," \n"); + + # At end of row? + if ($col_counter == $current_columns) { + push(@details, " \n"); + } + + +} + + +###################################################################### +# +# 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, "\n"); + push(@details, "\n"); + } + + # Entry for this directory in main & details file + push(@index, "\n"); + push(@details, "\n"); + + push(@details, "\n"); + + if (defined($montage)) { + push(@details, "
\n"); + } else { + push(@details, "
\n"); + } + + if (defined($first)) { + + my ($tmp_first, $tmp_last); + + push(@index, "
"); + push(@details, "
"); + + $tmp_first = &format_date ($first, 'index', 'dayonly'); + $tmp_last = &format_date ($last, 'index', 'dayonly'); + + if ($first ne $last) { + push(@index, "$tmp_first - $tmp_last"); + } else { + push(@index, "$tmp_first"); + } + + $tmp_first = &format_date ($first, 'detail', 'dayonly'); + $tmp_last = &format_date ($last, 'detail', 'dayonly'); + + if ($first ne $last) { + push(@details, "$tmp_first - $tmp_last"); + } else { + push(@details, "$tmp_first"); + } + + push(@index, "
\n"); + push(@details, "
\n"); + } + + + if (defined($montage)) { + + $anchortext = ""); + push(@index, "\n"); + + push(@index,"
"); + push(@index, "$dir"); + push(@index,"
\n"); + + $anchortext = ""); + push(@details, ""); + + push(@details, "
\n"); + + } else { + + push(@index,"
"); + push(@index, "$dir"); + push(@index,"
\n"); + + } + + push(@index, "
"); + push(@details, "
"); + + if ($dirtitle ne "") { + push(@index, "$dirtitle"); + push(@details, "$dirtitle"); + } + + push(@details, "
$dir"); + + push(@index, "
\n"); + push(@details, "
\n"); + + push(@details,"
\n"); + + push(@index, "\n"); + push(@details, "\n"); + + # At end of row? + if ($col_counter == $current_columns) { + push(@index, "\n"); + push(@details, "\n"); + } + + + # Increment for next item + $col_counter++; + $col_counter = 1 if ($col_counter > $current_columns); + + } # if dir had index file + +} + +###################################################################### +# +# Top of HTML index/detail files +# +###################################################################### +sub page_header { + + my $this = shift(@_); + my $linkto = shift(@_); + my $numlink = 0; + my $verstring; + + select(INDEX); + print "\n"; + print "\n"; + print "\n"; + $verstring = &versionstring(); + printf ("\n", $verstring); + printf ("\n"); + if (defined ($write_meta_tag{$titlemetatag})) { + print "\n"; + } + if (defined ($write_meta_tag{$columnsmetatag})) { + print "\n"; + } + if (defined ($write_meta_tag{$thumbxmetatag})) { + print "\n"; + } + if (defined ($write_meta_tag{$thumbymetatag})) { + print "\n"; + } + if (defined ($write_meta_tag{$reversemetatag})) { + print "\n"; + } + if (defined($firstdate)) { + print "\n"; + } + if (defined($lastdate)) { + print "\n"; + } + if (!defined ($opt_includeall) && defined (@opt_exclude) && scalar (@opt_exclude)) { + my $tmp = join (',', @opt_exclude); + my $etmp; + + # We need to "encode" this string in the HTML so that raw filenames + # (that people should not try to access) are not exposed to the + # outside world. + # + $etmp = &encodestring ($tmp); + printf ("\n", $etmp); + } + printf ("\n", $image_counter); + + if (defined (@opt_skipmont) && scalar (@opt_skipmont)) { + my $tmp = join (',', @opt_skipmont); + printf ("\n", $tmp); + } + print "$current_titletext\n"; + print "\n"; + print "\n"; + print "\n"; + + # Break out of frames + print "\n"; + + print "

$current_titletext

\n"; + + print "

"; + + # On all these links, check to see if the variable is also defined. If + # not (done in a .imageindexrc file perhaps) then skip the link + + if ((-e "$destdir/../$indexfile") and + ($do_dirs == 1) and defined($updirtext)) { + print "$updirtext"; + $numlink++; + } + + if (($do_detail == 1) and ($this eq 'index') and defined($detaillinktext)) { + print "  |  " if ($numlink != 0); + print "$detaillinktext"; + $numlink++; + } + + if (($this eq 'detail') and defined($indexlinktext)) { + print "  |  " if ($numlink != 0); + print "$indexlinktext"; + $numlink++; + } + + if (($do_slide == 1) and ($slide_counter > 1) and + defined($framelinktext)) { + print "  |  " if ($numlink != 0); + print "$framelinktext"; + $numlink++; + } + + #age test: + print "  |  " if ($numlink != 0); + print "$updirtext"; + #age ende + + print "\n
\n" if ($numlink != 0); + + print "

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

$tmp1 - $tmp2

\n"; + } else { + print "

$tmp2 - $tmp1

\n"; + } + } else { + print "

$tmp1

\n"; + } + } + + print "\n"; + + select(STDOUT); + +} + + +###################################################################### +# +# Bottom of HTML file +# +###################################################################### +sub page_footer { + + my $time = localtime(time); + + my $progurl = 'http://www.edwinh.org/imageindex/'; + + select(INDEX); + + print "
\n"; + + print "
"; + print "
\n"; + print "
\n"; + print "..erstellt mit imageindex.."; + print "
\n"; + print "\n\n"; + + select(STDOUT); +} + + +###################################################################### +# +# A "quickie" routine to show which files were excluded in a prior run +# +###################################################################### + +sub showexcluded { + my ($file) = @_; + my ($rfile, $tmp, $utmp, @files, $str); + + if (! defined ($file)) { + if (-r $indexfile) { + $rfile = $indexfile; + } + } + else { + $rfile = $file; + } + $tmp = &extract_meta_tag ($excludemetatag, $rfile); + if (defined($tmp)) { + # We need to "decode" this string as it has been encoded for storage + # in the HTML so that raw filenames (that people should not try to + # access) are not exposed to the outside world. + # + $utmp = &decodestring ($tmp); + (@files) = split (/,/, $utmp); + $str = join (',', @files); + printf ("File '$rfile' shows the following record of excluded files:\n"); + printf ("%s\n", $str); + } + else { + printf ("File '$rfile' shows no record of excluded files.\n"); + } + return; +} + +###################################################################### +# +# Ignore certain files via META data stored in the index.html file +# +# Exports global variable %skipmont used later during montage +# generation. +# +###################################################################### +sub exclude_files { + + my @files = @_; + my (@filelist, $f, %exclude, $token, @tokens); + + undef %exclude; + + # -skipmont flags override any META data found. Else, look for the META tag + # then process. Check to see if any of the -skipmont options were given as + # strings of filenames concatenated with ',' characters. If so, support it. + # + if (defined (@opt_skipmont)) { + foreach (@opt_skipmont) { + (@tokens) = split (/,/, $_); + foreach $token (@tokens) { + $skipmont{$token}++; + } + } + } + elsif (-r "$destdir/$indexfile") { + my $tmp = &extract_meta_tag ($skipmetatag, "$destdir/$indexfile"); + if (defined($tmp)) { + (@opt_skipmont) = split (/,/, $tmp); + my $str = join (',', @opt_skipmont); + printf ("Using saved skip-montage files: %s\n", $str); + foreach (@opt_skipmont) { + $skipmont{$_}++; + } + } + } + + # -exclude flags override any META data found. Else, look for the META tag + # then process. Check to see if any of the -exclude options were given as + # strings of filenames concatenated with ',' characters. If so, support it. + # + if (defined (@opt_exclude)) { + # -includeall takes priority over -exclude on the commandline if they are + # used together (wierd, but ...) + # + unless (defined ($opt_includeall)) { + foreach (@opt_exclude) { + (@tokens) = split (/,/, $_); + foreach $token (@tokens) { + $exclude{$token}++; + } + } + } + } + elsif (-r "$destdir/$indexfile") { + my $tmp = &extract_meta_tag ($excludemetatag, "$destdir/$indexfile"); + my $utmp; + if (defined($tmp) && !defined ($opt_includeall)) { + # We need to "decode" this string as it has been encoded for storage + # in the HTML so that raw filenames (that people should not try to + # access) are not exposed to the outside world. + # + $utmp = &decodestring ($tmp); + (@opt_exclude) = split (/,/, $utmp); + my $str = join (',', @opt_exclude); + printf ("Using saved excluded files: %s\n", $str); + foreach (@opt_exclude) { + $exclude{$_}++; + } + } + } + + foreach $f (@files) { + if (! $exclude{$f}) { + push (@filelist, $f); + } else { + print "Excluding '$f'\n"; + if (-d $f) { + chmod (0700, $f); + } + else { + chmod (0600, $f); + } + } + } + return (@filelist); +} + + +###################################################################### +# +# Nuke generated files if original image gone +# +###################################################################### +sub nuke_out_of_date { + foreach my $checkdir ($thumbnail_dir, $med_dir, $slide_dir) { + opendir(THUMBS,"$destdir/$checkdir") || die "Can't open dir $checkdir: ($!)\n"; + foreach (readdir(THUMBS)) { + next if (m/^\.?\.$/); + next if (m/$framefile/); + next if (m/$slidefile/); + next if (m/$montagefile/); + next if (m/$emoticonsmile/); + next if (m/$emoticonwink/); + next if (m/$emoticonfrown/); + if (!defined($thumb_backref{"$checkdir/$_"}) and + !defined($slide_backref{"$checkdir/$_"}) and + !defined($med_backref{"$checkdir/$_"})) { + print "Removing stale $destdir/$checkdir/$_\n"; + unlink("$destdir/$checkdir/$_") || warn "Can't unlink $destdir/$checkdir/$_: ($!)\n"; + $modified_thumb++; + + } + } + closedir(THUMBS); + } + +} + +###################################################################### +# +# Convert bytes to kb string +# +###################################################################### +sub convert_to_kb { + + my $bytes = shift(@_); + $bytes = sprintf("%dk", $bytes / 1024); + return($bytes); +} + +###################################################################### +# +# Sortq by integer date stamp +# +###################################################################### +sub bynumber { + if ($current_reverse == 0) { + $a <=> $b; + } else { + $b <=> $a; + } +} + + +###################################################################### +# +# Write frameset file for slideshows +# +###################################################################### +sub write_frameset { + + # This is impossible to get rid of + my $framefudge = 35; + my $verstring; + + open(FRAME,">$destdir/$slide_dir/$framefile") or die ("Can't open $destdir/$slide_dir/$framefile: $!\n"); + + select(FRAME); + + print "\n"; + print "\n"; + print "\n"; + $verstring = &versionstring(); + printf ("\n", $verstring); + printf ("\n"); + print ""; + print "$current_titletext\n"; + print "\n"; + print "\n"; + if ($frame_orient eq 'horizontal') { + printf("\n", $max_thumb_y + $framefudge); + } else { + printf("\n", $max_thumb_x + $framefudge); + } + print "\n"; + print "\n"; + print "No frames in this browser...go back\n"; + print "\n"; + print "\n"; + + select(STDOUT); + close (FRAME); + + + open(FRAME,">$destdir/$slide_dir/$slidefile") or die ("Can't open $destdir/$slide_dir/$slidefile: $!\n"); + select(FRAME); + + + print "\n"; + print "\n"; + print "\n"; + $verstring = &versionstring(); + printf ("\n", $verstring); + printf ("\n"); + print "\n"; + print "$current_titletext\n"; + print "\n\n"; + print "\n" if ($frame_orient eq 'horizontal'); + foreach (@frame) { + print; + } + print " \n" if ($frame_orient eq 'horizontal'); + print "
\n"; + print "\n\n"; + + select(STDOUT); + close(FRAME); + +} + + +###################################################################### +# +# Do next/index/prev links on slide pages +# +###################################################################### +sub next_prev_links { + + my $pathname = shift(@_); + + print "
"; + + if (defined($back{$pathname})) { + print "< previous | "; + } else { + print "< previous | "; + } + print "index"; + if (defined($forward{$pathname})) { + print " | next >"; + } else { + print " | next >"; + } + + print "
\n"; + +} + + +###################################################################### +# +# Lower-case all the filenames. I hate the uppercase filenames that come +# from my camera's default software (and Windud software). Plus I didn't +# want this "utility" in another script, so just place it here. +# +###################################################################### +sub lower_case_files { + my (@files) = @_; + my ($newfile, $lowername); + + foreach $name (@files) { + ($lowername = $name) =~ tr/A-Z/a-z/; + if ($name =~ /[A-Z]/) { + print "Moving '$name' to '$lowername'\n"; + move("$name","$lowername"); + } + } +} + + +###################################################################### +# +# extract the NAME tag from an HTML file +# +###################################################################### +sub extract_meta_tag { + my ($tag, $filename) = @_; + my ($name, $content, $retval); + + if (! (open (FILE, $filename))) { + print STDERR "Cannot open '$filename' for reading - $!\n"; + return (0); + } + # + # + while () { + if (//) { + $name = $1; + $content = $2; + if ($name eq $tag) { + $retval = $content; + last; + } + } + } + close (FILE); + return ($retval); +} + + +############################################################################### +# +# Rotate given image 90 degrees +# +############################################################################### +sub rotate_image { + + my $file = shift(@_); + my $argv = shift(@_); + + if ($file =~ m/^(cw|ccw)$/) { + # If file is cw or ccw, + # assume the args were given backwards + my $tmp = $file; + $file = $$argv[0]; + $$argv[0] = $tmp; + } + + -r "$file" || die("$file: ", $!); + -w "$file" || die("$file: ", $!); + + + # grab the mtime of the file so we can reset it after we update it + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, + $ctime,$blksize,$blocks) = stat($file); + my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); + + my ($name,$path,$suffix) = fileparse($file,'\.\S+'); + my $thumb; + my $medium; + + my $image = new Image::Magick; + + my $retval = $image->Read("$file"); + warn "$retval" if "$retval"; + + if (!defined($$argv[0]) or + ($$argv[0] !~ m/^cc?w$/i)) { + print "Need 'cw' or 'ccw' argument to rotate image clockwise/counterclockwise\n"; + exit(1); + } + + if ($$argv[0] =~ /^ccw$/i) { + $deg = -90; + } else { + $deg = 90; + } + + print "Rotating $file $deg degrees\n"; + $retval = $image->Rotate($deg); + warn "$retval" if "$retval"; + + $retval = $image->Write(filename=>"$file"); + warn "$retval" if "$retval"; + + system ("touch -t $posix_mtime $file"); + + # Nuke the generated images if they exist + # (touching the timestamp above breaks automatic regeneration logic) + if ($image->Get('format') =~ /JFIF/i) { + $thumb = $path . "$thumbnail_dir/$name" . $suffix; + $medium = $path . "$med_dir/$name" . $suffix; + } else { + $thumb = $path . "$thumbnail_dir/$name.jpg"; + $medium = $path . "$med_dir/$name.jpg"; + } + unlink($thumb) if (-e "$thumb"); + unlink($medium) if (-e "$medium"); + + + +} + +############################################################################### +# +# Set or display caption for a particular image +# +############################################################################### +sub caption_image { + + my $file = shift(@_); + my $argv = shift(@_); + my ($esc_comment, $tmpfile); + + -r "$file" || die("$file: ", $!); + + # grab the mtime of the file so we can reset it after we update it + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, + $ctime,$blksize,$blocks) = stat($file); + my $posix_mtime = POSIX::strftime ("%Y%m%d%H%M.%S",localtime($mtime)); + + my $image = new Image::Magick; + + my $retval = $image->Read("$file"); + warn "$retval" if "$retval"; + + my $format = $image->Get('format'); + warn "$retval" if "$retval"; + + # Set caption if another arg is present, or just display it + if (defined($$argv[0])) { + + -w "$file" || die("$file: ", $!); + + # Try to find wrjpgcom so we can use it for adding captions to JPG images + my $wrjpgcom_prog = &find_in_path ('wrjpgcom'); + my $quote_file = quotemeta ($file); + + # If a jpeg file and we found a wrjpgcom program in our path, use + # it! It simply puts the comment in the JPEG header without reading + # (uncompressing) and writing (re-compressing) the file out so + # there is no chance for data loss. + # + if (($format =~ /JFIF/i) and defined($wrjpgcom_prog)) { + + $tmpfile = "$file.$$"; + my $tmpfile_quote = "$quote_file.$$"; + $esc_comment = quotemeta ($$argv[0]); + # FIXME + # check to see how '?' and other punctuation is escaped and fix + # it seems things are not correct. + system ("$wrjpgcom_prog -replace -comment $esc_comment $quote_file > $tmpfile_quote"); + if (($? >> 8) != 0) { + printf(STDERR "Error in creating JPEG comment with 'wrjpgcom'. Leaving existing file intact.\n"); + } else { + move($tmpfile, $file); + } + + } else { + # Fall back to PerlMagick's routines. + # + $retval = $image->Comment("$$argv[0]"); + warn "$retval" if "$retval"; + + $retval = $image->Write(filename=>"$file", quality=>"95", + sampling_factor=>"1x1"); + warn "$retval" if "$retval"; + } + + system ("touch -t $posix_mtime $quote_file"); + + } else { + + my $text = $image->Get('comment'); + + if (defined($text)) { + print "$file: \"$text\"\n"; + } else { + print "$file: (no caption)\n"; + } + + } + +} + + +############################################################################### +# +# Print usage info from top of file +# +############################################################################### +sub usage { + + open(FILE,"$0") or die "Can't open $0: $OS_ERROR"; + while() { + last if (m/^\#\s+USAGE:/); + } + while() { + last if (m/^\#\#\#\#\#\#\#/); + s/^\# ?//; + print; + } + close(FILE); + +} + +###################################################################### +# +# Format timestamp for HTML pages. This routine assumes that the date +# given to it is in YYYYMMDDHHMM.SS format that we've created from the +# EXIF/mtime date using strftime(). +# +###################################################################### +sub format_date { + my ($date, $context, $dayonly) = @_; + my ($timeformat, $dateformat); + + if ($context eq 'frame') { + $timeformat = $frametimeformat; + $dateformat = $framedateformat; + } + elsif ($context eq 'index') { + $timeformat = $indextimeformat; + $dateformat = $indexdateformat; + } + elsif ($context eq 'slide') { + $timeformat = $slidetimeformat; + $dateformat = $slidedateformat; + } + else { + $timeformat = $detailtimeformat; + $dateformat = $detaildateformat; + } + + # Replace "macro" patterns in the format string first + # + $timeformat =~ s/\%R/\%H:\%M/g; + $timeformat =~ s/\%r/\%I:\%M:\%S \%p/g; + $dateformat =~ s/\%F/\%Y-\%m-\%d/g; + $dateformat =~ s/\%D/\%m\/\%d\/\%y/g; + + $date =~ /(\d\d\d\d)(\d\d)(\d\d)(\d\d)?(\d\d)?\.?(\d\d)?/; + my $year = $1; + my $month = $2; + my $day = $3; + my $hour = $4; + my $min = $5; + my $sec = $6; + my ($ampm, $two_digit_year, $twelve_hour); + + if ($year =~ /^\d\d(\d\d)$/) { + $two_digit_year = $1; + } + else { + $two_digit_year = '??'; # shouldn't ever been seen + } + + # If we're told to, only format a date with no time + # + if (defined ($dayonly)) { + $dateformat =~ s/\%Y/$year/g; + $dateformat =~ s/\%y/$two_digit_year/g; + $dateformat =~ s/\%m/$month/g; + $dateformat =~ s/\%d/$day/g; + $dateformat =~ s/\%\%/\%/g; + return ($dateformat); + } + else { + if (defined($hour)) { + $twelve_hour = $hour; + $ampm = 'AM'; + if ($hour > 12) { + $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; + + # Help smiley's render in a "mo-better" way when they are at the end of a + # caption and enclosed in parens + # + if ($caption =~ /(:\-?[\(\)])\s*\)\s*$/) { + my $tmp = $1; + $caption =~ s/:\-?[\(\)]\s*\)\s*$/$tmp\ \)/; + } + + $caption = &emoticonify ($caption, $slide); + + return ($caption); + +} + +############################################################################### +# +# Translate ASCII smiley's embedded into image captions into emoticons +# +############################################################################### +sub emoticonify { + my ($caption, $slide) = @_; + my ($thumbdir, $attr); + + return ($caption) if (! defined ($do_emoticons) || $do_emoticons == 0); + + # This is a hack, please ignore and move on ... nothing to see here. + # + $caption =~ s/\ /NoNBrEaKaBleSpacE/g; + + $thumbdir = $thumbnail_dir; + if ($slide) { + $thumbdir = '../' . $thumbdir; + } + $attr = 'STYLE="vertical-align: middle;" WIDTH="19" HEIGHT="19"'; + + if ($caption =~ s/:\-?\)/\\"/g) { + $emoticon{'smile'}++; + } + if ($caption =~ s/;\-?\)/\\"/g) { + $emoticon{'wink'}++; + } + if ($caption =~ s/:\-?\(/\\"/g) { + $emoticon{'frown'}++; + } + + # Undo the hack + # + $caption =~ s/NoNBrEaKaBleSpacE/\ /g; + return ($caption); + +} + +############################################################################### +# +# Write out PNG files representing the emoticons +# +############################################################################### +sub write_emoticon_png { + my ($type) = @_; + my ($img); + + if (! open (IMG, '>' . $destdir . '/' . $thumbnail_dir . "/$emoticonprefix" . $icon . ".png")) { + printf (STDERR "Could not open emoticon file for '$icon' for 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:R.N?=+?A#36P6)H9!(F)Z +CI1BYC1+=-K2?9J^^SQ<7J48K([9O4:P`````245.1*Y"8((` +` +EOF + +$png{'smile'} = <<'EOF'; +MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````$E!,5$7__P#, +MS`!F9@#_,P````#___]]YKD%````!G123E/______P"SOZ2_`````6)+1T0% +M^&_IQP```&U)1$%4>-I-C\$-P"`,`UV)`?I@@#ZZ03M`*L&?!]E_E=H$(5`> +MEP39#MR]E%+=&T@GD*NPD\A"PWBU@4,VADQ$*J,:OHF'<14(BX]14R#0%J1+ +M>!.I(&,I=(!Q3+IT2\\+N2D#A\JP)]ORKBM^`[0;1*VK3]P`````245.1*Y" +"8((` +` +EOF + +$png{'frown'} = <<'EOF'; +MB5!.1PT*&@H````-24A$4@```!,````3!`,```"`?BO_````'E!,5$7____, +MS`"9F0!F9@`S,P#,S#/___\S,S,```#__P`[/ZS;`````7123E,`0.;89@`` +M``%B2T=$"?'9I>P```"$241!5'C:78^Q#<,P#`19:@1I`WN!`%G`@`<(8"T0 +M>`.GE-R8*EV%OVU(24YA@L7A^>"31$3,G*@6!\!7=D$@\(8%!=K)Q&/#]U-4 +M=AC>\;&.0I0A:YQVG$FM)\$;-N<-TJEM;0 +@UQOZ@DOVMWO_7_P`Y9]*.M\PG><`````245.1*Y"8((` +` +EOF + +}