
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* ___________________
*
*  Copyright 2008 Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*
* $DateTime: 2008/04/10 13:35:00 $
*
* Adobe Output Module 2.0
*
* MediaGallery Originally Written by Quality Process Incorporated, Enhanced by
* Adobe System China. Contact Sheet written by Adobe System China.
* 
*
**************************************************************************/

/*
	
	Class AOM.PDFGenerator:
	
	Public Methods:
	
		setPageLayout(dimension, arrange, margins, spacings)
			dimension: Page size in the form of [width, height].
			arrange:   Define how many images placed in each row and column, in
			           the form of [imagePerRow, imagePerColumn].
			margins:   Page margine in horizontal and vertical, in the form of
			           [top, bottom, left, right].
			spacings:  Space interval between adjecent images, in the form of
			           [vertical, horizontal].
					   
		setBackgroundColor(r, g, b)
			Set the background color.
		
		setFontColor(r, g, b)
			Set the font color.
			
		setFontSize(fontSize)
	        Set the font size. By default the font size is 12.
			
		setFilename(b)
			Specify whether to display filename in the generated contact sheet.
			By default it is true.
		
		setFileExtension(b)
			Specify whether to display file extension in the generated contact
			sheet. By default it is true. To display extension implies to
            display filename.
			
		setResolution(resolution)
			Set the resolution of the images to be inserted into the generated
			PDF document.
			resolution: The resolution of the image. The unit is Pixel/Inch.
		
		setRotateToFit(b)
			Specify whether to rotate an image to best fit the contact sheet
            cell. An image is scaled to fit the space of the contact sheet,
            therefore there must be space not occupied by an image if the
            height/width ratio of the image differs from the ratio of the cell
            space allocated	for the image. If this properties is set to true,
            then the generator always rotate an image by 90 degree,
            counter-clock, if it can make better use of the cell space.
            Otherwise, an image is never rotated.
        
        setFullScreenMode(b)
            Specify whether the document supports full-screen mode presentation.
            By default it is false.
		
		setLoop(b)
            Specify whether the document transits to the first page when the
            auto-advancing is enabled, or a user manually trigger the page
            advancing, when the last page is presented. By default it is false.
        
        setTransitionMode(mode, duration)
            Specify the transition mode between each two adjacent pages during
            the full-screen-mode presentation. This property is ignored if the
            full-screen mode is set false.

            Parameters:
                mode: A string indicating the name of the transition mode. The
                      following mode are supported as documented in PDF
                      standard:
                        o Split
                        o Blinds
                        o Box
                        o Wipe
                        o Dissolve
                        o Glitter
                        o R
                        o Fly
                        o Push
                        o Cover
                        o Uncover
                        o Fade
                duration: Time in seconds that how long the transition from a
                          page to its following page lasts.
                          
        setUserPassword(password)
            Set the user password securing the document. A user is permitted to
            open a document with user password protection only if he/she can
            provide correct user password.

        setOwnerPassword(password)
            Set the owner password securing the document. A user is permitted to
            change the security protection settings of a document with owner
            password proctection only if he/she can provide correct owner
            password.

        setAllowPrint(b)
            Specify if print is allowed on the generated document. By default it
            is true.
        
        setWatermarkText(watermarkSettings)
        	watermarkSettings:
        		An object possessing the following properties depicting how the
        		watermark should be presented in the PDF:
		        	text: The watermark text.
		        	fontFamily: The name of the font.
		        	fontFace: The name of the font face.
		        	fontSize: The font size.
		        	fontColor: An integer representing the color, the most-significant two bytes
		        	           is R value, then G and B.
		        	opacity: Between 0 to 1.
		        	isBackground: Boolean.
		        	slopingAngle: [0, 2 * 3.1415926). The anlge by which the watermark rotates
		        			   counter-clockwise.
		        	placedPerImage: If the watermark is placed one per each image on the page.
        
		exportPDF(filepath, majorVer, minorVer)
			Generate PDF disk file.
			filepath: The filepath of the exported file.
			majorVer: The major version of the exported PDF document.
			majorVer: The minor version of the exported PDF document.
	
 */




AOM.ThumbnailLayout = function(pageDimension, pageArrange, pageMargins, spacings,
							   footerHeight, headerHeight)
{
    /* Page Dimension: [width, height]
     * Page Arrange: [imagePerRow, imagePerColumn]
     * Page Margins: [top, bottom, left, right]
     * Spacings: [vertical, horizontal]
     *      
     * Enforcedly cast in order to handle the case that string is passed
     * in.               
     */
    pageWidth = Number(pageDimension[0]);
    pageHeight = Number(pageDimension[1]);
    imagePerRow = Number(pageArrange[0]);
    imagePerCol = Number(pageArrange[1]);
    topMargin = Number(pageMargins[0]);
    bottomMargin = Number(pageMargins[1]);
    leftMargin = Number(pageMargins[2]);
    rightMargin = Number(pageMargins[3]);
    verticalSpacing = Number(spacings[0]);
    horizontalSpacing = Number(spacings[1]);
    
    /*
     *  The following code is a bit duplicated with PDFGenerator::getThumbnailSize()
     *  in PDFGenerator.dll. We make it since the pass of structed-parameter is difficult
     *  cross C++/Script border.
     */
    if(footerHeight == undefined)
    	footerHeight = 0;
    
    if(headerHeight == undefined)
    	headerHeight = 0;
    
    var neatHeight = pageHeight - bottomMargin - topMargin;
    var protectedArea = 0;
    if(footerHeight != 0 || headerHeight != 0)
		protectedArea = Math.floor(neatHeight * 0.01);
	if(footerHeight != 0 && headerHeight != 0)
		protectedArea *= 2;
	
	this.width = ((pageWidth - leftMargin - rightMargin) + horizontalSpacing)
                  / imagePerRow - horizontalSpacing;
	this.height = ((neatHeight - protectedArea - footerHeight - headerHeight) + verticalSpacing)
                   / imagePerCol - verticalSpacing;
}


