<?php
/**
 *
 * FusionCharts Exporter - 'PDF Resource' handles
 * FusionCharts (since v3.1) Server Side Export feature that
 * helps FusionCharts exported as PDF file.
 *
 *
 *    @author FusionCharts
 *    @description FusionCharts Exporter (Server-Side - PHP)
 *    @version 2.0 [ 12 February 2009 ]
 *
 */
/**
 *  ChangeLog / Version History:
 *  ----------------------------
 *
 *  1.1  [ 13 March 2009 ]
 *  Fixed PDF Image data Compressor
 *
 *
 *
 *   1.0 [ 12 February 2009 ]
 *
 *	FEATURES:
 *       - Integrated with new Export feature of FusionCharts 3.1 & FusionCharts Exporter v 2.0
 *       - can save to server side directory
 *       - can provide download or open in browser window/frame other than _self
 *       - can save single or multiple page PDF
 *
 *	ISSUES:
 * 		 - best viewed in 72 DPI
 *		 - Each page is of the same pixel size that of the chart
 *		 - no page margin or gutter as of now
 *		 - no custom text or other PDF elements can be incorporated
 *
 */
/**
 * Copyright (c) 2012 Infosoft Global Private Limited
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/**
 *  GENERAL NOTES
 *  -------------
 *
 *  Chart would POST export data (which consists of encoded image data stream,
 *  width, height, background color and various other export parameters like
 *  exportFormat, exportFileName, exportAction, exportTargetWindow) to this script.
 *
 *  The script would process this data using appropriate resource files build
 *  export binary (PDF/image)
 *
 *  It either saves the binary as file to a server side directory or push it as
 *  Download or open in a new browser window/frame.
 *
 *
 *  ISSUES
 *  ------
 *   Q> What if someone wishes to open in the same page where the chart existed as postback
 *      replacing the old page?
 *
 *   A> Not directly supported using any chart attribute or parameter but can do by
 *      removing/commenting the line containing 'header( content-disposition ...'
 *
 */
/**
 *   @requires	FCExporter.php  A file that includes this resource
 *
 *
 *   Details
 *   -------
 *
 *   The resource files would have these things as common:
 *
 *   	a) a constant - MIMETYPES that would have a string
 *         containing semicolon separated key value pairs.
 *		   Each key can be a format name specified in the
 *		   HANDLERASSOCIATIONS constant. The associated value
 *		   would be the mimetype for the specified format.
 *
 *		   e.g. define("MIMETYPES","jpg=image/jpeg;jpeg=image/jpeg;png=image/png;gif=image/gif");
 *
 *
 *		b) a constant - EXTENSIONS that again contain a string of
 *		   semicolon separated key value pair. Each key would again be the
 *		   format name and the extension would be the file extension.
 *
 *		   e.g. define("EXTENSIONS","jpg=jpg;jpeg=jpg;png=png;gif=gif");
 *
 *
 *      c) a function  - exportProcessor ( $stream , $meta )
 *		   It would take the FusionCharts exncoded image string as $stream &
 *		   an associative array $meta containging width, height and bgColor keys.
 *
 *         The function would return an object of mixed type which would contain
 *		   the processed binary/relevant export object.
 *
 *
 *		d) a function - exportOutput ( $exportObj, $exportSettings, $quality=1 )
 *         It would take the processed export object and other export setting as parameter.
 *         Moreover, it would take an optional parameter - $quality (in scale of 0 to 1).
 *         By Default, the $quality is passed as 1 (best quality)
 *
 *		   The function would return the file path on success or return false on failure.
 *
 *      [ The other code in the resource file can be anything that support this architecture ]
 *
 */

// =============================================================================
// ==                  Constants and  Variables                               ==
// =============================================================================
// ==============================================================================
//   Users are recommended NOT to perform any editing beyond this point.       ==
// ==============================================================================

/* ------------------------- EXPORT RESOURCES -------------------------------- */

