<?php
//
//  fpdf_tpl - Version 1.0.2
//
//    Copyright 2004,2005 Setasign - Jan Slabon
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

require_once("fpdf.php");

class own extends fpdf {
    /**
     * Array of Tpl-Data
     * @var array
     */
    var $tpls = array();

    /**
     * Current Template-ID
     * @var int
     */
    var $tpl = 0;
    
    /**
     * "In Template"-Flag
     * @var boolean
     */
    var $intpl = false;
    
    /**
     * Nameprefix of Templates used in Resources-Dictonary
     * @var string A String defining the Prefix used as Template-Object-Names. Have to beginn with an /
     */
    var $tplprefix = "/TPL";

    /**
     * Nameprefix of Fonts used in Resources-Dictonary
     * (not realy needed, but for future versions with import-function needed)
     * @var string
     */
    var $fontprefix = "/F";

    /**
     * Resources used By Templates and Pages
     * @var array
     */
    var $res = array();
    
    /**
     * Constructor
     * See FPDF-Documentation
     * @param string $orientation
     * @param string $unit
     * @param mixed $format
     */
    function fpdf_tpl($orientation='P',$unit='mm',$format='A4') {
        parent::fpdf($orientation,$unit,$format);
    }
    
    /**
     * Start a Template
     *
     * This method starts a template. You can give own coordinates to build an own sized
     * Template. Pay attention, that the margins are adapted to the new templatesize.
     * If you want to write outside the template, for example to build a clipped Template,
     * you have to set the Margins and "Cursor"-Position manual after beginTemplate-Call.
     *
     * If no parameter is given, the template uses the current page-size.
     * The Method returns an ID of the current Template. This ID is used later for using this template.
     * Warning: A created Template is used in PDF at all events. Still if you don't use it after creation!
     *
     * @param int $x The x-coordinate given in user-unit
     * @param int $y The y-coordinate given in user-unit
     * @param int $w The width given in user-unit
     * @param int $h The height given in user-unit
     * @return int The ID of new created Template
     */
    function beginTemplate($x=null,$y=null,$w=null,$h=null) {
        if ($this->page <= 0)
            $this->error("You have to add a page to fpdf first!");

        // Save settings
        $this->tpl++;
        $this->tpls[$this->tpl]['o_x'] = $this->x;
        $this->tpls[$this->tpl]['o_y'] = $this->y;
        $this->tpls[$this->tpl]['o_AutoPageBreak'] = $this->AutoPageBreak;
        $this->tpls[$this->tpl]['o_bMargin'] = $this->bMargin;
        $this->tpls[$this->tpl]['o_tMargin'] = $this->tMargin;
        $this->tpls[$this->tpl]['o_lMargin'] = $this->lMargin;
        $this->tpls[$this->tpl]['o_rMargin'] = $this->rMargin;
        $this->tpls[$this->tpl]['o_h'] = $this->h;
        $this->tpls[$this->tpl]['o_w'] = $this->w;

        $this->SetAutoPageBreak(false);
        
        if ($x == null)
            $x = 0;
        if ($y == null)
            $y = 0;
        if ($w == null)
            $w = $this->w;
        if ($h == null)
            $h = $this->h;

        // Define own high and width to calculate possitions correct
        $this->h = $h;
        $this->w = $w;

        $this->tpls[$this->tpl]['buffer'] = "";
        $this->tpls[$this->tpl]['x'] = $x;
        $this->tpls[$this->tpl]['y'] = $y;
        $this->tpls[$this->tpl]['w'] = $w;
        $this->tpls[$this->tpl]['h'] = $h;

        $this->intpl = true;
        $this->SetXY($x+$this->lMargin,$y+$this->tMargin);
        $this->SetRightMargin($this->w-$w+$this->rMargin);

        return $this->tpl;
    }
    
    /**
     * End Template
     *
     * This method ends a template and reset initiated variables on beginTemplate.
     *
     * @return mixed If a template is opened, the ID is returned. If not a false is returned.
     */
    function endTemplate() {
        if ($this->intpl) {
            $this->intpl = false;
            $this->SetAutoPageBreak($this->tpls[$this->tpl]['o_AutoPageBreak'],$this->tpls[$this->tpl]['o_bMargin']);
            $this->SetXY($this->tpls[$this->tpl]['o_x'],$this->tpls[$this->tpl]['o_y']);
            $this->tMargin = $this->tpls[$this->tpl]['o_tMargin'];
            $this->lMargin = $this->tpls[$this->tpl]['o_lMargin'];
            $this->rMargin = $this->tpls[$this->tpl]['o_rMargin'];
            $this->h = $this->tpls[$this->tpl]['o_h'];
            $this->w = $this->tpls[$this->tpl]['o_w'];
            return $this->tpl;
        } else {
            return false;
        }
    }
    
    /**
     * Use a Template in current Page or other Template
     *
     * You can use a template in a page or in another template.
     * You can give the used template a new size like you use the Image()-method.
     * All parameters are optional. The width or height is calculated automaticaly
     * if one is given. If no parameter is given the origin size as defined in
     * beginTemplate() is used.
     * The calculated or used width and height are returned as an array.
     *
     * @param int $tplidx A valid template-Id
     * @param int $_x The x-position
     * @param int $_y The y-position
     * @param int $_w The new width of the template
     * @param int $_h The new height of the template
     * @retrun array The height and width of the template
     */
    function useTemplate($tplidx, $_x=null, $_y=null, $_w=0, $_h=0) {
        if ($this->page <= 0)
            $this->error("You have to add a page to fpdf first!");

        if (!$this->tpls[$tplidx])
            $this->error("Template does not exist!");
            
        if ($this->intpl) {
            $this->res['tpl'][$this->tpl]['tpls'][$tplidx] =& $this->tpls[$tplidx];
        }
        extract($this->tpls[$tplidx]);

        if ($_x == null)
            $_x = $x;
        if ($_y == null)
            $_y = $y;
        $wh = $this->getTemplateSize($tplidx,$_w,$_h);
        $_w = $wh['w'];
        $_h = $wh['h'];

        $this->_out(sprintf("q %.4f 0 0 %.4f %.2f %.2f cm", ($_w/$w), ($_h/$h), $_x*$this->k, ($this->h-($_y+$_h))*$this->k)); // Translate
        $this->_out($this->tplprefix.$tplidx." Do Q");

        return array("w" => $_w, "h" => $_h);
    }
    
    /**
     * Get The calculated Size of a Template
     *
     * If one size is given, this method calculates the other one.
     *
     * @param int $tplidx A valid template-Id
     * @param int $_w The width of the template
     * @param int $_h The height of the template
     * @return array The height and width of the template
     */
    function getTemplateSize($tplidx, $_w=0, $_h=0) {
        if (!$this->tpls[$tplidx])
            return false;

        extract($this->tpls[$tplidx]);
        if ($_w == 0 and $_h == 0) {
            $_w = $w;
            $_h = $h;
        }

    	if($_w==0)
    		$_w=$_h*$w/$h;
    	if($_h==0)
    		$_h=$_w*$h/$w;
    		
        return array("w" => $_w, "h" => $_h);
    }
    
