<%!

// import statements are concentrated here rather than in the individual routines
import java.util.*;
import java.text.*;
import se.datadosen.util.*; 

// Return true if the object's string representation is equivalent to an empty string.
public boolean isEmptyString(Object obj) {
	return ( obj==void || obj==null || obj.toString().trim().equals("") );
}

%>
<ja:if not exists="userInterface">
	<%!
	//Double check and initalise user variables if they are not present (e.g. running from command line, or if some problem with onload.bsh) 
  	boolean protectSlideImages = false;
	boolean displayW3CLogosOnSlide = true;
	boolean sameSizeFilterIndex = false; 
	int numberOfMiniThumbs = 10; 
	boolean keyboardShortcuts = true; 
	String displayEXIF = "always";
	boolean polaroidMode = false;
	int imagePaddingAdjust = 100;
	boolean shutterfly = true;
	boolean navigationTop= true; 
	boolean navigationBottom = true;
	String watermark = "";
	int watermarkSize = 20;
	boolean commentAboveSlide = false;
	int slideShowDelay = 4; 
	boolean slideShowContinuous = false;
	int miniThumbAdjust = 25;
	String iconSet = "default";
	boolean showPhotoName = true;
	boolean promotionalLogos = true;
	String charityName = "Firefox";
	boolean multiNavSlide = true;
	boolean globalHeader = true;
	boolean globalFooter = true;
	boolean guestBook = false; 
	String slideLayout= "Automatic";
	boolean allow35mmFocusFromCcdDimension = true;
	String debugMetadata= "Normal";
	String customSep;
	// From the Image information tab in the UI
	boolean showFileName = true; boolean showFileSize = false; boolean showNativeFocus = true; boolean show35mmFocus = true; boolean showExposureTime = true; boolean showDateTaken = true; boolean showCamera = true; boolean showResolution = true; boolean showFlash = true; boolean showWhiteBalanceMode = true; boolean showIso = true; boolean showAperture = true; boolean showMeteringMode = true; boolean showExposureMode = true; boolean showUserComment = false; boolean showArtist = true; boolean showIptcHeadline = true; boolean showIptcKeywords = true; boolean showIptcByLine = true; boolean showIptcByLineTitle = true; boolean showIptcObjectName = true; boolean showIptcSpecialInstructions = true; boolean showIptcCategory = true; boolean showIptcUrgency = true; boolean showIptcUrgency = true; boolean showIptcSubCategories = true; boolean showIptcCity = true; boolean showIptcProvince = true; boolean showIptcCountry = true; boolean showIptcReference = true; boolean showIptcCredit = true; boolean showIptcSource = true; boolean showIptcCopyright = true; boolean showIptcWriter = true;
	%>
</ja:if>

<%!
// Declare non-UI variables which may be changed within a method
boolean displayEXIFuserselect; boolean displayEXIFnever; boolean displayEXIFalways; boolean slideLayoutAutomatic; boolean slideLayoutPortrait; boolean slideLayoutLandscape; String portraitModeLinefeed = ""; String buttonSet = ""; boolean metaPresent; String pathToRoot = ""; String pathToRoot2 = ""; String fName= ""; Double polaroidStretchFactor; int imagePaddingSlide; int imagePaddingBottom; int imageFrameWidth; int imageFrameHeight; int imageWidthBy3; int imageWidthDoubleBy3; boolean landscapeMode; boolean portraitMode; String miniThumbHTML = ""; String title1 = ""; String sep = ""; String navElementHTML = ""; String buttonSetPath = ""; boolean debugMetadataNormal; boolean debugMetadataAllOnlyDefined; boolean debugMetadataAllEvenUndefined;