// This constant lists the mime types related to each export format this resource handles
// The value is semicolon separated key value pair for each format
// Each key is the format and value is the mime type
define( "MIMETYPES" , "pdf=application/pdf" );

// This constant lists all the file extensions for the export formats
// The value is semicolon separated key value pair for each format
// Each key is the format and value is the file extension
define( "EXTENSIONS", "pdf=pdf" );


// =============================================================================
// ==                             Public Functions                            ==
// =============================================================================

/**
 *  Gets Export data from FCExporter - main module and build the export binary/objct.
 *  @param	$stream 	(string) export image data in FusionCharts compressed format
 *      	$meta		{array)	Image meta data in keys "width", "heigth" and "bgColor"
 *              $exportParams   {array} Export related parameters
 *  @return 			image object/binary
 */
function exportProcessor( $stream , $meta, $exportParams )
{

	// create a new export object
	// here it is an PDF generator class
	// pass all reqiured parameters
	$FCExporter = new FCPDFGenerator ( $stream, $meta ['width' ], $meta ['height' ], $meta ['bgColor' ] );

	// return export ready PDF binary data
	return $FCExporter->getPDFObjects();
}

/**
 *  exports (save/download) FusinoCharts exported PDF.
 *  @param	$exportObj 		(mixed) binary/objct exported by exportProcessor
 *	@param	$exportSettings	(array) various server-side export settings stored in keys like
 *									"type", "ready" "filepath" etc. Required for 'save' expotAction.
 *									For 'download' action "filepath" is blank (this is checked to find
 *									whether the action is "download" or not.
 *	@param	$quality		(integer) quality factor 0-1 (1 being the best quality). As of now we always pass 1.
 *
 *  @return 				false is fails. {filepath} if succeeds. Only returned when action is 'save'.
 */
function exportOutput ( $exportObj, $exportSettings, $quality = 1 )
{

	// calls imagepdf function that saves/downloads PDF binary
	// store saving status in $doneExport which receives false if fails and true on success
	$doneExport = imagepdf ( $exportObj, @$exportSettings ['filepath'] );

	// check $doneEport and if true sets status to {filepath}'s value
	// set false if fails
	$status =( $doneExport ? basename ( @$exportSettings ['filepath'] ) : false );

	// return status
	return $status;

}

/**
 *  emulates imagepng/imagegif/imagejpeg. It saves PDF data to server or to sets to download as per
 *  the exportAction. exportAction is 'download' when {filepath} is null.
 *  @param	$exportObj 	(string) PDF binary exported by exportProcessor
 *	@param	$filepath	(string) Path where the exported PDF is to be stored
 *									when the action is "download" it is null.
 *
 *  @return 			(boolean) false is fails. true if succeeds. Only returned when action is 'save'.
 */
function imagepdf ( $exportObject , $filepath )
{

	// when filepath is null the action is 'download'
	// hence write the PDF bimary yo response stream and immediately terminate/close/end stream
	// to prevent any garbage that might get into response and corrupt the PDF binary
	if ( !@$filepath ) die ( $exportObject );

	// open file path in write mode
	$fp = @fopen ( $filepath , "w" );

	if( $fp )
	{
		// write PDF binary
		$status = @fwrite( $fp , $exportObject );
		//close file
		$fc = @fclose( $fp );
	}

	// return status
	return ( bool ) @$status;

}


#################################################################################
##                                                                             ##
##                     	 		 EXPORT CLASS             		               ##
##                                                                             ##
#################################################################################

class FCPDFGenerator
{
	//Array - Stores multiple chart export data
	var $arrExportData;
	//stores number of pages = length of $arrExportData array
	var $numPages=0;

	//Constructor - By default the chart-export-data can be passed to this
	function FCPDFGenerator($imageData_FCFormat="", $width="", $height="", $bgcolor="ffffff"){
		if($imageData_FCFormat && $width && $height){
			$this->setBitmapData($imageData_FCFormat, $width, $height, $bgcolor);
		}

	}

