<?php

 /****************************************************************************

 * Software:    FPDF class extension

 *              Creates Pdf Tables

 * Version:	    3.4.3

 * Date:		2005/07/20

 * Author:	    Bintintan Andrei <andy@interpid.eu>

 *

 * Last Modification: 2007/12/07

 *

 * License: Free for Non Commercial use. 

 *          You may not sell or use this class in a Commercial Project without written permission from the Author. 

 *

 * If you like my work please DONATE!!! **** paypal@interpid.eu ***** 

 * 

 * For more classes visit ***** www.interpid.eu ***** 

 * 

 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 

 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE 

 * AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 

 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 

 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 

 * SUCH DAMAGE.

 * 

 * PLEASE REPORT ANY BUGS TO THE AUTHOR. 

 * 

 * THANK YOU. 

 * 

 */





/**

Modifications:

2007.03.03

	- added support for ROWSPAN

	- changed the class functions/variables for PHP5 

2006.09.25

    - corrected a bug for empty cell texts

    - corrected a bug for cell lines aligment error on new page

2006.05.18

    - added support for cell splitting if new page ocuurs. FPDF_TABLE::tbSetSplitMode(true/false)

    sets the splitting mode.

            true = normal mode, the cell is splitted where the split ocuurs.

            false = if splitting is required then the cell is drawed on the new page. If the cell

            Height is bigger then the page height then the cell will be splitted.

    - if the header does not have enough space for double it's Height then a new page ocuurs.

*/

require_once(dirname(__FILE__).'/class.multicelltag.php');



/**

 * FPDF_TABLE - FPDF TABLE Add On Class

 * @author 		Bintintan Andrei

 * @copyright 	2005 - 2007

 */

class fpdf_table extends fpdf_multicelltag 