// This sets up initial values for variables
void variableInitSlide() {

	utils.updateVars();

	if (debugMetadata.equalsIgnoreCase("All (only defined)")) {
		debugMetadataNormal = false;
		debugMetadataAllOnlyDefined = true;
		debugMetadataAllEvenUndefined = false;
	} else if (debugMetadata.equalsIgnoreCase("All")) {
		debugMetadataNormal = false;
		debugMetadataAllOnlyDefined = false;
		debugMetadataAllEvenUndefined = true;
	} else { // Default
		debugMetadataNormal = true;
		debugMetadataAllOnlyDefined = false;
		debugMetadataAllEvenUndefined = false;
	}

	// Change variables which use multiple options as words into boolean variables for easier and faster logic later
	if (displayEXIF.equalsIgnoreCase("userselect")) { displayEXIFuserselect = true; displayEXIFnever = false; displayEXIFalways = false; }
	if (displayEXIF.equalsIgnoreCase("never")) { displayEXIFuserselect = false; displayEXIFnever = true; displayEXIFalways = false; }
	if (displayEXIF.equalsIgnoreCase("always")) { displayEXIFuserselect = false; displayEXIFnever = false; displayEXIFalways = true; }

	// Keyboard shortcuts are not compatible with the guestbook, they will be disabled if the guestbook is used.
	if (guestBook) keyboardShortcuts = false;

	portraitModeLinefeed = "";
	if (slideLayout == void) {
		// Default: Automatic slide layout
		slideLayoutAutomatic = true;
		slideLayoutPortrait = false;
		slideLayoutLandscape = false;
	} else {
		if (slideLayout.equalsIgnoreCase("Portrait")) {
			slideLayoutAutomatic = false;
			slideLayoutPortrait = true;
			slideLayoutLandscape = false;
			portraitModeLinefeed = "<br />";
		} else if (slideLayout.equalsIgnoreCase("Landscape")) {
			slideLayoutAutomatic = false;
			slideLayoutPortrait = false;
			slideLayoutLandscape = true;
		} else {
			// Default: Automatic
			slideLayoutAutomatic = true;
			slideLayoutPortrait = false;
			slideLayoutLandscape = false;
		}
	}

	buttonSet = (iconSet.equalsIgnoreCase("default") ? album.get("BUTTON_SET") : iconSet);
	metaPresent = false;

	if (resSubPath != void) resPath = resPath + "/" + resSubPath;

	pathToRoot = rootPath;
	if (fileName != void) pathToRoot = pathToRoot + "/..";
	if (level == 0) {
		pathToRoot2 = "../";
	} else {
		pathToRoot2 = pathToRoot + "/";
	}
	
	buttonSetPath = resPath + "/" + buttonSet;

	// 'sep' is used to separate data in the slide's EXIF data output
	if (isEmptyString(customSep)) {
		sep = "<i>&nbsp\u003b|&nbsp\u003b</i>";
	} else {
		sep = "<i>" + customSep + "</i>";
	}

	// We save the label variable to fName, because we may change the label value using various config files
	fName = label;

	// Now check to see if the user wants to use a different label for their photo by checking for an entry in titles.properties, or a text file named with the same name as the filename

	// If meta.properties exists in the image directory and contains
	// a description of the folder, display it with the variable descript.
	File propsFile = new File(imageDirectory, "titles.properties");
	if (propsFile.exists()) {
		Map props = IO.readMapFile(propsFile);
		String newTitle = props.get(fileName);
		if (newTitle != null && newTitle.length() >0) label = newTitle;
	} 

	// If a text file with the same name as an image exists, use the contents of this for the image comment and/or label instead
	File cFile = new File(imageDirectory, fName+".txt");
	if (cFile.exists()) {
		Map captions = IO.readMapFile(cFile);
		String text = (String)captions.get("comment");
		if (text != null && text.length()> 0) comment = text;
		String text = (String)captions.get("title");
		if (text != null && text.length()> 0) label = text;
	}

	polaroidStretchFactor = 2.5;	// Not a user variable. If Polaroid mode is selected, this is the stretch factor for the bottom of the frame.

	// This routine will determine if the slide is in landscape or portrait mode
	
	if (slideLayoutAutomatic) {
		if ( imageHeight > imageWidth ) {
			portraitMode = true;
			landscapeMode = false;
			portraitModeLinefeed = "<br />";
		} else {
			portraitMode = false;
			landscapeMode = true;
		}
	} else {
		if (slideLayoutPortrait) {
			portraitMode = true;
			landscapeMode = false;
		} else { // slideLayoutLandscape
			portraitMode = false;
			landscapeMode = true;
		}
	}

	if (numberOfMiniThumbs > 0) {
		miniThumbHTML = utils.makeThumbLinks(); // Load the entire HTML code for the minithumbs into a variable so we can use it twice on the page
	} else {
		miniThumbHTML = null;
	}

	navElementHTML = utils.makeNavElement(); // Load the entire HTML code for the navigation buttons into a variable so we can use it twice on the page

}