    /**
     * See FPDF-Documentation ;-)
     */
    function SetFont($family,$style='',$size=0) {
        //Select a font; size given in points
    	global $fpdf_charwidths;

    	$family=strtolower($family);
    	if($family=='')
    		$family=$this->FontFamily;
    	if($family=='arial')
    		$family='helvetica';
    	elseif($family=='symbol' or $family=='zapfdingbats')
    		$style='';
    	$style=strtoupper($style);
    	if(is_int(strpos($style,'U')))
    	{
    		$this->underline=true;
    		$style=str_replace('U','',$style);
    	}
    	else
    		$this->underline=false;
    	if($style=='IB')
    		$style='BI';
    	if($size==0)
    		$size=$this->FontSizePt;
    	//Test if font is already selected
    	if($this->FontFamily==$family and $this->FontStyle==$style and $this->FontSizePt==$size and !$this->intpl)
            return;
    	//Test if used for the first time
    	$fontkey=$family.$style;
    	if(!isset($this->fonts[$fontkey]))
    	{
    		//Check if one of the standard fonts
    		if(isset($this->CoreFonts[$fontkey]))
    		{
    			if(!isset($fpdf_charwidths[$fontkey]))
    			{
    				//Load metric file
    				$file=$family;
    				if($family=='times' or $family=='helvetica')
    					$file.=strtolower($style);
    				$file.='.php';
    				if(defined('FPDF_FONTPATH'))
    					$file=FPDF_FONTPATH.$file;
    				include($file);
    				if(!isset($fpdf_charwidths[$fontkey]))
    					$this->Error('Could not include font metric file');
    			}
                $i = $this->findNextAvailFont();
                $this->fonts[$fontkey]=array('i'=>$i,'type'=>'core','name'=>$this->CoreFonts[$fontkey],'up'=>-100,'ut'=>50,'cw'=>$fpdf_charwidths[$fontkey]);
    		}
    		else
    			$this->Error('Undefined font: '.$family.' '.$style);
    	}
    	//Select it
    	$this->FontFamily=$family;
    	$this->FontStyle=$style;
    	$this->FontSizePt=$size;
    	$this->FontSize=$size/$this->k;
    	$this->CurrentFont=&$this->fonts[$fontkey];
    	if($this->page>0)
    		$this->_out(sprintf('BT '.$this->fontprefix.'%d %.2f Tf ET',$this->CurrentFont['i'],$this->FontSizePt));


        if ($this->intpl) {
            $this->res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey];
        } else {
            $this->res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey];
        }
    }
    
    /**
     * Find the next available Font-No.
     *
     * @return int
     */
    function findNextAvailFont() {
        return count($this->fonts)+1;
    }

    /**
     * See FPDF-Documentation ;-)
     */
    function Image($file,$x,$y,$w=0,$h=0,$type='',$link='') {
        parent::Image($file,$x,$y,$w,$h,$type,$link);
        if ($this->intpl) {
            $this->res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file];
        } else {
            $this->res['page'][$this->page]['images'][$file] =& $this->images[$file];
        }
    }
    
    /**
     * See FPDF-Documentation ;-)
     *
     * AddPage is not available when you're "in" a template.
     */
    function AddPage($orientation='') {
        if ($this->intpl)
            $this->Error('Adding pages in templates isn\'t possible!');
        parent::AddPage($orientation);
    }

    /**
     * Preserve adding Links in Templates ...won't work
     */
    function Link($x,$y,$w,$h,$link) {
        if ($this->intpl)
            $this->Error('Using links in templates aren\'t possible!');
        parent::Link($x,$y,$w,$h,$link);
    }
    
    function AddLink() {
        if ($this->intpl)
            $this->Error('Adding links in templates aren\'t possible!');
        return parent::AddLink();
    }
    
    function SetLink($link,$y=0,$page=-1) {
        if ($this->intpl)
            $this->Error('Setting links in templates aren\'t possible!');
        parent::SetLink($link,$y,$page);
    }
    
    /**
     * Private Method that writes the Resources-Objects
     */
    function _puttemplates() {
        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
	    reset($this->tpls);
        foreach($this->tpls AS $tplidx => $tpl) {

            $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
    		$this->_newobj();
    		$this->tpls[$tplidx]['n'] = $this->n;
    		$this->_out('<<'.$filter.'/Type /XObject');
            $this->_out('/Subtype /Form');
            $this->_out('/FormType 1');
            $this->_out(sprintf('/BBox [%.2f %.2f %.2f %.2f]',$tpl['x']*$this->k, ($tpl['h']-$tpl['y'])*$this->k, $tpl['w']*$this->k, ($tpl['h']-$tpl['y']-$tpl['h'])*$this->k));       // ($this->h-$tpl['y'])*$this->k
            $this->_out('/Resources ');

            $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
        	if (count($this->res['tpl'][$tplidx]['fonts'])) {
            	$this->_out('/Font <<');
                foreach($this->res['tpl'][$tplidx]['fonts'] as $font)
            		$this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R');
            	$this->_out('>>');
            }
        	if(count($this->res['tpl'][$tplidx]['images']) || count($this->res['tpl'][$tplidx]['tpls']))
        	{
                $this->_out('/XObject <<');
                if (count($this->res['tpl'][$tplidx]['images'])) {
                    foreach($this->res['tpl'][$tplidx]['images'] as $image)
              			$this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
                }
                if (count($this->res['tpl'][$tplidx]['tpls'])) {
                    foreach($this->res['tpl'][$tplidx]['tpls'] as $i => $tpl)
                        $this->_out($this->tplprefix.$i.' '.$tpl['n'].' 0 R');
                }
                $this->_out('>>');
        	}
        	$this->_out('>>');
        	
        	$this->_out('/Length '.strlen($p).' >>');
    		$this->_putstream($p);
    		$this->_out('endobj');
        }
    }
    
    /**
     * Private Method
     */
    function _putresources() {
    	$this->_putfonts();
    	$this->_putimages();
        $this->_puttemplates();
        //Resource dictionary
    	$this->offsets[2]=strlen($this->buffer);
    	$this->_out('2 0 obj');
    	$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
    	$this->_out('/Font <<');
        foreach($this->fonts as $font)
        	$this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R');
    	$this->_out('>>');
    	if(count($this->images) || count($this->tpls))
    	{
    		$this->_out('/XObject <<');
            if (count($this->images)) {
                foreach($this->images as $image)
        			$this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
            }
            if (count($this->tpls)) {
                foreach($this->tpls as $tplidx => $tpl)
        			$this->_out($this->tplprefix.$tplidx.' '.$tpl['n'].' 0 R');
            }
            $this->_out('>>');
    	}
    	$this->_out('>>');
    	$this->_out('endobj');
    }


    /**
     * Private Method
     */
    function _out($s) {
	   //Add a line to the document
	   if ($this->state==2) {
           if (!$this->intpl)
	           $this->pages[$this->page].=$s."\n";
           else
               $this->tpls[$this->tpl]['buffer'] .= $s."\n";
       } else {
		   $this->buffer.=$s."\n";
       }
    }

}
require_once("fpdi_pdf_parser.php");

class own2 extends own {
    /**
     * Actual filename
     * @var string
     */
    var $current_filename;

    /**
     * Parser-Objects
     * @var array
     */
    var $parsers;
    
    /**
     * Current parser
     * @var object
     */
    var $current_parser;
    
    /**
     * FPDF/FPDI - PDF-Version
     * @var double
     */
    var $PDFVersion = 1.3;

    /**
     * Highest version of imported PDF
     * @var double
     */
    var $importVersion = 1.3;

    /**
     * object stack
     * @var array
     */
    var $obj_stack;
    