	// Add chart export data
	function setBitmapData($imageData_FCFormat, $width, $height, $bgcolor="ffffff"){
		$this->arrExportData[$this->numPages]["width"]=$width;
		$this->arrExportData[$this->numPages]["height"]=$height;
		$this->arrExportData[$this->numPages]["bgcolor"]=$bgcolor;
		$this->arrExportData[$this->numPages]["imageData"]=$imageData_FCFormat;
		$this->numPages++;

	}
	//create image PDF object containing the chart image
	function addImageToPDF($id=0,$isCompressed=true){
		//PDF Object number
		$imgObjNo = 6 + $id*3;

		//Get chart Image binary
		$baImg=$this->getBitmapData24($id);
		//Compress image binary
		$imgBinary = $isCompressed?gzcompress($baImg, 9):$baImg;
		//get the lenght of the image binary
		$len=strlen($imgBinary);
		//Build PDF object containing the image binary and other formats required
		$imgObj=$imgObjNo." 0 obj\n<<\n/Subtype /Image /ColorSpace /DeviceRGB /BitsPerComponent 8 /HDPI 72 /VDPI 72 ".($isCompressed?"/Filter /FlateDecode ":"")."/Width ".$this->arrExportData[$id]["width"]." /Height ".$this->arrExportData[$id]["height"]." /Length ".$len." >>\nstream\n".$imgBinary."endstream\nendobj\n";


		return $imgObj;

	}

	//Main PDF builder function
	function getPDFObjects($isCompressed=true) {

		$PDFBytes="";
		//Store all PDF objects in this temporary string to be written to ByteArray
		$strTmpObj="";

		//start xref array
		$xRefList[0]="xref\n0 ";
		$xRefList[1]="0000000000 65535 f \n"; //Address Refenrece to obj 0

		//Build PDF objects sequentially
		//version and header
		$strTmpObj="%PDF-1.3\n%{FC}\n";
		$PDFBytes.=$strTmpObj;

		//OBJECT 1 : info (optional)
		$strTmpObj="1 0 obj<<\n/Author (FusionCharts)\n/Title (FusionCharts)\n/Creator (FusionCharts)\n>>\nendobj\n";
		$xRefList[]=$this->calculateXPos(strlen($PDFBytes)); //refenrece to obj 1
		$PDFBytes.=$strTmpObj;

		//OBJECT 2 : Starts with Pages Catalogue
		$strTmpObj="2 0 obj\n<< /Type /Catalog /Pages 3 0 R >>\nendobj\n";
		$xRefList[]=$this->calculateXPos(strlen($PDFBytes)); //refenrece to obj 2
		$PDFBytes.=$strTmpObj;

		//OBJECT 3 : Page Tree (reference to pages of the catalogue)
		$strTmpObj="3 0 obj\n<<  /Type /Pages /Kids [";
		for($i=0;$i<$this->numPages;$i++){
			$strTmpObj.=((($i+1)*3)+1)." 0 R\n";
		}
		$strTmpObj.="] /Count ".$this->numPages." >>\nendobj\n";

		$xRefList[]=$this->calculateXPos(strlen($PDFBytes)); //refenrece to obj 3
		$PDFBytes.=$strTmpObj;


		//Each image page
		for($itr=0;$itr<$this->numPages;$itr++){
			$iWidth=$this->arrExportData[$itr]["width"];
			$iHeight=$this->arrExportData[$itr]["height"];
			//OBJECT 4..7..10..n : Page config
			$strTmpObj=((($itr+2)*3)-2)." 0 obj\n<<\n/Type /Page /Parent 3 0 R \n/MediaBox [ 0 0 ".$iWidth." ".$iHeight." ]\n/Resources <<\n/ProcSet [ /PDF ]\n/XObject <</R".($itr+1)." ".(($itr+2)*3)." 0 R>>\n>>\n/Contents [ ".((($itr+2)*3)-1)." 0 R ]\n>>\nendobj\n";
			$xRefList[]=$this->calculateXPos(strlen($PDFBytes)); //refenrece to obj 4,7,10,13,16...
			$PDFBytes.=$strTmpObj;


			//OBJECT 5...8...11...n : Page resource object (xobject resource that transforms the image)
			$xRefList[]=$this->calculateXPos(strlen($PDFBytes)); //refenrece to obj 5,8,11,14,17...
			$PDFBytes.=$this->getXObjResource($itr);

			//OBJECT 6...9...12...n : Binary xobject of the page (image)
			$imgBA=$this->addImageToPDF($itr,$isCompressed);
			$xRefList[]=$this->calculateXPos(strlen($PDFBytes));//refenrece to obj 6,9,12,15,18...
			$PDFBytes.=$imgBA;
		}



		//xrefs	compilation
		$xRefList[0].=(count($xRefList)-1)."\n";

		//get trailer
		$trailer=$this->getTrailer(strlen($PDFBytes) ,count($xRefList)-1);

		//write xref and trailer to PDF
		$PDFBytes.=implode("",$xRefList);
		$PDFBytes.=$trailer;

		//write EOF
		$PDFBytes.="%%EOF\n";

		return $PDFBytes;

	}