// This function was taken from the JAlbum website
// Strip non-numeric ending on strings and convert result to float
float numericPart(String s) {
        try {
                int i;
                for (i=0; i<s.length(); i++) {
                        char c = s.charAt(i);
                        if (!Character.isDigit(c) && c != '.')
                                break;
                }
                return Float.parseFloat(s.substring(0,i));
        }
        catch (NumberFormatException ex) {
                System.out.println("numericPart: Cannot process " + s);
                ex.printStackTrace(System.out);
                return 0;
        }
}

String stripFirstChar(String s) {
	return s.substring(1, s.length());
}


/**
 * Try to fetch metadata from a given key.
 * @param meta	The meta-information map for the given file.
 * @param key	The name of the key in the map.
 * @return the value corresponding with the given key, or null.
 */
public String getValueForKey(Map map, String key) {
	if (map == void || map == null)
		return null;
	if (map.containsKey(key) && map.get(key) != null) {
		return map.get(key).toString();
	}
	return null;
}


// Display floating point numbers with two decimals
String formatNumber(double num) {
	NumberFormat formatter = new DecimalFormat("####0.##");
	return formatter.format(num);
}


//
// Helper methods and variables/constants for 35mm focal length computation
//
import java.util.Map;

// Try to find the "cameras.properties" file.
Map cameraProperties = null;
try {
	cameraProperties = IO.readMapFile(new File(skinDirectory, "cameras.properties"));
} catch (Exception e) {
	cameraProperties = null;
}

//Properties cameraProperties = getFolderConfig(skinDirectory, "cameras.properties");


// Constructs the camera identifier from Make and Model Exif tags.
// The returned identifier is used as seach key in tne "cameras.properties" file.
String getCameraIdentifier() {
	String model = getValueForKey(meta, "Model");
	String make = getValueForKey(meta, "Make");

	if ( isEmptyString(make) ) {
		if ( isEmptyString(model) ) {
			return null;
		} else {
			return model;
		}
	} else {
		if ( isEmptyString(model) ) {
			return make;
		}
	}

	if ( model.startsWith(make) )
		return model;
	else
		return make + " " + model;
}


// 35mm focus helper method.
// Retrieve the 35mm equivalent focus multiplier.
double getCamera35mmFocusMultiplier(String cameraIdentifier) {
	if ( isEmptyString(cameraIdentifier) ) {
		return 0.0;
	}
	String info = getValueForKey(cameraProperties, cameraIdentifier + ".focalLengthMultiplier");
	if (isEmptyString(info))
		return 0.0;
	return Double.parseDouble(info);
}


// 35mm focus helper method.
// Retrieve the 35mm equivalent focus offset.
double getCamera35mmFocusOffset(String cameraIdentifier) {
	if ( isEmptyString(cameraIdentifier) ) {
		return 0.0;
	}
	String info = getValueForKey(cameraProperties, cameraIdentifier + ".focalLengthOffset");
	if (isEmptyString(info))
		return 0.0;
	return Double.parseDouble(info);
}


// 35mm focus helper method.
// Compute the image width from the "Exif Image Width" value.
private double exifImageWidth() {
	String width = getValueForKey(meta, "Exif Image Width");
	if (isEmptyString(width))
		return 0.0; // We don't have this information
	StringTokenizer t = new StringTokenizer(width);
	// exif image width; just the number
	String imwStr = t.nextToken();
	// the width in pixels; integer
	return Double.parseDouble(imwStr);
}


// 35mm focus helper method.
// Compute the image height from the "Exif Image Height" value.
private double exifImageHeight() {
	String height = getValueForKey(meta, "Exif Image Height");
	if (isEmptyString(height))
		return 0.0; // We don't have this information
	StringTokenizer t = new StringTokenizer(height);
	// exif image height; just the number
	String imhStr = t.nextToken();
	// the height in pixels; integer
	return Double.parseDouble(imhStr);
}