    /**
     * done object stack
     * @var array
     */
    var $don_obj_stack;

    /**
     * Current Object Id.
     * @var integer
     */
    var $current_obj_id;
    
    /**
     * Constructor
     * See FPDF-Manual
     */
    function fpdi($orientation='P',$unit='mm',$format='A4') {
        parent::fpdf_tpl($orientation,$unit,$format);
    }
    
    /**
     * Set a source-file
     *
     * @param string $filename a valid filename
     * @return int number of available pages
     */
    function setSourceFile($filename) {
        $this->current_filename = $filename;
        $fn =& $this->current_filename;

        $this->parsers[$fn] = new fpdi_pdf_parser($fn,$this);
        $this->current_parser =& $this->parsers[$fn];
        
        return $this->parsers[$fn]->getPageCount();
    }
    
    /**
     * Import a page
     *
     * @param int $pageno pagenumber
     * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
     */
    function ImportPage($pageno) {
        $fn =& $this->current_filename;
        
        $this->parsers[$fn]->setPageno($pageno);

        $this->tpl++;
        $this->tpls[$this->tpl] = array();
        $this->tpls[$this->tpl]['parser'] =& $this->parsers[$fn];
        $this->tpls[$this->tpl]['resources'] = $this->parsers[$fn]->getPageResources();
        $this->tpls[$this->tpl]['buffer'] = $this->parsers[$fn]->getContent();
        // $mediabox holds the dimensions of the source page
        $mediabox = $this->parsers[$fn]->getPageMediaBox($pageno);
        
        // To build array that can used by pdf_tpl::useTemplate()
        $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl],$mediabox);

        return $this->tpl;
    }
    
    /**
     * Private method, that rebuilds all needed objects of source files
     */
    function _putOobjects() {
        if (is_array($this->parsers) && count($this->parsers) > 0) {
            foreach($this->parsers AS $filename => $p) {
                $this->current_parser =& $this->parsers[$filename];
                if (is_array($this->obj_stack[$filename])) {
                    while($n = key($this->obj_stack[$filename])) {
                        $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c,$this->obj_stack[$filename][$n][1]);
						
                        $this->_newobj($this->obj_stack[$filename][$n][0]);
                        
                        if ($nObj[0] == PDF_TYPE_STREAM) {
							$this->pdf_write_value ($nObj);
                        } else {
                            $this->pdf_write_value ($nObj[1]);
                        }
                        
                        $this->_out('endobj');
                        $this->obj_stack[$filename][$n] = null; // free memory
                        unset($this->obj_stack[$filename][$n]);
                        reset($this->obj_stack[$filename]);
                    }
                }
            }
        }
    }
    
    /**
     * Rewritten for handling own defined PDF-Versions
     * only needed by FPDF 1.52
     */
    function _begindoc() {
    	//Start document
    	$this->state=1;
    }
    
    /**
     * Sets the PDF Version to the highest of imported documents
     */
    function setVersion() {
        if ($this->importVersion > $this->PDFVersion)
            $this->PDFVersion = $this->importVersion;

        if (!method_exists($this, '_putheader')) {
            $this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer;
		}
    }
    
    /**
     * rewritten for handling higher PDF Versions
     */
    function _enddoc() {
        $this->setVersion();
        parent::_enddoc();
    }

    
    /**
     * Put resources
     */
    function _putresources() {
        $this->_putfonts();
    	$this->_putimages();
        $this->_puttemplates();
        $this->_putOobjects();
        
        //Resource dictionary
    	$this->offsets[2]=strlen($this->buffer);
    	$this->_out('2 0 obj');
    	$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
    	$this->_out('/Font <<');
        foreach($this->fonts as $font)
        	$this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R');
    	$this->_out('>>');
    	if(count($this->images) || count($this->tpls))
    	{
    		$this->_out('/XObject <<');
            if (count($this->images)) {
                foreach($this->images as $image)
        			$this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
            }
            if (count($this->tpls)) {
                foreach($this->tpls as $tplidx => $tpl)
        			$this->_out($this->tplprefix.$tplidx.' '.$tpl['n'].' 0 R');
            }
            $this->_out('>>');
    	}
    	$this->_out('>>');
    	$this->_out('endobj');
    }

    /**
     * Private Method that writes /XObjects - "Templates"
     */
    function _puttemplates() {
        $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
	    reset($this->tpls);
        foreach($this->tpls AS $tplidx => $tpl) {

            $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
    		$this->_newobj();
    		$this->tpls[$tplidx]['n'] = $this->n;
    		$this->_out('<<'.$filter.'/Type /XObject');
            $this->_out('/Subtype /Form');
            $this->_out('/FormType 1');
            $this->_out(sprintf('/BBox [%.2f %.2f %.2f %.2f]',$tpl['x']*$this->k, ($tpl['h']-$tpl['y'])*$this->k, $tpl['w']*$this->k, ($tpl['h']-$tpl['y']-$tpl['h'])*$this->k));
            $this->_out('/Resources ');

            if ($tpl['resources']) {
                $this->current_parser =& $tpl['parser'];
                $this->pdf_write_value($tpl['resources']);
            } else {
                $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
            	if (count($this->res['tpl'][$tplidx]['fonts'])) {
                	$this->_out('/Font <<');
                    foreach($this->res['tpl'][$tplidx]['fonts'] as $font)
                		$this->_out($this->fontprefix.$font['i'].' '.$font['n'].' 0 R');
                	$this->_out('>>');
                }
            	if(count($this->res['tpl'][$tplidx]['images']) || count($this->res['tpl'][$tplidx]['tpls']))
            	{
                    $this->_out('/XObject <<');
                    if (count($this->res['tpl'][$tplidx]['images'])) {
                        foreach($this->res['tpl'][$tplidx]['images'] as $image)
                  			$this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
                    }
                    if (count($this->res['tpl'][$tplidx]['tpls'])) {
                        foreach($this->res['tpl'][$tplidx]['tpls'] as $i => $tpl)
                            $this->_out($this->tplprefix.$i.' '.$tpl['n'].' 0 R');
                    }
                    $this->_out('>>');
            	}
            	$this->_out('>>');
            }

        	$this->_out('/Length '.strlen($p).' >>');
    		$this->_putstream($p);
    		$this->_out('endobj');
        }
    }

    /**
     * Rewritten to handle existing own defined objects
     */
    function _newobj($obj_id=false,$onlynewobj=false) {
        if (!$obj_id) {
            $obj_id = ++$this->n;
        }

    	//Begin a new object
        if (!$onlynewobj) {
            $this->offsets[$obj_id]=strlen($this->buffer);
            $this->_out($obj_id.' 0 obj');
            $this->current_obj_id = $obj_id; // for later use with encryption
        }
        
    }

    /**
     * Writes a value
     * Needed to rebuild the source document
     *
     * @param mixed $value A PDF-Value. Structure of values see cases in this method
     */
    function pdf_write_value(&$value)
    {

        switch ($value[0]) {

    		case PDF_TYPE_NUMERIC :
    		case PDF_TYPE_TOKEN :
                // A numeric value or a token.
    			// Simply output them
                $this->_out($value[1]." ");
    			break;

    		case PDF_TYPE_ARRAY :

    			// An array. Output the proper
    			// structure and move on.

    			$this->_out("[",false);
                for ($i = 0; $i < count($value[1]); $i++) {
    				$this->pdf_write_value($value[1][$i]);
    			}

    			$this->_out("]");
    			break;

    		case PDF_TYPE_DICTIONARY :

    			// A dictionary.
    			$this->_out("<<",false);

    			reset ($value[1]);

    			while (list($k, $v) = each($value[1])) {
    				$this->_out($k . " ",false);
    				$this->pdf_write_value($v);
    			}

    			$this->_out(">>");
    			break;

    		case PDF_TYPE_OBJREF :

    			// An indirect object reference
    			// Fill the object stack if needed
    			if (!isset($this->don_obj_stack[$this->current_parser->filename][$value[1]])) {
                    $this->_newobj(false,true);
                    $this->obj_stack[$this->current_parser->filename][$value[1]] = array($this->n,$value);
                    $this->don_obj_stack[$this->current_parser->filename][$value[1]] = array($this->n,$value);
                }
                $objid = $this->don_obj_stack[$this->current_parser->filename][$value[1]][0];

    			$this->_out("{$objid} 0 R"); //{$value[2]}
    			break;

    		case PDF_TYPE_STRING :

    			// A string.
                $this->_out('(' . $value[1] . ')');

    			break;

    		case PDF_TYPE_STREAM :

    			// A stream. First, output the
    			// stream dictionary, then the
    			// stream data itself.
                $this->pdf_write_value($value[1]);
    			$this->_out("stream");
    			$this->_out($value[2][1]);
    			$this->_out("endstream");
    			break;
            case PDF_TYPE_HEX :
            
                $this->_out("<" . $value[1] . ">");
                break;

    		case PDF_TYPE_NULL :
                // The null object.

    			$this->_out("null");
    			break;
    	}
    }
    
    
    /**
     * Private Method
     */
    function _out($s,$ln=true) {
	   //Add a line to the document
	   if ($this->state==2) {
           if (!$this->intpl)
	           $this->pages[$this->page].=$s.($ln == true ? "\n" : '');
           else
               $this->tpls[$this->tpl]['buffer'] .= $s.($ln == true ? "\n" : '');
       } else {
		   $this->buffer.=$s.($ln == true ? "\n" : '');
       }
    }

    /**
     * close all files opened by parsers
     */
    function closeParsers() {
      	foreach ($this->parsers as $parser){
        	$parser->closeFile();
        }
    }


}


