eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q'
  if 0;

###############################################################################
#
# createPhotoWebPages
# -------------------
#
# Creates HTML pages with thumbnails from a given image selection file list.
# Thumbnails are linked to a Image page with web size image.
#
# The tool creates up to 3 levels of HTML pages to navigate and display
# photo images in a web friendly form.
#
# JPEG, GIF, and PNG type images are supported.
#   Expected extensions are:
#     JPEG: "\.[jJ][pP][eE]?[gG]"
#     GIF : "\.[gG][iI][fF]"
#     PNG : "\.[pP][nN][gG]"
#
# The selected HTML Views are:
#
#  * top view (optional) which links to thumbnail views
#  * thumbnail views which link to the image pages and allow
#    to move to the next or previous thumbnail view
#  * image views which display the image and allow to move
#    to the next or previous image view.
#
# There is always only one top view file, but there may be different
# thumbnail view files. This depends on the number of images to be
# displayed and the groups that are defined (using SUBTITLE)
#
# Thumbnail images are created one directory above the <image> directory
# below thumbnails/<image> and have the same name as the original image file
# whith _thumbnail appended before the extension; in addition, images are
# generated which may serve as buttons (suffix _button before extension).
# Those are placed in a buttons/<image> directory.
# Websize images are created in a websize/<image> directory. (suffix _websize)
# Filename extensions are kept as is.
# A selection path name is expected to be relative, i.e. not starting with "/"
# in "Web ready" mode. (parameter WEBREADY=1)
#
# If an image or html file already exists then the script skips this particular
# file (i.e. does not recreate) or even exits the script. This is done to
# reduce the time to update files. To re-create a file simple delete it.
#
# The Selection file may contain additional configuration parameters which
# all have the format KEYWORD=<String>. Possible options are described below.
# For details see also the example Selection file.
#
# Credits:
#  lots of ideas and source code snippets (especially the image manipulation
#  and creation subroutines) have been taken from the perl script
#  'HTMLThumbnail v1.3', written by Benjamin Franz, with modifications from
#  Audin Malmin and Colin Marquardt (colin@marquardt-home.de).
#  (see also http://www.nihongo.org/snowhare/utilities)
#
# Prerequisites:
#
# This utility depends on the Independent JPEG Group's software
# (available at <URL:ftp://ftp.uu.net/graphics/jpeg>)
# (in particular it depends on the 'cjpeg' and 'djpeg' routines) and the
# NetPBM package (available at
# <URL:ftp://wuarchive.wustl.edu/graphics/graphics/packages/NetPBM>
# or <URL:ftp://ftp.x.org/R5contrib/>)
#
# If those are already installed on your system, you are good to
# go. If not, you will have to download and install them before you
# can use this utility.  In Debian Woody, these tools are in the
# libjpeg-progs (or libjpeg-mmx-progs) and netpbm packages.
#
# The tools are also available in the SuSE distribution
# (at least SuSE 7.3 ;-)
# Red Hat 8.1 has also been reported to work;
#
# MS Windows based platforms are not directly supported but it is at
# least feasible to adapt the script to also work on those platforms.
#
# Rotation of JPEG images may be done in a faster and cleaner way using
# jpegtran and jhead. This approach preserves the camera image information
# and ensures that the rotation is without quality losses.
# Set parameter USE_JPEGTRAN_JHEAD=1 if you have those tools installed.
# (the option "-order jpeg" also requires existance of the jhead tool.)
#
###############################################################################
#
# To do: (planned until version 3.0)
#
# Future work:
#
#  * provide more detailed formatting options for HTML
#    (e.g. text alignment, table = image width or window width,
#     text to be placed below or above thumbnails, or below, above or to
#     the right of an image)
#  * Thumbnail pages for Palm size displays - theme ?
#    (full image map ?)
#  * more advanced consistency checking to spot errors more easily
#    (e.g. 2 x IMAGE_CAPT per image not reasonable, etc.)
#  * check if SUBTITLE or HTML_SUB_TEXT follow IMAGE_NAME tag, or each other
#  * extract author name from /etc/password if no default is available
#  * make use of installation directory: store general program options
#    and default configuration files
#  * move deleted original images into a separate sub-directory "deleted"
#  * allow to keep size rather than to increase to web size in per image mode!
#  * check filename length restrictions (ISO9660, 255 max path,
#    30 max filename), probably include feature into rename section;
#    a real "cut file names to size I need" mode would be nice;
#  * use hashes for parameters and images to simplify loops and enhance
#    readability and maintainability (may also simplify imternationalization)
#  * more detailed HTML formatting options allowed on per image basis
#    (e.g. background color, text formatting, etc.)
#  * text overlays for buttons, improved image generation
#  * switching to ImageMagick or gimp  might speed things up ?
#  * provide "button" like appearance for image buttons as an alternative
#  * HTML themes ? different HTML backends ?
#  * create test suite to simplify changes
#  * restructure script using perl modules and packages / probably
#    trying more object oriented approach for image data handling
#    and processing;
#  * gphoto interface, probably possible to replace/enhance jhead
#  * find a solution to use the same physical image multiple times within
#    a selection file
#
# Known Bugs:
#
#  * In some corner cases it looks like the file extension of some images
#     gets truncated (e.g. jpg -> jp) during -scan
#  * if 1st image in subtitle is deleted and is by default the top selection
#    then subtitle group is not represented on top thumbnail page;
#    (bug should be fixed starting with v2.3.12)
#  * HTML_SUB_TEXT entries disappeared when deleting images with IMAGE_DEL
#    (bug should be fixed starting with version v2.3.10)
#
#  ("none" here does not mean "no" bugs, it means no "known" bugs ;-)
#
###############################################################################
#
# DISCLAIMER
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE.
#
# Use of this software in any way or in any form, source or binary,
# is not allowed in any country which prohibits disclaimers of any
# implied warranties of merchantability or fitness for a particular
# purpose or any disclaimers of a similar nature.
#
# IN NO EVENT SHALL I BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
# SPECIAL, INCIDENTAL,  OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
# USE OF THIS SOFTWARE AND ITS DOCUMENTATION (INCLUDING, BUT NOT
# LIMITED TO, LOST PROFITS) EVEN IF I HAVE BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE
#
# Copyright (C) 2002, 2003 Stefan Spaeth
#
# This software may be freely copied, changed or redistributed under
# the same terms and conditions as Perl itself.
#
###############################################################################

#####################################
###  Begin of perl main program.  ###
#####################################

use strict;

# we need the file find capability for the scan option:
use File::Find;
# copying a file is also required:
use File::Copy;

###############################################################################
# Constants:
###############################################################################

# Script Information:

# Script author:
my $ScriptAuthor  = "Stefan Spaeth";
# Script name:
my $ScriptName    = "createPhotoWebPages.pl";
# Version number:
my $ScriptVersion = "v2.4.03";

###############################################################################
# String constants: change these for language adaptations:
#                   these are the only language dependent text elements
#                   in the created HTML code
#                   (note: the [..] characters are not mandatory for pointers)
###############################################################################
# Examples are given with German texts:
#
# set the following Variable to 1 to get english texts:

my $UseEnglishText = 0;

###############################################################################
# Previous Page pointer
my $stringPreviousPage   = "[Vorherige Seite]";
# Next Page pointer
my $stringNextPage       = "[N&auml;chste Seite]";
# Selection
my $stringSelection      = "Auswahl";
# Part
my $stringPart           = "Teil";
# Original Image pointer
my $stringZoom           = "[Vergr&ouml;&szlig;ert]";
# Original Image pointer
my $stringOrigSize       = "[Originalbild]";
# Back to Thumbnail View pointer
my $stringBackToThumbNails = "[Zur&uuml;ck zur &Uuml;bersicht]";
# Back to page where link came from:
my $stringBack           = "[Zur&uuml;ck]";
# Last Change
my $stringLastChange     = "Letzte &Auml;nderung";
# Created with
my $stringCreatedWith    = "Erzeugt mit";
# Subtitle for Unnamed Section (if SUBTITLE is missing)
my $stringUnnamedSection = "Photo-Auswahl";
# Ask user to select one of the photo sub-selections in the top view:
my $stringChooseSubtitle = "W&auml;hlen Sie aus den vorhandenen Photo-Serien eine aus";

# Strings for the info html page:
my $stringNotAvailable   = "nicht verf&uuml;gbar";
my $stringNonStandard    = "nicht standardisiert";
my $stringYes            = "Ja";
my $stringNo             = "Nein";
my $stringFile           = "Datei";
my $stringWidth          = "Breite (in Pixeln)";
my $stringHeight         = "H&ouml;he (in Pixeln)";
my $stringGeometry       = "Seitenverh&auml;ltnis";
my $stringImageDate      = "Datum der Aufnahme";
my $stringImageTime      = "Uhrzeit der Aufnahme";
my $stringCameraModel    = "Kamera-Modell";
my $stringCameraVendor   = "Kamera-Hersteller";
my $stringFlashUsed      = "Blitz benutzt";
my $stringISOEquivalent  = "ISO Empfindlichkeit (&auml;quiv.)";
my $stringFocalLength    = "Brennweite";
my $stringExposureTime   = "Belichtungszeit";
my $stringAperture       = "Blende";
my $stringExposureBias   = "Belichtungaufschlag";
my $stringMeteringMode   = "Belichtungsmessung";
my $stringExposureMode   = "Belichtungsart";
my $stringFileName       = "Dateiname";
my $stringFileSize       = "Dateigr&ouml;&szlig;e";
my $stringFullPath       = "Vollst&auml;ndiger Pfadname";
my $stringOtherInfo      = "Sonstige Bildinformation";

if ($UseEnglishText == 1) {
  $stringPreviousPage   = "[Previous Page]";
  $stringNextPage       = "[Next Page]";
  $stringSelection      = "Selection";
  $stringPart           = "Part";
  $stringZoom           = "[Enlarged]";
  $stringOrigSize       = "[Original Image]";
  $stringBackToThumbNails = "[Back To Thumbnails]";
  $stringBack           = "[Back]";
  $stringLastChange     = "Last Change";
  $stringCreatedWith    = "Created with";
  $stringUnnamedSection = "Photo-Selection";
  $stringChooseSubtitle = "Choose a photo series to view in more detail";
  # Strings for the info html page:
  $stringNotAvailable   = "not available";
  $stringNonStandard    = "non-standard";
  $stringYes            = "Yes";
  $stringNo             = "No";
  $stringFile           = "File";
  $stringWidth          = "Width (in pixels)";
  $stringHeight         = "Height (in pixels)";
  $stringGeometry       = "Geometry";
  $stringImageDate      = "Image date";
  $stringImageTime      = "Image time";
  $stringCameraModel    = "Camera model";
  $stringCameraVendor   = "Camera manufacturer";
  $stringFlashUsed      = "Flash used";
  $stringISOEquivalent  = "ISO Equivalent";
  $stringFocalLength    = "Focal length";
  $stringExposureTime   = "Exposure time";
  $stringAperture       = "Aperture";
  $stringExposureBias   = "Exposure bias";
  $stringMeteringMode   = "Metering mode";
  $stringExposureMode   = "Exposure mode";
  $stringFileName       = "File name";
  $stringFileSize       = "File size";
  $stringFullPath       = "Full pathname";
  $stringOtherInfo      = "Other info";
}
###############################################################################
# Miscellaneous globally used variables and constants:
###############################################################################

my $globalTempDir          = "/tmp"; # temporary directory to be used
my $installDir             = $ENV{"HOME"} . "/.createPhotoWebPages"; # !!! to be overwritten by the Makefile
# this variable is set to '1' if names of deleted images should be logged to a file
my $globalLogDeletedImages = 1;
# the logfile name for the logging of deleted image files
my $globalDeletedImagesLogfileName = "deleted-images.log";
# default CSS stylesheet is expected in $installDir:
my $constantStylesheetName = "stylesheet";
# this variable is set to '1' if any IMAGE_DEL entry has been found:
my $globalImagesAreToBeDeleted = 0;
# this variable contains the complete "align=center " tag to be used
# for HTML tables (left or right aligned tables don't get an
# alignment attribute in the <table> tag:
my $globalTableAlignAttr = "";

# these constant values are used to encode automatic Update information:
my $constantUpdateNone          = 0; # no updates are required
my $constantUpdateHtml          = 1; # HTML pages require updates
my $constantUpdateImages        = 2; # created images require updates
my $constantUpdateHtmlAndImages = 3; # HTML pages and images require updates

# the minimum width that we expect from a table column in which a
# "normal" or "small" text link is to be placed:
my $constantSmallTextColumnWidth  = 150;
my $constantNormalTextColumnWidth = 200;

# indicates if error messages should be written to logfile:
my $globalLogFileIsActive = 0;

# the extension to be used for newly generated JPEG images:
my $constantJPEGExtension = ".jpg";
# the extension to be used for newly generated CSS stylesheets:
my $constantCSSExtension  = ".css";

###############################################################################
# Configuration file parameters:
#   these parameters may also be controlled via Configuration/Selection file
###############################################################################
# TITLE=<string> the overall title (also HTML title)
my $globalTitle = "Photo Thumbnails Web Page";
# SUBTITLE=<string> a subsection title
my $globalDefaultSectionTitle = "$stringUnnamedSection";
# SUB_PAGES_NO_TEXT=0|1 1= display just images and subtitle, 0=normal
my $globalSubPagesNoText = 0;
# AUTHOR=<string> the author information
my $globalAuthor = "$ScriptAuthor";
# COPYRIGHT=<string> the copyright information
my $globalCopyright = "";
# EMAIL=<string> the author email
my $globalEmail = "ssp_babo\@z.zgs.de";
# LEVEL_UP=<file-path-name> the file that is available one level higher
my $globalLevelUp = "";         # by default not available
# WEB_READY=0|1 1=create images in target directory, 0= create images near original file
my $globalWebReady = 0;
# TARGET_DIR=<directory-path> target directory, note that directory must exist!
my $globalTargetDir = "./";

# parameters for HTML generation:
# HTML_BG_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalBgColor             = "#ffffdd";      # Background color light yellow
# HTML_TEXT_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalTextColor           = "#000000";      # Text color black
# HTML_LINK_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalLinkColor           = "#3333ff";      # Link color blue
# HTML_VLINK_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalVLinkColor          = "#993399";      # Visited Link color violet
# HTML_ALINK_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalALinkColor          = "#000099";      # Active Link color dark blue
# parameters for image generation:
# IMG_BORDER_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalBorderColor         = "#000000";      # Border color black
# IMG_SHADOW_COLOR=#hhhhhh expects a hex number in the format "#hhhhhh"
my $globalDropShadowColor     = "#333333";      # Shadow color grey
# IMG_BORDER_WIDTH=0..5 width of image border, in number of pixels
my $globalImageBorderWidth    = 1; # Border width in pixels
# IMG_SHADOW_ENABLE=0|1 1= enables image shadow, 0= no image shadow
my $globalMakeDropShadow      = 1; # 0 = no drop shadow
# IMG_SHADOW_OFFSET=0..5 offset in number of pixels for thumbnail
my $globalDropShadowOffset    = 3; # /2 for buttons, *2 for websize
# IMG_SHADOW_BLUR=0..10 offset in number of pixels where shadow is blured
my $globalDropShadowBlur      = 5; # /2 for buttons, *2 for websize
# THUMBNAIL_ROTATION_ANGLE=0..10.0 rotation angle for thumbnails, 0=do not rotate
# (values below 0.1 degrees are ignored)
my $globalThumbnailRotationAngle = 0; # for Thumbnails only
# THUMBNAIL_RANDOM_ROTATE=0|1
my $globalThumbnailRandomRotate  = 0; # for Thumbnails only, 0 or 1
# WEBSIZE_ROTATION_ANGLE=0..5.0 rotation angle for websize images,
#  0=do not rotate (values below 0.1 degrees are ignored)
my $globalWebsizeRotationAngle   = 0; # for Websize images only
# WEBSIZE_RANDOM_ROTATE=0|1
my $globalWebsizeRandomRotate    = 0; # for Websize images only, 0 or 1
# IMG_JPEG_QUALITY=0..100 JPEG compression factor, e.g. 50, 75, 90
my $globalImageQualityFactor  = 75; # 75 % compression
# IMG_JPEG_PROGRESSIVE=0|1 determines how websize image is compressed
my $globalProgressiveJpeg     = 1; # progressive compression
# IMG_IMPRINT_DEFAULT=<text>
my $globalImageImprintDefault = "";
# IMG_ADD_JPG_INFO_PAGE=0|1 indicates if a jhead info html page should be added:
my $globalAddJpgInfoPage      = 0;

# number of thumbnail images per page:
# THUMBNAILS_PER_ROW=3..8
my $globalImagesPerRow        = 4; # create 3 up to 8 images per row:
# THUMBNAILS_MAX_ROWS=1..24
my $globalMaxRows             = 3; # create 1 up to 24 rows per page:

# max width/height of images:
# THUMBNAIL_SIZE=32..256
my $globalThumbnailSize      = 128; # 128 pixels wide or tall
# BUTTON_SIZE=32..256
my $globalButtonSize         = 64; # 96 pixels wide or tall
# WEBSIZE_SIZE=128..1024
my $globalWebSize            = 640; # 640 pixels wide or tall
# FULLSIZE_SIZE=512..4096
my $globalFullSize           = 800; # 8000 pixels wide or tall
# ALWAYS_WEBSIZE=0|1
my $globalAlwaysWebSize      = 0; # by default we keep smaller images' size
# USE_JPEGTRAN_JHEAD=0|1  a 1 activates a call to jhead/jpegtran to
# rotate images in order to keep EXIF info:
my $globalUseJpegtranJhead   = 1;
# LINK_ORIGINAL_IMAGE=0|1
my $globalLinkOrigImage      = 1; # value 0 or 1 (1=link to original image exists)
# FULLSIZE_ENABLE=0|1
my $globalFullSizeEnable     = 0; # by default we use no full size images
                                  # especially not in web-ready mode
# !!! * these should be shorter for burning on CD
# !!! * add a length check for every file
# filenames, directory names:
# THUMBNAIL_SUFFIX=<word> file name suffix for thumbnails (before extension)
my $globalThumbnailSuffix = "_thumbnail";
# BUTTON_SUFFIX=<word> file name suffix for buttons (before extension)
my $globalButtonSuffix    = "_button";
# WEBSIZE_SUFFIX=<word> file name suffix for websize images (before extension)
my $globalWebSizeSuffix   = "_websize";
# FULLSIZE_SUFFIX=<word> file name suffix for full size images (before extension)
my $globalFullSizeSuffix  = "_fullsize";
# THUMBNAIL_DIR=<directory-name> directory name, not path for thumbnail images
my $globalThumbnailDir    = "thumbnails";
# BUTTON_DIR=<directory-name> directory name, not path for button images
my $globalButtonDir       = "buttons";
# WEBSIZE_DIR=<directory-name> directory name, not path for websize images
my $globalWebSizeDir      = "websize";
# FULLSIZE_DIR=<directory-name> directory name, not path for full size images
my $globalFullSizeDir     = "fullsize";
# HTML_INDEX_NAME=<word> prefix part for html index files (_0, _1, .. is added)
my $globalHtmlPrefix      = "index";
# HTML_CREATE_TOP=0|1 create an enclosing toplevel file that references all
# subpages:
my $globalCreateTop = 0;
# HTML_TOP_CAPTION=<text>
my $globalTopCaption = $stringChooseSubtitle;
# HTML_TOP_BACK=<text>
my $globalTopBack = $stringBackToThumbNails;
# HTML_IMAGE_SUFFIX=<word> suffix to be appended to image html file name
my $globalHtmlImgSuffix   = "_img";
# HTML_INFO_SUFFIX=<word> suffix to be appended to image info html file name
my $globalHtmlInfoSuffix  = "_info";
# HTML_WRAPPER_SUFFIX=<word> suffix to be appended to image wrapper html file name
my $globalHtmlWrapperSuffix  = "_wrap";
# HTML_FILE_EXTENSION=html|htm file extension to be used for html files
my $globalHtmlExtension   = "html";
# HTML_IMAGE_DIR=<directory-name> directory in which html files for images are placed
my $globalHtmlDir         = "html";
# HTML_TEXT_ALIGN=left|center|right defines how headings and text are aligned
my $globalTextAlign = "center";
# HTML_TOP_TEXT_ALIGN=left|center|right defines how top thumbnail page text is aligned
my $globalTopTextAlign = "";
# HTML_SUB_TEXT_ALIGN=left|center|right defines how subtitle page text is aligned
my $globalSubTextAlign = "";
# HTML_IMG_TEXT_ALIGN=left|center|right defines how image page text is aligned
my $globalImgTextAlign = "";
# HTML_IMG_MAKE_DENSE=0|1, when active, compresses the image html page a bit
my $globalImgHtmlDense = 0;
# HTML_IMG_CREATE_ALWAYS=0|1, forces creation of HTML image files independently
my $globalImgHtmlCreateAlways = 0;
# HTML_CSS_FILENAME=Filename (extension ".css" is added later):
my $globalStylesheetName = $constantStylesheetName;
# HTML_CSS_ENABLE=0|1
my $globalCssEnable = 0;
my $globalFontMinus1_ON  = "<font size=-1>";
my $globalFontMinus1_OFF = "</font>";
# HTML_TEXT_BACK=<text>
my $globalTextBack = $stringBackToThumbNails;
# HTML_NO_PART_NUMBER=0|1 remove any reference to part and part number
my $globalNoPartNumber=0;
# HTML_NO_PART_ONE=0|1 remove reference for part and part number 1
my $globalNoPartOne=1;
# HTML_NO_TOOL_HINT=0|1 1 = remove hint "Created with createPhotoWebPages..."
my $globalNoToolHint=0;

# a few HTML table formatting parameters:
# HTML_TEXTCELL_HEIGHT=8..128 determines the height of a table cell with text content
my $globalTextHeight=20;
# HTML_THUMBNAIL_SPACING=2..16 the cellspacing parameter for the thumbnail table
my $globalThumbnailSpacing=4;
# HTML_IMAGE_SPACING=2..16 the cellspacing parameter for the image table
my $globalImageSpacing=2;
# HTML_THUMBNAIL_DISTANCE=4..128 the distance between thumbnails
my $globalThumbnailDistance=16;
# HTML_IMAGE_DISTANCE=4..128 the distance between thumbnails
my $globalImageDistance=12;

# Image text extraction from image filename:
# IMG_NAME_NO_ALPHA_PREFIX=1|0
my $globalRemoveAlphaPrefix     = 1; # e.g. [a-z][a-z]_... is removed
# IMG_NAME_NO_NUMBER_SUFFIX=1|0
my $globalSeparateNumericSuffix = 1; # e.g. ...[0-9]+$ is replaced by "... [0-9]+$"
# IMG_NAME_REPLACE_UNDERSCORE=1|0
my $globalReplaceUnderScores    = 1; # replace underscore by blank
# IMG_NAME_REPLACE_DASH=1|0
my $globalReplaceMinusSign      = 0; # replace minus sign by blank
# IMG_NAME_BLANK_BEFORE_CAPS=1|0
my $globalInsertBlankBeforeCaps = 1; # insert blank between [a-z0-9] and [A-Z]
# IMG_NAME_NO_NUMBER_PREFIX=1|0
my $globalRemoveNumberPrefix    = 0; # e.g. [0-9][0-9]_... is removed

# Iview support: IVIEW_SELECTION_FILE=<string>
my $globalIviewFileName = "Slideshow.txt";


###############################################################################
# Process ARGUMENT list
###############################################################################

# print help message if not at least 1 parameter available:
if ($#ARGV < 0) {
  print_help_message();
}

my $optionSelectionFile = "";
my $optionWrite         = 0;
my $optionScan          = 0;
my $optionImage         = 0;
my $optionHtml          = 0;
my $optionForce         = 0;
my $optionRename        = 0;
my $optionOrder         = "";
my $optionPrefix        = 0;
my $optionExt           = 0;
my $optionVerbose       = 0;
my @optionPathNames     = ();

print_info_message("Processing script parameters and options ...");

# Extract options and STDIN sign (--) if available
while ($_ = $ARGV[0], /^-/) {
  shift;
  $optionScan    = 1, next               if ((/^-s$/)||(/^-scan$/));
  $optionRename  = 1, next               if ((/^-r$/)||(/^-rename$/));
  $optionImage   = 1, next               if ((/^-i$/)||(/^-image[s]?$/));
  $optionHtml    = 1, next               if ((/^-t$/)||(/^-html$/));
  $optionOrder   = $ARGV[0], shift, next if ((/^-o$/)||(/^-order$/));
  $optionPrefix  = 1, next               if ((/^-p$/)||(/^-prefix$/));
  $optionWrite   = 1, next               if ((/^-w$/)||(/^-write$/));
  $optionExt     = 1, next               if ((/^-e$/)||(/^-ext(ension)?$/));
  $optionForce   = 1, next               if ((/^-f$/)||(/^-force$/));
  $optionVerbose = 1, next               if ((/^-v$/)||(/^-verbose$/));
  print_help_message(), next             if ((/^-h$/)||(/^-help$/));
  die "$0 : illegal or unknown option.\n";
}

# post-processing for switches:

# by default if neither -html or -image is used we set both (unless
# we do a file scan):
if (($optionHtml  == 0) && ($optionImage == 0) && ($optionScan == 0)) {
  $optionHtml  = 1;
  $optionImage = 1;
}

# we discard the order option unless wer are in scan mode:
if (($optionOrder ne "") && ($optionScan == 0)) {
  $optionOrder = "";
  print_error_message("the -o/-order option is only allowed when combined with -s/-scan.");
} else {
  if ($optionOrder ne "") {
    if (($optionOrder ne "name") && ($optionOrder ne "date")) {
      exit_perl_script("Illegal order parameter: $optionOrder " .
                       "(\"name\" or \"date\" allowed).");
    }
  }
}

# we discard the prefix option unless we are in both scan and rename mode:
if (($optionPrefix == 1) && (($optionScan == 0) && ($optionRename == 0))) {
  $optionPrefix = 0;
  print_error_message("the -p/-prefix option is only allowed when combined with " .
                      "-s/-scan AND/OR -r/-rename.");
}
# we discard the ext option unless we are in rename mode:
if (($optionExt == 1) && ($optionRename == 0)) {
  $optionExt = 0;
  print_error_message("the -e/-ext option is only allowed when combined with " .
                      "-r/-rename.");
}

# filename is current parameter:
if ($optionSelectionFile eq "") {
  if ($#ARGV < 0) {
    exit_perl_script("expecting selection file name parameter.\n");
  }
  $optionSelectionFile = $_;
  shift;
}