// 35mm focus helper method.
// Return the focal plane line density.
private double densityFactor(String exif_value) {
	// the resolution is a string, containing a fraction
	// [num]/[den] [unit]
	// for Canon camera's, [unit] is "inches".
	// Are there any cameras whose unit is "mm"?
		StringTokenizer t = new StringTokenizer(exif_value);
	// decode the resolution string
	String numStr = t.nextToken("/");
	String denStr = stripFirstChar(t.nextToken(" "));
	String untStr = t.nextToken();
	double num = Double.parseDouble(numStr);
	double den = Double.parseDouble(denStr);

	// Convert inches, cm or mm, if needed.
	// NOTE: this is still experimental code.
	if (untStr.equalsIgnoreCase("mm")) {
		return (double) (num / den);
	} else if (untStr.equalsIgnoreCase("cm")) {
		return (double) (num / den * 10);
	} else { // Default is inches
		return (double) (num / den * 25.4);
	}
}


// 35mm focus helper method.
// Return the width of the CCD in mm.
private double ccdWidth() {
	String ccdxr = getValueForKey(meta, "Focal Plane X Resolution");
	if (ccdxr == null || ccdxr.length() == 0)
		return 0.0;
	// The resolution is a string, containing a fraction:
	// [num]/[den] [unit] - parsed in densityFactor()
		return (densityFactor(ccdxr) * exifImageWidth());
}


// 35mm focus helper method.
// return the height of the CCD in mm.
private double ccdHeight() {
	String ccdyr = getValueForKey(meta, "Focal Plane Y Resolution");
	if (ccdyr == null || ccdyr.length() == 0)
		return 0.0;
	// The resolution is a string, containing a fraction:
	// [num]/[den] [unit] - parsed in densityFactor()
		return (densityFactor(ccdyr) * exifImageHeight());
}

// Compute and return the 35mm focal length.
// Returns 0 if it cannot be computed.
int focus35mm(/*String focalLength*/) {
	if (focalLength != null) {
		// There are 2 ways of computing the 35mm equivalent focus:
		// 
		// 1. Read the conversion parameters from cameras.properties
		//
		// 2. Use the Focal Plane X/Y resolution (if present)
		// Problem: if image is already rotated, the ccdWidth will return the
		//          actual ccdHeight and render the calculation invalid
		// Solution: take height if height > width
		// Problem: if the image was cropped, then the computation does
		//          NOT work (cropping is like "zooming", and the aspect ratio
		//          of the image is not necessarily perserved
		// Solution: disable this type of computation if your album
		//           contains cropped images
		//
		String cameraIdentifier = getCameraIdentifier();
		if (!isEmptyString(cameraIdentifier)) {
			camera35mmFocusMultiplier = getCamera35mmFocusMultiplier(cameraIdentifier);
			if (camera35mmFocusMultiplier > 0) {
			return (int)(numericPart(focalLength) * camera35mmFocusMultiplier + getCamera35mmFocusOffset(cameraIdentifier) + 0.5);
			} else {
				if (allow35mmFocusFromCcdDimension == true) {
					if (exifImageHeight() > exifImageWidth()) {
						if (ccdHeight() > 0.001)
							return (int)(numericPart(focalLength) * 35.0 / ccdHeight() + 0.5);
					} else {
						if (ccdWidth() > 0.001)
							return (int)(numericPart(focalLength) * 35.0 / ccdWidth() + 0.5);
					}
				}
			}
		}
	}
	// Default: no focal length
	return 0;
}

void indexLinksSlide () {
  if (album.get("rootName") == null) album.put("rootName", title);
  if (level != 0) {
    writePath(imageDirectory, "../"); //Note: This line altered from original routine in index.htt, also writePath() is found in index.bsh
    out.print("<br />");
  }
}

