#! /bin/sh
# This script creates tumbnails, reduced versions of large images, and an
# index.html file pointing to all of it.
# Design goals are
# (1) exist in a single script that can be mailed around easily, etc.
# (2) generate clean, simple, portable HTML
# (3) no databases required
# (4) portable portable portable
# There are many other programs that compromise some or all of these goals.
# They are real neat, they make great web pages, they organize your life.
# They don't fit in a single ugly Bourne shell script whose only point in
# life is to take pictures off your digital camera and make them basically
# presentable to your friends.
# This script is in the public domain, share and enjoy.
# The latest version is always at http://www.molenda.com/makethumbs/
# Written by Jason Molenda, 1998-09-13; rewritten less lamely 2001-10-26.
## Notes to people reading this script.
##
## Start at main() and trace the function calls from there.
## Remember that there is no scoping of variables, so local variables aren't.
## You'll see some function-local variables with common names ("$fn" etc)
## prefixed with an abbreviation of the function name so that they don't
## overwrite a similarly named variable in the caller.
## As much as I like Bourne shell, I should have just done this in
## perl or python. On the down side, whichever of those I pick, I'll
## hear constant whining from about half of my friends. No one likes
## Bourne shell, so it's the safest language choice. And to be honest,
## I sometimes think it's too easy when you have all those fancy high
## level data types, scoping, abstractions, and regular expression at your
## finger tips -- it's a little more fun when you need to work hard to
## make things work right.
## Return values from functions are prefixed with "RETURN_".
## Command line options are prefixed with "ARGV_".
## Defaults are prefixed with "DEFAULT_"
## Settings from the .makethumbsrc are prefixed with "DOTRC_"
## Globals variables are prefixed with "GLOBAL_".
## GLOBAL_ will hold all the settings that we should act on. These came
## either from a DEFAULT_, ARGV_, or DOTRC_ if the user overrode the DEFAULT_.
main ()
{
init_defaults
read_dotrc_file
find_necessary
find_optional
make_dir_transversible "."
parse_args $*
get_image_list
create_descriptions_file
point_stdout_to_index_file
progress_update_start_creating_index_html
[ $GLOBAL_print_html_header_footer -eq 1 ] && print_html_header
print_html_table_start
iterate_over_image_list
print_html_table_end
[ $GLOBAL_print_html_header_footer -eq 1 ] && print_html_footer
progress_update_done_creating_index_html
[ $GLOBAL_create_slideshow -eq 1 ] && create_slideshow
exit 0
}
# DEFAULT_ values are treated as const and are not normally referenced
# by functions in this script. Functions should refer to the GLOBAL_
# version -- it will either be set to the DEFAULT_ or to the ARGV_ value
# if the user overrides it on the cmd line.
init_defaults ()
{
TMPDIR=${TMPDIR-/tmp}
DEFAULT_columns=3
DEFAULT_max_thumb_size=150
DEFAULT_use_two_windows=0
DEFAULT_reduce_big_pics=1
DEFAULT_reduce_trigger_height=768
DEFAULT_reduce_trigger_width=1024
DEFAULT_reduce_height=600
DEFAULT_reduce_width=800
DEFAULT_remove_originals=0
DEFAULT_create_slideshow=1
DEFAULT_rotation_file="rot-state.txt"
DEFAULT_show_progress=1
DEFAULT_body_tag="
"
DEFAULT_meta_tag=""
DEFAULT_html_charset="undef"
DEFAULT_index_filename="index.html"
DEFAULT_index_hrefname="index.html"
DEFAULT_print_captions=1
DEFAULT_compact_index_page=0
DEFAULT_descriptions_file="descriptions.txt"
DEFAULT_compression_level="75"
DEFAULT_slideshow_previous_pre_link="["
DEFAULT_slideshow_previous="previous"
DEFAULT_slideshow_previous_post_link="]
"
DEFAULT_slideshow_next_pre_link="["
DEFAULT_slideshow_next="next"
DEFAULT_slideshow_next_post_link="]
"
DEFAULT_slideshow_ret_to_index_pre_link="["
DEFAULT_slideshow_ret_to_index="index"
DEFAULT_slideshow_ret_to_index_post_link="]
"
GLOBAL_columns=$DEFAULT_columns
GLOBAL_max_thumb_size=$DEFAULT_max_thumb_size
GLOBAL_use_two_windows=$DEFAULT_use_two_windows
GLOBAL_reduce_big_pics=$DEFAULT_reduce_big_pics
GLOBAL_reduce_trigger_height=$DEFAULT_reduce_trigger_height
GLOBAL_reduce_trigger_width=$DEFAULT_reduce_trigger_width
GLOBAL_reduce_height=$DEFAULT_reduce_height
GLOBAL_create_slideshow=$DEFAULT_create_slideshow
GLOBAL_reduce_width=$DEFAULT_reduce_width
GLOBAL_rotation_file=$DEFAULT_rotation_file
GLOBAL_show_progress=$DEFAULT_show_progress
GLOBAL_remove_originals=$DEFAULT_remove_originals
GLOBAL_body_tag=$DEFAULT_body_tag
GLOBAL_meta_tag=$DEFAULT_meta_tag
GLOBAL_html_charset=$DEFAULT_html_charset
GLOBAL_index_filename=$DEFAULT_index_filename
GLOBAL_index_hrefname=$DEFAULT_index_hrefname
GLOBAL_print_captions=$DEFAULT_print_captions
GLOBAL_compact_index_page=$DEFAULT_compact_index_page
GLOBAL_descriptions_file=$DEFAULT_descriptions_file
GLOBAL_compression_level=$DEFAULT_compression_level
GLOBAL_slideshow_previous_pre_link=$DEFAULT_slideshow_previous_pre_link
GLOBAL_slideshow_previous=$DEFAULT_slideshow_previous
GLOBAL_slideshow_previous_post_link=$DEFAULT_slideshow_previous_post_link
GLOBAL_slideshow_next_pre_link=$DEFAULT_slideshow_next_pre_link
GLOBAL_slideshow_next=$DEFAULT_slideshow_next
GLOBAL_slideshow_next_post_link=$DEFAULT_slideshow_next_post_link
GLOBAL_slideshow_ret_to_index_pre_link=$DEFAULT_slideshow_ret_to_index_pre_link
GLOBAL_slideshow_ret_to_index=$DEFAULT_slideshow_ret_to_index
GLOBAL_slideshow_ret_to_index_post_link=$DEFAULT_slideshow_ret_to_index_post_link
GLOBAL_stdout_has_been_redirected=0
GLOBAL_descriptions_file_validated=0
GLOBAL_descriptions_file_is_invalid=0
GLOBAL_already_failed_guessing_date_from_dir=0
t='$Revision: 1.117 $'
GLOBAL_makethumbs_version=`echo "$t" | sed 's,[^0-9.],,g'`
# OBSOLETE: Remove these two after a few more revisions 2001-12-27
GLOBAL_max_thumb_height=0
GLOBAL_max_thumb_width=0
}
parse_args ()
{
while [ $# -gt 0 ]
do
optname="`echo $1 | sed 's,=.*,,'`"
optarg="`echo $1 | sed 's,^[^=]*=,,'`"
case "$1" in
--maxthumbsize=* | --max-thumb-size=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_max_thumb_size=$optarg
GLOBAL_max_thumb_size=$ARGV_max_thumb_size
;;
--columns=* | --cols=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_columns=$optarg
GLOBAL_columns=$optarg
;;
--use-two-windows*|--enable-two-windows|--with-two-windows)
ARGV_use_two_windows=1
GLOBAL_use_two_windows=$ARGV_use_two_windows
;;
--remove-orig*)
ARGV_remove_originals=1
GLOBAL_remove_originals=$ARGV_remove_originals
;;
--progress)
ARGV_show_progress=1
GLOBAL_show_progress=$ARGV_show_progress
;;
--quiet | -q)
ARGV_show_progress=0
GLOBAL_show_progress=$ARGV_show_progress
;;
--disable-reduce|--without-reduce)
ARGV_reduce_big_pics=0
GLOBAL_reduce_big_pics=$ARGV_reduce_big_pics
;;
--disable-slideshow|--without-slideshow)
ARGV_create_slideshow=0
GLOBAL_create_slideshow=$ARGV_create_slideshow
;;
--enable-reduce|--with-reduce)
ARGV_reduce_big_pics=1
GLOBAL_reduce_big_pics=$ARGV_reduce_big_pics
;;
--compact-index|--compact)
ARGV_compact_index_page=1
GLOBAL_compact_index_page=$ARGV_compact_index_page
;;
--reduce-height=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_reduce_height=$optarg
GLOBAL_reduce_height=$ARGV_reduce_height
;;
--reduce-width=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_reduce_width=$optarg
GLOBAL_reduce_width=$ARGV_reduce_width
;;
--reduce-trigger-height=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_reduce_trigger_height=$optarg
GLOBAL_reduce_trigger_height=$ARGV_reduce_trigger_height
;;
--reduce-trigger-width=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_reduce_trigger_width=$optarg
GLOBAL_reduce_trigger_width=$ARGV_reduce_trigger_width
;;
--compression-level=*|--compression=*)
optarg=`echo $optarg | sed 's,[^0-9],,g'`
exit_if_empty "$optname" "$optarg"
ARGV_compression_level=$optarg
GLOBAL_compression_level=$ARGV_compression_level
;;
-h | --help | -v | --version | -V)
help
exit 1
;;
-*)
echo "`basename $0`: ERROR: Unrecognized option \"$1\"" >&2
;;
*)
ARGV_image_list="$ARGV_image_list $1"
;;
esac
shift
done
if [ -n "$ARGV_compact_index_page" ]
then
enable_compact_index_mode
fi
}
help ()
{
# The following exec goop so I don't have to manually redirect every
# message to stderr in this function.
exec 4>&1 # save stdout fd to fd #4
exec 1>&2 # redirect stdout to stderr
cat <<__EOM__
Usage: `basename $0` [options]
--compact-index Create a compact index page to pack in lots of thumbnails
--maxthumbsize=n Maximum size of thumbnails, in pixels, default $DEFAULT_max_thumb_size
--columns=n Number of thumbnails per line, default $DEFAULT_columns
--disable-reduce Don't create a reduced image if the picture is large
--disable-slideshow Don't create slideshow files
--compression=n Set JPEG compression percentage to n for generated images.
Default is 75. Things usually look OK down to the 40-50's.
--use-two-windows Bring up a new window to see images, default is `[ $DEFAULT_use_two_windows -eq 1 ] && echo enabled || echo disabled`
__EOM__
if [ $DEFAULT_show_progress -eq 1 ]
then
echo '--quiet Avoid unnecessary output while running'
else
echo '--progress Show progress updates as the script runs'
fi
cat <<__EOM__
--remove-originals Remove original images if we make reduced versions.
Useful when disk space is limited; default is `[ $DEFAULT_remove_originals -eq 1 ] && echo enabled || echo disabled`
WARNING!!! This option *will* remove your original images
if reduced images are available!
Run this script in a directory of JPEG files to create thumbnail images and
an ${GLOBAL_index_filename}. If an ${GLOBAL_index_filename} is already present, its HTML is sent to stdout.
Makethumbs will not overwrite files you've created by hand. Makethumbs
will not modify your original images. Makethumbs will not corrupt your
precious bodily fluids.
You may list the JPEG files on the command line if you prefer. By default,
`basename $0` works on every JPEG file in the current working directory.
You can permanently override options by creating a ~/.makethumbsrc file.
Many more features can be tweaked via the .makethumbsrc file. See the
documentation on the makethumbs home page for more information.
This script written by Jason Molenda, makethumbs(AT)molenda.com.
This is version ${GLOBAL_makethumbs_version}.
The latest version of this script is always available at
http://www.molenda.com/makethumbs/
__EOM__
exec 1>&4 # Copy stdout fd back from temporary save fd, #4
}
# RETURN_found is 1 if found, 0 if not found. If found, $RETURN_fullname
# contains the path + filename.
find_in_path ()
{
target="$1"
RETURN_found=0
OFS="$IFS"
IFS=:
for fip_i in $PATH
do
if [ -z "$fip_i" ]
then
dir="."
else
dir="$fip_i"
fi
if [ -f "$dir/$target" ]
then
RETURN_fullname="$dir/$target"
RETURN_found=1
break
fi
done
IFS="$OFS"
}
# Compact Index Mode is a special case because the setting of this
# one mode involves setting several other variables, unless the
# user has explicitly overridden them elsewhere.
enable_compact_index_mode ()
{
[ $GLOBAL_compact_index_page -eq 0 ] && return
if [ -z "$ARGV_max_thumb_size" -a -z "$DOTRC_max_thumb_size" ]
then
GLOBAL_max_thumb_size=75
fi
if [ -z "$ARGV_print_captions" -a -z "$DOTRC_print_captions" ]
then
GLOBAL_print_captions=0
fi
if [ -z "$ARGV_columns" -a -z "$DOTRC_columns" ]
then
GLOBAL_columns=6
fi
}
# Parse a user's $HOME/.makethumbsrc. The code here is kind of
# funky because I didn't want to just bourne-shell source the file
# (". $HOME/.makethumbsrc"); any syntax error in that startup file
# would have caused makethumbs to fail in weird ways and users could have
# trouble debugging it. It's a huge amount of work to get all the parsing
# correct so spaces, quote marks, apostrophies, etc., are all carried over
# accurately. The sed expression where $value gets set is not something
# I'm too proud of - this is always the sort of thing that is tricky in
# Bourne shell.
read_dotrc_file ()
{
dotrc_file="$HOME/.makethumbsrc"
GLOBAL_found_dotrc_vars=0
[ ! -f "$dotrc_file" ] && return
make_tmpfile dotrc
tmpf=$RETURN_tmpfile
add_cleanup $tmpf
cat "$dotrc_file" | grep -v '^[ ]*#' | grep = > $tmpf
[ ! -s $tmpf ] && return
while read line
do
varname_is_valid=0
varname=`echo "$line" | sed -e 's,=.*,,' -e 's,[^A-Za-z_0-9],,g' \
-e 's,^ARGV_,,' -e 's,^GLOBAL_,,' \
-e 's,^DEFAULT_,,' -e 's,^DOTRC_,,'`
value=`echo "$line" | sed -e 's,^[^=]*=,,' -e 's,^[ ]*,,' \
-e 's,[ ]*$,,' \
-e s,^\[\"\'\]\[\ \ \]\*,, \
-e s,\[\ \ \]\*\[\"\'\]\$,, \
-e 's,",\\\\",g'`
# Some of what the above sed does: Remove spaces at beginning or end.
# Remove " or ' marks at end/begin, and any spaces after them.
# Backslash escape any " marks so the evals below work right.
eval [ -n \"\$GLOBAL_$varname\" ] && varname_is_valid=1
if [ $varname_is_valid -eq 0 ]
then
echo ERROR: "$dotrc_file" has unrecognized variable name, \"$varname\"!>&2
continue
fi
# Try to do a little verification if the current value is numeric. I could
# probably add some extra checks if the current val is 0 or 1, making an
# assumption that it's a boolean value. (someone might try to use "yes"
# instead of '1', for instance)
curval=`eval echo \\$GLOBAL_$varname`
if echo "$curval" | grep '^[0-9]*$' >/dev/null 2>&1
then
if [ -z "$value" ]
then
echo WARNING: Variable $varname currently has a numeric value of $curval >&2
echo WARNING: but you\'re setting it to an empty value. >&2
else
if echo "$value" | grep '^[0-9]*$' >/dev/null 2>&1
then
:
else
echo WARNING: Variable $varname currently has a numeric value of $curval >&2
echo WARNING: but you\'re setting it to \"$value\". >&2
fi
fi
fi
GLOBAL_found_dotrc_vars=1
eval DOTRC_$varname=\"$value\"
eval GLOBAL_$varname=\$DOTRC_$varname
done < $tmpf
# If a user sets some variables, others should be set as well.
if [ -n "$DOTRC_compact_index_page" ]
then
enable_compact_index_mode
fi
# Warn about obsolete things.
if [ -n "$DOTRC_max_thumb_height" ]
then
echo WARNING: max_thumb_height and max_thumb_width in .makethumbsrc are obsolete, use 1>&2
echo WARNING: max_thumb_size to specify a single number instead. 1>&2
fi
}
# Returns a temp file in $RETURN_tmpfile.
# Takes an optional description name argument.
make_tmpfile ()
{
find_in_path mktemp
if [ $RETURN_found -eq 1 ]
then
RETURN_tmpfile=`mktemp -q "$TMPDIR/makethumbs-$1.XXXXXXX"`
if [ $? -eq 0 ]
then
return 0
fi
fi
base_tmpfile="$TMPDIR/makethumbs-$1.$$"
RETURN_tmpfile="$base_tmpfile"
n=0
while [ -f "$RETURN_tmpfile" ]
do
n=`expr $n + 1`
RETURN_tmpfile="${base_tmpfile}-$n"
done
return 0
}
# Ends program execution if a necessary program is missing.
find_necessary ()
{
missed_something=0
for progname in djpeg cjpeg pnmscale
do
find_in_path $progname
if [ $RETURN_found -eq 0 ]
then
missed_something=1
echo ERROR: Missing \"$progname\"! >&2
if [ $progname = djpeg -o $progname = cjpeg ]
then
echo ERROR: You may be able to find a copy of this at ftp://ftp.uu.net/graphics/jpeg >&2
fi
if [ $progname = pnmscale ]
then
echo ERROR: You need to install the \"netpbm\" utilities. >&2
echo ERROR: You can find this at http://netpbm.sourceforge.net/ >&2
fi
fi
done
if [ $missed_something -eq 1 ]
then
echo "" >&2
echo ERROR: You will find all the necessary utilities pre-installed >&2
echo ERROR: on most Linux systems. >&2
exit 1
fi
}
# Detect some optional helper programs and remember their presence.
find_optional ()
{
for fn_prog in metacam jhead dphotox dump-exif
do
fn_varname=`echo $fn_prog | tr -d '-'`
find_in_path $fn_prog
if [ $RETURN_found -eq 1 ]
then
eval GLOBAL_${fn_varname}_is_present=1
else
eval GLOBAL_${fn_varname}_is_present=0
fi
done
}
update_cleanups ()
{
trap "rm -f $GLOBAL_cleanuplist; exit 1" 0 1 2 15
}
add_cleanup ()
{
GLOBAL_cleanuplist="$GLOBAL_cleanuplist $*"
update_cleanups
}
remove_cleanup ()
{
for i in $*
do
GLOBAL_cleanuplist=`echo $GLOBAL_cleanuplist | sed "s|${i}||"`
done
update_cleanups
}
# Only accepts *one* argument, which may contain space chars.
make_file_readable ()
{
if [ -f "$*" ]
then
chmod a+r "$*"
chmod a-x "$*"
fi
}
# Only accepts *one* argument, which may contain space chars.
make_dir_transversible ()
{
if [ -d "$*" ]
then
chmod a+x "$*"
fi
}
exit_if_empty ()
{
desc="$1"
shift
val="$*"
if [ -z "$val" ]
then
echo ERROR: No argument given with \"$desc\" command line argument! >&2
exit 1
fi
}
# Sets file at $GLOBAL_image_list_tmpfile with images to process.
# Tries to normalize filename extensions.
get_image_list ()
{
make_tmpfile first-image-list
prelim_image_tmpfile="$RETURN_tmpfile"
make_tmpfile image-list
GLOBAL_image_list_tmpfile="$RETURN_tmpfile"
add_cleanup "$prelim_image_tmpfile" "$GLOBAL_image_list_tmpfile"
if [ -n "$ARGV_image_list" ]
then
for i in $ARGV_image_list
do
echo $ARGV_image_list >> $prelim_image_tmpfile
done
else
ls -1 | egrep -i '\.jpg$|\.jpeg$|\.png$|\.gif$' > $prelim_image_tmpfile
fi
cat $prelim_image_tmpfile | while read filename
do
[ -s "$filename" ] || continue
if echo "$filename" | egrep -- '-t\.|-r\.' >/dev/null 2>&1
then
continue
fi
normalized_filename=`echo "$filename" |
sed -e 's,.JPG$,.jpg,' -e 's,.jpeg$,.jpg,' -e 's,.JPEG$,.jpg,' \
-e 's,.PNG$,.png,' -e 's,.GIF$,.gif,'`
if [ ! -f "$normalized_filename" -a -n "$normalized_filename" ]
then
if mv "$filename" "$normalized_filename"
then
filename="$normalized_filename"
fi
fi
#FIXME: This will probably fail on a MacOS HFS+ type system where testing
# for "foo.JPG" and "foo.jpg" may both return true if one of the names
# is present... Maybe check inode #s or something to be sure.
if [ "$filename" != "$normalized_filename" -a \
-f "$filename" -a -f "$normalized_filename" ]
then
echo "WARNING: Both $filename and $normalized_filename exist, and this script does" 1>&2
echo " odd things when this happens!" 1>&2
fi
make_file_readable "$filename"
echo "$filename" >> $GLOBAL_image_list_tmpfile
done
if [ ! -s "$GLOBAL_image_list_tmpfile" ]
then
echo ERROR: No images found! >&2
exit 1
fi
}
point_stdout_to_index_file ()
{
if [ -n "$ARGV_image_list" ]
then
GLOBAL_print_html_header_footer=0
else
GLOBAL_print_html_header_footer=1
if [ -f "$GLOBAL_index_filename" ]
then
remove_file_if_unmodified "$GLOBAL_index_filename"
fi
if [ ! -f "$GLOBAL_index_filename" ]
then
exec > "$GLOBAL_index_filename"
add_cleanup "$GLOBAL_index_filename"
GLOBAL_stdout_has_been_redirected=1
else
GLOBAL_stdout_has_been_redirected=0
fi
make_file_readable "$GLOBAL_index_filename"
fi
}
iterate_over_image_list ()
{
col=0
first_row=1
GLOBAL_last_row_was_closed=0
progress_update_start_creating_reduced_images
while read fn
do
if [ $col -eq 0 ]
then
print_html_row_start
GLOBAL_last_row_was_closed=0
fi
print_html_column_start
print_image_entry "$fn"
print_html_column_end
col=`expr $col + 1`
if [ $col -eq $GLOBAL_columns ]
then
print_html_row_end
col=0
GLOBAL_last_row_was_closed=1
fi
done < $GLOBAL_image_list_tmpfile
progress_update_done_creating_reduced_images
}
print_image_entry ()
{
pie_fn="$*"
create_thumbnail "$pie_fn"
thumb_width=$RETURN_thumb_width
thumb_height=$RETURN_thumb_height
create_reduced "$pie_fn"
was_reduced=$RETURN_was_reduced
print_image_link $thumb_width $thumb_height $was_reduced "$pie_fn"
print_image_size "$pie_fn"
}
# Only reduce JPG images. Reducing GIF/PNG images is a little tricky -
# see the RCS comment on rev 1.60 for more details on why this is tricky.
maybe_remove_original ()
{
mro_name="$1"
RETURN_original_removed=0
RETURN_reduced_still_around=1
[ "$GLOBAL_remove_originals" -eq 0 ] && return
if echo "$mro_name" | grep -v '\.jpg$' > /dev/null 2>&1
then
return
fi
source_name_to_reduced_name "$mro_name"
mro_reduced_name="$RETURN_reduced_name"
if [ -f "$mro_name" -a -f "$mro_reduced_name" -a -s "$mro_reduced_name" ]
then
mv "$mro_name" "${mro_name}.bak"
mv "$mro_reduced_name" "$mro_name"
if [ ! -f "$mro_reduced_name" -a -f "$mro_name" -a -s "$mro_name" ]
then
rm -f "${mro_name}.bak"
RETURN_original_removed=1
RETURN_reduced_still_around=0
else
echo ERROR: I got confused while trying to remove original file >&2
echo ERROR: \"$mro_name\"! Aborting... >&2
exit 1
fi
fi
}
# A little complicated - parameters are
# THUMBNAIL-WIDTH THUMBNAIL-HEIGHT REDUCED-IMG-MADE? FILENAME
# $thumb_width $thumb_height $was_reduced "$fn"
print_image_link ()
{
twidth=$1; shift
theight=$1; shift
reduced=$1; shift
pil_fn="$*"
get_image_caption "$pil_fn"
pil_caption="$RETURN_caption"
source_name_to_thumb_name "$pil_fn"
pil_thumb_name="$RETURN_thumb_name"
if [ $GLOBAL_create_slideshow -eq 1 ]
then
image_name_to_html_name "$pil_fn"
pil_slideshow_name="$RETURN_html_name"
source_name_to_reduced_html_name "$pil_fn"
pil_reduced_slideshow_name="$RETURN_reduced_html_name"
fi
pil_image_name="$pil_fn"
source_name_to_reduced_name "$pil_image_name"
pil_reduced_name="$RETURN_reduced_name"
# The logic ahead is complicated. We change our behavior depending
# on whether (a) this image has a reduced version ($reduced), whether
# (b) we are making slideshows ($GLOBAL_create_slideshow).
# One odd case is if we are making a slideshow, and we are making reduced
# versions of images, but _this_ particular image didn't have a reduced
# version. In this one case, the _main_ link we emit is to the reduced
# HTML slideshow page. That way when people do Next/Previous, they'll
# proceed to the Reduced version (the one they most likely want).
if [ $reduced -eq 1 ]
then
if [ $GLOBAL_create_slideshow -eq 1 ]
then
main_link_href="$pil_reduced_slideshow_name"
secondary_link_href="$pil_slideshow_name"
else
main_link_href="$pil_reduced_name"
secondary_link_href="$pil_image_name"
fi
else
if [ $GLOBAL_create_slideshow -eq 1 ]
then
if [ $GLOBAL_reduce_big_pics -eq 1 ]
then
main_link_href="$pil_reduced_slideshow_name"
else
main_link_href="$pil_slideshow_name"
fi
else
main_link_href="$pil_image_name"
fi
fi
echo -n '
"
if [ $GLOBAL_print_captions -eq 1 ]
then
echo -n "
$pil_caption"
if [ $reduced -eq 1 ]
then
echo -n "
[Large version]"
fi
else
echo ""
fi
}
print_image_size ()
{
bytes=`ls -l "$fn" | awk '{print $5}'`
kbytes=`expr $bytes / 1000`
if [ $GLOBAL_print_captions -eq 1 ]
then
echo " (${kbytes}k)"
fi
}
foo_to_pnm ()
{
case "$*" in
*.jpg)
RETURN_foo_to_pnm="djpeg -ppm";;
*.gif)
RETURN_foo_to_pnm="giftopnm";;
*.tif)
RETURN_foo_to_pnm="tifftopnm";;
*.png)
RETURN_foo_to_pnm="pngtopnm";;
*)
echo ERROR: Unable to proceed with file "$*"! >&2
exit 1;;
esac
}
source_name_to_thumb_name ()
{
RETURN_thumb_name=`echo "$*" | sed 's,\.[^.]*$,-t.jpg,'`
if [ "$*" = "$RETURN_thumb_name" ]
then
echo ERROR: I couldn\'t create a thumb name for "\"$*\""! >&2
exit 1
fi
if echo "$RETURN_thumb_name" | egrep -- '-t-t\.' >/dev/null 2>&1
then
echo ERROR: I couldn\'t create a reduced name for "\"$*\""! >&2
exit 1
fi
}
source_name_to_reduced_name ()
{
RETURN_reduced_name=`echo "$*" | sed 's,\.[^.]*$,-r.jpg,'`
if [ "$*" = "$RETURN_reduced_name" ]
then
echo ERROR: I couldn\'t create a reduced name for "\"$*\""! >&2
exit 1
fi
if echo "$RETURN_reduced_name" | egrep -- '-r-r\.' >/dev/null 2>&1
then
echo ERROR: I couldn\'t create a reduced name for "\"$*\""! >&2
exit 1
fi
}
source_name_to_source_html_name ()
{
RETURN_source_html_name=`echo "$*" | sed 's,\.[^.]*$,.html,'`
if [ "$*" = "$RETURN_source_html_name" ]
then
echo ERROR: I couldn\'t create an HTML name for "\"$*\""! >&2
exit 1
fi
}
image_name_to_html_name ()
{
RETURN_html_name=`echo "$*" | sed 's,\.[^.]*$,.html,'`
if [ "$*" = "$RETURN_html_name" ]
then
echo ERROR: I couldn\'t create an HTML name for "\"$*\""! >&2
exit 1
fi
}
html_name_to_image_name ()
{
hntin_fn="$*"
hntin_basename=`echo "$hntin_fn" | sed -e 's,-[rt].html$,,' -e 's,.html$,,'`
hntin_imgname=`grep "^${hntin_basename}.[jpgpnggif]*\$" $GLOBAL_image_list_tmpfile | head -1`
RETURN_image_name="$hntin_imgname"
if [ ! -f "$RETURN_image_name" ]
then
echo ERROR: I couldn\'t guess the image name for HTML file "\"$*\""! >&2
exit 1
fi
}
# We may have a reduced image, or we may not. If the reduced image
# is present, we return the source image HTML name (so
# source_name_to_source_html_name is equivalent to this function),
# otherwise we return the reduced image HTML name.
source_name_to_reduced_html_name ()
{
source_name_to_reduced_name "$*"
sntrhn_reduced_name="$RETURN_reduced_name"
RETURN_reduced_html_name=`echo "$sntrhn_reduced_name" | sed 's,\.[jpgpnggif]*$,.html,'`
if [ "$sntrhn_reduced_name" = "$RETURN_reduced_html_name" ]
then
echo ERROR: I couldn\'t create an HTML name for "\"$sntrhn_reduced_name\""! >&2
exit 1
fi
}
reduced_name_to_source_name ()
{
is_reduced_name "$*"
if [ $RETURN_is_reduced_name -eq 1 ]
then
rntsn_is_reduced_file=1
rntsn_nameroot=`echo "$*" | sed 's,-r\.jpg$,,'`
else
rntsn_is_reduced_file=0
rntsn_nameroot=`echo "$*" | sed 's,\.[jpgpnggif]*$,,'`
fi
rntsn_source=`grep "^${rntsn_nameroot}\.[a-z][a-z][a-z]\$" $GLOBAL_image_list_tmpfile`
if [ -z "$rntsn_source" -o ! -f "$rntsn_source" ]
then
echo ERROR: I couldn\'t find the source name from \"$*\"! >&2
exit 1
fi
if [ "$*" = "$rntsn_source" -a $rntsn_is_reduced_file -eq 1 ]
then
echo ERROR: I couldn\'t find convert source name with \"$*\"! >&2
exit 1
fi
RETURN_source_name="$rntsn_source"
}
create_thumbnail ()
{
source_fn="$*"
source_name_to_thumb_name "$source_fn"
thumb_fn="$RETURN_thumb_name"
if [ -f "$thumb_fn" -a -s "$thumb_fn" ]
then
# The existing thumbnail may be of the wrong size
get_dimensions "$thumb_fn"
if [ $RETURN_width -ne $GLOBAL_max_thumb_size -a \
$RETURN_height -ne $GLOBAL_max_thumb_size ]
then
rm -f "$thumb_fn"
else
RETURN_thumb_width=$RETURN_width
RETURN_thumb_height=$RETURN_height
make_file_readable "$thumb_fn"
return
fi
fi
foo_to_pnm "$source_fn"
conv="$RETURN_foo_to_pnm"
add_cleanup "$thumb_fn"
check_for_rotation "$source_fn"
$conv "$source_fn" |
pnmscale -xysize $GLOBAL_max_thumb_size \
$GLOBAL_max_thumb_size 2>/dev/null |
$RETURN_rotation_cmd $RETURN_rotation_angle |
cjpeg -optimize -quality $GLOBAL_compression_level > "$thumb_fn"
make_file_readable "$thumb_fn"
remove_cleanup "$thumb_fn"
progress_update_created_thumbnail_image
if [ ! -s "$thumb_fn" ]
then
echo "ERROR: Unable to create thumbnail for \"$source_fn\"! Exiting." >&2
exit 1
fi
get_dimensions "$thumb_fn"
RETURN_thumb_width=$RETURN_width
RETURN_thumb_height=$RETURN_height
}
create_reduced ()
{
cr_source_fn="$*"
source_name_to_reduced_name "$cr_source_fn"
cr_reduced_fn="$RETURN_reduced_name"
RETURN_was_reduced=0
if [ $GLOBAL_reduce_big_pics -eq 0 ]
then
return
fi
if [ -f "$cr_reduced_fn" -a -s "$cr_reduced_fn" ]
then
make_file_readable "$cr_reduced_fn"
maybe_remove_original "$cr_source_fn"
[ $RETURN_reduced_still_around -eq 1 ] && RETURN_was_reduced=1
return
fi
get_dimensions "$cr_source_fn"
if [ $RETURN_width -ge $GLOBAL_reduce_trigger_width -o \
$RETURN_height -ge $GLOBAL_reduce_trigger_height ]
then
foo_to_pnm "$cr_source_fn"
start_conv="$RETURN_foo_to_pnm"
add_cleanup "$cr_reduced_fn"
check_for_rotation "$cr_source_fn"
$start_conv "$cr_source_fn" |
pnmscale -xysize $GLOBAL_reduce_width \
$GLOBAL_reduce_height 2>/dev/null |
$RETURN_rotation_cmd $RETURN_rotation_angle |
cjpeg -optimize -quality $GLOBAL_compression_level > "$cr_reduced_fn"
if [ $GLOBAL_jhead_is_present -eq 1 ]
then
jhead -te "$cr_source_fn" "$cr_reduced_fn" >/dev/null 2>&1
if [ $GLOBAL_compression_level -lt $DEFAULT_compression_level ]
then
jhead -dt "$cr_reduced_fn" >/dev/null 2>&1
fi
fi
make_file_readable "$cr_reduced_fn"
remove_cleanup "$cr_reduced_fn"
progress_update_created_reduced_image
maybe_remove_original "$cr_source_fn"
[ $RETURN_reduced_still_around -eq 1 ] && RETURN_was_reduced=1
fi
if [ $RETURN_was_reduced -eq 1 -a ! -s "$cr_reduced_fn" ]
then
echo "ERROR: Unable to create reduced img of \"$source_fn\"! Exiting." >&2
exit 1
fi
}
get_dimensions ()
{
gd_fn="$*"
foo_to_pnm "$gd_fn"
conv="$RETURN_foo_to_pnm"
dimens=`$conv "$gd_fn" | pnmfile | awk '{print $4 " " $6}'`
RETURN_width=`echo $dimens | cut -d ' ' -f1`
RETURN_height=`echo $dimens | cut -d ' ' -f2`
if [ -z "$RETURN_width" -o -z "$RETURN_height" ]
then
echo ERROR: Internal error in get_dimensions, for some reason I couldn\'t get >&2
echo ERROR: the dimensions of \"$gd_fn\". Exiting. >&2
exit 1
fi
}
check_for_rotation ()
{
cfr_fn="$*"
RETURN_do_rotation=0
RETURN_rotation_cmd=cat
RETURN_rotation_angle=""
[ ! -f $GLOBAL_rotation_file ] && return
if grep -i "^[a-z-]* ${fn}" $GLOBAL_rotation_file >/dev/null 2>&1
then
:
else
return
fi
rot_cmd=`grep -i "^[a-z-]* ${fn}" $GLOBAL_rotation_file | sed "s/ ${fn}//"`
if [ -z "$rot_cmd" ]
then
echo WARNING: Unrecognized rotation for "$cfr_fn"! Ignoring rotate. >&2
return
fi
if [ "$rot_cmd" != "clockwise" -a "$rot_cmd" != "counter-clockwise" ]
then
echo WARNING: Unrecognized rotation for "$cfr_fn"! Ignoring rotate. >&2
return
fi
RETURN_do_rotation=1
RETURN_rotation_cmd=pnmrotate
if [ "$rot_cmd" = "clockwise" ]
then
RETURN_rotation_angle=-90
else
RETURN_rotation_angle=90
fi
}
print_html_header ()
{
get_directory_title
echo ''
echo ''
echo ''
echo " $RETURN_directory_title"
[ -n "$GLOBAL_meta_tag" ] && echo "$GLOBAL_meta_tag"
if [ "$GLOBAL_html_charset" != "undef" ]
then
echo " "
fi
echo " "
echo ''
echo "$GLOBAL_body_tag"
echo ""
echo "$RETURN_directory_title
"
echo ""
get_directory_description
if [ -n "$RETURN_directory_description" ]
then
echo "$RETURN_directory_description"
echo "
"
echo ""
fi
}
print_html_table_start ()
{
if [ $GLOBAL_compact_index_page -eq 1 ]
then
echo '
'
else
echo ''
fi
}
print_html_table_end ()
{
if [ $GLOBAL_last_row_was_closed -eq 0 ]
then
echo ""
fi
echo ""
echo '
'
}
print_html_row_start ()
{
echo ""
echo ''
}
print_html_row_end ()
{
echo '
'
echo ""
if [ $GLOBAL_compact_index_page -eq 0 ]
then
#
echo "| |
"
fi
}
print_html_column_start ()
{
echo ' '
}
print_html_column_end ()
{
echo ' | '
}
print_html_footer ()
{
echo ""
echo "This page created on `date '+%Y-%m-%d'`."
cat << __EOM__
__EOM__
echo ''
echo ''
add_checksum_to_file "$GLOBAL_index_filename"
if [ $GLOBAL_stdout_has_been_redirected -eq 1 ]
then
remove_cleanup "$GLOBAL_index_filename"
fi
}
get_image_number ()
{
reduced_name_to_source_name "$*"
gin_fn="$RETURN_source_name"
gin_num=`cat -n $GLOBAL_image_list_tmpfile |
grep "^[ ]*[0-9]*[ ]*${gin_fn}\$" |
cut -f1 | sed 's,[^0-9],,g'`
if [ -z "$gin_num" ]
then
echo WARNING: Could not get image number for file \"$gin_fn\"! 1>&2
fi
RETURN_image_number="$gin_num"
gin_total=`cat $GLOBAL_image_list_tmpfile | wc -l | sed 's,[^0-9],,g'`
if [ -z "$gin_total" ]
then
echo WARNING: Could not determine number of image files! 1>&2
fi
RETURN_number_of_images="$gin_total"
}
#########################################################
#### slideshow functions
#########################################################
create_slideshow ()
{
[ $GLOBAL_create_slideshow -eq 0 ] && return
progress_update_start_creating_slideshow_pages
remove_existing_slideshow_html_files
create_slideshow_image_list
while read fileset
do
prev=`echo "$fileset" | cut -d\| -f1`
cur=`echo "$fileset" | cut -d\| -f2`
next=`echo "$fileset" | cut -d\| -f3`
create_slideshow_source_file "$prev" "$cur" "$next"
if [ $GLOBAL_reduce_big_pics -eq 1 -a $GLOBAL_remove_originals -ne 1 ]
then
create_slideshow_reduced_file "$prev" "$cur" "$next"
fi
progress_update_created_slideshow_page
done < $GLOBAL_slideshow_image_list
progress_update_done_creating_slideshow_pages
}
create_slideshow_source_file ()
{
cssf_prev_image="$1"
cssf_cur_image="$2"
cssf_next_image="$3"
image_name_to_html_name "$cssf_cur_image"
cssf_html_file="$RETURN_html_name"
add_cleanup "$cssf_html_file"
print_slideshow_file_header "$cssf_cur_image" "$cssf_html_file"
print_slideshow_top_navlinks "$cssf_prev_image" "$cssf_cur_image" \
"$cssf_next_image" "source" "$cssf_html_file"
print_slideshow_image "$cssf_cur_image" "$cssf_html_file" "source"
print_slideshow_bottom_navlinks "$cssf_prev_image" "$cssf_cur_image" \
"$cssf_next_image" "source" "$cssf_html_file"
print_image_exif_info "$cssf_cur_image" "$cssf_html_file"
print_slideshow_file_footer "$cssf_html_file"
remove_cleanup "$cssf_html_file"
}
create_slideshow_reduced_file ()
{
cssf_prev_image="$1"
cssf_cur_image="$2"
cssf_next_image="$3"
cssf_cur_source_image="$cssf_cur_image"
cssf_cur_reduced_image="$cssf_cur_image"
if [ "$cssf_prev_image" != NULL ]
then
source_name_to_reduced_name "$cssf_prev_image"
cssf_prev_image="$RETURN_reduced_name"
fi
source_name_to_reduced_name "$cssf_cur_image"
cssf_cur_reduced_image="$RETURN_reduced_name"
if [ -f "$RETURN_reduced_name" ]
then
cssf_cur_image="$RETURN_reduced_name"
fi
if [ "$cssf_next_image" != NULL ]
then
source_name_to_reduced_name "$cssf_next_image"
cssf_next_image="$RETURN_reduced_name"
fi
image_name_to_html_name "$cssf_cur_reduced_image"
cssf_html_file="$RETURN_html_name"
add_cleanup "$cssf_html_file"
print_slideshow_file_header "$cssf_cur_image" "$cssf_html_file"
print_slideshow_top_navlinks "$cssf_prev_image" "$cssf_cur_image" \
"$cssf_next_image" "reduced" "$cssf_html_file"
print_slideshow_image "$cssf_cur_image" "$cssf_html_file" "reduced"
print_slideshow_bottom_navlinks "$cssf_prev_image" "$cssf_cur_image" \
"$cssf_next_image" "reduced" "$cssf_html_file"
print_image_exif_info "$cssf_cur_image" "$cssf_html_file"
print_slideshow_file_footer "$cssf_html_file"
remove_cleanup "$cssf_html_file"
}
print_slideshow_file_header ()
{
psfh_image_file="$1"
psfh_html_file="$2"
get_image_caption "$psfh_image_file"
psfh_caption="$RETURN_caption"
# The following exec goop so I don't have to manually redirect every
# message to stderr in this function.
exec 4>&1 # save stdout fd to fd #4
exec > "$psfh_html_file" # redirect stdout to stderr
echo ''
echo ''
echo '
'
echo " $psfh_caption"
[ -n "$GLOBAL_meta_tag" ] && echo "$GLOBAL_meta_tag"
if [ "$GLOBAL_html_charset" != "undef" ]
then
echo " "
fi
echo " "
echo ' '
echo ''
echo "$GLOBAL_body_tag"
echo ""
get_directory_title
if [ -n "$RETURN_directory_title" ]
then
if [ $RETURN_directory_title_is_from_user -eq 1 ]
then
echo "$RETURN_directory_title / $psfh_caption
"
fi
fi
exec 1>&4 # Copy stdout fd back from temporary save fd, #4
make_file_readable "$psfh_html_file"
}
print_slideshow_file_footer ()
{
psff_html_file="$1"
html_name_to_image_name "$psff_html_file"
get_image_description "$RETURN_image_name"
if [ -n "$RETURN_image_description" ]
then
echo "$RETURN_image_description
" >> "$psff_html_file"
fi
echo "" >> "$psff_html_file"
echo "