// Bis hierher fpdi, die letzte Klasse von fpdi, wird der ersten Klasse von Table vererbt:
// --> extends own2 hieß ursprünglich extends fpdf. fpdf ist die erste vererbung ganz oben
// daher ist hier die schnittstelle, wo man die zwei Pakete vererben kann: http://www.cix88.de/cix_fpdf/fpdf_allgemein/cix_tut_052.php


require_once("class.string_tags.php");

if (!defined('PARAGRAPH_STRING')) define('PARAGRAPH_STRING', '~~~');

class FPDF_MULTICELLTAG extends own2{
var $wt_Current_Tag;
var $wt_FontInfo;//tags font info
var $wt_DataInfo;//parsed string data info
var $wt_DataExtraInfo;//data extra INFO
var $wt_TempData; //some temporary info



	function _wt_Reset_Datas(){
		$this->wt_Current_Tag = "";
		$this->wt_DataInfo = array();
		$this->wt_DataExtraInfo = array(
			"LAST_LINE_BR" => "",		//CURRENT LINE BREAK TYPE
			"CURRENT_LINE_BR" => "",	//LAST LINE BREAK TYPE
			"TAB_WIDTH" => 10			//The tab WIDTH IS IN mm
		);

		//if another measure unit is used ... calculate your OWN
		$this->wt_DataExtraInfo["TAB_WIDTH"] *= (72/25.4) / $this->k;
		/*
			$this->wt_FontInfo - do not reset, once read ... is OK!!!
		*/
	}//function _wt_Reset_Datas(){

	/**
        Sets current tag to specified style
        @param		$tag - tag name
        			$family - text font family
        			$style - text style
        			$size - text size
        			$color - text color
        @return 	nothing
	*/
    function SetStyle($tag,$family,$style,$size,$color)
	{

		if ($tag == "ttags") $this->Error (">> ttags << is reserved TAG Name.");
		if ($tag == "") $this->Error ("Empty TAG Name.");

		//use case insensitive tags
		$tag=trim(strtoupper($tag));
		$this->TagStyle[$tag]['family']=trim($family);
		$this->TagStyle[$tag]['style']=trim($style);
		$this->TagStyle[$tag]['size']=trim($size);
		$this->TagStyle[$tag]['color']=trim($color);
	}//function SetStyle


	/**
        Sets current tag style as the current settings
        	- if the tag name is not in the tag list then de "DEFAULT" tag is saved.
        	This includes a fist call of the function SaveCurrentStyle()
        @param		$tag - tag name
        @return 	nothing
	*/
	function ApplyStyle($tag){

		//use case insensitive tags
		$tag=trim(strtoupper($tag));

		if ($this->wt_Current_Tag == $tag) return;

		if (($tag == "") || (! isset($this->TagStyle[$tag]))) $tag = "DEFAULT";

		$this->wt_Current_Tag = $tag;

		$style = & $this->TagStyle[$tag];

		if (isset($style)){
            $this->SetFont($style['family'], $style['style'], $style['size']);
            //this is textcolor in FPDF format
            if (isset($style['textcolor_fpdf'])) {
            	$this->TextColor = $style['textcolor_fpdf'];
            	$this->ColorFlag=($this->FillColor!=$this->TextColor);
			}else
            {
	            if ($style['color'] <> ""){//if we have a specified color
	            	$temp = explode(",", $style['color']);
					$this->SetTextColor($temp[0], $temp[1], $temp[2]);
				}//fi
			}
			/**/
		}//isset
	}//function ApplyStyle($tag){

	/**
		Save the current settings as a tag default style under the DEFAUTLT tag name
        @param		none
        @return 	nothing
	*/
	function SaveCurrentStyle(){
		//*
		$this->TagStyle['DEFAULT']['family'] = $this->FontFamily;;
		$this->TagStyle['DEFAULT']['style'] = $this->FontStyle;
		$this->TagStyle['DEFAULT']['size'] = $this->FontSizePt;
		$this->TagStyle['DEFAULT']['textcolor_fpdf'] = $this->TextColor;
		$this->TagStyle['DEFAULT']['color'] = "";
		/**/
	}//function SaveCurrentStyle