String meta_str = "";
void loadMetaArray () {
	// The array called metaArray (below) is loaded with pairs of values. The first entry in the pair is the name of the meta field name to query.
	// The second entry in the pair is the variable name to check in the texts.properties (or local language version) in order to display the field
	// name to the user.
/*
	String[] metaArray = {"Artist", "artist", "Flash", "flash", "Exposure Time", "exposureTime", "Metering Mode", "meteringMode", "Exposure Mode", "exposureMode", "White Balance Mode", "whiteBalanceMode", "Aperture Value", "aperture", "Canon Makernote.Iso", "ISOSetting", "Iptc.Headline", "iptc.headline", "Iptc.Keywords", "iptc.keywords", "Iptc.By-line", "iptc.authorName", "Iptc.By-line Title", "iptc.authorTitle", "Iptc.Object Name", "iptc.objectName", "Iptc.Special Instructions", "iptc.specialInstructions", "Iptc.Category", "iptc.category", "Iptc.Urgency", "iptc.urgency", "Iptc.Supplemental Category(s)", "iptc.subCategories", "Iptc.City", "iptc.city", "Iptc.Province/State", "iptc.province", "Iptc.Country/Primary Location", "iptc.country", "Iptc.Original Transmission Reference", "iptc.reference", "Iptc.Credit", "iptc.credit", "Iptc.Source", "iptc.source", "Iptc.Copyright Notice", "iptc.copyright", "Iptc.Writer", "iptc.writer"};
	for (int i=0;i<metaArray.length;i=i+2) {
		if ( meta.get(metaArray[i]) != null ) {
			out.println(sep + " <b>" + texts.getString(metaArray[i+1]) + ":</b> " + meta.get(metaArray[i]) + " ");
		}
	}
*/
	meta_str = "";
	if (debugMetadataAllOnlyDefined) {
		loadFullDefinedMetaArray();
	} else if (debugMetadataAllEvenUndefined) {
		loadFullMetaArray();
	} else { // Default: normal output
		computeMetaInformation();
		out.println(meta_str);
	}
}

// Debug helper method
// Display all the defined and decoded meta information key-value pairs (sorted by key name).
void loadFullDefinedMetaArray () {
	Vector v = new Vector(meta.keySet());
	Collections.sort(v);
	Iterator it = v.iterator();
	out.println("<b> " + texts.getString("ui.displaying").toUpperCase() + " :</b> " + texts.getString("ui.debugMetadataAllOnlyDefined").toUpperCase());
	while ( it.hasNext() )
	{
		String element =  (String)it.next();
		String el_value = (String)meta.get(element);
		if (el_value != null) {
			out.println(sep + " <b> " + element.toUpperCase()  +" :</b> " + el_value);
		}
	}
}

// Debug helper method
// Same as previous, but also display the "undefined" key-value pairs.
void loadFullMetaArray () {
	Vector v = new Vector(meta.keySet());
	Collections.sort(v);
	Iterator it = v.iterator();
	out.println("<b> " + texts.getString("ui.displaying").toUpperCase() + " :</b> " + texts.getString("ui.debugMetadataAllEvenUndefined").toUpperCase());
	while ( it.hasNext() )
	{
		String element =  (String)it.next();
		String el_value = (String)meta.get(element);
		if (el_value != null) {
			out.println(sep + " <b> " + element.toUpperCase()  +" :</b> " + el_value);
		}
	}
}


// Separator between subsequent EXIF/IPTC fields
//final String sep = "<i>&nbsp\u003b|&nbsp\u003b</i>";