/**
 *  Parameter:
 *         document     For which Bridge window this generator works.
 *         interacitve  If false, indicate the gernator is work in batch mode (i.e. no
 *                      progress bar dialog box). This is for non-interactive PDF generation,
 *                      which is driven by a test driver script or a user customized script.
 */
AOM.PDFGenerator = function(document, interactive, imageCache)
{
	this.images = new Array();
	this.errorList = new Array();
	this.imageCache = imageCache;
	if(document != undefined) {
		this.document = document;
		this.documentID = document.id;
		this.interactive = interactive;
	}
	var commonFolder = Folder.commonFiles;
	var userData = Folder.temp;
		
	if($.os.indexOf("Windows") >= 0) {
		commonFolder =
			AOM.Paths.absoluteProjectOnWin + "\\contactsheet";
		this.libImageFolder = commonFolder + "\\resources\\plugins\\";
	} else {
		commonFolder =
			AOM.Paths.absoluteProjectOnMac + "/contactsheet";
		this.libImageFolder = commonFolder + "/resources/plugins/";
	}

	this.external = new AOM.PDFGeneratorExternal(this.libImageFolder);
	if(imageCache == undefined)
		return;

	this.fontSize = 12;
	this.tempImageFolder = userData + "/Adobe Output Module/";
	this.tempImageCacheFolder = imageCache.tempImageCacheFolder;
	var tempFolder = Folder(this.tempImageFolder);
	if(!tempFolder.exists)
		tempFolder.create();
		
	this.tempImageFolder = this.tempImageFolder + "task" + this.documentID + "/";
	tempFolder = Folder(this.tempImageFolder);
	if(!tempFolder.exists)
		tempFolder.create();
	
	// try to use original color profile if "Preserve Color Profile" is checked
	var preserveColorProfile = AOM.BridgePreferences.getPreserveColorProfile();
	this.preserveColorProfile = (preserveColorProfile == "true");
}



AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_LEFT = 0;
AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_MIDDLE = 1;
AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_RIGHT = 2;

AOM.PDFGenerator.WATERMARK_SUCCESS = 0;
AOM.PDFGenerator.WATERMARK_IMAGE_NOT_EXIST = -1;
AOM.PDFGenerator.WATERMARK_IMAGE_UNSUPPORT = -2;

AOM.PDFGenerator.MSG_INEXISTENT_FONT_FACE = 
		"$$$/ContactSheet/javascript/validation/sysfontFaceNotExist=(1) font face \"(3)\" of font family \"(2)\" is no longer available in the system. Please select another font face or family.";
AOM.PDFGenerator.MSG_INEXISTENT_FONT_FAMILY = 
		"$$$/ContactSheet/javascript/validation/sysfontFamilyNotExist=(1) font \"(2)\" is no longer available in the system. Please select another font.";



AOM.PDFGenerator.getFontInformation = function()
{
	var generator = new AOM.PDFGenerator();
	var result = generator.external.getFontInformation();
	generator = null;
	return result;
}




AOM.PDFGenerator.prototype.setBackgroundColor = function(r, g, b)
{
	this.external.background = [r, g, b];
}



AOM.PDFGenerator.prototype.setFontColor = function(r, g, b)
{
	this.external.fontColor = [r, g, b];
}



AOM.PDFGenerator.prototype.setFilename = function(bool)
{
	this.external.displayFilename = bool;
}



AOM.PDFGenerator.prototype.setFileExtension = function(bool)
{
	this.external.displayFileExtension = bool;
	if(bool)
		this.setFilename(true);
}