	/**
		Divides $this->wt_DataInfo and returnes a line from this variable
        @param		$w - Width of the text
        @return     $aLine = array() -> contains informations to draw a line
	*/
	function MakeLine($w){

		$aDataInfo = & $this->wt_DataInfo;
		$aExtraInfo = & $this->wt_DataExtraInfo;

		//last line break >> current line break
		$aExtraInfo['LAST_LINE_BR'] = $aExtraInfo['CURRENT_LINE_BR'];
		$aExtraInfo['CURRENT_LINE_BR'] = "";

	    if($w==0)
	        $w=$this->w - $this->rMargin - $this->x;

		$wmax = ($w - 2*$this->cMargin) * 1000;//max width

		$aLine = array();//this will contain the result
		$return_result = false;//if break and return result
		$reset_spaces = false;

		$line_width = 0;//line string width
		$total_chars = 0;//total characters included in the result string
		$space_count = 0;//numer of spaces in the result string
		$fw = & $this->wt_FontInfo;//font info array

		$last_sepch = ""; //last separator character
        
		foreach ($aDataInfo as $key => $val){
            
			$s = $val['text'];

			$tag = &$val['tag'];

            $bParagraph = false;            
            if (($s == "\t") && ($tag == 'pparg')){
                $bParagraph = true;
                $s = "\t";//place instead a TAB
            }

 			$s_lenght=strlen($s);

            $i = 0;//from where is the string remain
            $j = 0;//untill where is the string good to copy -- leave this == 1->> copy at least one character!!!
            $str = "";
            $s_width = 0;	//string width
            $last_sep = -1; //last separator position
            $last_sepwidth = 0;
            $last_sepch_width = 0;
            $ante_last_sep = -1; //ante last separator position
            $spaces = 0;
            
            //parse the whole string
	        while ($i < $s_lenght){
	        	$c = $s[$i];

   	        	if($c == "\n"){//Explicit line break
   	        		$i++; //ignore/skip this caracter
	        		$aExtraInfo['CURRENT_LINE_BR'] = "BREAK";
	        		$return_result = true;
	        		$reset_spaces = true;
	        		break;
	        	}

				//space
   	        	if($c == " "){
					$space_count++;//increase the number of spaces
					$spaces ++;
	        	}

	        	//	Font Width / Size Array
	        	if (!isset($fw[$tag]) || ($tag == "")){
	        		//if this font was not used untill now,
	        		$this->ApplyStyle($tag);
	        		$fw[$tag]['w'] = $this->CurrentFont['cw'];//width
	        		$fw[$tag]['s'] = $this->FontSize;//size
	        	}

                $char_width = $fw[$tag]['w'][$c] * $fw[$tag]['s'];

	        	//separators
	        	if(is_int(strpos(" ,.:;",$c))){

	        		$ante_last_sep = $last_sep;
	        		$ante_last_sepch = $last_sepch;
	        		$ante_last_sepwidth = $last_sepwidth;
	        		$ante_last_sepch_width = $last_sepch_width;

	        		$last_sep = $i;//last separator position
	        		$last_sepch = $c;//last separator char
	        		$last_sepch_width = $char_width;//last separator char
	        		$last_sepwidth = $s_width;

	        	}

	        	if ($c == "\t"){//TAB
	        		$c = $s[$i] = "";
                    $char_width = $aExtraInfo['TAB_WIDTH'] * 1000;
	        	}

                if ($bParagraph == true){
                    $c = $s[$i] = "";
                    $char_width = $this->wt_TempData['LAST_TAB_REQSIZE']*1000 - $this->wt_TempData['LAST_TAB_SIZE'];
                    if ($char_width < 0) $char_width = 0;                
                }
                
                

	        	$line_width += $char_width;

				if($line_width > $wmax){//Automatic line break

					$aExtraInfo['CURRENT_LINE_BR'] = "AUTO";

					if ($total_chars == 0) {
						/* This MEANS that the $w (width) is lower than a char width...
							Put $i and $j to 1 ... otherwise infinite while*/
						$i = 1;
						$j = 1;
                        $return_result = true;//YES RETURN THE RESULT!!!
                        break;
					}//fi

					if ($last_sep <> -1){
						//we have a separator in this tag!!!
						//untill now there one separator
						if (($last_sepch == $c) && ($last_sepch != " ") && ($ante_last_sep <> -1)){
							/*	this is the last character and it is a separator, if it is a space the leave it...
                                Have to jump back to the last separator... even a space
							*/
							$last_sep = $ante_last_sep;
							$last_sepch = $ante_last_sepch;
							$last_sepwidth = $ante_last_sepwidth;
						}

						if ($last_sepch == " "){
							$j = $last_sep;//just ignore the last space (it is at end of line)
							$i = $last_sep + 1;
							if ( $spaces > 0 ) $spaces --;
							$s_width = $last_sepwidth;
						}else{
							$j = $last_sep + 1;
							$i = $last_sep + 1;
							$s_width = $last_sepwidth + $last_sepch_width;
						}

					}elseif(count($aLine) > 0){
						//we have elements in the last tag!!!!
						if ($last_sepch == " "){//the last tag ends with a space, have to remove it

							$temp = & $aLine[ count($aLine)-1 ];

							if ($temp['text'][strlen($temp['text'])-1] == " "){

								$temp['text'] = substr($temp['text'], 0, strlen($temp['text']) - 1);
								$temp['width'] -= $fw[ $temp['tag'] ]['w'][" "] * $fw[ $temp['tag'] ]['s'];
								$temp['spaces'] --;

								//imediat return from this function
								break 2;
							}else{
								#die("should not be!!!");
							}//fi
						}//fi
					}//fi else

	        		$return_result = true;
	        		break;
				}//fi - Auto line break

	        	//increase the string width ONLY when it is added!!!!
	        	$s_width += $char_width;

	        	$i++;
	        	$j = $i;
	        	$total_chars ++;
	        }//while

	        $str = substr($s, 0, $j);

	        $sTmpStr = & $aDataInfo[$key]['text'];
            $sTmpStr = substr($sTmpStr, $i, strlen($sTmpStr));

            if (($sTmpStr == "") || ($sTmpStr === FALSE))//empty
                array_shift($aDataInfo);

	        if ($val['text'] == $str){
	        }
            
            if (!isset($val['href'])) $val['href']='';
            if (!isset($val['ypos'])) $val['ypos']=0;

	        //we have a partial result
            array_push($aLine, array(
	        	'text' => $str,
	        	'tag' => $val['tag'],
	        	'href' => $val['href'],
	        	'width' => $s_width,
	        	'spaces' => $spaces,
                'ypos' => $val['ypos']
	        ));
            
            $this->wt_TempData['LAST_TAB_SIZE'] = $s_width;
            $this->wt_TempData['LAST_TAB_REQSIZE'] = (isset($val['size'])) ? $val['size'] : 0;           
            
	        if ($return_result) break;//break this for

		}//foreach

		// Check the first and last tag -> if first and last caracters are " " space remove them!!!"

		if ((count($aLine) > 0) && ($aExtraInfo['LAST_LINE_BR'] == "AUTO")){
			//first tag
			$temp = & $aLine[0];
			if ( (strlen($temp['text']) > 0) && ($temp['text'][0] == " ")){
				$temp['text'] = substr($temp['text'], 1, strlen($temp['text']));
				$temp['width'] -= $fw[ $temp['tag'] ]['w'][" "] * $fw[ $temp['tag'] ]['s'];
				$temp['spaces'] --;
			}

			//last tag
			$temp = & $aLine[count($aLine) - 1];
			if ( (strlen($temp['text'])>0) && ($temp['text'][strlen($temp['text'])-1] == " ")){
				$temp['text'] = substr($temp['text'], 0, strlen($temp['text']) - 1);
				$temp['width'] -= $fw[ $temp['tag'] ]['w'][" "] * $fw[ $temp['tag'] ]['s'];
				$temp['spaces'] --;
			}
		}

		if ($reset_spaces){//this is used in case of a "Explicit Line Break"
			//put all spaces to 0 so in case of "J" align there is no space extension
			for ($k=0; $k< count($aLine); $k++) $aLine[$k]['spaces'] = 0;
		}//fi

		return $aLine;
	}//function MakeLine