{

	

/**

 * Number of Columns of the Table

 * @access 	protected

 * @var		int

 */

protected 	$iColumnsNr = 0;



/**

 *	Contains the Header Data - header characteristics and texts

 *	

 *	Characteristics constants for Header Type:

 *	EVERY CELL FROM THE TABLE IS A MULTICELL

 *

 *	WIDTH - this is the cell width. This value must be sent only to the HEADER!!!!!!!!

 *	T_COLOR - text color = array(r,g,b);

 *	T_SIZE - text size

 *	T_FONT - text font - font type = "Arial", "Times"

 *	T_ALIGN - text align - "RLCJ"

 *	V_ALIGN - text vertical alignment - "TMB"

 *	T_TYPE - text type (Bold Italic etc)

 *	LN_SPACE - space between lines

 *	BG_COLOR - background color = array(r,g,b);

 *	BRD_COLOR - border color = array(r,g,b);

 *	BRD_SIZE - border size --

 *	BRD_TYPE - border size -- up down, with border without!!! etc

 *	BRD_TYPE_NEW_PAGE - border type on new page - this is user only if specified(<>'')

 *	TEXT - header text -- THIS ALSO BELONGS ONLY TO THE HEADER!!!!

 *

 *	all these setting conform to the settings from the multicell functions!!!!	

 * 

 * @access 	protected

 * @var 	array

 */

protected 	$aTableHeaderType = array();



/**

 * Header is drawed or not

 * @access 	protected

 * @var		boolean

 */

protected	$bTableHeaderDraw = true;



/**

 * Table Border is drawed or not

 * @access 	protected

 * @var		boolean

 */

protected	$bTableBorderDraw = true;



/**

 * Table Data Characteristics 

 * @access 	protected

 * @var		array

 */

protected 	$aTableDataType = array();



/**

 * Table Characteristics 

 * @access 	protected

 * @var		array

 */

protected 	$aTableType = array();



/**

 * Page Split Variable - if the table does not have enough space on the current page then the cells will be splitted.

 * This onlu if $bTableSplit == TRUE

 * If $bTableSplit == FALSE then the current cell will be drawed on the next page

 *

 * @access 	protected

 * @var 	boolean

 */

protected	$bTableSplit = true;



/**

 * TRUE - if on current page was some data written

 * @access 	protected

 * @var		boolean

 */

protected	$bDataOnCurrentPage = false;



/**

 * Table Data Cache. Will contain the information about the rows of the table

 * @access 	protected

 * @var		array

 */

protected	$aDataCache = array();



/**

 * TRUE - if there is a Rowspan in the Data Cache

 * @access 	protected	

 * @var		boolean

 */

protected	$bRowSpanInCache = false;



/**

 * Sequence for Rowspan ID's. Every Rowspan gets a unique ID

 * @access 	protected

 * @var		int

 */

protected	$iRowSpanID = 0;



/**

 * Table Header Cache. Will contain the information about the header of the table

 * @access 	protected

 * @var		array

 */

protected	$aHeaderCache = array();



/**

 * Header Height. In user units!

 * @access 	protected

 * @var		int

 */

protected	$iHeaderHeight = 0;



/**

 * Table Start X Position

 * @access 	protected

 * @var		int

 */

protected	$iTableStartX = 0;



/**

 * Table Start Y Position

 * @access 	protected

 * @var		int

 */

protected	$iTableStartY = 0;



/**

 * CLASS FUNCTIONS 

 */



/**

 * Returns the current page Width

 *

 * @access 	protected

 * @param 	void

 * @return  integer - the Page Width

 */

protected function PageWidth(){

	return (int) $this->w - $this->rMargin - $this->lMargin;

}//function PageWidth





/**

 * Returns the current page Height

 *

 * @access 	protected

 * @param 	void

 * @return	integer - the Page Height

 */

protected function PageHeight(){

	return (int) $this->h - $this->tMargin - $this->bMargin;

}//function PageHeight







/**

 * CONSTRUCTOR

 * Table Initialization Function

 *

 * @access 	public

 * @param	integer	- $iColumnsNr - Number of Colums 

 * @param	boolean	- $bTableHeaderDraw	- true => Draw the table header on a new page

 * @param	boolean - $bTableBorderDraw - true => Draw the table border

 * @return 	null

 */

public function tbInitialize($iColumnsNr = 0, $bTableHeaderDraw = true, $bTableBorderDraw = true){



	$this->iColumnsNr = $iColumnsNr;

	$this->aTableHeaderType = Array();

	$this->bTableHeaderDraw = $bTableHeaderDraw;

	$this->aTableDataType = Array();

	

	$this->aDataCache = Array();

	$this->aHeaderCache = Array();

	

	$this->bTableBorderDraw = $bTableBorderDraw;

	$this->bTableHeaderDraw = $bTableHeaderDraw;

			

	$this->iTableStartX = $this->GetX();

	$this->iTableStartY = $this->GetY();



	$this->bDataOnCurrentPage = false;

}



/**

 * Sets the number of Columns in the Table

 *

 * @access 	public

 * @param	integer	$iNr	- the number of Columns

 * @return 	void

 */

public function tbSetColumnsNo($iNr){

	$this->iColumnsNr = $iNr;

}



/**

 * Sets the Split Mode of the Table. Default is ON(true)

 *

 * @access 	public

 * @param	boolean $bSplit - if TRUE then Split is Active

 * @return 	void

 */

public function tbSetSplitMode($bSplit = true){

	$this->bTableSplit = $bSplit;

}



/**

 * Sets the Header Type

 * 

 * $aHeaderType=

 *	 array(

 *		0=>array(

 *				"WIDTH" => 10,

 *				"T_COLOR" => array(120,120,120),

 *				"T_SIZE" => 5,

 *				...

 *				"TEXT" => "Header text 1"

 *			  ),

 *		1=>array(

 *				...

 *			  ),

 *	 );

 * 

 * 	where 0,1... are the column number

 * 

 * If the Header has multiple lines than $aHeaderType WILL HAVE TO Contain an array, which elements are composed from the array

 * desribed above.

 * 		$aHeaderType = array ($aHeaderType1, $aHeaderType2) where $aHeaderType1 and $aHeaderType2 have the above description

 *

 * @access 	public

 * @param	array	$type_arr - header type

 * @param	boolean $bMultiLines - the header has multiple lines or not

 * @return 	void

 */

public function tbSetHeaderType($aHeaderType, $bMultiLines = false){

	if ($bMultiLines == false)

		$this->aTableHeaderType[0] = $aHeaderType;

	else 

		$this->aTableHeaderType = $aHeaderType;

		

	//create the header cache data

	foreach ($this->aTableHeaderType as $val)

		$this->_tbAddDataToCache($val, 'header');	

		

	$this->_tbCacheParseRowspan(0, 'header');

		

	$this->tbHeaderHeight();

}





/**

 * Calculates the Header Height. 

 * If the Header height is bigger than the page height then the script dies. 

 * 

 * @access 	private

 * @param 	void

 * @return 	void

 */

private function tbHeaderHeight(){

	$this->iHeaderHeight = 0;

		

	$iItems = count($this->aHeaderCache);

	for ($i=0; $i< $iItems; $i++){

		$this->iHeaderHeight += $this->aHeaderCache[$i]['HEIGHT'];

	}

		

	if ($this->iHeaderHeight > $this->PageHeight()){

		die("Header Height({$this->iHeaderHeight}) bigger than Page Height({$this->PageHeight()})");

	}

}//private function tbHeaderHeight







/**

 * Calculates the X margin of the table depending on the ALIGN 

 *

 * @access 		protected

 * @param 		void

 * @return 		void

 */

protected function tbMarkMarginX(){

	

	

	if (isset($this->aTableType['TB_ALIGN'])){

		$tb_align = $this->aTableType['TB_ALIGN']; 

	}else{

		$tb_align='';

	}



	//set the table align

	switch($tb_align){

		case 'C':

			$this->iTableStartX = $this->lMargin + $this->aTableType['L_MARGIN'] + ($this->PageWidth() - $this->tbGetWidth())/2;

			break;

		case 'R':

			$this->iTableStartX = $this->lMargin + $this->aTableType['L_MARGIN'] + ($this->PageWidth() - $this->tbGetWidth());

			break;

		default:

			$this->iTableStartX = $this->lMargin + $this->aTableType['L_MARGIN'];

			break;

	}//

	

}//protected function tbMarkMarginX





/*

Characteristics constants for Data Type:

EVERY CELL FROM THE TABLE IS A MULTICELL

	T_COLOR - text color = array(r,g,b);

	T_SIZE - text size

	T_FONT - text font - font type = "Arial", "Times"

	T_ALIGN - text align - "RLCJ"

	V_ALIGN - text vertical alignment - "TMB"

	T_TYPE - text type (Bold Italic etc)

	LN_SPACE - space between lines

	BG_COLOR - background color = array(r,g,b);

	BRD_COLOR - border color = array(r,g,b);

	BRD_SIZE - border size --

	BRD_TYPE - border size -- up down, with border without!!! etc

	BRD_TYPE_NEW_PAGE - border type on new page - this is user only if specified(<>'')



	all these settings conform to the settings from the multicell functions!!!!

*/



/**

 * Sets the Data Type. This Data Type is used as the default type for datas in the entire table. Of corse the user can

 * specify for every cell some specific data types

 * $aDataType=

 *	 array(

 *		0=>array(

 *				"WIDTH" => 10,

 *				"T_COLOR" => array(120,120,120),

 *				"T_SIZE" => 5,

 *				...

 *				"TEXT" => "Header text 1"

 *			  ),

 *		1=>array(

 *				...

 *			  ),

 *	 );

 * 

 * 	where 0,1... are the column number

 * 

 * @access 	public

 * @param	array - $aDataType - Array Containing the Data Type

 * @return 	void

 */

public function tbSetDataType($aDataType){

	$this->aTableDataType = $aDataType;

}//function tbSetDataType





/**

 * Sets the Table Type

 * 

 * $aTableType = array(

 * 					"BRD_COLOR"=> array (120,120,120), //border color

 * 					"BRD_SIZE"=>5), //border line width

 * 					"iColumnsNr"=>5), //the number of columns

 * 					"TB_ALIGN"=>"L"), //the align of the table, possible values = L, R, C equivalent to Left, Right, Center

 * 					'L_MARGIN' => 0// left margin... reference from this->lmargin values

 * 					);

 * 

 * @access 	public

 * @param	array	$aTableType - array containing the Table Type

 * @return 	void

 */

public function tbSetTableType($aTableType){



	if (isset($aTableType['iColumnsNr'])) $this->iColumnsNr = $aTableType['iColumnsNr'];

	if (!isset($aTableType['L_MARGIN'])) $aTableType['L_MARGIN']=0;//default values



	$this->aTableType = $aTableType;

	$this->tbMarkMarginX();



}//function tbSetTableType





/**

 * Draws the Table Border

 * 

 * @access 	public

 * @param 	void

 * @return 	void

 */

public function tbDrawBorder(){



	if ( ! $this->bTableBorderDraw ) return;



	if ( ! $this->bDataOnCurrentPage) return; //there was no data on the current page



	//set the colors

	list($r, $g, $b) = $this->aTableType['BRD_COLOR'];

	$this->SetDrawColor($r, $g, $b);



	//set the line width

	$this->SetLineWidth($this->aTableType['BRD_SIZE']);

    

	//draw the border

	$this->Rect(

		$this->iTableStartX,

		$this->iTableStartY,

		$this->tbGetWidth(),

		$this->GetY() - $this->iTableStartY);



}//function tbDrawBorder





/**

 * End Page Special Border Draw. This is called in the case of a Page Split

 *

 * @access 	protected

 * @param 	void

 * @return 	void

 */

protected function _tbEndPageBorder(){

	if (isset($this->aTableType['BRD_TYPE_END_PAGE'])){



		if (strpos($this->aTableType['BRD_TYPE_END_PAGE'], 'B') >= 0){



			//set the colors

			list($r, $g, $b) = $this->aTableType['BRD_COLOR'];

			$this->SetDrawColor($r, $g, $b);



			//set the line width

			$this->SetLineWidth($this->aTableType['BRD_SIZE']);



			//draw the line

			$this->Line($this->table_startx, $this->GetY(), $this->table_startx + $this->tbGetWidth(), $this->GetY());

		}//fi

	}//fi

}//function _tbEndPageBorder



/**

 * Returns the table width in user units

 *

 * @access 	public

 * @param 	void

 * @return 	integer - table width

 */

public function tbGetWidth()

{

	//calculate the table width

	$tb_width = 0;

	

	for ($i=0; $i < $this->iColumnsNr; $i++){

		$tb_width += $this->aTableHeaderType[0][$i]['WIDTH'];

	}

	

	return $tb_width;

}//tbGetWidth





/**

 * Aligns the table to the Start X point

 *

 * @access 		protected

 * @param 		void

 * @return 		void

 * 

 */

protected function _tbAlign(){

	$this->SetX($this->iTableStartX);

}//function _tbAlign(){





/**

 * "Draws the Header". 

 * More specific puts the data from the Header Cache into the Data Cache

 * 

 * @access 	public

 * @param 	void

 * @return 	void

 */

public function tbDrawHeader(){

	

	foreach($this->aHeaderCache as $val){

		$this->aDataCache[] = $val;

	}

}//function tbDrawHeader





/**

 * Adds a line to the Table Data or Header Cache.

 * Call this function after the table initialization, table, header and data types are set

 * 

 * @access 	public

 * @param	array $data - Data to be Drawed

 * @param	booleab $header - Array Containing data is Header Data or Data Data

 * @return	null

 */

public function tbDrawData($data, $header = true){

	$this->_tbAddDataToCache($data);

}



/**

 * Adds the data to the cache

 *

 * @access	protected

 * @param	array	$data - array containing the data to be added

 * @param	string 	$sDataType - data type. Can be 'data' or 'header'. Depending on this data the $data is put in the selected cache

 * @return 	void

 */

protected function _tbAddDataToCache($data, $sDataType = 'data'){

	

	

	if (!is_array($data)) {

		//this is fatal error

		trigger_error("Invalid data value 0x00012. (not array)", E_USER_ERROR);

	}

	

	//IF UTF8 Support is needed then 

	if (isset($this->utf8_support) && ($this->utf8_support)){

		for($i=0; $i < $this->iColumnsNr; $i++){

			$data[$i]['TEXT'] = utf8_decode($data[$i]['TEXT']);

		}

		

		//keep the compatibility with the "old" fpdf utf8 support.

		if (isset($this->utf8_decoded)) $this->utf8_decoded = true;		

	}

		

	if ($sDataType == 'data')

		$aDataCache = & $this->aDataCache;

	elseif ($sDataType == 'header')

		$aDataCache = & $this->aHeaderCache;

		

	$aRowSpan = array();

	

	

	$hm = 0;

	$rowspan = false;

	

	/**

	 * If datacache is empty initialize it

	 */

	if (count($aDataCache) > 0) $aLastDataCache = end($aDataCache);

	else $aLastDataCache = array();

	

	//this variable will contain the active colspans

	$iActiveColspan = 0;

	

	//calculate the maximum height of the cells

	for($i=0; $i < $this->iColumnsNr; $i++){

		

        if (!isset($data[$i]['TEXT']) || ($data[$i]['TEXT']=='')) $data[$i]['TEXT'] = ' ';

		if (!isset($data[$i]['T_FONT'])) $data[$i]['T_FONT'] = $this->aTableDataType[$i]['T_FONT'];

		if (!isset($data[$i]['T_TYPE'])) $data[$i]['T_TYPE'] = $this->aTableDataType[$i]['T_TYPE'];

		if (!isset($data[$i]['T_SIZE'])) $data[$i]['T_SIZE'] = $this->aTableDataType[$i]['T_SIZE'];

		if (!isset($data[$i]['T_COLOR'])) $data[$i]['T_COLOR'] = $this->aTableDataType[$i]['T_COLOR'];

		if (!isset($data[$i]['T_ALIGN'])) $data[$i]['T_ALIGN'] = $this->aTableDataType[$i]['T_ALIGN'];

		if (!isset($data[$i]['V_ALIGN'])) $data[$i]['V_ALIGN'] = $this->aTableDataType[$i]['V_ALIGN'];

		if (!isset($data[$i]['LN_SIZE'])) $data[$i]['LN_SIZE'] = $this->aTableDataType[$i]['LN_SIZE'];

		if (!isset($data[$i]['BRD_SIZE'])) $data[$i]['BRD_SIZE'] = $this->aTableDataType[$i]['BRD_SIZE'];

		if (!isset($data[$i]['BRD_COLOR'])) $data[$i]['BRD_COLOR'] = $this->aTableDataType[$i]['BRD_COLOR'];

		if (!isset($data[$i]['BRD_TYPE'])) $data[$i]['BRD_TYPE'] = $this->aTableDataType[$i]['BRD_TYPE'];

		if (!isset($data[$i]['BG_COLOR'])) $data[$i]['BG_COLOR'] = $this->aTableDataType[$i]['BG_COLOR'];

		if (!isset($data[$i]['COLSPAN'])) $data[$i]['COLSPAN'] = 1; else $data[$i]['COLSPAN'] = (int) $data[$i]['COLSPAN'];

		if (!isset($data[$i]['ROWSPAN'])) $data[$i]['ROWSPAN'] = 1; else $data[$i]['ROWSPAN'] = (int) $data[$i]['ROWSPAN'];

		$data[$i]['HEIGHT'] = 0;	//default HEIGHT

		$data[$i]['SKIP'] = false;	//default SKIP (don't skip)

		$data[$i]['CELL_WIDTH'] = $this->aTableHeaderType[0][$i]['WIDTH'];	//copy this from the header settings

		$data[$i]['ROWSPAN_PRIMARY'] = FALSE;	//==true then this row has generated the rowspan

		$data[$i]['ROWSPAN_ID'] = 0;	//rowspan ID

		$data[$i]['HEIGHT'] = 0;	//default HEIGHT

				

		if ($data[$i]['LN_SIZE'] <= 0){

			trigger_error("Invalid Line Size {$data[$i]['LN_SIZE']}", E_USER_ERROR);

		}

		

		

		//if there is an active colspan on this line we just skip this cell

		if ($iActiveColspan > 1){

			$data[$i]['SKIP'] = true;

			//if ($i>0) $data[$i]['ROWSPAN'] = $data[$i-1]['ROWSPAN'];

			$iActiveColspan --;

			continue;

		}



		

		if (!empty($aLastDataCache)){

			

			//there was at least one row before

			

			if ( $aLastDataCache['DATA'][$i]['ROWSPAN'] > 1 ){

				/**

				 * This is rowspan over this cell. The cell will be ignored but some characteristics are kept

				 */

				

				//this cell will be skipped

				$data[$i]['SKIP'] =true;

				//decrease the rowspan value... one line less to be spanned

				$data[$i]['ROWSPAN'] = $aLastDataCache['DATA'][$i]['ROWSPAN'] - 1;

				$data[$i]['ROWSPAN_ID'] = $aLastDataCache['DATA'][$i]['ROWSPAN_ID'];

				$data[$i]['ROWSPAN_PRIMARY'] = false;

				//copy the colspan from the last value

				$data[$i]['COLSPAN'] = $aLastDataCache['DATA'][$i]['COLSPAN'];

				//cell with is the same as the one from the line before it

				$data[$i]['CELL_WIDTH'] = $aLastDataCache['DATA'][$i]['CELL_WIDTH'];

				

				if ($data[$i]['COLSPAN'] > 1){

					$iActiveColspan = $data[$i]['COLSPAN'];

				}

				

				continue; //jump to the next column

				

			}//if

			

		}//if

		

		

		//set the font settings

		$this->SetFont(	$data[$i]['T_FONT'],

						$data[$i]['T_TYPE'],

						$data[$i]['T_SIZE']);



		

		/**

		 * If we have colspan then we ignore the "colspanned" cells

		 */

		if ( $data[$i]['COLSPAN'] > 1 ){



			for ($j = 1; $j < $data[$i]['COLSPAN']; $j++){

				//if there is a colspan, then calculate the number of lines also with the with of the next cell

				if (($i + $j) < $this->iColumnsNr)

					$data[$i]['CELL_WIDTH'] += $this->aTableHeaderType[0][$i + $j]['WIDTH'];

			}//for

					

		}//if

		

		//add the cells that are with rowspan to the rowspan array - this is used later

		if ( $data[$i]['ROWSPAN'] > 1 ){

			$data[$i]['ROWSPAN_PRIMARY'] = true;

			$this->iRowSpanID++;

			$data[$i]['ROWSPAN_ID'] = $this->iRowSpanID;

			$aRowSpan[] = $i;

		}

       

		

        //$MaxLines = floor($AvailPageH / $data[$i]['LN_SIZE']);//floor this value, must be the lowest possible

        

        if (!isset($data[$i]['TEXT_STRLINES'])) $data[$i]['TEXT_STRLINES'] = $this->mt_StringToLines($data[$i]['CELL_WIDTH'], $data[$i]['TEXT']);

        $data[$i]['CELL_LINES'] = count($data[$i]['TEXT_STRLINES']);

        

        /**

         * IF THERE IS ROWSPAN ACTIVE Don't include this cell Height in the calculation. 

         * This will be calculated later with the sum of all heights

         */

        

        $data[$i]['HEIGHT'] = $data[$i]['LN_SIZE'] * $data[$i]['CELL_LINES'];

        

        if ( $data[$i]['ROWSPAN'] == 1 ){

        	$hm = max($hm, $data[$i]['HEIGHT']);//this would be the normal height

        }

        

		if ( $data[$i]['COLSPAN'] > 1 ){

			//just skip the other cells

			$iActiveColspan = $data[$i]['COLSPAN'];

		}//if

		

	}//for($i=0; $i < $this->iColumnsNr; $i++)	

	

	

	$aDataCache[] = array(

		'HEIGHT' => $hm,	//THIS LINE MAXIMUM HEIGHT

		'DEFAULT_HEIGHT' => $hm,	//THIS LINE DEFAULT MAXIMUM HEIGHT

		'DEFAULT_HEIGHT_SET' => true,

		'DATATYPE' => $sDataType,	//The data Type - Data/Header

		'DATA' => $data,	//this line's data

		'ROWSPAN' => $aRowSpan	//rowspan ID array

	);

	

	//we set the rowspan in cache variable to true if we have a rowspan

	if (! empty($aRowSpan) && (!$this->bRowSpanInCache)){

		$this->bRowSpanInCache = true;

	}

	

	return;

}//function _tbAddDataToCache







/**

 * Parses the Data Cache and calculates the maximum Height of each row. Normally the cell Height of a row is calculated 

 * when the data's are added, but when that row is involved in a Rowspan then it's Height can change!

 *

 * @access 	protected

 * @param	integer	$iStartIndex - the index from which to parse

 * @param	string	$sCacheType - what type has the cache - possible values: 'header' && 'data'

 * @return	void

 */

protected function _tbCacheParseRowspan($iStartIndex = 0, $sCacheType = 'data'){

	

	if ($sCacheType == 'data')

		$aDataCache = & $this->aDataCache;

	else 

		$aDataCache = & $this->aHeaderCache;

	

	$aRowSpans = array();

	

	$iItems = count($aDataCache);

	

	for ($ix = $iStartIndex; $ix < $iItems; $ix++){

		

		$val = & $aDataCache[$ix];

	

		if (!in_array($val['DATATYPE'], array('data', 'header') )) continue;

		

		//if there is no rowspan jump over

		if (empty($val['ROWSPAN'])) continue;

		

		foreach ($val['ROWSPAN'] as $k){

			

			#$val['HEIGHT'] = $val['DEFAULT_HEIGHT'];			

			

			if ($val['DATA'][$k]['ROWSPAN'] < 1) continue;	//skip the rows without rowspan

			

			/**

			if ($val['DEFAULT_HEIGHT_SET'] == false){

				$val['HEIGHT'] = $val['DEFAULT_HEIGHT'];

			}

			*/

			

			$aRowSpans[] = array(

				'row_id' => $ix,

				'cell_id' => &$val['DATA'][$k]

			);

			

			$h_rows = 0;

			

			//calculate the sum of the Heights for the lines that are included in the rowspan

			for ( $i=0; $i < $val['DATA'][$k]['ROWSPAN']; $i++){

				if (isset($aDataCache[$ix + $i]))

					$h_rows += $aDataCache[$ix + $i]['HEIGHT'];

			}

			

			//this is the cell height that makes the rowspan

			$h_cell = $val['DATA'][$k]['HEIGHT'];

			

			//if the 

			//$val['DATA'][$k]['HEIGHT_MAX'] = max($h_cell, $h_rows);

			

			/**

			 * The Rowspan Cell's Height is bigger than the sum of the Rows Heights that he is spanning

			 * In this case we have to increase the height of each row

			 */

			if ($h_cell > $h_rows){

				//calculate the value of the HEIGHT to be added to each row

				$add_on = ($h_cell - $h_rows) / $val['DATA'][$k]['ROWSPAN'];

				for ( $i=0; $i < $val['DATA'][$k]['ROWSPAN']; $i++){

					if (isset($aDataCache[$ix + $i])){

						$aDataCache[$ix + $i]['HEIGHT'] += $add_on;

						$aDataCache[$ix + $i]['DEFAULT_HEIGHT_SET'] = false;

					}

				}//for

			}//

			

		}//foreach

	}//foreach

	

	

	

	

	/**

	 * Calculate the height of each cell that makes the rowspan. 

	 * The height of this cell is the sum of the heights of the rows where the rowspan occurs

	 */

	

	foreach ($aRowSpans as $val1){

		$h_rows = 0;

		//calculate the sum of the Heights for the lines that are included in the rowspan

		for ( $i=0; $i < $val1['cell_id']['ROWSPAN']; $i++){

			if (isset($aDataCache[$val1['row_id'] + $i]))

				$h_rows += $aDataCache[$val1['row_id'] + $i]['HEIGHT'];

		}

		$val1['cell_id']['HEIGHT_MAX'] = $h_rows;

		if (false == $this->bTableSplit){

		    $aDataCache[$val1['row_id']]['HEIGHT_ROWSPAN'] = $h_rows;

		}

	}

	

}//function _tbCacheParseRowspan





/**

 * Splits a cell into 2 cells. The first cell will have maximum $iHeightMax height

 *

 * @access 		protected

 * @param		array - $aCellData - array containing cell data

 * @param 		integer - $iRowHeight - the Height of the row that contains this cell

 * @param		integer - $iHeightMax - the maximum Height of the first cell

 * @return 		$aNewData - the second cell value

 */

protected function tbSplitCell(&$aCellData, $iHeightRow = 0, $iHeightMax = 0){

	

	//$aTData will contain the second cell data

	$aCell2Data = $aCellData;

	$fHeightSplit = 0;		//The Height where the split will be made

	

	/**

	 * Have to look at the V_ALIGN of the cells and calculate exaclty for each cell how much space is left

	 */

	switch($aCellData['V_ALIGN']){

		case 'M':

			//Middle align

			$x = ($iHeightRow - $aCellData['HEIGHT']) / 2;



			if ($iHeightMax  <= $x){

				//CASE 1

				$fHeightSplit = 0;

				$aCellData['V_OFFSET'] = $x - $iHeightMax;

				$aCellData['V_ALIGN'] = 'T';//top align

				

			}elseif( ($x + $aCellData['HEIGHT']) >= $iHeightMax){

				//CASE 2

				$fHeightSplit = $iHeightMax - $x;

				$aCellData['V_ALIGN'] = 'B';//top align

				$aCell2Data['V_ALIGN'] = 'T';//top align

			}else{//{

				//CASE 3

				$fHeightSplit = $iHeightMax;

				$aCellData['V_OFFSET'] = $x;

				$aCellData['V_ALIGN'] = 'B';//bottom align

			}

													

			break;

		case 'B':

			//Bottom Align

			if (($iHeightRow - $aCellData['HEIGHT']) > $iHeightMax){

				//if the text has enough place on the other page then we show nothing on this page

				$fHeightSplit = 0;

			}else{

				//calculate the space that the text needs on this page 

				$fHeightSplit = $iHeightMax - ($iHeightRow - $aCellData['HEIGHT']);

			}

			

			break;

			

		case 'T':

		default:

			//Top Align and default align 

			$fHeightSplit = $iHeightMax;

			break;

	}



	//calculate the number of the lines that have space on the $fHeightSplit

	$iNoLinesCPage = floor($fHeightSplit / $aCellData['LN_SIZE']);

	//if the number of the lines is bigger than the number of the lines in the cell decrease the number of the lines

	if ($iNoLinesCPage > $aCellData['CELL_LINES']){

		$iNoLinesCPage = $aCellData['CELL_LINES'];

	}

										

	$aCellData['TEXT_SPLITLINES'] = array_splice($aCellData['TEXT_STRLINES'], $iNoLinesCPage);

	#$aCellData['CELL_LINES'] = $iNoLinesCPage;

	$aCellData['CELL_LINES'] = count($aCellData['TEXT_STRLINES']);

	

	//calculate the new height for this cell

	$aCellData['HEIGHT'] = $aCellData['LN_SIZE'] * $aCellData['CELL_LINES'];

	

	#$fRowH = max($fRowH, $aData[$j]['HEIGHT'] );

	

	//this is the second cell from the splitted one

	$aCell2Data['TEXT_STRLINES'] = $aCellData['TEXT_SPLITLINES'];

	$aCell2Data['CELL_LINES'] = count ($aCell2Data['TEXT_STRLINES']);

	$aCell2Data['HEIGHT'] = $aCell2Data['LN_SIZE'] * $aCell2Data['CELL_LINES'];

	

	return array($aCell2Data, $fHeightSplit);

				

}//function tbSplitCell()



/**

 * Splits the Data Cache into Pages. 

 * Parses the Data Cache and when it is needed then a "new page" command is inserted into the Data Cache. 

 *

 * @access 	protected

 * @param 	void

 * @return 	void

 */

protected function _tbCachePaginate(){



	$iPageHeight = $this->PageHeight();



	/**

	 * This Variable will contain the remained page Height

	 */

	$iLeftHeight = $iPageHeight - $this->GetY() + $this->tMargin;



	//the number of lines that the header contains

	if ($this->bTableHeaderDraw){

		$iHeaderLines = count($this->aHeaderCache);	

	}else {

		$iHeaderLines = 0;

	}

		

	$bWasData = true;		//can be deleted

	$iLastOkKey = -1;		//can be deleted

	

	$bDataOnThisPage = false;

	$bHeaderOnThisPage = false;

	$iLastDataKey = 0;



	

	//will contain the rowspans on the current page, EMPTY THIS VARIABLE AT EVERY NEW PAGE!!!

	$aRowSpans = array();

	

	$aDC = & $this->aDataCache;

	

	$iItems = count($aDC);

	

	for ($i=0; $i < $iItems; $i++){

		

		$val = & $aDC[$i];

		

		$bIsHeader = $val['DATATYPE'] == 'header';

		

		if (($bIsHeader) && ($bWasData)){

			$iLastDataKey = $iLastOkKey;

		}//fi

		

		if (isset($val['ROWSPAN'])){

			

			foreach ($val['ROWSPAN'] as $k=>$v){

				$aRowSpans[] = array($i, $v);

				$aDC[$i]['DATA'][$v]['HEIGHT_LEFT_RW'] = $iLeftHeight;

			}//foreach

			

		}//fi

		

		$iLeftHeightLast = $iLeftHeight;

		

		$iRowHeight = $val['HEIGHT'];

		$iRowHeightRowspan = 0;

		if ((false == $this->bTableSplit) && (isset($val['HEIGHT_ROWSPAN']))){

		    $iRowHeightRowspan = $val['HEIGHT_ROWSPAN'];

		}

		

        $iLeftHeightRowspan = $iLeftHeight - $iRowHeightRowspan;				

		$iLeftHeight -= $iRowHeight;



		if (($iLeftHeight >= 0) && ($iLeftHeightRowspan >=0)){

			//this row has enough space on the page

			if (true == $bIsHeader) {

				$bHeaderOnThisPage = true;

			}else{

				$iLastDataKey = $i;

				$bDataOnThisPage = true;

			}

			$iLastOkKey = $i;

			$bLastOkType = $bIsHeader;

			

		}else{

							

			/**

			 * THERE IS NOT ENOUGH SPACE ON THIS PAGE - HAVE TO SPLIT

			 * Decide the split type

			 * 

			 * SITUATION 1:

			 * IF

			 * 		- the current data type is header OR

			 * 		- on this page we had no data(that means untill this point was nothing or just header) AND bTableSplit is off

			 * THEN we just add new page on the positions of LAST DATA KEY ($iLastDataKey)

			 * 

			 * SITUATION 2:

			 * IF

			 * 		- TableSplit is OFF and the height of the current data is bigger than the Page Height minus (-) Header Height

			 * THEN we split the current cell

			 * 

			 * SITUATION 3:

			 * 		- normal split flow

			 *  

			 */

			

			//use this switch for flow control

			switch(1){

				case 1:

				

				//SITUATION 1:

				if ((true == $bIsHeader) OR ((false == $bHeaderOnThisPage) AND (false == $bDataOnThisPage) AND (false == $this->bTableSplit)) ){

					$iItems = $this->tbInsertNewPage($iLastDataKey, null, (!$bIsHeader) && (!$bHeaderOnThisPage));

					break;//exit from switch(1);

				}



				$bSplitCommand = $this->bTableSplit;

				

				//SITUATION 2:

				if ($val['HEIGHT'] > ($iPageHeight - $this->iHeaderHeight)){

					//even if the bTableSplit is OFF - split the data!!!

					$bSplitCommand = true;

				}



				if (true == $bSplitCommand){

				/***************************************************

				 * * * * * * * * * * * * * * * * * * * * * * * * * * 

				 * SPLIT IS ACTIVE

				 * * * * * * * * * * * * * * * * * * * * * * * * * * 

				 ***************************************************/

								

					//if we can draw on this page at least one line from the cells

					$bAtLeastOneLine = false;

					

					$aData = $val['DATA'];

					

					$fRowH = $iLeftHeightLast;

					#$fRowH = 0;

					$fRowHTdata = 0;

					

					$aTData = array();

					

					//parse the data's on this line

					for( $j=0; $j < $this->iColumnsNr; $j++){

						

						$aTData[$j] = $aData[$j];

						

						/**

						 * The cell is Skipped or is a Rowspan. For active split we handle rowspanned cells later

						 */

						if (($aData[$j]['SKIP'] === TRUE) || ($aData[$j]['ROWSPAN'] > 1)) continue;

						

						list($aTData[$j]) = $this->tbSplitCell($aData[$j], $val['HEIGHT'], $iLeftHeightLast);

						

						$fRowH = max($fRowH, $aData[$j]['HEIGHT'] );

						$fRowHTdata = max($fRowHTdata, $aTData[$j]['HEIGHT'] );

										

					}//for

					

					$val['HEIGHT'] = $fRowH;

					$val['DATA'] = $aData;				

					

					$v_new = $val;

					$v_new['HEIGHT'] = $fRowHTdata;

					$v_new['ROWSPAN'] = array();

					/**

					 * Parse separately the rows with the ROWSPAN

					 */

					

					

					$bNeedParseCache = false;

					

					$aRowSpan = $aDC[$i]['ROWSPAN'];

					

					

					foreach ($aRowSpans as $rws_key => $rws){

										

						$rData = & $aDC[$rws[0]]['DATA'][$rws[1]];

															

						if ($rData['HEIGHT_MAX'] > $rData['HEIGHT_LEFT_RW']){

							/**

							 * This cell has a rowspan in IT

							 * We have to split this cell only if its height is bigger than the space to the end of page

							 * that was set when the cell was parsed. HEIGHT_LEFT_RW 

							 */

													

							list($aTData[$rws[1]], $fHeightSplit)  = $this->tbSplitCell($rData, $rData['HEIGHT_MAX'] , $rData['HEIGHT_LEFT_RW']);

				

							$rData['HEIGHT_MAX'] = $rData['HEIGHT_LEFT_RW'];

												

							$aTData[$rws[1]]['ROWSPAN'] = $aTData[$rws[1]]['ROWSPAN'] - ($i - $rws[0]);

							

							$v_new['ROWSPAN'][] = $rws[1];

							

							$bNeedParseCache = true;												

						}//fi

					}//foreach

					

					$v_new['DATA'] = $aTData;

					

					//Insert the new page, and get the new number of the lines

					$iItems = $this->tbInsertNewPage($i, $v_new);

					

					if ($bNeedParseCache) $this->_tbCacheParseRowspan($i+1);

					

				}else{

					

				/***************************************************

				 * * * * * * * * * * * * * * * * * * * * * * * * * * 

				 * SPLIT IS INACTIVE

				 * * * * * * * * * * * * * * * * * * * * * * * * * * 

				 ***************************************************/

	

					/**

					 * Check if we have a rowspan that needs to be splitted

					 */

					

					#var_dump($aRowSpans); die();

					$bNeedParseCache = false;

					

					$aRowSpan = $aDC[$i]['ROWSPAN'];

					

					foreach ($aRowSpans as $rws){

										

						$rData = & $aDC[$rws[0]]['DATA'][$rws[1]];

						

						if ($rws[0] == $i)continue;	//means that this was added at the last line, that will not appear on this page

										

						if ($rData['HEIGHT_MAX'] > $rData['HEIGHT_LEFT_RW']){

							/**

							 * This cell has a rowspan in IT

							 * We have to split this cell only if its height is bigger than the space to the end of page

							 * that was set when the cell was parsed. HEIGHT_LEFT_RW 

							 */

							

							list($aTData, $fHeightSplit)  = $this->tbSplitCell($rData, $rData['HEIGHT_MAX'] , $rData['HEIGHT_LEFT_RW'] - $iLeftHeightLast);

				

							$rData['HEIGHT_MAX'] = $rData['HEIGHT_LEFT_RW'] - $iLeftHeightLast;

												

							$aTData['ROWSPAN'] = $aTData['ROWSPAN'] - ($i - $rws[0]);

							

							$aDC[$i]['DATA'][$rws[1]] = $aTData;

							

							$aRowSpan[] = $rws[1];

							$aDC[$i]['ROWSPAN'] = $aRowSpan;

							

							$bNeedParseCache = true;

													

						}//fi

					}//for

					

					if ($bNeedParseCache) $this->_tbCacheParseRowspan($i);

	

					//Insert the new page, and get the new number of the lines

					$iItems = $this->tbInsertNewPage($i);

	

				}//else				

							

				

			}//switch(1);

			

			$iLeftHeight = $iPageHeight;

			$aRowSpans = array();

			$bDataOnThisPage = false;	//new page

			

		}//else

		

		

	}//for

	

}//function _tbCachePaginate







/**

 * Inserts a new page in the Data Cache, after the specified Index. If sent then also a new data is inserted after the new page

 *

 * @access 		protected

 * @param		integer	- $iIndex - after this index is the new page inserted

 * @param		resource - $rNewData - default null. If specified this data is inserted after the new page

 * @param 		boolean	- $bInsertHeader - true then the header is inserted, false - no header is inserted

 * @return 		integer - the new number of lines that the Data Cache Contains. 

 */

protected function tbInsertNewPage($iIndex, $rNewData = null, $bInsertHeader = true){

	

	//the number of lines that the header contains

	if ((true == $this->bTableHeaderDraw) && (true == $bInsertHeader)){

		$iHeaderLines = count($this->aHeaderCache);	

	}else {

		$iHeaderLines = 0;

	}

	

	$aDC = & $this->aDataCache;

	$iItems = count($aDC);		//the number of elements in the cache



	//if we have a NewData to be inserted after the new page then we have to shift the data with 1

	if( null != $rNewData) $iShift = 1;

	else $iShift = 0;

	

	//shift the array with the number of lines that the header contains + one line for the new page

	for($j = $iItems; $j > $iIndex; $j--){

		$aDC[$j + $iHeaderLines + $iShift] = $aDC[$j - 1];

	}//for

		

	$aDC[$iIndex + $iShift] = array(

	    'HEIGHT' => 0, 

	    'DATATYPE' => 'new_page',

	);

	

	$j = $iShift;

	

	

	if ($iHeaderLines > 0){

		//only if we have a header

		

		//insert the header into the corresponding positions

		foreach ($this->aHeaderCache as $rHeaderVal){

			$j++;

			$aDC[$iIndex + $j] = $rHeaderVal;

		}//foreach

		

	}//fi

	

	if( 1 == $iShift ){

		$j++;

		$aDC[$iIndex + $j] = $rNewData;	

	}//fi

	

	/**/

	$this->bDataOnCurrentPage = false;

	

	return count($aDC);

	

}//function tbInsertNewPage







/**

 * Sends all the Data Cache to the PDF Document. 

 * This is the REAL Function that Outputs the table data to the pdf document

 *

 * @access 	protected

 * @param 	void

 * @return 	void

 */

protected function _tbCachePrepOutputData(){

	

	$aDataCache = & $this->aDataCache;

			

	$iItems = count($aDataCache);

	

	for ($k=0; $k < $iItems; $k++){

		

	$val = & $aDataCache[$k];

		

		//each array contains one line

		$this->_tbAlign();

		

		if ($val['DATATYPE'] == 'new_page') {

			//add a new page

			$this->tbAddPage();

			continue;

		}

		

		$data = &$val['DATA'];

				

		//Draw the cells of the row

		for( $i = 0; $i < $this->iColumnsNr; $i++ )

		{

			

			//Save the current position

			$x=$this->GetX();

			$y=$this->GetY();

			

			if ($data[$i]['SKIP'] === FALSE){

				

				if (isset($data[$i]['HEIGHT_MAX'])) 

					$h = $data[$i]['HEIGHT_MAX'];

				else 

					$h = $val['HEIGHT'];

				

			

				//border size BRD_SIZE

				$this->SetLineWidth($data[$i]['BRD_SIZE']);

		

				//fill color = BG_COLOR

				list($r, $g, $b) = $data[$i]['BG_COLOR'];

				$this->SetFillColor($r, $g, $b);

		

				//Draw Color = BRD_COLOR

				list($r, $g, $b) = $data[$i]['BRD_COLOR'];

				$this->SetDrawColor($r, $g, $b);

		

				//Text Color = T_COLOR

				list($r, $g, $b) = $data[$i]['T_COLOR'];

				$this->SetTextColor($r, $g, $b);

		

				//Set the font, font type and size

				$this->SetFont(	$data[$i]['T_FONT'],

								$data[$i]['T_TYPE'],

								$data[$i]['T_SIZE']);

			

				//print the text

				$this->tbMultiCellTbl(

						$data[$i]['CELL_WIDTH'],

						$data[$i]['LN_SIZE'],

						$data[$i]['TEXT_STRLINES'],

						$data[$i]['BRD_TYPE'],

						$data[$i]['T_ALIGN'],

						$data[$i]['V_ALIGN'],

						1,

						$h - $data[$i]['HEIGHT']

						);

			}

	

			//Put the position to the right of the cell

			$this->SetXY($x + $data[$i]['CELL_WIDTH'],$y);

	

			//if we have colspan, just ignore the next cells

			if (isset($data[$i]['COLSPAN'])){

				$i = $i + (int) $data[$i]['COLSPAN'] - 1;

			}

	

		}//for

	

		$this->bDataOnCurrentPage = true;

	

		//Go to the next line

		$this->Ln($val['HEIGHT']);

	}//foreach	

	

}//function _tbCachePrepOutputData





/**

 * Prepares the cache for Output.

 * 

 * Parses the cache for Rowspans, Paginates the cache and then send the data to the pdf document

 * @access 	protected

 * @param 	void

 * @return 	void

 */

protected function _tbCachePrepOutput(){

	

	if ($this->bRowSpanInCache) $this->_tbCacheParseRowspan();

	

	$this->_tbCachePaginate();

	

	$this->_tbCachePrepOutputData();

}







/**

 * Adds a new page in the pdf document and initializes the table and the header if necessary.

 *

 * @access 	protected

 * @param	void	 

 * @return	void

 */

protected function tbAddPage($bHeader = true){

    

	$this->tbDrawBorder();//draw the table border



    $this->_tbEndPageBorder();//if there is a special handling for end page??? this is specific for me

    

    $bUtf8Set = false;



	//IF UTF8 Support is needed then 

	if (isset($this->utf8_support) && ($this->utf8_support)){

		//keep the compatibility with the "old" fpdf utf8 support.

		if (isset($this->utf8_decoded)){

			$old_utf8_decoded = $this->utf8_decoded;

			$this->utf8_decoded = false;

			$bUtf8Set = true;

		}		

	}    

    

    $this->AddPage($this->CurOrientation);//add a new page

    

	//IF UTF8 Support is needed then

	if ($bUtf8Set){

		 $this->utf8_decoded = $old_utf8_decoded;

	}



    $this->bDataOnCurrentPage = false;



    $this->iTableStartX = $this->GetX();

    $this->iTableStartY = $this->GetY();

    $this->tbMarkMarginX();

    

}//function tbAddPage





/**   This method allows printing text with line breaks.

      It works like a modified MultiCell

      Call:

      @param

                        $w - width

                        $h - line height

                        $txtData - the outputed text

                        $border - border(LRTB 0 or 1)

                        $align - horizontal align 'JLR'

                        $valign - Vertical Alignment - Top, Middle, Bottom

                        $fill - fill (1/0)

                        $vh - vertical adjustment - the Multicell Height will be with this VH Higher!!!!

                        $vtop - vertical top add-on

      @return           nothing

*/

/**

 * Ouputs a Table Cell. It works like a modified MultiCell.

 *

 * @param 	integer $w	- cell width

 * @param	integer $h	- line height

 * @param	array	$txtData - variable that contains the data to be outputted. This data is already formatted!!!

 * @param	string 	$border - border(LRTB 0 or 1)

 * @param	string 	$align - horizontal align 'JLR'

 * @param 	string 	$valign - Vertical Alignment - Top, Middle, Bottom

 * @param 	string	$fill - Cell Fill (0 no Fill, 1 fill)

 * @param	integer	$vh	- Vertical Adjustment	- the Multicell Height will be with this VH Higher!!!!

 * @param	integer	$vtop - vertical top add-on

 * @param	integer $pad_left - Cell Pad left - NOT IMPLEMENTED

 * @param 	integer $pad_top - Cell Pad left - NOT IMPLEMENTED

 * @param 	integer $pad_right - Cell Pad left - NOT IMPLEMENTED

 * @param 	integer $pad_bottom - Cell Pad left - NOT IMPLEMENTED

 * @return 	void

 */

function tbMultiCellTbl($w, $h, $txtData, $border=0, $align='J', $valign='T', $fill=0, $vh=0, $vtop=0, $pad_left=0, $pad_top=0, $pad_right=0, $pad_bottom=0){



	$b1 = '';//border for top cell

	$b2 = '';//border for middle cell

	$b3 = '';//border for bottom cell

	$wh_Top = 0;

	

	if ($vtop > 0){//if this parameter is set

		if($vtop < $vh){//only if the top add-on is bigger than the add-width

			$wh_Top = $vtop;

			$vh = $vh - $vtop;

		}

	}

	

	if($border)

	{

		if($border==1)

		{

			$border = 'LTRB';

			$b1 = 'LRT';//without the bottom

			$b2 = 'LR';//without the top and bottom

			$b3 = 'LRB';//without the top

		}

		else

		{

			$b2='';

			if(is_int(strpos($border,'L')))

				$b2.='L';

			if(is_int(strpos($border,'R')))

				$b2.='R';

			$b1=is_int(strpos($border,'T')) ? $b2.'T' : $b2;

			$b3=is_int(strpos($border,'B')) ? $b2.'B' : $b2;



		}

	}

	

	if (empty($txtData)){

		//draw the top borders!!!

		$this->Cell($w,$vh,'',$border,2,$align,$fill);//19.01.2007 - andy

		return;

	}

	

	

	



	switch ($valign){

		case 'T':

			$wh_T = $wh_Top;//Top width

			$wh_B = $vh - $wh_T;//Bottom width

			break;

		case 'M':

			$wh_T = $wh_Top + $vh/2;

			$wh_B = $vh/2;

			break;

		case 'B':

			$wh_T = $wh_Top + $vh;

			$wh_B = 0;

			break;

		default://default is TOP ALIGN

			$wh_T = $wh_Top;//Top width

			$wh_B = $vh - $wh_T;//Bottom width

	}



	//save the X position

	$x = $this->x;

	/*

		if $wh_T == 0 that means that we have no vertical adjustments so I will skip the cells that

		draws the top and bottom borders

	*/



	if ($wh_T > 0)//only when there is a difference

	{

		//draw the top borders!!!

		$this->Cell($w,$wh_T,'',$b1,2,$align,$fill);//19.01.2007 - andy

	}

	

	$b2 = is_int(strpos($border,'T')) && ($wh_T == 0) ? $b2.'T' : $b2;

	$b2 = is_int(strpos($border,'B')) && ($wh_B == 0) ? $b2.'B' : $b2;		

	$this->MultiCellTag($w, $h, $txtData, $b2, $align, 1, $pad_left, $pad_top, $pad_right, $pad_bottom, false);



	if ($wh_B > 0){//only when there is a difference



		//go to the saved X position

		//a multicell always runs to the begin of line

		$this->x = $x;



		$this->Cell($w, $wh_B, '', $b3, 2, $align,$fill);//19.01.2007 - andy



		#$this->x = $this->lMargin;//andy 23.02.2006

		$this->x = $x;

	}



}//function tbMultiCellTbl





/**

 * Sends to the pdf document the cache data

 * 

 * @access 	public

 * @param 	void

 * @return 	void

 */

public function tbOuputData(){

	$this->_tbCachePrepOutput();



	//IF UTF8 Support is needed then 

	if (isset($this->utf8_support) && ($this->utf8_support)){

		//keep the compatibility with the "old" fpdf utf8 support.

		if (isset($this->utf8_decoded)) $this->utf8_decoded = false;		

	}

}//function tbOuputData





}//end of pdf_table class



?>