AOM.PDFGenerator.prototype.loadSelectedOne = function(imageSet, imageCount, taskManager)
{
	if(this.onStopCode < 0)
        return;
    if(this.onStopCode > imageCount - 1) {
        /* For preview operation, remove the tasks for the images placed
         * to second and subsequent pages.
         *
         * Be careful. since the image corresponding to this task
         * still in the set, so need to minues one from its length. */             
        taskManager.removeTask(this.taskCount - this.finishedTask - 1);
        return;
    }
    if(this.onStopCode > 0 && this.repeatOnPage != undefined && this.repeatOnPage == true
                           && this.isPreview != undefined && this.isPreview == true) {
    	/* For preview operation, remove the tasks for the images placed
         * to second and subsequent pages. And since this is repeat one image
         * per page, we need only single image.
         *
         * Be careful. since the image corresponding to this task
         * still in the set, so need to minus one from its length. */             
        taskManager.removeTask(this.taskCount - this.finishedTask - 1);
        return;
    }
	while (true) {
		var theImage = imageSet.shift();
		if (theImage == undefined) {
			++this.onStopCode;
			break;
		}
		
    	var thumbnail = theImage.thumbnail;
		var currentImage = new Object();
		var imageSize;
		currentImage.filename = thumbnail.path;
		currentImage.index = thumbnail.index;
		currentImage.colorMode = "";
		
		var imageBits;
		var result;
		var succeed = true;

    	if (thumbnail.exist) {
    		imageBits = new BitmapData(File(thumbnail.cacheImagePath).fsName, true);
			currentImage.thumbFilename = File(thumbnail.cacheImagePath).fsName;
			
			if(imageBits == undefined || imageBits.width == undefined || imageBits.height == undefined) {
				this.imageCache.releaseByName(thumbnail.cacheImageName);
				result = this.imageCache.createNewCacheItem(thumbnail, thumbnail.demandInfo);
				if (result.tempFileName == undefined) {
					succeed = false;
					this.errorList.push(thumbnail.path);
				} else  {
					imageBits = result.imageBits;
					currentImage.thumbFilename = File(this.tempImageCacheFolder + File.encode(result.tempFileName) + ".jpg").fsName;
				}
			}
			if (succeed) {
				currentImage.thumbImageSize = [imageBits.width, imageBits.height];
				currentImage.annotation = theImage.name;
				if (imageBits.needDispose != false)
					imageBits.dispose();
				
				if(this.repeatOnPage != undefined && this.repeatOnPage == true) {
					var imagePerPage = this.pageLayout.arrange[0] * this.pageLayout.arrange[1];
					for(var i = 0; i < imagePerPage; ++i)
						this.images.push(currentImage);
				} else {
					this.images.push(currentImage);
				}
				++this.onStopCode;
				if(this.onStopCode > imageCount - 1) {
	        		taskManager.removeTask(this.taskCount - this.finishedTask - 1);
	        		return;
    			}
    			if(this.onStopCode > 0 && this.repeatOnPage != undefined && this.repeatOnPage == true
                           && this.isPreview != undefined && this.isPreview == true) {         
        			taskManager.removeTask(this.taskCount - this.finishedTask - 1);
        			return;
    			}
			}
			
		}else {
			result = this.imageCache.createNewCacheItem(thumbnail, thumbnail.demandInfo);
			if (result.diskFull) {
				AOM.alert(AOM.AMG.localize("$$$/MediaGallery/JavaScript/notEnoughStorage=There is insufficient disk space to complete this operation."));
				taskManager.pause();
				taskManager.killTasks();
				taskManager.progress.close();
				this.imageCache.deleteLatest();
				return;
			}
			if (result.tempFileName == undefined) {
				succeed = false;
				this.errorList.push(thumbnail.path);
				break;
			}else {
				var tempFile = new File(this.tempImageCacheFolder + File.encode(result.tempFileName) + ".jpg");
				imageBits = result.imageBits;
				currentImage.thumbFilename = tempFile.fsName;
				currentImage.thumbImageSize = [imageBits.width, imageBits.height];
				currentImage.annotation = theImage.name;
				if (imageBits.needDispose != false)
					imageBits.dispose();
				
				if(this.repeatOnPage != undefined && this.repeatOnPage == true) {
					var imagePerPage = this.pageLayout.arrange[0] * this.pageLayout.arrange[1];
					for(var i = 0; i < imagePerPage; ++i)
						this.images.push(currentImage);
				}else {
					this.images.push(currentImage);
				}
				
				++this.onStopCode;
				break;
			} // end else
		} //end else
    } // end while
    this.finishedTask ++;
}





AOM.PDFGenerator.prototype.setPageLayout = function(dimension, arrange, margins, spacings, headerHeight, footerHeight)
{
	dimension = AOM.PDFGenerator.round(dimension);
	arrange = AOM.PDFGenerator.round(arrange);
	margins = AOM.PDFGenerator.round(margins);
	spacings = AOM.PDFGenerator.round(spacings);
	headerHeight = AOM.PDFGenerator.round(headerHeight);
	footerHeight = AOM.PDFGenerator.round(footerHeight);

	this.pageLayout = new Object();
	this.pageLayout.dimension = dimension;
	this.pageLayout.margins = margins;
	this.pageLayout.spacings = spacings;
	this.pageLayout.arrange = arrange;	
	this.pageLayout.footerHeight = footerHeight;
	this.pageLayout.headerHeight = headerHeight;
	this.thumbnailLayout = new AOM.ThumbnailLayout(dimension, arrange, margins, spacings, footerHeight, headerHeight);
}


AOM.PDFGenerator.prototype.setFitType = function(type)
{
	if(type != "shrink" && type != "fit") {
		throw "Bad Argument";
	}
	this.fitType = type;
}



AOM.PDFGenerator.prototype.setFontSize = function(ftSize)
{
	this.fontSize = ftSize;
}


AOM.PDFGenerator.prototype.setFontStyle = function(fontFamily, fontFace)
{
	this.external.fontFamily = fontFamily;
	this.external.fontFace = fontFace;
}



AOM.PDFGenerator.prototype.setResolution = function(ppi)
{
	this.ppi = ppi;
}

AOM.PDFGenerator.prototype.setJpegQuality = function(quality)
{
	this.jpegQuality = quality;
}


AOM.PDFGenerator.prototype.setRotateToFit = function(b)
{
	this.rotateToFit = b;
}


AOM.PDFGenerator.prototype.setAnnotationRatio = function(ratio)
{
	this.annotationRatio = ratio;
}


AOM.PDFGenerator.prototype.setFullScreenMode = function(mode)
{
	this.external.setFullScreenMode(mode);
}



AOM.PDFGenerator.prototype.setLoop = function(mode)
{
	this.external.setLoop(mode);
}



AOM.PDFGenerator.prototype.setTransitionMode = function(mode, duration)
{
	this.external.setTransitionMode(mode, duration)
}



AOM.PDFGenerator.prototype.setPageDuration = function(duration)
{
	this.external.pageDuration = duration;
}