	/**
		Draws a MultiCell with TAG recognition parameters
        @param		$w - with of the cell
        			$h - height of the cell
        			$pData - string or data to be printed
        			$border - border
        			$align	- align
        			$fill - fill
                    $pDataIsString - true if $pData is a string
                                   - false if $pData is an array containing lines formatted with $this->MakeLine($w) function
                                        (the false option is used in relation with StringToLines, to avoid double formatting of a string

        			These paramaters are the same and have the same behavior as at Multicell function
        @return     nothing
	*/
	function MultiCellTag($w, $h, $pData, $border=0, $align='J', $fill=0, $pDataIsString = true){

		//save the current style settings, this will be the default in case of no style is specified
		$this->SaveCurrentStyle();
		$this->_wt_Reset_Datas();
        
        //if data is string
        if ($pDataIsString === true) $this->DivideByTags($pData);

		$b = $b1 = $b2 = $b3 = '';//borders

		//save the current X position, we will have to jump back!!!!
		$startX = $this -> GetX();

	    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;
	        }

	        //used if there is only one line
	        $b = '';
	        $b .= is_int(strpos($border,'L')) ? 'L' : "";
	        $b .= is_int(strpos($border,'R')) ? 'R' : "";
	        $b .= is_int(strpos($border,'T')) ? 'T' : "";
	        $b .= is_int(strpos($border,'B')) ? 'B' : "";
	    }

	    $first_line = true;
        $last_line = false;
        
        if ($pDataIsString === true){
	        $last_line = !(count($this->wt_DataInfo) > 0);
        }else {
            $last_line = !(count($pData) > 0);
        }
                                                                      
		while(!$last_line){
			if ($fill == 1){
				//fill in the cell at this point and write after the text without filling
				$this->Cell($w,$h,"",0,0,"",1);
				$this->SetX($startX);//restore the X position
			}

            if ($pDataIsString === true){
			    //make a line
			    $str_data = $this->MakeLine($w);
			    //check for last line
			    $last_line = !(count($this->wt_DataInfo) > 0);
            }else {
                //make a line
			    $str_data = array_shift($pData);
			    //check for last line
			    $last_line = !(count($pData) > 0);
            }

			if ($last_line && ($align == "J")){//do not Justify the Last Line
				$align = "L";
			}

			//outputs a line
			$this->PrintLine($w, $h, $str_data, $align);


			//see what border we draw:
			if($first_line && $last_line){
				//we have only 1 line
				$real_brd = $b;
			}elseif($first_line){
				$real_brd = $b1;
			}elseif($last_line){
				$real_brd = $b3;
			}else{
				$real_brd = $b2;
			}

			if ($first_line) $first_line = false;

			//draw the border and jump to the next line
			$this->SetX($startX);//restore the X
			$this->Cell($w,$h,"",$real_brd,2);
		}//while(! $last_line){

		//APPLY THE DEFAULT STYLE
		$this->ApplyStyle("DEFAULT");

		$this->x=$this->lMargin;
	}//function MultiCellExt


	/**
		This method divides the string into the tags and puts the result into wt_DataInfo variable.
        @param		$pStr - string to be printed
        @return     nothing
	*/
    
    function DivideByTags($pStr, $return = false){

		$pStr = str_replace("\t", "<ttags>\t</ttags>", $pStr);
        $pStr = str_replace(PARAGRAPH_STRING, "<pparg>\t</pparg>", $pStr);
		$pStr = str_replace("\r", "", $pStr);

		//initialize the String_TAGS class
		$sWork = new String_TAGS(5);

		//get the string divisions by tags
        $this->wt_DataInfo = $sWork->get_tags($pStr);
               
        if ($return) return $this->wt_DataInfo;
    }//function DivideByTags($pStr){
    
	/**
		This method parses the current text and return an array that contains the text information for
        each line that will be drawed.
        @param		$w - with of the cell
        			$pStr - String to be parsed
        @return     $aStrLines - array - contains parsed text information.
	*/
	function StringToLines($w = 0, $pStr){

		//save the current style settings, this will be the default in case of no style is specified
		$this->SaveCurrentStyle();
		$this->_wt_Reset_Datas();
        
        $this->DivideByTags($pStr);
             
	    $last_line = !(count($this->wt_DataInfo) > 0);
        
        $aStrLines = array();

		while (!$last_line){

			//make a line
			$str_data = $this->MakeLine($w);
            array_push($aStrLines, $str_data);

			//check for last line
			$last_line = !(count($this->wt_DataInfo) > 0);
		}//while(! $last_line){

		//APPLY THE DEFAULT STYLE
		$this->ApplyStyle("DEFAULT");

		return $aStrLines;
	}//function StringToLines    

    
	/**
		Draws a line returned from MakeLine function
        @param		$w - with of the cell
        			$h - height of the cell
        			$aTxt - array from MakeLine
        			$align - text align
        @return     nothing
	*/
	function PrintLine($w, $h, $aTxt, $align='J'){

		if($w==0)
			$w=$this->w-$this->rMargin - $this->x;

		$wmax = $w; //Maximum width

		$total_width = 0;	//the total width of all strings
		$total_spaces = 0;	//the total number of spaces

		$nr = count($aTxt);//number of elements

		for ($i=0; $i<$nr; $i++){
            $total_width += ($aTxt[$i]['width']/1000);
            $total_spaces += $aTxt[$i]['spaces'];
		}

		//default
		$w_first = $this->cMargin;

		switch($align){
			case 'J':
				if ($total_spaces > 0)
					$extra_space = ($wmax - 2 * $this->cMargin - $total_width) / $total_spaces;
				else $extra_space = 0;
				break;
			case 'L':
				break;
			case 'C':
            	$w_first = ($wmax - $total_width) / 2;
				break;
			case 'R':
				$w_first = $wmax - $total_width - $this->cMargin;;
				break;
		}

		// Output the first Cell
		if ($w_first != 0){
			$this->Cell($w_first, $h, "", 0, 0, "L", 0);
		}

		$last_width = $wmax - $w_first;

        while (list($key, $val) = each($aTxt)) {
            
            $bYPosUsed = false;
                       
			//apply current tag style
			$this->ApplyStyle($val['tag']);

			//If > 0 then we will move the current X Position
			$extra_X = 0;
            
            if ($val['ypos'] != 0){
                $lastY = $this->y;
                $this->y = $lastY - $val['ypos'];
                $bYPosUsed = true;
            }

			//string width
			$width = $this->GetStringWidth($val['text']);
			$width = $val['width'] / 1000;

			if ($width == 0) continue;// No width jump over!!!

			if($align=='J'){
				if ($val['spaces'] < 1) $temp_X = 0;
				else $temp_X = $extra_space;

				$this->ws = $temp_X;

				$this->_out(sprintf('%.3f Tw', $temp_X * $this->k));

				$extra_X = $extra_space * $val['spaces'];//increase the extra_X Space

			}else{
				$this->ws = 0;
				$this->_out('0 Tw');
			}//fi

			//Output the Text/Links
			$this->Cell($width, $h, $val['text'], 0, 0, "C", 0, $val['href']);

			$last_width -= $width;//last column width

			if ($extra_X != 0){
				$this -> SetX($this->GetX() + $extra_X);
				$last_width -= $extra_X;
			}//fi
            
            if ($bYPosUsed) $this->y = $lastY;
            
		}//while

		// Output the Last Cell
		if ($last_width != 0){
			$this->Cell($last_width, $h, "", 0, 0, "", 0);
		}//fi
	}//function PrintLine(
}//class