// Only display the meta information specified by the end-user.
void computeMetaInformation() {
		if (meta == void || meta == null || meta_str.length() > 0)
			return;
		boolean first = true;
		String separator = "";

		// 1. File information
		if (showFileName) {
			meta_str += (separator + "<b>" + texts.getString("fileName") + ":</b> " + fileName);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showFileSize) {
			meta_str += (separator + "<b>" + texts.getString("fileSize") + ":</b> " + fileSize + " bytes");
			if (first) { separator = " " + sep + " "; first = false; }
		}

		// 2. EXIF information
		if (showDateTaken && originalDate != null) {
			meta_str += (separator + "<b>" + texts.getString("date") + ":</b> " + originalDate);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showCamera) {
			String str = cameraMake;
			if (cameraModel != null) {
				if (str == null) {
					str = cameraModel;
				} else {
					str += " (" + cameraModel + ")";
				}
			}
			if (str != null) {
				meta_str += (separator + "<b>" + texts.getString("camera") + ":</b> " + str);
				if (first) { separator = " " + sep + " "; first = false; }
			}
		}
		if (showResolution && resolution != null) {
			meta_str += (separator + "<b>" + texts.getString("resolution") + ":</b> " + resolution);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (focalLength != null) {
			int f_35mm = focus35mm();
			String str;
			if (showNativeFocus) {
				str = focalLength;
				if (show35mmFocus && f_35mm > 0)
					str += " (<b>" + texts.getString("35mmFocalLength") + ":</b> " + f_35mm + "mm)";
				meta_str += (separator + "<b>" + texts.getString("nativeFocalLength") + ":</b> " + str);
				if (first) { separator = " " + sep + " "; first = false; }
			} else {
				if (show35mmFocus) {
					str = f_35mm + "mm";
					meta_str += (separator + "<b>" + texts.getString("35mmFocalLength") + ":</b> " + str);
					if (first) { separator = " " + sep + " "; first = false; }
				}
			}
		}
		if (showUserComment) {
			String str = meta.get("User Comment");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("userComment") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showArtist) {
			String str = meta.get("Artist");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("artist") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showFlash) {
			String str = null;
			if (meta.containsKey("Flash")) {
				str = meta.get("Flash");
			} else if (meta.containsKey("Canon Makernote.Flash Mode")) {
				str = meta.get("Canon Makernote.Flash Mode");
			}
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("flash") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showExposureTime) {
			String str = meta.get("Exposure Time");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("exposureTime") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showMeteringMode) {
			String str = meta.get("Metering Mode");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("meteringMode") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showExposureMode) {
			String str = meta.get("Exposure Mode");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("exposureMode") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showWhiteBalanceMode) {
			String str = meta.get("White Balance Mode");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("whiteBalanceMode") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showAperture) {
			String str = meta.get("Aperture Value");
			if (str == void || str == null) str = meta.get("F-Number");
			// TODO - Add code for other camera makes
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("aperture") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIso) {
			String str = meta.get("ISO Speed Ratings");
			if (str == void || str == null) str = meta.get("Canon Makernote.Iso");
			// TODO: Add code for other camera makes
			if (str != void && str != null && str.length() > 0) {
				meta_str += (separator + "<b>" + texts.getString("ISOSetting") + ":</b> " + str);
				if (first) { separator = " " + sep + " "; first = false; }
			}
		}
		// 3. IPTC information
		if (showIptcHeadline) {
			String str = meta.get("Iptc.Headline");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.headline") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcKeywords) {
			String str = meta.get("Iptc.Keywords");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.keywords") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcByLine) {
			String str = meta.get("Iptc.By-line");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.authorName") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcByLineTitle) {
			String str = meta.get("Iptc.By-line Title");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.authorTitle") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcObjectName) {
			String str = meta.get("Iptc.Object Name");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.objectName") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcSpecialInstructions) {
			String str = meta.get("Iptc.Special Instructions");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.specialInstructions") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcCategory) {
			String str = meta.get("Iptc.Category");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.category") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcUrgency) {
			String str = meta.get("Iptc.Urgency");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.urgency") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcSubCategories) {
			String str = meta.get("Iptc.Supplemental Category(s)");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.subCategories") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcCity) {
			String str = meta.get("Iptc.City");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.city") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcProvince) {
			String str = meta.get("Iptc.Province/State");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.province") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcCountry) {
			String str = meta.get("Iptc.Country/Primary Location");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.country") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcReference) {
			String str = meta.get("Iptc.Original Transmission Reference");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.reference") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcCredit) {
			String str = meta.get("Iptc.Credit");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.credit") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcSource) {
			String str = meta.get("Iptc.Source");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.source") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcCopyright) {
			String str = meta.get("Iptc.Copyright Notice");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.copyright") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
		if (showIptcWriter) {
			String str = meta.get("Iptc.Writer");
			if (str != void && str != null && str.length() > 0)
			meta_str += (separator + "<b>" + texts.getString("iptc.writer") + ":</b> " + str);
			if (first) { separator = " " + sep + " "; first = false; }
		}
}

void displayMetaInformation()
{
	out.println(meta_str);
/*
	if (world_map != null) {
		out.println(world_map);
	}
*/
}
%>