AOM.PDFGenerator.prototype.setUserPassword = function(userPw)
{
	this.external.setUserPassword(userPw);
}




AOM.PDFGenerator.prototype.setOwnerPassword = function(ownerPw)
{
	this.external.setOwnerPassword(ownerPw);
}




AOM.PDFGenerator.prototype.setPrintPermission = function(b)
{
	this.external.setPrintPermission(b);
}



AOM.PDFGenerator.prototype.setPlaceByColumn = function(b)
{
    this.external.placeByColumn = b;
}



AOM.PDFGenerator.prototype.setRepeatOnPage = function(b)
{
	this.repeatOnPage = b;
}



AOM.PDFGenerator.prototype.setHeaderHeight = function(height)
{
	var newThumbnailLayout = new AOM.ThumbnailLayout(
		this.pageLayout.dimension,
		this.pageLayout.arrange,
		this.pageLayout.margins,
		this.pageLayout.spacings,
		this.pageLayout.footerHeight,
		height);
	
	this.pageLayout.headerHeight = AOM.PDFGenerator.round(height);
	this.thumbnailLayout = newThumbnailLayout;
}



AOM.PDFGenerator.prototype.setFooterHeight = function(height)
{
	var newThumbnailLayout = new AOM.ThumbnailLayout(
		this.pageLayout.dimension,
		this.pageLayout.arrange,
		this.pageLayout.margins,
		this.pageLayout.spacings,
		height,
		this.pageLayout.headerHeight);

	this.pageLayout.footerHeight = AOM.PDFGenerator.round(height);
	this.thumbnailLayout = newThumbnailLayout;
}


AOM.PDFGenerator.FooterHeaderText = function(text, fontFamily, fontFace, fontSize, color,
											 alignment, atFooter)
{
	this.text = text;
	this.fontFamily = fontFamily;
	this.fontFace = fontFace;
	this.fontSize = fontSize;
	this.color = color;
	this.alignment = alignment;
	this.atFooter = atFooter;
}


AOM.PDFGenerator.prototype.setFooterText = function(text, fontFamily, fontFace, fontSize, color, alignment)
{
	this.footerText = new AOM.PDFGenerator.FooterHeaderText(text.replace(/\\/g, "\\\\"), fontFamily, fontFace,
															fontSize, color, alignment);
}


AOM.PDFGenerator.prototype.setHeaderText = function(text, fontFamily, fontFace, fontSize, color, alignment)
{
	this.headerText = new AOM.PDFGenerator.FooterHeaderText(text.replace(/\\/g, "\\\\"), fontFamily, fontFace,
															fontSize, color, alignment);
}


AOM.PDFGenerator.prototype.setDivideLine = function(atFooter, width, color)
{
	var x1, y, x2, y;
	var pageWidth = Number(this.pageLayout.dimension[0]);
    var leftMargin = Number(this.pageLayout.margins[2]);
    var rightMargin = Number(this.pageLayout.margins[3]);
		
	x1 = 0;
	x2 = pageWidth - rightMargin - leftMargin;
	y = atFooter ? this.pageLayout.footerHeight - Math.ceil(width / 2) : width / 2;
	
	if(atFooter) {
		this.external.footerLines = new Array();
		this.footerDivideLineWidth = width;
	} else {
		this.external.headerLines = new Array();
		this.headerDivideLineWidth = width;
	}

	this.external.addLine(x1, y, x2, y, width, color, atFooter);
}


AOM.PDFGenerator.prototype.setPageNumber = function(fontFamily, fontFace, fontSize, color, alignment, atFooter)
{
	this.pageNumber = new AOM.PDFGenerator.FooterHeaderText(
		"\\p", fontFamily, fontFace, fontSize,
		color, alignment, atFooter);
}


AOM.PDFGenerator.prototype.processFooterHeader = function()
{
	var headerDL = this.headerDivideLineWidth == undefined ?
				   0 : this.headerDivideLineWidth;
	var footerDL = this.footerDivideLineWidth == undefined ?
				   0 : this.footerDivideLineWidth;
				   
	var headerVerticalCenter =
			(this.pageLayout.headerHeight - headerDL) / 2 + headerDL;
	var footerVerticalCenter =
			(this.pageLayout.footerHeight - footerDL) / 2;

	this.external.pageFooters = new Array();
	this.external.pageHeaders = new Array();
	if(this.footerText != undefined) {
		var x = this.getHorizontalCoordination(this.footerText.alignment);
		var y = footerVerticalCenter - this.footerText.fontSize / 2;
		
		this.external.addFooterHeader(
			this.footerText.text,
			this.footerText.fontFamily,
			this.footerText.fontFace,
			this.footerText.fontSize,
			this.footerText.color, x, y, true,
			this.footerText.alignment);
	}
	if(this.headerText != undefined) {
		var x = this.getHorizontalCoordination(this.headerText.alignment);
		var y = headerVerticalCenter - this.headerText.fontSize / 2;
		this.external.addFooterHeader(
			this.headerText.text,
			this.headerText.fontFamily,
			this.headerText.fontFace,
			this.headerText.fontSize,
			this.headerText.color, x, y, false,
			this.headerText.alignment);
	}
	if(this.pageNumber != undefined) {
		var x = this.getHorizontalCoordination(this.pageNumber.alignment);
		var y = this.pageNumber.atFooter ?
		        footerVerticalCenter - this.pageNumber.fontSize / 2 :
		        headerVerticalCenter - this.pageNumber.fontSize / 2;
		
		this.external.addFooterHeader(
			"\\p", this.pageNumber.fontFamily, this.pageNumber.fontFace,
			this.pageNumber.fontSize, this.pageNumber.color, x, y,
			this.pageNumber.atFooter, this.pageNumber.alignment);
	}
}