class FPDF_TABLE extends FPDF_MULTICELLTAG
{

var $tb_columns; 		//number of columns of the table
var $tb_header_type; 	//array which contains the header characteristics and texts
var $tb_header_draw;	//TRUE or FALSE, the header is drawed or not
var $tb_header_height;	//This is the Table Header Maximum Height
var $tb_border_draw;	//TRUE or FALSE, the table border is drawed or not
var $tb_data_type; 		//array which contains the data characteristics (only the characteristics)
var $tb_table_type; 	//array which contains the table charactersitics
var $table_startx, $table_starty;	//the X and Y position where the table starts
var $tb_split_normal;   /*  << ** special request from Marc Ulfig >>
                            = false - the split is made only if the cell width does not fit into a page height
                            = true - the split of a cell will ocuur whenever necesary
                        */
var $Draw_Header_Command;	//command which determines in the DrawData first the header draw
var $Data_On_Current_Page; // = true/false ... if on current page was some data written

//returns the width of the page in user units
function PageWidth(){
	return (int) $this->w-$this->rMargin-$this->lMargin;
}

//constructor(not a real one, but have to call it first)
//we initialize all the variables that we use
function Table_Init($col_no = 0, $header_draw = true, $border_draw = true){
	$this->tb_columns = $col_no;
	$this->tb_header_type = Array();
	$this->tb_header_draw = $header_draw;
    $this->tb_header_height = 0;
	$this->tb_border_draw = $border_draw;
	$this->tb_data_type = Array();
    $this->tb_split_normal = true;
	$this->tb_type = Array();
	$this->table_startx = $this->GetX();
	$this->table_starty = $this->GetY();

	$this->Draw_Header_Command = false; //by default we don't draw the header
	$this->Data_On_Current_Page = false;
}

//Sets the number of columns of the table
function Set_Table_Columns($nr){
	$this->tb_columns = $nr;
}

//Sets the number of columns of the table
function Set_Table_SplitMode($pSplit = true){
	$this->tb_split_normal = $pSplit;
}

/*
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!!!!
*/

/*
Function: Set_Header_Type($type_arr) -- sets the array for the header type

type array =
	 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
*/

function Set_Header_Type($type_arr){
	$this->tb_header_type = $type_arr;
}


/*
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!!!!
*/

/*
Function: Set_data_Type($type_arr) -- sets the array for the header type

type array =
	 array(
		0=>array(
				"T_COLOR" => array(120,120,120),
				"T_SIZE" => 5,
				...
				"BRD_TYPE" => 1
			  ),
		1=>array(
				...
			  ),
	 );
where 0,1... are the column number
*/

function Set_Data_Type($type_arr){
	$this->tb_data_type = $type_arr;
}



/*
Function Set_Table_Type

$type_arr = array(
				"BRD_COLOR"=> array (120,120,120), //border color
				"BRD_SIZE"=>5), //border line width
				"TB_COLUMNS"=>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
				)
*/
function Set_Table_Type($type_arr){

	if (isset($type_arr['TB_COLUMNS'])) $this->tb_columns = $type_arr['TB_COLUMNS'];
	if (!isset($type_arr['L_MARGIN'])) $type_arr['L_MARGIN']=0;//default values

	$this->tb_table_type = $type_arr;

}

//this functiondraws the exterior table border!!!!
function Draw_Table_Border(){
/*				"BRD_COLOR"=> array (120,120,120), //border color
				"BRD_SIZE"=>5), //border line width
				"TB_COLUMNS"=>5), //the number of columns
				"TB_ALIGN"=>"L"), //the align of the table, possible values = L, R, C equivalent to Left, Right, Center
*/

	if ( ! $this->tb_border_draw ) return;

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

	//set the colors
	list($r, $g, $b) = $this->tb_table_type['BRD_COLOR'];
	$this->SetDrawColor($r, $g, $b);

	//set the line width
	$this->SetLineWidth($this->tb_table_type['BRD_SIZE']);
    
    #echo $this->GetY()-$this->table_starty." ";;

	//draw the border
	$this->Rect(
		$this->table_startx,
		$this->table_starty,
		$this->Get_Table_Width(),
		$this->GetY()-$this->table_starty);

}

function End_Page_Border(){
	if (isset($this->tb_table_type['BRD_TYPE_END_PAGE'])){

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

			//set the colors
			list($r, $g, $b) = $this->tb_table_type['BRD_COLOR'];
			$this->SetDrawColor($r, $g, $b);

			//set the line width
			$this->SetLineWidth($this->tb_table_type['BRD_SIZE']);

			//draw the line
			$this->Line($this->table_startx, $this->GetY(), $this->table_startx + $this->Get_Table_Width(), $this->GetY());
		}
	}
}

//returns the table width in user units
function Get_Table_Width()
{
	//calculate the table width
	$tb_width = 0;
	for ($i=0; $i < $this->tb_columns; $i++){
		$tb_width += $this->tb_header_type[$i]['WIDTH'];
	}
	return $tb_width;
}

//alignes the table to C, L or R(default is L)
function Table_Align(){
	//check if the table is aligned
	if (isset($this->tb_table_type['TB_ALIGN'])) $tb_align = $this->tb_table_type['TB_ALIGN']; else $tb_align='';

	//set the table align
	switch($tb_align){
		case 'C':
			$this->SetX($this->lMargin + $this->tb_table_type['L_MARGIN'] + ($this->PageWidth() - $this->Get_Table_Width())/2);
			break;
		case 'R':
			$this->SetX($this->lMargin + $this->tb_table_type['L_MARGIN'] + ($this->PageWidth() - $this->Get_Table_Width()));
			break;
		default:
			$this->SetX($this->lMargin + $this->tb_table_type['L_MARGIN']);
			break;
	}//if (isset($this->tb_table_type['TB_ALIGN'])){
}

//Draws the Header
function Draw_Header(){
	$this->Draw_Header_Command = true;
    $this->tb_header_height = 0;
}

function Init_Table_Position(){
   	$this->Table_Align();

	$this->table_startx = $this->GetX();
	$this->table_starty = $this->GetY();
}

/**   
      Draws the header line from a table.
      Call:
      @param            none
      @return           nothing
*/
function Draw_Header_(){
    
    $this->tb_header_height = 0;
    
    $this->Init_Table_Position();
    
    $this->Draw_Header_Command = false;

	//if the header will be showed
	if ( ! $this->tb_header_draw ) return;
      
    $this->DrawTableLine($this->tb_header_type, false, 1);
	
	$this->Data_On_Current_Page = true;    

}


/**   
      Draws a data line from the table.
      Call this function after the table initialization, table, header and data types are set
      Call:
      @param            $data - array containing line informations 
                        $header - header will be drawed or not in case of new page 
      @return           nothing
*/
function Draw_Data($data, $header = true){
    
    $this->DrawTableLine($data, $header, 0);   
}