# more parameters only for needed -scan option:
if (($optionScan == 0) && ($#ARGV >= 0)) {
  exit_perl_script("unexpected parameter " . $ARGV[0] . "\n");
} else {
  if (($optionScan == 1) && ($#ARGV < 0)) {
    exit_perl_script("Scan requires at least one search path to be specified.\n");
  } else {
    @optionPathNames     = @ARGV;
  }
}

# process default update value:
my $globalUpdateValue      = $constantUpdateNone;
# indicates if we need to check for updates (even though force option
# is not activated):
my $globalUpdateIsRequired = 0;
if ($optionForce == 1) {
  if (($optionHtml == 1) && ($optionImage == 0)) {
    $globalUpdateValue      = $constantUpdateHtml;
    $globalUpdateIsRequired = 1;
  } elsif (($optionHtml == 0) && ($optionImage == 1)) {
    $globalUpdateValue      = $constantUpdateImages;
    $globalUpdateIsRequired = 1;
  } elsif (($optionHtml == 1) && ($optionImage == 1)) {
    $globalUpdateValue      = $constantUpdateHtmlAndImages;
    $globalUpdateIsRequired = 1;
  }
}

if ($globalUpdateIsRequired == 1) {
  print_info_message("Global Update will be done.");
} else {
  print_info_message("Global Update will not be done.");
}
if ($optionHtml == 1) {
  print_info_message("HTML pages will be generated.");
} else {
  print_info_message("HTML pages will not be generated.");
}
if ($optionImage == 1) {
  print_info_message("Images will be generated.");
} else {
  print_info_message("Images will not be generated.");
}

if (($optionForce == 1) && (($optionHtml == 1) || ($optionImage == 1))) {
  print_info_message("HTML page and Image generation will be forced.");
} else {
  print_info_message("HTML page and Image generation will not be forced.");
}

###############################################################################
# global arrays which store all image related information
###############################################################################

# the image information arrays:
# the complete image path including filename and extension:
my @list_of_images =();
# the filename and relative path of the html file related to the image
my @list_of_html_names =();
# the filename and relative path of the html file related to the image info
my @list_of_info_names =();
# the filename and relative path of the wrapper html file
my @list_of_wrapper_names =();
# the filename and relative path of the button image related to the image
my @list_of_button_names =();
# the filename and relative path of the thumbnail image related to the image
my @list_of_thumbnail_names =();
# the filename and relative path of the websize image related to the image
my @list_of_websize_names =();
# the filename and relative path of the full size image related to the image
my @list_of_fullsize_names =();
# an indicator of update information: see $constantUpdate* constant variables
my @list_of_update_info = ();
# the image path portion:
my @list_of_image_fpaths =();
# contains a potential available "[a-z][a-z]_" prefix of the filename:
my @list_of_image_fprefixes =();
# the image filename portion:
my @list_of_image_fnames =();
# the image filename portion that is unique (for images with the same name):
my @list_of_image_uniquifiers =();
# the image filename extension portion:
my @list_of_image_fexts =();
# the image HTML link (if existing):
my @list_of_image_links =();
# the image file type ("jpg", "gif", "png"):
my @list_of_image_ftypes =();
# the "readable" image name (brief name):
my @list_of_image_names =();
# indication if EXIF information exists in JPEG image
my @list_of_image_exif =();
# the images date and time(i.e. when it was taken with the camera):
my @list_of_image_dates =();
my @list_of_image_times =();
# the size of the original image (in bytes):
my @list_of_image_fsizes =();
# the width in pixels (of the original image:
my @list_of_image_widths =();
# the height in pixels (of the original image:
my @list_of_image_heights =();
# the image caption (descriptive subtitle):
my @list_of_image_captions =();
# the image alternative texts (for the html ALT tag):
my @list_of_alt_texts =();
# other EXIF info fields:
my @list_of_exif_flashused =();
my @list_of_exif_isoequiv =();
my @list_of_exif_cameravendor =();
my @list_of_exif_cameramodel =();
my @list_of_exif_focallength =();
my @list_of_exif_exposuretime =();
my @list_of_exif_aperture =();
my @list_of_exif_exposurebias =();
my @list_of_exif_meteringmode =();
my @list_of_exif_exposuremode =();
# the ratio of the image: 3:2, 4:3 or non-standard
my @list_of_geometry_ratio =();
# the image imprinted caption, e.g. copyright infos
# (to be added to the image by ppmcaption):
my @list_of_image_imprints =();
# list which indicates if a info page should be created:
my @list_of_info_page_markers =();
# info about whether and how to rotate the original
# image before using it (values 0,90,180,270):
my @list_of_rot_infos =();
# this array marks those images which should be deleted in the current run:
#  (0 = no action, 1 = delete all html pages and generated images referring
#   to that image)
my @list_of_del_markers =();
# info about whether image represents the "top" image of
# the sub-selection:
my @list_of_top_select_markers =();
# descriptive multi-line text to be placed below image
# in the image html pages:
my @list_of_has_text_infos =();
my @list_of_text_infos =();
my @list_of_text_infos_index =();
# descriptive multi-line text to be placed on top of
# the thumbnail html pages:
my @list_of_sub_text_infos =();
my @list_of_sub_text_infos_index =();
my @list_of_section_titles =();
my @list_of_section_styles =(); # 0=normal, 1=no-text
# descriptive multi-line text to be placed on top
# of the top view  html page:
my @list_of_top_text_infos =();
# descriptive multi-line text to be placed on top
# of the selection file:
my @list_of_selection_comments =();
# these text lines may replace the address tag content:
my @list_of_address_text_infos =();
# the parameter array:
my @list_of_params =();
# an index which indicates the image number from which on a certain parameter
# is considered valid (only relevant for a few parameters, e.g. SUBTITLE, IMAGE_TEXT)
my @list_of_imgidx =();
# a list of comments from the beginning of the selection file
# which is preserved when file is overwritten:
my @list_of_file_comments = ();

# arrays which are used for scan and rename:
# array of files which have been grabbed during scan:
my @list_of_scan_file_names =();
# list of image dates: (extracted with jhead, empty string otherwise)
my @list_of_scan_image_dates =();
# list of image types: (jpg, gif, or png)
my @list_of_scan_file_types =();

###############################################################################
# read in selection list file given as script option
###############################################################################

# do a consistency check on the selection file if it exists:
if (-e $optionSelectionFile) {
  # make sure it is a plain file and a text file:
  if (! -f $optionSelectionFile) {
    exit_perl_script("Selection file $optionSelectionFile is not a plain file.");
  }
  if (! -T $optionSelectionFile) {
    exit_perl_script("Selection file $optionSelectionFile is not a text file.");
  }
  if ((-z $optionSelectionFile) && ($optionScan != 1)) {
    exit_perl_script("Selection file $optionSelectionFile has size zero.");
  }
}
# finally, if we can mix it up with an image file we exit as well:
my $file_type = get_file_type($optionSelectionFile);
if ($file_type ne "unknown") {
  exit_perl_script("Selection file $optionSelectionFile looks like an image.");
}

# OK, now let's also open the messages log file:
my $globalLogFileName = $optionSelectionFile . ".log";
if (-e $globalLogFileName) {
  if (-w $globalLogFileName) {
    print STDOUT " info: Opening log file $globalLogFileName (overwriting) ...\n";
    my $is_ok = open(LOGFILE, ">" . $globalLogFileName);
    if ((!defined($is_ok)) || ($is_ok != 1)) {
      exit_perl_script("File $globalLogFileName could not be opened for writing.");
    }
  } else {
    exit_perl_script("Could not open log file $globalLogFileName in overwrite mode.");
  }
} else {
  print STDOUT " info: Opening log file $globalLogFileName (new file) ...\n";
  my $is_ok = open(LOGFILE, ">" . $globalLogFileName);
  if ((!defined($is_ok)) || ($is_ok != 1)) {
    exit_perl_script("File $globalLogFileName could not be opened for writing.");
  }
}
$globalLogFileIsActive = 1;
# write init message:
print LOGFILE "###############################################################\n";
print LOGFILE "#### $ScriptName $ScriptVersion, " . get_todays_date() . "\n";
print LOGFILE "###############################################################\n";
print LOGFILE "Finished option processing, starting execution of script ...\n";

# check if selection filename exists and is readable as long as we are in non-scan mode:
if ($optionScan != 1) {
  my $is_ok = (-r $optionSelectionFile);
  if ((!defined($is_ok)) || ($is_ok != 1)) {
    exit_perl_script("file $optionSelectionFile is not readable or does" .
                     " not exist.");
  }
}

print_info_message("Parsing selection list and configuration file $optionSelectionFile ...");

# open file or STDIN
open(INFILE, $optionSelectionFile);

# the image index variable used during parameter processing:
my $image_number   = 0;         # first image index is image 0, then 1, ...
# a parameter key and value pair:
my ($key, $value);
# we collect the initial comments to be written back:
my $is_initial_comment = 1;

# process the parameter configuration file line by line:
# first read in images and parameter information:
while (<INFILE>) {
  # after reading in a line:
  chop;                         # remove newline
  s/^[  ]*//;                   # remove leading whitespaces
  s/[  ]*$//;                   # remove trailing whitespaces
  # skip empty lines:
  next if ($_ eq "");
  # skip comments starting with "#": (but collect initial comments)
  if ($_ =~ /^\#/) {
    if ($_ =~ /^\#\#\# end of user comments \#\#\#/) {
      $is_initial_comment = 0;
    }
    if ($is_initial_comment == 1) {
      push(@list_of_file_comments, $_);
    }
    next;
  }
  # it seems we have found the first non-comment line:
  $is_initial_comment = 0;
  # check for parameter lines (with exception of image path):
  if ((/^[A-Z_0-9]+\s*\=/) && !(/^IMAGE_PATH\s*\=/)) {
    # yes, parameter line: store for later processing
    push(@list_of_params, $_);
    # store current image index:
    push(@list_of_imgidx, $image_number);
    # process WEB_READY, as we need it ASAP ;-) (this is a ugly hack I know)
    ($key, $value) = return_parameter_data($_);
    if ($key eq "WEB_READY") {
      if (($value == 1) || ($value == 0)) {
        $globalWebReady = $value;
      }
    }
    next;
  }
  # assume image path name or IMAGE_PATH=... entry:
  # (obviously an "=" sign should not be used as part of an image path!)
  s%\\%/%g;                     # replace "\" by "/" for HTML
  if (/^IMAGE_PATH\s*\=/) {
    ($key, $value) = return_parameter_data($_);
  } else {
    # we dispose of the IMAGE_PATH tag and keep the image path only:
    $value = $_;
  }
  # in Non WEB_READY mode we discard absolute path names, which start with "/":
  if (($value =~ m%^\/%) && ($globalWebReady == 0)) {
    print_error_message("Given image path name\n" .
                        "    $value must not start with \"\/\" (slash) in non-Web-Ready mode. Path is ignored.");
  } elsif ($value ne "") {
    push(@list_of_images, $value);
    # optional characteristics per image: set to "" by default:
    push(@list_of_image_names, "");
    push(@list_of_html_names, "");
    push(@list_of_info_names, "");
    push(@list_of_wrapper_names, "");
    push(@list_of_button_names, "");
    push(@list_of_thumbnail_names, "");
    push(@list_of_websize_names, "");
    push(@list_of_fullsize_names, "");
    push(@list_of_update_info, $globalUpdateValue);
    push(@list_of_info_page_markers, 0);
    push(@list_of_image_fpaths, "");
    push(@list_of_image_fprefixes, "");
    push(@list_of_image_fnames, "");
    push(@list_of_image_uniquifiers, "");
    push(@list_of_image_fexts, "");
    push(@list_of_image_links, "");
    push(@list_of_image_ftypes, "");
    push(@list_of_image_fsizes, "");
    push(@list_of_image_exif, -1); # we don't know yet
    push(@list_of_image_dates, "");
    push(@list_of_image_times, "");
    push(@list_of_image_widths, 0);
    push(@list_of_image_heights, 0);
    push(@list_of_exif_flashused, "");
    push(@list_of_exif_isoequiv, "");
    push(@list_of_exif_cameravendor, "");
    push(@list_of_exif_cameramodel, "");
    push(@list_of_exif_focallength, "");
    push(@list_of_exif_exposuretime, "");
    push(@list_of_exif_aperture, "");
    push(@list_of_exif_exposurebias, "");
    push(@list_of_exif_meteringmode, "");
    push(@list_of_exif_exposuremode, "");
    push(@list_of_geometry_ratio, "");
    push(@list_of_image_captions, "");
    push(@list_of_alt_texts, "");
    push(@list_of_image_imprints, "$globalImageImprintDefault");
    push(@list_of_rot_infos, 0); # default rotation is 0 degree (= no rotation)
    push(@list_of_del_markers, 0); # do not delete images by default ;-)
    push(@list_of_top_select_markers, 0); # 0 or 1, by default not selected
    push(@list_of_has_text_infos, 0); # 0 or 1, by default we assume no text infos exist
    push(@list_of_section_titles, "");
    push(@list_of_section_styles, "");
    # ok, accepted image:
    $image_number++;
  } else {
    print_error_message("Empty string found in image path entry. Skipping ...");
  }
}

close(INFILE);

# in scan mode we may not want to have existing image entries in our
# configuration file:
if (($#list_of_images >= 0) && ($optionScan == 1)) {
  print_warning_message("Image entries do already exist in file $optionSelectionFile !");
}

###############################################################################
# process config file parameters in a separate loop:
###############################################################################

print_info_message("Processing configuration parameters read from file $optionSelectionFile ...");

# the parameter list index:
my $pix = 0;
# the current image that is selected for the top page 
# (0 = first image to be selected by default, unless later overridden)
my $current_top_selection = 0;
# currently chosen section style:
my $current_section_style = $globalSubPagesNoText;
# indicates that we have discovered a parse error:
my $ParseError = 0;
my $ParseMessage = "";
ParamLoop : for($pix=0;$pix<=$#list_of_params;$pix++) {
  ($key, $value) = return_parameter_data($list_of_params[$pix]);
  $ParseError = 0;
  $ParseMessage = "Expecting value \"0\" or \"1\"."; # default message
  if ($key eq "TITLE") {
    $globalTitle = $value;
  } elsif ($key eq "AUTHOR") {
    $globalAuthor = $value;
  } elsif ($key eq "COPYRIGHT") {
    $globalCopyright = $value;
  } elsif ($key eq "EMAIL") {
    if ($value =~ /^[-a-zA-Z0-9_\.]+\@[-a-zA-Z0-9_\.]+$/) {
      $globalEmail = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Expecting \"@\" character embedded in [-a-zA-Z0-9_\.] strings.";
    }
  } elsif ($key eq "LEVEL_UP") {
    $globalLevelUp = $value;
  } elsif ($key eq "WEB_READY") {
    if (($value == 1) || ($value == 0)) {
      $globalWebReady = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "TARGET_DIR") {
    # we expect useful directory names ...
    if ($value =~ m%^[-0-9a-zA-Z._/]+$%) {
      $globalTargetDir = $value;
      if ($globalTargetDir !~ m%/$%) {
        $globalTargetDir = $globalTargetDir . "/";
      }
    } else {
      $ParseError = 1;
      $ParseMessage = "Directory names may only contain: \"-\",a-z,A-Z,0-9,\".\",\"_\", and \"/\".";
    }
  } elsif ($key eq "HTML_BG_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalBgColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "HTML_TEXT_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalTextColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "HTML_LINK_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalLinkColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "HTML_VLINK_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalVLinkColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "HTML_ALINK_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalALinkColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "IMG_BORDER_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalBorderColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "IMG_SHADOW_COLOR") {
    if ($value =~ /^\#[0-9a-fA-F]{6}$/) {
      $globalDropShadowColor = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Color format: \"#XXXXXX\", X=[0-9a-fA-F].";
    }
  } elsif ($key eq "SUBTITLE") {
    if ($list_of_imgidx[$pix] != 0) {
      $list_of_top_select_markers[$current_top_selection] = 1;
    }
    $list_of_section_titles[$list_of_imgidx[$pix]] = $value;
    $list_of_section_styles[$list_of_imgidx[$pix]] = $current_section_style;
    $current_top_selection = $list_of_imgidx[$pix];
  } elsif ($key eq "IMAGE_DEL") {
    if ($value == 1) {
      # this image is requested for deletion:
      $list_of_del_markers[$list_of_imgidx[$pix]] = $value;
      $globalImagesAreToBeDeleted = 1;
    } elsif ($value != 0) {
      $ParseError = 1;
    }
  } elsif ($key eq "SUB_PAGES_NO_TEXT") {
    if ($value == 1) {
      # starting with that image we want to omit all texts:
      $current_section_style = $value;;
    } elsif ($value != 0) {
      $ParseError = 1;
    }
  } elsif ($key eq "IMAGE_SEL") {
    if ($value == 1) {
      # this image is selected for the top view:
      $current_top_selection = $list_of_imgidx[$pix];
    } elsif ($value != 0) {
      $ParseError = 1;
    }
  } elsif ($key eq "IMAGE_EXIF") {
    if (($value == 1) || ($value == 0)) {
      # this image provides EXIF infos:
      $list_of_image_exif[$list_of_imgidx[$pix]] = $value;
    } else {
      $ParseError = 1;
    }
  } elsif (($key eq "IMAGE_CAPT") || ($key eq "IMAGE_CAPTION")) {
    $list_of_image_captions[$list_of_imgidx[$pix]] = $value;
  } elsif ($key eq "IMAGE_NAME") {
    $list_of_image_names[$list_of_imgidx[$pix]] = $value;
  } elsif ($key eq "IMAGE_LINK") {
    $list_of_image_links[$list_of_imgidx[$pix]] = $value;
  } elsif ($key eq "IMAGE_ALT") {
    $list_of_alt_texts[$list_of_imgidx[$pix]] = $value;
  } elsif ($key eq "IMAGE_IMPR") {
    $list_of_image_imprints[$list_of_imgidx[$pix]] = $value;
  } elsif ($key eq "IMAGE_INFO") {
    if (($value == 1) || ($value == 0)) {
      # update the info page marker which indicates if an info page
      # should be created by or not:
      $list_of_info_page_markers[$list_of_imgidx[$pix]] = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMAGE_ROT") {
    if (($value == 0) || ($value == 90) || ($value == -90) || ($value == 180) ||
        ($value == -180) || ($value == 270) || ($value == -270)) {
      # convert negative values:
      $value = 270 if ($value ==  -90);
      $value = 180 if ($value == -180);
      $value =  90 if ($value == -270);
      $list_of_rot_infos[$list_of_imgidx[$pix]] = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed values: 0, 90, 180, 270, -90, -180, -270.";
    }
  } elsif ($key eq "HTML_TOP_TEXT") {
    push(@list_of_top_text_infos, $value);
  } elsif ($key eq "HTML_ADDRESS_TEXT") {
    push(@list_of_address_text_infos, $value);
  } elsif ($key eq "IMAGE_TEXT") {
    push(@list_of_text_infos, $value);
    push(@list_of_text_infos_index, $list_of_imgidx[$pix]);
    # mark in list that this image has text infos available:
    $list_of_has_text_infos[$list_of_imgidx[$pix]] = 1;
  } elsif ($key eq "HTML_SUB_TEXT") {
    push(@list_of_sub_text_infos, $value);
    push(@list_of_sub_text_infos_index, $list_of_imgidx[$pix]);
  } elsif ($key eq "IMG_BORDER_WIDTH") {
    if (($value >= 0) && ($value < 128)) {
      $globalImageBorderWidth = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 0 - 128.";
    }
  } elsif ($key eq "IMG_SHADOW_ENABLE") {
    if (($value == 1) || ($value == 0)) {
      $globalMakeDropShadow = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMG_SHADOW_OFFSET") {
    if (($value >= 0) && ($value < 128)) {
      $globalDropShadowOffset = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 0 - 128.";
    }
  } elsif ($key eq "IMG_SHADOW_BLUR") {
    if (($value >= 0) && ($value < 128)) {
      $globalDropShadowBlur = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 0 - 128.";
    }
  } elsif ($key eq "IMG_IMPRINT_DEFAULT") {
    my $oldImageImprintDefault = $globalImageImprintDefault;
    $globalImageImprintDefault = $value;
    # replace only the default assignments,
    # try to keep potentially existing changes (via IMAGE_IMPR=) as is:
    ImageLoop : for(my $idx=0; $idx <= $#list_of_image_imprints; $idx++) {
      if ($list_of_image_imprints[$idx] eq $oldImageImprintDefault) {
        $list_of_image_imprints[$idx] = $globalImageImprintDefault;
      }
    }
  } elsif ($key eq "IMG_ADD_JPG_INFO_PAGE") {
    if (($value == 1) || ($value == 0)) {
      $globalAddJpgInfoPage = $value;
      # update the info page marker which indicates if an info page
      # should be created by default or not:
      UpdateInfoPageMarker : for (my $idx= 0; $idx <= $#list_of_images; $idx++) {
        $list_of_info_page_markers[$idx] = $globalAddJpgInfoPage;
      }
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "THUMBNAIL_RANDOM_ROTATE") {
    if (($value == 1) || ($value == 0)) {
      $globalThumbnailRandomRotate = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "THUMBNAIL_ROTATION_ANGLE") {
    if (($value >= 0) && ($value <= 10)) {
      $globalThumbnailRotationAngle = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed fixed point number range: 0.0 - 10.0 (0 = do not rotate).";
    }
  } elsif ($key eq "WEBSIZE_RANDOM_ROTATE") {
    if (($value == 1) || ($value == 0)) {
      $globalWebsizeRandomRotate = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "WEBSIZE_ROTATION_ANGLE") {
    if (($value >= 0) && ($value <= 5)) {
      $globalWebsizeRotationAngle = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed fixed point number range: 0.0 - 5.0 (0 = do not rotate).";
    }
  } elsif ($key eq "IMG_JPEG_QUALITY") {
    if (($value >= 0) && ($value <= 100)) {
      $globalImageQualityFactor = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 0 - 100.";
    }
  } elsif ($key eq "IMG_JPEG_PROGRESSIVE") {
    if (($value == 1) || ($value == 0)) {
      $globalProgressiveJpeg = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "THUMBNAILS_PER_ROW") {
    if (($value >= 3) && ($value <= 8)) {
      $globalImagesPerRow = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 3 - 8.";
    }
  } elsif ($key eq "THUMBNAILS_MAX_ROWS") {
    if (($value >= 1) && ($value <= 128)) {
      $globalMaxRows = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 1 - 128.";
    }
  } elsif ($key eq "USE_JPEGTRAN_JHEAD") {
    if (($value == 1) || ($value == 0)) {
      $globalUseJpegtranJhead = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "THUMBNAIL_SIZE") {
    if (($value >= 64) && ($value <= 256)) {
      $globalThumbnailSize = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 64 - 256.";
    }
  } elsif ($key eq "BUTTON_SIZE") {
    if (($value >= 32) && ($value <= 256)) {
      $globalButtonSize = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 32 - 256.";
    }
  } elsif ($key eq "WEBSIZE_SIZE") {
    if (($value >= 256) && ($value <= 1024)) {
      $globalWebSize = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 256 - 1024.";
    }
  } elsif ($key eq "FULLSIZE_SIZE") {
    if (($value >= 512) && ($value <= 4096)) {
      $globalFullSize = int($value);
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 512 - 4096.";
    }
  } elsif ($key eq "ALWAYS_WEBSIZE") {
    if (($value == 1) || ($value == 0)) {
      $globalAlwaysWebSize = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "FULLSIZE_ENABLE") {
    if (($value == 1) || ($value == 0)) {
      $globalFullSizeEnable = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "THUMBNAIL_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalThumbnailSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "BUTTON_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalButtonSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "WEBSIZE_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalWebSizeSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "FULLSIZE_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalFullSizeSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "THUMBNAIL_DIR") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalThumbnailDir = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "BUTTON_DIR") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalButtonDir = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "WEBSIZE_DIR") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalWebSizeDir = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "FULLSIZE_DIR") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalFullSizeDir = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "LINK_ORIGINAL_IMAGE") {
    if (($value == 1) || ($value == 0)) {
      $globalLinkOrigImage = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_INDEX_NAME") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalHtmlPrefix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "HTML_CREATE_TOP") {
    if (($value == 1) || ($value == 0)) {
      $globalCreateTop = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_TOP_CAPTION") {
    $globalTopCaption = $value;
  } elsif ($key eq "HTML_TOP_BACK") {
    $globalTopBack = $value;
  } elsif ($key eq "HTML_CSS_FILENAME") {
    $globalStylesheetName = $value;
  } elsif ($key eq "HTML_CSS_ENABLE") {
    $globalCssEnable = $value;
    if ($globalCssEnable == 1) {
      # remove any <font size=-1></font> entries in favour of CSS Sytlesheets:
      $globalFontMinus1_ON  = "";
      $globalFontMinus1_OFF = "";
    }
  } elsif ($key eq "HTML_INFO_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalHtmlInfoSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "HTML_WRAPPER_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalHtmlWrapperSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "HTML_IMAGE_SUFFIX") {
    if ($value =~ /^[-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalHtmlImgSuffix = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "HTML_FILE_EXTENSION") {
    if (($value eq "htm") || ($value eq "html")) {
      $globalHtmlExtension = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed strings: \"html\" or \"htm\".";
    }
  } elsif ($key eq "HTML_IMAGE_DIR") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalHtmlDir = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "IVIEW_SELECTION_FILE") {
    if ($value =~ /^[0-9a-zA-Z][-0-9a-zA-Z._]+[0-9a-zA-Z]$/) {
      $globalIviewFileName = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed characters: [-0-9a-zA-Z._], not starting or ending with \"-\", \".\" or \"_\".";
    }
  } elsif ($key eq "HTML_NO_PART_NUMBER") {
    if (($value == 1) || ($value == 0)) {
      $globalNoPartNumber = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_NO_PART_ONE") {
    if (($value == 1) || ($value == 0)) {
      $globalNoPartOne = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_NO_TOOL_HINT") {
    if (($value == 1) || ($value == 0)) {
      $globalNoToolHint = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_TEXT_BACK") {
    $globalTextBack = $value;
  } elsif ($key eq "HTML_TEXTCELL_HEIGHT") {
    if (($value >= 8) && ($value <= 128)) {
      $globalTextHeight = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 8 - 128.";
    }
  } elsif ($key eq "HTML_THUMBNAIL_SPACING") {
    if (($value >= 2) && ($value <= 16)) {
      $globalThumbnailSpacing = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 2 - 16.";
    }
  } elsif ($key eq "HTML_IMAGE_SPACING") {
    if (($value >= 2) && ($value <= 16)) {
      $globalImageSpacing = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 2 - 16.";
    }
  } elsif ($key eq "HTML_THUMBNAIL_DISTANCE") {
    if (($value >= 4) && ($value <= 128)) {
      $globalThumbnailDistance = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 4 - 128.";
    }
  } elsif ($key eq "HTML_IMAGE_DISTANCE") {
    if (($value >= 4) && ($value <= 128)) {
      $globalImageDistance = $value;
    } else {
      $ParseError = 1;
      $ParseMessage = "Allowed integer range: 4 - 128.";
    }
  } elsif ($key eq "HTML_TEXT_ALIGN") {
    # convert from upper case to lower case:
    $value =~ tr/A-Z/a-z/;
    if (($value ne "center") && ($value ne "left") && ($value ne "right")) {
      $ParseError = 1;
      $ParseMessage = "Allowed strings: \"center\", \"left\" or \"right\".";
    } else {
      $globalTextAlign = $value;
    }
  } elsif ($key eq "HTML_TOP_TEXT_ALIGN") {
    # convert from upper case to lower case:
    $value =~ tr/A-Z/a-z/;
    if (($value ne "center") && ($value ne "left") && ($value ne "right")) {
      $ParseError = 1;
      $ParseMessage = "Allowed strings: \"center\", \"left\" or \"right\".";
    } else {
      $globalTopTextAlign = $value;
    }
  } elsif ($key eq "HTML_SUB_TEXT_ALIGN") {
    # convert from upper case to lower case:
    $value =~ tr/A-Z/a-z/;
    if (($value ne "center") && ($value ne "left") && ($value ne "right")) {
      $ParseError = 1;
      $ParseMessage = "Allowed strings: \"center\", \"left\" or \"right\".";
    } else {
      $globalSubTextAlign = $value;
    }
  } elsif ($key eq "HTML_IMG_TEXT_ALIGN") {
    # convert from upper case to lower case:
    $value =~ tr/A-Z/a-z/;
    if (($value ne "center") && ($value ne "left") && ($value ne "right")) {
      $ParseError = 1;
      $ParseMessage = "Allowed strings: \"center\", \"left\" or \"right\".";
    } else {
      $globalImgTextAlign = $value;
    }
  } elsif ($key eq "HTML_IMG_MAKE_DENSE") {
    if (($value == 1) || ($value == 0)) {
      $globalImgHtmlDense = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "HTML_IMG_CREATE_ALWAYS") {
    if (($value == 1) || ($value == 0)) {
      $globalImgHtmlCreateAlways = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMG_NAME_NO_ALPHA_PREFIX") {
    if (($value == 1) || ($value == 0)) {
      $globalRemoveAlphaPrefix = $value;
    } else {
      $ParseError = 1;
    }
} elsif ($key eq "IMG_NAME_NO_NUMBER_PREFIX") {
    if (($value == 1) || ($value == 0)) {
      $globalRemoveNumberPrefix = $value;
    } else {
      $ParseError = 1;
    }
} elsif ($key eq "IMG_NAME_NO_NUMBER_SUFFIX") {
    if (($value == 1) || ($value == 0)) {
      $globalSeparateNumericSuffix = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMG_NAME_REPLACE_UNDERSCORE") {
    if (($value == 1) || ($value == 0)) {
      $globalReplaceUnderScores = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMG_NAME_REPLACE_DASH") {
    if (($value == 1) || ($value == 0)) {
      $globalReplaceMinusSign = $value;
    } else {
      $ParseError = 1;
    }
  } elsif ($key eq "IMG_NAME_BLANK_BEFORE_CAPS") {
    if (($value == 1) || ($value == 0)) {
      $globalInsertBlankBeforeCaps = $value;
    } else {
      $ParseError = 1;
    }
  } else {
    # unknown parameter:
    $ParseError = 1;
    $ParseMessage = "Unknown parameter/value pair.";
  }
  if ($ParseError == 1) {
    print_error_message("Parameter: \"$key\" / \"$value\": $ParseMessage");
  }
}

###############################################################################
# post-processing of parameters:
###############################################################################

# if WebReady is active, check if target directory exists:
if ($globalWebReady == 1) {
  if (! -d $globalTargetDir) {
    exit_perl_script("Given Target Directory $globalTargetDir does not exist.");
  }
  if ($globalTargetDir =~ /^\//) {
    exit_perl_script("Given Target Directory $globalTargetDir must not start with \"/\".");
  }
  # the web ready option automatically disables any link to the original image
  $globalLinkOrigImage = 0;
}

# post-process copyright info:
if ($globalCopyright eq "") {
  $globalCopyright = "Copyright &copy; " . get_todays_year() . " $globalAuthor";
}

# if there hasn't been any, or only one subtitle, we also don't need a top page:
if ($current_top_selection == 0) {
  # disable creation of top file:
  $globalCreateTop = 0;
} else {
  # make sure last entry is added:
  $list_of_top_select_markers[$current_top_selection] = 1;
}

# determine actual value for Table align attribute:
if ($globalTextAlign eq "center") {
  $globalTableAlignAttr = " align=center";
}
# determine actual value for Top Text alignment attribute:
if ($globalTopTextAlign eq "") {
  $globalTopTextAlign = $globalTextAlign;
}
# determine actual value for Subtitle Text alignment attribute:
if ($globalSubTextAlign eq "") {
  $globalSubTextAlign = $globalTextAlign;
}
# determine actual value for Image Text alignment attribute:
if ($globalImgTextAlign eq "") {
  $globalImgTextAlign = $globalTextAlign;
}

# make sure we disallow IMAGE_IMPR, IMAGE_ROT, IMAGE_INFO entries if
# IMAGE_LINK is set:
LoopCleanImageLinks : for(my $image_index=0;$image_index<=$#list_of_images;$image_index++) {
  
}

###############################################################################
# Scanning for image files:
###############################################################################

if ($optionScan == 1) {

  print_info_message("Scanning for image files ...");

  # read in the given files and directories recursively:
  # filter everything except directories and image files:
  @optionPathNames = qw(.) unless @optionPathNames;
  find sub { my $path_name = $File::Find::name;
             my $file_type = "";
             if (-d $path_name) {
               $file_type  = "dir";
             } else {
               $file_type = get_file_type($path_name);
             }
             if (($file_type ne "unknown") && ($file_type ne "dir")) {
               # either jpg, png or gif:
               push(@list_of_scan_file_names, $path_name);
               push(@list_of_scan_file_types, $file_type);
             }
           }, @optionPathNames;
  # extract image date (if possible):
  FileNameLoop :
      for (my $file_index=0; $file_index <= $#list_of_scan_file_names; $file_index++) {
        my ($idate,$itime) = ("", "");
        # check if we have jpeg image and have program jhead available:
        if (($globalUseJpegtranJhead == 1) && 
            ($list_of_scan_file_types[$file_index] eq "jpg")) {
          my (@info) = `jhead $list_of_scan_file_names[$file_index]`;
          ScanImageInfo : for(my $info_index=0; $info_index<=$#info; $info_index++) {
            my $info_line = $info[$info_index];
            if ($info_line =~ m/Date\/Time\s*:\s*(.+) /) {
              $idate        = $1;
            }
            if ($info_line =~ m/Date\/Time\s*:\s*.+ (.+)/) {
              $itime        = $1;
            }
          }
        }
        $list_of_scan_image_dates[$file_index] = $idate . $itime;
      }
  # sort filenames if requested:
  my %hash_of_scan_files = ();
  if ($optionOrder ne "") {
    if ($optionOrder eq "name") {
      # we only sort the file names and ignore file type and image date:
      # note that we sort without regard to case:
      @list_of_scan_file_names = sort { lc($a) cmp lc($b) } @list_of_scan_file_names;
    } elsif ($optionOrder eq "date") {
      # we sort according to image date (as much as possible):
      # first we build a hash array, and then sort the date key array:
      BuildHash : for(my $idx = 0; $idx <= $#list_of_scan_file_names; $idx++) {
        # we add a suffix to be able to handle "same time" files:
        my $suffix_number = 0;
        my $key = $list_of_scan_image_dates[$idx];
        # we allow 9999 files with the same or no date/time before we run into an error:
        my $max_suffix_number = 10000;
        AddSuffix : while ($suffix_number < $max_suffix_number) {
          if ($suffix_number < 10) {
            $suffix_number = "000" . $suffix_number;
          } elsif ($suffix_number < 100) {
            $suffix_number = "00" . $suffix_number;
          } elsif ($suffix_number < 1000) {
            $suffix_number = "0" . $suffix_number;
          }
          my $final_key = $key . $suffix_number;
          if ( defined $hash_of_scan_files{$final_key} ) {
            $suffix_number = $suffix_number + 1;
            if ($suffix_number >= $max_suffix_number) {
              exit_perl_script("sort according to image date failed.");
            }
          } else {
            # add to hash:
            $hash_of_scan_files{$final_key} = $list_of_scan_file_names[$idx];
            $list_of_scan_image_dates[$idx] = $final_key;
            # exit while loop:
            last AddSuffix;
          }
        }
      }
      @list_of_scan_image_dates = sort @list_of_scan_image_dates;
    }
  }
  # finally add the scanned images to our selection file array:
  # check if we look for dates or names:
  my $max_index_value;
  if ($optionOrder ne "date") {
    $max_index_value = $#list_of_scan_file_names;
  } else {
    $max_index_value = $#list_of_scan_image_dates;
  }
  # we calculate the two-letter filename prefix already in this state:
  my @all_chars = qw(a b c d e f g h i j k l m n o p q r s t u v w x y z);
  my $cidx1     = 0;
  my $cidx2     = 0;
  my $current_prefix = "";
  AddToSelectionList : 
      for (my $idx = 0; $idx <= $max_index_value; $idx++) {
        if ($optionOrder ne "date") {
          push(@list_of_images, $list_of_scan_file_names[$idx]);
        } else {
          my $index = $list_of_scan_image_dates[$idx];
          push(@list_of_images, $hash_of_scan_files{$index});
        }
        # optional characteristics per image: set to "" by default:
        push(@list_of_image_names, "");
        push(@list_of_html_names, "");
        push(@list_of_info_names, "");
        push(@list_of_wrapper_names, "");
        push(@list_of_button_names, "");
        push(@list_of_thumbnail_names, "");
        push(@list_of_websize_names, "");
        push(@list_of_fullsize_names, "");
        push(@list_of_update_info, $globalUpdateValue);
        push(@list_of_info_page_markers, $globalAddJpgInfoPage);
        push(@list_of_image_fpaths, "");
        push(@list_of_image_fnames, "");
        push(@list_of_image_uniquifiers, "");
        push(@list_of_image_fexts, "");
        push(@list_of_image_links, "");
        push(@list_of_image_ftypes, "");
        push(@list_of_image_fsizes, "");
        push(@list_of_image_exif, -1);
        push(@list_of_image_dates, "");
        push(@list_of_image_times, "");
        push(@list_of_image_widths, 0);
        push(@list_of_image_heights, 0);
        push(@list_of_exif_flashused, "");
        push(@list_of_exif_isoequiv, "");
        push(@list_of_exif_cameravendor, "");
        push(@list_of_exif_cameramodel, "");
        push(@list_of_exif_focallength, "");
        push(@list_of_exif_exposuretime, "");
        push(@list_of_exif_aperture, "");
        push(@list_of_exif_exposurebias, "");
        push(@list_of_exif_meteringmode, "");
        push(@list_of_exif_exposuremode, "");
        push(@list_of_geometry_ratio, "");
        push(@list_of_image_captions, "");
        push(@list_of_alt_texts, "");
        push(@list_of_rot_infos, 0); # default rotation is 0 degree (= no rotation)
        push(@list_of_del_markers, 0); # do not delete images by default ;-)
        push(@list_of_top_select_markers, 0); # 0 or 1, be default not selected
        push(@list_of_has_text_infos, 0); # 0 or 1, ny default we assume no text infos exist
        # a common subtitle for all automatically scanned files:
        push(@list_of_section_titles, "Images compiled with scan option ...");
        push(@list_of_section_styles, "");
        # we calculate and add the prefix only if selected by option:
        if ($optionPrefix == 1) {
          $current_prefix = $all_chars[$cidx2] . $all_chars[$cidx1] . "_";
          $cidx1++;
          if ($cidx1 > $#all_chars) {
            $cidx1 = 0;
            $cidx2++;
            if ($cidx2 > $#all_chars) {
              $cidx2 = 0;
            }
          }
        }
        push(@list_of_image_fprefixes, $current_prefix);
      }
}

###############################################################################
# preprocess image name and caption and section subtitles first:
#   if no explicit caption is given, we extract the image name
#   from the image file name (using configurable rules);
#   extract JPEG EXIF info as far as possible:
###############################################################################

print_info_message("Processing image information ...");

# choose a predefined string as default Selection title:
my $current_section_title = $globalDefaultSectionTitle;
$current_section_style    = $globalSubPagesNoText;
# currently selected image path:
my $full_image_path = "";
my $image_path      = "";
# currently selected image:
my $image_file_name = "";
# parts of the filename:
my $image_name      = "";
my $image_extension = "";
my $image_prefix    = "";
my $image_text      = "";

# the following two loops uniquify the image filenames:
my @localListOfImageNames = ();
LocalImageNamesLoop : for(my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
  # pre-process image path and filename first:
  $full_image_path = $list_of_images[$image_index];
  # split image path name in separate parts:
  $full_image_path =~ m%(.*)(?=/)(/)?([^/]+)%;
  $image_file_name = $3;
  $image_file_name =~ m%(.*)(?=\.)(.)?([^.]+)%;
  $localListOfImageNames[$image_index]      = $1;
}
UniquifyLoop : for(my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
  # first we have to uniquify all image raw file names:
  LoopFromHere : for(my $new_image_index=$image_index+1; $new_image_index <= $#list_of_images; $new_image_index++) {
    if ($localListOfImageNames[$image_index] eq $localListOfImageNames[$new_image_index]) {
      if ($list_of_image_uniquifiers[$new_image_index] eq "") {
	$list_of_image_uniquifiers[$new_image_index] = 1;
      } else {
        $list_of_image_uniquifiers[$new_image_index]++; # we expect a number
      }
    }
  }
}

# split the image path into separate parts as part of the preprocessing:
# store separated parts in arrays for later use:
ImageNameLoop : for(my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
  # process image path and filename first:
  $full_image_path = $list_of_images[$image_index];
  # split image path name in separate parts:
  $full_image_path =~ m%(.*)(?=/)(/)?([^/]+)%;
  $image_path      = $1 . $2;
  $image_file_name = $3;
  $image_file_name =~ m%(.*)(?=\.)(.)?([^.]+)%;
  $image_name      = $1;
  $image_extension = $3;
  $image_name      =~ m/^([a-z][a-z]\_)?(.)+/;
  $image_prefix    = $1;
  # clean up image text:
  $image_text = create_auto_image_name($image_name);
  $list_of_image_fpaths[$image_index]    = $image_path;
  # in scan mode do not replace an automatically created prefix
  # with an existing one if the rename option is active:
  if (($optionScan == 0) || ($optionRename == 0) || ($optionPrefix == 0)) {
    # replace generated prefix with already existing one:
    if ($image_prefix ne "") {
      $list_of_image_fprefixes[$image_index]  = $image_prefix;
    }
  }
  $list_of_image_fnames[$image_index]     = $image_name;
  $list_of_image_fexts[$image_index]      = $image_extension;
  # check if we have alternative for image name:
  if ($list_of_image_names[$image_index] eq "") {
    $list_of_image_names[$image_index]    = $image_text;
  }
  # check if we have an alternative text for the ALT tag:
  if ($list_of_alt_texts[$image_index] eq "") {
    $list_of_alt_texts[$image_index]      = $list_of_image_names[$image_index];
  }
  if ($list_of_image_captions[$image_index] eq "") {
    $list_of_image_captions[$image_index] = $list_of_image_names[$image_index];
  }
  if ($list_of_section_titles[$image_index] eq "") {
    $list_of_section_titles[$image_index] = $current_section_title;
  } else {
    $current_section_title = $list_of_section_titles[$image_index];
  }
  if ($list_of_section_styles[$image_index] eq "") {
    $list_of_section_styles[$image_index] = $current_section_style;
  } else {
    $current_section_style = $list_of_section_styles[$image_index];
  }
  $list_of_html_names[$image_index] =
    $globalHtmlDir . "/" .$list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalHtmlImgSuffix . "." . $globalHtmlExtension;
  $list_of_info_names[$image_index] =
    $globalHtmlDir . "/" .$list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalHtmlInfoSuffix . "." . $globalHtmlExtension;
  $list_of_wrapper_names[$image_index] =
    $globalHtmlDir . "/" .$list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalHtmlWrapperSuffix . "." . $globalHtmlExtension;
  # finally extract additional usefule information from the image itself
  # (if possible! we try to use jhead for JPEG images):
  if ((-e $full_image_path) && (-r $full_image_path) && (-f $full_image_path)) {
    # additional info grabbed via "jhead" for JPEG images:
    my $image_ftype        = "";
    my $image_fsize        = 0;
    my $image_width        = 0;
    my $image_height       = 0;
    my $image_date         = "";
    my $image_time         = "";
    my $image_fdate        = "";
    my $image_ftime        = "";
    my $image_flashused    = "N/A";
    my $image_isoequiv     = "N/A";
    my $image_cameravendor = "N/A";
    my $image_cameramodel  = "N/A";
    my $image_focallength  = "N/A";
    my $image_exposuretime = "N/A";
    my $image_aperture     = "N/A";
    my $image_exposurebias = "N/A";
    my $image_meteringmode = "N/A";
    my $image_exposuremode = "N/A";
    my $image_geometry     = "";
    # ok, image file exists, is readable, and a file:
    $image_ftype = get_file_type($full_image_path);
    $list_of_image_ftypes[$image_index] = $image_ftype;
    if ($image_ftype eq "unknown") {
      print_error_message("Given image path name: $full_image_path seems not to be an image!");
      $list_of_image_exif[$image_index] = 0; # no EXIF info as well ;-)
    } elsif (($image_ftype eq "jpg") && ($globalUseJpegtranJhead  == 1)) {
      # we try to read EXIF info only if we don't know yet:
      if ($list_of_image_exif[$image_index] != 0) {
	# ok, try to extract jhead info:
	print_info_message("Trying to extract jhead info for $full_image_path ...");
	my (@info) = `jhead $full_image_path`;
	ScanImageInfo : for(my $info_index=0; $info_index<=$#info; $info_index++) {
	  my $info_line = $info[$info_index];
	  if ($info_line =~ m/Date\/Time\s*:\s*(.+) /) {
	    $image_date        = $1;
	    # convert image to ISO 8601 format:
	    $image_date        =~ s/([0-9]{4}):([0-9]{2}):([0-9]{2})/$1-$2-$3/g;
	  }
	  if ($info_line =~ m/File date\s*:\s*(.+) /) {
	    $image_fdate       = $1;
	    # convert image to ISO 8601 format:
	    $image_fdate        =~ s/([0-9]{4}):([0-9]{2}):([0-9]{2})/$1-$2-$3/g;
	  }
	  if ($info_line =~ m/Date\/Time\s*:\s*.+ (.+)/) {
	    $image_time        = $1;
	  }
	  if ($info_line =~ m/File date\s*:\s*.+ (.+)/) {
	    $image_ftime       = $1;
	  }
	  if ($info_line =~ m/File size\s*:\s*(\d+) bytes/) {
	    my $extracted_size = $1;
	    # convert size to readable value:
	    if ($extracted_size > 1024*1024) {
	      $image_fsize = sprintf("%.2f", $extracted_size/(1024*1024)) . " MBytes";
	    } elsif ($extracted_size > 1024) {
	      $image_fsize = sprintf("%.2f", $extracted_size/1024) . " kBytes";
	    } else {
	      $image_fsize = $extracted_size . " Bytes";
	    }
	  }
	  if ($info_line =~ m/Resolution\s*:\s*(\d+) x (\d+)/) {
	    $image_width       = $1;
	    $image_height      = $2;
	  }
	  if ($info_line =~ m/Flash used\s*:\s*(.+)/) {
	    $image_flashused = $1;
	  }
	  if ($info_line =~ m/ISO equiv.\s*:\s*(.+)/) {
	    $image_isoequiv = $1;
	  }
	  if ($info_line =~ m/Camera make\s*:\s*(.+)/) {
	    $image_cameravendor = $1;
	  }
	  if ($info_line =~ m/Camera model\s*:\s*(.+)/) {
	    $image_cameramodel = $1;
	  }
	  if ($info_line =~ m/Focal length\s*:\s*(.+)/) {
	    $image_focallength  = $1;
	  }
	  if ($info_line =~ m/Exposure time\s*:\s*(.+)/) {
	    $image_exposuretime = $1;
	  }
	  if ($info_line =~ m/Aperture\s*:\s*(.+)/) {
	    $image_aperture = $1;
	  }
	  if ($info_line =~ m/Exposure bias\s*:\s*(.+)/) {
	    $image_exposurebias = $1;
	  }
	  if ($info_line =~ m/Metering Mode\s*:\s*(.+)/) {
	    $image_meteringmode = $1;
	  }
	  if ($info_line =~ m/Exposure\s*:\s*(.+)/) {
	    $image_exposuremode = $1;
	  }
	}
	# issue a warning if we cannot read the JPEG info header:
	if (! @info) {
	  print_warning_message("Image path name: $full_image_path has no readable JPEG info.");
          $list_of_image_exif[$image_index] = 0; # no EXIF info available
	} else {
	  $list_of_image_exif[$image_index] = 1;
        }
      }
      # try to grab some info anyhow via other possibilities:
      if (($image_width ==0) || ($image_height ==0)) {
        ($image_width, $image_height) = get_image_size($full_image_path);
      }
    } else {                    # PNG or GIF images:
      # obviously no EXIF info available:
      $list_of_image_exif[$image_index] = 0;
      # try to gather as much info as possible for other file types:
      print_info_message("Trying to extract GIF and PNG info for $full_image_path ...");
      ($image_width, $image_height) = get_image_size($full_image_path);
    }
    if (($image_width != 0) && ($image_height != 0)) {
      # try to extract the geometry (3:2 or 4:3, or non-standard):
      if ((($image_width / $image_height) == 1.5) || (($image_height / $image_width) == 1.5)) {
        $image_geometry = "3 : 2";
      } elsif ((($image_width / $image_height) == 0.75) || (($image_height / $image_width) == 0.75)) {
        $image_geometry = "4 : 3";
      } else {
        $image_geometry = "$stringNonStandard";
      }
    } else {
      $image_geometry = "N/A";
      if ($image_width == 0) {
        $image_width = "N/A";
      }
      if ($image_height == 0) {
        $image_height = "N/A";
      }
    }
    # check if size = 0:
    if ($image_fsize == 0) {
      $image_fsize = "N/A";
    }
    # use file date and time if we don't have image date and time available):
    if (($image_date eq "") && ($image_fdate ne "")) {
      $image_date = $image_fdate . " ($stringFile)";
    } elsif ($image_date eq "") {
      $image_date  = "N/A";
    } 
    if ($image_fdate eq "") {
      $image_fdate = "N/A";
    }
    if (($image_time eq "") && ($image_ftime ne "")) {
      $image_time = $image_ftime . " ($stringFile)";
    } elsif ($image_time eq ""){
      $image_time  = "N/A";
    } 
    if ($image_ftime eq "") {
      $image_ftime = "N/A";
    }
    # finally assign info that has been gathered to image arrays:
    $list_of_image_ftypes[$image_index]      = $image_ftype;
    $list_of_image_fsizes[$image_index]      = $image_fsize;
    $list_of_image_dates[$image_index]       = $image_date;
    $list_of_image_times[$image_index]       = $image_time;
    $list_of_image_widths[$image_index]      = $image_width;
    $list_of_image_heights[$image_index]     = $image_height;
    $list_of_exif_flashused[$image_index]    = $image_flashused;
    $list_of_exif_isoequiv[$image_index]     = $image_isoequiv;
    $list_of_exif_cameravendor[$image_index] = $image_cameravendor;
    $list_of_exif_cameramodel[$image_index]  = $image_cameramodel;
    $list_of_exif_focallength[$image_index]  = $image_focallength;
    $list_of_exif_exposuretime[$image_index] = $image_exposuretime;
    $list_of_exif_aperture[$image_index]     = $image_aperture;
    $list_of_exif_exposurebias[$image_index] = $image_exposurebias;
    $list_of_exif_meteringmode[$image_index] = $image_meteringmode;
    $list_of_exif_exposuremode[$image_index] = $image_exposuremode;
    $list_of_geometry_ratio[$image_index]    = $image_geometry;
    print_info_message("Image infos $full_image_path: type $image_ftype, size $image_fsize, " .
                       "$image_width x $image_height, date: $image_date, time: $image_time");
  } else {
    print_error_message("Given image path name: $full_image_path does not exist, " .
                        "is not readable, or does not denote a file name path.");
  }
}

###############################################################################
# Process Button, Thumbnail and Web image file names first:
###############################################################################

print_info_message("Processing image filenames ...");

# temporary variable for path and file extension (as these are modified):
my $image_path      = "";
# new filenames:
my $thumbnail_name  = "";
my $button_name     = "";
my $websize_name    = "";
my $fullsize_name    = "";
my $ThumbnailImage  = "";
my $ButtonImage     = "";
my $WebSizeImage    = "";
my $FullSizeImage    = "";

ImageNameProcessingLoop :
  for (my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
    # JPEG extension for generated image files is $constantJPEGExtension:
    $thumbnail_name  = $list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalThumbnailSuffix . $constantJPEGExtension;
    $button_name     = $list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalButtonSuffix . $constantJPEGExtension;
    $websize_name    = $list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalWebSizeSuffix . $constantJPEGExtension;
    $fullsize_name    = $list_of_image_fnames[$image_index] . $list_of_image_uniquifiers[$image_index] .
      $globalFullSizeSuffix . $constantJPEGExtension;
    # relative pathnames for generated images:
    $ButtonImage     = $globalButtonDir . "/" . $button_name;
    $ThumbnailImage  = $globalThumbnailDir . "/" . $thumbnail_name;
    $WebSizeImage    = $globalWebSizeDir . "/" . $websize_name;
    $FullSizeImage   = $globalFullSizeDir . "/" . $fullsize_name;
    # store relative pathnames in arrays:
    $list_of_button_names[$image_index]    = $ButtonImage;
    $list_of_thumbnail_names[$image_index] = $ThumbnailImage;
    $list_of_websize_names[$image_index]   = $WebSizeImage;
    $list_of_fullsize_names[$image_index]  = $FullSizeImage;
  }

###############################################################################
# The delete part: basically we delete created images and html pages here
#                  if this is requested with the IMAGE_DEL = 1 entry
###############################################################################
my $ThumbnailImage   = "";
my $ButtonImage      = "";
my $WebSizeImage     = "";
my $FullSizeImage    = "";
my $HtmlFileName     = "";
my $InfoFileName     = "";
my $WrapperFileName  = "";

if ($globalImagesAreToBeDeleted == 1) {

  print_info_message("Deleting image entries and associated files (if required) ...");

  if ($globalLogDeletedImages == 1) {
      if ((-e $globalDeletedImagesLogfileName) && (! -w $globalDeletedImagesLogfileName)) {
	  exit_perl_script("image deletion log file " . $globalDeletedImagesLogfileName . " exists, but is not writeable.");
      }
      print_info_message("Logging deleted image files to logfile ./$globalDeletedImagesLogfileName ...");
      open(LOGDELFILE, ">>" . $globalDeletedImagesLogfileName);
      print LOGDELFILE "# deleted images in $optionSelectionFile:\n";
  }

  # this time we need an adaptive loop as we remove list entries on the fly:
  my $image_count = $#list_of_images;
  my $image_index=0;
  # we do this before rename to avoid renaming files before deleting them:
  ImageDeletionLoop :
      while ($image_index <= $#list_of_images) {
        # these are the all the filenames for images and HTML pages:
        $ThumbnailImage  = $list_of_thumbnail_names[$image_index];
        $ButtonImage     = $list_of_button_names[$image_index];
        $WebSizeImage    = $list_of_websize_names[$image_index];
        $FullSizeImage   = $list_of_fullsize_names[$image_index];
        $HtmlFileName    = $list_of_html_names[$image_index];
        $InfoFileName    = $list_of_info_names[$image_index];
        $WrapperFileName = $list_of_wrapper_names[$image_index];
        if ($globalWebReady == 0) {
          # add real image path if necessary:
          $ThumbnailImage  = $list_of_image_fpaths[$image_index] . $ThumbnailImage;
          $ButtonImage     = $list_of_image_fpaths[$image_index] . $ButtonImage;
          $WebSizeImage    = $list_of_image_fpaths[$image_index] . $WebSizeImage;
          $FullSizeImage   = $list_of_image_fpaths[$image_index] . $FullSizeImage;
        } else {
          $ThumbnailImage  = $globalTargetDir . $ThumbnailImage;
          $ButtonImage     = $globalTargetDir . $ButtonImage;
          $WebSizeImage    = $globalTargetDir . $WebSizeImage;
          $FullSizeImage   = $globalTargetDir . $FullSizeImage;
          $HtmlFileName    = $globalTargetDir . $HtmlFileName;
          $InfoFileName    = $globalTargetDir . $InfoFileName;
          $WrapperFileName = $globalTargetDir . $WrapperFileName;
        }
        # ok, now check if we have to delete the associated created files:
        if ($list_of_del_markers[$image_index] == 1)  {
          print_info_message("Deleting selection file entry for image $list_of_images[$image_index] ...");
          # check if we should log the delete-image operation:
          if ($globalLogDeletedImages == 1) {
	      print LOGDELFILE $list_of_images[$image_index] . "\n";
          }
          # first we mark previous and next page for regeneration of HTML:
          if ($image_index > 0) {
            $list_of_update_info[$image_index - 1] =
              $list_of_update_info[$image_index - 1] | $constantUpdateHtml;
          }
          if ($image_index < $#list_of_images) {
            $list_of_update_info[$image_index + 1] =
              $list_of_update_info[$image_index + 1] | $constantUpdateHtml;
          }
          # set indication that updates for HTML are required:
          $globalUpdateIsRequired = 1;
	  # we do not delete any files in case of IMAGE_LINK entries:
	  if ($list_of_image_links[$image_index] eq "") {
	    # now, check if files exist and delete them if they exist:
	    if ((-e $ThumbnailImage) && (-f $ThumbnailImage)) {
	      my $is_ok = unlink $ThumbnailImage;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete image file $ThumbnailImage.");
	      } else {
		print_info_message("IMAGE_DEL: image file $ThumbnailImage has been deleted.");
	      }
	    }
	    if ((-e $ButtonImage) && (-f $ButtonImage)) {
	      my $is_ok = unlink $ButtonImage;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete image file $ButtonImage.");
	      } else {
		print_info_message("IMAGE_DEL: image file $ButtonImage has been deleted.");
	      }
	    }
	    if ((-e $WebSizeImage) && (-f $WebSizeImage)) {
	      my $is_ok = unlink $WebSizeImage;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete image file $WebSizeImage.");
	      } else {
		print_info_message("IMAGE_DEL: image file $WebSizeImage has been deleted.");
	      }
	    }
	    if ($globalFullSizeEnable == 1) {
	      # check if we have to handle full siez images at all:
	      if ((-e $FullSizeImage) && (-f $FullSizeImage)) {
		my $is_ok = unlink $FullSizeImage;
		if (! $is_ok) {
		  print_error_message("IMAGE_DEL: Could not delete image file $FullSizeImage.");
		} else {
		  print_info_message("IMAGE_DEL: image file $FullSizeImage has been deleted.");
		}
	      }
	    }
	    if ((-e $HtmlFileName) && (-f $HtmlFileName)) {
	      my $is_ok = unlink $HtmlFileName;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete html file $HtmlFileName.");
	      } else {
		print_info_message("IMAGE_DEL: html file $HtmlFileName has been deleted.");
	      }
	    }
	    if ((-e $InfoFileName) && (-f $InfoFileName)) {
	      my $is_ok = unlink $InfoFileName;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete html file $InfoFileName.");
	      } else {
		print_info_message("IMAGE_DEL: html file $InfoFileName has been deleted.");
	      }
	    }
	    if ((-e $WrapperFileName) && (-f $WrapperFileName)) {
	      my $is_ok = unlink $WrapperFileName;
	      if (! $is_ok) {
		print_error_message("IMAGE_DEL: Could not delete html file $WrapperFileName.");
	      } else {
		print_info_message("IMAGE_DEL: html file $WrapperFileName has been deleted.");
	      }
	    }
	  }
	  # make sure we preserve any active IMAGE_SEL entries: i.e. we pick a
	  # replacement if our selection is deleted
	  if ($list_of_top_select_markers[$image_index] == 1) {
	    if (($image_index > 0) && 
		($list_of_section_titles[$image_index] eq
		 $list_of_section_titles[$image_index - 1])) {
	      $list_of_top_select_markers[$image_index - 1] = 1;
	    } elsif (($image_index < $#list_of_images) && 
		     ($list_of_section_titles[$image_index] eq
		      $list_of_section_titles[$image_index + 1])) {
	      $list_of_top_select_markers[$image_index + 1] = 1;
	    }
	  }
          # finally, cleanup all image arrays and remove this entry:
          splice @list_of_images, $image_index, 1;
          splice @list_of_image_names, $image_index, 1;
          splice @list_of_html_names, $image_index, 1;
          splice @list_of_info_names, $image_index, 1;
          splice @list_of_wrapper_names, $image_index, 1;
          splice @list_of_button_names, $image_index, 1;
          splice @list_of_thumbnail_names, $image_index, 1;
          splice @list_of_websize_names, $image_index, 1;
          splice @list_of_fullsize_names, $image_index, 1;
          splice @list_of_update_info, $image_index, 1;
          splice @list_of_info_page_markers, $image_index, 1;
          splice @list_of_image_fpaths, $image_index, 1;
          splice @list_of_image_fprefixes, $image_index, 1;
          splice @list_of_image_fnames, $image_index, 1;
          splice @list_of_image_fexts, $image_index, 1;
          splice @list_of_image_links, $image_index, 1;
          splice @list_of_image_ftypes, $image_index, 1;
          splice @list_of_image_fsizes, $image_index, 1;
          splice @list_of_image_exif, $image_index, 1;
          splice @list_of_image_dates, $image_index, 1;
          splice @list_of_image_times, $image_index, 1;
          splice @list_of_image_widths, $image_index, 1;
          splice @list_of_image_heights, $image_index, 1;
          splice @list_of_exif_flashused, $image_index, 1;
          splice @list_of_exif_isoequiv, $image_index, 1;
          splice @list_of_exif_cameravendor, $image_index, 1;
          splice @list_of_exif_cameramodel, $image_index, 1;
          splice @list_of_exif_focallength, $image_index, 1;
          splice @list_of_exif_exposuretime, $image_index, 1;
          splice @list_of_exif_aperture, $image_index, 1;
          splice @list_of_exif_exposurebias, $image_index, 1;
          splice @list_of_exif_meteringmode, $image_index, 1;
          splice @list_of_exif_exposuremode, $image_index, 1;
          splice @list_of_geometry_ratio, $image_index, 1;
          splice @list_of_image_captions, $image_index, 1;
          splice @list_of_alt_texts, $image_index, 1;
          splice @list_of_image_imprints, $image_index, 1;
          splice @list_of_rot_infos, $image_index, 1;
          splice @list_of_del_markers, $image_index, 1;
          splice @list_of_top_select_markers, $image_index, 1;
          splice @list_of_section_styles, $image_index, 1;
          splice @list_of_section_titles, $image_index, 1;
	  # we have to correct sub_text_info fields:
          SubTextInfoCorrectionLoop :
              for(my $img_idx=0; $img_idx <= $#list_of_sub_text_infos_index; $img_idx++) {
                if ($list_of_sub_text_infos_index[$img_idx] > $image_index) {
                  $list_of_sub_text_infos_index[$img_idx]--;
                }
              }
          # it is more complicated for the text infos themselves;
          # we may have to modify the index numbers of other images and remove
          # any text info entries related to the deleted image:
          # 1. check if current page contains text infos:
          if ($list_of_has_text_infos[$image_index] == 1) {
            # loop over array and remove all associated elements:
            my $img_info_count = $#list_of_text_infos;
            my $img_idx=0;
            ImageTextInfoDeletionLoop :
                while ($img_idx <= $img_info_count) {
                  if ($list_of_text_infos_index[$img_idx] == $image_index) {
                    splice @list_of_text_infos_index, $img_idx, 1;
                    splice @list_of_text_infos, $img_idx, 1;
                    # update text info count
                    $img_info_count = $#list_of_text_infos;
                    next;
                  }
                  $img_idx++;
                }
          }
          # 2. correct index number of all index values larger than
          # the current index of the deleted image by -1 (the actual
          # index of all images behind current image is decremented)
          ImageTextInfoCorrectionLoop :
              for(my $img_idx=0; $img_idx <= $#list_of_text_infos_index; $img_idx++) {
                if ($list_of_text_infos_index[$img_idx] > $image_index) {
                  $list_of_text_infos_index[$img_idx]--;
                }
              }
          # 3. finally remove current entry:
          splice @list_of_has_text_infos, $image_index, 1;
          # update image count:
          $image_count = $#list_of_images;
        } else {
          # count to next image:
          $image_index++;
        }
      }
  if ($globalLogDeletedImages == 1) {
     close(LOGDELFILE);
  }
}

###############################################################################
# The rename part: basically we rename the original images here
#                  however, we also check if existing, already created, images
#                  can be renamed as well.
###############################################################################
if ($optionRename == 1) {

  print_info_message("Renaming image files (if required) ...");

  # duplicate name arrays:
  my @old_list_of_images = @list_of_images;
  my @old_list_of_button_names = @list_of_button_names;
  my @old_list_of_thumbnail_names = @list_of_thumbnail_names;
  my @old_list_of_websize_names = @list_of_websize_names;
  my @old_list_of_fullsize_names = @list_of_fullsize_names;
  my @old_list_of_html_names = @list_of_html_names;
  my @old_list_of_info_names = @list_of_info_names;
  my @old_list_of_wrapper_names = @list_of_wrapper_names;
  my @old_list_of_image_fprefixes = @list_of_image_fprefixes;
  # temporary variables for new names:
  my $new_image_name = "";
  my $new_file_ext   = "";
  my $new_button_image = "";
  my $new_thumbnail_image = "";
  my $new_websize_image = "";
  my $new_fullsize_image = "";
  my $new_html_name = "";
  my $new_info_name = "";
  my $new_wrapper_name = "";
  my $image_path = "";
  my $copy_file_name = "";
  # modifications of file name prefixes:
  # we do not need to re-create the prefix in scan mode:
  if (($optionPrefix == 1) && ($optionScan == 0)) {
    print_info_message("New filename prefixes are assigned to all image files ...");
    # create new prefix as given by selection file order:
    my @all_chars = qw(a b c d e f g h i j k l m n o p q r s t u v w x y z);
    my $cidx1     = 0;
    my $cidx2     = 0;
    my $current_prefix = "";
    for (my $idx=0; $idx <= $#list_of_images; $idx++) { 
      $current_prefix = $all_chars[$cidx2] . $all_chars[$cidx1] . "_";
      $cidx1++;
      if ($cidx1 > $#all_chars) {
	$cidx1 = 0;
	$cidx2++;
	if ($cidx2 > $#all_chars) {
	  $cidx2 = 0;
	}
      }
      $list_of_image_fprefixes[$idx] = $current_prefix;
    }
  }
  # th actual rename loop:
  ImageRenameLoop :
      for (my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
        # temporary variable which stores info about update requirements per image
        # (also for previous and next image ...):
        my $update_value_prev = $constantUpdateNone;
        my $update_value      = $constantUpdateNone;
        my $update_value_next = $constantUpdateNone;
        $new_image_name = create_auto_file_name($list_of_image_names[$image_index]);
        $new_file_ext   = $list_of_image_fexts[$image_index];
        # change file extension to lower case chars if desired:
        if ($optionExt == 1) {
          $new_file_ext =~ tr/A-Z/a-z/;
        }
        $list_of_images[$image_index] =
          $list_of_image_fpaths[$image_index] .
            $list_of_image_fprefixes[$image_index] .
              $new_image_name . "." . $new_file_ext;
	# we do not rename if we are only an IMAGE_LINK entry:
	if ($list_of_image_links[$image_index] ne "") {
	  # change name back to original name:
	  $list_of_images[$image_index] = $old_list_of_images[$image_index];
	}
        my $is_ok = 0;
        # now do the renaming (but only if strings really differ): 
        if ($list_of_images[$image_index] ne $old_list_of_images[$image_index]) {
          print_info_message("Renaming image $old_list_of_images[$image_index] into $list_of_images[$image_index] ...");
          if (! -e $list_of_images[$image_index]) {
            # we rename only if target name does not exist:
            $is_ok = rename $old_list_of_images[$image_index], $list_of_images[$image_index];
            if ($is_ok != 1) {
              print_error_message("Could not rename image $old_list_of_images[$image_index].");
            }
          } else {
            print_error_message("Image renaming failed: $list_of_images[$image_index] already exists.");
          }
          if ($is_ok != 1) {
            # if renaming fails we return to the original name:
            $list_of_images[$image_index] = $old_list_of_images[$image_index];
          } else {
            # ok, update other parameters:
            $list_of_image_fnames[$image_index] =
              $list_of_image_fprefixes[$image_index] . $new_image_name;
            $list_of_image_fexts[$image_index]  = $new_file_ext;
            # we need at least to update html files for the current file as well
            # as for the files associated to the previous and next image:
            $update_value_prev = $update_value_prev | $constantUpdateHtml;
            $update_value      = $update_value      | $constantUpdateHtml;
            $update_value_next = $update_value_next | $constantUpdateHtml;
            $globalUpdateIsRequired = 1;
            # otherwise we check for other existing files which need also to be renamed:
            # note that the files are only renamed but may not contain the correct
            # content if they are not forced to be regenerated:
            $new_button_image    =
              $globalButtonDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalButtonSuffix . $constantJPEGExtension;
            $new_thumbnail_image =
              $globalThumbnailDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalThumbnailSuffix . $constantJPEGExtension;
            $new_websize_image  =
              $globalWebSizeDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalWebSizeSuffix . $constantJPEGExtension;
            $new_fullsize_image  =
              $globalFullSizeDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalFullSizeSuffix . $constantJPEGExtension;
            $new_html_name      =
              $globalHtmlDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalHtmlImgSuffix . "." . $globalHtmlExtension;
            $new_info_name      =
              $globalHtmlDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalHtmlInfoSuffix . "." . $globalHtmlExtension;
            $new_wrapper_name      =
              $globalHtmlDir . "/" . $list_of_image_fprefixes[$image_index] .
                $new_image_name . $list_of_image_uniquifiers[$image_index] .
		  $globalHtmlWrapperSuffix . "." . $globalHtmlExtension;
            # copy new names into arrays:
            $list_of_button_names[$image_index]    = $new_button_image;
            $list_of_thumbnail_names[$image_index] = $new_thumbnail_image;
            $list_of_websize_names[$image_index]   = $new_websize_image;
            $list_of_fullsize_names[$image_index]  = $new_fullsize_image;
            $list_of_html_names[$image_index]      = $new_html_name;
            $list_of_info_names[$image_index]      = $new_info_name;
            $list_of_wrapper_names[$image_index]   = $new_wrapper_name;
            # create full path names:
            $image_path = $list_of_image_fpaths[$image_index];
            # generated image path must change for web ready pages:
            if ($globalWebReady == 1) {
              $image_path = "$globalTargetDir";
            }
            # create final pathname:
            $new_button_image    = $image_path . $new_button_image;
            $new_thumbnail_image = $image_path . $new_thumbnail_image;
            $new_websize_image   = $image_path . $new_websize_image;
            $new_fullsize_image  = $image_path . $new_fullsize_image;
            if ($globalWebReady == 1) {
              $new_html_name    = $image_path . $new_html_name;
              $new_info_name    = $image_path . $new_info_name;
              $new_wrapper_name = $image_path . $new_wrapper_name;
            }
            # now we try to rename also the buttons, thumbnails etc.:
            #   (note that if we fail the new name is retained as the original image
            #    has already been renamed.)
            #
            # rename button image:
            if (-e $image_path . $old_list_of_button_names[$image_index]) {
              if (! -e $new_button_image) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_button_names[$image_index], $new_button_image;
                if ($is_ok != 1) {
                  print_error_message("Could not rename button $old_list_of_button_names[$image_index].");
                }
              } else {
                print_error_message("Button renaming failed: $list_of_button_names[$image_index] already exists.");
              }
            }
            # rename thumbnail image:
            if (-e $image_path . $old_list_of_thumbnail_names[$image_index]) {
              if (! -e $new_thumbnail_image) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_thumbnail_names[$image_index], $new_thumbnail_image;
                if ($is_ok != 1) {
                  print_error_message("Could not rename thumbnail $old_list_of_thumbnail_names[$image_index].");
                }
              } else {
                print_error_message("Thumbnail renaming failed: $list_of_thumbnail_names[$image_index] already exists.");
              }
            }
            # rename websize image:
            if (-e $image_path . $old_list_of_websize_names[$image_index]) {
              if (! -e $new_websize_image) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_websize_names[$image_index], $new_websize_image;
                if ($is_ok != 1) {
                  print_error_message("Could not rename websize image $old_list_of_websize_names[$image_index].");
                }
              } else {
                print_error_message("Websize image renaming failed: $list_of_websize_names[$image_index] already exists.");
              }
            }
	    if ($globalFullSizeEnable == 1) {
	      # rename fullsize image (if enabled):
	      if (-e $image_path . $old_list_of_fullsize_names[$image_index]) {
		if (! -e $new_fullsize_image) {
		  # we rename only if target name does not exist:
		  $is_ok = rename $image_path . $old_list_of_fullsize_names[$image_index], $new_fullsize_image;
		  if ($is_ok != 1) {
		    print_error_message("Could not rename fullsize image $old_list_of_fullsize_names[$image_index].");
		  }
		} else {
		  print_error_message("Fullsize image renaming failed: $list_of_fullsize_names[$image_index] already exists.");
		}
	      }
            }
            # the html pages path may be different from the images path:
            if ($globalWebReady == 1) {
              $image_path = "$globalTargetDir";
            } else {
              $image_path = "";
            }
            # rename image html file:
            if (-e $image_path . $old_list_of_html_names[$image_index]) {
              if (! -e $new_html_name) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_html_names[$image_index],  $new_html_name;
                if ($is_ok != 1) {
                  print_error_message("Could not rename image html page $old_list_of_html_names[$image_index].");
                }
              } else {
                print_error_message("Image html page renaming failed: $list_of_html_names[$image_index] already exists.");
              }
            }
            # rename image info html file:
            if (-e $image_path . $old_list_of_info_names[$image_index]) {
              if (! -e $new_info_name) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_info_names[$image_index],  $new_info_name;
                if ($is_ok != 1) {
                  print_error_message("Could not rename image info html page $old_list_of_info_names[$image_index].");
                }
              } else {
                print_error_message("Image info html page renaming failed: $list_of_info_names[$image_index] already exists.");
              }
            }
            # rename image wrapper html file:
            if (-e $image_path . $old_list_of_wrapper_names[$image_index]) {
              if (! -e $new_wrapper_name) {
                # we rename only if target name does not exist:
                $is_ok = rename $image_path . $old_list_of_wrapper_names[$image_index],  $new_wrapper_name;
                if ($is_ok != 1) {
                  print_error_message("Could not rename image info html page $old_list_of_wrapper_names[$image_index].");
                }
              } else {
                print_error_message("Image info html page renaming failed: $list_of_wrapper_names[$image_index] already exists.");
              }
            }
          }
        }
        # if the image exists we check for in-place rotation in rename mode:
        if (-e $list_of_images[$image_index]) {
          # FINALLY: create the rotated image and set the rotation value to 0 degrees:
          #   copy the original file to *.orig.* first; if this works, rotate the original
          #   image and replace the original file (only if jpegtran and jhead are available!)
          my $rotation_angle  = $list_of_rot_infos[$image_index];
          if (($rotation_angle != 0) && ($globalUseJpegtranJhead == 1)) {
            $copy_file_name =
              $list_of_image_fpaths[$image_index] .
                $list_of_image_fnames[$image_index] . ".orig." . $new_file_ext;
            # rename button image:
            if (! -e $copy_file_name) {
              # we copy only if target name does not exist:
              $is_ok = copy($list_of_images[$image_index], $copy_file_name);
              if ($is_ok != 1) {
                print_error_message("Could not copy original image $list_of_images[$image_index].");
              } else {
                # ok, we have a backup copy, rotate the original image:
                print_info_message("Rotating image $list_of_images[$image_index] (original saved to $copy_file_name).");
                process_original_image($copy_file_name,
                                       $list_of_images[$image_index],
                                       $rotation_angle);
                # set the rotation angle back to 0 degrees:
                $list_of_rot_infos[$image_index] = 0;
                # we need to update all images and html for the current file and only
                # html for previous and next file:
                $update_value_prev = $update_value_prev | $constantUpdateHtml;
                $update_value      = $update_value      | $constantUpdateHtmlAndImages;
                $update_value_next = $update_value_next | $constantUpdateHtml;
                $globalUpdateIsRequired = 1;
              }
            } else {
              print_error_message("Copy target: $copy_file_name already exists.");
            }
          }
        }
        # update the update lists ;-)
        if ($image_index > 0) {
          $list_of_update_info[$image_index - 1] =
            $list_of_update_info[$image_index - 1] | $update_value_prev;
        }
        $list_of_update_info[$image_index] =
          $list_of_update_info[$image_index] | $update_value;
        if ($image_index < $#list_of_images) {
          $list_of_update_info[$image_index + 1] =
            $list_of_update_info[$image_index + 1] | $update_value_next;
        }
      }
}

###############################################################################
# In scan and rename mode write a new selection file:
# (also if write is explicitly requested with -write option)
###############################################################################
if (($optionScan == 1) || ($optionRename == 1) ||
    ($optionWrite == 1)|| ($globalImagesAreToBeDeleted == 1)) {
  # write selection file:
  print_info_message("Writing back selection list and configuration file $optionSelectionFile ...");
  if ((-e $optionSelectionFile) && ((! -w $optionSelectionFile) || (! -f $optionSelectionFile))) {
    exit_perl_script("file " . $optionSelectionFile . " exists and is not writeable.");
  }
  open(SELFILE, ">" . $optionSelectionFile);
  select SELFILE;
  if ($#list_of_file_comments < 0) {
    print "###########################################################################\n";
    print "# \n";
    print "# Automatically generated image selection file\n";
    print "# \n";
    print "#    Created using: $ScriptName $ScriptVersion\n";
    print "#    Creation date: " . get_todays_date() . "\n";
    print "# \n";
    print "###########################################################################\n";
  } else {
    PrintInitialComments : for(my $idx=0; $idx<=$#list_of_file_comments; $idx++) {
      print "$list_of_file_comments[$idx]\n";
    }
  }
  print "### end of user comments ###\n";
  print "\n";
  print "# general information:\n";
  print "TITLE=$globalTitle\n";
  print "AUTHOR=$globalAuthor\n";
  print "EMAIL=$globalEmail\n";
  print "COPYRIGHT=$globalCopyright\n";
  print "\n";
  print "# html basic data:\n";
  print "WEB_READY  = $globalWebReady\n";
  print "TARGET_DIR = $globalTargetDir\n";
  print "LEVEL_UP   = $globalLevelUp\n";
  print "# name of iview selection file to be written:\n";
  print "IVIEW_SELECTION_FILE = $globalIviewFileName\n";
  print "# indicates if original image should be linked:\n";
  print "LINK_ORIGINAL_IMAGE = $globalLinkOrigImage\n";
  print "\n";
  print "# usage of \"jpegtran\" and \"jhead\" is heavily recommended:\n";
  print "USE_JPEGTRAN_JHEAD = $globalUseJpegtranJhead\n";
  print "\n";
  print "# html colors:\n";
  print "HTML_BG_COLOR    = $globalBgColor\n";
  print "HTML_TEXT_COLOR  = $globalTextColor\n";
  print "HTML_LINK_COLOR  = $globalLinkColor\n";
  print "HTML_VLINK_COLOR = $globalVLinkColor\n";
  print "HTML_ALINK_COLOR = $globalALinkColor\n";
  print "\n";
  print "# image colors:\n";
  print "IMG_BORDER_COLOR = $globalBorderColor\n";
  print "IMG_SHADOW_COLOR = $globalDropShadowColor\n";
  print "\n";
  print "# image parameters:\n";
  print "IMG_BORDER_WIDTH         = $globalImageBorderWidth\n";
  print "IMG_SHADOW_ENABLE        = $globalMakeDropShadow\n";
  print "IMG_SHADOW_OFFSET        = $globalDropShadowOffset\n";
  print "IMG_SHADOW_BLUR          = $globalDropShadowBlur\n";
  print "THUMBNAIL_ROTATION_ANGLE = $globalThumbnailRotationAngle\n";
  print "THUMBNAIL_RANDOM_ROTATE  = $globalThumbnailRandomRotate\n";
  print "WEBSIZE_ROTATION_ANGLE   = $globalWebsizeRotationAngle\n";
  print "WEBSIZE_RANDOM_ROTATE    = $globalWebsizeRandomRotate\n";
  print "IMG_JPEG_QUALITY         = $globalImageQualityFactor\n";
  print "IMG_JPEG_PROGRESSIVE     = $globalProgressiveJpeg\n";
  print "IMG_ADD_JPG_INFO_PAGE    = $globalAddJpgInfoPage\n";
  if ($globalImageImprintDefault eq "") {
    print "# IMG_IMPRINT_DEFAULT    = Default text imprinted on Websize image.\n";
  } else {
    print "IMG_IMPRINT_DEFAULT      = $globalImageImprintDefault\n";
  }
  print "\n";
  print "# thumbnail pages parameters:\n";
  print "THUMBNAILS_PER_ROW  = $globalImagesPerRow\n";
  print "THUMBNAILS_MAX_ROWS = $globalMaxRows\n";
  print "\n";
  print "# image sizes, file suffixes and subdirectories for generated images:\n";
  print "BUTTON_SIZE      = $globalButtonSize\n";
  print "THUMBNAIL_SIZE   = $globalThumbnailSize\n";
  print "WEBSIZE_SIZE     = $globalWebSize\n";
  print "ALWAYS_WEBSIZE   = $globalAlwaysWebSize\n";
  print "BUTTON_SUFFIX    = $globalButtonSuffix\n";
  print "THUMBNAIL_SUFFIX = $globalThumbnailSuffix\n";
  print "WEBSIZE_SUFFIX   = $globalWebSizeSuffix\n";
  print "BUTTON_DIR       = $globalButtonDir\n";
  print "THUMBNAIL_DIR    = $globalThumbnailDir\n";
  print "WEBSIZE_DIR      = $globalWebSizeDir\n";
  print "\n";
  if ($globalFullSizeEnable == 1) {
    print "FULLSIZE_ENABLE  = $globalFullSizeEnable\n";
    print "FULLSIZE_SIZE    = $globalFullSize\n";
    print "FULLSIZE_SUFFIX  = $globalFullSizeSuffix\n";
    print "FULLSIZE_DIR     = $globalFullSizeDir\n";
  }
  print "\n";
  print "# general html pages parameters:\n";
  print "HTML_INDEX_NAME     = $globalHtmlPrefix\n";
  print "HTML_IMAGE_SUFFIX   = $globalHtmlImgSuffix\n";
  print "HTML_INFO_SUFFIX    = $globalHtmlInfoSuffix\n";
  print "HTML_WRAPPER_SUFFIX = $globalHtmlWrapperSuffix\n";
  print "HTML_FILE_EXTENSION = $globalHtmlExtension\n";
  print "HTML_IMAGE_DIR      = $globalHtmlDir\n";
  print "HTML_NO_PART_NUMBER = $globalNoPartNumber\n";
  print "HTML_NO_PART_ONE    = $globalNoPartOne\n";
  print "HTML_NO_TOOL_HINT   = $globalNoToolHint\n";
  print "HTML_TEXT_BACK      = $globalTextBack\n";
  print "HTML_CSS_ENABLE     = $globalCssEnable\n";
  print "HTML_CSS_FILENAME   = $globalStylesheetName\n";
  print "HTML_TEXT_ALIGN     = $globalTextAlign\n";
  print "HTML_TOP_TEXT_ALIGN = $globalTopTextAlign\n";
  print "HTML_SUB_TEXT_ALIGN = $globalSubTextAlign\n";
  print "HTML_IMG_TEXT_ALIGN = $globalImgTextAlign\n";
  print "HTML_IMG_MAKE_DENSE = $globalImgHtmlDense\n";
  print "HTML_IMG_CREATE_ALWAYS  = $globalImgHtmlCreateAlways\n";
  print "\n";
  print "# html detailed formatting parameters:\n";
  print "HTML_TEXTCELL_HEIGHT    = $globalTextHeight\n";
  print "HTML_THUMBNAIL_SPACING  = $globalThumbnailSpacing\n";
  print "HTML_IMAGE_SPACING      = $globalImageSpacing\n";
  print "HTML_THUMBNAIL_DISTANCE = $globalThumbnailDistance\n";
  print "HTML_IMAGE_DISTANCE     = $globalImageDistance\n";
  print "\n";
  print "# parameters for automatic name extraction from file name:\n";
  print "IMG_NAME_NO_ALPHA_PREFIX    = $globalRemoveAlphaPrefix\n";
  print "IMG_NAME_NO_NUMBER_PREFIX   = $globalRemoveNumberPrefix\n";
  print "IMG_NAME_NO_NUMBER_SUFFIX   = $globalSeparateNumericSuffix\n";
  print "IMG_NAME_REPLACE_UNDERSCORE = $globalReplaceUnderScores\n";
  print "IMG_NAME_REPLACE_DASH       = $globalReplaceMinusSign\n";
  print "IMG_NAME_BLANK_BEFORE_CAPS  = $globalInsertBlankBeforeCaps\n";
  print "\n";
  print "# html top page parameters:\n";
  print "HTML_CREATE_TOP   = $globalCreateTop\n";
  print "HTML_TOP_CAPTION  = $globalTopCaption\n";
  print "HTML_TOP_BACK     = $globalTopBack\n";
  if ($#list_of_top_text_infos >= 0) {
    PrintTopText : for(my $idx=0; $idx <= $#list_of_top_text_infos; $idx++) {
      print "HTML_TOP_TEXT   = $list_of_top_text_infos[$idx]\n";
    }
  } else {
    print "# HTML_TOP_TEXT     = Add additional text to be placed on the top\n";
    print "# HTML_TOP_TEXT     = thumbnail page.\n";
  }
  # address tag text infos (if existing):
  if ($#list_of_address_text_infos >= 0) {
    PrintAddressText : for(my $idx=0; $idx <= $#list_of_address_text_infos; $idx++) {
      print "HTML_ADDRESS_TEXT = $list_of_address_text_infos[$idx]\n";
    }
  } else {
    print "# HTML_ADDRESS_TEXT = Add additional text to be placed inside the address tag\n";
    print "# HTML_ADDRESS_TEXT = on each html page.\n";
  }
  print "\n";
  print "###########################################################################\n";
  print "# The IMAGE section:\n";
  print "###########################################################################\n";
  print "# Example:\n";
  print "#\n";
  print "# SUBTITLE      = A subtitle which separates images belonging to a group\n";
  print "# HTML_SUB_TEXT = Additional text (multiple lines) that is printed on the \n";
  print "# HTML_SUB_TEXT = thumbnail page belonging to that subtitle.\n";
  print "# HTML_SUB_TEXT = <i>Simple</i> &quot;HTML&quot; tags may be included.\n";
  print "#\n";
  print "# IMAGE_NAME = My Image Name\n";
  print "# IMAGE_CAPT = This is the caption text printed next to the image.\n";
  print "# IMAGE_ALT  = Alternative text for the ALT image html tag.\n";
  print "# IMAGE_IMPR = This text is imprinted into the websize image.\n";
  print "# IMAGE_ROT  = 90 (indicates if image needs to be rotated first)\n";
  print "# IMAGE_SEL  = 1  (indicates if image is selection of subtitle group)\n";
  print "# IMAGE_EXIF = 0  (a zero indicates that no EXIF info is available)\n";
  print "# IMAGE_INFO = 1  (indicates if image info file is written)\n";
  print "# IMAGE_TEXT = This is additional descriptive text that is printed onto\n";
  print "# IMAGE_TEXT = the HTML page that displays the websize image.\n";
  print "# IMAGE_PATH = ./images/aa_MyImageName.jpg\n";
  print "#\n";
  print "###########################################################################\n";
  print "\n";
  my $current_section_title = "";
  LoopOverImages : for(my $image_index=0;$image_index<=$#list_of_images;$image_index++) {
    # check if we should print a new subtitle:
    if ($list_of_section_titles[$image_index] ne $current_section_title) {
      $current_section_title = $list_of_section_titles[$image_index];
      if ($list_of_section_styles[$image_index] != $globalSubPagesNoText) {
	print "SUB_PAGES_NO_TEXT = $list_of_section_styles[$image_index]\n";
      }
      print "SUBTITLE      = $current_section_title\n";
      # check for more subtitle comments
    PrintSubTextInfos: for(my $idx=0;$idx<=$#list_of_sub_text_infos_index;$idx++) {
        if ($list_of_sub_text_infos_index[$idx] == $image_index) {
          print "HTML_SUB_TEXT = $list_of_sub_text_infos[$idx]\n";
        }
      }
      print "\n";
    }
    print "IMAGE_NAME = $list_of_image_names[$image_index]\n";
    print "IMAGE_CAPT = $list_of_image_captions[$image_index]\n";
    if (($list_of_alt_texts[$image_index] eq "") ||
        ($list_of_alt_texts[$image_index] eq $list_of_image_names[$image_index])) {
      print "# IMAGE_ALT  = ... (IMAGE_NAME by default)\n";
    } else {
      print "IMAGE_ALT  = $list_of_alt_texts[$image_index]\n";
    }
    if ($list_of_image_imprints[$image_index] eq "") {
      print "# IMAGE_IMPR = ... (empty by default)\n";
    } else {
      print "IMAGE_IMPR = $list_of_image_imprints[$image_index]\n";
    }
    print "IMAGE_ROT  = $list_of_rot_infos[$image_index]\n";
    print "IMAGE_SEL  = $list_of_top_select_markers[$image_index]\n";
    if ($list_of_image_exif[$image_index] == 0) {
      print "IMAGE_EXIF = 0\n"; # we are only interested in zero values ;-)
    }
    if ($list_of_info_page_markers[$image_index] != $globalAddJpgInfoPage) {
      print "IMAGE_INFO = $list_of_info_page_markers[$image_index]\n";
    } else {
      print "# IMAGE_INFO = 0|1\n";
    }
    if ($list_of_has_text_infos[$image_index] == 0) {
      print "# IMAGE_TEXT = ...\n";
      print "# IMAGE_TEXT = ...\n";
    } else {
      PrintImageTextInfos : for (my $idx=0;$idx <= $#list_of_text_infos_index; $idx++) {
        if ($list_of_text_infos_index[$idx] == $image_index) {
          print "IMAGE_TEXT = $list_of_text_infos[$idx]\n";
        }
      }
    }
    if ($list_of_image_links[$image_index] ne "") {
      print "IMAGE_LINK = $list_of_image_links[$image_index]\n";
    }
    print "IMAGE_PATH = $list_of_images[$image_index]\n";
    print "\n";
  }
  close (SELFILE);
}

###############################################################################
# replace image parameters inside Image caption and descriptive text
# and ALT text:
###############################################################################
# we look for parameters in the form "%KEYWORD%" and replace them with the
# actual image property. The following parameters are supported:
# %IMGDATE%      - the date and time when the image was taken,
# %IMGTIME%        as extracted from the JPEG info
# %IMGFSIZE%     - the image file size (with M/k/Bytes unit appended)
# %IMGWIDTH%     - the image width in pixels
# %IMGHEIGHT%    - the image height in pixels
# %IMGTYPE%      - jpg, png, or gif images are possible properties
# %IMGFNAME%     - the image file name
# %IMGFLASH%     - flash used for image
# %IMGISOEQUIV%  - ISO equivalent (100, 200, 400, ...)
# %IMGCAMVENDOR% - Camera Vendor
# %IMGCAMMODEL%  - Camera Model
# %IMGFOCLEN%    - Focal length
# %IMGEXPOSURE%  - Exposure time
# %IMGAPERTURE%  - Aperture
# %IMGEXPBIAS%   - Exposure Bias
# %IMGMETERMODE% - Metering mode
# %IMGEXPMODE%   - Exposure mode
# %IMGRATIO%     - Image ratio (3:2, 4:3, or non-standard)

ImageTextReplacementLoop :
  for (my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
    # replace caption:
    $list_of_image_captions[$image_index] =
      replace_img_param_variables($list_of_image_captions[$image_index],
                                  $image_index);
    # replace ALT text:
    $list_of_alt_texts[$image_index] =
      replace_img_param_variables($list_of_alt_texts[$image_index],
                                  $image_index);
    # replace image text if existing (all lines):
    if ($list_of_has_text_infos[$image_index] == 1) {
      SearchTextInfo : for(my $txtidx=0; $txtidx <= $#list_of_text_infos; $txtidx++) {
        if ($list_of_text_infos_index[$txtidx] == $image_index) {
          $list_of_text_infos[$txtidx] =
            replace_img_param_variables($list_of_text_infos[$txtidx], $image_index);
        }
      }
    }
  }

###############################################################################
# replace doc parameters inside top caption, top descriptive text,
# and address text:
###############################################################################
# we look for parameters in the form "%KEYWORD%" and replace them with the
# actual document property. The following parameters are supported:
# %DOCAUTHOR%     - the html author name
# %DOCEMAIL%      - the html author email
# %DOCDATE%       - the current date (when the script is executed)
# %DOCCOPY%       - the html document copright clause

# replace top caption info variables if existing:
$globalTopCaption = replace_doc_param_variables($globalTopCaption);
# replace address text info variables if existing (all lines):
if ($#list_of_address_text_infos >= 0) {
  SearchAddressTextInfo : for(my $txtidx=0; $txtidx <= $#list_of_address_text_infos; $txtidx++) {
    $list_of_address_text_infos[$txtidx] =
      replace_doc_param_variables($list_of_address_text_infos[$txtidx]);
  }
}
# replace top text info variables if existing (all lines):
if ($#list_of_top_text_infos >= 0) {
  SearchTopTextInfo : for(my $txtidx=0; $txtidx <= $#list_of_top_text_infos; $txtidx++) {
    $list_of_top_text_infos[$txtidx] =
      replace_doc_param_variables($list_of_top_text_infos[$txtidx]);
  }
}


###############################################################################
# process HTML Files:
###############################################################################

if (($optionHtml == 1) || (($globalUpdateIsRequired == 1) && ($optionScan == 0))) {

  print_info_message("Processing html files ...");

  # *** Size calculations for an HTML table with thumbnails:
  # max number of images per page:
  my $globalMaxImages             = $globalImagesPerRow * $globalMaxRows;
  # the width and height values for a thumbnail table:
  my $thumbnailSpacing            = $globalThumbnailSpacing;
  my $thumbnailOffset             = $globalThumbnailDistance;
  my $thumbnailColumnWidth        = $globalThumbnailSize + $thumbnailOffset;
  my $thumbnailRowHeight          = $globalThumbnailSize + $thumbnailOffset;
  my $thumbnailTableWidth         = $thumbnailColumnWidth * $globalImagesPerRow +
    $thumbnailOffset;
  # header and trailer-tables must be adapted to multiple columns (3..8):
  # required when we combine all but the outer two columns:
  my $thumbnailMiddleColumn       = $globalImagesPerRow - 2;
  my $thumbnailMiddleWidth        = $thumbnailMiddleColumn * $thumbnailColumnWidth;

  # *** Size calculations for an HTML table with the basic image:
  # the width and height values for an Image table:
  my $imgtableSpacing             = $globalImageSpacing;
  my $imgtableOffset              = $globalImageDistance;
  my $imgtableButtonWidth         = $globalButtonSize + 2 * $imgtableOffset;
  my $imgtableTableWidth          = $globalWebSize + $imgtableOffset * 2 + 5 * $imgtableSpacing;
  my $imgtableNextPrevWidth       = int( ($imgtableTableWidth -
                                          2 * $imgtableButtonWidth) / 3);
  my $imgtableBackWidth           = $imgtableTableWidth - 2 * $imgtableNextPrevWidth -
    2 * $imgtableButtonWidth;
  # check if minimum widths of text fields are met:
  if ($imgtableNextPrevWidth < $constantSmallTextColumnWidth) {
    $imgtableNextPrevWidth = $constantSmallTextColumnWidth;
  }
  if ($imgtableBackWidth < $constantSmallTextColumnWidth) {
    $imgtableBackWidth     = $constantSmallTextColumnWidth;
  }
  # recalculate width of image table:
  $imgtableTableWidth      = $imgtableBackWidth + 2 * $imgtableNextPrevWidth +
    2 * $imgtableButtonWidth + $imgtableOffset * 2 +
      5 * $imgtableSpacing;
  # *** Size calculations for an HTML table with the info pages for image:
  my $infotableSpacing        = $globalImageSpacing;
  my $infotableOffset         = $globalImageDistance;
  my $infotableTableWidth     = $imgtableTableWidth;
  my $infotableThumbnailWidth = $constantNormalTextColumnWidth;
  my $infotableTextWidth      = ($infotableTableWidth - $constantNormalTextColumnWidth);

  # file number index (as we might need multiple thumbnail pages)
  my $filenumber = 0;
  # current image index in list (used for image and html processing):
  my $image_index = 0;

  # other output filenames:
  my $cur_out_file_name  = "";
  my $prev_out_file_name = "";
  my $next_out_file_name = $globalHtmlPrefix . "_" . $filenumber .
    "." . $globalHtmlExtension;

  # variables for previous and next image linking:
  my $prev_img_file_name   = "";
  my $next_img_file_name   = "";
  my $prev_img_button_name = "";
  my $next_img_button_name = "";
  my $prev_img_button_text = "";
  my $next_img_button_text = "";
  my $prev_img_link        = "";
  my $next_img_link        = "";

  # indicates if we still have image html files to process:
  my $no_more_files = 0;

  # number of images still to process:
  my $number_of_images_left = $#list_of_images + 1;

  # create top thumbnail view file on demand:
  my $top_file_name             = $globalHtmlPrefix . "." . $globalHtmlExtension;
  my $top_images_processed      = 0;
  my $image_is_selected_for_top = 0;

  # temporary variables:
  my $image_text       = "";
  my $image_link       = "";
  my $image_style      = 0;
  my $image_alt_text   = "";
  my $image_file_ext   = "";
  my $OriginalImage    = "";
  my $RotationAngle    = "";
  my $RotatedImage     = "";
  my $ThumbnailImage   = "";
  my $ButtonImage      = "";
  my $WebSizeImage     = "";
  my $FullSizeImage    = "";
  my $CssStyleSheet    = "";

  ###############################################################################
  # open global CSS stylesheet file:
  ###############################################################################
  # first check if CSS styleshet file already exists:
  my $CssFileExists = 0;
  if ($globalWebReady == 1) {
    $CssStyleSheet = $globalTargetDir . $globalStylesheetName . $constantCSSExtension;
    if (-e $CssStyleSheet) {
      $CssFileExists = 1;
    }
  } else {
    $CssStyleSheet = $globalStylesheetName . $constantCSSExtension;
    if (-e $CssStyleSheet) {
      $CssFileExists = 1;
    }
  }
  if ($globalCssEnable == 1) {
    if ( $CssFileExists == 0) {
      # if CSS file doesn't exist, check if we can copy a default CSS styleshet file:
      my $defaultStyleSheet =  $installDir . "/" . 
	                       $constantStylesheetName . $constantCSSExtension;
      if (-e $defaultStyleSheet) {
	print_info_message("Copying default CSS Stylesheet file $defaultStyleSheet to $CssStyleSheet.");
        my $is_ok = copy($defaultStyleSheet, $CssStyleSheet);
        if ($is_ok != 1) {
          print_error_message("Could not copy default style sheet file " .
                              $defaultStyleSheet);
        }
      } else {
        # else write a new CSS styleshet file from scratch:
	print_info_message("Writing new CSS Stylesheet file $CssStyleSheet ...");
        open(CSSFILE,  ">" . $CssStyleSheet);
        select(CSSFILE);
        print "/* DATEI/FILE: $globalStylesheetName$constantCSSExtension */\n";
        print "\n";
        print "body { background-color:$globalBgColor; }\n";
        print "\n";
        print "h1,h2,h3,h4,p,ul,ol,li,div,td,th,address,blockquote,nobr,b,i {\n";
        print " font-family:Helvetica,Arial,sans-serif; }\n";
        print "\n";
        print "h1 { font-size:26px; margin-bottom:10px; }\n";
        print "\n";
        print "h2 { font-size:21px; margin-bottom:10px; }\n";
        print "\n";
        print "h2.sh2 { font-size:21px; }\n";
        print "\n";
        print "h3,h3.xmp,h3.xpl,h3.inf,h3.tip,h3.akt { font-size:18px; }\n";
        print "\n";
        print "h4 { font-size:16px; }\n";
        print "\n";
        print "p,ul,ol,li,div,td,th,blockquote { font-size:15px; }\n";
        print "\n";
        print "address { font-size:14px; }\n";
      }
    } else {
      print_warning_message("CSS Stylesheet file $CssStyleSheet already exists. Skipping ...");
    }
  }

  ###############################################################################
  # open global iview file:
  ###############################################################################
  if ($globalWebReady == 1) {
    if (-e "$globalTargetDir$globalIviewFileName") {
      print_warning_message("Iview selection file $globalTargetDir$globalIviewFileName already exists. Overwriting ...");
    }
    open(IVIEWFILE,  ">" . $globalTargetDir . $globalIviewFileName);
  } else {
    if (-e "$globalIviewFileName") {
      print_warning_message("Iview selection file $globalIviewFileName already exists. Overwriting ...");
    }
    open(IVIEWFILE,  ">" . $globalIviewFileName);
  }

  ###############################################################################
  # open global index file:
  ###############################################################################
  if ($globalCreateTop == 1) {
    if ($globalWebReady == 1) {
      open(TOPFILE, ">" . $globalTargetDir . $top_file_name);
    } else {
      open(TOPFILE, ">" . $top_file_name);
    }
    select(TOPFILE);
    # initialize HTML file:
    print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n";
    print "          \"http://www.w3.org/TR/html4/loose.dtd\">\n";
    print "<html>\n";
    print "<head>\n";
    print "   <meta name=\"generator\" content=\"$ScriptName $ScriptVersion\">\n";
    print "   <meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n";
    print "   <meta name=\"author\" content=\"$globalAuthor\">\n";
    print "   <meta name=\"description\" content=\"$globalTitle, top\">\n";
    print "   <title>$globalTitle, $optionSelectionFile, top</title>\n";
    if ($globalCssEnable == 1) {
      print "   <link rel=\"stylesheet\" type=\"text/css\" href=\"$globalStylesheetName$constantCSSExtension\">\n";
      print "</head>\n";
      print "\n";
      print "<body>\n";
    }
    else {
      print "</head>\n";
      print "\n";
      print "<body bgcolor=\"$globalBgColor\" text=\"$globalTextColor\" link=\"$globalLinkColor\" vlink=\"$globalVLinkColor\" alink=\"$globalALinkColor\">\n";
    }
    print "   <h2 align=$globalTextAlign>$globalTitle</h2>\n";
    print "\n";
    # a table with $globalImagesPerRow columns per row:
    print "   <table" . $globalTableAlignAttr . " cellspacing=$thumbnailSpacing cellpadding=0 width=\"$thumbnailTableWidth\" border=0 summary=\"lists all images in thumbnail size\">\n";
    print "      <tr>\n";
    print "         <td align=$globalTopTextAlign valign=middle colspan=$globalImagesPerRow>\n";
    if ($#list_of_top_text_infos >= 0) {
      PrintTopTextInfos : for(my $idx=0; $idx<=$#list_of_top_text_infos;$idx++) {
        print "            $list_of_top_text_infos[$idx]\n";
      }
    } else {
      print "            $stringChooseSubtitle\n";
    }
    print "         </td>\n";
    print "      </tr>\n";
    print "\n";
    print "      <tr>\n";
    print "         <td align=$globalTextAlign valign=middle colspan=$globalImagesPerRow height=$globalTextHeight>\n";
    if ($globalLevelUp eq "") {
      print "            &nbsp;\n";
    } else {
      print "            <a href=\"$globalLevelUp\">$globalTopBack</a>\n";
    }
    print "         </td>\n";
    print "      </tr>\n";
  }

  ###############################################################################
  # create detailed thumbnail view files
  ###############################################################################
  FileLoop : while ($no_more_files == 0) {
    # number of images already processed:
    my $images_processed = 0;
    # remember current section title:
    my $current_section_title = $list_of_section_titles[$image_index];

    ###############################################################################
    # create thumbnail HTML files, multiple files may be needed:
    ###############################################################################

    # determine if there is still a new page to come because of
    # a new section title:
    my $expect_new_section_title = 0;
    CheckSectionTitles : for(my $idx=$image_index; $idx<=$#list_of_images; $idx++) {
      if ($list_of_section_titles[$idx] ne $current_section_title) {
        $expect_new_section_title = 1;
        last CheckSectionTitles;
      }
    }

    # output file names:
    $prev_out_file_name = $cur_out_file_name;
    $cur_out_file_name  = $next_out_file_name;
    if ( ($number_of_images_left > $globalMaxImages) ||
         ($expect_new_section_title == 1)               ) {
      $filenumber++;
      $next_out_file_name = $globalHtmlPrefix . "_" . $filenumber .
        "." . $globalHtmlExtension;
    } else {
      $next_out_file_name = "";
      $no_more_files = 1;
      $filenumber++;
    }

    # create part number:
    my $part_number = "$stringPart $filenumber";
    my $part_number_appended = " / " . $part_number;
    # discard part number on request:
    if (($globalNoPartNumber == 1) ||
        (($globalNoPartOne == 1) && ($filenumber == 1))) {
      $part_number = "&nbsp;";
      $part_number_appended = "";
    }
    ##########################################################################
    # initialize HTML Thumbnail pages file:
    ##########################################################################

    # open file:
    if ($globalWebReady == 1) {
      if (-e "$globalTargetDir$cur_out_file_name") {
        print_warning_message("Html file $globalTargetDir$cur_out_file_name already exists. Overwriting ...");
      }
      open(OUTFILE, ">" . $globalTargetDir . $cur_out_file_name);
    } else {
      if (-e "$cur_out_file_name") {
        print_warning_message("Html file $globalTargetDir$cur_out_file_name already exists. Overwriting ...");
      }
      open(OUTFILE, ">" . $cur_out_file_name);
    }
    select(OUTFILE);

    print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n";
    print "          \"http://www.w3.org/TR/html4/loose.dtd\">\n";
    print "<html>\n";
    print "<head>\n";
    print "   <meta name=\"generator\" content=\"$ScriptName $ScriptVersion\">\n";
    print "   <meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n";
    print "   <meta name=\"author\" content=\"$globalAuthor\">\n";
    print "   <meta name=\"description\" content=\"$globalTitle" . $part_number_appended . "\">\n";
    print "   <title>$globalTitle, $optionSelectionFile" . $part_number_appended . "</title> \n";
    if ($globalCssEnable == 1) {
      print "   <link rel=\"stylesheet\" type=\"text/css\" href=\"$globalStylesheetName$constantCSSExtension\">\n";
      print "</head>\n";
      print "\n";
      print "<body>\n";
    }
    else {
      print "</head>\n";
      print "\n";
      print "<body bgcolor=\"$globalBgColor\" text=\"$globalTextColor\" link=\"$globalLinkColor\" vlink=\"$globalVLinkColor\" alink=\"$globalALinkColor\">\n";
    }
    print "   <h2 align=$globalTextAlign>$globalTitle" . $part_number_appended . "</h2>\n";
    print "\n";
    if ($list_of_section_titles[$image_index] !~ /^NONE[0-9]*$/) {
      print "   <h3 align=$globalTextAlign>$list_of_section_titles[$image_index]</h3>\n";
      print "\n";
    }
    # a table with $globalImagesPerRow columns per row:
    print "   <table" . $globalTableAlignAttr . " cellspacing=$thumbnailSpacing cellpadding=0 width=\"$thumbnailTableWidth\" border=0 summary=\"lists all images in thumbnail size\">\n";
    # check for additional text infos:
    my $sub_text_info_found = 0;
    AddSubTextInfos : for(my $idx=0; $idx<=$#list_of_sub_text_infos_index; $idx++) {
      if ($list_of_sub_text_infos_index[$idx] == $image_index) {
        if ($sub_text_info_found == 0) {
          print "      <tr>\n";
          print "         <td align=$globalSubTextAlign valign=middle colspan=$globalImagesPerRow>\n";
          $sub_text_info_found = 1;
        }
        print "            $list_of_sub_text_infos[$idx]\n";
      }
    }
    if ($sub_text_info_found > 0) {
      print "         </td>\n";
      print "      </tr>\n";
    }
    # the header line which points to previous/up/next:
    print "      <tr>\n";
    print "         <td align=left valign=middle width=$thumbnailColumnWidth height=$globalTextHeight>\n";
    if ($prev_out_file_name ne "") {
      print "            <a href=\"$prev_out_file_name\">$stringPreviousPage</a>\n";
    } else {
      print "            &nbsp;\n";
    }
    print "         </td>\n";
    print "         <td align=center valign=middle colspan=$thumbnailMiddleColumn height=$globalTextHeight>\n";
    if ($globalCreateTop == 1) {
      print "            <a href=\"$top_file_name\">$globalTextBack</a>\n";
    } elsif ($globalLevelUp eq "") {
      print "            $part_number\n";
    } else {
      print "            <a href=\"$globalLevelUp\">$globalTopBack</a>\n";
    }
    print "         </td>\n";
    print "         <td align=right valign=middle width=$thumbnailColumnWidth height=$globalTextHeight>\n";
    if ($next_out_file_name ne "") {
      print "            <a href=\"$next_out_file_name\">$stringNextPage</a>\n";
    } else {
      print "            &nbsp;\n";
    }
    print "         </td>\n";
    print "      </tr>\n";
    print "\n";

    ##########################################################################
    # create image html files and insert thumbnails and links in html pages:
    ##########################################################################

    # new filenames:
    my $HtmlFileName    = "";   # the websize image html page
    my $InfoFileName    = "";   # the additional EXIF info page
    my $WrapperFileName = "";   # the size with the original image

    # create Image html dir if it does not exist:
    if ($globalWebReady == 1) {
      if (! -d "$globalTargetDir$globalHtmlDir") {
        print_info_message("Creating html directory \"$globalTargetDir$globalHtmlDir\".");
        mkdir "$globalTargetDir$globalHtmlDir", 0755;
      }
    } else {
      if (! -d "./$globalHtmlDir") {
        print_info_message("Creating html directory \"./$globalHtmlDir\".");
        mkdir "./$globalHtmlDir", 0755;
      }
    }
    # loop over all images for this thumbnail page:
    ImageLoop : while ($images_processed < $globalMaxImages) {
      # we stop with image processing if either no images are left, or
      # we find a new subtitle which indicates we have to start a new
      # thumbnail page before we can continue:
      if ( ($image_index <= $#list_of_images) &&
           ($current_section_title eq $list_of_section_titles[$image_index]) ) {
        #####################################################################
        # process image path and filenames first:
	$image_style     = $list_of_section_styles[$image_index];
	if ($image_style == 0) {
	  $image_text      = $list_of_image_names[$image_index];
	  $image_alt_text  = $list_of_alt_texts[$image_index];
	} else { # > 0 means blank style
	  $image_text      = "";
	  $image_alt_text  = "";
	}
	# current image link:
	$image_link = $list_of_image_links[$image_index];
        # remember for later if image is selected for top page:
        $image_is_selected_for_top = $list_of_top_select_markers[$image_index];
        $OriginalImage   = $list_of_images[$image_index];
        # define name for potentially rotated image with original path!
        $image_file_ext  = $list_of_image_fexts[$image_index];
        $image_file_ext  =~ tr/A-Z/a-z/;
        $RotationAngle   = $list_of_rot_infos[$image_index];
        $RotatedImage    = $list_of_image_fpaths[$image_index] .
          $list_of_image_fnames[$image_index] .
            "_$RotationAngle" . "deg." . $image_file_ext;
        if ($RotationAngle != 0) {
          # replace original image by rotated version if required:
          $OriginalImage = $RotatedImage;
        }
        $ThumbnailImage = $list_of_thumbnail_names[$image_index];
        $ButtonImage    = $list_of_button_names[$image_index];
        $WebSizeImage   = $list_of_websize_names[$image_index];
        $FullSizeImage  = $list_of_fullsize_names[$image_index];
        if ($globalWebReady == 0) {
          # add real image path if necessary:
          $ThumbnailImage = $list_of_image_fpaths[$image_index] . $ThumbnailImage;
          $ButtonImage    = $list_of_image_fpaths[$image_index] . $ButtonImage;
          $WebSizeImage   = $list_of_image_fpaths[$image_index] . $WebSizeImage;
          $FullSizeImage  = $list_of_image_fpaths[$image_index] . $FullSizeImage;
        }
        # check if a "next" image does follow:
        if (($image_index + 1) <= $#list_of_images) {
          # get data of next image ahead to be able
          # to link to next image:
          # (the previous image is already known!)
          $next_img_button_text = $list_of_image_names[$image_index + 1];
          $next_img_file_name   = $list_of_html_names[$image_index + 1];
	  $next_img_link        = $list_of_image_links[$image_index + 1];
          # we need an intermediate variable as the path varies:
          $next_img_button_name = $list_of_button_names[$image_index + 1];
          if ($globalWebReady == 0) {
            # add real image path if necessary:
            $next_img_button_name = $list_of_image_fpaths[$image_index + 1] .
              $next_img_button_name;
          }
	  if ($next_img_link ne "") {
	    # we do not point to the image_link page in next/previous cases:
	    $next_img_file_name   = "";
	  }
        } else {
          # no next image!
          $next_img_file_name   = "";
          $next_img_button_name = "";
          $next_img_button_text = "";
	  $next_img_link        = "";
        }
        #####################################################################
        # add image to iview file:
        my $iview_image_name = "";
        if ($globalWebReady == 1) {
          $iview_image_name = $globalTargetDir . $WebSizeImage;
        } else {
          $iview_image_name = $OriginalImage;
        }
        # convert slashes to backslashes again:
        $iview_image_name =~ s%/%\\%g;
        # add ".\" if pathname does not start with "\" or "."
        if ($iview_image_name !~ m/^(\.|\\)/) {
          $iview_image_name = ".\\" . $iview_image_name;
        }
	if ($image_link eq "") {
	  print(IVIEWFILE  $iview_image_name . "\n");
	}
        #####################################################################
        # generate Image HTML page in html dir:
        # the HTML image file: (if not an IMAGE_LINK)
	if ($image_link eq "") {
	  $HtmlFileName = $list_of_html_names[$image_index];
	  print_info_message("Processing image HTML file: $HtmlFileName ...");
	  my $is_ok;
	  if ($globalWebReady == 1) {
	    $is_ok = (-e "$globalTargetDir$HtmlFileName");
	  } else {
	    $is_ok = (-e "$HtmlFileName");
	  }
	  # check if image html should be updated if it already exists:
	  my $update_value = $list_of_update_info[$image_index];
	  if (((defined($is_ok)) || ($is_ok == 1)) &&
	      (($update_value != $constantUpdateHtml) &&
	       ($update_value != $constantUpdateHtmlAndImages)) &&
	      ($globalImgHtmlCreateAlways == 0)) {
	    print_warning_message("file " . $HtmlFileName . " does already exist. Skipping ...");
	  } else {
	    if ((defined($is_ok)) || ($is_ok == 1)) {
	      print_warning_message("file " . $HtmlFileName . " does already exist. Overwriting ...");
	    }
	    if ($globalWebReady == 1) {
	      open(HTMLFILE, ">" . $globalTargetDir . $HtmlFileName);
	    } else {
	      open(HTMLFILE, ">" . $HtmlFileName);
	    }
	    select(HTMLFILE);
	    # initialize HTML file for single image:
	    print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n";
	    print "          \"http://www.w3.org/TR/html4/loose.dtd\">\n";
	    print "<html>\n";
	    print "<head>\n";
	    print "   <meta name=\"generator\" content=\"$ScriptName $ScriptVersion\">\n";
	    print "   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
	    print "   <meta name=\"author\" content=\"$globalAuthor\">\n";
	    print "   <meta name=\"description\" content=\"$image_text" . $part_number_appended . "\">\n";
	    if ($current_section_title !~ /^NONE[0-9]*$/) {
	      print "   <title>$current_section_title, $image_text</title> \n";
	    } else {
	      print "   <title>$image_text</title> \n";
	    }
	    if ($globalCssEnable == 1) {
	      print "   <link rel=\"stylesheet\" type=\"text/css\" href=\"../$globalStylesheetName$constantCSSExtension\">\n";
	      print "</head>\n";
	      print "\n";
	      print "<body>\n";
	    }
	    else {
	      print "</head>\n";
	      print "\n";
	      print "<body bgcolor=\"$globalBgColor\" text=\"$globalTextColor\" link=\"$globalLinkColor\" vlink=\"$globalVLinkColor\" alink=\"$globalALinkColor\">\n";
	    }
	    if ($globalImgHtmlDense == 0) {
	      if ($current_section_title !~ /^NONE[0-9]*$/) {
		print "   <h2 align=$globalTextAlign>$current_section_title" . $part_number_appended . "</h2>\n";
		print "\n";
	      }
	      print "   <h3 align=$globalTextAlign>$image_text</h3>\n";
	      print "\n";
	    }
	    # a five column table:
	    print "   <table" . $globalTableAlignAttr . " cellspacing=$imgtableSpacing cellpadding=0 width=\"$imgtableTableWidth\" border=0 summary=\"align web size image and buttons\">\n";
	    print "      <tr>\n";
	    print "         <td align=center valign=top width=$imgtableButtonWidth>\n";
	    if ($prev_img_file_name ne "") {
	      print "            <a href=\"../$prev_img_file_name\">\n";
	      print "            <img src=\"../" . $prev_img_button_name . "\" ALT=\"$prev_img_button_text\" border=0></a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    print "         <td align=left valign=middle width=$imgtableNextPrevWidth>\n";
	    if ($prev_img_file_name ne "") {
	      print "            <a href=\"../$prev_img_file_name\">$stringPreviousPage</a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    print "         <td align=center valign=middle width=$imgtableBackWidth>\n";
	    if ($globalImgHtmlDense == 1) {
	      if ($current_section_title !~ /^NONE[0-9]*$/) {
		print "            <h3>$current_section_title</h3>\n";
	      }
	      print "            <h4>$image_text</h4>\n";
	    }
	    print "            <a href=\"../$cur_out_file_name\">$globalTextBack</a>\n";
	    print "         </td>\n";
	    print "         <td align=right valign=middle width=$imgtableNextPrevWidth>\n";
	    if ($next_img_file_name ne "") {
	      print "            <a href=\"../$next_img_file_name\">$stringNextPage</a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    # we align center rather than right because of Netscape 6 bug:
	    print "         <td align=center valign=top width=$imgtableButtonWidth>\n";
	    if ($next_img_file_name ne "") {
	      print "            <a href=\"../$next_img_file_name\">\n";
	      print "            <img src=\"../" . $next_img_button_name . "\" alt=\"$next_img_button_text\" border=0></a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    print "      </tr>\n";
	    print "\n";
	    print "      <tr>\n";
	    print "         <td colspan=5 align=$globalTextAlign valign=middle>\n";
	    # the image:
	    if (($globalLinkOrigImage == 1) || ($globalFullSizeEnable == 1)) {
	      print "            <a href=\"../$list_of_wrapper_names[$image_index]\">\n";
	    }
	    print "            <img src=\"../" . $WebSizeImage . "\" alt=\"$image_alt_text\" border=0>\n";
	    if (($globalLinkOrigImage == 1) || ($globalFullSizeEnable == 1)) {
	      print "            </a>\n";
	    }
	    print "         </td>\n";
	    print "      </tr>\n";
	    print "\n";
	    # image caption:
	    print "      <tr>\n";
	    print "         <td colspan=5 align=$globalTextAlign valign=middle height=$globalTextHeight>\n";
	    if ($image_style == 0) {
	      print "            <b>$list_of_image_captions[$image_index]</b>\n";
	    }
	    print "         </td>\n";
	    print "      </tr>\n";
	    print "\n";
	    # check if we have additional text which should be printed:
	    if ($list_of_has_text_infos[$image_index] == 1) {
	      # ok, use separate row to display info text:
	      print "      <tr>\n";
	      print "         <td>&nbsp;</td>\n";
	      print "         <td colspan=3 align=$globalImgTextAlign valign=middle>\n";
	      PrintTextInfos : for (my $idx=0;$idx <= $#list_of_text_infos_index; $idx++) {
		if ($list_of_text_infos_index[$idx] == $image_index) {
		  print "            $list_of_text_infos[$idx]\n";
		}
	      }
	      print "         </td>\n";
	      print "         <td>&nbsp;</td>\n";
	      print "      </tr>\n";
	      print "\n";
	    }
	    # bottom line
	    print "      <tr>\n";
	    print "         <td align=left valign=middle colspan=2 height=$globalTextHeight>\n";
	    print "            <a href=\"../$cur_out_file_name\">$globalTextBack</a>\n";
	    print "         </td>\n";
	    print "         <td align=center valign=middle height=$globalTextHeight>\n";
	    if ($list_of_info_page_markers[$image_index] == 1) {
	      print  "           <a href=\"../" . $list_of_info_names[$image_index] . "\">[$stringOtherInfo]</a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    print "         <td align=right valign=middle colspan=2 height=$globalTextHeight>\n";
	    if ($globalFullSizeEnable == 1) {
	      print "            <a href=\"../$FullSizeImage\">$stringZoom</a>\n";
	    } elsif ($globalLinkOrigImage == 1) {
	      print "            <a href=\"../$OriginalImage\">$stringOrigSize</a>\n";
	    } else {
	      print "            &nbsp;\n";
	    }
	    print "         </td>\n";
	    print "      </tr>\n";
	    print "   </table>\n";
	    print "\n";
	    print "   <hr>\n";
	    print_html_address_tag("   ");
	    print "   <hr>\n";
	    print "\n";
	    print "</body>\n";
	    print "</html>\n";
	  }
	  close(HTMLFILE);
	  # check if we have to create the info file:
	  # check if we have jpeg image and have program jhead available:
	  if ((($globalWebReady == 0) && ($globalLinkOrigImage == 1)) || 
	      ($globalFullSizeEnable == 1)) {
	    ##################################################################
	    # generate Image Wrapper HTML page in html dir:
	    # the HTML Image Wrapper file:
	    $WrapperFileName = $list_of_wrapper_names[$image_index];
	    print_info_message("Processing image wrapper HTML file: $WrapperFileName ...");
	    my $is_ok;
	    if ($globalWebReady == 1) {
	      $is_ok = (-e "$globalTargetDir$WrapperFileName");
	    } else {
	      $is_ok = (-e "$WrapperFileName");
	    }
	    # check if image wrapper html should be updated if it already exists:
	    my $update_value = $list_of_update_info[$image_index];
	    if (((defined($is_ok)) || ($is_ok == 1)) &&
		(($update_value != $constantUpdateHtml) &&
		 ($update_value != $constantUpdateHtmlAndImages)) &&
		($globalImgHtmlCreateAlways == 0) ) {
	      print_warning_message("file " . $WrapperFileName . " does already exist. Skipping ...");
	    } else {
	      if ((defined($is_ok)) || ($is_ok == 1)) {
		print_warning_message("file " . $WrapperFileName . " does already exist. Overwriting ...");
	      }
	      if ($globalWebReady == 1) {
		open(WRAPFILE, ">" . $globalTargetDir . $WrapperFileName);
	      } else {
		open(WRAPFILE, ">" . $WrapperFileName);
	      }
	      select(WRAPFILE);
	      # initialize HTML wrapper file for single image:
	      print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n";
	      print "          \"http://www.w3.org/TR/html4/loose.dtd\">\n";
	      print "<html>\n";
	      print "<head>\n";
	      print "   <meta name=\"generator\" content=\"$ScriptName $ScriptVersion\">\n";
	      print "   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
	      print "   <meta name=\"author\" content=\"$globalAuthor\">\n";
	      print "   <meta name=\"description\" content=\"Html wrapper for $image_text\">\n";
	      print "   <title>HTML Wrapper for $image_text</title>\n";
	      print "</head>\n";
	      print "\n";
	      print "<body>\n";
	      print "\n";
	      if ($globalFullSizeEnable == 0) {
		print "   <a href=\"../$HtmlFileName\"><img src=\"../$OriginalImage\" alt=\"$image_alt_text\" border=0></a>\n";
	      } else {
		print "   <a href=\"../$HtmlFileName\"><img src=\"../$FullSizeImage\" alt=\"$image_alt_text\" border=0></a>\n";
	      }
	      print "\n";
	      print "</body>\n";
	      print "</html>\n";
	      close(WRAPFILE);
	    }
	  }
	  # check if we have to create the info file:
	  # check if we have jpeg image and have program jhead available:
	  my $file_type = $list_of_image_ftypes[$image_index];
	  if (($globalUseJpegtranJhead == 1) && 
	      ($list_of_info_page_markers[$image_index] == 1) &&
	      ($file_type eq "jpg")) {
	    ##################################################################
	    # generate Image Info HTML page in html dir:
	    # the HTML image file:
	    $InfoFileName = $list_of_info_names[$image_index];
	    print_info_message("Processing image info HTML file: $InfoFileName ...");
	    my $is_ok;
	    if ($globalWebReady == 1) {
	      $is_ok = (-e "$globalTargetDir$InfoFileName");
	    } else {
	      $is_ok = (-e "$InfoFileName");
	    }
	    # check if image info html should be updated if it already exists:
	    my $update_value = $list_of_update_info[$image_index];
	    if (((defined($is_ok)) || ($is_ok == 1)) &&
		(($update_value != $constantUpdateHtml) &&
		 ($update_value != $constantUpdateHtmlAndImages)) &&
		($globalImgHtmlCreateAlways == 0) ) {
	      print_warning_message("file " . $InfoFileName . " does already exist. Skipping ...");
	    } else {
	      if ((defined($is_ok)) || ($is_ok == 1)) {
		print_warning_message("file " . $InfoFileName . " does already exist. Overwriting ...");
	      }
	      if ($globalWebReady == 1) {
		open(INFOFILE, ">" . $globalTargetDir . $InfoFileName);
	      } else {
		open(INFOFILE, ">" . $InfoFileName);
	      }
	      select(INFOFILE);
	      # initialize HTML info file for single image:
	      print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n";
	      print "          \"http://www.w3.org/TR/html4/loose.dtd\">\n";
	      print "<html>\n";
	      print "<head>\n";
	      print "   <meta name=\"generator\" content=\"$ScriptName $ScriptVersion\">\n";
	      print "   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
	      print "   <meta name=\"author\" content=\"$globalAuthor\">\n";
	      print "   <meta name=\"description\" content=\"JPEG information for $image_text\">\n";
	      print "   <title>JPEG info for $image_text</title>\n";
	      if ($globalCssEnable == 1) {
		print "   <link rel=\"stylesheet\" type=\"text/css\" href=\"../$globalStylesheetName$constantCSSExtension\">\n";
		print "</head>\n";
		print "\n";
		print "<body>\n";
	      } else {
		print "</head>\n";
		print "\n";
		print "<body bgcolor=\"$globalBgColor\" text=\"$globalTextColor\" link=\"$globalLinkColor\" vlink=\"$globalVLinkColor\" alink=\"$globalALinkColor\">\n";
	      }
	      print "   <h2 align=$globalTextAlign>$image_text</h2>\n";
	      print "\n";
	      print "   <h3 align=$globalTextAlign>JPEG Information</h3>\n";
	      print "\n";
	      # a two column table:
	      print "   <table" . $globalTableAlignAttr . " cellspacing=$infotableSpacing cellpadding=0 width=\"$infotableTableWidth\" border=0 summary=\"prints the JPEG info for the image\">\n";
	      print "      <tr>\n";
	      print "         <td align=left valign=middle width=$infotableThumbnailWidth>\n";
	      print "            <a href=\"../" . $HtmlFileName . "\">$stringBack</a>\n";
	      print "         </td>\n";
	      print "         <td align=right valign=middle width=$infotableTextWidth height=$globalTextHeight>\n";
	      print "            <a href=\"../$cur_out_file_name\">$globalTextBack</a>\n";
	      print "         </td>\n";
	      print "      <tr>\n";
	      print "         <td colspan=2 align=$globalTextAlign valign=middle height=$globalTextHeight>\n";
	      print "            <b>$list_of_image_captions[$image_index]</b>\n";
	      print "         </td>\n";
	      print "      </tr>\n";
	      print "      <tr>\n";
	      print "         <td align=left valign=middle width=$infotableThumbnailWidth>\n";
	      print "            <a href=\"../" . $HtmlFileName . "\">\n";
	      print "            <img src=\"../$ThumbnailImage\" alt=\"$image_alt_text\" border=0></a>\n";
	      print "            <br>\n";
	      print "            <a href=\"../" . $HtmlFileName . "\">\n";
	      print "            $globalFontMinus1_ON" . $image_text . "$globalFontMinus1_OFF</a>\n";
	      print "         </td>\n";
	      print "         <td align=left valign=middle width=" . 2 * $infotableTextWidth . ">\n";
	      # check if we have additional text which should be printed:
	      if ($list_of_has_text_infos[$image_index] == 1) {
		PrintTextInfos : for (my $idx=0;$idx <= $#list_of_text_infos_index; $idx++) {
		  if ($list_of_text_infos_index[$idx] == $image_index) {
		    print "            $list_of_text_infos[$idx]\n";
		  }
		}
	      } else {
		print "            &nbsp;\n";
	      }
	      print "         </td>\n";
	      print "      </tr>\n";
	      my $iflashused    = $list_of_exif_flashused[$image_index];
	      my $iisoequiv     = $list_of_exif_isoequiv[$image_index];
	      my $icameravendor = $list_of_exif_cameravendor[$image_index];
	      my $icameramodel  = $list_of_exif_cameramodel[$image_index];
	      my $ifocallength  = $list_of_exif_focallength[$image_index];
	      my $iexposuretime = $list_of_exif_exposuretime[$image_index];
	      my $iaperture     = $list_of_exif_aperture[$image_index];
	      my $iexposurebias = $list_of_exif_exposurebias[$image_index];
	      my $imeteringmode = $list_of_exif_meteringmode[$image_index];
	      my $iexposuremode = $list_of_exif_exposuremode[$image_index];
	      my $igeometry     = $list_of_geometry_ratio[$image_index];
	      my $iwidth        = $list_of_image_widths[$image_index];
	      my $iheight       = $list_of_image_heights[$image_index];
	      my $idate         = $list_of_image_dates[$image_index];
	      my $itime         = $list_of_image_times[$image_index];
	      my $ifilesize     = $list_of_image_fsizes[$image_index];
	      my $ifilename     = $list_of_image_fnames[$image_index] . "." .
		$list_of_image_fexts[$image_index];
	      my $ifullpath     = $list_of_images[$image_index];
	      # Print the info available on the image:
	      # the image itself:
	      print "      <tr>\n";
	      print "         <td colspan=2 align=$globalTextAlign valign=middle height=$globalTextHeight>\n";
	      print "            <b>$stringOtherInfo (N/A = $stringNotAvailable)</b>\n";
	      print "         </td>\n";
	      print "      </tr>\n";
	      print_info_table_entry($stringFileName, $ifilename);
	      print_info_table_entry($stringFileSize, $ifilesize);
	      print_info_table_entry($stringWidth, $iwidth);
	      print_info_table_entry($stringHeight, $iheight);
	      print_info_table_entry($stringGeometry, $igeometry);
	      print_info_table_entry($stringImageDate, $idate);
	      print_info_table_entry($stringImageTime, $itime);
	      print_info_table_entry($stringFlashUsed, $iflashused);
	      print_info_table_entry($stringExposureTime, $iexposuretime);
	      print_info_table_entry($stringISOEquivalent, $iisoequiv);
	      print_info_table_entry($stringFocalLength, $ifocallength);
	      print_info_table_entry($stringAperture, $iaperture);
	      print_info_table_entry($stringMeteringMode, $imeteringmode);
	      print_info_table_entry($stringExposureMode, $iexposuremode);
	      print_info_table_entry($stringExposureBias, $iexposurebias);
	      print_info_table_entry($stringCameraModel, $icameramodel);
	      print_info_table_entry($stringCameraVendor, $icameravendor);
	      print_info_table_entry($stringFullPath, $ifullpath);
	      print "   </table>\n";
	      print "\n";
	      print "   <hr>\n";
	      print_html_address_tag("   ");
	      print "   <hr>\n";
	      print "\n";
	      print "</body>\n";
	      print "</html>\n";
	      close(INFOFILE);
	    }
	  }
	} else {
	  print_info_message("No HTML pages generated for IMAGE_LINK: $image_link ($list_of_images[$image_index]).");
	}
	# update previous/next information:
	$prev_img_file_name   = "$HtmlFileName";
	if ($image_link ne "") {
	  $prev_img_file_name   = "";
	}
	$prev_img_button_name = "$ButtonImage";
	$prev_img_button_text = "$image_text";
	# increase current image index:
	$image_index++;
	$number_of_images_left--;
      } else {
	# set OriginalImage to empty string to disable writing of
	# image information from now on:
	$OriginalImage   = "";
      }
      ######################################################################
      # switch back to original thumbnail part file:
      select(OUTFILE);
      $images_processed++;
      if (($images_processed % $globalImagesPerRow) == 1) {
        print "      <tr>\n";
      }
      if (($image_link ne "") && ($OriginalImage ne "")) {
	# we link to another page:
	# TODO ...
        print "         <td width=$thumbnailColumnWidth height=$thumbnailRowHeight align=center valign=top>\n";
        print "            <a href=\"" . $image_link . "\">\n";
        print "            <img src=\"$ThumbnailImage\" alt=\"$image_alt_text\" border=0></a>\n";
	if ($image_style == 0) {
          print "            <br>\n";
          print "            <a href=\"" . $image_link . "\">\n";
          print "            $globalFontMinus1_ON" . $image_text . "$globalFontMinus1_OFF</a>\n";
	}
      } elsif ($OriginalImage ne "") {
	# we link to an image page:
        print "         <td width=$thumbnailColumnWidth height=$thumbnailRowHeight align=center valign=top>\n";
        print "            <a href=\"" . $HtmlFileName . "\">\n";
        print "            <img src=\"$ThumbnailImage\" alt=\"$image_alt_text\" border=0></a>\n";
	if ($image_style == 0) {
          print "            <br>\n";
          print "            <a href=\"" . $HtmlFileName . "\">\n";
          print "            $globalFontMinus1_ON" . $image_text . "$globalFontMinus1_OFF</a>\n";
	}
      } else {
        print "         <td>&nbsp;\n";
      }
      print "         </td>\n";
      if (($images_processed % $globalImagesPerRow) == 0) {
        print "      </tr>\n";
        print "\n";
        if ($OriginalImage eq "") {
          $images_processed = $globalMaxImages;
        }
      }
      ######################################################################
      # switch back to top file:
      if (($globalCreateTop == 1) && ($image_is_selected_for_top == 1) && 
          ($OriginalImage ne "")) {
        select(TOPFILE);
        $top_images_processed++;
        if (($top_images_processed % $globalImagesPerRow) == 1) {
          print "\n";
          print "      <tr>\n";
        }
        print "         <td width=$thumbnailColumnWidth height=$thumbnailRowHeight align=center valign=top>\n";
        print "            <a href=\"" . $cur_out_file_name . "\">\n";
        print "            <img src=\"$ThumbnailImage\" alt=\"$image_alt_text\" border=0></a>\n";
        if ($image_style == 0) {
          print "            <br>\n";
          print "            <a href=\"" . $cur_out_file_name . "\">\n";
          if ($current_section_title !~ /^NONE[0-9]*$/) {
            print "            $globalFontMinus1_ON" . $current_section_title . "$globalFontMinus1_OFF</a>\n";
          } else {
            print "            $globalFontMinus1_ON" . $image_text . "$globalFontMinus1_OFF</a>\n";
          }
        }
        print "         </td>\n";
        if (($top_images_processed % $globalImagesPerRow) == 0) {
          print "      </tr>\n";
        }
      }
    }
    #########################################################################
    # close HTML part file:
    select(OUTFILE);
    print "      <tr>\n";
    print "         <td align=$globalTextAlign valign=middle colspan=$globalImagesPerRow height=$globalTextHeight>\n";
    if ($current_section_title !~ /^NONE[0-9]*$/) {
      print "            <b>$current_section_title" . $part_number_appended . "</b>\n";
    } else {
      print "            <b>$part_number</b>\n";
    }
    print "         </td>\n";
    print "      </tr>\n";
    print "\n";
    print "      <tr>\n";
    print "         <td align=left valign=bottom width=$thumbnailColumnWidth height=$globalTextHeight>\n";
    if ($prev_out_file_name ne "") {
      print "            <a href=\"$prev_out_file_name\">$stringPreviousPage</a>\n";
    } else {
      print "            &nbsp;\n";
    }
    print "         </td>\n";
    print "         <td align=center valign=bottom colspan=$thumbnailMiddleColumn height=$globalTextHeight>\n";
    if ($globalCreateTop == 1) {
      print "            <a href=\"$top_file_name\">$globalTextBack</a>\n";
    } elsif ($globalLevelUp eq "") {
      print "            &nbsp;\n";
    } else {
      print "            <a href=\"$globalLevelUp\">$globalTopBack</a>\n";
    }
    print "         </td>\n";
    print "         <td align=right valign=bottom width=$thumbnailColumnWidth height=$globalTextHeight>\n";
    if ($next_out_file_name ne "") {
      print "            <a href=\"$next_out_file_name\">$stringNextPage</a>\n";
    } else {
      print "            &nbsp;\n";
    }
    print "         </td>\n";
    print "      </tr>\n";
    print "   </table>\n";
    print "\n";
    print "   <hr>\n";
    print_html_address_tag("   ");
    print "   <hr>\n";
    print "\n";
    print "</body>\n";
    print "</html>\n";

    # close file:
    close (OUTFILE);
  }
  #############################################################################
  # close HTML top file:
  select(TOPFILE);
  my $end_row = 0;
  while (($top_images_processed % $globalImagesPerRow) != 0) {
    print "         <td>&nbsp;</td>\n";
    $top_images_processed++;
    $end_row = 1;
  }
  if ($end_row == 1) {
    print "      </tr>\n";
  }
  print "\n";
  print "      <tr>\n";
  print "         <td align=$globalTextAlign valign=middle colspan=$globalImagesPerRow height=$globalTextHeight>\n";
  print "            $globalTopCaption\n";
  print "         </td>\n";
  print "      </tr>\n";
  print "\n";
  print "      <tr>\n";
  print "         <td align=$globalTextAlign valign=middle colspan=$globalImagesPerRow height=$globalTextHeight>\n";
  if ($globalLevelUp eq "") {
    print "            &nbsp;\n";
  } else {
    print "            <a href=\"$globalLevelUp\">$globalTopBack</a>\n";
  }
  print "         </td>\n";
  print "      </tr>\n";
  print "   </table>\n";
  print "\n";
  print "   <hr>\n";
  print_html_address_tag("   ");
  print "   <hr>\n";
  print "\n";
  print "</body>\n";
  print "</html>\n";

  # close file:
  close (TOPFILE);

  print_info_message("Finished generation of HTML files.");

  # end of if-clause which starts at beginning of HTML section:

} else {
  print_info_message("Generation of HTML files not requested.");
}

###############################################################################
# Now generate Icons and Web image files themselves:
###############################################################################

if (($optionImage == 1) || (($globalUpdateIsRequired == 1) && ($optionScan == 0))) {

  print_info_message("Creating image files ...");

  # new filenames:
  my $image_path      = "";
  my $image_extension = "";
  my $OriginalImage   = "";
  my $RotatedImage    = "";
  my $RotationAngle   = 0;
  my $ThumbnailImage  = "";
  my $ButtonImage     = "";
  my $WebSizeImage    = "";
  my $FullSizeImage   = "";
  my $WebSizeText     = "";
  my $FullSizeText    = "";

  ImageCreationLoop :
      for (my $image_index=0; $image_index <= $#list_of_images; $image_index++) {
	# we don't do anything for image links:
	if ($list_of_image_links[$image_index] ne "") {
	  print_info_message("No images generated for IMAGE_LINK: $list_of_image_links[$image_index] ($list_of_images[$image_index]).");
	  next ImageCreationLoop;
	}
        # convert image file extension to lower case:
        $image_path      = $list_of_image_fpaths[$image_index];
        $image_extension = $list_of_image_fexts[$image_index];
        # generate Icon and Web images:
        $OriginalImage   = $list_of_images[$image_index];
        # define name for potentially rotated image with original path!
        $RotationAngle   = $list_of_rot_infos[$image_index];
        $RotatedImage    = $list_of_image_fpaths[$image_index] .
          $list_of_image_fnames[$image_index] .
            "_$RotationAngle" . "deg." . $image_extension;
        # generated image path must change for web ready pages:
        if ($globalWebReady == 1) {
          $image_path = "$globalTargetDir";
        }
        # recall relative pathnames from arrays:
        $ButtonImage     = $list_of_button_names[$image_index];
        $ThumbnailImage  = $list_of_thumbnail_names[$image_index];
        $WebSizeImage    = $list_of_websize_names[$image_index];
        $WebSizeText     = $list_of_image_imprints[$image_index];
        $FullSizeImage   = $list_of_fullsize_names[$image_index];
        $FullSizeText    = $list_of_image_imprints[$image_index];
        # relative pathnames for generated images:
        $ButtonImage     = $image_path . $ButtonImage;
        $ThumbnailImage  = $image_path . $ThumbnailImage;
        $WebSizeImage    = $image_path . $WebSizeImage;
        $FullSizeImage   = $image_path . $FullSizeImage;
        # do we need an update for the images: values 2 and 3;
        my $update_value = $list_of_update_info[$image_index];
        if (($optionImage == 1) ||
            (($update_value == $constantUpdateImages) ||
             ($update_value == $constantUpdateHtmlAndImages)) ) {
          # check first if directories exist - and create them if necessary:
          # note that each new image file might be placed in separate directories:
          if (! -d "$image_path$globalThumbnailDir") {
            print_info_message("Creating thumbnail directory \"$image_path$globalThumbnailDir\".");
            mkdir "$image_path$globalThumbnailDir", 0755;
          }
          if (! -d "$image_path$globalButtonDir") {
            print_info_message("Creating button directory \"$image_path$globalButtonDir\".");
            mkdir "$image_path$globalButtonDir", 0755;
          }
          if (! -d "$image_path$globalWebSizeDir") {
            print_info_message("Creating websize image directory \"$image_path$globalWebSizeDir\".");
            mkdir "$image_path$globalWebSizeDir", 0755;
          }
	  if ($globalFullSizeEnable == 1) {
	    if (! -d "$image_path$globalFullSizeDir") {
	      print_info_message("Creating full size image directory \"$image_path$globalFullSizeDir\".");
	      mkdir "$image_path$globalFullSizeDir", 0755;
	    }
	  }
          # call image processing:
          print_info_message("processing image " . $OriginalImage . " ...");
          if ($RotationAngle != 0) {
            # replace original image by rotated (flipped) image if required:
            # we overwrite only if force is globally activated for images:
            if ((! -e "$RotatedImage") ||
                (($update_value == $constantUpdateImages) ||
                 ($update_value == $constantUpdateHtmlAndImages)) ) {
              if (-e "$RotatedImage") {
                print_info_message("Image file $RotatedImage already exists. Overwriting ...");
              }
              process_original_image($OriginalImage,
                                     $RotatedImage,
                                     $RotationAngle);
              # make sure we update other images and html pages as well:
              # (dependencies may exist if e.g. image height/width influences formatting)
              $globalUpdateIsRequired = 1;
              if ($image_index > 0) {
                $list_of_update_info[$image_index - 1] =
                  $list_of_update_info[$image_index - 1] | $constantUpdateHtml;
              }
              $list_of_update_info[$image_index] =
                $list_of_update_info[$image_index] | $constantUpdateHtmlAndImages;
              if ($image_index < $#list_of_images) {
                $list_of_update_info[$image_index + 1] =
                  $list_of_update_info[$image_index + 1] | $constantUpdateHtml;
              }
            } else {
              print_warning_message("Image file $RotatedImage already exists. Skipping ...");
            }
            $OriginalImage = $RotatedImage;
          }
          # generate new images (button, thumbnail, websize image):
          if ((! -e "$ButtonImage") ||
              (($update_value == $constantUpdateImages) ||
               ($update_value == $constantUpdateHtmlAndImages)) ) {
            if (-e "$ButtonImage") {
              print_info_message("Image file $ButtonImage already exists. Overwriting ...");
            }
            process_button_image($OriginalImage,
                                 $ButtonImage,
                                 $globalButtonSize);
          } else {
            print_warning_message("Image file $ButtonImage already exists. Skipping ...");
          }
          if ((! -e "$ThumbnailImage") ||
              (($update_value == $constantUpdateImages) ||
               ($update_value == $constantUpdateHtmlAndImages)) ) {
            if (-e "$ThumbnailImage") {
              print_info_message("Image file $ThumbnailImage already exists. Overwriting ...");
            }
            process_thumbnail_image($OriginalImage,
                                    $ThumbnailImage,
                                    $globalThumbnailSize);
          } else {
            print_warning_message("Image file $ThumbnailImage already exists. Skipping ...");
          }
          if ((! -e "$WebSizeImage") ||
              (($update_value == $constantUpdateImages) ||
               ($update_value == $constantUpdateHtmlAndImages)) ) {
            if (-e "$WebSizeImage") {
              print_info_message("Image file $WebSizeImage already exists. Overwriting ...");
            }
            # we add width and height because we keep image at size if it
            # is already smaller than the requested websize:
            process_websize_image($OriginalImage,
                                  $WebSizeImage,
                                  $globalWebSize,
                                  $WebSizeText,
                                  $list_of_image_widths[$image_index],
                                  $list_of_image_heights[$image_index]);
          } else {
            print_warning_message("Image file $WebSizeImage already exists. Skipping ...");
          }
	  if ($globalFullSizeEnable == 1) {
	    if ((! -e "$FullSizeImage") ||
		(($update_value == $constantUpdateImages) ||
		 ($update_value == $constantUpdateHtmlAndImages)) ) {
	      if (-e "$FullSizeImage") {
		print_info_message("Image file $FullSizeImage already exists. Overwriting ...");
	      }
	      # we add width and height because we keep image at size if it
	      # is already smaller than the requested full size:
	      process_fullsize_image($OriginalImage,
				     $FullSizeImage,
				     $globalFullSize,
				     $FullSizeText,
				     $list_of_image_widths[$image_index],
				     $list_of_image_heights[$image_index]);
	    } else {
	      print_warning_message("Image file $FullSizeImage already exists. Skipping ...");
	    }
	  }
        }
      }

  print_info_message("Finished generation of images.");

} else {
  print_info_message("Generation of image files not requested.");
}

###############################################################################
print_info_message("Finished perl script $0.");
###############################################################################

close(LOGFILE);

###################################
###  End of perl main program.  ###
###################################

###############################################################################
# Subprograms
###############################################################################

# return string which contains current date:
# in dd.mm.yyyy format:
sub get_todays_date {
  my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
    localtime();
  if ($mday < 10) {
    $mday = "0" . $mday;
  }
  $mon = $mon + 1;              # month is 0 to 11
  if ($mon < 10) {
    $mon = "0" . $mon;
  }
  $year = 1900 + $year;
  return $mday . "." . $mon . "." . $year;
}

# return string which contains current year:
# in yyyy format:
sub get_todays_year {
  my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
    localtime();
  $year = 1900 + $year;
  return $year;
}

# print help options
sub print_help_message {
  print STDOUT  "\n";
  print STDOUT  "$ScriptName $ScriptVersion\n";
  print STDOUT  "\n";
  print STDOUT  "   Creates Thumbnail html view files for images in various\n";
  print STDOUT  "   directories from an image selection file that contains a file\n";
  print STDOUT  "   path list for all images. Various details may be controlled with\n";
  print STDOUT  "   configuration options placed in the same image selection file).\n";
  print STDOUT  "   Optionally the script may on top scan for image files and/or\n";
  print STDOUT  "   rename the images files (either applied to a given list of\n";
  print STDOUT  "   images or to the scanned image list). A new or updated image\n";
  print STDOUT  "   selection and configuration file is written in those cases.\n";
  print STDOUT  "\n";
  print STDOUT  " usage: $ScriptName [options] ImageSelectionFileName [path-names]\n";
  print STDOUT  "\n";
  print STDOUT  "  General purpose options:\n";
  print STDOUT  "\n";
  print STDOUT  "   [-h|-help]        Print this help message and exit.\n";
  print STDOUT  "   [-v|-verbose]     Give detailed error/info/warning messages.\n";
  print STDOUT  "   [-f|-force]       Force all image and/or html files to be (re)created.\n";
  print STDOUT  "   [-w|-write]       Force configuration file to be rewritten.\n";
  print STDOUT  "\n";
  print STDOUT  "  Main task options:\n";
  print STDOUT  "\n";
  print STDOUT  "   [-t|-html]        Only the html files are created or forced.\n";
  print STDOUT  "   [-i|-image]       Only the image files are created or forced.\n";
  print STDOUT  "   [-s|-scan]        Scan the given files and directories first and write\n";
  print STDOUT  "                     an appropriate image selection file. This is the\n";
  print STDOUT  "                     only option that requires the optional path-names.\n";
  print STDOUT  "                     Note that an existing selection file is still read.\n";
  print STDOUT  "   [-r|-rename]      Rename image files according to IMAGE_NAME entry.\n";
  print STDOUT  "                     This option also replaces the original image if an\n";
  print STDOUT  "                     image needs to be rotated first (given that lossless.\n";
  print STDOUT  "                     rotation is feasible).\n";
  print STDOUT  "\n";
  print STDOUT  "  The following options are additional -scan and -rename switches:\n";
  print STDOUT  "\n";
  print STDOUT  "   [-o|-order Param] Sort according to given order:\n";
  print STDOUT  "                      * Param=\"name\", sort according to file name.\n";
  print STDOUT  "                      * Param=\"date\", sort according to jpeg image date.\n";
  print STDOUT  "                        The date parameter uses the JPEG EXIF info image\n";
  print STDOUT  "                        creation date and requires the program jhead.\n";
  print STDOUT  "                      (-order can only be used in connection with -scan).\n";
  print STDOUT  "   [-p|-prefix]      Add a two letter sort prefix (if not yet existing)\n";
  print STDOUT  "                     according to sort order given above (if specified,\n";
  print STDOUT  "                     also adds number suffix in case of ambiguities).\n";
  print STDOUT  "                      (-prefix can only be used in connection with -scan\n";
  print STDOUT  "                       and -rename).\n";
  print STDOUT  "   [-e|-ext]         Change upper case filename extension to lower case.\n";
  print STDOUT  "                     If not used, the default is to keep extension as is.\n";
  print STDOUT  "                      (-ext can only be used in connection with -scan\n";
  print STDOUT  "                       or -rename).\n";
  print STDOUT  "\n";
  exit_perl_script("Help message termination, $ScriptName");
}

# print error message to STDERR:
sub print_error_message($) {
  my $message = shift;
  print STDERR " error: " . $message . "\n";
  if ($globalLogFileIsActive == 1) {
    print LOGFILE " error: " . $message . "\n";
  }
}

#print informative message
sub print_info_message($) {
  my $message = shift;
  if ($optionVerbose == 1) {
    print STDOUT " info: " . $message . "\n";
  }
  if ($globalLogFileIsActive == 1) {
    print LOGFILE " info: " . $message . "\n";
  }
}

#print informative message
sub print_warning_message($) {
  my $message = shift;
  if ($optionVerbose == 1) {
    print STDOUT " warning: " . $message . "\n";
  }
  if ($globalLogFileIsActive == 1) {
    print LOGFILE " warning: " . $message . "\n";
  }
}
                          
# exit perl script without normal operation:
sub exit_perl_script($) {
  my $message = shift;
  if ($globalLogFileIsActive == 1) {
    print LOGFILE " exiting script: " . $message . "\n";
    close(LOGFILE);
  }
  die( $message . " / stopped");
}

# print a info table entry when creating the info html page
# (a call is required for each JPEG image parameter)
sub print_info_table_entry($$) {
  my $keyword = shift;
  my $value   = shift;
  my $lowercase_value = $value;
  $lowercase_value =~ tr/A-Z/a-z/;
  if ($lowercase_value eq "yes") {
    $value = $stringYes;
  } elsif ($lowercase_value eq "no") {
    $value = $stringNo;
  }
  print "      <tr>\n";
  print "         <td align=left valign=middle height=$globalTextHeight>\n";
  print "            <b>$keyword:</b>\n";
  print "         </td>\n";
  print "         <td align=left valign=middle height=$globalTextHeight>\n";
  print "            $value\n";
  print "         </td>\n";
  print "      </tr>\n";
}

# replace image parameters inside a line of text:
sub replace_img_param_variables($$) {
  my $textline    = shift;
  my $image_index = shift;
  if (($textline ne "") || ($textline =~ m/\%IMG[A-Z]+\%/)) {
    # ok we need to replace at least one parameter:
    my $imgdate         = $list_of_image_dates[$image_index];
    my $imgtime         = $list_of_image_times[$image_index];
    my $imgfsize        = $list_of_image_fsizes[$image_index];
    my $imgwidth        = $list_of_image_widths[$image_index];
    my $imgheight       = $list_of_image_heights[$image_index];
    my $imgtype         = $list_of_image_ftypes[$image_index];
    my $imgfname        = $list_of_image_fnames[$image_index] . "." .
      $list_of_image_fexts[$image_index];
    my $imgflashused    = $list_of_exif_flashused[$image_index];
    my $imgisoequiv     = $list_of_exif_isoequiv[$image_index];
    my $imgcameravendor = $list_of_exif_cameravendor[$image_index];
    my $imgcameramodel  = $list_of_exif_cameramodel[$image_index];
    my $imgfocallength  = $list_of_exif_focallength[$image_index];
    my $imgexposuretime = $list_of_exif_exposuretime[$image_index];
    my $imgaperture     = $list_of_exif_aperture[$image_index];
    my $imgexposurebias = $list_of_exif_exposurebias[$image_index];
    my $imgmeteringmode = $list_of_exif_meteringmode[$image_index];
    my $imgexposuremode = $list_of_exif_exposuremode[$image_index];
    my $imggeometry     = $list_of_geometry_ratio[$image_index];
    # replace text:
    $textline =~ s/\%IMGDATE\%/$imgdate/g;
    $textline =~ s/\%IMGTIME\%/$imgtime/g;
    $textline =~ s/\%IMGFSIZE\%/$imgfsize/g;
    $textline =~ s/\%IMGWIDTH\%/$imgwidth/g;
    $textline =~ s/\%IMGHEIGHT\%/$imgheight/g;
    $textline =~ s/\%IMGTYPE\%/$imgtype/g;
    $textline =~ s/\%IMGFNAME\%/$imgfname/g;
    $textline =~ s/\%IMGFLASH\%/$imgflashused/g;
    $textline =~ s/\%IMGISOEQUIV\%/$imgisoequiv/g;
    $textline =~ s/\%IMGCAMVENDOR\%/$imgcameravendor/g;
    $textline =~ s/\%IMGCAMMODEL\%/$imgcameramodel/g;
    $textline =~ s/\%IMGFOCLEN\%/$imgfocallength/g;
    $textline =~ s/\%IMGEXPOSURE\%/$imgexposuretime/g;
    $textline =~ s/\%IMGAPERTURE\%/$imgaperture/g;
    $textline =~ s/\%IMGEXPBIAS\%/$imgexposurebias/g;
    $textline =~ s/\%IMGMETERMODE\%/$imgmeteringmode/g;
    $textline =~ s/\%IMGEXPMODE\%/$imgexposuremode/g;
    $textline =~ s/\%IMGRATIO\%/$imggeometry/g;
  }
  return $textline;
}

# replace document parameters inside a line of text:
sub replace_doc_param_variables($) {
  my $textline    = shift;
  if (($textline ne "") || ($textline =~ m/\%DOC[A-Z]+\%/)) {
    # required variables: $globalAuthor, $globalCopyright, $globalEmail, 
    my $current_date = get_todays_date();
    # replace text:
    $textline =~ s/\%DOCAUTHOR\%/$globalAuthor/g;
    $textline =~ s/\%DOCEMAIL\%/$globalEmail/g;
    $textline =~ s/\%DOCCOPY\%/$globalCopyright/g;
    $textline =~ s/\%DOCDATE\%/$current_date/g;
  }
  return $textline;
}

# print the HTML address tag (to be placed at the end of each HTML file):
sub print_html_address_tag($) {
  my $lineoffset = shift;
  print $lineoffset . "<address>\n";
  if ($#list_of_address_text_infos >= 0) {
    PrintAddrTag : for(my $idx=0; $idx <= $#list_of_address_text_infos; $idx++) {
      print "$lineoffset  " . $list_of_address_text_infos[$idx] . "\n";
    }
    print $lineoffset . "</address>\n";
  } else {
    print "$lineoffset  " . $globalFontMinus1_ON . "$stringLastChange: " . get_todays_date() . ",\n";
    print "$lineoffset  $globalAuthor, <a href=\"mailto:$globalEmail\">$globalEmail</a><br>\n";
    print "$lineoffset  $globalCopyright";
    if ($globalNoToolHint == 0) {
      print "<br>\n";
      print "$lineoffset  $stringCreatedWith $ScriptName $ScriptVersion" . $globalFontMinus1_OFF . "\n";
    } else {
      print "$globalFontMinus1_OFF\n";
    }
    print $lineoffset . "</address>\n";
  }
}

# split a parameter line in two parts: key and value:
sub return_parameter_data($) {
  my $parame_line   = shift;
  my ($key, $value) = split(/\s*\=\s*/, $parame_line, 2);
  return ($key, $value);
}

# automatically create an image name from the image filename:
sub create_auto_image_name($) {
  # this is the filename without extension:
  my $image_text = shift;
  # clean up image text:
  # remove a potentially available alpha prefix:
  if ($globalRemoveAlphaPrefix == 1) {
    $image_text      =~ s/^[a-z][a-z]\_//;
  }
  # remove a potentially available number prefix:
  if ($globalRemoveNumberPrefix == 1) {
    $image_text      =~ s/^[0-9]+\_//;
  }
  # replace underscores by blanks
  if ($globalReplaceUnderScores == 1) {
    $image_text      =~ s/\_/ /g;
  }
  # replace dashes/minus signs by blank:
  if ($globalReplaceMinusSign == 1) {
    $image_text      =~ s/\-/ /g;
  }
  # break a change from lower caps to upper caps by
  # inserting a blank:
  if ($globalInsertBlankBeforeCaps == 1) {
    $image_text      =~ s/([a-z0-9]+)([A-Z])/\1 \2/g;
  }
  # a trailing integer number following a letter is
  # separated with a blank:
  if ($globalSeparateNumericSuffix == 1) {
    $image_text      =~ s/([a-zA-Z]+)([0-9]+$)/\1 \2/;
  }
  # return cleaned up file name as image name:
  return $image_text;
}

# automatically create a file name from the image name:
sub create_auto_file_name($) {
  # this is the image name without prefix or extension:
  my $image_text = shift;
  # the filename:
  my $file_name = "";
  # clean up image text:
  # start by replacing HTML entities "&[A-Za-z]+;" with first letter in entity:
  $image_text      =~ s/\&([a-zA-Z])([a-zA-Z]+)\;/\1/g;
  # now replace anything which is not a nice filename character by "-":
  $image_text      =~ s/[^-a-zA-Z0-9_.\s]+/\-/g;
  # remove blanks in front and behind "_", "-" and ".":
  # (also collapse multiple occurences of these chars to one)
  $image_text      =~ s/(\s*)(\-+)(\s*)/\-/g;
  $image_text      =~ s/(\s*)(\_+)(\s*)/\_/g;
  $image_text      =~ s/(\s*)(\.+)(\s*)/\./g;
  # a trailing integer number following a letter is
  # concatenated with leading text:
  if ($globalSeparateNumericSuffix == 1) {
    $image_text      =~ s/([a-zA-Z]+)(\s+)([0-9]+$)/\1\3/;
  }
  # connect a change from lower caps to upper caps by
  # removing any whitespace:
  if ($globalInsertBlankBeforeCaps == 1) {
    $image_text      =~ s/([a-z0-9]+)(\s+)([A-Z])/\1\3/g;
  }
  # replace blanks by underscores
  if ($globalReplaceUnderScores == 1) {
    $image_text      =~ s/\s+/\_/g;
  }
  # replace dashes/minus signs by blank: 
  # (only valid if underscores chars are not replace)
  if ($globalReplaceMinusSign == 1) {
    $image_text      =~ s/\s+/\-/g;
  }
  # add a potentially available alpha prefix:
  # note that prefix is expected to be empty string if not used:
  $file_name = $image_text;
  # return cleaned up file name as image name:
  return $file_name;
}

# Determines the size of a jpeg
sub jpegsize($) {
  my($JPEG) = shift;
  
  my($count) = 2;
  my($length)= length($JPEG);
  my($ch)    = "";
  my ($c1,$c2,$a,$b,$c,$d,$width,$height);
  
  while (($ch ne "\xda") && ($count<$length)) {
    # Find next marker (JPEG markers begin with 0xFF)
    while (($ch ne "\xff") && ($count < $length)) {
      $ch=substr($JPEG,$count,1);
      $count++;
    }
    # JPEG markers can be padded with unlimited 0xFF's
    while (($ch eq "\xff") && ($count<$length)) {
      $ch=substr($JPEG,$count,1);
      $count++;
    }
    # Now, $ch contains the value of the marker.
    if ((ord($ch) >= 0xC0) && (ord($ch) <= 0xC3)) {
      $count+=3;
      ($a,$b,$c,$d)=unpack("C"x4,substr($JPEG,$count,4));
      $width=$c<<8|$d;
      $height=$a<<8|$b;
      return($width,$height);
    } else {
      # We **MUST** skip variables, since FF's within variable names are
      # NOT valid JPEG markers
      ($c1,$c2)= unpack("C"x2,substr($JPEG,$count,2));
      $count += $c1<<8|$c2;
    }
  }
}

# Determines the size of an anymap
sub pnmsize($) {
  my($pnm) = shift;
  
  my ($width,$height);
  
  ($width,$height)=$pnm=~m#^P\d+\s+(\d+)\s+(\d+)\s+\d+\s#ois;  
  return ($width,$height) if ($width && $height);
}

# Determines the size of a gif
sub gifsize($) {
  my($GIF) = shift;
  
  my ($type,$a,$b,$c,$d,$s,$width,$height);
  
  $type=substr($GIF,0,6);
  if (!($type =~ m/GIF8[7,9]a/) || (length($s=substr($GIF, 6, 4)) != 4) ) {
    return;
  }
  ($a,$b,$c,$d)=unpack("C"x4,$s);
  $width= $b<<8|$a;
  $height= $d<<8|$c;
  return ($width,$height);
}

# Get the size in bytes, and type of the file:
sub get_image_stats($){
  my ($OrigImgFile) = shift;
  my $filetype = get_file_type($OrigImgFile);
  if (! open (ORIGINAL,$OrigImgFile)) {
    print_error_message("get_image_stats: Could not open '$OrigImgFile'" .
                        " for reading: $!.");
    return;
  }
  my ($image)=<ORIGINAL>;
  close(ORIGINAL);
  my ($fsize);
  if (($filetype ne 'jpg') && 
      ($filetype ne 'gif') && ($filetype ne 'png')) {
    $image = `anytopnm $OrigImgFile`;
    if (length ($image) == 0) {
      print_error_message("get_image_stats: Could not determine type" .
                          " of '$OrigImgFile'. Skipping ...");
      return;
    }
  }
  $fsize=length($image);
  return ($fsize,$filetype);
}

# Get the height and width of the file:
sub get_image_size($){
  my ($OrigImgFile) = shift;
  my ($width,$height);
  my $filetype = get_file_type($OrigImgFile);
  if (! open (ORIGINAL,$OrigImgFile)) {
    print_error_message("get_image_size: Could not open '$OrigImgFile'" .
                        " for reading: $!.");
    return;
  }
  my ($image)=<ORIGINAL>;
  close(ORIGINAL);
  if ($filetype eq 'jpg') {
    ($width,$height)= &jpegsize($image);
  } elsif ($filetype eq 'gif') {
    ($width,$height) = &gifsize($image);
  }
  if (! ($height && $width)) {
    # try anytopbm ...
    $image = `anytopnm $OrigImgFile`;
    if (length ($image) == 0) {
      print_error_message("get_image_size: Could not determine type" .
                          " of '$OrigImgFile'. Skipping ...");
      return;
    }
    ($width,$height) = &pnmsize($image);
  }
  if (! ($height && $width)) {
    print_error_message("get_image_size: Could not determine height and width" .
                        " of '$OrigImgFile'. Skipping ...");
    return;
  }
  return ($width,$height);
}

# Guess the file type:
sub get_file_type($) {
  my ($file) = shift;
  my ($filetype) = '';
  if ($file =~ m/\.[jJ][pP][eE]?[gG]$/i) {
    $filetype = 'jpg';
  } elsif ($file =~ m/\.[gG][iI][fF]$/i) {
    $filetype = 'gif';
  } elsif ($file =~ m/\.[pP][nN][gG]$/i) {
    $filetype = 'png';
  } else {
    $filetype = 'unknown';
  }
  return ($filetype);
}

# Rotate original image by angles of 90, 180 or 270 degrees:
sub rotate_original($$) {
  my ($image) = shift;
  my ($angle) = shift;
  my $temp;
  print_info_message("Rotating original image, rotation angle: $angle");
  if (! &savefile ("$globalTempDir/$$-scratchfile1.pnm",$image)) {
    print_error_message("Failed to save image $globalTempDir/$$-scratchfile1.pnm to temp dir.");
    return;
  }

  if ( ($temp = &readfile("$globalTempDir/$$-scratchfile1.pnm")) ) {
    # Rotate the image
    if ($angle == 90) {
      $image = `pnmflip -rotate90 $globalTempDir/$$-scratchfile1.pnm`;
    } elsif ($angle == 180) {
      $image = `pnmflip -rotate180 $globalTempDir/$$-scratchfile1.pnm`;
    } elsif ($angle == 270) {
      $image = `pnmflip -rotate270 $globalTempDir/$$-scratchfile1.pnm`;
    }
    unlink ("$globalTempDir/$$-scratchfile1.pnm");
  } else {
    $image = '';
  }

  return $image;
}

# Rotate a thumbnail image through an angle:
sub rotate_thumbnail($$$) {
  my ($image) = shift;
  my ($angle) = shift;
  my ($bgcolor) = shift;
  my $temp;
  print_info_message("Rotating thumbnail image, rotation angle: $angle");
  if (! &savefile ("$globalTempDir/$$-scratchfile1.pnm",$image)) {
    print_error_message("Failed to save image $globalTempDir/$$-scratchfile0.pnm to temp dir.");
    return;
  }
  # Add a two pixel bgcolor margin to the icon (so the alpha
  # mask doesn't eat any of the icon)
  `pnmmargin -color '$bgcolor' 2 $globalTempDir/$$-scratchfile1.pnm > $globalTempDir/$$-scratchfile0.pnm`;

  # Make and rotate our alpha mask
  my ($width,$height) = &pnmsize($image);
  `ppmmake '#ffffff' $width $height > $globalTempDir/$$-scratchfile1.pnm`;
  `pnmmargin -color '#000000' 2 $globalTempDir/$$-scratchfile1.pnm > $globalTempDir/$$-scratchfile3.pnm`;
  `pnmrotate $angle $globalTempDir/$$-scratchfile3.pnm > $globalTempDir/$$-scratchfile2.pnm`;
  `ppmtopgm $globalTempDir/$$-scratchfile2.pnm > $globalTempDir/$$-scratchfile1.pnm`;

  # Rotate the image
  `pnmrotate $angle $globalTempDir/$$-scratchfile0.pnm > $globalTempDir/$$-scratchfile2.pnm`;

  if ( ($temp = &readfile("$globalTempDir/$$-scratchfile1.pnm")) ) {
    my ($newwidth,$newheight) = &pnmsize($temp);

    # Regenerate our background
    `ppmmake '$bgcolor' $newwidth $newheight > $globalTempDir/$$-scratchfile0.pnm`;

    # Composite the rotated image
    $image = `pnmcomp -invert -alpha $globalTempDir/$$-scratchfile1.pnm $globalTempDir/$$-scratchfile0.pnm $globalTempDir/$$-scratchfile2.pnm`;
    unlink ("$globalTempDir/$$-scratchfile0.pnm","$globalTempDir/$$-scratchfile1.pnm",
            "$globalTempDir/$$-scratchfile2.pnm","$globalTempDir/$$-scratchfile3.pnm");
  } else {
    $image = '';
  }

  return $image;
}

# Rotate a websize image through an angle:
sub rotate_websize($$$) {
  my ($image) = shift;
  my ($angle) = shift;
  my ($bgcolor) = shift;
  my $temp;
  print_info_message("Rotating websize image, rotation angle: $angle");
  if (! &savefile ("$globalTempDir/$$-scratchfile1.pnm",$image)) {
    print_error_message("Failed to save image $globalTempDir/$$-scratchfile0.pnm to temp dir.");
    return;
  }
  # Add a four pixel bgcolor margin to the icon (so the alpha
  # mask doesn't eat any of the icon)
  `pnmmargin -color '$bgcolor' 4 $globalTempDir/$$-scratchfile1.pnm > $globalTempDir/$$-scratchfile0.pnm`;

  # Make and rotate our alpha mask
  my ($width,$height) = &pnmsize($image);
  `ppmmake '#ffffff' $width $height > $globalTempDir/$$-scratchfile1.pnm`;
  `pnmmargin -color '#000000' 4 $globalTempDir/$$-scratchfile1.pnm > $globalTempDir/$$-scratchfile3.pnm`;
  `pnmrotate $angle $globalTempDir/$$-scratchfile3.pnm > $globalTempDir/$$-scratchfile2.pnm`;
  `ppmtopgm $globalTempDir/$$-scratchfile2.pnm > $globalTempDir/$$-scratchfile1.pnm`;

  # Rotate the image
  `pnmrotate $angle $globalTempDir/$$-scratchfile0.pnm > $globalTempDir/$$-scratchfile2.pnm`;

  if ( ($temp = &readfile("$globalTempDir/$$-scratchfile1.pnm")) ) {
    my ($newwidth,$newheight) = &pnmsize($temp);

    # Regenerate our background
    `ppmmake '$bgcolor' $newwidth $newheight > $globalTempDir/$$-scratchfile0.pnm`;

    # Composite the rotated image
    $image = `pnmcomp -invert -alpha $globalTempDir/$$-scratchfile1.pnm $globalTempDir/$$-scratchfile0.pnm $globalTempDir/$$-scratchfile2.pnm`;
    unlink ("$globalTempDir/$$-scratchfile0.pnm","$globalTempDir/$$-scratchfile1.pnm",
            "$globalTempDir/$$-scratchfile2.pnm","$globalTempDir/$$-scratchfile3.pnm");
  } else {
    $image = '';
  }
  
  return $image;
}

# Return a scaled portable anymap of the requested image:
sub scale_image_file($$) {
  my ($file) = shift;
  my ($size) = shift;

  my ($filetype) = &get_file_type($file);
  my ($icon) = '';

  # generate a PNM scaled to web size
  if ($filetype eq 'jpg') {
    $icon=`djpeg '$file' | pnmscale -xysize $size $size`;
  } elsif ($filetype eq 'gif') { # Because 'anytopnm' screws up for gifs
    $icon=`giftopnm '$file' | pnmscale -xysize $size $size`;
  } elsif ($filetype eq 'png') {
    $icon=`pngtopnm '$file' | pnmscale -xysize $size $size`;
  } else {                      # maybe we'll get lucky and 'anytopnm' will guess right.
    $icon=`anytopnm '$file' | pnmscale -xysize $size $size`;
  }
  return ($icon);
}

# Return a raw, unscaled portable anymap of the requested image:
sub raw_image_file($) {
  my ($file) = shift;

  my ($filetype) = &get_file_type($file);
  my ($icon) = '';

  # generate a PNM image
  if ($filetype eq 'jpg') {
    $icon=`djpeg '$file'`;
  } elsif ($filetype eq 'gif') { # Because 'anytopnm' screws up for gifs
    $icon=`giftopnm '$file'`;
  } elsif ($filetype eq 'png') {
    $icon=`pngtopnm '$file'`;
  } else {                      # maybe we'll get lucky and 'anytopnm' will guess right.
    $icon=`anytopnm '$file'`;
  }
  return ($icon);
}

sub scale_pnm_image($$) {
  my ($image) = shift;
  my ($size)  = shift;

  if (! &savefile ("$globalTempDir/$$-scratchfile0.pnm",$image)) {
    &error("ERROR: (scale_PNM) Failed to save image " .
           "'$globalTempDir/$$-scratchfile0.pnm' to temp dir '$globalTempDir'\n");
    return;
  }
  $image=`pnmscale -xysize $size $size $globalTempDir/$$-scratchfile0.pnm`;

  unlink ("$globalTempDir/$$-scratchfile0.pnm");

  return ($image);
}

# Add a drop shadow to the image if requested:
# (Note: Only works with 'flat' color backgrounds;
#        graphic backgrounds are not supported at this time.)
sub add_dropshadow($$$$$) {

  my ($image)            = shift;
  my ($dropshadowblur)   = shift;
  my ($dropshadowoffset) = shift;
  my ($dropshadowcolor)  = shift;
  my ($rawbgcolor)       = shift;

  my ($leftpad,$rightpad,$toppad,$bottompad,$iwidth,$iheight,$dswidth,$dsheight);

  $leftpad   = $dropshadowblur-$dropshadowoffset;
  $leftpad   = 0 if ($leftpad < 0);

  $rightpad  = $dropshadowblur+$dropshadowoffset;
  $rightpad  = 0 if ($rightpad < 0);

  $toppad    = $dropshadowblur-$dropshadowoffset;
  $toppad    = 0 if ($toppad < 0);

  $bottompad = $dropshadowblur+$dropshadowoffset;
  $bottompad = 0 if ($bottompad < 0);

  ($dswidth,$dsheight) = &pnmsize($image);

  my $dsoffset = $dropshadowoffset;
  $dsoffset += $leftpad;

  $iwidth   = $dswidth;
  $iheight  = $dsheight;
  $dswidth  = $dswidth+$leftpad+$rightpad;
  $dsheight = $dsheight+$toppad+$bottompad;

  # Make the color block for the dropshadow
  `ppmmake '$dropshadowcolor' $iwidth $iheight > $globalTempDir/$$-scratchfile0.pnm`;

  # Make the padded block
  `ppmmake '$rawbgcolor' $dswidth $dsheight > $globalTempDir/$$-scratchfile1.pnm`;

  # insert the dropshadow block into the padded block
  `pnmpaste -replace $globalTempDir/$$-scratchfile0.pnm $dsoffset $dsoffset $globalTempDir/$$-scratchfile1.pnm> $globalTempDir/$$-scratchfile2.pnm`;

  # Blur the shadow, if a blur has been specified
  if ($dropshadowblur) {
    `pnmconvol $globalTempDir/$$-dropshadow.cnv $globalTempDir/$$-scratchfile2.pnm > $globalTempDir/$$-scratchfile0.pnm`;
  } else {
    # "mv" - but faster
    # Should add error checks....
    unlink ("$globalTempDir/$$-scratchfile0.pnm");
    link ("$globalTempDir/$$-scratchfile2.pnm","$globalTempDir/$$-scratchfile0.pnm");
    unlink ("$globalTempDir/$$-scratchfile2.pnm");
  }

  # Insert the raw image into its drop shadow frame
  if (! &savefile ("$globalTempDir/$$-scratchfile1.pnm",$image)) {
    print_error_message("Failed to save image to temp dir.");
    next;
  }
  $image = `pnmpaste -replace $globalTempDir/$$-scratchfile1.pnm $leftpad $toppad $globalTempDir/$$-scratchfile0.pnm`;

  # Dispose of the temporary files
  unlink ("$globalTempDir/$$-scratchfile0.pnm",
          "$globalTempDir/$$-scratchfile1.pnm",
          "$globalTempDir/$$-scratchfile2.pnm");

  return ($image);
}

# Add text to an image:
sub add_text {

  my ($image) = shift;
  my ($font)  = shift;
  my ($text)  = shift;

  # to work with fonts from
  #   http://dreamer.nitro.dk/linux/lfp/lfpfonts-src-0.80.tar.gz,
  # ppmcaption needs the following patch:
  #
  #--- font-bdf.c.orig	Wed May 23 22:17:44 2001
  #+++ font-bdf.c	Sun Jan 27 02:57:02 2002
  #@@ -252,6 +252,12 @@
  #                !strncmp (buf, "CHARS ", 5) ||
  #                !strncmp (buf, "SWIDTH ", 7) ||
  #                !strncmp (buf, "DWIDTH ", 7) ||
  #+               !strncmp (buf, "RESOLUTION ", 11) ||
  #+               !strncmp (buf, "UNDERLINE_POSITION ", 19) ||
  #+               !strncmp (buf, "UNDERLINE_THICKNESS ", 20) ||
  #+               !strncmp (buf, "_XMBDFED_INFO ", 14) ||
  #+               !strncmp (buf, "WEIGHT ", 7) ||
  #+               !strncmp (buf, "QUAD_WIDTH ", 11) ||
  #                !strncmp (buf, "ENDFONT", 7))
  #         {
  #           /* ignore */

  if (! &savefile ("$globalTempDir/$$-scratchfile0.pnm",$image)) {
    print_error_message("(add_text): Failed to save image $globalTempDir/$$-scratchfile0.pnm to temp dir.");
    return;
  }
  # ppmcaption is from http://www.jwz.org/ppmcaption/
  $image =  `ppmcaption -font $font -pos -10 -10 -right -blur 1 -fg black -bg white -opacity 0.7 -text "$text" $globalTempDir/$$-scratchfile0.pnm`;

  unlink ("$globalTempDir/$$-scratchfile0.pnm");
  return $image;
}

# Add a colored border to an image:
sub add_border {

  my ($image)     = shift;
  my ($edgewidth) = shift;
  my ($edgecolor) = shift;

  if (! &savefile ("$globalTempDir/$$-scratchfile0.pnm",$image)) {
    print_error_message("(add_border): Failed to save image $globalTempDir/$$-scratchfile0.pnm to temp dir.");
    return;
  }
  $image =  `pnmmargin -color $edgecolor $edgewidth $globalTempDir/$$-scratchfile0.pnm`;
  unlink ("$globalTempDir/$$-scratchfile0.pnm");
  return $image;
}

# saves the data passed in 'image' to $filename:
sub savefile($$) {

  my ($filename) = shift;
  my ($image)    = shift;

  if (! ($filename =~ m#^\s*[|><]#o)) {
         $filename=">$filename";
  }
  if (! open (NEWFILE,"$filename")) {
    print_error_message("$filename could not be opened for writing: $!.");
    return 0;
  }
  
  print NEWFILE $image;
  close (NEWFILE);
  1;
}

# returns the contents of $filename:
sub readfile($) {

  my ($filename)=shift;
  my $data;

  if (! open (NEWFILE,$filename)) {
    print_error_message("$filename could not be opened for reading: $!.");
    return;
  }

  my ($savedreadstate) = $/;
  undef $/;
  $data = <NEWFILE>;
  $/ = $savedreadstate;
  close (NEWFILE);

  return ($data);
}

# Generates the convolution map needed by pnmcovol to do a blur:
sub get_convol_map($) {
  my ($blursize) = shift;

  my ($map,$offset,$rowvalue,$x,$y);

  # Round the blur *up* to the nearest odd integer
  # if it is not '0'
  if ($blursize && (! ($blursize % 2))) {
    $blursize++;
  }

  $offset   = $blursize * $blursize;
  $rowvalue = $offset + 1;
  $offset   = $offset * 2;
  $map = "P2\n$blursize $blursize\n$offset\n";

  for ($y=1;$y<=$blursize;$y++) {
    for ($x=1;$x<=$blursize;$x++) {
      $map .= $rowvalue;
      if ($x != $blursize) {
        $map .= " ";
      }
    }
    $map .= "\n";
  }
  return ($map);
}

# create a button image:
sub process_button_image($$$) {
  my ($OriginalImgFile)  = shift;
  my ($ButtonImgFile)    = shift;
  my ($ButtonSize)       = shift;
  my ($fsize,$ftype) = &get_image_stats($OriginalImgFile);
  my ($buttonicon);
  print_info_message("Creating button image '$ButtonImgFile' ...");
  if (! $fsize) {
    print_error_message("Unable to determine '$OriginalImgFile' contents. Skipping ...");
    return;
  } else {
    # create Button:
    $buttonicon = &scale_image_file($OriginalImgFile, $ButtonSize);
    if (length($buttonicon) == 0) {
      print_error_message("Unable to generate a button version of" .
                          " '$OriginalImgFile'. Skipping ...");
      return;
    }
    # Add a border to the image:
    if ($globalImageBorderWidth > 0) {
      $buttonicon = &add_border($buttonicon,$globalImageBorderWidth,$globalBorderColor);
      if (length($buttonicon) == 0) {
        print_error_message("Could not add requested border to button for " .
                            "'$OriginalImgFile'. Skipping ...");
        return;
      }
    }
    # Add a drop shadow to the image if requested
    # (Note: Only works with 'flat' color backgrounds;
    #        graphic backgrounds are not supported at this time.)
    if ($globalMakeDropShadow != 0) {
      # Round the dropshadow blur *up* to the nearest odd integer
      # if it is not '0'
      if ($globalDropShadowBlur && (! ($globalDropShadowBlur % 2))) {
        $globalDropShadowBlur++;
      }
      # If we need to blur a drop shadow, we need a convolution map for it
      if ($globalMakeDropShadow && $globalDropShadowBlur) {
        my ($convol_map) = &get_convol_map($globalDropShadowBlur);
        &savefile ("$globalTempDir/$$-dropshadow.cnv",$convol_map);
      }
      # add shadow:
      $buttonicon = &add_dropshadow($buttonicon,$globalDropShadowBlur,$globalDropShadowOffset,
                                    $globalDropShadowColor,$globalBgColor);
      if (length($buttonicon) == 0) {
        print_error_message("Unable to add requested drop shadow to button " .
                            "for '$OriginalImgFile'.");
        return;
      }
      # delete temp file
      if ($globalMakeDropShadow && $globalDropShadowBlur) {
        unlink "$globalTempDir/$$-dropshadow.cnv";
      }
    }
    # Compress button into JPEG format and save it
    &savefile("| cjpeg -quality $globalImageQualityFactor -optimize > '$ButtonImgFile'",
              $buttonicon);
  }
}

# create a thumbnail image:
sub process_thumbnail_image($$$) {
  my ($OriginalImgFile)  = shift;
  my ($ThumbnailImgFile) = shift;
  my ($ThumbnailSize)    = shift;
  print_info_message("Creating thumbnail image '$ThumbnailImgFile' ...");
  my ($fsize,$ftype) = &get_image_stats($OriginalImgFile);
  if (! $fsize) {
    print_error_message("Unable to determine '$OriginalImgFile' contents. Skipping ...");
    return;
  }
  my ($thumbnailicon);
  # we first create the thumbnail bigger than final size to enhance
  # quality in case we need to rotate:
  my $PreThumbnailFactor = 3;
  # create Thumbnail:
  $thumbnailicon = &scale_image_file($OriginalImgFile, $ThumbnailSize * $PreThumbnailFactor);
  if (length($thumbnailicon) == 0) {
    print_error_message("Unable to to do pre-scaling of thumbnail version for" .
                        " '$OriginalImgFile'. Skipping ...");
    return;
  }
  # Add a border to the image:
  if ($globalImageBorderWidth > 0) {
    $thumbnailicon = &add_border($thumbnailicon,
                                 $globalImageBorderWidth * $PreThumbnailFactor,
                                 $globalBorderColor);
    if (length($thumbnailicon) == 0) {
      print_error_message("Could not add requested border to pre-thumbnail for " .
                          "'$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # Add a drop shadow to the image if requested
  # (Note: Only works with 'flat' color backgrounds;
  #        graphic backgrounds are not supported at this time.)
  if ($globalMakeDropShadow != 0) {
    # Round the dropshadow blur *up* to the nearest odd integer
    # if it is not '0'
    if ($globalDropShadowBlur && (! ($globalDropShadowBlur % 2))) {
      $globalDropShadowBlur++;
    }
    my $DropShadowBlur = $globalDropShadowBlur * $PreThumbnailFactor;
    my $DropShadowOffset = $globalDropShadowOffset * $PreThumbnailFactor;
    # If we need to blur a drop shadow, we need a convolution map for it
    if ($globalMakeDropShadow && $DropShadowBlur) {
      my ($convol_map) = &get_convol_map($DropShadowBlur);
      &savefile ("$globalTempDir/$$-dropshadow.cnv",$convol_map);
    }
    # add shadow:
    $thumbnailicon = &add_dropshadow($thumbnailicon,
                                     $DropShadowBlur,$DropShadowOffset,
                                     $globalDropShadowColor,$globalBgColor);
    if (length($thumbnailicon) == 0) {
      print_error_message("Unable to add requested drop shadow to pre-thumbnail " .
                          "for '$OriginalImgFile'.");
      return;
    }
    # delete temp file
    if ($globalMakeDropShadow && $DropShadowBlur) {
      unlink "$globalTempDir/$$-dropshadow.cnv";
    }
  }
  # Rotate the image, if requested
  my $randomrotationangle = 
    (rand($globalThumbnailRotationAngle * 2.0) - $globalThumbnailRotationAngle);
  if (!($globalThumbnailRotationAngle == 0) && (abs($randomrotationangle) > 0.1)) {
    if ($globalThumbnailRandomRotate != 0) {
      # integer values as quality is degraded with small float values:
      $thumbnailicon = &rotate_thumbnail($thumbnailicon,
                                         $randomrotationangle,
                                         $globalBgColor);
    } else {
      $thumbnailicon = &rotate_thumbnail($thumbnailicon,
                                         $globalThumbnailRotationAngle,
                                         $globalBgColor);
    }
    if (length($thumbnailicon) == 0) {
      print_error_message("Could not rotate pre-thumbnail for '$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # final scaling from bigger thumbnail size to target thumbnail size:
  $thumbnailicon = &scale_pnm_image($thumbnailicon, $ThumbnailSize);
  if (length($thumbnailicon) == 0) {
    print_error_message("Unable to to do final scaling of thumbnail version for" .
                        " '$OriginalImgFile'. Skipping ...");
    return;
  }
  # Compress thumbnail into JPEG format and save it
  &savefile("| cjpeg -quality $globalImageQualityFactor -optimize > '$ThumbnailImgFile'",
            $thumbnailicon);
}

# create a Web Size (VGA) image:
sub process_websize_image($$$$$) {
  my ($OriginalImgFile) = shift;
  my ($WebSizeImgFile)  = shift;
  my ($WebSizeSize)     = shift;
  my ($WebSizeText)     = shift;
  my ($ImageWidth)      = shift;
  my ($ImageHeight)     = shift;
  print_info_message("Creating web size image '$WebSizeImgFile' ...");
  my ($fsize,$ftype) = &get_image_stats($OriginalImgFile);
  if (! $fsize) {
    print_error_message("Unable to determine '$OriginalImgFile' contents. Skipping ...");
    return;
  }
  my ($websizeicon);
  if (($globalAlwaysWebSize == 1) ||
      ($ImageWidth > $WebSizeSize) || ($ImageHeight >$WebSizeSize)) {
    # create scaled-down WebSize:
    $websizeicon = &scale_image_file($OriginalImgFile, $WebSizeSize);
  } else {
    # create raw image, but keep size
    $websizeicon = &raw_image_file($OriginalImgFile);
  }
  if (length($websizeicon) == 0) {
    print_error_message("Unable to generate a websize version of" .
                        " '$OriginalImgFile'. Skipping ...");
    return;
  }

  # Add text to the image:
  # (font from
  #    http://dreamer.nitro.dk/linux/lfp/lfpfonts-src-0.80.tar.gz,
  # needs a patched ppmcaption (see function add_text))
  if ($WebSizeText ne "") {
    $websizeicon = &add_text($websizeicon, "$installDir/gamow-10-m-r-sc.bdf", "$WebSizeText");
    if (length($websizeicon) == 0) {
      print_error_message("Could not add requested text to websize image for " .
                          "'$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # Add a border to the image:
  if ($globalImageBorderWidth > 0) {
    $websizeicon = &add_border($websizeicon,$globalImageBorderWidth,$globalBorderColor);
    if (length($websizeicon) == 0) {
      print_error_message("Could not add requested border to websize image for " .
                          "'$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # Add a drop shadow to the image if requested
  # (Note: Only works with 'flat' color backgrounds;
  #        graphic backgrounds are not supported at this time.)
  if ($globalMakeDropShadow != 0) {
    # Round the dropshadow blur *up* to the nearest odd integer
    # if it is not '0'
    if ($globalDropShadowBlur && (! ($globalDropShadowBlur % 2))) {
      $globalDropShadowBlur++;
    }
    # If we need to blur a drop shadow, we need a convolution map for it
    if ($globalMakeDropShadow && $globalDropShadowBlur) {
      my ($convol_map) = &get_convol_map($globalDropShadowBlur);
      &savefile ("$globalTempDir/$$-dropshadow.cnv",$convol_map);
    }
    # add shadow:
    $websizeicon = &add_dropshadow($websizeicon,$globalDropShadowBlur*2,$globalDropShadowOffset*2,
                                   $globalDropShadowColor,$globalBgColor);
    if (length($websizeicon) == 0) {
      print_error_message("Unable to add requested drop shadow to thumbnail " .
                          "for '$OriginalImgFile'.");
      return;
    }
    # delete temp file
    if ($globalMakeDropShadow && $globalDropShadowBlur) {
      unlink "$globalTempDir/$$-dropshadow.cnv";
    }
  }
  # Rotate the image, if requested
  my $randomrotationangle = 
    (rand($globalWebsizeRotationAngle * 2.0) - $globalWebsizeRotationAngle);
  if (!($globalWebsizeRotationAngle == 0) && (abs($randomrotationangle) > 0.1)) {
    if ($globalWebsizeRandomRotate != 0) {
      # integer values as quality is degraded with small float values:
      $websizeicon = &rotate_websize($websizeicon,
                                     $randomrotationangle,
                                     $globalBgColor);
    } else {
      $websizeicon = &rotate_websize($websizeicon,
                                     $globalWebsizeRotationAngle,
                                     $globalBgColor);
    }
    if (length($websizeicon) == 0) {
      print_error_message("Could not rotate websize image for '$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # Compress websize image into JPEG format and save it
  if ($globalProgressiveJpeg != 1) {
    &savefile("| cjpeg -quality $globalImageQualityFactor -optimize > '$WebSizeImgFile'",
              $websizeicon);
  } else {
    &savefile("| cjpeg -quality $globalImageQualityFactor -optimize -progressive > '$WebSizeImgFile'",
              $websizeicon);
  }
}

# create a Full Size (SVGA) image:
sub process_fullsize_image($$$$$) {
  my ($OriginalImgFile) = shift;
  my ($FullSizeImgFile) = shift;
  my ($FullSizeSize)    = shift;
  my ($FullSizeText)    = shift;
  my ($ImageWidth)      = shift;
  my ($ImageHeight)     = shift;
  print_info_message("Creating full size image '$FullSizeImgFile' ...");
  my ($fsize,$ftype) = &get_image_stats($OriginalImgFile);
  if (! $fsize) {
    print_error_message("Unable to determine '$OriginalImgFile' contents. Skipping ...");
    return;
  }
  my ($fullsizeicon);
  if (($ImageWidth > $FullSizeSize) || ($ImageHeight >$FullSizeSize)) {
    # create scaled-down WebSize:
    $fullsizeicon = &scale_image_file($OriginalImgFile, $FullSizeSize);
  } else {
    # create raw image, but keep size
    $fullsizeicon = &raw_image_file($OriginalImgFile);
  }
  if (length($fullsizeicon) == 0) {
    print_error_message("Unable to generate a full size version of" .
                        " '$OriginalImgFile'. Skipping ...");
    return;
  }

  # Add text to the image:
  # (font from
  #    http://dreamer.nitro.dk/linux/lfp/lfpfonts-src-0.80.tar.gz,
  # needs a patched ppmcaption (see function add_text))
  if ($FullSizeText ne "") {
    $fullsizeicon = &add_text($fullsizeicon, "$installDir/gamow-10-m-r-sc.bdf", "$FullSizeText");
    if (length($fullsizeicon) == 0) {
      print_error_message("Could not add requested text to full size image for " .
                          "'$OriginalImgFile'. Skipping ...");
      return;
    }
  }
  # Compress websize image into JPEG format and save it
  if ($globalProgressiveJpeg != 1) {
    &savefile("| cjpeg -quality $globalImageQualityFactor -optimize > '$FullSizeImgFile'",
              $fullsizeicon);
  } else {
    &savefile("| cjpeg -quality $globalImageQualityFactor -optimize -progressive > '$FullSizeImgFile'",
              $fullsizeicon);
  }
}

# create a new original rotated image if required:
sub process_original_image($$$) {
  my ($OriginalImgFile)  = shift;
  my ($RotatedImgFile)   = shift;
  my ($RotationAngle)    = shift;
  print_info_message("Creating rotated image '$RotatedImgFile' from '$OriginalImgFile' ...");
  my ($filetype)     = &get_file_type($OriginalImgFile);
  if (($globalUseJpegtranJhead == 1) && ($filetype eq 'jpg')) {
    # we use jpegtran and jhead for JPEG images if possible to  get
    # a rotation which preserves camera infos and has no losses.
    # given subroutine angle is Counter-Clock-wise:
    my $ClockWiseAngle = 360 - $RotationAngle;
    system("cp $OriginalImgFile $RotatedImgFile && jhead -cmd \"jpegtran -rotate $ClockWiseAngle -outfile &o &i\" $RotatedImgFile");
  } else {
    my ($fsize,$ftype) = &get_image_stats($OriginalImgFile);
    if (! $fsize) {
      print_error_message("Unable to determine '$OriginalImgFile' contents. Skipping ...");
      return;
    }
    my ($rotatedimage) = '';
    # create PNM Image:
    if ($filetype eq 'jpg') {
      $rotatedimage=`djpeg '$OriginalImgFile'`;
    } elsif ($filetype eq 'gif') { # Because 'anytopnm' screws up for gifs
      $rotatedimage=`giftopnm '$OriginalImgFile'`;
    } elsif ($filetype eq 'png') {
      $rotatedimage=`pngtopnm '$OriginalImgFile'`;
    } else {                    # maybe we'll get lucky and 'anytopnm' will guess right.
      $rotatedimage=`anytopnm '$OriginalImgFile'`;
    }
    # create Rotated Image:
    $rotatedimage = &rotate_original($rotatedimage, $RotationAngle);
    if (length($rotatedimage) == 0) {
      print_error_message("Could not rotate '$OriginalImgFile'. Skipping ...");
      return;
    }
    # Compress thumbnail into JPEG format and save it
    &savefile("| cjpeg -quality 100 -optimize > '$RotatedImgFile'",
              $rotatedimage);
  }
}

#############################################################################
# End of perl script.
#############################################################################