AOM.PDFGenerator.prototype.getHorizontalCoordination = function(alignment)
{
	var x;
	var pageWidth = Number(this.pageLayout.dimension[0]);
    var leftMargin = Number(this.pageLayout.margins[2]);
    var rightMargin = Number(this.pageLayout.margins[3]);
    
	switch(alignment) {
		case AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_MIDDLE:
			x = (pageWidth - leftMargin - rightMargin) / 2.0;
			break;
		case AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_RIGHT:
			x = (pageWidth - leftMargin - rightMargin);
			break;
		case AOM.PDFGenerator.HORIZONTAL_ALIGNMENT_LEFT:
		default:
			x = 0;
	}
	
	return x;
}

AOM.PDFGenerator.prototype.validatePageLayout = function()
{
	var anyError = AOM.CSConstant.LAYOUT_OK;

	var fontRatio = AOM.PDFGenerator.computeFontRatio(this.fontSize, this.thumbnailLayout.height);
    // To Do: This is a bit code duplication with computeThumbnailedImageSize.
    var protectArea = (fontRatio == 0 ? 0 : 0.02);
	var width = this.thumbnailLayout.width;
    var height = this.thumbnailLayout.height * (1.0 - fontRatio - protectArea);
    if(width <= 8)
		anyError = AOM.CSConstant.LAYOUT_WIDTH_ERROR;
	else if(height <= 8)
		anyError = AOM.CSConstant.LAYOUT_HEIGHT_ERROR;
		
	return anyError;
}


/**
 *  Detect if the system fonts are deleted between two Bridge live sessions, and during
 *  a Bridge live session (This still has some problem due to Bridge's integration of
 *  CoolType can not correctly detect system font change after system font has been
 *  loaded.
 *
 *  As long as the font folder is untouched, or is changed by only install new fonts,
 *  this function returns "true". It is a rare operation for a user to delete font
 *  so this function in most cases returns "true".
 */
AOM.PDFGenerator.prototype.validateSysFontExists = function() {
	var error;
	if(this.external.displayFilename &&
	   error = this.sysFontExists(this.external.fontFamily, this.external.fontFace)) {
		this.onStopErr = AOM.localizeWithArgs(
			error,
			[AOM.localize("$$$/ContactSheet/javascript/title/fileName=Filename"),
			 this.external.fontFamily,
			 this.external.fontFace]
		);
		return false;
	}
	if(this.pageNumber &&
	   error = this.sysFontExists(this.pageNumber.fontFamily, this.pageNumber.fontFace)) {
		this.onStopErr = AOM.localizeWithArgs(
			error,
			[AOM.localize("$$$/ContactSheet/javascript/title/pageNumberTitle=Page number"),
			 this.pageNumber.fontFamily,
			 this.pageNumber.fontFace]
		);
		return false;
	}
	if(this.headerText &&
	   this.headerText.text.length > 0 &&
	   error = this.sysFontExists(this.headerText.fontFamily, this.headerText.fontFace)) {
		this.onStopErr = AOM.localizeWithArgs(
			error,
			[AOM.localize("$$$/ContactSheet/javascript/title/header=Header"),
			 this.headerText.fontFamily,
			 this.headerText.fontFace]
		);
		return false;
	}
	if(this.footerText &&
	   this.footerText.text.length > 0 &&
	   error = this.sysFontExists(this.footerText.fontFamily, this.footerText.fontFace)) {
		this.onStopErr = AOM.localizeWithArgs(
			error,
			[AOM.localize("$$$/ContactSheet/javascript/title/footer=Footer"),
			 this.footerText.fontFamily,
			 this.footerText.fontFace]
		);
		return false;
	}
	if(this.external.watermark &&
	   this.external.watermark.insertText &&
	   error = this.sysFontExists(this.external.watermark.fontFamily, this.external.watermark.fontFace)) {
		this.onStopErr = AOM.localizeWithArgs(
			error,
			[AOM.localize("$$$/ContactSheet/javascript/title/watermark=Watermark"),
			 this.external.watermark.fontFamily,
			 this.external.watermark.fontFace]
		);
		return false;
	}
	return true;
}

/**
 *  Detect if the given font family and face available in the system. This function
 *  returns "undefined" and has no side-effect to the rest of AOM if the font family
 *  and face is available, which is the case most of time.
 *
 *  When detecting a face (or a family) is not available, it deletes the corresponding
 *  item from all font face dropdown lists (or from all font family dropdown lists).
 *  However, it does not always succeed to detect a font's inexistence after the AOM
 *  system font list has already been initialized (i.e.
 *  AOM.SysFontGroupHelper.getSysFontFamilyList() has been invoked), due to a CoolType
 *  out-of-synchronization with system fonts. In such cases, the program proceed normally
 *  but the font in the generated PDF may be substitued with another font or the text may
 *  be displayed as dots in Acrobat.
 */
