﻿//------------------------------------------------------------------------------
//
// ADOBE SYSTEMS INCORPORATED
// Copyright 2003 Adobe Systems Incorporated 
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute 
// this file in accordance with the terms of the Adobe license 
// agreement accompanying it. If you have received this file 
// from a source other than Adobe, then your use, modification, 
// or distribution of it requires the prior written permission 
// of Adobe.
//
//------------------------------------------------------------------------------


///////////////////////////////////////////////////////////////////////////
//
// PUBLIC
//

//-----------------------------------------------------------------------------
// 
// RTFParser(...)
// 
// Purpose: ctor
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

function RTFParser( rtfGroup )
{
	this.rtf		= rtfGroup;		// tokenized rtf stream
	this.fontTable	= null;			// fonts
	this.colorTable	= null;			// colors
	this.tabChar	= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';	// tab characters

	this.source		= '';			// the output source

	this.paragraphs	= new Array;	// Array of all paragraphs	
	this.curPar		= null;			// current paragraph
	
	this.entryIndex = 0;			// current parsing entry
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: parse tokenized stream
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.parse = function()
{
	if( this.rtf != null )
	{
		this.openParagraph();
		
		this.parseGroup( this.rtf );

		this.closeParagraph();
		
		//
		// optimization
		//
		
		for( var i=0; i<this.paragraphs.length; i++ )
		{
			// remove empty style tags which contain styles without any contents like e.g. '...<b></b>...'
			this.paragraphs[i].removeEmptyStyles();
			// replace charcters consits of space with &nbsp;
			this.paragraphs[i].replaceSpaces();
		}
	}
	
	return true;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: parse a rtf Group
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.parseGroup = function( rtfGroup )
{
	for( var i=0; i<rtfGroup.entries.length; i++ )
	{
		var entry = rtfGroup.entries[i];
		
		if( entry.constructor.name == 'RTFEntry' )
		{
			this.parseEntry( entry );
		}
		else
		{
			switch( entry.type )
			{
				case '':
					this.parseGroup( entry );
					break;
					
				case 'field':
				{
					this.parseGroup( entry );
				}
				break;
				
				case 'destination':
				{
					if( entry.parentGroup.type == 'field' && entry.entries.length > 0 && entry.entries[0].name == 'fldinst' )
					{
						if( rtf2html[32][6] )
						{
							var path = '';
							
							for( var j=0; j<entry.entries.length && path == ''; j++ )
							{
								//
								// find sub group of 'fldinst' destination
								//
								if( entry.entries[j].constructor.name == 'RTFGroup' )
								{
									for( var k=0; k<entry.entries[j].entries.length && path == ''; k++ )
									{
										//
										// find path string
										//
										if( entry.entries[j].entries[k].type == 'character' )
										{
											var imagetext = entry.entries[j].entries[k].value;
											
											while( imagetext[0] == ' ' )
												imagetext = imagetext.substr( 1, imagetext.length-1 );
												
											if( imagetext.search( 'INCLUDEPICTURE' ) == 0 ) 
											{
												imagetext = imagetext.substr( 14, imagetext.length-14 );
												
												var quote = false;
												var slash = false;
												
												for( var p=0; p<imagetext.length; p++ )
												{
													if( imagetext[p] == '"' )
													{
														quote = !quote;
													}
													else if( quote )
													{
														if( !slash || imagetext[p] != '\\' )
															path += imagetext[p];
															
														slash = imagetext[p] == '\\';
													}
												}
											}
											else if( imagetext.search( '\\import' ) == 0 ||
													imagetext.search( '\\import' ) == 1 )
											{
												imagetext = imagetext.substr( 7, imagetext.length-7 );
												
												while( imagetext[0] == ' ' )
													imagetext = imagetext.substr( 1, imagetext.length-1 );
												
												var slash = false;
													
												for( var i=0; i<imagetext.length; i++ )
												{
													if( !slash || imagetext[i] != '\\' )
														path += imagetext[i];
														
													slash = imagetext[i] == '\\';
												}
											}
										}
									}
								}
							}
							
							//
							// insert image
							//
							if( path != '' )
							{
								var file = new JSXFile( path );
								
								if( file.exists )
									path = file.absurl;
									
								var imgEntry = new RTFEntry( 'ctrlWord', 'img', null )
								imgEntry.mlvalue = path;
								var mapEntry = this.getEntryForCtrlWord( imgEntry.name );
								this.addTag( imgEntry, mapEntry );
							}
						}
					}
				}
				break;
				
			}
		}
	}
	
	return true;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: parse a rtf entry
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.parseEntry = function( rtfEntry )
{
	if( rtfEntry.constructor.name == 'RTFEntry' )
	{
		switch( rtfEntry.type )
		{
			case 'character':
			{
				this.addText( rtfEntry.value );
			}
			break;
			
			case 'ctrlWord':
			{
				var mapEntry = this.getEntryForCtrlWord( rtfEntry.name );
				
				if( mapEntry != null )
				{
					switch( mapEntry[3] )
					{
						case 'paragraph':
							this.openParagraph( true );
							break;
							
						case 'style':
						{
							if( mapEntry[6] )
								this.addStyle( rtfEntry );
						}
						break;
						
						case 'palign':
						{
							if( mapEntry[6] )
							{
								var alignment = 'left';
								
								switch( mapEntry[0] )
								{
									case 'qc':
										alignment = 'center';
										break;
									case 'ql':
										alignment = 'left';
										break;
									case 'qr':
										alignment = 'right';
										break;
								}
								
								if( this.curPar.alignment != alignment )
								{
									this.openParagraph( true );
									this.curPar.alignment = alignment;
								}
							}
						}
						break;
						
						case 'ctrl':
						{
							this.processCtrl( rtfEntry, mapEntry );
						}
						break;
						
						case 'tag':
							this.addTag( rtfEntry, mapEntry );
							break;
					}
				}
			}
			break;
			
			case 'ctrlSymbol':
			{
				var mapEntry = this.getEntryForCtrlSymbol( rtfEntry.name.charCodeAt(0) );
				
				if( mapEntry != null )
				{
					switch( mapEntry[3] )
					{
						case 'tag':
							this.addTag( rtfEntry, mapEntry );
							break;
					}
				}
			}
			break;
		}
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: return source code
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.getSource = function()
{
	var sourceCode = this.source;
	
	if( sourceCode == '' )
	{
		for( var i=0; i<this.paragraphs.length; i++ )
			this.source += this.paragraphs[i].getSource();
			
		sourceCode = this.source;
	}
	
	return sourceCode;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: add text to output stream
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.addText = function( text )
{
	this.curPar.addText( app.encodeHTML(text) );
	return true;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: add style to output stream
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.addStyle = function( style )
{
	var mapEntry = this.getEntryForCtrlWord( style.name );
	
	if( mapEntry == null )
		mapEntry = this.getEntryForCtrlSymbol( style.name.charCodeAt(0) );
	
	if( mapEntry != null && this.curPar != null )
	{
		switch( mapEntry[1] )
		{
			case 'font':
			{
				switch( mapEntry[0] )
				{
					case 'f':
					{
						this.setupFontTable();
						style.mlvalue = this.fontTable[style.value];
						this.curPar.openStyle( style, mapEntry );
					}
					break;
					
					case 'fs':
					{
						style.mlvalue = parseInt( Number( style.value / 10 ).toString() );
						this.curPar.openStyle( style, mapEntry );
					}
					break;
			
					case 'cf':
					{
						this.setupColorTable();
						style.mlvalue = this.colorTable[style.value];
						this.curPar.openStyle( style, mapEntry );
					}
					break;
				}		
			}
			break;
			
			default:
			{
				if( style.value == 0 )
					this.curPar.closeStyle( style, mapEntry, true );
				else
					this.curPar.openStyle( style, mapEntry );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: add tag to output stream
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.addTag = function( rtfEntry, mapEntry )
{
	if( mapEntry[2] )
	{
		this.curPar.addItem( rtfEntry, mapEntry, true );
	}
	else
	{
		this.curPar.addItem( rtfEntry, mapEntry, true );
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: process control word
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.processCtrl = function( rtfEntry, mapEntry )
{
	switch( mapEntry[0] )
	{
		case 'pard':
			this.curPar.closeAllStyles();
			this.curPar.alignment = 'left';
			break;
		
		case 'tab':
			this.curPar.addText( this.tabChar );
			break;
			
		case 'deftab':
		{
			this.tabChar = '';
			for( var i=0; i<rtfEntry.value/72; i++ )
				this.tabChar += '&nbsp;';
		}
		break;
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: open new paragraph (and close last one)
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.openParagraph = function( copyStyles, copyAlignment )
{
	if( copyStyles == null || copyStyles == undefined )
		copyStyles = false;
	if( copyAlignment == null || copyAlignment == undefined )
		copyAlignment = false;
		
	// 
	// first close last paragraph and get its styles
	//
	var styles = this.closeParagraph( copyStyles );
	
	var pMapEntry = this.getEntryForCtrlWord( 'par' );
	this.curPar = new HTMLItem( null, pMapEntry, true );

	//
	// add styles from previous paragraph
	//		
	if( styles != null )
	{
		for( var i=0; i<styles.length; i++ )
		{
			this.curPar.addItem( styles[i].rtfEntry, styles[i].mapEntry, true );
		}
	}
	
	//
	// get paragraph alignment from previous paragraph
	//
	if( copyAlignment && this.paragraphs.length > 0 )
	{
		this.curPar.alignment = this.paragraphs[this.paragraphs.length-1].alignment;
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: close paragraph
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.closeParagraph = function( copyStyles )
{
	if( copyStyles == null || copyStyles == undefined )
		copyStyles = false;

	var styles = null;
			
	if( this.curPar != null )
	{
		styles = this.curPar.closeAllStyles();
		
		if( !copyStyles )
			styles = null;
		
		this.paragraphs.push( this.curPar );
	}
	
	return styles;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: get mapping entry for control word
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.getEntryForCtrlWord = function( word )
{
	var entry = null;
	
	for( var i=0; i<rtf2html.length && entry == null; i++ )
	{
		if( rtf2html[i][0] == word )
			entry = rtf2html[i];
	}
	
	return entry;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: get mapping entry for control symbol
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.getEntryForCtrlSymbol = function( symbol )
{
	var entry = null;
	
	for( var i=0; i<rtfSymbol2html.length && entry == null; i++ )
	{
		if( rtfSymbol2html[i][0] == symbol )
			entry = rtfSymbol2html[i];
	}
	
	return entry;
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: setup font table if not setup yet
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.setupFontTable = function()
{
	if( this.fontTable == null )
	{
		//
		// array of font names
		//
		this.fontTable = new Array;
		
		//
		// process first font table group
		//
		var ftGroup = null;
		
		for( var i=0; i<this.rtf.entries.length && ftGroup == null; i++ )
		{
			if( this.rtf.entries[i].type == 'fonttable' )
				ftGroup = this.rtf.entries[i];
		}
		
		if( ftGroup != null )
		{
			///////////////////////////////////////////////////////////////////////////
			// each single font could be in a separated sub group or inside this group
			//
			
			//
			// process each font group of font table
			//
			var fontIndex = -1;
			
			for( var i=0; i<ftGroup.entries.length; i++ )
			{
				if( ftGroup.entries[i].type == 'font' )
				{
					var name = null;
					
					for( var j=1; j<ftGroup.entries[i].entries.length; j++ )
					{
						if( ftGroup.entries[i].entries[j].type == 'name' )
							name = ftGroup.entries[i].entries[j].name;
					}
					
					if( name != null )
						this.fontTable[ftGroup.entries[i].entries[0].value] = name;
				}
				else
				{
					if( ftGroup.entries[i].constructor.name != 'RTFGroup' )
					{
						if( ftGroup.entries[i].name == 'f' )
							fontIndex = ftGroup.entries[i].value;
							
						if( fontIndex>-1 && ftGroup.entries[i].type == 'name' )
						{
							this.fontTable[fontIndex] = ftGroup.entries[i].name;
							fontIndex = -1;
						}
					}
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// 
// function(...)
// 
// Purpose: setup color table
// 
// Author : pwollek
// 
//-----------------------------------------------------------------------------

RTFParser.prototype.setupColorTable = function()
{
	if( this.colorTable == null )
	{
		//
		// array of font names
		//
		this.colorTable = new Array;
		
		//
		// process first font table group
		//
		var ctGroup = null;
		
		for( var i=0; i<this.rtf.entries.length && ctGroup == null; i++ )
		{
			if( this.rtf.entries[i].type == 'colortable' )
				ctGroup = this.rtf.entries[i];
		}
		
		if( ctGroup != null )
		{
			var red = -1;
			var blue = -1;
			var green = -1;
			
			for( var i=0; i<ctGroup.entries.length; i++ )
			{
				switch( ctGroup.entries[i].name )
				{
					case 'red':
					{
						if( red == -1 )
							red = ctGroup.entries[i].value;
					}
					break;
					
					case 'blue':
					{
						if( blue == -1 )
							blue = ctGroup.entries[i].value;
					}
					break;
					
					case 'green':
					{
						if( green == -1 )
							green = ctGroup.entries[i].value;
					}
					break;
					
				}
				
				if( red != -1 && blue != -1 && green != -1 )
				{
					var value = '#';
					
					var tmp = Number(red).toString(16);
					if( tmp.length < 2 )
						tmp = '0' + tmp;
					value += tmp;
					
					tmp = Number(green).toString(16);
					if( tmp.length < 2 )
						tmp = '0' + tmp;
					value += tmp;
					
					tmp = Number(blue).toString(16);
					if( tmp.length < 2 )
						tmp = '0' + tmp;
					value += tmp;
					
					this.colorTable.push( value );

					red = -1;
					blue = -1;
					green = -1;
				}
			}
		}
	}
}