	//Build Image resource object that transforms the image from First Quadrant system to Second Quadrant system
	function getXObjResource($itr=0) {
		return ((($itr+2)*3)-1)." 0 obj\n<< /Length ".(24+strlen($this->arrExportData[$itr]["width"].$this->arrExportData[$itr]["height"]))." >>\nstream\nq\n".$this->arrExportData[$itr]["width"]." 0 0 ".$this->arrExportData[$itr]["height"]." 0 0 cm\n/R".($itr+1)." Do\nQ\nendstream\nendobj\n";
	}

	// Calculate the XREF position of eah PDF Object
	function calculateXPos($posn){
		return (str_pad($posn, 10, '0', STR_PAD_LEFT))." 00000 n \n";
	}

	// Calculate and build Trailer of PDF
	function getTrailer($xrefpos,$numxref=7) {
		return "trailer\n<<\n/Size ".$numxref."\n/Root 2 0 R\n/Info 1 0 R\n>>\nstartxref\n".$xrefpos."\n";
	}


	// Parse Chart Image data in to PDF Ready Image Data
	function getBitmapData24($id=0){
		$imageData24="";

		// Split the data into rows using ; as separator
		$rows = explode(";", $this->arrExportData[$id]["imageData"]);

		// Detect the background color
		if (!($this->arrExportData[$id]["bgcolor"])){
			$this->arrExportData[$id]["bgcolor"] = "ffffff";
		}

		// Iterate through all the rows
		for($i= 0; $i<count($rows); $i++){
			// Parse all the pixels in this row
			$pixels = explode(",", $rows[$i]);
			// Iterate through the pixels
			for($j=0; $j<count($pixels); $j++){
				// Split the pixel into color and repeat value
				$thispix = explode("_", $pixels[$j]);
				// Reference to color
				$c = $thispix[0];
				// Reference to repeat factor
				$r = ( int ) $thispix[1];
					//If color is not empty (i.e., not background pixel)
					if ($c==""){
						$c=$this->arrExportData[$id]["bgcolor"];
					}
					if (strlen($c)<6){
						//If the hexadecimal code is less than 6 characters, pad with 0
						$c = str_pad($c, 6, '0', STR_PAD_LEFT);
					}
					$rgb = pack("CCC",hexdec(substr($c, 0, 2)),hexdec(substr($c, 2, 2)),hexdec(substr($c, 4, 2)));

					$strPixelRGB_hex=str_repeat(($rgb),$r);
					// Set the pixel
					$imageData24.= $strPixelRGB_hex;
			}
		}

		return $imageData24;
	}


}


return 'true';
?>