AOM.PDFGenerator.prototype.sysFontExists = function(family, face) {
	var faces = this.external.getSystemFontFaces(family);
	if(faces == undefined || faces.length == 0) {
		AOM.SysFontGroupHelper.removeItemFromSysFontFamilyList(family);
		return AOM.PDFGenerator.MSG_INEXISTENT_FONT_FAMILY;
	}
	for(var i = 0; i < faces.length; ++i) {
		// Compare against the programmatic value, rather than the
		// display value.
		var pair = faces[i].split("|||");
		if(pair.length == 2 && pair[1] == face)
			return;
	}
	AOM.SysFontGroupHelper.removeItemFromSysFontFamilyList();
	return AOM.PDFGenerator.MSG_INEXISTENT_FONT_FACE;
}


AOM.PDFGenerator.prototype.exportPDF = function(filename, majorVer, minorVer, imageCount, preview)
{
	var taskManager;
	if(this.interactive != false)
		taskManager = new AOM.TaskManager("GeneratePDF" + this.documentID, AOM.localize("$$$/MediaGallery/Javascript/PDF/DialogTitle=Generate PDF Contact Sheet"));
	else
		taskManager = new AOM.TaskManager();

	taskManager.configureCancel({button: AOM.localize("$$$/MediaGallery/Javascript/PDF/CancelButton=Cancel") })
	
	this.filename = filename;
	var generator = this;
	
	generator.isPreview = preview;
	
	if(this.onStop != undefined &&
       this.onStopDetail != undefined) {
        var stopDetail = function() {
            if((preview || generator.viewPDF) && generator.onStopCode > 0 &&
               (generator.errorList == undefined || generator.errorList.length == 0)) {
                taskManager.showProgressOnStop = false;
            }
            return generator.onStopDetail();
        }
        taskManager.configureOnStop({
            description: AOM.localize("$$$/MediaGallery/Javascript/PDF/Processed=Contact Sheet Processed."),
            detail: stopDetail,
            action: this.onStop
        });
    }
    
    var preValidate = function() {
	    if(generator.thumbnailLayout == undefined) {
            generator.onStopCode = -3;
            taskManager.stop();
            return;
        }

		// The external object is not initialized when the task manager does not start.
		// So the validation to font existence should not be prior this point.
        if(!generator.validateSysFontExists()) {
			generator.onStopCode = -5;
			taskManager.stop();
			return;
        }
	
    	generator.onStopCode = 0;
    }
	
	var generatePDF = function() {
	    if(generator.onStopCode <= 0)
	        return;
	
	    var target = File(filename);
    	if(target.exists) {
    		var attemptRemove = target.remove();
    		if(!attemptRemove) {
    		    generator.onStopCode = -1;
    			return;
    		}
    	} else {
    		var targetFolder = target.parent;
    		if(!targetFolder.exists) {
    		    generator.onStopCode = -2;
    			return;
    		}
    	}
	    var i;
		for(i = 0; i < generator.images.length; ++i) {
			// Currently Bridge DOM supports exporting 8-bit images only,
			// so we don't bother to retrieve the bit-per-component from
			// the meta-data but hard code it as 8.			
    		generator.external.addImage(
    			generator.images[i].thumbFilename,
    			generator.images[i].thumbImageSize,
    			8,
    			generator.images[i].annotation,
    			generator.images[i].colorMode);
    	}
    	generator.external.setPageLayout(
    		generator.pageLayout.dimension,
    		generator.pageLayout.arrange,
    		generator.pageLayout.margins,
    		generator.pageLayout.spacings,
    		generator.pageLayout.footerHeight,
    		generator.pageLayout.headerHeight);
    	generator.external.setAnnotationRatio(
    			AOM.PDFGenerator.computeFontRatio(
                    generator.fontSize, generator.thumbnailLayout.height));
        generator.processFooterHeader();
        
    	var exported = generator.external.exportPDF(filename, majorVer, minorVer);
    	
    	if(exported < 0)
            generator.onStopCode = -4;
    }
    
	var setXMP = function() {
	    if (generator.onStopCode <= 0)
	        return;
	
	    var target = File(filename);
    	if (!target.exists)
			return;

		var dest = new Thumbnail(target);
		
		try {
			// Init AdobeXMPScript external object 
			AOM.XMPUtil.loadXMPLib();
			
			// Get synchronous destination thumbnail XMPMeta object.
			var destXMPMeta = new XMPMeta(dest.synchronousMetadata.serialize());
			
			// Update some metadata infos.
			var appName = "Adobe Bridge " + AOM.CS_VERSION + " (" + (File.fs == "Macintosh" ? "Macintosh" : "Windows") + ")";
			AOM.XMPUtil.setStringProp(destXMPMeta, XMPConst.NS_XMP, "CreatorTool", appName);
					
			// Write metadata back to the destination file.
			var xmp = new XMPFile(dest.path, XMPConst.UNKNOWN, XMPConst.OPEN_FOR_UPDATE);
			if (xmp.canPutXMP(destXMPMeta)) {
				xmp.putXMP(destXMPMeta);
			}
			xmp.closeFile(XMPConst.CLOSE_UPDATE_SAFELY);
		}
		catch(e) {
			// Do nothing on error as it's not mandatory to write xmp into the file
		}
	}
	
    var cleanUp = function() {
        if(generator.onStopCode <= 0)
            return;
		
    	if(generator.external.watermark && generator.external.watermark.intermediaFileName) {
	    	var wmFile = File(generator.external.watermark.intermediaFileName);
	    	if(wmFile.exists)
	    		wmFile.remove();
	    }
	    generator.imageCache.update();
    }
    
    var selections = this.document.getSelection();
    taskManager.startGroup(AOM.localize("$$$/MediaGallery/Javascript/PDF/Generating=Generating PDF ..."), 3 + selections.length);
    taskManager.addTask(preValidate, AOM.localize("$$$/MediaGallery/Javascript/PDF/PreparePDF=Prepare PDF ..."));
    
    var imageSet = new Array();
    if(imageCount == undefined)
        imageCount = selections.length;
    
    this.finishedTask = 0;
    
    for(var index = 0; index < selections.length; ++index) {
		var currentImage = {};
		currentImage.name = selections[index].name;
		currentImage.thumbnail = selections[index];
		currentImage.thumbnail.index = index;
		selections[index] = null;
        
		var demandInfo = {};
		demandInfo.rotation = 0;
		demandInfo.colorProfilePreserved = this.preserveColorProfile;
			
		var fontRatio = AOM.PDFGenerator.computeFontRatio(this.fontSize, this.thumbnailLayout.height);
		var protectArea = (fontRatio == 0 ? 0 : 0.02);
		  
		var stThumbImagePart = new Object;
		stThumbImagePart.width = this.thumbnailLayout.width;
		stThumbImagePart.height = this.thumbnailLayout.height * (1.0 - fontRatio - protectArea);
		
		demandInfo.rotateToFit = this.rotateToFit;
		demandInfo.height = stThumbImagePart.height;
		demandInfo.width = stThumbImagePart.width;
		demandInfo.fullResolution = this.ppi == 0 || this.ppi == undefined;
		demandInfo.jpegQuality = this.jpegQuality;
		if (preview) {
			this.ppi = 72;
			demandInfo.fullResolution = false;
		}
		if(!demandInfo.fullResolution || preview) {
			demandInfo.height *= (Number(this.ppi) / 72.0);
			demandInfo.width *= (Number(this.ppi) / 72.0);
		}
		
		var result;
		if(preview) {
			result = this.imageCache.findDemand(currentImage.thumbnail, demandInfo, 1.1, 20);
		} else {
			result = this.imageCache.findDemand(currentImage.thumbnail, demandInfo, 1, 0);
		}
		
		currentImage.thumbnail.exist = false;
		if (result.originalFileSupported) {
			if(result.cacheImagePath != undefined) {
				var imageFile = new File(result.cacheImagePath);
				if (imageFile.exists) {
			        currentImage.thumbnail.cacheImagePath = result.cacheImagePath;
			        currentImage.thumbnail.cacheImageName = result.cacheImageName;
			        currentImage.thumbnail.exist = true;
			    } else {
			    	this.imageCache.releaseByIndex(result.index);
				}
			}
			currentImage.thumbnail.demandInfo = demandInfo;
			imageSet.push(currentImage);
		} else {
			this.errorList.push(currentImage.thumbnail.path);
		}
	}
	
	this.taskCount = 0;
	var addImage = function() {
        generator.loadSelectedOne(imageSet, imageCount, taskManager);
    }
	var tempAtTail = true;
	var atHead = true;
	if (imageSet.length == 0)
		tempAtTail = false;
    for(var j = 0; j < imageSet.length; j++) {
    	if(!imageSet[j].thumbnail.exist) {
			tempAtTail = false;
			this.taskCount++;
			if (atHead && j >= imageCount)
				taskManager.addTask(addImage, AOM.localizeWithArgs("$$$/MediaGallery/Javascript/PDF/ProcessImage=Process image: (1) ...", "$$$/MediaGallery/Javascript/PDF/ExistentImages=Loading images from cache"));
			else
				taskManager.addTask(addImage, AOM.localizeWithArgs("$$$/MediaGallery/Javascript/PDF/ProcessImage=Process image: (1) ...", imageSet[j].thumbnail.name));
			atHead = false;
		} else {
			tempAtTail = true;
		}
	}
	if(tempAtTail == true)
	{
		this.taskCount++;
		taskManager.addTask(addImage, AOM.localizeWithArgs("$$$/MediaGallery/Javascript/PDF/ProcessImage=Process image: (1) ...", "$$$/MediaGallery/Javascript/PDF/ExistentImages=Loading images from cache"));
	}
	
	taskManager.addTask(generatePDF, AOM.localize("$$$/MediaGallery/Javascript/PDF/GeneratingPDF=Generating PDF Contact Sheet ..."));
	
	if (preview) {
		var generatePreview = function()
		{
			if(generator.onStopCode <= 0)
				return;
	    
			var bd = new BitmapData(filename);
		
			var previewFolder = File(filename).path + "/preview" + generator.documentID;
			if(!Folder(previewFolder).exists)
				Folder(previewFolder).create();  
			var f = new File(previewFolder + "/preview.jpg");
			if (f.exists)
				f.remove();
			bd.exportTo(f);
			
			var cssFile = new File(AOM.CS.Paths.resources + "htmls/preview.css");
			var htmlFile = new File(AOM.CS.Paths.resources + "htmls/preview.htm");
			cssFile.copy(previewFolder + "/preview.css");
			
			/* htmlFile.copy(previewFolder + "/preview.htm");
			 *
			 * Change background brightness to align the Image Backdrop. */
			AOM.previewPalette(generator.document).display(
				AOM.PreviewPanel.previewHtml(
					htmlFile.fsName, new File(previewFolder + "/preview.htm")));
		}
	    taskManager.addTask(generatePreview, AOM.localize("$$$/MediaGallery/Javascript/PDF/CreatePreview=Create preview..."));
    }
	else {
		taskManager.addTask(setXMP, AOM.localize("$$$/MediaGallery/Javascript/PDF/GeneratingPDF=Generating PDF Contact Sheet ..."));
	}
	
    taskManager.addTask(cleanUp, AOM.localize("$$$/MediaGallery/Javascript/PDF/Completing=Completing generation ..."));
    taskManager.start();
}