/**   Draws a data or header line from the table.
      Call:
      @param            $data - array containing line informations 
                        $header - header will be drawed or not in case of new page 
                        $pDataType =    0 - normal line
                                        1 - header line
                                        2 - not implemented
      @return           nothing
*/
function DrawTableLine($data, $header, $pDataType = 0){

	$h = 0;
    $hm = 0;
	$xx = Array();
	$tt = Array();
    
    if (!$this->Data_On_Current_Page) $this->Init_Table_Position();
    
    if ($pDataType == 0){//data line
        if ($this->Draw_Header_Command){//draw the header
                $this->Draw_Header_();
	    }
    }    
         
    //maximum Height available on this page
    $AvailPageH = $this->PageBreakTrigger - $this->GetY();
    
    if ($AvailPageH <= 0){ //there is no more space on this page
        $this->TableAddPage($header);//add a page without header
        $this->DrawTableLine($data, $header, $pDataType);//recall this function
        return;//exit this function
    }

        
    
    $MaxPageH = $this->PageBreakTrigger - $this->tMargin;
    $split = false;
    
    $backdata = $data; //backup data in case of split or recall;
    
	//calculate the maximum height of the cells
	for($i=0; $i < $this->tb_columns; $i++)
	{
        if (!isset($data[$i]['TEXT']) || ($data[$i]['TEXT']=='')) $data[$i]['TEXT'] = " ";
		if (!isset($data[$i]['T_FONT'])) $data[$i]['T_FONT'] = $this->tb_data_type[$i]['T_FONT'];
		if (!isset($data[$i]['T_TYPE'])) $data[$i]['T_TYPE'] = $this->tb_data_type[$i]['T_TYPE'];
		if (!isset($data[$i]['T_SIZE'])) $data[$i]['T_SIZE'] = $this->tb_data_type[$i]['T_SIZE'];
		if (!isset($data[$i]['T_COLOR'])) $data[$i]['T_COLOR'] = $this->tb_data_type[$i]['T_COLOR'];
		if (!isset($data[$i]['T_ALIGN'])) $data[$i]['T_ALIGN'] = $this->tb_data_type[$i]['T_ALIGN'];
		if (!isset($data[$i]['V_ALIGN'])) $data[$i]['V_ALIGN'] = $this->tb_data_type[$i]['V_ALIGN'];
		if (!isset($data[$i]['LN_SIZE'])) $data[$i]['LN_SIZE'] = $this->tb_data_type[$i]['LN_SIZE'];
		if (!isset($data[$i]['BRD_SIZE'])) $data[$i]['BRD_SIZE'] = $this->tb_data_type[$i]['BRD_SIZE'];
		if (!isset($data[$i]['BRD_COLOR'])) $data[$i]['BRD_COLOR'] = $this->tb_data_type[$i]['BRD_COLOR'];
		if (!isset($data[$i]['BRD_TYPE'])) $data[$i]['BRD_TYPE'] = $this->tb_data_type[$i]['BRD_TYPE'];
		if (!isset($data[$i]['BG_COLOR'])) $data[$i]['BG_COLOR'] = $this->tb_data_type[$i]['BG_COLOR'];

		$this->SetFont(	$data[$i]['T_FONT'],
						$data[$i]['T_TYPE'],
						$data[$i]['T_SIZE']);

		$data[$i]['CELL_WIDTH'] = $this->tb_header_type[$i]['WIDTH'];

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

			$colspan = (int) $data[$i]['COLSPAN'];//convert to integer

			for ($j = 1; $j < $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->tb_columns)
					$data[$i]['CELL_WIDTH'] += $this->tb_header_type[$i + $j]['WIDTH'];
			}
		}
       
        $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->StringToLines($data[$i]['CELL_WIDTH'], $data[$i]['TEXT']);
        $NoLines = count($data[$i]['TEXT_STRLINES']);
        
        $hm = max($hm, $data[$i]['LN_SIZE'] * $NoLines);//this would be the normal height
        
        if ($NoLines > $MaxLines){
            $split = true;
            $NoLines = $MaxLines;
            $data[$i]['TEXT_SPLITLINES'] = array_splice($data[$i]['TEXT_STRLINES'], $MaxLines);
        }      
               
        $data[$i]['CELL_LINES'] = $NoLines;

		//this is the maximum cell height
		$h = max($h, $data[$i]['LN_SIZE'] * $data[$i]['CELL_LINES']);

		if (isset($data[$i]['COLSPAN'])){
			//just skip the other cells
			$i = $i + $colspan - 1;
		}       
	}


    if ($pDataType == 0){//data line        
    
        if (!$this->tb_split_normal){//split only if the cell height is bigger than a page size        
        
            if ($split && (($hm + $this->tb_header_height) < $MaxPageH)){//if the header is splitted and it has space on a page
                $this->TableAddPage($header);//add a page without header
                $this->DrawTableLine($backdata, $header, 0);//recall this function
                return;
            }
            
        }
        
    }    
    
       
    if ($pDataType == 1){//header line
        $this->tb_header_height = $hm;

        if ($split && ($hm < $MaxPageH)){//if the header is splitted and it has space on a page
            //new page
            $this->TableAddPage(false);//add a page without header
            $this->DrawTableLine($backdata, $header, 1);//recall this function
            return;            
        }
        
        if ( ((2*$h) > $AvailPageH) && ((2*$h) < $MaxPageH)){
        /*
            if the header double size is bigger then the available size 
            but the double size is smaller than a page size
            >>> we draw a new page
        **/
            $this->TableAddPage(false);//add a page withot header
            $this->DrawTableLine($backdata, $header, 1);//recall this function
            return;
        }
    }
    
	$this->Table_Align();

	//Draw the cells of the row
	for( $i = 0; $i < $this->tb_columns; $i++ )
	{

		//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']);

		//Save the current position
		$x=$this->GetX();
		$y=$this->GetY();

		//print the text
		$this->MultiCellTable(
				$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]['LN_SIZE'] * $data[$i]['CELL_LINES']
				);

		//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;
		}

	}

	$this->Data_On_Current_Page = true;

	//Go to the next line
	$this->Ln($h);
    
    if ($split){
        
	    //calculate the maximum height of the cells
	    for($i=0; $i < $this->tb_columns; $i++){
            $backdata[$i]['TEXT_STRLINES'] = isset($data[$i]['TEXT_SPLITLINES']) ? $data[$i]['TEXT_SPLITLINES'] : array();
        }
        
        $this->TableAddPage($header);//we have a page split, add a page

        if ($pDataType == 1) $header = false;
        $this->DrawTableLine($backdata, $header, $pDataType);
    }
}//DrawTableLine

/**   Adds a new page in the pdf document and initializes the table and the header if necessary.
      Call:
      @param
                        $header - boolean - if the header is drawed or not
      @return           nothing
*/
function TableAddPage($header = true){
    $this->Draw_Table_Border();//draw the table border

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

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

    $this->Data_On_Current_Page = false;

    $this->table_startx = $this->GetX();
    $this->table_starty = $this->GetY();
    if ($header) $this ->Draw_Header();//if we have to draw the header!!!    
}//TableAddPage

/**   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!!!!
      @return           nothing
*/
function MultiCellTable($w, $h, $txtData, $border=0, $align='J', $valign='T', $fill=0, $vh=0)
{

	$b1 = '';//border for top cell
	$b2 = '';//border for middle cell
	$b3 = '';//border for bottom cell

	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;

		}
	}

	switch ($valign){
		case 'T':
			$wh_T = 0;//Top width
			$wh_B = $vh - $wh_T;//Bottom width
			break;
		case 'M':
			$wh_T = $vh/2;
			$wh_B = $vh/2;
			break;
		case 'B':
			$wh_T = $vh;
			$wh_B = 0;
			break;
		default://default is TOP ALIGN
			$wh_T = 0;//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);
	}

	$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->MultiCell($w,$h,$txt,$b2,$align,$fill);
	$this->MultiCellTag($w, $h, $txtData, $b2, $align, 1, 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);

		$this->x=$this->lMargin;
	}

}


}//end of pdf_table class

?>