/**
 *  Retrieve the ratio (0.00 - 1.00) by which the annotation part
 *  ocuppies the Contact Sheet thumbnail height.
 *
 *  Parameters:
 *      fontSize: Type integer.
 *      stThumbHeight: Type integer.
 */
AOM.PDFGenerator.computeFontRatio = function(fontSize, stThumbHeight)
{
	if (!stThumbHeight) stThumbHeight = 1e-5;
	return fontSize / (stThumbHeight * 0.6);
}


AOM.PDFGenerator.getSystemFontFamilies = function()
{
	var generator = new AOM.PDFGenerator();
	var result = generator.external.getSystemFontFamilies();
	generator = null;
	return result;
}



AOM.PDFGenerator.getColorMode = function(metaData)
{
	metaData.namespace = "http://ns.adobe.com/photoshop/1.0/";
	switch(metaData.ColorMode) {
		case "4":
			return "DeviceCMYK";
		case "1":
			return "DeviceGray";
		case "3":
			return "DeviceRGB";
		default:
			return undefined;
	}
}

 

AOM.PDFGenerator.getSystemFontFaces = function(fontFamilyName)

{
	var generator = new AOM.PDFGenerator();
	var result = generator.external.getSystemFontFaces(fontFamilyName);
	generator = null;
	return result;
}



/**
 * The external object accepts only integer values for some parameters. This function
 * round a float number or an array of float numbers to an integer or an array of
 * integers.
 *
 * value   a number, or a string that can be converted to a number, or an array
 *         whose each item is either a number or a string that can be converted
 *         to a number.
 *
 * To Do: If the external object is modified to accept float number, just change this
 *        function to a pass-through operation.
 */
AOM.PDFGenerator.round = function(value)
{
	if(value == undefined)
		return;
	if(value instanceof Array) {
		for(var i = 0; i < value.length; ++i) {
			value[i] = Math.round(value[i]);
		}
	} else {
		value = Math.round(value);
	}
	return value;
}


AOM.PDFGenerator.prototype.setWatermark = function(watermarkSettings)
{
	this.external.watermark = watermarkSettings;
	if(this.external.watermark.insertText == true)
		return AOM.PDFGenerator.WATERMARK_SUCCESS;

	if(this.external.watermark.insertImage == true &&
	   this.external.watermark.imagePath != undefined)
		return this.loadWatermarkImageData();
		
	return AOM.PDFGenerator.WATERMARK_SUCCESS;
}


AOM.PDFGenerator.prototype.loadWatermarkImageData = function()
{
	var imageFile = File(File.encode(this.external.watermark.imagePath));
	if(!imageFile.exists)
		return AOM.PDFGenerator.WATERMARK_IMAGE_NOT_EXIST;
	
	var bitmap = new BitmapData(imageFile, this.preserveColorProfile);
	if(bitmap.width != undefined && bitmap.height != undefined) {
		this.external.watermark.imageDimension = [bitmap.width, bitmap.height];

		var wmTempFolder = this.tempImageFolder + "watermark";
		var folder = new Folder(wmTempFolder);
		if(!folder.exists)
			folder.create();
		var wmTempImageFile = File(wmTempFolder + "/wm.jpg");
		bitmap.exportTo(wmTempImageFile.fsName, 100, this.preserveColorProfile);
		
		
		// Currently we don't need the information from the meta-data,
		// because we handle only two format in C++ code, JPEG and PNG,
		// for which we can obtain the component-per-pixel information
		// from the file directly.
		//
		// var t = new Thumbnail(File.encode(path.fsName));
		// var meta = t.synchronousMetadata;
		//
		// if(meta != undefined) {
		//	this.external.watermark.imageColorMode =
		//		AOM.PDFGenerator.getColorMode(meta);
		// }
		this.external.watermark.imageColorMode = "";

		this.external.watermark.intermediaFileName = wmTempImageFile.fsName;
		bitmap.dispose();
		return AOM.PDFGenerator.WATERMARK_SUCCESS;
	}

	return AOM.PDFGenerator.WATERMARK_IMAGE_UNSUPPORT;
}


AOM.PDFGenerator.combineDiacriticMarks = function(text)

{
	var generator = new AOM.PDFGenerator();
	var result = generator.external.combineDiacriticMarks(text);
	generator = null;
	return result;
}




/* Test Code */

/*
var g = new AOM.PDFGenerator();

g.setPageLayout([595, 842], [2, 4], [50, 50, 50, 50], [20, 20]);

// g.setFontSize(2);
// g.setFontStyle("Arial");
// g.setFontColor(255, 255, 255);
// g.setBackgroundColor(0, 0, 0);
g.setResolution(300);
// g.setRotateToFit(true);
// g.setFilename(true);
// g.setFileExtension(false);
// g.setUserPassword("abc1");
// g.setOwnerPassword("abco");
// g.setPrintPermission(true);
// g.setFullScreenMode(true);
// g.setTransitionMode("Box", 5);
// g.exportPDF("/Volumes/fengdong-1/Temp/output1.pdf", 1, 6);

// $.write("Processed: " + g.exportPDF("/Users/output_mac_1219.2.pdf", 1, 6) + "\n");
$.write("Processed: " + g.exportPDF("D:\\output_buginvest_1219.1.pdf", 1, 6) + "\n");
if(g.errorList != undefined && g.errorList.length > 0)
	$.write(g.errorList);
*/
